train 3.2.14 → 3.2.20

Sign up to get free protection for your applications and to get access to all the features.
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