Module: Train

Defined in:
lib/train/errors.rb,
lib/train.rb,
lib/train/file.rb,
lib/train/globals.rb,
lib/train/options.rb,
lib/train/plugins.rb,
lib/train/version.rb,
lib/train/audit_log.rb,
lib/train/file/local.rb,
lib/train/file/remote.rb,
lib/train/file/local/unix.rb,
lib/train/file/remote/aix.rb,
lib/train/file/remote/qnx.rb,
lib/train/file/remote/unix.rb,
lib/train/file/remote/linux.rb,
lib/train/file/local/windows.rb,
lib/train/file/remote/windows.rb

Overview

Author

Dominik Richter (<[email protected]>)

Defined Under Namespace

Modules: Extras, Options, Platforms, Transports Classes: AuditLog, ClientError, CommandTimeoutReached, Error, File, PlatformDetectionFailed, PlatformUuidDetectionFailed, PluginLoadError, Plugins, TransportError, UnknownCacheType, UserError

Constant Summary collapse

VERSION =
"3.12.7".freeze

Class Method Summary collapse

Class Method Details

.create(name, *args) ⇒ Transport

Create a new transport instance, with the plugin indicated by the given name.

Parameters:

  • name (String)

    of the plugin

  • *args (Array)

    list of arguments for the plugin

Returns:

  • (Transport)

    instance of the new transport or nil



18
19
20
21
# File 'lib/train.rb', line 18

def self.create(name, *args)
  cls = load_transport(name)
  cls.new(*args) unless cls.nil?
end

.group_keys_and_keyfiles(conf) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/train.rb', line 180

def self.group_keys_and_keyfiles(conf)
  # in case the user specified a key-file, register it that way
  # we will clear the list of keys and put keys and key_files separately
  keys_mixed = conf[:keys]
  return if keys_mixed.nil?

  conf[:key_files] = []
  conf[:keys] = []
  keys_mixed.each do |key|
    if !key.nil? && File.file?(key)
      conf[:key_files].push(key)
    else
      conf[:keys].push(key)
    end
  end
end

.load_transport(transport_name) ⇒ Train::Transport

