train-core 2.1.7 → 2.1.13

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/train.rb +20 -20
  3. data/lib/train/errors.rb +1 -1
  4. data/lib/train/extras.rb +2 -2
  5. data/lib/train/extras/command_wrapper.rb +24 -24
  6. data/lib/train/extras/stat.rb +27 -27
  7. data/lib/train/file.rb +30 -30
  8. data/lib/train/file/local.rb +8 -8
  9. data/lib/train/file/local/unix.rb +5 -5
  10. data/lib/train/file/local/windows.rb +1 -1
  11. data/lib/train/file/remote.rb +8 -8
  12. data/lib/train/file/remote/aix.rb +1 -1
  13. data/lib/train/file/remote/linux.rb +2 -2
  14. data/lib/train/file/remote/qnx.rb +8 -8
  15. data/lib/train/file/remote/unix.rb +10 -14
  16. data/lib/train/file/remote/windows.rb +5 -5
  17. data/lib/train/globals.rb +1 -1
  18. data/lib/train/options.rb +8 -8
  19. data/lib/train/platforms.rb +8 -8
  20. data/lib/train/platforms/common.rb +1 -1
  21. data/lib/train/platforms/detect/helpers/os_common.rb +36 -32
  22. data/lib/train/platforms/detect/helpers/os_linux.rb +12 -12
  23. data/lib/train/platforms/detect/helpers/os_windows.rb +27 -29
  24. data/lib/train/platforms/detect/scanner.rb +4 -4
  25. data/lib/train/platforms/detect/specifications/api.rb +8 -8
  26. data/lib/train/platforms/detect/specifications/os.rb +252 -252
  27. data/lib/train/platforms/detect/uuid.rb +5 -7
  28. data/lib/train/platforms/platform.rb +9 -5
  29. data/lib/train/plugin_test_helper.rb +12 -12
  30. data/lib/train/plugins.rb +5 -5
  31. data/lib/train/plugins/base_connection.rb +13 -13
  32. data/lib/train/plugins/transport.rb +7 -7
  33. data/lib/train/transports/cisco_ios_connection.rb +20 -20
  34. data/lib/train/transports/local.rb +22 -22
  35. data/lib/train/transports/mock.rb +33 -35
  36. data/lib/train/transports/ssh.rb +47 -47
  37. data/lib/train/transports/ssh_connection.rb +28 -28
  38. data/lib/train/transports/winrm.rb +37 -37
  39. data/lib/train/transports/winrm_connection.rb +12 -12
  40. data/lib/train/version.rb +1 -1
  41. metadata +2 -2
@@ -1,11 +1,9 @@
1
- # encoding: utf-8
2
-
3
- require 'train/plugins'
4
- require 'digest'
1
+ require "train/plugins"
2
+ require "digest"
5
3
 
6
4
  module Train::Transports
7
5
  class Mock < Train.plugin(1)
8
- name 'mock'
6
+ name "mock"
9
7
 
10
8
  def initialize(conf = nil)
11
9
  @conf = conf || {}
@@ -17,39 +15,39 @@ module Train::Transports
17
15
  end
18
16
 
19
17
  def to_s
20
- 'Mock Transport'
18
+ "Mock Transport"
21
19
  end
22
20
 
23
21
  private
24
22
 
25
23
  def trace_calls
26
24
  interface_methods = {
27
- 'Train::Transports::Mock' =>
25
+ "Train::Transports::Mock" =>
28
26
  Train::Transports::Mock.instance_methods(false),
29
- 'Train::Transports::Mock::Connection' =>
27
+ "Train::Transports::Mock::Connection" =>
30
28
  Connection.instance_methods(false),
31
- 'Train::Transports::Mock::Connection::File' =>
29
+ "Train::Transports::Mock::Connection::File" =>
32
30
  Connection::FileCommon.instance_methods(false),
33
- 'Train::Transports::Mock::Connection::OS' =>
31
+ "Train::Transports::Mock::Connection::OS" =>
34
32
  Train::Platform.instance_methods(false),
35
33
  }
36
34
 
37
35
  # rubocop:disable Metrics/ParameterLists
