tenderloin 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/LICENCE +21 -0
  2. data/README.md +50 -0
  3. data/Version +1 -0
  4. data/bin/loin +5 -0
  5. data/config/default.rb +26 -0
  6. data/lib/tenderloin/actions/base.rb +93 -0
  7. data/lib/tenderloin/actions/box/add.rb +22 -0
  8. data/lib/tenderloin/actions/box/destroy.rb +14 -0
  9. data/lib/tenderloin/actions/box/download.rb +63 -0
  10. data/lib/tenderloin/actions/box/unpackage.rb +46 -0
  11. data/lib/tenderloin/actions/runner.rb +138 -0
  12. data/lib/tenderloin/actions/vm/boot.rb +52 -0
  13. data/lib/tenderloin/actions/vm/destroy.rb +18 -0
  14. data/lib/tenderloin/actions/vm/halt.rb +14 -0
  15. data/lib/tenderloin/actions/vm/import.rb +32 -0
  16. data/lib/tenderloin/actions/vm/move_hard_drive.rb +53 -0
  17. data/lib/tenderloin/actions/vm/provision.rb +71 -0
  18. data/lib/tenderloin/actions/vm/reload.rb +17 -0
  19. data/lib/tenderloin/actions/vm/shared_folders.rb +47 -0
  20. data/lib/tenderloin/actions/vm/start.rb +17 -0
  21. data/lib/tenderloin/actions/vm/up.rb +55 -0
  22. data/lib/tenderloin/box.rb +143 -0
  23. data/lib/tenderloin/busy.rb +73 -0
  24. data/lib/tenderloin/cli.rb +59 -0
  25. data/lib/tenderloin/commands.rb +154 -0
  26. data/lib/tenderloin/config.rb +144 -0
  27. data/lib/tenderloin/downloaders/base.rb +13 -0
  28. data/lib/tenderloin/downloaders/file.rb +21 -0
  29. data/lib/tenderloin/downloaders/http.rb +47 -0
  30. data/lib/tenderloin/env.rb +156 -0
  31. data/lib/tenderloin/fusion_vm.rb +85 -0
  32. data/lib/tenderloin/ssh.rb +49 -0
  33. data/lib/tenderloin/util.rb +51 -0
  34. data/lib/tenderloin/vm.rb +63 -0
  35. data/lib/tenderloin/vmx_file.rb +28 -0
  36. data/lib/tenderloin.rb +14 -0
  37. data/script/tenderloin-ssh-expect.sh +23 -0
  38. data/templates/Tenderfile +8 -0
  39. data/test/tenderloin/actions/base_test.rb +32 -0
  40. data/test/tenderloin/actions/box/add_test.rb +37 -0
  41. data/test/tenderloin/actions/box/destroy_test.rb +18 -0
  42. data/test/tenderloin/actions/box/download_test.rb +118 -0
  43. data/test/tenderloin/actions/box/unpackage_test.rb +100 -0
  44. data/test/tenderloin/actions/runner_test.rb +262 -0
  45. data/test/tenderloin/actions/vm/boot_test.rb +55 -0
  46. data/test/tenderloin/actions/vm/destroy_test.rb +24 -0
  47. data/test/tenderloin/actions/vm/down_test.rb +32 -0
  48. data/test/tenderloin/actions/vm/export_test.rb +88 -0
  49. data/test/tenderloin/actions/vm/forward_ports_test.rb +50 -0
  50. data/test/tenderloin/actions/vm/halt_test.rb +27 -0
  51. data/test/tenderloin/actions/vm/import_test.rb +36 -0
  52. data/test/tenderloin/actions/vm/move_hard_drive_test.rb +108 -0
  53. data/test/tenderloin/actions/vm/package_test.rb +181 -0
  54. data/test/tenderloin/actions/vm/provision_test.rb +103 -0
  55. data/test/tenderloin/actions/vm/reload_test.rb +44 -0
  56. data/test/tenderloin/actions/vm/resume_test.rb +27 -0
  57. data/test/tenderloin/actions/vm/shared_folders_test.rb +117 -0
  58. data/test/tenderloin/actions/vm/start_test.rb +28 -0
  59. data/test/tenderloin/actions/vm/suspend_test.rb +27 -0
  60. data/test/tenderloin/actions/vm/up_test.rb +98 -0
  61. data/test/tenderloin/box_test.rb +139 -0
  62. data/test/tenderloin/busy_test.rb +83 -0
  63. data/test/tenderloin/commands_test.rb +269 -0
  64. data/test/tenderloin/config_test.rb +123 -0
  65. data/test/tenderloin/downloaders/base_test.rb +20 -0
  66. data/test/tenderloin/downloaders/file_test.rb +32 -0
  67. data/test/tenderloin/downloaders/http_test.rb +40 -0
  68. data/test/tenderloin/env_test.rb +345 -0
  69. data/test/tenderloin/ssh_test.rb +103 -0
  70. data/test/tenderloin/util_test.rb +64 -0
  71. data/test/tenderloin/vm_test.rb +89 -0
  72. data/test/test_helper.rb +92 -0
  73. metadata +241 -0