Load the transport plugin indicated by name. If the plugin is not yet found in the plugin registry, it will be attempted to load from ‘train/transports/plugin_name`.

Parameters:

  • name (String)

    of the plugin

Returns:

  • (Train::Transport)

    the transport plugin



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/train.rb', line 39

def self.load_transport(transport_name)
  transport_name = transport_name.to_s
  transport_class = Train::Plugins.registry[transport_name]
  return transport_class unless transport_class.nil?

  # Try to load the transport name from the core transports...
  require "train/transports/" + transport_name
  Train::Plugins.registry[transport_name]
rescue LoadError => _
  begin
    # If it's not in the core transports, try loading from a train plugin gem.
    gem_name = "train-" + transport_name
    require gem_name
    return Train::Plugins.registry[transport_name]
    # rubocop: disable Lint/HandleExceptions
  rescue LoadError => _
    # rubocop: enable Lint/HandleExceptions
    # Intentionally empty rescue - we're handling it below anyway
  end

  ex = Train::PluginLoadError.new("Can't find train plugin #{transport_name}. Please install it first.")
  ex.transport_name = transport_name
  raise ex
end

.options(name) ⇒ Hash

Retrieve the configuration options of a transport plugin.

Parameters:

  • name (String)

    of the plugin

Returns:

  • (Hash)

    map of default options



27
28
29
30
31
# File 'lib/train.rb', line 27

def self.options(name)
  cls = load_transport(name)
  # Merging default_audit_log_options so that they will get listed in the options that are available.
  cls.default_options.merge(cls.default_audit_log_options) unless cls.nil?
end

.plugin(version = 1) ⇒ Transport

Create a new plugin by inheriting from the class returned by this method. Create a versioned plugin by providing the transport layer plugin version to this method. It will then select the correct class to inherit from.

The plugin version determines what methods will be available to your plugin.

Parameters:

  • version (Int) (defaults to: 1)

    1 the plugin version to use

Returns:

  • (Transport)

    the versioned transport base class



30
31
32
33
34
35
36
37
38
# File 'lib/train/plugins.rb', line 30

def self.plugin(version = 1)
  if version != 1
    raise ClientError,
      "Only understand train plugin version 1. You are trying to "\
      "initialize a train plugin #{version}, which is not supported "\
      "in the current release of train."
  end
  ::Train::Plugins::Transport
end

.src_rootObject



2
3
4
# File 'lib/train/globals.rb', line 2

def self.src_root
  File.expand_path(File.join(__FILE__, "..", "..", ".."))
end

.target_config(config = nil) ⇒ Object

Legacy code to unpack a series of items from an incoming Hash Inspec::Config.unpack_train_credentials now handles this in most cases that InSpec needs If you need to unpack a URI, use unpack_target_from_uri TODO: deprecate; can’t issue a warning because train doesn’t have a logger until the connection is setup (See base_connection.rb)



68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/train.rb', line 68

def self.target_config(config = nil)
  conf = config.dup
  # Symbolize keys
  conf.keys.each do |key|
    unless key.is_a? Symbol
      conf[key.to_sym] = conf.delete(key)
    end
  end

  group_keys_and_keyfiles(conf) # TODO: move logic into SSH plugin
  return conf if conf[:target].to_s.empty?

  unpack_target_from_uri(conf[:target], conf).merge(conf)
end

.unpack_target_from_uri(uri_string, opts = {}) ⇒ Object

Given a string that looks like a URI, unpack connection credentials. The name of the desired transport is always taken from the ‘scheme’ slot of the URI; the remaining portion of the URI is parsed as if it were an HTTP URL, and then the URL components are stored in the credentials hash. It is up to the transport to interpret the fields in a sensible way for that transport. New transport authors are encouraged to use transport://credset format (see inspec/inspec/issues/3661) rather than inventing a new field mapping.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/train.rb', line 90

def self.unpack_target_from_uri(uri_string, opts = {}) # rubocop: disable Metrics/AbcSize
  creds = {}
  return creds if uri_string.empty?

  # split up the target's host/scheme configuration
  uri = parse_uri(uri_string)
  unless uri.host.nil? && uri.scheme.nil?
    creds[:backend]  ||= uri.scheme
    creds[:host]     ||= uri.hostname
    creds[:port]     ||= uri.port
    creds[:user]     ||= uri.user
    creds[:path]     ||= uri.path
    creds[:password] ||=
      if opts[:www_form_encoded_password] && !uri.password.nil?
        Addressable::URI.unencode_component(uri.password)
      else
        uri.password
      end
  end

  # ensure path is nil, if its empty; e.g. required to reset defaults for winrm # TODO: move logic into winrm plugin
  creds[:path] = nil if !creds[:path].nil? && creds[:path].to_s.empty?

  # compact! is available in ruby 2.4+
  # TODO: rewrite next line using compact! once we drop support for ruby 2.3
  creds = creds.delete_if { |_, value| value.nil? }

  # merge train options in from the URI query string
  creds.merge!(uri.query_values.map { |k, v| [k.to_sym, v] }.to_h) unless uri.query_values.nil?

  # return the updated config
  creds
end

.validate_backend(credentials, default_transport_name = "local") ⇒ Object

Examine the given credential information, and if all is well, return the transport name. TODO: this actually does no validation of the credential options whatsoever



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/train.rb', line 151

def self.validate_backend(credentials, default_transport_name = "local")
  return default_transport_name if credentials.nil?

  transport_name = credentials[:backend]

  # TODO: Determine if it is ever possible (or supported) for transport_name to be 'localhost'
  # TODO: After inspec/inspec/pull/3750 is merged, should be able to remove nil from the list
  if credentials[:sudo] && [nil, "local", "localhost"].include?(transport_name)
    raise Train::UserError, "Sudo is only valid when running against a remote host. "\
      "To run this locally with elevated privileges, run the command with `sudo ...`."
  end

  return transport_name unless transport_name.nil?

  unless credentials[:target].nil?
    # We should not get here, because if target_uri unpacking was successful,
    # it would have set credentials[:backend]
    raise Train::UserError, "Cannot determine backend from target "\
         "configuration #{credentials[:target]}. Valid example: ssh://192.168.0.1"
  end

  unless credentials[:host].nil?
    raise Train::UserError, "Host configured, but no backend was provided. Please "\
         "specify how you want to connect. Valid example: ssh://192.168.0.1"
  end

  credentials[:backend] = default_transport_name
end