38
36
  # rubocop:disable Lint/Eval
39
- set_trace_func proc { |event, _file, _line, id, binding, classname|
40
- unless classname.to_s.start_with?('Train::Transports::Mock') and
41
- event == 'call' and
42
- interface_methods[classname.to_s].include?(id)
37
+ set_trace_func(proc { |event, _file, _line, id, binding, classname|
38
+ unless classname.to_s.start_with?("Train::Transports::Mock") &&
39
+ (event == "call") &&
40
+ interface_methods[classname.to_s].include?(id)
43
41
  next
44
42
  end
45
43
  # kindly borrowed from the wonderful simple-tracer by matugm
46
44
  arg_names = eval(
47
- 'method(__method__).parameters.map { |arg| arg[1].to_s }',
45
+ "method(__method__).parameters.map { |arg| arg[1].to_s }",
48
46
  binding)
49
- args = eval("#{arg_names}.map { |arg| eval(arg) }", binding).join(', ')
50
- prefix = '-' * (classname.to_s.count(':') - 2) + '> '
47
+ args = eval("#{arg_names}.map { |arg| eval(arg) }", binding).join(", ")
48
+ prefix = "-" * (classname.to_s.count(":") - 2) + "> "
51
49
  puts("#{prefix}#{id} #{args}")
52
- }
50
+ })
53
51
  # rubocop:enable all
54
52
  end
55
53
  end
@@ -67,14 +65,14 @@ class Train::Transports::Mock
67
65
  end
68
66
 
69
67
  def uri
70
- 'mock://'
68
+ "mock://"
71
69
  end
72
70
 
73
71
  def mock_os(value = {})
74
72
  # if a user passes a nil value, set to an empty hash so the merge still succeeds
75
73
  value ||= {}
76
- value.each { |k, v| value[k] = 'unknown' if v.nil? }
77
- value = { name: 'mock', family: 'mock', release: 'unknown', arch: 'unknown' }.merge(value)
74
+ value.each { |k, v| value[k] = "unknown" if v.nil? }
75
+ value = { name: "mock", family: "mock", release: "unknown", arch: "unknown" }.merge(value)
78
76
 
79
77
  platform = Train::Platforms.name(value[:name])
80
78
  platform.find_family_hierarchy
@@ -100,26 +98,26 @@ class Train::Transports::Mock
100
98
  end
101
99
 
102
100
  def mock_command(cmd, stdout = nil, stderr = nil, exit_status = 0)
103
- @cache[:command][cmd] = Command.new(stdout || '', stderr || '', exit_status)
101
+ @cache[:command][cmd] = Command.new(stdout || "", stderr || "", exit_status)
104
102
  end
105
103
 
106
104
  def command_not_found(cmd)
107
105
  if @options[:verbose]
108
- STDERR.puts('Command not mocked:')
109
- STDERR.puts(' '+cmd.to_s.split("\n").join("\n "))
110
- STDERR.puts(' SHA: ' + Digest::SHA256.hexdigest(cmd.to_s))
106
+ $stderr.puts("Command not mocked:")
107
+ $stderr.puts(" " + cmd.to_s.split("\n").join("\n "))
108
+ $stderr.puts(" SHA: " + Digest::SHA256.hexdigest(cmd.to_s))
111
109
  end
112
110
  # return a non-zero exit code
113
111
  mock_command(cmd, nil, nil, 1)
114
112
  end
115
113
 
116
114
  def file_not_found(path)
117
- STDERR.puts('File not mocked: '+path.to_s) if @options[:verbose]
115
+ $stderr.puts("File not mocked: " + path.to_s) if @options[:verbose]
118
116
  File.new(self, path)
119
117
  end
120
118
 
121
119
  def to_s
122
- 'Mock Connection'
120
+ "Mock Connection"
123
121
  end
124
122
 
125
123
  private
@@ -142,23 +140,23 @@ end
142
140
  class Train::Transports::Mock::Connection
143
141
  class File < Train::File
144
142
  def self.from_json(json)
