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,77 @@
1
+ # encoding: utf-8
2
+
3
+ describe 'file interface' do
4
+ let(:backend) { get_backend.call }
5
+
6
+ describe 'a path that doesnt exist' do
7
+ let(:file) {
8
+ backend.file('/do_not_create_this_path_please_or_my_tests_will_fail')
9
+ }
10
+
11
+ it 'does not exist' do
12
+ file.exist?.must_equal(false)
13
+ end
14
+
15
+ it 'is not a file' do
16
+ file.file?.must_equal(false)
17
+ end
18
+
19
+ it 'has type nil' do
20
+ file.type.must_be_nil
21
+ end
22
+
23
+ it 'has no content' do
24
+ file.content.must_be_nil
25
+ end
26
+
27
+ it 'has no owner' do
28
+ file.owner.must_be_nil
29
+ end
30
+
31
+ it 'has no group' do
32
+ file.group.must_be_nil
33
+ end
34
+
35
+ it 'has mode nil' do
36
+ file.mode.must_be_nil
37
+ end
38
+
39
+ it 'checks mode? nil' do
40
+ file.mode?(nil).must_equal(true)
41
+ end
42
+
43
+ it 'has no link_path' do
44
+ file.link_path.must_be_nil
45
+ end
46
+
47
+ it 'has an md5sum' do
48
+ file.md5sum.must_be_nil
49
+ end
50
+
51
+ it 'has an sha256sum' do
52
+ file.sha256sum.must_be_nil
53
+ end
54
+
55
+ it 'has a modified time' do
56
+ file.mtime.must_be_nil
57
+ end
58
+
59
+ it 'has inode size' do
60
+ # Must be around 11 Bytes, +- 4
61
+ file.size.must_be_nil
62
+ end
63
+
64
+ it 'has no selinux_label' do
65
+ file.selinux_label.must_be_nil
66
+ end
67
+
68
+ it 'has no product_version' do
69
+ file.product_version.must_be_nil
70
+ end
71
+
72
+ it 'has no file_version' do
73
+ file.file_version.must_be_nil
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+
3
+ describe 'file interface' do
4
+ let(:backend) { get_backend.call }
5
+
6
+ describe 'pipe / fifo' do
7
+ let(:file) { backend.file('/tmp/pipe') }
8
+
9
+ it 'exists' do
10
+ file.exist?.must_equal(true)
11
+ end
12
+
13
+ it 'is a pipe' do
14
+ file.pipe?.must_equal(true)
15
+ end
16
+
17
+ it 'has type :pipe' do
18
+ file.type.must_equal(:pipe)
19
+ end
20
+
21
+ # # TODO add back content
22
+ # it 'has no content' do
23
+ # file.content.must_be_nil
24
+ # end
25
+
26
+ it 'has owner name root' do
27
+ file.owner.must_equal('root')
28
+ end
29
+
30
+ it 'has group name' do
31
+ file.group.must_equal(Test.root_group(backend.os))
32
+ end
33
+
34
+ it 'has mode 0644' do
35
+ file.mode.must_equal(00644)
36
+ end
37
+
38
+ it 'checks mode? 0644' do
39
+ file.mode?(00644).must_equal(true)
40
+ end
41
+
42
+ it 'has no link_path' do
43
+ file.link_path.must_be_nil
44
+ end
45
+
46
+ # # TODO add back content
47
+ # it 'has no md5sum' do
48
+ # file.md5sum.must_be_nil
49
+ # end
50
+ #
51
+ # # TODO add back content
52
+ # it 'has no sha256sum' do
53
+ # file.sha256sum.must_be_nil
54
+ # end
55
+
56
+ it 'has a modified time' do
57
+ file.mtime.must_be_close_to(Time.now.to_i - Test.mtime/2, Test.mtime)
58
+ end
59
+
60
+ it 'has inode size of 0' do
61
+ file.size.must_equal(0)
62
+ end
63
+
64
+ it 'has selinux label handling' do
65
+ res = Test.selinux_label(backend, file.path)
66
+ file.selinux_label.must_equal(res)
67
+ end
68
+
69
+
70
+ it 'has no product_version' do
71
+ file.product_version.must_equal(nil)
72
+ end
73
+
74
+ it 'has no file_version' do
75
+ file.file_version.must_equal(nil)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+
3
+ describe 'file interface' do
4
+ let(:backend) { get_backend.call }
5
+
6
+ describe 'symlink file' do
7
+ let(:file) { backend.file('/tmp/symlink') }
8
+
9
+ it 'exists' do
10
+ file.exist?.must_equal(true)
11
+ end
12
+
13
+ it 'is a symlink' do
14
+ file.symlink?.must_equal(true)
15
+ end
16
+
17
+ it 'is pointing to a file' do
18
+ file.file?.must_equal(true)
19
+ end
20
+
21
+ it 'is not pointing to a folder' do
22
+ file.directory?.must_equal(false)
23
+ end
24
+
25
+ it 'has type :file' do
26
+ file.type.must_equal(:file)
27
+ end
28
+
29
+ it 'has content' do
30
+ file.content.must_equal('hello world')
31
+ end
32
+
33
+ it 'has owner name root' do
34
+ file.owner.must_equal('root')
35
+ end
36
+
37
+ it 'has uid 0' do
38
+ file.uid.must_equal(0)
39
+ end
40
+
41
+ it 'has group name' do
42
+ file.group.must_equal(Test.root_group(backend.os))
43
+ end
44
+
45
+ it 'has gid 0' do
46
+ file.gid.must_equal(0)
47
+ end
48
+
49
+ it 'has mode 0777' do
50
+ file.source.mode.must_equal(00777)
51
+ end
52
+
53
+ it 'has mode 0765' do
54
+ file.mode.must_equal(00765)
55
+ end
56
+
57
+ it 'checks mode? 0765' do
58
+ file.mode?(00765).must_equal(true)
59
+ end
60
+
61
+ it 'has link_path' do
62
+ file.link_path.must_equal('/tmp/file')
63
+ end
64
+
65
+ it 'has an md5sum' do
66
+ file.md5sum.must_equal('5eb63bbbe01eeed093cb22bb8f5acdc3')
67
+ end
68
+
69
+ it 'has an sha256sum' do
70
+ file.sha256sum.must_equal('b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9')
71
+ end
72
+
73
+ it 'has a modified time' do
74
+ file.mtime.must_be_close_to(Time.now.to_i - Test.mtime/2, Test.mtime)
75
+ end
76
+
77
+ it 'has size' do
78
+ # Must be around 11 Bytes, +- 4
79
+ file.size.must_be_close_to(11, 4)
80
+ end
81
+
82
+ it 'has selinux label handling' do
83
+ res = Test.selinux_label(backend, file.path)
84
+ file.selinux_label.must_equal(res)
85
+ end
86
+
87
+ it 'has no product_version' do
88
+ file.product_version.must_equal(nil)
89
+ end
90
+
91
+ it 'has no file_version' do
92
+ file.file_version.must_equal(nil)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ describe 'run_command' do
4
+ let(:backend) { get_backend.call() }
5
+
6
+ it 'can echo commands' do
7
+ res = backend.run_command('echo hello world')
8
+ res.stdout.must_equal("hello world\n")
9
+ res.stderr.must_equal('')
10
+ res.exit_status.must_equal(0)
11
+ end
12
+
13
+ it 'can echo commands to stderr' do
14
+ # TODO: Specinfra often fails on this test.
15
+ # Fix and re-enable it.
16
+ res = backend.run_command('>&2 echo hello world')
17
+ res.stdout.must_equal('')
18
+ res.stderr.must_equal("hello world\n")
19
+ res.exit_status.must_equal(0)
20
+ end
21
+
22
+ it 'prints a correct exit status' do
23
+ res = backend.run_command('exit 123')
24
+ res.stdout.must_equal('')
25
+ res.stderr.must_equal('')
26
+ res.exit_status.must_equal(123)
27
+ end
28
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'helper'
6
+ require 'train/transports/mock'
7
+ require 'train/extras'
8
+ require 'base64'
9
+
10
+ describe 'linux command' do
11
+ let(:cls) { Train::Extras::LinuxCommand }
12
+ let(:cmd) { rand.to_s }
13
+ let(:backend) {
14
+ backend = Train::Transports::Mock.new.connection
15
+ backend.mock_os({ family: 'linux' })
16
+ backend
17
+ }
18
+
19
+ it 'wraps commands in sudo' do
20
+ lc = cls.new(backend, { sudo: true })
21
+ lc.run(cmd).must_equal "sudo #{cmd}"
22
+ end
23
+
24
+ it 'doesnt wrap commands in sudo if user == root' do
25
+ lc = cls.new(backend, { sudo: true, user: 'root' })
26
+ lc.run(cmd).must_equal cmd
27
+ end
28
+
29
+ it 'wraps commands in sudo with all options' do
30
+ opts = rand.to_s
31
+ lc = cls.new(backend, { sudo: true, sudo_options: opts })
32
+ lc.run(cmd).must_equal "sudo #{opts} #{cmd}"
33
+ end
34
+
35
+ it 'runs commands in sudo with password' do
36
+ pw = rand.to_s
37
+ lc = cls.new(backend, { sudo: true, sudo_password: pw })
38
+ bpw = Base64.strict_encode64(pw + "\n")
39
+ lc.run(cmd).must_equal "echo #{bpw} | base64 -d | sudo -S #{cmd}"
40
+ end
41
+
42
+ it 'wraps commands in sudo_command instead of sudo' do
43
+ sudo_command = rand.to_s
44
+ lc = cls.new(backend, { sudo: true, sudo_command: sudo_command })
45
+ lc.run(cmd).must_equal "#{sudo_command} #{cmd}"
46
+ end
47
+
48
+ it 'wraps commands in sudo_command with all options' do
49
+ opts = rand.to_s
50
+ sudo_command = rand.to_s
51
+ lc = cls.new(backend, { sudo: true, sudo_command: sudo_command, sudo_options: opts })
52
+ lc.run(cmd).must_equal "#{sudo_command} #{opts} #{cmd}"
53
+ end
54
+
55
+ it 'runs commands in sudo_command with password' do
56
+ pw = rand.to_s
57
+ sudo_command = rand.to_s
58
+ lc = cls.new(backend, { sudo: true, sudo_command: sudo_command, sudo_password: pw })
59
+ bpw = Base64.strict_encode64(pw + "\n")
60
+ lc.run(cmd).must_equal "echo #{bpw} | base64 -d | #{sudo_command} -S #{cmd}"
61
+ end
62
+ end
63
+
64
+ describe 'powershell command' do
65
+ let(:cls) { Train::Extras::PowerShellCommand }
66
+ let(:cmd) { rand.to_s }
67
+ let(:backend) {
68
+ backend = Train::Transports::Mock.new.connection
69
+ backend.mock_os({ family: 'windows' })
70
+ backend
71
+ }
72
+
73
+ it 'wraps commands in powershell' do
74
+ lc = cls.new(backend, {})
75
+ tmp =
76
+ lc.run(cmd).must_equal "powershell -encodedCommand #{WinRM::PowershellScript.new('$ProgressPreference=\'SilentlyContinue\';' + cmd).encoded}"
77
+ end
78
+ end
@@ -0,0 +1,180 @@
1
+ # encoding: utf-8
2
+ require 'helper'
3
+ require 'securerandom'
4
+ require 'train/extras/file_common'
5
+
6
+ describe 'file common' do
7
+ let(:cls) { Train::Extras::FileCommon }
8
+ let(:new_cls) { cls.new(nil, nil, false) }
9
+
10
+ def mockup(stubs)
11
+ Class.new(cls) do
12
+ stubs.each do |k,v|
13
+ define_method k.to_sym do
14
+ v
15
+ end
16
+ end
17
+ end.new(nil, nil, false)
18
+ end
19
+
20
+ it 'has the default type of unknown' do
21
+ new_cls.type.must_equal :unknown
22
+ end
23
+
24
+ it 'calculates md5sum from content' do
25
+ content = 'hello world'
26
+ new_cls.stub :content, content do |i|
27
+ i.md5sum.must_equal '5eb63bbbe01eeed093cb22bb8f5acdc3'
28
+ end
29
+ end
30
+
31
+ it 'sets md5sum of nil content to nil' do
32
+ new_cls.stub :content, nil do |i|
33
+ i.md5sum.must_be_nil
34
+ end
35
+ end
36
+
37
+ it 'calculates md5sum from content' do
38
+ content = 'hello world'
39
+ new_cls.stub :content, content do |i|
40
+ i.sha256sum.must_equal 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'
41
+ end
42
+ end
43
+
44
+ it 'sets sha256sum of nil content to nil' do
45
+ new_cls.stub :content, nil do |i|
46
+ i.sha256sum.must_be_nil
47
+ end
48
+ end
49
+
50
+ describe 'type' do
51
+ it 'recognized type == file' do
52
+ fc = mockup(type: :file)
53
+ fc.file?.must_equal true
54
+ end
55
+
56
+ it 'recognized type == block_device' do
57
+ fc = mockup(type: :block_device)
58
+ fc.block_device?.must_equal true
59
+ end
60
+
61
+ it 'recognized type == character_device' do
62
+ fc = mockup(type: :character_device)
63
+ fc.character_device?.must_equal true
64
+ end
65
+
66
+ it 'recognized type == socket' do
67
+ fc = mockup(type: :socket)
68
+ fc.socket?.must_equal true
69
+ end
70
+
71
+ it 'recognized type == directory' do
72
+ fc = mockup(type: :directory)
73
+ fc.directory?.must_equal true
74
+ end
75
+
76
+ it 'recognized type == pipe' do
77
+ fc = mockup(type: :pipe)
78
+ fc.pipe?.must_equal true
79
+ end
80
+
81
+ it 'recognized type == symlink' do
82
+ fc = mockup(type: :symlink)
83
+ fc.symlink?.must_equal true
84
+ end
85
+ end
86
+
87
+ describe 'version' do
88
+ it 'recognized wrong version' do
89
+ fc = mockup(product_version: rand, file_version: rand)
90
+ fc.version?(rand).must_equal false
91
+ end
92
+
93
+ it 'recognized product_version' do
94
+ x = rand
95
+ fc = mockup(product_version: x, file_version: rand)
96
+ fc.version?(x).must_equal true
97
+ end
98
+
99
+ it 'recognized file_version' do
100
+ x = rand
101
+ fc = mockup(product_version: rand, file_version: x)
102
+ fc.version?(x).must_equal true
103
+ end
104
+ end
105
+
106
+ describe 'unix_mode_mask' do
107
+
108
+ let(:fc) { mockup(type: :file) }
109
+
110
+ it 'check owner mode calculation' do
111
+ fc.unix_mode_mask('owner', 'x').must_equal 0100
112
+ fc.unix_mode_mask('owner', 'w').must_equal 0200
113
+ fc.unix_mode_mask('owner', 'r').must_equal 0400
114
+ end
115
+
116
+ it 'check group mode calculation' do
117
+ fc.unix_mode_mask('group', 'x').must_equal 0010
118
+ fc.unix_mode_mask('group', 'w').must_equal 0020
119
+ fc.unix_mode_mask('group', 'r').must_equal 0040
120
+ end
121
+
122
+ it 'check other mode calculation' do
123
+ fc.unix_mode_mask('other', 'x').must_equal 0001
124
+ fc.unix_mode_mask('other', 'w').must_equal 0002
125
+ fc.unix_mode_mask('other', 'r').must_equal 0004
126
+ end
127
+
128
+ it 'check all mode calculation' do
129
+ fc.unix_mode_mask('all', 'x').must_equal 0111
130
+ fc.unix_mode_mask('all', 'w').must_equal 0222
131
+ fc.unix_mode_mask('all', 'r').must_equal 0444
132
+ end
133
+ end
134
+
135
+ describe 'basename helper' do
136
+ def fc(path)
137
+ mockup(type: :file, path: path)
138
+ end
139
+
140
+ it 'works with an empty path' do
141
+ fc('').basename.must_equal ''
142
+ end
143
+
144
+ it 'separates a simple path (defaults to unix mode)' do
145
+ fc('/dir/file').basename.must_equal 'file'
146
+ end
147
+
148
+ it 'separates a simple path (Unix mode)' do
149
+ fc('/dir/file').basename(nil, '/').must_equal 'file'
150
+ end
151
+
152
+ it 'separates a simple path (Windows mode)' do
153
+ fc('C:\dir\file').basename(nil, '\\').must_equal 'file'
154
+ end
155
+
156
+ it 'identifies a folder name (Unix mode)' do
157
+ fc('/dir/file/').basename(nil, '/').must_equal 'file'
158
+ end
159
+
160
+ it 'identifies a folder name (Windows mode)' do
161
+ fc('C:\dir\file\\').basename(nil, '\\').must_equal 'file'
162
+ end
163
+
164
+ it 'ignores tailing separators (Unix mode)' do
165
+ fc('/dir/file///').basename(nil, '/').must_equal 'file'
166
+ end
167
+
168
+ it 'ignores tailing separators (Windows mode)' do
169
+ fc('C:\dir\file\\\\\\').basename(nil, '\\').must_equal 'file'
170
+ end
171
+
172
+ it 'doesnt work with backward slashes (Unix mode)' do
173
+ fc('C:\dir\file').basename(nil, '/').must_equal 'C:\\dir\file'
174
+ end
175
+
176
+ it 'doesnt work with forward slashes (Windows mode)' do
177
+ fc('/dir/file').basename(nil, '\\').must_equal '/dir/file'
178
+ end
179
+ end
180
+ end