train 3.2.14 → 3.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. metadata +29 -149
  3. data/LICENSE +0 -201
  4. data/lib/train.rb +0 -193
  5. data/lib/train/errors.rb +0 -44
  6. data/lib/train/extras.rb +0 -11
  7. data/lib/train/extras/command_wrapper.rb +0 -201
  8. data/lib/train/extras/stat.rb +0 -136
  9. data/lib/train/file.rb +0 -212
  10. data/lib/train/file/local.rb +0 -82
  11. data/lib/train/file/local/unix.rb +0 -96
  12. data/lib/train/file/local/windows.rb +0 -68
  13. data/lib/train/file/remote.rb +0 -40
  14. data/lib/train/file/remote/aix.rb +0 -29
  15. data/lib/train/file/remote/linux.rb +0 -21
  16. data/lib/train/file/remote/qnx.rb +0 -41
  17. data/lib/train/file/remote/unix.rb +0 -110
  18. data/lib/train/file/remote/windows.rb +0 -110
  19. data/lib/train/globals.rb +0 -5
  20. data/lib/train/options.rb +0 -81
  21. data/lib/train/platforms.rb +0 -102
  22. data/lib/train/platforms/common.rb +0 -34
  23. data/lib/train/platforms/detect.rb +0 -12
  24. data/lib/train/platforms/detect/helpers/os_common.rb +0 -160
  25. data/lib/train/platforms/detect/helpers/os_linux.rb +0 -80
  26. data/lib/train/platforms/detect/helpers/os_windows.rb +0 -142
  27. data/lib/train/platforms/detect/scanner.rb +0 -85
  28. data/lib/train/platforms/detect/specifications/api.rb +0 -20
  29. data/lib/train/platforms/detect/specifications/os.rb +0 -629
  30. data/lib/train/platforms/detect/uuid.rb +0 -32
  31. data/lib/train/platforms/family.rb +0 -31
  32. data/lib/train/platforms/platform.rb +0 -109
  33. data/lib/train/plugin_test_helper.rb +0 -51
  34. data/lib/train/plugins.rb +0 -40
  35. data/lib/train/plugins/base_connection.rb +0 -198
  36. data/lib/train/plugins/transport.rb +0 -49
  37. data/lib/train/transports/cisco_ios_connection.rb +0 -133
  38. data/lib/train/transports/local.rb +0 -240
  39. data/lib/train/transports/mock.rb +0 -183
  40. data/lib/train/transports/ssh.rb +0 -271
  41. data/lib/train/transports/ssh_connection.rb +0 -342
  42. data/lib/train/version.rb +0 -7
