train 0.19.1 → 0.20.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +15 -2
- data/lib/train/extras/command_wrapper.rb +36 -12
- data/lib/train/extras/os_detect_windows.rb +30 -1
- data/lib/train/version.rb +1 -1
- data/test/unit/extras/command_wrapper_test.rb +83 -35
- data/test/unit/extras/os_detect_windows_test.rb +10 -5
- data/test/windows/local_test.rb +1 -1
- data/test/windows/winrm_test.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2e42871db47191abec6fe3d94b56a60863abd48
|
4
|
+
data.tar.gz: 9004baaebe7b8021f840647bdc5032b88fb130fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32c7d11c6aeaee6a856dfa3ce914e8d6a64d23108d8239473a7364b82929cace43c495f21765486d5b4fec02911c67747075376bde4ed8239fb88c54d30cac6d
|
7
|
+
data.tar.gz: 1335190030d7f53d31b7c5ee4f7c882fc101e80b9ae808da030c4090ecac2cc95a1b609049dbccd78ab9e7235a1c649dc9a206700de9c4689c2c1b890425f77f
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,20 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [0.
|
4
|
-
[Full Changelog](https://github.com/chef/train/compare/v0.19.
|
3
|
+
## [0.20.0](https://github.com/chef/train/tree/0.20.0) (2016-09-21)
|
4
|
+
[Full Changelog](https://github.com/chef/train/compare/v0.19.1...0.20.0)
|
5
|
+
|
6
|
+
**Fixed bugs:**
|
7
|
+
|
8
|
+
- get `Preparing modules for first use.` when I use train on Windows [\#153](https://github.com/chef/train/issues/153)
|
9
|
+
|
10
|
+
**Merged pull requests:**
|
11
|
+
|
12
|
+
- `Preparing modules for first use.` error message on Windows [\#152](https://github.com/chef/train/pull/152) ([chris-rock](https://github.com/chris-rock))
|
13
|
+
- Convert `wmic` architecture to a normal standard [\#151](https://github.com/chef/train/pull/151) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
|
14
|
+
- Login shell [\#149](https://github.com/chef/train/pull/149) ([jonathanmorley](https://github.com/jonathanmorley))
|
15
|
+
|
16
|
+
## [v0.19.1](https://github.com/chef/train/tree/v0.19.1) (2016-09-16)
|
17
|
+
[Full Changelog](https://github.com/chef/train/compare/v0.19.0...v0.19.1)
|
5
18
|
|
6
19
|
**Implemented enhancements:**
|
7
20
|
|
@@ -28,6 +28,9 @@ module Train::Extras
|
|
28
28
|
class LinuxCommand < CommandWrapperBase
|
29
29
|
Train::Options.attach(self)
|
30
30
|
|
31
|
+
option :shell, default: false
|
32
|
+
option :shell_options, default: nil
|
33
|
+
option :shell_command, default: nil
|
31
34
|
option :sudo, default: false
|
32
35
|
option :sudo_options, default: nil
|
33
36
|
option :sudo_password, default: nil
|
@@ -38,12 +41,14 @@ module Train::Extras
|
|
38
41
|
@backend = backend
|
39
42
|
validate_options(options)
|
40
43
|
|
44
|
+
@shell = options[:shell]
|
45
|
+
@shell_options = options[:shell_options] # e.g. '--login'
|
46
|
+
@shell_command = options[:shell_command] # e.g. '/bin/sh'
|
41
47
|
@sudo = options[:sudo]
|
42
48
|
@sudo_options = options[:sudo_options]
|
43
49
|
@sudo_password = options[:sudo_password]
|
44
50
|
@sudo_command = options[:sudo_command]
|
45
51
|
@user = options[:user]
|
46
|
-
@prefix = build_prefix
|
47
52
|
end
|
48
53
|
|
49
54
|
# (see CommandWrapperBase::verify)
|
@@ -71,29 +76,48 @@ module Train::Extras
|
|
71
76
|
|
72
77
|
# (see CommandWrapperBase::run)
|
73
78
|
def run(command)
|
74
|
-
|
79
|
+
shell_wrap(sudo_wrap(command))
|
75
80
|
end
|
76
81
|
|
77
82
|
def self.active?(options)
|
78
|
-
options.is_a?(Hash) &&
|
83
|
+
options.is_a?(Hash) && (
|
84
|
+
options[:sudo] ||
|
85
|
+
options[:shell]
|
86
|
+
)
|
79
87
|
end
|
80
88
|
|
81
89
|
private
|
82
90
|
|
83
|
-
|
84
|
-
|
85
|
-
return
|
91
|
+
# wrap the cmd in a sudo command
|
92
|
+
def sudo_wrap(cmd)
|
93
|
+
return cmd unless @sudo
|
94
|
+
return cmd if @user == 'root'
|
86
95
|
|
87
96
|
res = (@sudo_command || 'sudo') + ' '
|
88
97
|
|
89
|
-
unless @sudo_password.nil?
|
90
|
-
b64pw = Base64.strict_encode64(@sudo_password + "\n")
|
91
|
-
res = "echo #{b64pw} | base64 --decode | #{res}-S "
|
92
|
-
end
|
98
|
+
res = "#{safe_string(@sudo_password + "\n")} | #{res}-S " unless @sudo_password.nil?
|
93
99
|
|
94
100
|
res << @sudo_options.to_s + ' ' unless @sudo_options.nil?
|
95
101
|
|
96
|
-
res
|
102
|
+
res + cmd
|
103
|
+
end
|
104
|
+
|
105
|
+
# wrap the cmd in a subshell allowing for options to
|
106
|
+
# passed to the subshell
|
107
|
+
def shell_wrap(cmd)
|
108
|
+
return cmd unless @shell
|
109
|
+
|
110
|
+
shell = @shell_command || '$SHELL'
|
111
|
+
options = ' ' + @shell_options.to_s unless @shell_options.nil?
|
112
|
+
|
113
|
+
"#{safe_string(cmd)} | #{shell}#{options}"
|
114
|
+
end
|
115
|
+
|
116
|
+
# encapsulates encoding the string into a safe form, and decoding for use.
|
117
|
+
# @return [String] A command line snippet that can be used as part of a pipeline.
|
118
|
+
def safe_string(str)
|
119
|
+
b64str = Base64.strict_encode64(str)
|
120
|
+
"echo #{b64str} | base64 --decode"
|
97
121
|
end
|
98
122
|
end
|
99
123
|
|
@@ -113,7 +137,7 @@ module Train::Extras
|
|
113
137
|
# especially in local mode, we cannot be sure that we get a Powershell
|
114
138
|
# we may just get a `cmd`.
|
115
139
|
# TODO: we may want to opt for powershell.exe -command instead of `encodeCommand`
|
116
|
-
"powershell -encodedCommand #{encoded(safe_script(script))}"
|
140
|
+
"powershell -NoProfile -encodedCommand #{encoded(safe_script(script))}"
|
117
141
|
end
|
118
142
|
|
119
143
|
# suppress the progress stream from leaking to stderr
|
@@ -50,8 +50,37 @@ module Train::Extras
|
|
50
50
|
@platform[:build] = sys_info[:BuildNumber]
|
51
51
|
@platform[:name] = sys_info[:Caption]
|
52
52
|
@platform[:name] = @platform[:name].gsub('Microsoft', '').strip unless @platform[:name].empty?
|
53
|
-
@platform[:arch] =
|
53
|
+
@platform[:arch] = read_wmic_cpu
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
# `OSArchitecture` from `read_wmic` does not match a normal standard
|
58
|
+
# For example, `x86_64` shows as `64-bit`
|
59
|
+
def read_wmic_cpu
|
60
|
+
res = @backend.run_command('wmic cpu get architecture /format:list')
|
61
|
+
if res.exit_status == 0
|
62
|
+
sys_info = {}
|
63
|
+
res.stdout.lines.each { |line|
|
64
|
+
m = /^\s*([^=]*?)\s*=\s*(.*?)\s*$/.match(line)
|
65
|
+
sys_info[m[1].to_sym] = m[2] unless m.nil? || m[1].nil?
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
# This converts `wmic os get architecture` output to a normal standard
|
70
|
+
# https://msdn.microsoft.com/en-us/library/aa394373(VS.85).aspx
|
71
|
+
arch_map = {
|
72
|
+
0 => 'i386',
|
73
|
+
1 => 'mips',
|
74
|
+
2 => 'alpha',
|
75
|
+
3 => 'powerpc',
|
76
|
+
5 => 'arm',
|
77
|
+
6 => 'ia64',
|
78
|
+
9 => 'x86_64',
|
79
|
+
}
|
80
|
+
|
81
|
+
# The value of `wmic cpu get architecture` is always a number between 0-9
|
82
|
+
arch_number = sys_info[:Architecture].to_i
|
83
|
+
arch_map[arch_number]
|
84
|
+
end
|
56
85
|
end
|
57
86
|
end
|
data/lib/train/version.rb
CHANGED
@@ -16,47 +16,95 @@ describe 'linux command' do
|
|
16
16
|
backend
|
17
17
|
}
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
describe 'sudo wrapping' do
|
20
|
+
it 'wraps commands in sudo' do
|
21
|
+
lc = cls.new(backend, { sudo: true })
|
22
|
+
lc.run(cmd).must_equal "sudo #{cmd}"
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
it 'doesnt wrap commands in sudo if user == root' do
|
26
|
+
lc = cls.new(backend, { sudo: true, user: 'root' })
|
27
|
+
lc.run(cmd).must_equal cmd
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
it 'wraps commands in sudo with all options' do
|
31
|
+
opts = rand.to_s
|
32
|
+
lc = cls.new(backend, { sudo: true, sudo_options: opts })
|
33
|
+
lc.run(cmd).must_equal "sudo #{opts} #{cmd}"
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
it 'runs commands in sudo with password' do
|
37
|
+
pw = rand.to_s
|
38
|
+
lc = cls.new(backend, { sudo: true, sudo_password: pw })
|
39
|
+
bpw = Base64.strict_encode64(pw + "\n")
|
40
|
+
lc.run(cmd).must_equal "echo #{bpw} | base64 --decode | sudo -S #{cmd}"
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
it 'wraps commands in sudo_command instead of sudo' do
|
44
|
+
sudo_command = rand.to_s
|
45
|
+
lc = cls.new(backend, { sudo: true, sudo_command: sudo_command })
|
46
|
+
lc.run(cmd).must_equal "#{sudo_command} #{cmd}"
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'wraps commands in sudo_command with all options' do
|
50
|
+
opts = rand.to_s
|
51
|
+
sudo_command = rand.to_s
|
52
|
+
lc = cls.new(backend, { sudo: true, sudo_command: sudo_command, sudo_options: opts })
|
53
|
+
lc.run(cmd).must_equal "#{sudo_command} #{opts} #{cmd}"
|
54
|
+
end
|
47
55
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
56
|
+
it 'runs commands in sudo_command with password' do
|
57
|
+
pw = rand.to_s
|
58
|
+
sudo_command = rand.to_s
|
59
|
+
lc = cls.new(backend, { sudo: true, sudo_command: sudo_command, sudo_password: pw })
|
60
|
+
bpw = Base64.strict_encode64(pw + "\n")
|
61
|
+
lc.run(cmd).must_equal "echo #{bpw} | base64 --decode | #{sudo_command} -S #{cmd}"
|
62
|
+
end
|
53
63
|
end
|
54
64
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
65
|
+
describe 'shell wrapping' do
|
66
|
+
it 'wraps commands in a default shell with login' do
|
67
|
+
lc = cls.new(backend, { shell: true, shell_options: '--login' })
|
68
|
+
bcmd = Base64.strict_encode64(cmd)
|
69
|
+
lc.run(cmd).must_equal "echo #{bcmd} | base64 --decode | $SHELL --login"
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'wraps sudo commands in a default shell with login' do
|
73
|
+
lc = cls.new(backend, { sudo: true, shell: true, shell_options: '--login' })
|
74
|
+
bcmd = Base64.strict_encode64("sudo #{cmd}")
|
75
|
+
lc.run(cmd).must_equal "echo #{bcmd} | base64 --decode | $SHELL --login"
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'wraps sudo commands and sudo passwords in a default shell with login' do
|
79
|
+
pw = rand.to_s
|
80
|
+
lc = cls.new(backend, { sudo: true, sudo_password: pw, shell: true, shell_options: '--login' })
|
81
|
+
bpw = Base64.strict_encode64(pw + "\n")
|
82
|
+
bcmd = Base64.strict_encode64("echo #{bpw} | base64 --decode | sudo -S #{cmd}")
|
83
|
+
lc.run(cmd).must_equal "echo #{bcmd} | base64 --decode | $SHELL --login"
|
84
|
+
p bcmd
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'wraps commands in a default shell when shell is true' do
|
88
|
+
lc = cls.new(backend, { shell: true })
|
89
|
+
bcmd = Base64.strict_encode64(cmd)
|
90
|
+
lc.run(cmd).must_equal "echo #{bcmd} | base64 --decode | $SHELL"
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'doesnt wrap commands in a shell when shell is false' do
|
94
|
+
lc = cls.new(backend, { shell: false })
|
95
|
+
lc.run(cmd).must_equal cmd
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'wraps commands in a `shell` instead of default shell' do
|
99
|
+
lc = cls.new(backend, { shell: true, shell_command: '/bin/bash' })
|
100
|
+
bcmd = Base64.strict_encode64(cmd)
|
101
|
+
lc.run(cmd).must_equal "echo #{bcmd} | base64 --decode | /bin/bash"
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'wraps commands in a default shell with login' do
|
105
|
+
lc = cls.new(backend, { shell: true, shell_command: '/bin/bash', shell_options: '--login' })
|
106
|
+
bcmd = Base64.strict_encode64(cmd)
|
107
|
+
lc.run(cmd).must_equal "echo #{bcmd} | base64 --decode | /bin/bash --login"
|
108
|
+
end
|
61
109
|
end
|
62
110
|
end
|
@@ -17,6 +17,7 @@ describe 'os_detect_windows' do
|
|
17
17
|
detector = OsDetectWindowsTester.new
|
18
18
|
detector.backend.mock_command('cmd /c ver', "\r\nMicrosoft Windows [Version 6.3.9600]\r\n", '', 0)
|
19
19
|
detector.backend.mock_command('wmic os get * /format:list',"\r\r\nBuildNumber=9600\r\r\nCaption=Microsoft Windows Server 2012 R2 Standard\r\r\nOSArchitecture=64-bit\r\r\nVersion=6.3.9600\r\r\n" , '', 0)
|
20
|
+
detector.backend.mock_command('wmic cpu get architecture /format:list',"\r\r\nArchitecture=9\r\r\n" , '', 0)
|
20
21
|
detector
|
21
22
|
}
|
22
23
|
|
@@ -24,7 +25,7 @@ describe 'os_detect_windows' do
|
|
24
25
|
detector.detect_windows
|
25
26
|
detector.platform[:family].must_equal('windows')
|
26
27
|
detector.platform[:name].must_equal('Windows Server 2012 R2 Standard')
|
27
|
-
detector.platform[:arch].must_equal('
|
28
|
+
detector.platform[:arch].must_equal('x86_64')
|
28
29
|
detector.platform[:release].must_equal('6.3.9600')
|
29
30
|
end
|
30
31
|
end
|
@@ -34,6 +35,7 @@ describe 'os_detect_windows' do
|
|
34
35
|
detector = OsDetectWindowsTester.new
|
35
36
|
detector.backend.mock_command('cmd /c ver', "\r\nMicrosoft Windows [Version 6.1.7601]\r\n", '', 0)
|
36
37
|
detector.backend.mock_command('wmic os get * /format:list',"\r\r\nBuildNumber=7601\r\r\nCaption=Microsoft Windows Server 2008 R2 Standard \r\r\nOSArchitecture=64-bit\r\r\nVersion=6.1.7601\r\r\n" , '', 0)
|
38
|
+
detector.backend.mock_command('wmic cpu get architecture /format:list',"\r\r\nArchitecture=9\r\r\n" , '', 0)
|
37
39
|
detector
|
38
40
|
}
|
39
41
|
|
@@ -41,7 +43,7 @@ describe 'os_detect_windows' do
|
|
41
43
|
detector.detect_windows
|
42
44
|
detector.platform[:family].must_equal('windows')
|
43
45
|
detector.platform[:name].must_equal('Windows Server 2008 R2 Standard')
|
44
|
-
detector.platform[:arch].must_equal('
|
46
|
+
detector.platform[:arch].must_equal('x86_64')
|
45
47
|
detector.platform[:release].must_equal('6.1.7601')
|
46
48
|
end
|
47
49
|
end
|
@@ -51,6 +53,7 @@ describe 'os_detect_windows' do
|
|
51
53
|
detector = OsDetectWindowsTester.new
|
52
54
|
detector.backend.mock_command('cmd /c ver', "\r\nMicrosoft Windows [Version 6.1.7601]\r\n", '', 0)
|
53
55
|
detector.backend.mock_command('wmic os get * /format:list',"\r\r\nBuildNumber=7601\r\r\nCaption=Microsoft Windows 7 Enterprise \r\r\nOSArchitecture=32-bit\r\r\nVersion=6.1.7601\r\r\n\r\r\n" , '', 0)
|
56
|
+
detector.backend.mock_command('wmic cpu get architecture /format:list',"\r\r\nArchitecture=0\r\r\n" , '', 0)
|
54
57
|
detector
|
55
58
|
}
|
56
59
|
|
@@ -58,7 +61,7 @@ describe 'os_detect_windows' do
|
|
58
61
|
detector.detect_windows
|
59
62
|
detector.platform[:family].must_equal('windows')
|
60
63
|
detector.platform[:name].must_equal('Windows 7 Enterprise')
|
61
|
-
detector.platform[:arch].must_equal('
|
64
|
+
detector.platform[:arch].must_equal('i386')
|
62
65
|
detector.platform[:release].must_equal('6.1.7601')
|
63
66
|
end
|
64
67
|
end
|
@@ -68,6 +71,7 @@ describe 'os_detect_windows' do
|
|
68
71
|
detector = OsDetectWindowsTester.new
|
69
72
|
detector.backend.mock_command('cmd /c ver', "\r\nMicrosoft Windows [Version 10.0.10240]\r\n", '', 0)
|
70
73
|
detector.backend.mock_command('wmic os get * /format:list',"\r\r\nBuildNumber=10240\r\r\nCaption=Microsoft Windows 10 Pro\r\r\nOSArchitecture=64-bit\r\r\nVersion=10.0.10240\r\r\n\r\r\n" , '', 0)
|
74
|
+
detector.backend.mock_command('wmic cpu get architecture /format:list',"\r\r\nArchitecture=9\r\r\n" , '', 0)
|
71
75
|
detector
|
72
76
|
}
|
73
77
|
|
@@ -75,7 +79,7 @@ describe 'os_detect_windows' do
|
|
75
79
|
detector.detect_windows
|
76
80
|
detector.platform[:family].must_equal('windows')
|
77
81
|
detector.platform[:name].must_equal('Windows 10 Pro')
|
78
|
-
detector.platform[:arch].must_equal('
|
82
|
+
detector.platform[:arch].must_equal('x86_64')
|
79
83
|
detector.platform[:release].must_equal('10.0.10240')
|
80
84
|
end
|
81
85
|
end
|
@@ -84,7 +88,8 @@ describe 'os_detect_windows' do
|
|
84
88
|
let(:detector) {
|
85
89
|
detector = OsDetectWindowsTester.new
|
86
90
|
detector.backend.mock_command('cmd /c ver', "\r\nMicrosoft Windows [Version 4.10.1998]\r\n", '', 0)
|
87
|
-
detector.backend.mock_command('wmic os get * /format:list', nil
|
91
|
+
detector.backend.mock_command('wmic os get * /format:list', nil, '', 1)
|
92
|
+
detector.backend.mock_command('wmic cpu get architecture /format:list', nil, '', 1)
|
88
93
|
detector
|
89
94
|
}
|
90
95
|
|
data/test/windows/local_test.rb
CHANGED
@@ -24,7 +24,7 @@ describe 'windows local command' do
|
|
24
24
|
os[:name].must_equal 'Windows Server 2012 R2 Datacenter'
|
25
25
|
os[:family].must_equal "windows"
|
26
26
|
os[:release].must_equal '6.3.9600'
|
27
|
-
os[:arch].must_equal '
|
27
|
+
os[:arch].must_equal 'x86_64'
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'run echo test' do
|
data/test/windows/winrm_test.rb
CHANGED
@@ -30,7 +30,7 @@ describe 'windows winrm command' do
|
|
30
30
|
os[:name].must_equal 'Windows Server 2012 R2 Datacenter'
|
31
31
|
os[:family].must_equal 'windows'
|
32
32
|
os[:release].must_equal '6.3.9600'
|
33
|
-
os[:arch].must_equal '
|
33
|
+
os[:arch].must_equal 'x86_64'
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'run echo test' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: train
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.20.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominik Richter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-09-
|
11
|
+
date: 2016-09-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|