train 0.19.1 → 0.20.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|