@@ -0,0 +1,269 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class CommandsTest < Test::Unit::TestCase
4
+ setup do
5
+ Tenderloin::Env.stubs(:load!)
6
+
7
+ @persisted_vm = mock("persisted_vm")
8
+ @persisted_vm.stubs(:execute!)
9
+ Tenderloin::Env.stubs(:persisted_vm).returns(@persisted_vm)
10
+ Tenderloin::Env.stubs(:require_persisted_vm)
11
+ end
12
+
13
+ context "init" do
14
+ setup do
15
+ FileUtils.stubs(:cp)
16
+ @rootfile_path = File.join(Dir.pwd, Tenderloin::Env::ROOTFILE_NAME)
17
+ @template_path = File.join(PROJECT_ROOT, "templates", Tenderloin::Env::ROOTFILE_NAME)
18
+ end
19
+
20
+ should "error and exit if a rootfile already exists" do
21
+ File.expects(:exist?).with(@rootfile_path).returns(true)
22
+ Tenderloin::Commands.expects(:error_and_exit).once
23
+ Tenderloin::Commands.init
24
+ end
25
+
26
+ should "copy the templated rootfile to the current path" do
27
+ File.expects(:exist?).with(@rootfile_path).returns(false)
28
+ FileUtils.expects(:cp).with(@template_path, @rootfile_path).once
29
+ Tenderloin::Commands.init
30
+ end
31
+ end
32
+
33
+ context "up" do
34
+ setup do
35
+ Tenderloin::Env.stubs(:persisted_vm).returns(nil)
36
+ Tenderloin::VM.stubs(:execute!)
37
+ Tenderloin::Env.stubs(:require_box)
38
+ end
39
+
40
+ should "require load the environment" do
41
+ Tenderloin::Env.expects(:load!).once
42
+ Tenderloin::Commands.up
43
+ end
44
+
45
+ should "require a box" do
46
+ Tenderloin::Env.expects(:require_box).once
47
+ Tenderloin::Commands.up
48
+ end
49
+
50
+ should "call the up action on VM if it doesn't exist" do
51
+ Tenderloin::VM.expects(:execute!).with(Tenderloin::Actions::VM::Up).once
52
+ Tenderloin::Commands.up
53
+ end
54
+
55
+ should "call start on the persisted vm if it exists" do
56
+ Tenderloin::Env.stubs(:persisted_vm).returns(@persisted_vm)
57
+ @persisted_vm.expects(:start).once
58
+ Tenderloin::VM.expects(:execute!).never
59
+ Tenderloin::Commands.up
60
+ end
61
+ end
62
+
63
+ context "down" do
64
+ setup do
65
+ @persisted_vm.stubs(:destroy)
66
+ end
67
+
68
+ should "require a persisted VM" do
69
+ Tenderloin::Env.expects(:require_persisted_vm).once
70
+ Tenderloin::Commands.down
71
+ end
72
+
73
+ should "destroy the persisted VM and the VM image" do
74
+ @persisted_vm.expects(:destroy).once
75
+ Tenderloin::Commands.down
76
+ end
77
+ end
78
+
79
+ context "reload" do
80
+ should "require a persisted VM" do
81
+ Tenderloin::Env.expects(:require_persisted_vm).once
82
+ Tenderloin::Commands.reload
83
+ end
84
+
85
+ should "call the `reload` action on the VM" do
86
+ @persisted_vm.expects(:execute!).with(Tenderloin::Actions::VM::Reload).once
87
+ Tenderloin::Commands.reload
88
+ end
89
+ end
90
+
91
+ context "ssh" do
92
+ setup do
93
+ Tenderloin::SSH.stubs(:connect)
94
+ end
95
+
96
+ should "require a persisted VM" do
97
+ Tenderloin::Env.expects(:require_persisted_vm).once
98
+ Tenderloin::Commands.ssh
99
+ end
100
+
101
+ should "connect to SSH" do
102
+ Tenderloin::SSH.expects(:connect).once
103
+ Tenderloin::Commands.ssh
104
+ end
105
+ end
106
+
107
+ context "halt" do
108
+ should "require a persisted VM" do
109
+ Tenderloin::Env.expects(:require_persisted_vm).once
110
+ Tenderloin::Commands.halt
111
+ end
112
+
113
+ should "call the `halt` action on the VM" do
114
+ @persisted_vm.expects(:execute!).with(Tenderloin::Actions::VM::Halt).once
115
+ Tenderloin::Commands.halt
116
+ end
117
+ end
118
+
119
+ context "suspend" do
120
+ setup do
121
+ @persisted_vm.stubs(:suspend)
122
+ @persisted_vm.stubs(:saved?).returns(false)
123
+ end
124
+
125
+ should "require a persisted VM" do
126
+ Tenderloin::Env.expects(:require_persisted_vm).once
127
+ Tenderloin::Commands.suspend
128
+ end
129
+
130
+ should "suspend the VM" do
131
+ @persisted_vm.expects(:suspend).once
132
+ Tenderloin::Commands.suspend
133
+ end
134
+ end
135
+
136
+ context "resume" do
137
+ setup do
138
+ @persisted_vm.stubs(:resume)
139
+ @persisted_vm.stubs(:saved?).returns(true)
140
+ end
141
+
142
+ should "require a persisted VM" do
143
+ Tenderloin::Env.expects(:require_persisted_vm).once
144
+ Tenderloin::Commands.resume
145
+ end
146
+
147
+ should "save the state of the VM" do
148
+ @persisted_vm.expects(:resume).once
149
+ Tenderloin::Commands.resume
150
+ end
151
+ end
152
+
153
+ context "package" do
154
+ setup do
155
+ @persisted_vm.stubs(:package)
156
+ @persisted_vm.stubs(:powered_off?).returns(true)
157
+ end
158
+
159
+ should "require a persisted vm" do
160
+ Tenderloin::Env.expects(:require_persisted_vm).once
161
+ Tenderloin::Commands.package
162
+ end
163
+
164
+ should "error and exit if the VM is not powered off" do
165
+ @persisted_vm.stubs(:powered_off?).returns(false)
166
+ Tenderloin::Commands.expects(:error_and_exit).once
167
+ @persisted_vm.expects(:package).never
168
+ Tenderloin::Commands.package
169
+ end
170
+
171
+ should "call package on the persisted VM" do
172
+ @persisted_vm.expects(:package).once
173
+ Tenderloin::Commands.package
174
+ end
175
+
176
+ should "pass the out path and include_files to the package method" do
177
+ out_path = mock("out_path")
178
+ include_files = mock("include_files")
179
+ @persisted_vm.expects(:package).with(out_path, include_files).once
180
+ Tenderloin::Commands.package(out_path, include_files)
181
+ end
182
+
183
+ should "default to an empty array when not include_files are specified" do
184
+ out_path = mock("out_path")
185
+ @persisted_vm.expects(:package).with(out_path, []).once
186
+ Tenderloin::Commands.package(out_path)
187
+ end
188
+ end
189
+
190
+ context "box" do
191
+ setup do
192
+ Tenderloin::Commands.stubs(:box_foo)
193
+ Tenderloin::Commands.stubs(:box_add)
194
+ Tenderloin::Commands.stubs(:box_remove)
195
+ end
196
+
197
+ should "load the environment" do
198
+ Tenderloin::Env.expects(:load!).once
199
+ Tenderloin::Commands.box(["add"])
200
+ end
201
+
202
+ should "error and exit if the first argument is not 'add' or 'remove'" do
203
+ Tenderloin::Commands.expects(:error_and_exit).once
204
+ Tenderloin::Commands.box(["foo"])
205
+ end
206
+
207
+ should "not error and exit if the first argument is 'add' or 'remove'" do
208
+ commands = ["add", "remove"]
209
+
210
+ commands.each do |command|
211
+ Tenderloin::Commands.expects(:error_and_exit).never
212
+ Tenderloin::Commands.expects("box_#{command}".to_sym).once
213
+ Tenderloin::Commands.box([command])
214
+ end
215
+ end
216
+
217
+ should "forward any additional arguments" do
218
+ Tenderloin::Commands.expects(:box_add).with(1,2,3).once
219
+ Tenderloin::Commands.box(["add",1,2,3])
220
+ end
221
+ end
222
+
223
+ context "box list" do
224
+ setup do
225
+ @boxes = ["foo", "bar"]
226
+
227
+ Tenderloin::Box.stubs(:all).returns(@boxes)
228
+ Tenderloin::Commands.stubs(:puts)
229
+ end
230
+
231
+ should "call all on box and sort the results" do
232
+ @all = mock("all")
233
+ @all.expects(:sort).returns(@boxes)
234
+ Tenderloin::Box.expects(:all).returns(@all)
235
+ Tenderloin::Commands.box_list
236
+ end
237
+ end
238
+
239
+ context "box add" do
240
+ setup do
241
+ @name = "foo"
242
+ @path = "bar"
243
+ end
244
+
245
+ should "execute the add action with the name and path" do
246
+ Tenderloin::Box.expects(:add).with(@name, @path).once
247
+ Tenderloin::Commands.box_add(@name, @path)
248
+ end
249
+ end
250
+
251
+ context "box remove" do
252
+ setup do
253
+ @name = "foo"
254
+ end
255
+
256
+ should "error and exit if the box doesn't exist" do
257
+ Tenderloin::Box.expects(:find).returns(nil)
258
+ Tenderloin::Commands.expects(:error_and_exit).once
259
+ Tenderloin::Commands.box_remove(@name)
260
+ end
261
+
262
+ should "call destroy on the box if it exists" do
263
+ @box = mock("box")
264
+ Tenderloin::Box.expects(:find).with(@name).returns(@box)
265
+ @box.expects(:destroy).once
266
+ Tenderloin::Commands.box_remove(@name)
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,123 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class ConfigTest < Test::Unit::TestCase
4
+ context "resetting" do
5
+ setup do
6
+ Tenderloin::Config.run { |config| }
7
+ Tenderloin::Config.execute!
8
+ end
9
+
10
+ should "return the same config object typically" do
11
+ config = Tenderloin::Config.config
12
+ assert config.equal?(Tenderloin::Config.config)
13
+ end
14
+
15
+ should "create a new object if cleared" do
16
+ config = Tenderloin::Config.config
17
+ Tenderloin::Config.reset!
18
+ assert !config.equal?(Tenderloin::Config.config)
19
+ end
20
+
21
+ should "empty the runners" do
22
+ assert !Tenderloin::Config.config_runners.empty?
23
+ Tenderloin::Config.reset!
24
+ assert Tenderloin::Config.config_runners.empty?
25
+ end
26
+ end
27
+
28
+ context "accessing configuration" do
29
+ setup do
30
+ Tenderloin::Config.run { |config| }
31
+ Tenderloin::Config.execute!
32
+ end
33
+
34
+ should "forward config to the class method" do
35
+ assert_equal Tenderloin.config, Tenderloin::Config.config
36
+ end
37
+ end
38
+
39
+ context "initializing" do
40
+ teardown do
41
+ Tenderloin::Config.instance_variable_set(:@config_runners, nil)
42
+ Tenderloin::Config.instance_variable_set(:@config, nil)
43
+ end
44
+
45
+ should "not run the blocks right away" do
46
+ obj = mock("obj")
47
+ obj.expects(:foo).never
48
+ Tenderloin::Config.run { |config| obj.foo }
49
+ Tenderloin::Config.run { |config| obj.foo }
50
+ Tenderloin::Config.run { |config| obj.foo }
51
+ end
52
+
53
+ should "run the blocks when execute! is ran" do
54
+ obj = mock("obj")
55
+ obj.expects(:foo).times(2)
56
+ Tenderloin::Config.run { |config| obj.foo }
57
+ Tenderloin::Config.run { |config| obj.foo }
58
+ Tenderloin::Config.execute!
59
+ end
60
+
61
+ should "run the blocks with the same config object" do
62
+ Tenderloin::Config.run { |config| assert config }
63
+ Tenderloin::Config.run { |config| assert config }
64
+ Tenderloin::Config.execute!
65
+ end
66
+
67
+ should "not be loaded, initially" do
68
+ assert !Tenderloin::Config.config.loaded?
69
+ end
70
+
71
+ should "be loaded after running" do
72
+ Tenderloin::Config.run {}
73
+ Tenderloin::Config.execute!
74
+ assert Tenderloin::Config.config.loaded?
75
+ end
76
+ end
77
+
78
+ context "base class" do
79
+ setup do
80
+ @base = Tenderloin::Config::Base.new
81
+ end
82
+
83
+ should "forward [] access to methods" do
84
+ @base.expects(:foo).once
85
+ @base[:foo]
86
+ end
87
+
88
+ should "return a hash of instance variables" do
89
+ data = { :foo => "bar", :bar => "baz" }
90
+
91
+ data.each do |iv, value|
92
+ @base.instance_variable_set("@#{iv}".to_sym, value)
93
+ end
94
+
95
+ result = @base.instance_variables_hash
96
+ assert_equal data.length, result.length
97
+
98
+ data.each do |iv, value|
99
+ assert_equal value, result[iv]
100
+ end
101
+ end
102
+
103
+ should "convert instance variable hash to json" do
104
+ @json = mock("json")
105
+ @iv_hash = mock("iv_hash")
106
+ @iv_hash.expects(:to_json).once.returns(@json)
107
+ @base.expects(:instance_variables_hash).returns(@iv_hash)
108
+ assert_equal @json, @base.to_json
109
+ end
110
+ end
111
+
112
+ context "chef config" do
113
+ setup do
114
+ @config = Tenderloin::Config::ChefConfig.new
115
+ @config.json = "HEY"
116
+ end
117
+
118
+ should "not include the 'json' key in the config dump" do
119
+ result = JSON.parse(@config.to_json)
120
+ assert !result.has_key?("json")
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,20 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
2
+
3
+ class BaseDownloaderTest < Test::Unit::TestCase
4
+ should "include the util class so subclasses have access to it" do
5
+ assert Tenderloin::Downloaders::Base.include?(Tenderloin::Util)
6
+ end
7
+
8
+ context "base instance" do
9
+ setup do
10
+ @base = Tenderloin::Downloaders::Base.new
11
+ end
12
+
13
+ should "implement prepare which does nothing" do
14
+ assert_nothing_raised do
15
+ assert @base.respond_to?(:download!)
16
+ @base.download!("source", "destination")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
2
+
3
+ class FileDownloaderTest < Test::Unit::TestCase
4
+ setup do
5
+ @downloader, @tempfile = mock_downloader(Tenderloin::Downloaders::File)
6
+ @uri = "foo.box"
7
+ end
8
+
9
+ context "downloading" do
10
+ setup do
11
+ @file = mock("file")
12
+ @file.stubs(:read)
13
+ @file.stubs(:eof?).returns(false)
14
+ @downloader.stubs(:open).yields(@file)
15
+ end
16
+
17
+ should "open with the given uri" do
18
+ @downloader.expects(:open).with(@uri).once
19
+ @downloader.download!(@uri, @tempfile)
20
+ end
21
+
22
+ should "buffer the read from the file and write to the tempfile" do
23
+ data = mock("data")
24
+ write_seq = sequence("write_seq")
25
+ @file.stubs(:eof?).returns(false).in_sequence(write_seq)
26
+ @file.expects(:read).returns(data).in_sequence(write_seq)
27
+ @tempfile.expects(:write).with(data).in_sequence(write_seq)
28
+ @file.stubs(:eof?).returns(true).in_sequence(write_seq)
29
+ @downloader.download!(@uri, @tempfile)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
2
+
3
+ class HttpDownloaderTest < Test::Unit::TestCase
4
+ setup do
5
+ @downloader, @tempfile = mock_downloader(Tenderloin::Downloaders::HTTP)
6
+ @downloader.stubs(:report_progress)
7
+ @downloader.stubs(:complete_progress)
8
+ @uri = "foo.box"
9
+ end
10
+
11
+ context "downloading" do
12
+ setup do
13
+ @parsed_uri = mock("parsed")
14
+ URI.stubs(:parse).with(@uri).returns(@parsed_uri)
15
+ end
16
+
17
+ should "parse the URI and use that parsed URI for Net::HTTP" do
18
+ URI.expects(:parse).with(@uri).returns(@parsed_uri).once
19
+ Net::HTTP.expects(:get_response).with(@parsed_uri).once
20
+ @downloader.download!(@uri, @tempfile)
21
+ end
22
+
23
+ should "read the body of the response and place each segment into the file" do
24
+ response = mock("response")
25
+ response.stubs(:content_length)
26
+ segment = mock("segment")
27
+ segment.stubs(:length).returns(7)
28
+
29
+ Net::HTTP.stubs(:get_response).yields(response)
30
+ response.expects(:read_body).once.yields(segment)
31
+ @tempfile.expects(:write).with(segment).once
32
+
33
+ @downloader.download!(@uri, @tempfile)
34
+ end
35
+ end
36
+
37
+ context "reporting progress" do
38
+ # TODO: Testing for this, probably
39
+ end
40
+ end