train 0.29.2 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -2
  3. data/lib/train.rb +1 -0
  4. data/lib/train/errors.rb +6 -0
  5. data/lib/train/extras.rb +0 -1
  6. data/lib/train/platforms.rb +82 -0
  7. data/lib/train/platforms/common.rb +34 -0
  8. data/lib/train/platforms/detect.rb +12 -0
  9. data/lib/train/platforms/detect/helpers/os_common.rb +56 -0
  10. data/lib/train/platforms/detect/helpers/os_linux.rb +75 -0
  11. data/lib/train/{extras/os_detect_windows.rb → platforms/detect/helpers/os_windows.rb} +3 -10
  12. data/lib/train/platforms/detect/scanner.rb +84 -0
  13. data/lib/train/platforms/detect/specifications/os.rb +480 -0
  14. data/lib/train/platforms/family.rb +26 -0
  15. data/lib/train/platforms/platform.rb +80 -0
  16. data/lib/train/plugins/base_connection.rb +75 -27
  17. data/lib/train/transports/docker.rb +17 -28
  18. data/lib/train/transports/local.rb +21 -22
  19. data/lib/train/transports/mock.rb +44 -30
  20. data/lib/train/transports/ssh_connection.rb +55 -67
  21. data/lib/train/transports/winrm_connection.rb +16 -26
  22. data/lib/train/version.rb +1 -1
  23. data/test/unit/file/remote/linux_test.rb +2 -2
  24. data/test/unit/platforms/detect/os_common_test.rb +85 -0
  25. data/test/unit/platforms/detect/os_linux_test.rb +124 -0
  26. data/test/unit/{extras/os_detect_windows_test.rb → platforms/detect/os_windows_test.rb} +5 -2
  27. data/test/unit/platforms/detect/scanner_test.rb +61 -0
  28. data/test/unit/platforms/family_test.rb +32 -0
  29. data/test/unit/platforms/os_detect_test.rb +175 -0
  30. data/test/unit/{extras/os_common_test.rb → platforms/platform_test.rb} +103 -18
  31. data/test/unit/platforms/platforms_test.rb +42 -0
  32. data/test/unit/plugins/connection_test.rb +106 -8
  33. data/test/unit/transports/local_test.rb +20 -15
  34. data/test/unit/transports/mock_test.rb +16 -6
  35. data/test/unit/transports/ssh_test.rb +17 -15
  36. metadata +28 -19
  37. data/lib/train/extras/linux_lsb.rb +0 -60
  38. data/lib/train/extras/os_common.rb +0 -151
  39. data/lib/train/extras/os_detect_arista_eos.rb +0 -34
  40. data/lib/train/extras/os_detect_darwin.rb +0 -40
  41. data/lib/train/extras/os_detect_esx.rb +0 -22
  42. data/lib/train/extras/os_detect_linux.rb +0 -164
  43. data/lib/train/extras/os_detect_openvms.rb +0 -29
  44. data/lib/train/extras/os_detect_unix.rb +0 -106
  45. data/lib/train/extras/uname.rb +0 -28
  46. data/lib/train/transports/local_os.rb +0 -51
  47. data/test/unit/extras/os_detect_linux_test.rb +0 -230
@@ -55,67 +55,6 @@ class Train::Transports::SSH
55
55
  @session = nil
56
56
  end
57
57
 
