train-core 2.1.7 → 2.1.13

Sign up to get free protection for your applications and to get access to all the features.
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,8 +1,6 @@
1
- # encoding: utf-8
2
-
3
- require 'digest/sha1'
4
- require 'securerandom'
5
- require 'json'
1
+ require "digest/sha1"
2
+ require "securerandom"
3
+ require "json"
6
4
 
7
5
  module Train::Platforms::Detect
8
6
  class UUID
@@ -24,10 +22,10 @@ module Train::Platforms::Detect
24
22
  else
25
23
  if @platform[:uuid_command]
26
24
  result = @backend.run_command(@platform[:uuid_command])
27
- return uuid_from_string(result.stdout.chomp) if result.exit_status.zero? && !result.stdout.empty?
25
+ return uuid_from_string(result.stdout.chomp) if result.exit_status == 0 && !result.stdout.empty?
28
26
  end
29
27
 
30
- raise 'Could not find platform uuid! Please set a uuid_command for your platform.'
28
+ raise "Could not find platform uuid! Please set a uuid_command for your platform."
31
29
  end
32
30
  end
33
31
  end
@@ -44,7 +44,7 @@ module Train::Platforms
44
44
  @cleaned_name = nil if force
45
45
  @cleaned_name ||= begin
46
46
  name = (@platform[:name] || @name)
47
- name.downcase!.tr!(' ', '_') if name =~ /[A-Z ]/
47
+ name.downcase!.tr!(" ", "_") if name =~ /[A-Z ]/
48
48
  name
49
49
  end
50
50
  end
@@ -59,7 +59,7 @@ module Train::Platforms
59
59
  if respond_to?(name)
60
60
  send(name)
61
61
  else
62
- 'unknown'
62
+ "unknown"
63
63
  end
64
64
  end
65
65
 
@@ -73,6 +73,10 @@ module Train::Platforms
73
73
  @platform
74
74
  end
75
75
 
76
+ def cisco_ios? # TODO: kinda a hack. needed to prevent tests from corrupting.
77
+ false
78
+ end
79
+
76
80
  # Add generic family? and platform methods to an existing platform
77
81
  #
78
82
  # This is done later to add any custom
@@ -84,8 +88,8 @@ module Train::Platforms
84
88
  # Add in family methods
85
89
  family_list = Train::Platforms.families
86
90
  family_list.each_value do |k|
87
- next if respond_to?(k.name + '?')
88
- define_singleton_method(k.name + '?') do
91
+ next if respond_to?(k.name + "?")
92
+ define_singleton_method(k.name + "?") do
89
93
  family_hierarchy.include?(k.name)
90
94
  end
91
95
  end
@@ -99,7 +103,7 @@ module Train::Platforms
99
103
  end
100
104
 
101
105
  # Create method for name if its not already true
102
- m = name + '?'
106
+ m = name + "?"
103
107
  return if respond_to?(m)
104
108
  define_singleton_method(m) do
105
109
  true
@@ -3,27 +3,27 @@
3
3
 
4
4
  # Load Train. We certainly need the plugin system, and also several other parts
5
5
  # that are tightly coupled. Train itself is fairly light, and non-invasive.
6
- require 'train'
6
+ require "train"
7
7
 
8
8
  # You can select from a number of test harnesses. Since Train is closely related
9
9
  # to InSpec, and InSpec uses Spec-style controls in profile code, you will
10
10
  # probably want to use something like minitest/spec, which provides Spec-style
11
11
  # tests.
12
- require 'minitest/spec'
13
- require 'minitest/autorun'
12
+ require "minitest/spec"
13
+ require "minitest/autorun"
14
14
 
15
15
  # Data formats commonly used in testing
16
- require 'json'
17
- require 'ostruct'
16
+ require "json"
17
+ require "ostruct"
18
18
 
19
19
  # Utilities often needed
20
- require 'fileutils'
21
- require 'tmpdir'
22
- require 'pathname'
20
+ require "fileutils"
21
+ require "tmpdir"
22
+ require "pathname"
23
23
 
24
24
  # You might want to put some debugging tools here. We run tests to find bugs,
25
25
  # after all.
26
- require 'byebug'
26
+ require "byebug"
27
27
 
28
28
  # Configure MiniTest to expose things like `let`
29
29
  class Module