data/lib/train.rb DELETED
@@ -1,193 +0,0 @@
1
- # encoding: utf-8
2
- #
3
- # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
-
5
- require_relative "train/version"
6
- require_relative "train/options"
7
- require_relative "train/plugins"
8
- require_relative "train/errors"
9
- require_relative "train/platforms"
10
- require "uri"
11
-
12
- module Train
13
- # Create a new transport instance, with the plugin indicated by the
14
- # given name.
15
- #
16
- # @param [String] name of the plugin
17
- # @param [Array] *args list of arguments for the plugin
18
- # @return [Transport] instance of the new transport or nil
19
- def self.create(name, *args)
20
- cls = load_transport(name)
21
- cls.new(*args) unless cls.nil?
22
- end
23
-
24
- # Retrieve the configuration options of a transport plugin.
25
- #
26
- # @param [String] name of the plugin
27
- # @return [Hash] map of default options
28
- def self.options(name)
29
- cls = load_transport(name)
30
- cls.default_options unless cls.nil?
31
- end
32
-
33
- # Load the transport plugin indicated by name. If the plugin is not
34
- # yet found in the plugin registry, it will be attempted to load from
35
- # `train/transports/plugin_name`.
36
- #
37
- # @param [String] name of the plugin
38
- # @return [Train::Transport] the transport plugin
39
- def self.load_transport(transport_name)
40
- transport_name = transport_name.to_s
41
- transport_class = Train::Plugins.registry[transport_name]
42
- return transport_class unless transport_class.nil?
43
-
44
- # Try to load the transport name from the core transports...
45
- require "train/transports/" + transport_name
46
- Train::Plugins.registry[transport_name]
47
- rescue LoadError => _
48
- begin
49
- # If it's not in the core transports, try loading from a train plugin gem.
50
- gem_name = "train-" + transport_name
51
- require gem_name
52
- return Train::Plugins.registry[transport_name]
53
- # rubocop: disable Lint/HandleExceptions
54
- rescue LoadError => _
55
- # rubocop: enable Lint/HandleExceptions
56
- # Intentionally empty rescue - we're handling it below anyway
57
- end
58
-
59
- ex = Train::PluginLoadError.new("Can't find train plugin #{transport_name}. Please install it first.")
60
- ex.transport_name = transport_name
61
- raise ex
62
- end
63
-
64
- # Legacy code to unpack a series of items from an incoming Hash
65
- # Inspec::Config.unpack_train_credentials now handles this in most cases that InSpec needs
66
- # If you need to unpack a URI, use unpack_target_from_uri
67
- # TODO: deprecate; can't issue a warning because train doesn't have a logger until the connection is setup (See base_connection.rb)
68
- def self.target_config(config = nil)
69
- conf = config.dup
70
- # Symbolize keys
71
- conf.keys.each do |key|
72
- unless key.is_a? Symbol
73
- conf[key.to_sym] = conf.delete(key)
74
- end
75
- end
76
-
77
- group_keys_and_keyfiles(conf) # TODO: move logic into SSH plugin
78
- return conf if conf[:target].to_s.empty?
79
-
80
- unpack_target_from_uri(conf[:target], conf).merge(conf)
81
- end
82
-
83
- # Given a string that looks like a URI, unpack connection credentials.
84
- # The name of the desired transport is always taken from the 'scheme' slot of the URI;
85
- # the remaining portion of the URI is parsed as if it were an HTTP URL, and then
86
- # the URL components are stored in the credentials hash. It is up to the transport
87
- # to interpret the fields in a sensible way for that transport.
88
- # New transport authors are encouraged to use transport://credset format (see
89
- # inspec/inspec/issues/3661) rather than inventing a new field mapping.
90
- def self.unpack_target_from_uri(uri_string, opts = {}) # rubocop: disable Metrics/AbcSize
91
- creds = {}
92
- return creds if uri_string.empty?
93
-
94
- # split up the target's host/scheme configuration
95
- uri = parse_uri(uri_string)
96
- unless uri.host.nil? && uri.scheme.nil?
97
- creds[:backend] ||= uri.scheme
98
- creds[:host] ||= uri.hostname
99
- creds[:port] ||= uri.port
100
- creds[:user] ||= uri.user
101
- creds[:path] ||= uri.path
102
- creds[:password] ||=
103
- if opts[:www_form_encoded_password] && !uri.password.nil?
104
- URI.decode_www_form_component(uri.password)
105
- else
106
- uri.password
107
- end
108
- end
109
-
110
- # ensure path is nil, if its empty; e.g. required to reset defaults for winrm # TODO: move logic into winrm plugin
111
- creds[:path] = nil if !creds[:path].nil? && creds[:path].to_s.empty?
112
-
113
- # compact! is available in ruby 2.4+
114
- # TODO: rewrite next line using compact! once we drop support for ruby 2.3
115
- creds = creds.delete_if { |_, value| value.nil? }
116
-
117
- # return the updated config
118
- creds
119
- end
120
-
121
- # Parse a URI. Supports empty URI's with paths, e.g. `mock://`
122
- #
123
- # @param string [string] URI string, e.g. `schema://domain.com`
124
- # @return [URI::Generic] parsed URI object
125
- def self.parse_uri(string)
126
- URI.parse(string)
127
- rescue URI::InvalidURIError => e
128
- # A use-case we want to catch is parsing empty URIs with a schema
129
- # e.g. mock://. To do this, we match it manually and fake the hostname
130
- case string
131
- when %r{^([a-z]+)://$}
132
- string += "dummy"
133
- when /^([a-z]+):$/
134
- string += "//dummy"
135
- else
136
- raise Train::UserError, e
137
- end
138
-
139
- uri = URI.parse(string)
140
- uri.host = nil
141
- uri
142
- end
143
- private_class_method :parse_uri
144
-
145
- # Examine the given credential information, and if all is well,
146
- # return the transport name.
147
- # TODO: this actually does no validation of the credential options whatsoever
148
- def self.validate_backend(credentials, default_transport_name = "local")
149
- return default_transport_name if credentials.nil?
150
-
151
- transport_name = credentials[:backend]
152
-
153
- # TODO: Determine if it is ever possible (or supported) for transport_name to be 'localhost'
154
- # TODO: After inspec/inspec/pull/3750 is merged, should be able to remove nil from the list
155
- if credentials[:sudo] && [nil, "local", "localhost"].include?(transport_name)
156
- raise Train::UserError, "Sudo is only valid when running against a remote host. "\
157
- "To run this locally with elevated privileges, run the command with `sudo ...`."
158
- end
159
-
160
- return transport_name unless transport_name.nil?
161
-
162
- unless credentials[:target].nil?
163
- # We should not get here, because if target_uri unpacking was successful,
164
- # it would have set credentials[:backend]
165
- raise Train::UserError, "Cannot determine backend from target "\
166
- "configuration #{credentials[:target]}. Valid example: ssh://192.168.0.1"
167
- end
168
-
169
- unless credentials[:host].nil?
170
- raise Train::UserError, "Host configured, but no backend was provided. Please "\
171
- "specify how you want to connect. Valid example: ssh://192.168.0.1"
172
- end
173
-
174
- credentials[:backend] = default_transport_name
175
- end
176
-
177
- def self.group_keys_and_keyfiles(conf)
178
- # in case the user specified a key-file, register it that way
179
- # we will clear the list of keys and put keys and key_files separately
180
- keys_mixed = conf[:keys]
181
- return if keys_mixed.nil?
182
-
183
- conf[:key_files] = []
184
- conf[:keys] = []
185
- keys_mixed.each do |key|
186
- if !key.nil? && File.file?(key)
187
- conf[:key_files].push(key)
188
- else
189
- conf[:keys].push(key)
190
- end
191
- end
192
- end
193
- end
data/lib/train/errors.rb DELETED
@@ -1,44 +0,0 @@
1
- # encoding: utf-8
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- # Author:: Dominik Richter (<dominik.richter@gmail.com>)
5
- # Author:: Christoph Hartmann (<chris@lollyrock.com>)
6
- #
7
- # Copyright (C) 2013, Fletcher Nichol
8
- #
9
- # Licensed under the Apache License, Version 2.0 (the "License");
10
-
11
- module Train
12
- # Base exception for any exception explicitly raised by the Train library.
13
- class Error < ::StandardError
14
- attr_reader :reason
15
-
16
- def initialize(message = "", reason = :not_provided)
17
- super(message)
18
- @reason = reason
19
- end
20
- end
21
-
22
- # Base exception class for all exceptions that are caused by user input
23
- # errors.
24
- class UserError < Error; end
25
-
26
- # We could not load a plugin, because of a user error
27
- class PluginLoadError < UserError
28
- attr_accessor :transport_name
29
- end
30
-
31
- # Base exception class for all exceptions that are caused by incorrect use
32
- # of an API.
33
- class ClientError < Error; end
34
-
35
- # Base exception class for all exceptions that are caused by other failures
36
- # in the transport layer.
37
- class TransportError < Error; end
38
-
39
- # Exception for when no platform can be detected.
40
- class PlatformDetectionFailed < Error; end
41
-
42
- # Exception for when a invalid cache type is passed.
43
- class UnknownCacheType < Error; end
44
- end
data/lib/train/extras.rb DELETED
@@ -1,11 +0,0 @@
1
- # encoding: utf-8
2
- #
3
- # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
-
5
- module Train::Extras
6
- require_relative "extras/command_wrapper"
7
- require_relative "extras/stat"
8
-
9
- CommandResult = Struct.new(:stdout, :stderr, :exit_status)
10
- LoginCommand = Struct.new(:command, :arguments)
11
- end
@@ -1,201 +0,0 @@
1
- # encoding: utf-8
2
- # author: Dominik Richter
3
- # author: Christoph Hartmann
4
-
5
- require "base64"
6
- require_relative "../errors"
7
-
8
- module Train::Extras
9
- # Define the interface of all command wrappers.
10
- class CommandWrapperBase
11
- # Verify that the command wrapper is initialized properly and working.
12
- #
13
- # @return [Any] verification result, nil if all went well, otherwise a message
14
- def verify
15
- raise Train::ClientError, "#{self.class} does not implement #verify()"
16
- end
17
-
18
- # Wrap a command and return the augmented command which can be executed.
19
- #
20
- # @param [Strin] command that will be wrapper
21
- # @return [String] result of wrapping the command
22
- def run(_command)
23
- raise Train::ClientError, "#{self.class} does not implement #run(command)"
24
- end
25
- end
26
-
27
- # Wrap linux commands and add functionality like sudo.
28
- class LinuxCommand < CommandWrapperBase
29
- Train::Options.attach(self)
30
-
31
- option :shell, default: false
32
- option :shell_options, default: nil
33
- option :shell_command, default: nil
34
- option :sudo, default: false
35
- option :sudo_options, default: nil
36
- option :sudo_password, default: nil
37
- option :sudo_command, default: nil
38
- option :user
39
-
40
- attr_reader :backend
41
-
42
- def initialize(backend, options)
43
- @backend = backend
44
- validate_options(options)
45
-
46
- @shell = options[:shell]
47
- @shell_options = options[:shell_options] # e.g. '--login'
48
- @shell_command = options[:shell_command] # e.g. '/bin/sh'
49
- @sudo = options[:sudo]
50
- @sudo_options = options[:sudo_options]
51
- @sudo_password = options[:sudo_password]
52
- @sudo_command = options[:sudo_command]
53
- @user = options[:user]
54
- end
55
-
56
- # (see CommandWrapperBase::verify)
57
- def verify
58
- cmd = if @sudo
59
- # Wrap it up. It needs /dev/null on the outside to disable stdin
60
- # NOTE: can't use @sudo_command because -v conflicts with -E.
61
- # See test-kitchen's use of this variable for conflict.
62
- "sh -c '(sudo -v) < /dev/null'"
63
- else
64
- run("echo")
65
- end
66
-
67
- # rubocop:disable Style/BlockDelimiters
68
- res = @backend.with_sudo_pty {
69
- @backend.run_command(cmd)
70
- }
71
- return nil if res.exit_status == 0
72
-
73
- rawerr = "#{res.stdout} #{res.stderr}".strip
74
-
75
- case rawerr
76
- when "Sorry, try again"
77
- ["Wrong sudo password.", :bad_sudo_password]
78
- when "sudo: no tty present and no askpass program specified"
79
- ["Sudo requires a password, please configure it.", :sudo_password_required]
80
- when "sudo: command not found"
81
- ["Can't find sudo command. Please either install and "\
82
- "configure it on the target or deactivate sudo.", :sudo_command_not_found]
83
- when "sudo: sorry, you must have a tty to run sudo"
84
- ["Sudo requires a TTY. Please see the README on how to configure "\
85
- "sudo to allow for non-interactive usage.", :sudo_no_tty]
86
- else
87
- [rawerr, nil]
88
- end
89
- end
90
-
91
- def verify!
92
- msg, reason = verify
93
- return nil unless msg
94
-
95
- raise Train::UserError.new("Sudo failed: #{msg}", reason)
96
- end
97
-
98
- # (see CommandWrapperBase::run)
99
- def run(command)
100
- shell_wrap(sudo_wrap(command))
101
- end
102
-
103
- def self.active?(options)
104
- options.is_a?(Hash) && (
105
- options[:sudo] ||
106
- options[:shell]
107
- )
108
- end
109
-
110
- private
111
-
112
- # wrap the cmd in a sudo command
113
- def sudo_wrap(cmd)
114
- return cmd unless @sudo
115
- return cmd if @user == "root"
116
-
117
- res = (@sudo_command || "sudo") + " "
118
-
119
- if @sudo_password
120
- str = safe_string(@sudo_password + "\n")
121
- res = "#{str} | #{res}-S "
122
- end
123
-
124
- res << "#{@sudo_options} " if @sudo_options
125
-
126
- res + cmd
127
- end
128
-
129
- # wrap the cmd in a subshell allowing for options to
130
- # passed to the subshell
131
- def shell_wrap(cmd)
132
- return cmd unless @shell
133
-
134
- shell = @shell_command || "$SHELL"
135
- options = " #{@shell_options}" if @shell_options
136
-
137
- "#{safe_string(cmd)} | #{shell}#{options}"
138
- end
139
-
140
- # encapsulates encoding the string into a safe form, and decoding for use.
141
- # @return [String] A command line snippet that can be used as part of a pipeline.
142
- def safe_string(str)
143
- b64str = Base64.strict_encode64(str)
144
- "echo #{b64str} | base64 --decode"
145
- end
146
- end
147
-
148
- # Wrap windows commands.
149
- class WindowsCommand < CommandWrapperBase
150
- Train::Options.attach(self)
151
-
152
- option :shell_command, default: nil
153
-
154
- def initialize(backend, options)
155
- @backend = backend
156
- validate_options(options)
157
-
158
- @shell_command = options[:shell_command] # e.g. 'powershell'
159
- end
160
-
161
- # (see CommandWrapperBase::run)
162
- def run(command)
163
- powershell_wrap(command)
164
- end
165
-
166
- private
167
-
168
- # Wrap the cmd in an encoded command to allow pipes and quotes
169
- def powershell_wrap(cmd)
170
- shell = @shell_command || "powershell"
171
-
172
- # Prevent progress stream from leaking into stderr
173
- script = "$ProgressPreference='SilentlyContinue';" + cmd
174
-
175
- # Encode script so PowerShell can use it
176
- script = script.encode("UTF-16LE", "UTF-8")
177
- base64_script = Base64.strict_encode64(script)
178
-
179
- cmd = "#{shell} -NoProfile -EncodedCommand #{base64_script}"
180
- cmd
181
- end
182
- end
183
-
184
- class CommandWrapper
185
- include_options LinuxCommand
186
- include_options WindowsCommand
187
-
188
- def self.load(transport, options)
189
- if transport.platform.unix?
190
- return nil unless LinuxCommand.active?(options)
191
-
192
- res = LinuxCommand.new(transport, options)
193
- res.verify!
194
-
195
- res
196
- elsif transport.platform.windows?
197
- WindowsCommand.new(transport, options)
198
- end
199
- end
200
- end
201
- end