58
- def os
59
- @os ||= OS.new(self)
60
- end
61
-
62
- def file(path)
63
- @files[path] ||= \
64
- if os.aix?
65
- Train::File::Remote::Aix.new(self, path)
66
- elsif os.solaris?
67
- Train::File::Remote::Unix.new(self, path)
68
- elsif os[:name] == 'qnx'
69
- Train::File::Remote::Qnx.new(self, path)
70
- else
71
- Train::File::Remote::Linux.new(self, path)
72
- end
73
- end
74
-
75
- # (see Base::Connection#run_command)
76
- def run_command(cmd)
77
- stdout = stderr = ''
78
- exit_status = nil
79
- cmd.dup.force_encoding('binary') if cmd.respond_to?(:force_encoding)
80
- logger.debug("[SSH] #{self} (#{cmd})")
81
-
82
- session.open_channel do |channel|
83
- # wrap commands if that is configured
84
- cmd = @cmd_wrapper.run(cmd) unless @cmd_wrapper.nil?
85
-
86
- if @transport_options[:pty]
87
- channel.request_pty do |_ch, success|
88
- fail Train::Transports::SSHPTYFailed, 'Requesting PTY failed' unless success
89
- end
90
- end
91
-
92
- channel.exec(cmd) do |_, success|
93
- abort 'Couldn\'t execute command on SSH.' unless success
94
-
95
- channel.on_data do |_, data|
96
- stdout += data
97
- end
98
-
99
- channel.on_extended_data do |_, _type, data|
100
- stderr += data
101
- end
102
-
103
- channel.on_request('exit-status') do |_, data|
104
- exit_status = data.read_long
105
- end
106
-
107
- channel.on_request('exit-signal') do |_, data|
108
- exit_status = data.read_long
109
- end
110
- end
111
- end
112
- @session.loop
113
-
114
- CommandResult.new(stdout, stderr, exit_status)
115
- rescue Net::SSH::Exception => ex
116
- raise Train::Transports::SSHFailed, "SSH command failed (#{ex.message})"
117
- end
118
-
119
58
  # (see Base::Connection#login_command)
120
59
  def login_command
121
60
  level = logger.debug? ? 'VERBOSE' : 'ERROR'
@@ -225,6 +164,61 @@ class Train::Transports::SSH
225
164
  retry
226
165
  end
227
166
 
167
+ def file_via_connection(path)
168
+ if os.aix?
169
+ Train::File::Remote::Aix.new(self, path)
170
+ elsif os.solaris?
171
+ Train::File::Remote::Unix.new(self, path)
172
+ elsif os[:name] == 'qnx'
173
+ Train::File::Remote::Qnx.new(self, path)
174
+ else
175
+ Train::File::Remote::Linux.new(self, path)
176
+ end
177
+ end
178
+
179
+ def run_command_via_connection(cmd)
180
+ stdout = stderr = ''
181
+ exit_status = nil
182
+ cmd.dup.force_encoding('binary') if cmd.respond_to?(:force_encoding)
183
+ logger.debug("[SSH] #{self} (#{cmd})")
184
+
185
+ session.open_channel do |channel|
186
+ # wrap commands if that is configured
187
+ cmd = @cmd_wrapper.run(cmd) unless @cmd_wrapper.nil?
188
+
189
+ if @transport_options[:pty]
190
+ channel.request_pty do |_ch, success|
191
+ fail Train::Transports::SSHPTYFailed, 'Requesting PTY failed' unless success
192
+ end
193
+ end
194
+
195
+ channel.exec(cmd) do |_, success|
196
+ abort 'Couldn\'t execute command on SSH.' unless success
197
+
198
+ channel.on_data do |_, data|
199
+ stdout += data
200
+ end
201
+
202
+ channel.on_extended_data do |_, _type, data|
203
+ stderr += data
204
+ end
205
+
206
+ channel.on_request('exit-status') do |_, data|
207
+ exit_status = data.read_long
208
+ end
209
+
210
+ channel.on_request('exit-signal') do |_, data|
211
+ exit_status = data.read_long
212
+ end
213
+ end
214
+ end
215
+ @session.loop
216
+
217
+ CommandResult.new(stdout, stderr, exit_status)
218
+ rescue Net::SSH::Exception => ex
219
+ raise Train::Transports::SSHFailed, "SSH command failed (#{ex.message})"
220
+ end
221
+
228
222
  # Returns a connection session, or establishes one when invoked the
229
223
  # first time.
230
224
  #
@@ -247,11 +241,5 @@ class Train::Transports::SSH
247
241
  options_to_print[:password] = '<hidden>' if options_to_print.key?(:password)
248
242
  "#{@username}@#{@hostname}<#{options_to_print.inspect}>"
249
243
  end
250
-
251
- class OS < OSCommon
252
- def initialize(backend)
253
- super(backend)
254
- end
255
- end
256
244
  end
