train 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 (50) 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/azure.rb +23 -23
  34. data/lib/train/transports/cisco_ios_connection.rb +20 -20
  35. data/lib/train/transports/clients/azure/graph_rbac.rb +2 -2
  36. data/lib/train/transports/clients/azure/vault.rb +4 -4
  37. data/lib/train/transports/docker.rb +4 -10
  38. data/lib/train/transports/gcp.rb +23 -23
  39. data/lib/train/transports/helpers/azure/file_credentials.rb +8 -8
  40. data/lib/train/transports/helpers/azure/file_parser.rb +1 -1
  41. data/lib/train/transports/helpers/azure/subscription_number_file_parser.rb +1 -1
  42. data/lib/train/transports/local.rb +22 -22
  43. data/lib/train/transports/mock.rb +33 -35
  44. data/lib/train/transports/ssh.rb +47 -47
  45. data/lib/train/transports/ssh_connection.rb +28 -28
  46. data/lib/train/transports/vmware.rb +32 -34
  47. data/lib/train/transports/winrm.rb +37 -37
  48. data/lib/train/transports/winrm_connection.rb +12 -12
  49. data/lib/train/version.rb +1 -1
  50. 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
@@ -1,27 +1,27 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'train/plugins'
4
- require 'ms_rest_azure'
5
- require 'azure_mgmt_resources'
6
- require 'azure_graph_rbac'
7
- require 'azure_mgmt_key_vault'
8
- require 'socket'
9
- require 'timeout'
10
- require 'train/transports/helpers/azure/file_credentials'
11
- require 'train/transports/clients/azure/graph_rbac'
12
- require 'train/transports/clients/azure/vault'
3
+ require "train/plugins"
4
+ require "ms_rest_azure"
5
+ require "azure_mgmt_resources"
6
+ require "azure_graph_rbac"
7
+ require "azure_mgmt_key_vault"
8
+ require "socket"
9
+ require "timeout"
10
+ require "train/transports/helpers/azure/file_credentials"
11
+ require "train/transports/clients/azure/graph_rbac"
12
+ require "train/transports/clients/azure/vault"
13
13
 
14
14
  module Train::Transports
15
15
  class Azure < Train.plugin(1)
16
- name 'azure'
17
- option :tenant_id, default: ENV['AZURE_TENANT_ID']
18
- option :client_id, default: ENV['AZURE_CLIENT_ID']
19
- option :client_secret, default: ENV['AZURE_CLIENT_SECRET']
20
- option :subscription_id, default: ENV['AZURE_SUBSCRIPTION_ID']
21
- option :msi_port, default: ENV['AZURE_MSI_PORT'] || '50342'
16
+ name "azure"
17
+ option :tenant_id, default: ENV["AZURE_TENANT_ID"]
18
+ option :client_id, default: ENV["AZURE_CLIENT_ID"]
19
+ option :client_secret, default: ENV["AZURE_CLIENT_SECRET"]
20
+ option :subscription_id, default: ENV["AZURE_SUBSCRIPTION_ID"]
21
+ option :msi_port, default: ENV["AZURE_MSI_PORT"] || "50342"
22
22
 
23
23
  # This can provide the client id and secret
24
- option :credentials_file, default: ENV['AZURE_CRED_FILE']
24
+ option :credentials_file, default: ENV["AZURE_CRED_FILE"]
25
25
 
26
26
  def connection(_ = nil)
27
27
  @connection ||= Connection.new(@options)
@@ -30,7 +30,7 @@ module Train::Transports
30
30
  class Connection < BaseConnection
31
31
  attr_reader :options
32
32
 
33
- DEFAULT_FILE = ::File.join(Dir.home, '.azure', 'credentials')
33
+ DEFAULT_FILE = ::File.join(Dir.home, ".azure", "credentials")
34
34
 
35
35
  def initialize(options)
36
36
  @apis = {}
@@ -51,14 +51,14 @@ module Train::Transports
51
51
  @options[:msi_port] = @options[:msi_port].to_i unless @options[:msi_port].nil?
52
52
 
53
53
  # additional platform details
54
- release = Gem.loaded_specs['azure_mgmt_resources'].version
54
+ release = Gem.loaded_specs["azure_mgmt_resources"].version
55
55
  @platform_details = { release: "azure_mgmt_resources-v#{release}" }
56
56
 
57
57
  connect
58
58
  end
59
59
 
60
60
  def platform
61
- force_platform!('azure', @platform_details)
61
+ force_platform!("azure", @platform_details)
62
62
  end
63
63
 
64
64
  def azure_client(klass = ::Azure::Resources::Profiles::Latest::Mgmt::Client, opts = {})
@@ -85,13 +85,13 @@ module Train::Transports
85
85
  def connect
86
86
  if msi_auth?
87
87
  # this needs set for azure cloud to authenticate
88
- ENV['MSI_VM'] = 'true'
88
+ ENV["MSI_VM"] = "true"
89
89
  provider = ::MsRestAzure::MSITokenProvider.new(@options[:msi_port])
90
90
  else
91
91
  provider = ::MsRestAzure::ApplicationTokenProvider.new(
92
92
  @options[:tenant_id],
93
93
  @options[:client_id],
94
- @options[:client_secret],
94
+ @options[:client_secret]
95
95
  )
96
96
  end
97
97
 
@@ -166,7 +166,7 @@ module Train::Transports
166
166
  def port_open?(port, seconds = 3)
167
167
  Timeout.timeout(seconds) do
168
168
  begin
169
- TCPSocket.new('localhost', port).close
169
+ TCPSocket.new("localhost", port).close
170
170
  true
171
171
  rescue SystemCallError
172
172
  false
@@ -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