@@ -38,11 +38,11 @@ module TrainPluginBaseHelper
38
38
  plugin_test_helper_path = Pathname.new(caller_locations(4, 1).first.absolute_path)
39
39
  plugin_src_root = plugin_test_helper_path.parent.parent
40
40
  base.let(:plugin_src_path) { plugin_src_root }
41
- base.let(:plugin_fixtures_path) { File.join(plugin_src_root, 'test', 'fixtures') }
41
+ base.let(:plugin_fixtures_path) { File.join(plugin_src_root, "test", "fixtures") }
42
42
  end
43
43
 
44
- let(:train_src_path) { File.expand_path(File.join(__FILE__, '..', '..')) }
45
- let(:train_fixtures_path) { File.join(train_src_path, 'test', 'fixtures') }
44
+ let(:train_src_path) { File.expand_path(File.join(__FILE__, "..", "..")) }
45
+ let(:train_fixtures_path) { File.join(train_src_path, "test", "fixtures") }
46
46
  let(:registry) { Train::Plugins.registry }
47
47
  end
48
48
 
data/lib/train/plugins.rb CHANGED
@@ -3,11 +3,11 @@
3
3
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
4
  # Author:: Christoph Hartmann (<chris@lollyrock.com>)
5
5
 
6
- require 'train/errors'
6
+ require "train/errors"
7
7
 
8
8
  module Train
9
9
  class Plugins
10
- require 'train/plugins/transport'
10
+ require "train/plugins/transport"
11
11
 
12
12
  class << self
13
13
  # Retrieve the current plugin registry, containing all plugin names
@@ -30,10 +30,10 @@ module Train
30
30
  # @return [Transport] the versioned transport base class
31
31
  def self.plugin(version = 1)
32
32
  if version != 1
33
- fail ClientError,
34
- 'Only understand train plugin version 1. You are trying to '\
33
+ raise ClientError,
34
+ "Only understand train plugin version 1. You are trying to "\
35
35
  "initialize a train plugin #{version}, which is not supported "\
36
- 'in the current release of train.'
36
+ "in the current release of train."
37
37
  end
38
38
  ::Train::Plugins::Transport
39
39
  end
@@ -1,9 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'train/errors'
4
- require 'train/extras'
5
- require 'train/file'
6
- require 'logger'
3
+ require "train/errors"
4
+ require "train/extras"
5
+ require "train/file"
6
+ require "logger"
7
7
 
8
8
  class Train::Plugins::Transport
9
9
  # A Connection instance can be generated and re-generated, given new
@@ -21,7 +21,7 @@ class Train::Plugins::Transport
21
21
  # @yield [self] yields itself for block-style invocation
22
22
  def initialize(options = nil)
23
23
  @options = options || {}
24
- @logger = @options.delete(:logger) || Logger.new(STDOUT)
24
+ @logger = @options.delete(:logger) || Logger.new($stdout, level: :fatal)
25
25
  Train::Platforms::Detect::Specifications::OS.load
26
26
  Train::Platforms::Detect::Specifications::Api.load
27
27
 
@@ -64,12 +64,12 @@ class Train::Plugins::Transport
64
64
  # Enable caching types for Train. Currently we support
65
65
  # :api_call, :file and :command types
66
66
  def enable_cache(type)
67
- fail Train::UnknownCacheType, "#{type} is not a valid cache type" unless @cache_enabled.keys.include?(type.to_sym)
67
+ raise Train::UnknownCacheType, "#{type} is not a valid cache type" unless @cache_enabled.keys.include?(type.to_sym)
68
68
  @cache_enabled[type.to_sym] = true
69
69
  end
70
70
 
71
71
  def disable_cache(type)
72
- fail Train::UnknownCacheType, "#{type} is not a valid cache type" unless @cache_enabled.keys.include?(type.to_sym)
72
+ raise Train::UnknownCacheType, "#{type} is not a valid cache type" unless @cache_enabled.keys.include?(type.to_sym)
73
73
  @cache_enabled[type.to_sym] = false
74
74
  clear_cache(type.to_sym)
75
75
  end
@@ -81,13 +81,13 @@ class Train::Plugins::Transport
81
81
 
82
82
  def to_json
83
83
  {
84
- 'files' => Hash[@cache[:file].map { |x, y| [x, y.to_json] }],
84
+ "files" => Hash[@cache[:file].map { |x, y| [x, y.to_json] }],
85
85
  }
