apcera-stager-api 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.travis.yml +15 -0
  4. data/CHANGELOG.md +17 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +21 -0
  7. data/README.md +86 -0
  8. data/Rakefile +8 -0
  9. data/apcera-stager-api-contrib.gemspec +25 -0
  10. data/lib/apcera-stager-api.rb +1 -0
  11. data/lib/apcera/stager/error.rb +8 -0
  12. data/lib/apcera/stager/loader.rb +11 -0
  13. data/lib/apcera/stager/stager.rb +335 -0
  14. data/spec/apcera/stager/stager_spec.rb +571 -0
  15. data/spec/fixtures/cassettes/complete.yml +55 -0
  16. data/spec/fixtures/cassettes/dependencies_add.yml +101 -0
  17. data/spec/fixtures/cassettes/dependencies_remove.yml +101 -0
  18. data/spec/fixtures/cassettes/done.yml +28 -0
  19. data/spec/fixtures/cassettes/download.yml +37400 -0
  20. data/spec/fixtures/cassettes/environment_add.yml +53 -0
  21. data/spec/fixtures/cassettes/environment_remove.yml +53 -0
  22. data/spec/fixtures/cassettes/fail.yml +28 -0
  23. data/spec/fixtures/cassettes/invalid/complete.yml +55 -0
  24. data/spec/fixtures/cassettes/invalid/dependencies_add.yml +53 -0
  25. data/spec/fixtures/cassettes/invalid/dependencies_remove.yml +53 -0
  26. data/spec/fixtures/cassettes/invalid/done.yml +55 -0
  27. data/spec/fixtures/cassettes/invalid/download.yml +54 -0
  28. data/spec/fixtures/cassettes/invalid/environment_add.yml +53 -0
  29. data/spec/fixtures/cassettes/invalid/environment_remove.yml +53 -0
  30. data/spec/fixtures/cassettes/invalid/fail.yml +28 -0
  31. data/spec/fixtures/cassettes/invalid/metadata.yml +28 -0
  32. data/spec/fixtures/cassettes/invalid/provides_add.yml +53 -0
  33. data/spec/fixtures/cassettes/invalid/provides_remove.yml +53 -0
  34. data/spec/fixtures/cassettes/invalid/relaunch.yml +53 -0
  35. data/spec/fixtures/cassettes/invalid/snapshot.yml +53 -0
  36. data/spec/fixtures/cassettes/invalid/templates_add.yml +53 -0
  37. data/spec/fixtures/cassettes/invalid/templates_remove.yml +53 -0
  38. data/spec/fixtures/cassettes/invalid/upload.yml +55 -0
  39. data/spec/fixtures/cassettes/metadata.yml +51 -0
  40. data/spec/fixtures/cassettes/provides_add.yml +53 -0
  41. data/spec/fixtures/cassettes/provides_remove.yml +53 -0
  42. data/spec/fixtures/cassettes/relaunch.yml +28 -0
  43. data/spec/fixtures/cassettes/snapshot.yml +28 -0
  44. data/spec/fixtures/cassettes/templates_add.yml +53 -0
  45. data/spec/fixtures/cassettes/templates_remove.yml +53 -0
  46. data/spec/fixtures/cassettes/upload.yml +55 -0
  47. data/spec/spec_helper.rb +31 -0
  48. data/spec/tmp/.gitkeep +0 -0
  49. metadata +190 -0