257
245
  end
@@ -46,26 +46,6 @@ class Train::Transports::WinRM
46
46
  @session = nil
47
47
  end
48
48
 
49
- def os
50
- @os ||= OS.new(self)
51
- end
52
-
53
- def file(path)
54
- @files[path] ||= Train::File::Remote::Windows.new(self, path)
55
- end
56
-
57
- def run_command(command)
58
- return if command.nil?
59
- logger.debug("[WinRM] #{self} (#{command})")
60
- out = ''
61
-
62
- response = session.run(command) do |stdout, _|
63
- out << stdout if stdout
64
- end
65
-
66
- CommandResult.new(out, response.stderr, response.exitcode)
67
- end
68
-
69
49
  # (see Base::Connection#login_command)
70
50
  def login_command
71
51
  case RbConfig::CONFIG['host_os']
@@ -111,6 +91,22 @@ class Train::Transports::WinRM
111
91
 
112
92
  PING_COMMAND = "Write-Host '[WinRM] Established\n'".freeze
113
93
 
94
+ def file_via_connection(path)
95
+ Train::File::Remote::Windows.new(self, path)
96
+ end
97
+
98
+ def run_command_via_connection(command)
99
+ return if command.nil?
100
+ logger.debug("[WinRM] #{self} (#{command})")
101
+ out = ''
102
+
103
+ response = session.run(command) do |stdout, _|
104
+ out << stdout if stdout
105
+ end
106
+
107
+ CommandResult.new(out, response.stderr, response.exitcode)
108
+ end
109
+
114
110
  # Create a local RDP document and return it
115
111
  #
116
112
  # @param opts [Hash] configuration options
@@ -193,11 +189,5 @@ class Train::Transports::WinRM
193
189
  options_to_print[:password] = '<hidden>' if options_to_print.key?(:password)
194
190
  "#{@username}@#{@hostname}<#{options_to_print.inspect}>"
195
191
  end
196
-
197
- class OS < OSCommon
198
- def initialize(backend)
199
- super(backend, { family: 'windows' })
200
- end
201
- end
202
192
  end
203
193
  end
@@ -3,5 +3,5 @@
3
3
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
4
 
5
5
  module Train
6
- VERSION = '0.29.2'.freeze
6
+ VERSION = '0.30.0'.freeze
7
7
  end
@@ -7,14 +7,14 @@ describe Train::File::Remote::Linux do
7
7
  let(:cls) { Train::File::Remote::Linux }
8
8
  let(:backend) {
9
9
  backend = Train::Transports::Mock.new.connection
10
- backend.mock_os({ family: 'linux' })
10
+ backend.mock_os({ name: 'linux', family: 'unix' })
11
11
  backend
12
12
  }
13
13
 
14
14
  def mock_stat(args, out, err = '', code = 0)
15
15
  backend.mock_command(
16
16
  "stat #{args} 2>/dev/null --printf '%s\n%f\n%U\n%u\n%G\n%g\n%X\n%Y\n%C'",
17
- out, err, code,
17
+ out, err, code
18
18
  )
19
19
  end
20
20
 