86
86
  end
87
87
 
88
88
  def load_json(j)
89
- require 'train/transports/mock'
90
- j['files'].each do |path, jf|
89
+ require "train/transports/mock"
90
+ j["files"].each do |path, jf|
91
91
  @cache[:file][path] = Train::Transports::Mock::Connection::File.from_json(jf)
92
92
  end
93
93
  end
@@ -137,7 +137,7 @@ class Train::Plugins::Transport
137
137
  #
138
138
  # @return [LoginCommand] array of command line tokens
139
139
  def login_command
140
- fail NotImplementedError, "#{self.class} does not implement #login_command()"
140
+ raise NotImplementedError, "#{self.class} does not implement #login_command()"
141
141
  end
142
142
 
143
143
  # Block and return only when the remote host is prepared and ready to
@@ -161,7 +161,7 @@ class Train::Plugins::Transport
161
161
  #
162
162
  # @return [CommandResult] contains the result of running the command
163
163
  def run_command_via_connection(_command, &_data_handler)
164
- fail NotImplementedError, "#{self.class} does not implement #run_command_via_connection()"
164
+ raise NotImplementedError, "#{self.class} does not implement #run_command_via_connection()"
165
165
  end
166
166
 
167
167
  # Interact with files on the target. Read, write, and get metadata
@@ -170,7 +170,7 @@ class Train::Plugins::Transport
170
170
  # @param [String] path which is being inspected
171
171
  # @return [FileCommon] file object that allows for interaction
172
172
  def file_via_connection(_path, *_args)
173
- fail NotImplementedError, "#{self.class} does not implement #file_via_connection(...)"
173
+ raise NotImplementedError, "#{self.class} does not implement #file_via_connection(...)"
174
174
  end
175
175
 
176
176
  def clear_cache(type)
@@ -3,17 +3,17 @@
3
3
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
4
  # Author:: Christoph Hartmann (<chris@lollyrock.com>)
5
5
 
6
- require 'logger'
7
- require 'train/errors'
8
- require 'train/extras'
9
- require 'train/options'
6
+ require "logger"
7
+ require "train/errors"
8
+ require "train/extras"
9
+ require "train/options"
10
10
 
11
11
  class Train::Plugins
12
12
  class Transport
13
13
  include Train::Extras
14
14
  Train::Options.attach(self)
15
15
 
16
- require 'train/plugins/base_connection'
16
+ require "train/plugins/base_connection"
17
17
 
18
18
  # Initialize a new Transport object
19
19
  #
@@ -21,7 +21,7 @@ class Train::Plugins
21
21
  # @return [Transport] the transport object
22
22
  def initialize(options = {})
23
23
  @options = merge_options({}, options || {})
24
- @logger = @options[:logger] || Logger.new(STDOUT)
24
+ @logger = @options[:logger] || Logger.new($stdout, level: :fatal)
25
25
  end
26
26
 
27
27
  # Create a connection to the target. Options may be provided
@@ -30,7 +30,7 @@ class Train::Plugins
30
30
  # @param [Hash] _options = nil provide optional configuration params
31
31
  # @return [Connection] the connection for this configuration
32
32
  def connection(_options = nil)
33
- fail Train::ClientError, "#{self.class} does not implement #connection()"
33
+ raise Train::ClientError, "#{self.class} does not implement #connection()"
34
34
  end
35
35
 
36
36
  # Register the inheriting class with as a train plugin using the
@@ -24,8 +24,8 @@ class Train::Transports::SSH
24
24
  end
25
25
 
26
26
  def unique_identifier
27
- result = run_command_via_connection('show version | include Processor')
28
- result.stdout.split(' ')[-1]
27
+ result = run_command_via_connection("show version | include Processor")
28
+ result.stdout.split(" ")[-1]
29
29
  end
30
30
 
31
31
  private
@@ -45,26 +45,26 @@ class Train::Transports::SSH
45
45
  if @enable_password
46
46
  # This verifies we are not in privileged exec mode before running the
47
47
  # enable command. Otherwise, the password will be in history.
48
- if run_command_via_connection('show privilege').stdout.split[-1] != '15'
48
+ if run_command_via_connection("show privilege").stdout.split[-1] != "15"
49
49
  # Extra newlines to get back to prompt if incorrect password is used
50
50
  run_command_via_connection("enable\n#{@enable_password}\n\n\n")