@@ -0,0 +1,571 @@
1
+ require 'spec_helper'
2
+
3
+ describe Apcera::Stager do
4
+ before do
5
+ @appdir = "site"
6
+ @stager_url = "http://example.com"
7
+ end
8
+
9
+ describe "initialize" do
10
+ it "should raise an exception when initialized without a stager url" do
11
+ expect { Apcera::Stager.new }.to raise_error(Apcera::Error::StagerURLRequired)
12
+ end
13
+
14
+ it "should initialize with the stager url passed as an argument" do
15
+ stager = Apcera::Stager.new({:stager_url => @stager_url})
16
+ stager.class.should == Apcera::Stager
17
+ stager.stager_url.should == @stager_url
18
+ end
19
+
20
+ it "should initialize when the ENV variable STAGER_URL is present" do
21
+ begin
22
+ ENV["STAGER_URL"] = @stager_url
23
+ stager = Apcera::Stager.new
24
+ stager.class.should == Apcera::Stager
25
+ stager.stager_url.should == @stager_url
26
+ ensure
27
+ ENV["STAGER_URL"] = nil
28
+ end
29
+ end
30
+ end
31
+
32
+ context do
33
+ before do
34
+ @stager = Apcera::Stager.new({:stager_url => @stager_url})
35
+
36
+ # Best way to get our current path is to get the gem_dir!
37
+ spec = Gem::Specification.find_by_name("apcera-stager-api").gem_dir
38
+
39
+ # Lets write files in spec/tmp for tests!
40
+ @stager.root_path = File.join(spec, "spec", "tmp")
41
+ @stager.pkg_path = File.join(@stager.root_path, "pkg.tar.gz")
42
+ @stager.updated_pkg_path = File.join(@stager.root_path, "updated.tar.gz")
43
+ @stager.system_options = { :out => "/dev/null", :err => "/dev/null" }
44
+
45
+ # We don't want to exit in tests.
46
+ @stager.stub(:exit0r)
47
+ @stager.stub(:output_error)
48
+ @stager.stub(:output)
49
+ end
50
+
51
+ after do
52
+ # Don't trust urls above, they could be changed in tests.
53
+ # This does an actual delete and we want to be specific.
54
+ spec = Gem::Specification.find_by_name("apcera-stager-api").gem_dir
55
+ test_files = File.join(spec, "spec", "tmp", "*")
56
+
57
+ # Remove the test files.
58
+ FileUtils.rm_rf Dir.glob(test_files)
59
+ end
60
+
61
+ context "setup_chroot" do
62
+ it "should setup a working chrooted environment in /stagerfs" do
63
+ @stager.should_receive(:execute).with("sudo mkdir -p /stagerfs/etc")
64
+ @stager.should_receive(:execute).with("sudo cp /etc/resolv.conf /stagerfs/etc/resolv.conf")
65
+ @stager.should_receive(:execute).with("sudo mkdir -p /stagerfs/proc")
66
+ @stager.should_receive(:execute).with("sudo mount --bind /proc /stagerfs/proc")
67
+ @stager.should_receive(:execute).with("sudo mkdir -p /stagerfs/dev")
68
+ @stager.should_receive(:execute).with("sudo mount --rbind /dev /stagerfs/dev")
69
+
70
+ @stager.setup_chroot
71
+ end
72
+ end
73
+
74
+ context "download" do
75
+ it "should download the app package to pkg.tar.gz" do
76
+ VCR.use_cassette('download') do
77
+ @stager.download
78
+ end
79
+ File.exist?(@stager.pkg_path).should == true
80
+ end
81
+
82
+ it "should bubble errors to fail" do
83
+ @stager.should_receive(:exit0r).with(1) { raise }
84
+
85
+ VCR.use_cassette('invalid/download') do
86
+ expect { @stager.download }.to raise_error(Apcera::Error::DownloadError, "package download failed.\n")
87
+ end
88
+ end
89
+ end
90
+
91
+ context "extract" do
92
+ before do
93
+ VCR.use_cassette('download') do
94
+ @stager.download
95
+ end
96
+ end
97
+
98
+ it "should decompress the package to a supplied path" do
99
+ @stager.extract(@appdir)
100
+ expected_path = File.join(@stager.root_path, @appdir)
101
+ File.exist?(expected_path).should == true
102
+ @stager.app_path.should == expected_path
103
+ end
104
+
105
+ it "should bubble errors to fail" do
106
+ @stager.should_receive(:exit0r).with(1) { raise }
107
+
108
+ err = Apcera::Error::ExecuteError.new
109
+ @stager.should_receive(:execute_app).with("tar -zxf #{@stager.pkg_path}").and_raise(err)
110
+
111
+ expect { @stager.extract(@appdir) }.to raise_error(err.class)
112
+ end
113
+ end
114
+
115
+ context "execute" do
116
+ before do
117
+ VCR.use_cassette('download') do
118
+ @stager.download
119
+ end
120
+
121
+ @stager.extract(@appdir)
122
+ end
123
+
124
+ it "should execute commands with clean bundler environment" do
125
+ Bundler.should_receive(:with_clean_env).at_least(:once).and_yield
126
+
127
+ @stager.execute("cat thing").should == nil
128
+ @stager.execute("cat #{File.join(@stager.app_path, "app", "Gemfile")}").should == true
129
+ end
130
+
131
+ it "should bubble errors to fail" do
132
+ @stager.should_receive(:exit0r).with(1) { raise }
133
+
134
+ cmd = "cat thing"
135
+ expect {@stager.execute(cmd) }.to raise_error(Apcera::Error::ExecuteError, "failed to execute: #{cmd}.\n")
136
+ end
137
+ end
138
+
139
+ context "execute_app" do
140
+ before do
141
+ VCR.use_cassette('download') do
142
+ @stager.download
143
+ end
144
+ end
145
+
146
+ it "should execute commands in app dir with clean bundler environment" do
147
+ Bundler.should_receive(:with_clean_env).at_least(:once).and_yield
148
+
149
+ @stager.extract(@appdir)
150
+
151
+ @stager.execute_app("cat thing").should == nil
152
+ @stager.execute_app("cat #{File.join("app", "Gemfile")}").should == true
153
+ end
154
+
155
+ it "should bubble errors to fail when app path is missing (no extract)" do
156
+ @stager.should_receive(:exit0r).with(1) { raise }
157
+
158
+ cmd = "cat thing"
159
+ expect {@stager.execute_app(cmd) }.to raise_error(Apcera::Error::AppPathError, "app path not set, please run extract!\n")
160
+ end
161
+
162
+ it "should bubble errors to fail" do
163
+ @stager.should_receive(:exit0r).with(1) { raise }
164
+
165
+ @stager.extract(@appdir)
166
+
167
+ cmd = "cat thing"
168
+ expect {@stager.execute_app(cmd) }.to raise_error(Apcera::Error::ExecuteError, "failed to execute: #{cmd}.\n")
169
+ end
170
+ end
171
+
172
+ context "upload" do
173
+ it "should compress a new package and send to the staging coordinator" do
174
+ VCR.use_cassette('download') do
175
+ @stager.download
176
+ end
177
+
178
+ @stager.extract(@appdir)
179
+
180
+ VCR.use_cassette('upload') do
181
+ @stager.upload
182
+ end
183
+
184
+ File.exist?(@stager.updated_pkg_path).should be_true
185
+ end
186
+
187
+ it "should compress using tar czf" do
188
+ VCR.use_cassette('download') do
189
+ @stager.download
190
+ end
191
+
192
+ @stager.extract(@appdir)
193
+
194
+ @stager.should_receive(:execute_app).with("cd #{@stager.app_path}/.. && tar czf #{@stager.updated_pkg_path} #{@appdir}").and_return
195
+
196
+ @stager.upload
197
+ end
198
+
199
+ it "should upload the original package when the app wasn't extracted" do
200
+ VCR.use_cassette('download') do
201
+ @stager.download
202
+ end
203
+
204
+ @stager.should_not_receive(:download)
205
+ @stager.should_receive(:upload_file).with(@stager.pkg_path).and_return
206
+
207
+ @stager.upload
208
+ end
209
+
210
+ it "should upload the original package when the app wasn't downloaded or extracted" do
211
+ @stager.should_receive(:download).and_return
212
+ @stager.should_receive(:upload_file).with(@stager.pkg_path).and_return
213
+
214
+ @stager.upload
215
+ end
216
+
217
+ it "should bubble errors to fail" do
218
+ VCR.use_cassette('download') do
219
+ @stager.download
220
+ end
221
+
222
+ @stager.should_receive(:exit0r).with(1) { raise }
223
+
224
+ @stager.extract(@appdir)
225
+
226
+ VCR.use_cassette('invalid/upload') do
227
+ expect { @stager.upload }.to raise_error(RestClient::ResourceNotFound, "404 Resource Not Found")
228
+ end
229
+ end
230
+ end
231
+
232
+ context "complete" do
233
+ before do
234
+ VCR.use_cassette('download') do
235
+ @stager.download
236
+ end
237
+
238
+ @stager.extract(@appdir)
239
+ end
240
+
241
+ it "should compress a new package and send to the staging coordinator then be done" do
242
+ VCR.use_cassette('complete') do
243
+ @stager.complete
244
+ end
245
+
246
+ File.exist?(File.join(@stager.root_path, "#{@appdir}.tar.gz"))
247
+ end
248
+
249
+ it "should bubble errors to fail" do
250
+ @stager.should_receive(:exit0r).with(1) { raise }
251
+
252
+ VCR.use_cassette('invalid/complete') do
253
+ expect { @stager.complete }.to raise_error(RestClient::ResourceNotFound, "404 Resource Not Found")
254
+ end
255
+ end
256
+ end
257
+
258
+ context "done" do
259
+ it "should send done to the staging coordinator" do
260
+ @stager.should_receive(:exit0r).with(0)
261
+
262
+ VCR.use_cassette('done') do
263
+ @stager.done
264
+ end
265
+ end
266
+
267
+ it "should bubble errors to fail" do
268
+ @stager.should_receive(:exit0r).with(1) { raise }
269
+
270
+ VCR.use_cassette('invalid/done') do
271
+ expect { @stager.done }.to raise_error(RestClient::ResourceNotFound, "404 Resource Not Found")
272
+ end
273
+ end
274
+ end
275
+
276
+ context "snapshot" do
277
+ before do
278
+ VCR.use_cassette('download') do
279
+ @stager.download
280
+ end
281
+
282
+ @stager.extract(@appdir)
283
+ end
284
+
285
+ it "should send a snapshot request to the staging coordinator" do
286
+
287
+ VCR.use_cassette('snapshot') do
288
+ @stager.snapshot
289
+ end
290
+ VCR.use_cassette('done') do
291
+ @stager.done
292
+ end
293
+ end
294
+
295
+ it "should bubble errors to fail" do
296
+ @stager.should_receive(:exit0r).with(1) { raise }
297
+
298
+ VCR.use_cassette('invalid/snapshot') do
299
+ expect { @stager.snapshot }.to raise_error(RestClient::ResourceNotFound, "404 Resource Not Found")
300
+ end
301
+ end
302
+ end
303
+
304
+ context "fail" do
305
+ it "should have a fail hook that exits with exit code 1" do
306
+ # Make sure we don't exit and that we called exit 1.
307
+ @stager.should_receive(:exit0r).with(1)
308
+
309
+ VCR.use_cassette('fail') do
310
+ @stager.fail.should == "OK"
311
+ end
312
+ end
313
+ end
314
+
315
+ context "meta" do
316
+ it "should recieve package metadata and cache it" do
317
+ VCR.use_cassette('metadata') do
318
+ @stager.meta.class.should == Hash
319
+ end
320
+ end
321
+
322
+ it "should throw errors" do
323
+ VCR.use_cassette('invalid/metadata') do
324
+ expect { @stager.meta }.to raise_error(RestClient::ResourceNotFound, "404 Resource Not Found")
325
+ end
326
+ end
327
+ end
328
+
329
+ context "relaunch" do
330
+ it "should allow you to trigger a stager relaunch" do
331
+ @stager.should_receive(:exit0r).with(0) { 0 }
332
+
333
+ VCR.use_cassette('relaunch') do
334
+ @stager.relaunch.should == 0
335
+ end
336
+ end
337
+
338
+ it "should bubble errors to fail" do
339
+ @stager.should_receive(:exit0r).with(1) { raise }
340
+
341
+ VCR.use_cassette('invalid/relaunch') do
342
+ expect { @stager.relaunch }.to raise_error(RestClient::ResourceNotFound, "404 Resource Not Found")
343
+ end
344
+ end
345
+ end
346
+
347
+ context "start_command" do
348
+ it "should return the package start command" do
349
+ VCR.use_cassette('metadata') do
350
+ @stager.start_command.should == "./startme"
351
+ end
352
+ end
353
+
354
+ it "should allow you to set the start command" do
355
+ VCR.use_cassette('environment_add') do
356
+ @stager.start_command = "./startme"
357
+ end
358
+ end
359
+ end
360
+
361
+ context "start_path" do
362
+ it "should return the package start path" do
363
+ VCR.use_cassette('metadata') do
364
+ @stager.start_path.should == "/app"
365
+ end
366
+ end
367
+
368
+ it "should allow you to set the start path" do
369
+ VCR.use_cassette('environment_add') do
370
+ @stager.start_path = "/app"
371
+ end
372
+ end
373
+ end
374
+
375
+ context "environment_add" do
376
+ it "should add an environment variable" do
377
+ VCR.use_cassette('environment_add') do
378
+ @stager.environment_add("TEST_VAR", "foo")
379
+ end
380
+ end
381
+
382
+ it "should bubble errors to fail" do
383
+ @stager.should_receive(:exit0r).with(1) { raise }
384
+
385
+ VCR.use_cassette('invalid/environment_add') do
386
+ expect { @stager.environment_add("TEST_VAR", "foo") }.to raise_error
387
+ end
388
+ end
389
+ end
390
+
391
+ context "environment_remove" do
392
+ it "should environment_remove an environment variable" do
393
+ VCR.use_cassette('environment_remove') do
394
+ @stager.environment_remove("TEST_VAR")
395
+ end
396
+ end
397
+
398
+ it "should bubble errors to fail" do
399
+ @stager.should_receive(:exit0r).with(1) { raise }
400
+
401
+ VCR.use_cassette('invalid/environment_remove') do
402
+ expect { @stager.environment_remove("TEST_VAR") }.to raise_error
403
+ end
404
+ end
405
+ end
406
+
407
+ context "provides_add" do
408
+ it "should add to its list of provides" do
409
+ VCR.use_cassette('provides_add') do
410
+ @stager.provides_add("os", "linux")
411
+ end
412
+ end
413
+
414
+ it "should bubble errors to fail" do
415
+ @stager.should_receive(:exit0r).with(1) { raise }
416
+
417
+ VCR.use_cassette('invalid/provides_add') do
418
+ expect { @stager.provides_add("os", "linux") }.to raise_error
419
+ end
420
+ end
421
+ end
422
+
423
+ context "provides_remove" do
424
+ it "should remove from its list of provides" do
425
+ VCR.use_cassette('provides_remove') do
426
+ @stager.provides_remove("os", "linux")
427
+ end
428
+ end
429
+
430
+ it "should bubble errors to fail" do
431
+ @stager.should_receive(:exit0r).with(1) { raise }
432
+
433
+ VCR.use_cassette('invalid/provides_remove') do
434
+ expect { @stager.provides_remove("os", "linux") }.to raise_error
435
+ end
436
+ end
437
+ end
438
+
439
+ context "dependencies_add" do
440
+ it "should add to its list of dependencies" do
441
+ VCR.use_cassette('dependencies_add') do
442
+ @stager.dependencies_add("os", "someos").should == true
443
+ end
444
+ end
445
+
446
+ it "should return false if the dependency is already there" do
447
+ VCR.use_cassette('dependencies_add') do
448
+ @stager.dependencies_add("os", "linux").should == false
449
+ end
450
+ end
451
+
452
+ it "should bubble errors to fail" do
453
+ @stager.should_receive(:exit0r).with(1) { raise }
454
+
455
+ VCR.use_cassette('invalid/dependencies_add') do
456
+ expect { @stager.dependencies_add("os", "linux") }.to raise_error
457
+ end
458
+ end
459
+ end
460
+
461
+ context "dependencies_remove" do
462
+ it "should remove from its list of dependencies" do
463
+ VCR.use_cassette('dependencies_remove') do
464
+ @stager.dependencies_remove("os", "linux")
465
+ end
466
+ end
467
+
468
+ it "should return false if the dependency doesn't exist" do
469
+ VCR.use_cassette('dependencies_remove') do
470
+ @stager.dependencies_remove("os", "someos").should == false
471
+ end
472
+ end
473
+
474
+ it "should bubble errors to fail" do
475
+ @stager.should_receive(:exit0r).with(1) { raise }
476
+
477
+ VCR.use_cassette('invalid/dependencies_remove') do
478
+ expect { @stager.dependencies_remove("os", "linux") }.to raise_error
479
+ end
480
+ end
481
+ end
482
+
483
+ context "templates_add" do
484
+ it "should add to its list of templates" do
485
+ VCR.use_cassette('templates_add') do
486
+ @stager.templates_add("/path/to/template")
487
+ end
488
+ end
489
+
490
+ it "should add to its list of templates with delimiters" do
491
+ VCR.use_cassette('templates_add') do
492
+ @stager.templates_add("/path/to/template", "{{", "}}")
493
+ end
494
+ end
495
+
496
+ it "should bubble errors to fail" do
497
+ @stager.should_receive(:exit0r).with(1) { raise }
498
+
499
+ VCR.use_cassette('invalid/templates_add') do
500
+ expect { @stager.templates_add("/path/to/template") }.to raise_error
501
+ end
502
+ end
503
+ end
504
+
505
+ context "templates_remove" do
506
+ it "should remove from its list of templates" do
507
+ VCR.use_cassette('templates_remove') do
508
+ @stager.templates_remove("/path/to/template")
509
+ end
510
+ end
511
+
512
+ it "should remove from its list of templates with delimiters" do
513
+ VCR.use_cassette('templates_remove') do
514
+ @stager.templates_remove("/path/to/template", "{{", "}}")
515
+ end
516
+ end
517
+
518
+ it "should bubble errors to fail" do
519
+ @stager.should_receive(:exit0r).with(1) { raise }
520
+
521
+ VCR.use_cassette('invalid/templates_remove') do
522
+ expect { @stager.templates_remove("/path/to/template") }.to raise_error
523
+ end
524
+ end
525
+ end
526
+
527
+ context "output" do
528
+ before do
529
+ @stager.unstub(:output)
530
+ end
531
+
532
+ it "should print to stdout" do
533
+ $stdout.should_receive(:puts).with("test")
534
+ @stager.output("test")
535
+ end
536
+ end
537
+
538
+ context "output_error" do
539
+ before do
540
+ @stager.unstub(:output_error)
541
+ end
542
+
543
+ it "should print to stderr" do
544
+ $stderr.should_receive(:puts).with("test")
545
+ @stager.output_error("test")
546
+ end
547
+ end
548
+
549
+ context "exit0r" do
550
+ before do
551
+ @stager.unstub(:exit0r)
552
+ end
553
+
554
+ it "should wrap successful exit" do
555
+ begin
556
+ @stager.exit0r(0)
557
+ rescue SystemExit => e
558
+ e.status.should == 0
559
+ end
560
+ end
561
+
562
+ it "should wrap errored exit" do
563
+ begin
564
+ @stager.exit0r(1)
565
+ rescue SystemExit => e
566
+ e.status.should == 1
567
+ end
568
+ end
569
+ end
570
+ end
571
+ end