145
- res = new(json['backend'],
146
- json['path'],
147
- json['follow_symlink'])
148
- res.type = json['type']
143
+ res = new(json["backend"],
144
+ json["path"],
145
+ json["follow_symlink"])
146
+ res.type = json["type"]
149
147
  Train::File::DATA_FIELDS.each do |f|
150
- m = (f.tr('?', '') + '=').to_sym
148
+ m = (f.tr("?", "") + "=").to_sym
151
149
  res.method(m).call(json[f])
152
150
  end
153
151
  res
154
152
  end
155
153
 
156
154
  Train::File::DATA_FIELDS.each do |m|
157
- attr_accessor m.tr('?', '').to_sym
158
- next unless m.include?('?')
155
+ attr_accessor m.tr("?", "").to_sym
156
+ next unless m.include?("?")
159
157
 
160
158
  define_method m.to_sym do
161
- method(m.tr('?', '').to_sym).call
159
+ method(m.tr("?", "").to_sym).call
162
160
  end
163
161
  end
164
162
  attr_accessor :type
@@ -18,9 +18,9 @@
18
18
  # See the License for the specific language governing permissions and
19
19
  # limitations under the License.
20
20
 
21
- require 'net/ssh'
22
- require 'net/scp'
23
- require 'train/errors'
21
+ require "net/ssh"
22
+ require "net/scp"
23
+ require "train/errors"
24
24
 
25
25
  module Train::Transports
26
26
  # Wrapped exception for any internally raised SSH-related errors.
@@ -34,10 +34,10 @@ module Train::Transports
34
34
  #
35
35
  # @author Fletcher Nichol <fnichol@nichol.ca>
36
36
  class SSH < Train.plugin(1) # rubocop:disable Metrics/ClassLength
37
- name 'ssh'
37
+ name "ssh"
38
38
 
39
- require 'train/transports/ssh_connection'
40
- require 'train/transports/cisco_ios_connection'
39
+ require "train/transports/ssh_connection"
40
+ require "train/transports/cisco_ios_connection"
41
41
 
42
42
  # add options for submodules
43
43
  include_options Train::Extras::CommandWrapper
@@ -45,7 +45,7 @@ module Train::Transports
45
45
  # common target configuration
46
46
  option :host, required: true
47
47
  option :port, default: 22, required: true
48
- option :user, default: 'root', required: true
48
+ option :user, default: "root", required: true
49
49
  option :key_files, default: nil
50
50
  option :password, default: nil
51
51
 
@@ -60,7 +60,7 @@ module Train::Transports
60
60
  option :pty, default: false
61
61
  option :proxy_command, default: nil
62
62
  option :bastion_host, default: nil
63
- option :bastion_user, default: 'root'
63
+ option :bastion_user, default: "root"
64
64
  option :bastion_port, default: 22
65
65
  option :non_interactive, default: false
66
66
  option :verify_host_key, default: false
@@ -89,34 +89,34 @@ module Train::Transports
89
89
  super(options)
90
90
 
91
91
  key_files = Array(options[:key_files])
92
- options[:auth_methods] ||= ['none']
92
+ options[:auth_methods] ||= ["none"]
93
93
 
94
94
  unless key_files.empty?
95
- options[:auth_methods].push('publickey')
95
+ options[:auth_methods].push("publickey")
96
96
  options[:keys_only] = true if options[:password].nil?
97
97
  options[:key_files] = key_files
98
98
  end
99
99
 
100
100
  unless options[:password].nil?
101
- options[:auth_methods].push('password', 'keyboard-interactive')
101
+ options[:auth_methods].push("password", "keyboard-interactive")
102
102
  end
103
103
 
104
- if options[:auth_methods] == ['none']
104
+ if options[:auth_methods] == ["none"]
105
105
  if ssh_known_identities.empty?
106
- fail Train::ClientError,
107
- 'Your SSH Agent has no keys added, and you have not specified a password or a key file'
106
+ raise Train::ClientError,
107
+ "Your SSH Agent has no keys added, and you have not specified a password or a key file"
108
108
  else
109
- logger.debug('[SSH] Using Agent keys as no password or key file have been specified')
110
- options[:auth_methods].push('publickey')
109
+ logger.debug("[SSH] Using Agent keys as no password or key file have been specified")
110
+ options[:auth_methods].push("publickey")
111
111
  end
