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,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