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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +71 -0
  3. data/CHANGELOG.md +308 -0
  4. data/Gemfile +30 -0
  5. data/LICENSE +201 -0
  6. data/README.md +156 -0
  7. data/Rakefile +148 -0
  8. data/lib/train.rb +117 -0
  9. data/lib/train/errors.rb +23 -0
  10. data/lib/train/extras.rb +17 -0
  11. data/lib/train/extras/command_wrapper.rb +148 -0
  12. data/lib/train/extras/file_aix.rb +20 -0
  13. data/lib/train/extras/file_common.rb +161 -0
  14. data/lib/train/extras/file_linux.rb +16 -0
  15. data/lib/train/extras/file_unix.rb +79 -0
  16. data/lib/train/extras/file_windows.rb +91 -0
  17. data/lib/train/extras/linux_lsb.rb +60 -0
  18. data/lib/train/extras/os_common.rb +136 -0
  19. data/lib/train/extras/os_detect_darwin.rb +32 -0
  20. data/lib/train/extras/os_detect_linux.rb +148 -0
  21. data/lib/train/extras/os_detect_unix.rb +99 -0
  22. data/lib/train/extras/os_detect_windows.rb +57 -0
  23. data/lib/train/extras/stat.rb +133 -0
  24. data/lib/train/options.rb +80 -0
  25. data/lib/train/plugins.rb +40 -0
  26. data/lib/train/plugins/base_connection.rb +86 -0
  27. data/lib/train/plugins/transport.rb +49 -0
  28. data/lib/train/transports/docker.rb +103 -0
  29. data/lib/train/transports/local.rb +52 -0
  30. data/lib/train/transports/local_file.rb +90 -0
  31. data/lib/train/transports/local_os.rb +51 -0
  32. data/lib/train/transports/mock.rb +147 -0
  33. data/lib/train/transports/ssh.rb +163 -0
  34. data/lib/train/transports/ssh_connection.rb +225 -0
  35. data/lib/train/transports/winrm.rb +184 -0
  36. data/lib/train/transports/winrm_connection.rb +194 -0
  37. data/lib/train/version.rb +7 -0
  38. data/test/integration/.kitchen.yml +43 -0
  39. data/test/integration/Berksfile +3 -0
  40. data/test/integration/bootstrap.sh +17 -0
  41. data/test/integration/chefignore +1 -0
  42. data/test/integration/cookbooks/test/metadata.rb +1 -0
  43. data/test/integration/cookbooks/test/recipes/default.rb +100 -0
  44. data/test/integration/cookbooks/test/recipes/prep_files.rb +47 -0
  45. data/test/integration/docker_run.rb +153 -0
  46. data/test/integration/docker_test.rb +24 -0
  47. data/test/integration/docker_test_container.rb +24 -0
  48. data/test/integration/helper.rb +61 -0
  49. data/test/integration/sudo/customcommand.rb +15 -0
  50. data/test/integration/sudo/nopasswd.rb +16 -0
  51. data/test/integration/sudo/passwd.rb +21 -0
  52. data/test/integration/sudo/reqtty.rb +17 -0
  53. data/test/integration/sudo/run_as.rb +12 -0
  54. data/test/integration/test-travis-1.yaml +13 -0
  55. data/test/integration/test-travis-2.yaml +13 -0
  56. data/test/integration/test_local.rb +19 -0
  57. data/test/integration/test_ssh.rb +39 -0
  58. data/test/integration/tests/path_block_device_test.rb +74 -0
  59. data/test/integration/tests/path_character_device_test.rb +74 -0
  60. data/test/integration/tests/path_file_test.rb +79 -0
  61. data/test/integration/tests/path_folder_test.rb +90 -0
  62. data/test/integration/tests/path_missing_test.rb +77 -0
  63. data/test/integration/tests/path_pipe_test.rb +78 -0
  64. data/test/integration/tests/path_symlink_test.rb +95 -0
  65. data/test/integration/tests/run_command_test.rb +28 -0
  66. data/test/unit/extras/command_wrapper_test.rb +78 -0
  67. data/test/unit/extras/file_common_test.rb +180 -0
  68. data/test/unit/extras/linux_file_test.rb +167 -0
  69. data/test/unit/extras/os_common_test.rb +269 -0
  70. data/test/unit/extras/os_detect_linux_test.rb +189 -0
  71. data/test/unit/extras/os_detect_windows_test.rb +99 -0
  72. data/test/unit/extras/stat_test.rb +148 -0
  73. data/test/unit/extras/windows_file_test.rb +44 -0
  74. data/test/unit/helper.rb +7 -0
  75. data/test/unit/plugins/connection_test.rb +44 -0
  76. data/test/unit/plugins/transport_test.rb +111 -0
  77. data/test/unit/plugins_test.rb +22 -0
  78. data/test/unit/train_test.rb +156 -0
  79. data/test/unit/transports/local_file_test.rb +184 -0
  80. data/test/unit/transports/local_test.rb +87 -0
  81. data/test/unit/transports/mock_test.rb +87 -0
  82. data/test/unit/transports/ssh_test.rb +109 -0
  83. data/test/unit/version_test.rb +8 -0
  84. data/test/windows/local_test.rb +46 -0
  85. data/test/windows/winrm_test.rb +52 -0
  86. data/train.gemspec +38 -0
  87. 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