@@ -0,0 +1,85 @@
1
+ # encoding: utf-8
2
+
3
+ require 'helper'
4
+
5
+ class OsDetectLinuxTester
6
+ attr_reader :platform
7
+ include Train::Platforms::Detect::Helpers::OSCommon
8
+
9
+ def initialize
10
+ @platform = {}
11
+ end
12
+ end
13
+
14
+ describe 'os_common' do
15
+ let(:detector) { OsDetectLinuxTester.new }
16
+
17
+ describe 'winrm? check' do
18
+ it 'return winrm? true' do
19
+ require 'train/transports/winrm'
20
+ be = Train::Transports::WinRM::Connection.new(nil)
21
+ detector.instance_variable_set(:@backend, be)
22
+ detector.winrm?.must_equal(true)
23
+ end
24
+
25
+ it 'return winrm? false' do
26
+ be = mock('Backend')
27
+ detector.instance_variable_set(:@backend, be)
28
+ detector.winrm?.must_equal(false)
29
+ end
30
+ end
31
+
32
+ describe 'unix file contents' do
33
+ it 'return new file contents' do
34
+ be = mock('Backend')
35
+ output = mock('Output', exit_status: 0)
36
+ output.expects(:stdout).returns('test')
37
+ be.stubs(:run_command).with('test -f /etc/fstab && cat /etc/fstab').returns(output)
38
+ detector.instance_variable_set(:@backend, be)
39
+ detector.instance_variable_set(:@files, {})
40
+ detector.unix_file_contents('/etc/fstab').must_equal('test')
41
+ end
42
+
43
+ it 'return new file contents cached' do
44
+ be = mock('Backend')
45
+ detector.instance_variable_set(:@backend, be)
46
+ detector.instance_variable_set(:@files, { '/etc/profile' => 'test' })
47
+ detector.unix_file_contents('/etc/profile').must_equal('test')
48
+ end
49
+ end
50
+
51
+ describe 'unix file exist?' do
52
+ it 'file does exist' do
53
+ be = mock('Backend')
54
+ be.stubs(:run_command).with('test -f /etc/test').returns(mock('Output', exit_status: 0))
55
+ detector.instance_variable_set(:@backend, be)
56
+ detector.unix_file_exist?('/etc/test').must_equal(true)
57
+ end
58
+ end
59
+
60
+ describe '#detect_linux_arch' do
61
+ it 'uname m call' do
62
+ be = mock('Backend')
63
+ be.stubs(:run_command).with('uname -m').returns(mock('Output', stdout: "x86_64\n"))
64
+ detector.instance_variable_set(:@backend, be)
65
+ detector.instance_variable_set(:@uname, {})
66
+ detector.unix_uname_m.must_equal('x86_64')
67
+ end
68
+
69
+ it 'uname s call' do
70
+ be = mock('Backend')
71
+ be.stubs(:run_command).with('uname -s').returns(mock('Output', stdout: "linux"))
72
+ detector.instance_variable_set(:@backend, be)
73
+ detector.instance_variable_set(:@uname, {})
74
+ detector.unix_uname_s.must_equal('linux')
75
+ end
76
+
77
+ it 'uname r call' do
78
+ be = mock('Backend')
79
+ be.stubs(:run_command).with('uname -r').returns(mock('Output', stdout: "17.0.0\n"))
80
+ detector.instance_variable_set(:@backend, be)
81
+ detector.instance_variable_set(:@uname, {})
82
+ detector.unix_uname_r.must_equal('17.0.0')
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+
3
+ require 'helper'
4
+ require 'train/transports/mock'
5
+
6
+ class OsDetectLinuxTester
7
+ include Train::Platforms::Detect::Helpers::OSCommon
8
+ end
9
+
10
+ describe 'os_linux' do
11
+ let(:detector) { OsDetectLinuxTester.new }
12
+
13
+ describe 'redhatish_platform cleaner' do
14
+ it 'normal redhat' do
15
+ detector.redhatish_platform('Red Hattter').must_equal('redhat')
16
+ end
17
+
18
+ it 'custom redhat' do
19
+ detector.redhatish_platform('Centos Pro 11').must_equal('centos')
20
+ end
21
+ end
22
+
23
+ describe 'redhatish_version cleaner' do
24
+ it 'normal rawhide' do
25
+ detector.redhatish_version('18 (Rawhide) Pro').must_equal('18 (rawhide)')
26
+ end
27
+
28
+ it 'normal linux' do
29
+ detector.redhatish_version('derived from Ubuntu Linux 11').must_equal('11')
30
+ end
31
+ end
32
+
33
+ describe 'lsb parse' do
34
+ it 'lsb config' do
35
+ lsb = "DISTRIB_ID=Ubuntu\nDISTRIB_RELEASE=14.06\nDISTRIB_CODENAME=xenial"
36
+ expect = { :id=>'Ubuntu', :release=>'14.06', :codename=>'xenial' }
37
+ detector.lsb_config(lsb).must_equal(expect)
38
+ end
39
+
40
+ it 'lsb releasel' do
41
+ lsb = "Distributor ID: Ubuntu\nRelease: 14.06\nCodename: xenial"
42
+ expect = { :id=>'Ubuntu', :release=>'14.06', :codename=>'xenial' }
43
+ detector.lsb_release(lsb).must_equal(expect)
44
+ end
45
+ end
46
+
47
+ describe '#linux_os_release' do
48
+ describe 'when no os-release data is available' do
49
+ it 'returns nil' do
50
+ detector.expects(:unix_file_contents).with('/etc/os-release').returns(nil)
51
+ detector.linux_os_release.must_be_nil
52
+ end
53
+ end
54
+ end
55
+
56
+ describe 'when os-release data exists with no CISCO_RELEASE_INFO' do
57
+ let(:os_release) { { 'KEY1' => 'VALUE1' } }
58
+
59
+ it 'returns a correct hash' do
60
+ detector.expects(:unix_file_contents).with('/etc/os-release').returns('os-release data')
61
+ detector.expects(:parse_os_release_info).with('os-release data').returns(os_release)
62
+ detector.linux_os_release['KEY1'].must_equal('VALUE1')
63
+ end
64
+ end
65
+
66
+ describe 'when os-release data exists with CISCO_RELEASE_INFO' do
67
+ let(:os_release) { { 'KEY1' => 'VALUE1', 'CISCO_RELEASE_INFO' => 'cisco_file' } }
68
+ let(:cisco_release) { { 'KEY1' => 'NEWVALUE1', 'KEY2' => 'VALUE2' } }
69
+
70
+ it 'returns a correct hash' do
71
+ detector.expects(:unix_file_contents).with('/etc/os-release').returns('os-release data')
72
+ detector.expects(:unix_file_contents).with('cisco_file').returns('cisco data')
73
+ detector.expects(:parse_os_release_info).with('os-release data').returns(os_release)
74
+ detector.expects(:parse_os_release_info).with('cisco data').returns(cisco_release)
75
+
76
+ os_info = detector.linux_os_release
77
+ os_info['KEY1'].must_equal('NEWVALUE1')
78
+ os_info['KEY2'].must_equal('VALUE2')
79
+ end
80
+ end
81
+
82
+ describe '#parse_os_release_info' do
83
+ describe 'when nil is supplied' do
84
+ it 'returns an empty hash' do
85
+ detector.parse_os_release_info(nil).must_equal({})
86
+ end
87
+ end
88
+
89
+ describe 'when unexpectedly-formatted data is supplied' do
90
+ let(:data) do
91
+ <<-EOL
92
+ blah blah
93
+ no good data here
94
+ EOL
95
+ end
96
+
97
+ it 'returns an empty hash' do
98
+ detector.parse_os_release_info(nil).must_equal({})
99
+ end
100
+ end
101
+
102
+ describe 'when properly-formatted data is supplied' do
103
+ let(:data) do
104
+ <<-EOL
105
+ KEY1=value1
106
+ KEY2=
107
+ KEY3=value3
108
+ KEY4="value4 with spaces"
109
+ KEY5="value5 with a = sign"
110
+ EOL
111
+ end
112
+
113
+ it 'parses the data correctly' do
114
+ parsed_data = detector.parse_os_release_info(data)
115
+
116
+ parsed_data['KEY1'].must_equal('value1')
117
+ parsed_data.key?('KEY2').must_equal(false)
118
+ parsed_data['KEY3'].must_equal('value3')
119
+ parsed_data['KEY4'].must_equal('value4 with spaces')
120
+ parsed_data['KEY5'].must_equal('value5 with a = sign')
121
+ end
122
+ end
123
+ end
124
+ end
@@ -1,8 +1,11 @@
1
- require 'train/extras'
1
+ # encoding: utf-8
2
+
3
+ require 'helper'
4
+ require 'train/transports/mock'
2
5
 
3
6
  class OsDetectWindowsTester
4
7
  attr_reader :platform, :backend
5
- include Train::Extras::DetectWindows
8
+ include Train::Platforms::Detect::Helpers::Windows
6
9
 
7
10
  def initialize
8
11
  @platform = {}