Class: Train::Plugins::Transport::BaseConnection

Inherits:
Object
  • Object
show all
Includes:
Extras
Defined in:
lib/train/plugins/base_connection.rb

Overview

A Connection instance can be generated and re-generated, given new connection details such as connection port, hostname, credentials, etc. This object is responsible for carrying out the actions on the remote host such as executing commands, transferring files, etc.

Author:

  • Fletcher Nichol <fnichol@nichol.ca>

Instance Method Summary collapse

Constructor Details

#initialize(options = nil) {|self| ... } ⇒ BaseConnection

Create a new Connection instance.

Parameters:

  • options (Hash) (defaults to: nil)

    connection options

Yields:

  • (self)

    yields itself for block-style invocation



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/train/plugins/base_connection.rb', line 21

def initialize(options = nil)
  @options = options || {}
  @logger = @options.delete(:logger) || Logger.new($stdout, level: :fatal)
  Train::Platforms::Detect::Specifications::OS.load
  Train::Platforms::Detect::Specifications::Api.load

  # default caching options
  @cache_enabled = {
    file: true,
    command: false,
    api_call: false,
  }

  @cache = {}
  @cache_enabled.each_key do |type|
    clear_cache(type)
  end
end

Instance Method Details

#backend_typeObject



109
110
111
# File 'lib/train/plugins/base_connection.rb', line 109

def backend_type
  @options[:backend] || "unknown"
end

#cache_enabled?(type) ⇒ Boolean

Returns:

  • (Boolean)


63
64
65
# File 'lib/train/plugins/base_connection.rb', line 63

def cache_enabled?(type)
  @cache_enabled[type.to_sym]
end

#cached_client(type, key) ⇒ Object

Returns cached client if caching enabled. Otherwise returns whatever is given in the block.

Examples:


def demo_client
  cached_client(:api_call, :demo_connection) do
    DemoClient.new(args)
  end
end

Parameters:

  • type (symbol)

    one of [:api_call, :file, :command]

  • key (symbol)

    for your cached connection



57
58
59
60
61
# File 'lib/train/plugins/base_connection.rb', line 57

def cached_client(type, key)
  return yield unless cache_enabled?(type)

  @cache[type][key] ||= yield
end

#closeObject

Closes the session connection, if it is still active.



83
84
85
# File 'lib/train/plugins/base_connection.rb', line 83

def close
  # this method may be left unimplemented if that is applicable
end

#disable_cache(type) ⇒ Object



75
76
77
78
79
80
# File 'lib/train/plugins/base_connection.rb', line 75

def disable_cache(type)
  raise Train::UnknownCacheType, "#{type} is not a valid cache type" unless @cache_enabled.keys.include?(type.to_sym)

  @cache_enabled[type.to_sym] = false
  clear_cache(type.to_sym)
end

#download(remotes, local) ⇒ Object

Download remote files or directories to local host.

