train 0.12.1
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 +7 -0
- data/.rubocop.yml +71 -0
- data/CHANGELOG.md +308 -0
- data/Gemfile +30 -0
- data/LICENSE +201 -0
- data/README.md +156 -0
- data/Rakefile +148 -0
- data/lib/train.rb +117 -0
- data/lib/train/errors.rb +23 -0
- data/lib/train/extras.rb +17 -0
- data/lib/train/extras/command_wrapper.rb +148 -0
- data/lib/train/extras/file_aix.rb +20 -0
- data/lib/train/extras/file_common.rb +161 -0
- data/lib/train/extras/file_linux.rb +16 -0
- data/lib/train/extras/file_unix.rb +79 -0
- data/lib/train/extras/file_windows.rb +91 -0
- data/lib/train/extras/linux_lsb.rb +60 -0
- data/lib/train/extras/os_common.rb +136 -0
- data/lib/train/extras/os_detect_darwin.rb +32 -0
- data/lib/train/extras/os_detect_linux.rb +148 -0
- data/lib/train/extras/os_detect_unix.rb +99 -0
- data/lib/train/extras/os_detect_windows.rb +57 -0
- data/lib/train/extras/stat.rb +133 -0
- data/lib/train/options.rb +80 -0
- data/lib/train/plugins.rb +40 -0
- data/lib/train/plugins/base_connection.rb +86 -0
- data/lib/train/plugins/transport.rb +49 -0
- data/lib/train/transports/docker.rb +103 -0
- data/lib/train/transports/local.rb +52 -0
- data/lib/train/transports/local_file.rb +90 -0
- data/lib/train/transports/local_os.rb +51 -0
- data/lib/train/transports/mock.rb +147 -0
- data/lib/train/transports/ssh.rb +163 -0
- data/lib/train/transports/ssh_connection.rb +225 -0
- data/lib/train/transports/winrm.rb +184 -0
- data/lib/train/transports/winrm_connection.rb +194 -0
- data/lib/train/version.rb +7 -0
- data/test/integration/.kitchen.yml +43 -0
- data/test/integration/Berksfile +3 -0
- data/test/integration/bootstrap.sh +17 -0
- data/test/integration/chefignore +1 -0
- data/test/integration/cookbooks/test/metadata.rb +1 -0
- data/test/integration/cookbooks/test/recipes/default.rb +100 -0
- data/test/integration/cookbooks/test/recipes/prep_files.rb +47 -0
- data/test/integration/docker_run.rb +153 -0
- data/test/integration/docker_test.rb +24 -0
- data/test/integration/docker_test_container.rb +24 -0
- data/test/integration/helper.rb +61 -0
- data/test/integration/sudo/customcommand.rb +15 -0
- data/test/integration/sudo/nopasswd.rb +16 -0
- data/test/integration/sudo/passwd.rb +21 -0
- data/test/integration/sudo/reqtty.rb +17 -0
- data/test/integration/sudo/run_as.rb +12 -0
- data/test/integration/test-travis-1.yaml +13 -0
- data/test/integration/test-travis-2.yaml +13 -0
- data/test/integration/test_local.rb +19 -0
- data/test/integration/test_ssh.rb +39 -0
- data/test/integration/tests/path_block_device_test.rb +74 -0
- data/test/integration/tests/path_character_device_test.rb +74 -0
- data/test/integration/tests/path_file_test.rb +79 -0
- data/test/integration/tests/path_folder_test.rb +90 -0
- data/test/integration/tests/path_missing_test.rb +77 -0
- data/test/integration/tests/path_pipe_test.rb +78 -0
- data/test/integration/tests/path_symlink_test.rb +95 -0
- data/test/integration/tests/run_command_test.rb +28 -0
- data/test/unit/extras/command_wrapper_test.rb +78 -0
- data/test/unit/extras/file_common_test.rb +180 -0
- data/test/unit/extras/linux_file_test.rb +167 -0
- data/test/unit/extras/os_common_test.rb +269 -0
- data/test/unit/extras/os_detect_linux_test.rb +189 -0
- data/test/unit/extras/os_detect_windows_test.rb +99 -0
- data/test/unit/extras/stat_test.rb +148 -0
- data/test/unit/extras/windows_file_test.rb +44 -0
- data/test/unit/helper.rb +7 -0
- data/test/unit/plugins/connection_test.rb +44 -0
- data/test/unit/plugins/transport_test.rb +111 -0
- data/test/unit/plugins_test.rb +22 -0
- data/test/unit/train_test.rb +156 -0
- data/test/unit/transports/local_file_test.rb +184 -0
- data/test/unit/transports/local_test.rb +87 -0
- data/test/unit/transports/mock_test.rb +87 -0
- data/test/unit/transports/ssh_test.rb +109 -0
- data/test/unit/version_test.rb +8 -0
- data/test/windows/local_test.rb +46 -0
- data/test/windows/winrm_test.rb +52 -0
- data/train.gemspec +38 -0
- metadata +295 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
describe Train::Plugins do
|
5
|
+
it 'provides a method to create new v1 transport plugins' do
|
6
|
+
Train.plugin(1).must_equal Train::Plugins::Transport
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'fails when called with an unsupported plugin version' do
|
10
|
+
proc {
|
11
|
+
Train.plugin(2)
|
12
|
+
}.must_raise Train::ClientError
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'defaults to v1 plugins' do
|
16
|
+
Train.plugin.must_equal Train::Plugins::Transport
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'provides a registry of plugins' do
|
20
|
+
Train::Plugins.registry.must_be_instance_of(Hash)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Author:: Dominik Richter (<dominik.richter@gmail.com>)
|
4
|
+
require 'helper'
|
5
|
+
|
6
|
+
describe Train do
|
7
|
+
before do
|
8
|
+
Train::Plugins.registry.clear
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#create' do
|
12
|
+
it 'raises an error if the plugin isnt found' do
|
13
|
+
proc { Train.create('missing') }.must_raise Train::UserError
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'load a plugin if it isnt in the registry yet via symbol' do
|
17
|
+
Kernel.stub :require, true do
|
18
|
+
ex = Class.new(Train.plugin 1) { name 'existing' }
|
19
|
+
train = Train.create(:existing)
|
20
|
+
train.class.must_equal ex
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'load a plugin if it isnt in the registry yet via string' do
|
25
|
+
Kernel.stub :require, true do
|
26
|
+
ex = Class.new(Train.plugin 1) { name 'existing' }
|
27
|
+
train = Train.create('existing')
|
28
|
+
train.class.must_equal ex
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#options' do
|
34
|
+
it 'raises exception if a given transport plugin isnt found' do
|
35
|
+
proc { Train.options('missing') }.must_raise Train::UserError
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'provides empty options of a transport plugin' do
|
39
|
+
Class.new(Train.plugin 1) { name 'none' }
|
40
|
+
Train.options('none').must_equal({})
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'provides all options of a transport plugin' do
|
44
|
+
Class.new(Train.plugin 1) {
|
45
|
+
name 'one'
|
46
|
+
option :one, required: true, default: 123
|
47
|
+
}
|
48
|
+
Train.options('one').must_equal({
|
49
|
+
one: {
|
50
|
+
required: true,
|
51
|
+
default: 123,
|
52
|
+
}
|
53
|
+
})
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#target_config' do
|
58
|
+
it 'configures resolves target' do
|
59
|
+
org = {
|
60
|
+
target: 'ssh://user:pass@host.com:123/path',
|
61
|
+
}
|
62
|
+
res = Train.target_config(org)
|
63
|
+
res[:backend].must_equal 'ssh'
|
64
|
+
res[:host].must_equal 'host.com'
|
65
|
+
res[:user].must_equal 'user'
|
66
|
+
res[:password].must_equal 'pass'
|
67
|
+
res[:port].must_equal 123
|
68
|
+
res[:target].must_equal org[:target]
|
69
|
+
res[:path].must_equal '/path'
|
70
|
+
org.keys.must_equal [:target]
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'resolves a target while keeping existing fields' do
|
74
|
+
org = {
|
75
|
+
target: 'ssh://user:pass@host.com:123/path',
|
76
|
+
backend: rand,
|
77
|
+
host: rand,
|
78
|
+
user: rand,
|
79
|
+
password: rand,
|
80
|
+
port: rand,
|
81
|
+
path: rand
|
82
|
+
}
|
83
|
+
res = Train.target_config(org)
|
84
|
+
res.must_equal org
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'resolves a winrm target' do
|
88
|
+
org = {
|
89
|
+
target: 'winrm://Administrator@192.168.10.140',
|
90
|
+
backend: 'winrm',
|
91
|
+
host: '192.168.10.140',
|
92
|
+
user: 'Administrator',
|
93
|
+
password: nil,
|
94
|
+
port: nil,
|
95
|
+
path: nil
|
96
|
+
}
|
97
|
+
res = Train.target_config(org)
|
98
|
+
res.must_equal org
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'keeps the configuration when incorrect target is supplied' do
|
102
|
+
org = {
|
103
|
+
target: 'wrong',
|
104
|
+
}
|
105
|
+
res = Train.target_config(org)
|
106
|
+
res[:backend].must_be_nil
|
107
|
+
res[:host].must_be_nil
|
108
|
+
res[:user].must_be_nil
|
109
|
+
res[:password].must_be_nil
|
110
|
+
res[:port].must_be_nil
|
111
|
+
res[:path].must_be_nil
|
112
|
+
res[:target].must_equal org[:target]
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'always takes ruby sumbols as configuration fields' do
|
116
|
+
org = {
|
117
|
+
'target' => 'ssh://user:pass@host.com:123/path',
|
118
|
+
'backend' => rand,
|
119
|
+
'host' => rand,
|
120
|
+
'user' => rand,
|
121
|
+
'password' => rand,
|
122
|
+
'port' => rand,
|
123
|
+
'path' => rand
|
124
|
+
}
|
125
|
+
nu = org.each_with_object({}) { |(x, y), acc|
|
126
|
+
acc[x.to_sym] = y; acc
|
127
|
+
}
|
128
|
+
res = Train.target_config(org)
|
129
|
+
res.must_equal nu
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#validate_backend' do
|
134
|
+
it 'just returns the backend if it is provided' do
|
135
|
+
x = rand
|
136
|
+
Train.validate_backend({ backend: x }).must_equal x
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'returns the local backend if nothing was provided' do
|
140
|
+
Train.validate_backend({}).must_equal :local
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'returns the default backend if nothing was provided' do
|
144
|
+
x = rand
|
145
|
+
Train.validate_backend({}, x).must_equal x
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'fails if no backend was given but a target is provided' do
|
149
|
+
proc { Train.validate_backend({ target: rand }) }.must_raise Train::UserError
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'fails if no backend was given but a host is provided' do
|
153
|
+
proc { Train.validate_backend({ host: rand }) }.must_raise Train::UserError
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# author: Dominik Richter
|
4
|
+
# author: Christoph Hartmann
|
5
|
+
|
6
|
+
require 'helper'
|
7
|
+
require 'train/transports/local'
|
8
|
+
|
9
|
+
describe 'local file transport' do
|
10
|
+
let(:transport) { Train::Transports::Local.new }
|
11
|
+
let(:connection) { transport.connection }
|
12
|
+
|
13
|
+
it 'gets file contents' do
|
14
|
+
res = rand.to_s
|
15
|
+
File.stub :read, res do
|
16
|
+
connection.file(rand.to_s).content.must_equal(res)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'checks for file existance' do
|
21
|
+
File.stub :exist?, true do
|
22
|
+
connection.file(rand.to_s).exist?.must_equal(true)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
{
|
27
|
+
exist?: :exist?,
|
28
|
+
file?: :file?,
|
29
|
+
socket?: :socket?,
|
30
|
+
directory?: :directory?,
|
31
|
+
symlink?: :symlink?,
|
32
|
+
pipe?: :pipe?,
|
33
|
+
character_device?: :chardev?,
|
34
|
+
block_device?: :blockdev?,
|
35
|
+
}.each do |method, file_method|
|
36
|
+
it "checks if file is a #{method}" do
|
37
|
+
File.stub file_method.to_sym, true do
|
38
|
+
connection.file(rand.to_s).method(method.to_sym).call.must_equal(true)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'checks a file\'s link path' do
|
44
|
+
out = rand.to_s
|
45
|
+
File.stub :realpath, out do
|
46
|
+
File.stub :symlink?, true do
|
47
|
+
connection.file(rand.to_s).link_path.must_equal out
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'file metadata' do
|
53
|
+
let(:stat) { Struct.new(:mode, :size, :mtime, :uid, :gid) }
|
54
|
+
let(:uid) { rand }
|
55
|
+
let(:gid) { rand }
|
56
|
+
let(:statres) { stat.new(00140755, rand, (rand*100).to_i, uid, gid) }
|
57
|
+
|
58
|
+
def meta_stub(method, param, &block)
|
59
|
+
pwres = Struct.new(:name)
|
60
|
+
Etc.stub :getpwuid, pwres.new('owner') do
|
61
|
+
Etc.stub :getgrgid, pwres.new('group') do
|
62
|
+
File.stub method, param do; yield; end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'recognizes type' do
|
68
|
+
meta_stub :stat, statres do
|
69
|
+
connection.file(rand.to_s).type.must_equal :socket
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'recognizes mode' do
|
74
|
+
meta_stub :stat, statres do
|
75
|
+
connection.file(rand.to_s).mode.must_equal 00755
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'recognizes mtime' do
|
80
|
+
meta_stub :stat, statres do
|
81
|
+
connection.file(rand.to_s).mtime.must_equal statres.mtime
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'recognizes size' do
|
86
|
+
meta_stub :stat, statres do
|
87
|
+
connection.file(rand.to_s).size.must_equal statres.size
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'recognizes owner' do
|
92
|
+
meta_stub :stat, statres do
|
93
|
+
connection.file(rand.to_s).owner.must_equal 'owner'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'recognizes uid' do
|
98
|
+
meta_stub :stat, statres do
|
99
|
+
connection.file(rand.to_s).uid.must_equal uid
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'recognizes group' do
|
104
|
+
meta_stub :stat, statres do
|
105
|
+
connection.file(rand.to_s).group.must_equal 'group'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'recognizes gid' do
|
110
|
+
meta_stub :stat, statres do
|
111
|
+
connection.file(rand.to_s).gid.must_equal gid
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'recognizes selinux label' do
|
116
|
+
meta_stub :stat, statres do
|
117
|
+
label = rand.to_s
|
118
|
+
res = Train::Extras::CommandResult.new(label, nil, 0)
|
119
|
+
connection.stub :run_command, res do
|
120
|
+
connection.file(rand.to_s).selinux_label.must_equal label
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'recognizes source type' do
|
126
|
+
meta_stub :lstat, statres do
|
127
|
+
connection.file(rand.to_s).source.type.must_equal :socket
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'recognizes source mode' do
|
132
|
+
meta_stub :lstat, statres do
|
133
|
+
connection.file(rand.to_s).source.mode.must_equal 00755
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'recognizes source mtime' do
|
138
|
+
meta_stub :lstat, statres do
|
139
|
+
connection.file(rand.to_s).source.mtime.must_equal statres.mtime
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'recognizes source size' do
|
144
|
+
meta_stub :lstat, statres do
|
145
|
+
connection.file(rand.to_s).source.size.must_equal statres.size
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'recognizes source owner' do
|
150
|
+
meta_stub :lstat, statres do
|
151
|
+
connection.file(rand.to_s).source.owner.must_equal 'owner'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'recognizes source uid' do
|
156
|
+
meta_stub :lstat, statres do
|
157
|
+
connection.file(rand.to_s).source.uid.must_equal uid
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'recognizes source owner' do
|
162
|
+
meta_stub :lstat, statres do
|
163
|
+
connection.file(rand.to_s).source.owner.must_equal 'owner'
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'recognizes source gid' do
|
168
|
+
meta_stub :lstat, statres do
|
169
|
+
connection.file(rand.to_s).source.gid.must_equal gid
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'recognizes source selinux label' do
|
174
|
+
meta_stub :lstat, statres do
|
175
|
+
label = rand.to_s
|
176
|
+
res = Train::Extras::CommandResult.new(label, nil, 0)
|
177
|
+
connection.stub :run_command, res do
|
178
|
+
connection.file(rand.to_s).source.selinux_label.must_equal label
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# author: Dominik Richter
|
4
|
+
# author: Christoph Hartmann
|
5
|
+
|
6
|
+
require 'helper'
|
7
|
+
require 'train/transports/local'
|
8
|
+
|
9
|
+
# overwrite os detection to simplify mock tests, otherwise run_command tries to
|
10
|
+
# determine the OS first and fails the tests
|
11
|
+
class Train::Transports::Local::Connection
|
12
|
+
class OS < OSCommon
|
13
|
+
def initialize(backend)
|
14
|
+
super(backend, { family: 'train_mock_os' })
|
15
|
+
end
|
16
|
+
|
17
|
+
def detect_family
|
18
|
+
# no op, we do not need to detect the os
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'local transport' do
|
24
|
+
let(:transport) { Train::Transports::Local.new }
|
25
|
+
let(:connection) { transport.connection }
|
26
|
+
|
27
|
+
it 'can be instantiated' do
|
28
|
+
transport.wont_be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'gets the connection' do
|
32
|
+
connection.must_be_kind_of Train::Transports::Local::Connection
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'doesnt wait to be read' do
|
36
|
+
connection.wait_until_ready.must_be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'can be closed' do
|
40
|
+
connection.close.must_be_nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'has no login command' do
|
44
|
+
connection.login_command.must_be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'when running a local command' do
|
48
|
+
let(:cmd_runner) { Minitest::Mock.new }
|
49
|
+
|
50
|
+
def mock_run_cmd(cmd, &block)
|
51
|
+
cmd_runner.expect :run_command, nil
|
52
|
+
Mixlib::ShellOut.stub :new, cmd_runner do |*args|
|
53
|
+
block.call()
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'gets stdout' do
|
58
|
+
mock_run_cmd(rand) do
|
59
|
+
x = rand
|
60
|
+
cmd_runner.expect :stdout, x
|
61
|
+
cmd_runner.expect :stderr, nil
|
62
|
+
cmd_runner.expect :exitstatus, nil
|
63
|
+
connection.run_command(rand).stdout.must_equal x
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'gets stderr' do
|
68
|
+
mock_run_cmd(rand) do
|
69
|
+
x = rand
|
70
|
+
cmd_runner.expect :stdout, nil
|
71
|
+
cmd_runner.expect :stderr, x
|
72
|
+
cmd_runner.expect :exitstatus, nil
|
73
|
+
connection.run_command(rand).stderr.must_equal x
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'gets exit_status' do
|
78
|
+
mock_run_cmd(rand) do
|
79
|
+
x = rand
|
80
|
+
cmd_runner.expect :stdout, nil
|
81
|
+
cmd_runner.expect :stderr, nil
|
82
|
+
cmd_runner.expect :exitstatus, x
|
83
|
+
connection.run_command(rand).exit_status.must_equal x
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|