112
112
  end
113
113
 
114
114
  if options[:pty]
115
- logger.warn('[SSH] PTY requested: stderr will be merged into stdout')
115
+ logger.warn("[SSH] PTY requested: stderr will be merged into stdout")
116
116
  end
117
117
 
118
118
  if [options[:proxy_command], options[:bastion_host]].all? { |type| !type.nil? }
119
- fail Train::ClientError, 'Only one of proxy_command or bastion_host needs to be specified'
119
+ raise Train::ClientError, "Only one of proxy_command or bastion_host needs to be specified"
120
120
  end
121
121
 
122
122
  super
@@ -142,30 +142,30 @@ module Train::Transports
142
142
  # @api private
143
143
  def connection_options(opts)
144
144
  connection_options = {
145
- logger: logger,
146
- user_known_hosts_file: '/dev/null',
147
- hostname: opts[:host],
148
- port: opts[:port],
149
- username: opts[:user],
150
- compression: opts[:compression],
151
- compression_level: opts[:compression_level],
152
- keepalive: opts[:keepalive],
153
- keepalive_interval: opts[:keepalive_interval],
154
- timeout: opts[:connection_timeout],
155
- connection_retries: opts[:connection_retries],
145
+ logger: logger,
146
+ user_known_hosts_file: "/dev/null",
147
+ hostname: opts[:host],
148
+ port: opts[:port],
149
+ username: opts[:user],
150
+ compression: opts[:compression],
151
+ compression_level: opts[:compression_level],
152
+ keepalive: opts[:keepalive],
153
+ keepalive_interval: opts[:keepalive_interval],
154
+ timeout: opts[:connection_timeout],
155
+ connection_retries: opts[:connection_retries],
156
156
  connection_retry_sleep: opts[:connection_retry_sleep],
157
- max_wait_until_ready: opts[:max_wait_until_ready],
158
- auth_methods: opts[:auth_methods],
159
- keys_only: opts[:keys_only],
160
- keys: opts[:key_files],
161
- password: opts[:password],
162
- forward_agent: opts[:forward_agent],
163
- proxy_command: opts[:proxy_command],
164
- bastion_host: opts[:bastion_host],
165
- bastion_user: opts[:bastion_user],
166
- bastion_port: opts[:bastion_port],
167
- non_interactive: opts[:non_interactive],
168
- transport_options: opts,
157
+ max_wait_until_ready: opts[:max_wait_until_ready],
158
+ auth_methods: opts[:auth_methods],
159
+ keys_only: opts[:keys_only],
160
+ keys: opts[:key_files],
161
+ password: opts[:password],
162
+ forward_agent: opts[:forward_agent],
163
+ proxy_command: opts[:proxy_command],
164
+ bastion_host: opts[:bastion_host],
165
+ bastion_user: opts[:bastion_user],
166
+ bastion_port: opts[:bastion_port],
167
+ non_interactive: opts[:non_interactive],
168
+ transport_options: opts,
169
169
  }
170
170
  # disable host key verification. The hash key and value to use
171
171
  # depends on the version of net-ssh in use.
@@ -203,20 +203,20 @@ module Train::Transports
203
203
  # 5.0+ style
204
204
  {
205
205
  # It's not a boolean anymore.
206
- 'true' => :always,
207
- 'false' => :never,
206
+ "true" => :always,
207
+ "false" => :never,
208
208
  true => :always,
209
209
  false => :never,
210
210
  # May be correct value, but strings from JSON config
211
- 'always' => :always,
212
- 'never' => :never,
211
+ "always" => :always,
212
+ "never" => :never,
213
213
  nil => :never,
214
214
  }.fetch(given, given)
215
215
  else
216
216
  # up to 4.2 style
217
217
  {
218
- 'true' => true,
219
- 'false' => false,
218
+ "true" => true,
219
+ "false" => false,
220
220
  nil => false,
221
221
  }.fetch(given, given)
222
222
  end
@@ -18,9 +18,9 @@
18
18
  # See the License for the specific language governing permissions and
19
19
  # limitations under the License.
20
20
 