51
51
  end
52
52
  end
53
53
 
54
54
  # Prevent `--MORE--` by removing terminal length limit
55
- run_command_via_connection('terminal length 0')
55
+ run_command_via_connection("terminal length 0")
56
56
 
57
57
  @session
58
58
  end
59
59
 
60
60
  def run_command_via_connection(cmd, &_data_handler)
61
61
  # Ensure buffer is empty before sending data
62
- @buf = ''
62
+ @buf = ""
63
63
 
64
64
  logger.debug("[SSH] Running `#{cmd}` on #{self}")
65
65
  session.send_data(cmd + "\r\n")
66
66
 
67
- logger.debug('[SSH] waiting for prompt')
67
+ logger.debug("[SSH] waiting for prompt")
68
68
  until @buf =~ @prompt
69
69
  if @buf =~ /Bad (secrets|password)|Access denied/
70
70
  raise BadEnablePassword
@@ -74,16 +74,16 @@ class Train::Transports::SSH
74
74
 
75
75
  # Save the buffer and clear it for the next command
76
76
  output = @buf.dup
77
- @buf = ''
77
+ @buf = ""
78
78
 
79
79
  format_result(format_output(output, cmd))
80
80
  end
81
81
 
82
82
  ERROR_MATCHERS = [
83
- 'Bad IP address',
84
- 'Incomplete command',
85
- 'Invalid input detected',
86
- 'Unrecognized host',
83
+ "Bad IP address",
84
+ "Incomplete command",
85
+ "Invalid input detected",
86
+ "Unrecognized host",
87
87
  ].freeze
88
88
 
89
89
  # IOS commands do not have an exit code so we must compare the command
@@ -92,9 +92,9 @@ class Train::Transports::SSH
92
92
  # result.
93
93
  def format_result(result)
94
94
  if ERROR_MATCHERS.none? { |e| result.include?(e) }
95
- CommandResult.new(result, '', 0)
95
+ CommandResult.new(result, "", 0)
96
96
  else
97
- CommandResult.new('', result, 1)
97
+ CommandResult.new("", result, 1)
98
98
  end
99
99
  end
100
100
 
@@ -107,10 +107,10 @@ class Train::Transports::SSH
107
107
  trailing_line_endings = /(\r\n)+$/
108
108
 
109
109
  output
110
- .sub(leading_prompt, '')
111
- .sub(command_string, '')
112
- .gsub(trailing_prompt, '')
113
- .gsub(trailing_line_endings, '')
110
+ .sub(leading_prompt, "")
111
+ .sub(command_string, "")
112
+ .gsub(trailing_prompt, "")
113
+ .gsub(trailing_line_endings, "")
114
114
  end
115
115
 
116
116
  # Create an SSH channel that writes to @buf when data is received
@@ -121,9 +121,9 @@ class Train::Transports::SSH
121
121
  @buf += data
122
122
  end
123
123
 
124
- ch.send_channel_request('shell') do |_, success|
125
- raise 'Failed to open SSH shell' unless success
126
- logger.debug('[SSH] shell opened')
124
+ ch.send_channel_request("shell") do |_, success|
125
+ raise "Failed to open SSH shell" unless success
126
+ logger.debug("[SSH] shell opened")
127
127
  end
128
128
  end
129
129
  end
@@ -3,13 +3,13 @@
3
3
  # author: Dominik Richter
4
4
  # author: Christoph Hartmann
5
5
 
6
- require 'train/plugins'
7
- require 'train/errors'
8
- require 'mixlib/shellout'
6
+ require "train/plugins"
7
+ require "train/errors"
8
+ require "mixlib/shellout"
9
9
 
10
10
  module Train::Transports
11
11
  class Local < Train.plugin(1)
12
- name 'local'
12
+ name "local"
13
13
 
14
14
  class PipeError < Train::TransportError; end
15
15
 
@@ -33,7 +33,7 @@ module Train::Transports
33
33
  end
34
34
 
35
35
  def uri
36
- 'local://'
36
+ "local://"
37
37
  end
38
38
 
39
39
  private
@@ -41,10 +41,10 @@ module Train::Transports
41
41
  def select_runner(options)
42
42
  if os.windows?
43
43
  # Force a 64 bit poweshell if needed