Parameters:

  • remotes (Array<String>)

    paths to remote files or directories

  • local (String)

    path to local destination. If ‘local` is an existing directory, `remote` will be downloaded into the directory using its original name

Raises:

  • (TransportFailed)

    if the files could not all be downloaded successfully, which may vary by implementation



194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/train/plugins/base_connection.rb', line 194

def download(remotes, local)
  FileUtils.mkdir_p(File.dirname(local))

  Array(remotes).each do |remote|
    new_content = file(remote).content
    local_file = File.join(local, File.basename(remote))

    logger.debug("Attempting to download '#{remote}' as file #{local_file}")

    File.open(local_file, "w") { |fp| fp.write(new_content) }
  end
end

#enable_cache(type) ⇒ Object

Enable caching types for Train. Currently we support :api_call, :file and :command types



69
70
71
72
73
# File 'lib/train/plugins/base_connection.rb', line 69

def enable_cache(type)
  raise Train::UnknownCacheType, "#{type} is not a valid cache type" unless @cache_enabled.keys.include?(type.to_sym)

  @cache_enabled[type.to_sym] = true
end

#file(path, *args) ⇒ Object

This is the main file call for all connections. This will call the private file_via_connection on the connection with optional caching



159
160
161
162
163
# File 'lib/train/plugins/base_connection.rb', line 159

def file(path, *args)
  return file_via_connection(path, *args) unless cache_enabled?(:file)

  @cache[:file][path] ||= file_via_connection(path, *args)
end

#force_platform!(name, platform_details = nil) ⇒ Object Also known as: direct_platform



100
101
102
103
104
105
106
107
# File 'lib/train/plugins/base_connection.rb', line 100

def force_platform!(name, platform_details = nil)
  plat = Train::Platforms.name(name)
  plat.backend = self
  plat.platform = platform_details unless platform_details.nil?
  plat.find_family_hierarchy
  plat.add_platform_methods
  plat
end

#inspectObject



113
114
115
# File 'lib/train/plugins/base_connection.rb', line 113

def inspect
  "%s[%s]" % [self.class, backend_type]
end

#load_json(j) ⇒ Object



93
94
95
96
97
98
# File 'lib/train/plugins/base_connection.rb', line 93

def load_json(j)
  require_relative "../transports/mock"
  j["files"].each do |path, jf|
    @cache[:file][path] = Train::Transports::Mock::Connection::File.from_json(jf)
  end
end

#login_commandLoginCommand

Builds a LoginCommand which can be used to open an interactive session on the remote host.

Returns:

Raises:

  • (NotImplementedError)


211
212
213
# File 'lib/train/plugins/base_connection.rb', line 211

def 
  raise NotImplementedError, "#{self.class} does not implement #login_command()"
end

#platformPlatform Also known as: os

Get information on the operating system which this transport connects to.

Returns:

  • (Platform)

    system information



122
123
124
# File 'lib/train/plugins/base_connection.rb', line 122

def platform
  @platform ||= Train::Platforms::Detect.scan(self)
end

#run_command(cmd, opts = {}, &data_handler) ⇒ Object

This is the main command call for all connections. This will call the private run_command_via_connection on the connection with optional caching

This command accepts an optional data handler block. When provided, inbound data will be published vi ‘data_handler.call(data)`. This can allow callers to receive and render updates from remote command execution.

Parameters:

  • the (String)

    command to run

  • optional (Hash)

    hash of options for this command. The derived connection class’s implementation of run_command_via_connection should receive and apply these options.



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/train/plugins/base_connection.rb', line 139

def run_command(cmd, opts = {}, &data_handler)
  # Some implementations do not accept an opts argument.
  # We cannot update all implementations to accept opts due to them being separate plugins.
  # Therefore here we check the implementation's arity to maintain compatibility.
  case method(:run_command_via_connection).arity.abs
  when 1
    return run_command_via_connection(cmd, &data_handler) unless cache_enabled?(:command)

    @cache[:command][cmd] ||= run_command_via_connection(cmd, &data_handler)
  when 2
    return run_command_via_connection(cmd, opts, &data_handler) unless cache_enabled?(:command)

    @cache[:command][cmd] ||= run_command_via_connection(cmd, opts, &data_handler)
  else
    raise NotImplementedError, "#{self.class} does not implement run_command_via_connection with arity #{method(:run_command_via_connection).arity}"
  end
end

#to_jsonObject



87
88
89
90
91
# File 'lib/train/plugins/base_connection.rb', line 87

def to_json
  {
    "files" => Hash[@cache[:file].map { |x, y| [x, y.to_json] }],
  }
end

#upload(locals, remote) ⇒ Object

Uploads local files or directories to remote host.

Parameters:

  • locals (Array<String>)

    paths to local files or directories

  • remote (String)

    path to remote destination

Raises:

  • (TransportFailed)

    if the files could not all be uploaded successfully, which may vary by implementation



171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/train/plugins/base_connection.rb', line 171

def upload(locals, remote)
  unless file(remote).directory?
    raise TransportError, "#{self.class} expects remote directory as second upload parameter"
  end

  Array(locals).each do |local|
    new_content = File.read(local)
    remote_file = File.join(remote, File.basename(local))

    logger.debug("Attempting to upload '#{local}' as file #{remote_file}")

    file(remote_file).content = new_content
  end
end

#wait_until_readyObject

Block and return only when the remote host is prepared and ready to execute command and upload files. The semantics and details will vary by implementation, but a round trip through the hosted service is preferred to simply waiting on a socket to become available.



220
221
222
# File 'lib/train/plugins/base_connection.rb', line 220

def wait_until_ready
  # this method may be left unimplemented if that is applicable log
end

#with_sudo_ptyObject



40
41
42
# File 'lib/train/plugins/base_connection.rb', line 40

def with_sudo_pty
  yield
end