21
- require 'net/ssh'
22
- require 'net/scp'
23
- require 'timeout'
21
+ require "net/ssh"
22
+ require "net/scp"
23
+ require "timeout"
24
24
 
25
25
  class Train::Transports::SSH
26
26
  # A Connection instance can be generated and re-generated, given new
@@ -63,17 +63,17 @@ class Train::Transports::SSH
63
63
  end
64
64
 
65
65
  def ssh_opts
66
- level = logger.debug? ? 'VERBOSE' : 'ERROR'
67
- fwd_agent = options[:forward_agent] ? 'yes' : 'no'
66
+ level = logger.debug? ? "VERBOSE" : "ERROR"
67
+ fwd_agent = options[:forward_agent] ? "yes" : "no"
68
68
 
69
69
  args = %w{ -o UserKnownHostsFile=/dev/null }
70
70
  args += %w{ -o StrictHostKeyChecking=no }
71
71
  args += %w{ -o IdentitiesOnly=yes } if options[:keys]
72
72
  args += %w{ -o BatchMode=yes } if options[:non_interactive]
73
- args += %W( -o LogLevel=#{level} )
74
- args += %W( -o ForwardAgent=#{fwd_agent} ) if options.key?(:forward_agent)
73
+ args += %W{ -o LogLevel=#{level} }
74
+ args += %W{ -o ForwardAgent=#{fwd_agent} } if options.key?(:forward_agent)
75
75
  Array(options[:keys]).each do |ssh_key|
76
- args += %W( -i #{ssh_key} )
76
+ args += %W{ -i #{ssh_key} }
77
77
  end
78
78
  args
79
79
  end
@@ -86,19 +86,19 @@ class Train::Transports::SSH
86
86
  return @proxy_command unless @proxy_command.nil?
87
87
  args = %w{ ssh }
88
88
  args += ssh_opts
89
- args += %W( #{@bastion_user}@#{@bastion_host} )
90
- args += %W( -p #{@bastion_port} )
89
+ args += %W{ #{@bastion_user}@#{@bastion_host} }
90
+ args += %W{ -p #{@bastion_port} }
91
91
  args += %w{ -W %h:%p }
92
- args.join(' ')
92
+ args.join(" ")
93
93
  end
94
94
 
95
95
  # (see Base::Connection#login_command)
96
96
  def login_command
97
97
  args = ssh_opts
98
- args += %W( -o ProxyCommand='#{generate_proxy_command}' ) if check_proxy
99
- args += %W( -p #{@port} )
100
- args += %W( #{@username}@#{@hostname} )
101
- LoginCommand.new('ssh', args)
98
+ args += %W{ -o ProxyCommand='#{generate_proxy_command}' } if check_proxy
99
+ args += %W{ -p #{@port} }
100
+ args += %W{ #{@username}@#{@hostname} }
101
+ LoginCommand.new("ssh", args)
102
102
  end
103
103
 
104
104
  # (see Base::Connection#upload)
@@ -138,7 +138,7 @@ class Train::Transports::SSH
138
138
  retries: @max_wait_until_ready / delay,
139
139
  delay: delay,
140
140
  message: "Waiting for SSH service on #{@hostname}:#{@port}, " \
141
- "retrying in #{delay} seconds",
141
+ "retrying in #{delay} seconds"
142
142
  )
143
143
  run_command(PING_COMMAND.dup)
144
144
  end
@@ -172,14 +172,14 @@ class Train::Transports::SSH
172
172
  def establish_connection(opts)
173
173
  logger.debug("[SSH] opening connection to #{self}")
174
174
  if check_proxy
175
- require 'net/ssh/proxy/command'
175
+ require "net/ssh/proxy/command"
176
176
  @options[:proxy] = Net::SSH::Proxy::Command.new(generate_proxy_command)
177
177
  end
178
178
  Net::SSH.start(@hostname, @username, @options.clone.delete_if { |_key, value| value.nil? })
179
179
  rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH => e
180
180
  if (opts[:retries] -= 1) <= 0
181
181
  logger.warn("[SSH] connection failed, terminating (#{e.inspect})")
182
- raise Train::Transports::SSHFailed, 'SSH session could not be established'
182
+ raise Train::Transports::SSHFailed, "SSH session could not be established"
183
183
  end
184
184
 
185
185
  if opts[:message]
@@ -200,7 +200,7 @@ class Train::Transports::SSH
200
200
  Train::File::Remote::Aix.new(self, path)
201
201
  elsif os.solaris?
202
202
  Train::File::Remote::Unix.new(self, path)
203
- elsif os[:name] == 'qnx'
203
+ elsif os[:name] == "qnx"
204
204
  Train::File::Remote::Qnx.new(self, path)
205
205
  elsif os.windows?
206
206
  Train::File::Remote::Windows.new(self, path)
@@ -210,7 +210,7 @@ class Train::Transports::SSH
210
210
  end
211
211
 
212
212
  def run_command_via_connection(cmd, &data_handler)
213
- cmd.dup.force_encoding('binary') if cmd.respond_to?(:force_encoding)
213
+ cmd.dup.force_encoding("binary") if cmd.respond_to?(:force_encoding)
214
214
  logger.debug("[SSH] #{self} (#{cmd})")
215
215
 
216
216
  reset_session if session.closed?
@@ -228,7 +228,7 @@ class Train::Transports::SSH
228
228
  # transport. This retries the command if this is the case.
229
229
  # See:
230
230
  # https://github.com/inspec/train/pull/271
231
- logger.debug('[SSH] Possible Cisco IOS race condition, retrying command')
231
+ logger.debug("[SSH] Possible Cisco IOS race condition, retrying command")
232
232
 
233
233
  # Only attempt retry up to 5 times to avoid infinite loop
234
234
  @ios_cmd_retries += 1
@@ -246,7 +246,7 @@ class Train::Transports::SSH
246
246
  def session(retry_options = {})
247
247
  @session ||= establish_connection({
248
248
  retries: @connection_retries.to_i,
249
- delay: @connection_retry_sleep.to_i,
249
+ delay: @connection_retry_sleep.to_i,
250
250
  }.merge(retry_options))
251
251
  end
252
252
 
@@ -260,7 +260,7 @@ class Train::Transports::SSH
260
260
  # @api private
261
261
  def to_s
262
262
  options_to_print = @options.clone
263
- options_to_print[:password] = '<hidden>' if options_to_print.key?(:password)
263
+ options_to_print[:password] = "<hidden>" if options_to_print.key?(:password)
264
264
  "#{@username}@#{@hostname}<#{options_to_print.inspect}>"
265
265
  end
266
266
 
@@ -274,7 +274,7 @@ class Train::Transports::SSH
274
274
  #
275
275
  # @api private
276
276
  def execute_on_channel(cmd, &data_handler)
277
- stdout = stderr = ''
277
+ stdout = stderr = ""
278
278
  exit_status = nil
279
279
  session.open_channel do |channel|
280
280
  # wrap commands if that is configured
@@ -282,11 +282,11 @@ class Train::Transports::SSH
282
282
 
283
283
  if @transport_options[:pty]
284
284
  channel.request_pty do |_ch, success|
285
- fail Train::Transports::SSHPTYFailed, 'Requesting PTY failed' unless success
285
+ raise Train::Transports::SSHPTYFailed, "Requesting PTY failed" unless success
286
286
  end
287
287
  end
288
288
  channel.exec(cmd) do |_, success|
289
- abort 'Couldn\'t execute command on SSH.' unless success
289
+ abort "Couldn't execute command on SSH." unless success
290
290
  channel.on_data do |_, data|
291
291
  yield(data) unless data_handler.nil?
292
292
  stdout += data
@@ -297,11 +297,11 @@ class Train::Transports::SSH
297
297
  stderr += data
298
298
  end
299
299
 
300
- channel.on_request('exit-status') do |_, data|
300
+ channel.on_request("exit-status") do |_, data|
301
301
  exit_status = data.read_long
302
302
  end
303
303
 
304
- channel.on_request('exit-signal') do |_, data|
304
+ channel.on_request("exit-signal") do |_, data|
305
305
  exit_status = data.read_long
306
306
  end
307
307
  end