44
- if RUBY_PLATFORM == 'i386-mingw32' && os.arch == 'x86_64'
44
+ if RUBY_PLATFORM == "i386-mingw32" && os.arch == "x86_64"
45
45
  powershell_cmd = "#{ENV['SystemRoot']}\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe"
46
46
  else
47
- powershell_cmd = 'powershell'
47
+ powershell_cmd = "powershell"
48
48
  end
49
49
 
50
50
  # Attempt to use a named pipe but fallback to ShellOut if that fails
@@ -67,7 +67,7 @@ module Train::Transports
67
67
  when :windows_shell
68
68
  WindowsShellRunner.new
69
69
  else
70
- fail "Runner type `#{command_runner}` not supported"
70
+ raise "Runner type `#{command_runner}` not supported"
71
71
  end
72
72
  end
73
73
 
@@ -82,7 +82,7 @@ module Train::Transports
82
82
  res.run_command
83
83
  Local::CommandResult.new(res.stdout, res.stderr, res.exitstatus)
84
84
  rescue Errno::ENOENT => _
85
- CommandResult.new('', '', 1)
85
+ CommandResult.new("", "", 1)
86
86
  end
87
87
 
88
88
  def file_via_connection(path)
@@ -112,10 +112,10 @@ module Train::Transports
112
112
  end
113
113
 
114
114
  class WindowsShellRunner
115
- require 'json'
116
- require 'base64'
115
+ require "json"
116
+ require "base64"
117
117
 
118
- def initialize(powershell_cmd = 'powershell')
118
+ def initialize(powershell_cmd = "powershell")
119
119
  @powershell_cmd = powershell_cmd
120
120
  end
121
121
 
@@ -124,7 +124,7 @@ module Train::Transports
124
124
  script = "$ProgressPreference='SilentlyContinue';" + script
125
125
 
126
126
  # Encode script so PowerShell can use it
127
- script = script.encode('UTF-16LE', 'UTF-8')
127
+ script = script.encode("UTF-16LE", "UTF-8")
128
128
  base64_script = Base64.strict_encode64(script)
129
129
 
130
130
  cmd = "#{@powershell_cmd} -NoProfile -EncodedCommand #{base64_script}"
@@ -136,14 +136,14 @@ module Train::Transports
136
136
  end
137
137
 
138
138
  class WindowsPipeRunner
139
- require 'json'
140
- require 'base64'
141
- require 'securerandom'
139
+ require "json"
140
+ require "base64"
141
+ require "securerandom"
142
142
 
143
- def initialize(powershell_cmd = 'powershell')
143
+ def initialize(powershell_cmd = "powershell")
144
144
  @powershell_cmd = powershell_cmd
145
145
  @pipe = acquire_pipe
146
- fail PipeError if @pipe.nil?
146
+ raise PipeError if @pipe.nil?
147
147
  end
148
148
 
149
149
  def run_command(cmd)
@@ -167,7 +167,7 @@ module Train::Transports
167
167
  # PowerShell needs time to create pipe.
168
168
  100.times do
169
169
  begin
170
- pipe = open("//./pipe/#{pipe_name}", 'r+')
170
+ pipe = open("//./pipe/#{pipe_name}", "r+")
171
171
  break
172
172
  rescue
173
173
  sleep 0.1
@@ -178,7 +178,7 @@ module Train::Transports
178
178
  end
179
179
 
180
180
  def start_pipe_server(pipe_name)
181
- require 'win32/process'
181
+ require "win32/process"
182
182
 
183
183
  script = <<-EOF
184
184
  $ErrorActionPreference = 'Stop'
@@ -213,14 +213,14 @@ module Train::Transports
213
213
  }
214
214
  EOF
215
215
 
216
- utf8_script = script.encode('UTF-16LE', 'UTF-8')
216
+ utf8_script = script.encode("UTF-16LE", "UTF-8")
217
217
  base64_script = Base64.strict_encode64(utf8_script)
218
218
  cmd = "#{@powershell_cmd} -NoProfile -ExecutionPolicy bypass -NonInteractive -EncodedCommand #{base64_script}"
219
219
 
220
220
  server_pid = Process.create(command_line: cmd).process_id
221
221
 
222
222
  # Ensure process is killed when the Train process exits
223
- at_exit { Process.kill('KILL', server_pid) }
223
+ at_exit { Process.kill("KILL", server_pid) }
224
224
  end
225
225
  end
226
226
  end