bosh_cli 0.16
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.
- data/README +4 -0
- data/Rakefile +55 -0
- data/bin/bosh +17 -0
- data/lib/cli.rb +76 -0
- data/lib/cli/cache.rb +44 -0
- data/lib/cli/changeset_helper.rb +142 -0
- data/lib/cli/command_definition.rb +52 -0
- data/lib/cli/commands/base.rb +245 -0
- data/lib/cli/commands/biff.rb +300 -0
- data/lib/cli/commands/blob.rb +125 -0
- data/lib/cli/commands/cloudcheck.rb +169 -0
- data/lib/cli/commands/deployment.rb +147 -0
- data/lib/cli/commands/job.rb +42 -0
- data/lib/cli/commands/job_management.rb +117 -0
- data/lib/cli/commands/log_management.rb +81 -0
- data/lib/cli/commands/maintenance.rb +131 -0
- data/lib/cli/commands/misc.rb +240 -0
- data/lib/cli/commands/package.rb +112 -0
- data/lib/cli/commands/property_management.rb +125 -0
- data/lib/cli/commands/release.rb +469 -0
- data/lib/cli/commands/ssh.rb +271 -0
- data/lib/cli/commands/stemcell.rb +184 -0
- data/lib/cli/commands/task.rb +213 -0
- data/lib/cli/commands/user.rb +28 -0
- data/lib/cli/commands/vms.rb +53 -0
- data/lib/cli/config.rb +154 -0
- data/lib/cli/core_ext.rb +145 -0
- data/lib/cli/dependency_helper.rb +62 -0
- data/lib/cli/deployment_helper.rb +263 -0
- data/lib/cli/deployment_manifest_compiler.rb +28 -0
- data/lib/cli/director.rb +633 -0
- data/lib/cli/director_task.rb +64 -0
- data/lib/cli/errors.rb +48 -0
- data/lib/cli/event_log_renderer.rb +351 -0
- data/lib/cli/job_builder.rb +226 -0
- data/lib/cli/package_builder.rb +254 -0
- data/lib/cli/packaging_helper.rb +248 -0
- data/lib/cli/release.rb +176 -0
- data/lib/cli/release_builder.rb +215 -0
- data/lib/cli/release_compiler.rb +178 -0
- data/lib/cli/release_tarball.rb +272 -0
- data/lib/cli/runner.rb +771 -0
- data/lib/cli/stemcell.rb +83 -0
- data/lib/cli/task_log_renderer.rb +40 -0
- data/lib/cli/templates/help_message.erb +75 -0
- data/lib/cli/validation.rb +42 -0
- data/lib/cli/version.rb +7 -0
- data/lib/cli/version_calc.rb +48 -0
- data/lib/cli/versions_index.rb +126 -0
- data/lib/cli/yaml_helper.rb +62 -0
- data/spec/assets/biff/bad_gateway_config.yml +28 -0
- data/spec/assets/biff/good_simple_config.yml +63 -0
- data/spec/assets/biff/good_simple_golden_config.yml +63 -0
- data/spec/assets/biff/good_simple_template.erb +69 -0
- data/spec/assets/biff/multiple_subnets_config.yml +40 -0
- data/spec/assets/biff/network_only_template.erb +34 -0
- data/spec/assets/biff/no_cc_config.yml +27 -0
- data/spec/assets/biff/no_range_config.yml +27 -0
- data/spec/assets/biff/no_subnet_config.yml +16 -0
- data/spec/assets/biff/ok_network_config.yml +30 -0
- data/spec/assets/biff/properties_template.erb +6 -0
- data/spec/assets/deployment.MF +0 -0
- data/spec/assets/plugins/bosh/cli/commands/echo.rb +43 -0
- data/spec/assets/plugins/bosh/cli/commands/ruby.rb +24 -0
- data/spec/assets/release/jobs/cacher.tgz +0 -0
- data/spec/assets/release/jobs/cacher/config/file1.conf +0 -0
- data/spec/assets/release/jobs/cacher/config/file2.conf +0 -0
- data/spec/assets/release/jobs/cacher/job.MF +6 -0
- data/spec/assets/release/jobs/cacher/monit +1 -0
- data/spec/assets/release/jobs/cleaner.tgz +0 -0
- data/spec/assets/release/jobs/cleaner/job.MF +4 -0
- data/spec/assets/release/jobs/cleaner/monit +1 -0
- data/spec/assets/release/jobs/sweeper.tgz +0 -0
- data/spec/assets/release/jobs/sweeper/config/test.conf +1 -0
- data/spec/assets/release/jobs/sweeper/job.MF +5 -0
- data/spec/assets/release/jobs/sweeper/monit +1 -0
- data/spec/assets/release/packages/mutator.tar.gz +0 -0
- data/spec/assets/release/packages/stuff.tgz +0 -0
- data/spec/assets/release/release.MF +17 -0
- data/spec/assets/release_invalid_checksum.tgz +0 -0
- data/spec/assets/release_invalid_jobs.tgz +0 -0
- data/spec/assets/release_no_name.tgz +0 -0
- data/spec/assets/release_no_version.tgz +0 -0
- data/spec/assets/stemcell/image +1 -0
- data/spec/assets/stemcell/stemcell.MF +6 -0
- data/spec/assets/stemcell_invalid_mf.tgz +0 -0
- data/spec/assets/stemcell_no_image.tgz +0 -0
- data/spec/assets/valid_release.tgz +0 -0
- data/spec/assets/valid_stemcell.tgz +0 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/unit/base_command_spec.rb +66 -0
- data/spec/unit/biff_spec.rb +135 -0
- data/spec/unit/cache_spec.rb +36 -0
- data/spec/unit/cli_commands_spec.rb +481 -0
- data/spec/unit/config_spec.rb +139 -0
- data/spec/unit/core_ext_spec.rb +77 -0
- data/spec/unit/dependency_helper_spec.rb +52 -0
- data/spec/unit/deployment_manifest_compiler_spec.rb +63 -0
- data/spec/unit/director_spec.rb +511 -0
- data/spec/unit/director_task_spec.rb +48 -0
- data/spec/unit/event_log_renderer_spec.rb +171 -0
- data/spec/unit/hash_changeset_spec.rb +73 -0
- data/spec/unit/job_builder_spec.rb +454 -0
- data/spec/unit/package_builder_spec.rb +567 -0
- data/spec/unit/release_builder_spec.rb +65 -0
- data/spec/unit/release_spec.rb +66 -0
- data/spec/unit/release_tarball_spec.rb +33 -0
- data/spec/unit/runner_spec.rb +140 -0
- data/spec/unit/ssh_spec.rb +78 -0
- data/spec/unit/stemcell_spec.rb +17 -0
- data/spec/unit/version_calc_spec.rb +27 -0
- data/spec/unit/versions_index_spec.rb +132 -0
- metadata +338 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
describe Bosh::Cli::DirectorTask do
|
|
6
|
+
|
|
7
|
+
before :each do
|
|
8
|
+
@director = Bosh::Cli::Director.new("http://target", "user", "pass")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "tracks partial output responses from director" do
|
|
12
|
+
@task = Bosh::Cli::DirectorTask.new(@director, 10)
|
|
13
|
+
|
|
14
|
+
@director.stub!(:get).
|
|
15
|
+
with("/tasks/10/output", nil, nil, "Range" => "bytes=0-").
|
|
16
|
+
and_return([206, "test\nout", { :content_range => "bytes 0-7/100" }])
|
|
17
|
+
|
|
18
|
+
@director.stub!(:get).
|
|
19
|
+
with("/tasks/10/output", nil, nil, "Range" => "bytes=8-").
|
|
20
|
+
and_return([206, "put", { :content_range => "bytes 8-10/100" }])
|
|
21
|
+
|
|
22
|
+
@director.stub!(:get).
|
|
23
|
+
with("/tasks/10/output", nil, nil, "Range" => "bytes=11-").
|
|
24
|
+
and_return([206, " success\n", { :content_range => "bytes 11-19/100" }])
|
|
25
|
+
|
|
26
|
+
@director.stub!(:get).
|
|
27
|
+
with("/tasks/10/output", nil, nil, "Range" => "bytes=20-").
|
|
28
|
+
and_return([416, "done", {}])
|
|
29
|
+
|
|
30
|
+
@task.output.should == "test\n"
|
|
31
|
+
@task.output.should == nil # No newline yet
|
|
32
|
+
@task.output.should == "output success\n" # Got a newline
|
|
33
|
+
@task.output.should == "done\n" # Flushed
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "supports explicit output flush" do
|
|
37
|
+
@task = Bosh::Cli::DirectorTask.new(@director, 10)
|
|
38
|
+
|
|
39
|
+
@director.stub!(:get).
|
|
40
|
+
with("/tasks/10/output", nil, nil, "Range" => "bytes=0-").
|
|
41
|
+
and_return([206, "test\nout", { :content_range => "bytes 0-7/100" }])
|
|
42
|
+
|
|
43
|
+
@task.output.should == "test\n"
|
|
44
|
+
@task.flush_output.should == "out\n"
|
|
45
|
+
# Nothing in buffer at this point
|
|
46
|
+
@task.flush_output.should == nil
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
describe Bosh::Cli::EventLogRenderer do
|
|
6
|
+
|
|
7
|
+
def make_event(stage, task, index, total,
|
|
8
|
+
state = "started", tags = [], progress = 0)
|
|
9
|
+
event = {
|
|
10
|
+
"time" => Time.now.to_i,
|
|
11
|
+
"stage" => stage,
|
|
12
|
+
"task" => task,
|
|
13
|
+
"index" => index,
|
|
14
|
+
"total" => total,
|
|
15
|
+
"state" => state,
|
|
16
|
+
"tags" => tags,
|
|
17
|
+
"progress" => progress
|
|
18
|
+
}
|
|
19
|
+
JSON.generate(event)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def make_renderer(*args)
|
|
23
|
+
Bosh::Cli::EventLogRenderer.new(*args)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "allows adding events" do
|
|
27
|
+
renderer = make_renderer
|
|
28
|
+
renderer.add_event(make_event("Preparing", "Binding release",
|
|
29
|
+
1, 9, "started"))
|
|
30
|
+
renderer.add_event(make_event("Preparing", "Binding existing deployment",
|
|
31
|
+
2, 9, "started"))
|
|
32
|
+
renderer.events_count.should == 2
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "silently ignores malformed events" do
|
|
36
|
+
renderer = make_renderer
|
|
37
|
+
renderer.add_event(make_event(nil, "Binding release", 1, 9, nil, []))
|
|
38
|
+
renderer.add_event(make_event("Preparing", "Binding existing deployment",
|
|
39
|
+
2, nil, nil))
|
|
40
|
+
renderer.add_event(JSON.generate("a" => "b"))
|
|
41
|
+
renderer.events_count.should == 0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "sets current stage based on the most recent event " +
|
|
45
|
+
"but ignores events from non-current stages" do
|
|
46
|
+
renderer = make_renderer
|
|
47
|
+
renderer.add_event(make_event("Preparing", "Binding release", 1, 9))
|
|
48
|
+
renderer.current_stage.should == "Preparing"
|
|
49
|
+
renderer.add_event(make_event("Preparing", "Binding existing deployment",
|
|
50
|
+
2, 9))
|
|
51
|
+
renderer.current_stage.should == "Preparing"
|
|
52
|
+
renderer.add_event(make_event("Updating resource pool",
|
|
53
|
+
"Deleting outdated VM", 1, 5))
|
|
54
|
+
renderer.current_stage.should == "Updating resource pool"
|
|
55
|
+
renderer.events_count.should == 3
|
|
56
|
+
renderer.add_event(make_event("Preparing", "Some additional stuff", 3, 9))
|
|
57
|
+
renderer.current_stage.should == "Updating resource pool"
|
|
58
|
+
renderer.events_count.should == 3
|
|
59
|
+
renderer.add_event(make_event("Updating job router", "Canary update", 1, 1))
|
|
60
|
+
renderer.current_stage.should == "Updating job router"
|
|
61
|
+
renderer.events_count.should == 4
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "can render event log with progress bar" do
|
|
65
|
+
buf = StringIO.new
|
|
66
|
+
Bosh::Cli::Config.output = buf
|
|
67
|
+
|
|
68
|
+
renderer = make_renderer
|
|
69
|
+
renderer.add_event(make_event("Preparing", "Binding release", 1, 9))
|
|
70
|
+
|
|
71
|
+
lines = renderer.render.split("\n")
|
|
72
|
+
|
|
73
|
+
lines.count.should == 3
|
|
74
|
+
lines[1].should == "Preparing"
|
|
75
|
+
lines[2].should =~ /Binding release/
|
|
76
|
+
lines[2].should =~ /\|\s+\| 0\/9/
|
|
77
|
+
|
|
78
|
+
renderer.add_event(make_event("Preparing", "Moving stuff", 2, 9))
|
|
79
|
+
|
|
80
|
+
lines = renderer.render.split("\n")
|
|
81
|
+
lines.count.should == 1
|
|
82
|
+
lines[0].should =~ /Binding release/
|
|
83
|
+
lines[0].should =~ /\|\s+\| 0\/9/
|
|
84
|
+
|
|
85
|
+
renderer.add_event(make_event("Preparing", "Moving stuff",
|
|
86
|
+
2, 9, "finished"))
|
|
87
|
+
|
|
88
|
+
lines = renderer.render.split("\n")
|
|
89
|
+
lines.count.should == 2
|
|
90
|
+
lines[0].should =~ /moving stuff/
|
|
91
|
+
lines[1].should =~ /Binding release/
|
|
92
|
+
lines[1].should =~ /\|o+\s+\| 1\/9/
|
|
93
|
+
|
|
94
|
+
# throwing in out-of-order event
|
|
95
|
+
renderer.add_event(make_event("Preparing", "Binding release",
|
|
96
|
+
1, 9, "finished"))
|
|
97
|
+
|
|
98
|
+
(3..9).each do |i|
|
|
99
|
+
renderer.add_event(make_event("Preparing", "event #{i}",
|
|
100
|
+
i, 9))
|
|
101
|
+
renderer.add_event(make_event("Preparing", "event #{i}",
|
|
102
|
+
i, 9, "finished"))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
lines = renderer.render.split("\n")
|
|
106
|
+
lines.count.should == 9
|
|
107
|
+
lines[-1].should =~ /\|o+\| 9\/9/
|
|
108
|
+
|
|
109
|
+
renderer.add_event(make_event("Updating", "prepare update",
|
|
110
|
+
1, 2, "started", ["stuff", "thing"]))
|
|
111
|
+
|
|
112
|
+
lines = renderer.render.split("\n")
|
|
113
|
+
lines.count.should == 4
|
|
114
|
+
|
|
115
|
+
lines[0].should =~ /Done/
|
|
116
|
+
lines[0].should =~ /9\/9/
|
|
117
|
+
lines[1].should == ""
|
|
118
|
+
lines[2].should == "Updating %s" % ["stuff, thing".green]
|
|
119
|
+
lines[3].should =~ /0\/2/
|
|
120
|
+
|
|
121
|
+
lines = renderer.finish(:done).split("\n")
|
|
122
|
+
lines[0].should =~ /Done/
|
|
123
|
+
lines[0].should =~ /2\/2/
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "renders erorr state properly" do
|
|
127
|
+
buf = StringIO.new
|
|
128
|
+
Bosh::Cli::Config.output = buf
|
|
129
|
+
|
|
130
|
+
renderer = make_renderer
|
|
131
|
+
renderer.add_event(make_event("Preparing", "Binding release", 1, 9))
|
|
132
|
+
renderer.add_event(make_event("Preparing", "Moving stuff", 2, 9))
|
|
133
|
+
renderer.add_event(make_event("Preparing", "Moving stuff", 2, 9, "finished"))
|
|
134
|
+
renderer.add_event(make_event("Updating", "prepare update",
|
|
135
|
+
1, 2, "started", ["stuff", "thing"]))
|
|
136
|
+
|
|
137
|
+
lines = renderer.finish(:error).split("\n")
|
|
138
|
+
lines[-1].should =~ /Error/
|
|
139
|
+
lines[-1].should =~ /0\/2/
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "supports tracking individual tasks progress" do
|
|
143
|
+
renderer = make_renderer
|
|
144
|
+
renderer.add_event(make_event("Preparing", "Binding release",
|
|
145
|
+
1, 2, "started", [], 0))
|
|
146
|
+
renderer.add_event(make_event("Preparing", "Binding release",
|
|
147
|
+
1, 2, "in_progress", [], 25))
|
|
148
|
+
|
|
149
|
+
lines = renderer.render.split("\n")
|
|
150
|
+
|
|
151
|
+
lines[1].should =~ /Preparing/
|
|
152
|
+
lines[2].should =~ /\|o+\s+\| 0\/2/
|
|
153
|
+
lines[2].should =~ /Binding release/
|
|
154
|
+
|
|
155
|
+
renderer.add_event(make_event("Preparing", "Binding release",
|
|
156
|
+
1, 2, "in_progress", [], 50))
|
|
157
|
+
|
|
158
|
+
lines = renderer.render.split("\n")
|
|
159
|
+
|
|
160
|
+
lines[0].should =~ /\|o+\s+\| 0\/2/
|
|
161
|
+
lines[0].should =~ /Binding release/
|
|
162
|
+
|
|
163
|
+
renderer.add_event(make_event("Preparing", "Binding release",
|
|
164
|
+
1, 2, "finished", []))
|
|
165
|
+
|
|
166
|
+
lines = renderer.render.split("\n")
|
|
167
|
+
lines[1].should_not =~ /Binding release/
|
|
168
|
+
lines[1].should =~ /\|o+\s+\| 1\/2/
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
describe Bosh::Cli::HashChangeset do
|
|
6
|
+
|
|
7
|
+
def changeset(m1, m2)
|
|
8
|
+
cs = Bosh::Cli::HashChangeset.new
|
|
9
|
+
cs.add_hash(m1, :old)
|
|
10
|
+
cs.add_hash(m2, :new)
|
|
11
|
+
cs
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "contains changeset for two hashes" do
|
|
15
|
+
m1 = {
|
|
16
|
+
:foo => {
|
|
17
|
+
:bar => 1,
|
|
18
|
+
:baz => "purr",
|
|
19
|
+
:nc => "nc",
|
|
20
|
+
:properties => {
|
|
21
|
+
:a => "1",
|
|
22
|
+
:b => "2",
|
|
23
|
+
:c => "3"
|
|
24
|
+
},
|
|
25
|
+
:arr => %w(a b c)
|
|
26
|
+
},
|
|
27
|
+
:arr => [1, 2, 3],
|
|
28
|
+
:zb => { :a => 2, :b => 3}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
m2 = {
|
|
32
|
+
:foo => {
|
|
33
|
+
:bar => 2,
|
|
34
|
+
:baz => "meow",
|
|
35
|
+
:nc => "nc",
|
|
36
|
+
:properties => {
|
|
37
|
+
:a => "2",
|
|
38
|
+
:d => "7",
|
|
39
|
+
:c => 3
|
|
40
|
+
},
|
|
41
|
+
:arr => %w(a b c d e)
|
|
42
|
+
},
|
|
43
|
+
:arr => [1, 2, 3],
|
|
44
|
+
:zb => "test"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
mc = changeset(m1, m2)
|
|
48
|
+
|
|
49
|
+
mc[:foo][:bar].changed?.should be_true
|
|
50
|
+
mc[:foo][:bar].old.should == 1
|
|
51
|
+
mc[:foo][:bar].new.should == 2
|
|
52
|
+
|
|
53
|
+
mc[:foo][:baz].changed?.should be_true
|
|
54
|
+
mc[:foo][:baz].old.should == "purr"
|
|
55
|
+
mc[:foo][:baz].new.should == "meow"
|
|
56
|
+
|
|
57
|
+
mc[:foo][:nc].changed?.should be_false
|
|
58
|
+
mc[:foo][:nc].same?.should be_true
|
|
59
|
+
mc[:foo][:nc].old.should == "nc"
|
|
60
|
+
mc[:foo][:nc].new.should == "nc"
|
|
61
|
+
|
|
62
|
+
mc[:foo][:properties][:a].changed?.should be_true
|
|
63
|
+
mc[:foo][:properties][:b].removed?.should be_true
|
|
64
|
+
mc[:foo][:properties][:c].mismatch?.should be_true
|
|
65
|
+
mc[:foo][:properties][:d].added?.should be_true
|
|
66
|
+
|
|
67
|
+
mc[:foo][:arr].changed?.should be_true
|
|
68
|
+
mc[:arr].same?.should be_true
|
|
69
|
+
|
|
70
|
+
mc[:zb].mismatch?.should be_true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
describe Bosh::Cli::JobBuilder do
|
|
6
|
+
|
|
7
|
+
before(:each) do
|
|
8
|
+
@release_dir = Dir.mktmpdir
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def new_builder(name, packages = [], templates = { }, built_packages = [],
|
|
12
|
+
create_spec = true, final = false, blobstore = mock("blobstore"))
|
|
13
|
+
# Workaround for Hash requirement
|
|
14
|
+
if templates.is_a?(Array)
|
|
15
|
+
templates = templates.inject({ }) { |h, e| h[e] = e; h }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
spec = {
|
|
19
|
+
"name" => name,
|
|
20
|
+
"packages" => packages,
|
|
21
|
+
"templates" => templates
|
|
22
|
+
}
|
|
23
|
+
add_spec(name) if create_spec
|
|
24
|
+
|
|
25
|
+
Bosh::Cli::JobBuilder.new(spec, @release_dir,
|
|
26
|
+
final, blobstore, built_packages)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def add_file(job_name, file, contents = nil)
|
|
30
|
+
job_dir = File.join(@release_dir, "jobs", job_name)
|
|
31
|
+
file_path = File.join(job_dir, file)
|
|
32
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
|
33
|
+
FileUtils.touch(file_path)
|
|
34
|
+
if contents
|
|
35
|
+
File.open(file_path, "w") { |f| f.write(contents) }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def add_spec(job_name)
|
|
40
|
+
add_file(job_name, "spec")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def add_monit(job_name, file="monit")
|
|
44
|
+
add_file(job_name, file)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def add_templates(job_name, *files)
|
|
48
|
+
job_template_path = File.join(@release_dir, "jobs", job_name, "templates")
|
|
49
|
+
FileUtils.mkdir_p(job_template_path)
|
|
50
|
+
|
|
51
|
+
files.each do |file|
|
|
52
|
+
add_file(job_name, "templates/#{file}")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def remove_templates(job_name, *files)
|
|
57
|
+
job_template_path = File.join(@release_dir, "jobs", job_name, "templates")
|
|
58
|
+
|
|
59
|
+
files.each do |file|
|
|
60
|
+
FileUtils.rm_rf(File.join(job_template_path, file))
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "creates a new builder" do
|
|
65
|
+
add_templates("foo", "a.conf", "b.yml")
|
|
66
|
+
add_monit("foo")
|
|
67
|
+
builder = new_builder("foo", ["foo", "bar", "baz"],
|
|
68
|
+
["a.conf", "b.yml"], ["foo", "bar", "baz"])
|
|
69
|
+
builder.packages.should == ["foo", "bar", "baz"]
|
|
70
|
+
builder.templates.should =~ ["a.conf", "b.yml"]
|
|
71
|
+
builder.release_dir.should == @release_dir
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "has a fingerprint" do
|
|
75
|
+
add_templates("foo", "a.conf", "b.yml")
|
|
76
|
+
add_monit("foo")
|
|
77
|
+
builder = new_builder("foo", ["foo", "bar"],
|
|
78
|
+
["a.conf", "b.yml"], ["foo", "bar"])
|
|
79
|
+
builder.fingerprint.should == "457a3618ecca5fcf375e479627eb0a574c63574d"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "has a stable portable fingerprint" do
|
|
83
|
+
add_templates("foo", "a.conf", "b.yml")
|
|
84
|
+
add_monit("foo")
|
|
85
|
+
b1 = new_builder("foo", ["foo", "bar"],
|
|
86
|
+
["a.conf", "b.yml"], ["foo", "bar"])
|
|
87
|
+
f1 = b1.fingerprint
|
|
88
|
+
b1.reload.fingerprint.should == f1
|
|
89
|
+
|
|
90
|
+
b2 = new_builder("foo", ["foo", "bar"],
|
|
91
|
+
["a.conf", "b.yml"], ["foo", "bar"])
|
|
92
|
+
b2.fingerprint.should == f1
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "changes fingerprint when new template file is added" do
|
|
96
|
+
add_templates("foo", "a.conf", "b.yml")
|
|
97
|
+
add_monit("foo")
|
|
98
|
+
|
|
99
|
+
b1 = new_builder("foo", ["foo", "bar"],
|
|
100
|
+
["a.conf", "b.yml"], ["foo", "bar"])
|
|
101
|
+
f1 = b1.fingerprint
|
|
102
|
+
|
|
103
|
+
add_templates("foo", "baz")
|
|
104
|
+
b2 = new_builder("foo", ["foo", "bar"],
|
|
105
|
+
["a.conf", "b.yml", "baz"], ["foo", "bar"])
|
|
106
|
+
b2.fingerprint.should_not == f1
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "changes fingerprint when template files is changed" do
|
|
110
|
+
add_templates("foo", "a.conf", "b.yml")
|
|
111
|
+
add_monit("foo")
|
|
112
|
+
|
|
113
|
+
b1 = new_builder("foo", ["foo", "bar"],
|
|
114
|
+
["a.conf", "b.yml"], ["foo", "bar"])
|
|
115
|
+
f1 = b1.fingerprint
|
|
116
|
+
|
|
117
|
+
add_file("foo", "templates/a.conf", "bzz")
|
|
118
|
+
b1.reload.fingerprint.should_not == f1
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "changes fingerprint when new monit file is added" do
|
|
122
|
+
add_templates("foo", "a.conf", "b.yml")
|
|
123
|
+
add_monit("foo", "foo.monit")
|
|
124
|
+
|
|
125
|
+
b1 = new_builder("foo", ["foo", "bar"],
|
|
126
|
+
["a.conf", "b.yml"], ["foo", "bar"])
|
|
127
|
+
f1 = b1.fingerprint
|
|
128
|
+
|
|
129
|
+
add_monit("foo", "bar.monit")
|
|
130
|
+
b2 = new_builder("foo", ["foo", "bar"],
|
|
131
|
+
["a.conf", "b.yml"], ["foo", "bar"])
|
|
132
|
+
b2.fingerprint.should_not == f1
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "can read template file names from hash" do
|
|
136
|
+
add_templates("foo", "a.conf", "b.yml")
|
|
137
|
+
add_monit("foo")
|
|
138
|
+
builder = new_builder("foo", ["foo", "bar", "baz"],
|
|
139
|
+
{ "a.conf" => 1, "b.yml" => 2 },
|
|
140
|
+
["foo", "bar", "baz"])
|
|
141
|
+
builder.templates.should =~ ["a.conf", "b.yml"]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "whines if name is blank" do
|
|
145
|
+
lambda {
|
|
146
|
+
new_builder("")
|
|
147
|
+
}.should raise_error(Bosh::Cli::InvalidJob, "Job name is missing")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "whines on funny characters in name" do
|
|
151
|
+
lambda {
|
|
152
|
+
new_builder("@#!", [])
|
|
153
|
+
}.should raise_error(Bosh::Cli::InvalidJob,
|
|
154
|
+
"`@#!' is not a valid BOSH identifier")
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "whines if some templates are missing" do
|
|
158
|
+
add_templates("foo", "a.conf", "b.conf")
|
|
159
|
+
|
|
160
|
+
lambda {
|
|
161
|
+
new_builder("foo", [], ["a.conf", "b.conf", "c.conf"])
|
|
162
|
+
}.should raise_error(Bosh::Cli::InvalidJob,
|
|
163
|
+
"Some template files required by 'foo' job " +
|
|
164
|
+
"are missing: c.conf")
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "whines about extra packages" do
|
|
168
|
+
add_templates("foo", "a.conf", "b.conf")
|
|
169
|
+
|
|
170
|
+
lambda {
|
|
171
|
+
new_builder("foo", [], ["a.conf"], [])
|
|
172
|
+
}.should raise_error(Bosh::Cli::InvalidJob,
|
|
173
|
+
"There are unused template files for job 'foo'" +
|
|
174
|
+
": b.conf")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "whines if some packages are missing" do
|
|
178
|
+
lambda {
|
|
179
|
+
new_builder("foo", ["foo", "bar", "baz", "app42"], { }, ["foo", "bar"])
|
|
180
|
+
}.should raise_error(Bosh::Cli::InvalidJob,
|
|
181
|
+
"Some packages required by 'foo' job are missing: " +
|
|
182
|
+
"baz, app42")
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it "whines if there is no spec file" do
|
|
186
|
+
lambda {
|
|
187
|
+
new_builder("foo", ["foo", "bar", "baz", "app42"], { },
|
|
188
|
+
["foo", "bar", "baz", "app42"], false)
|
|
189
|
+
}.should raise_error(Bosh::Cli::InvalidJob,
|
|
190
|
+
"Cannot find spec file for 'foo'")
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "whines if there is no monit file" do
|
|
194
|
+
lambda {
|
|
195
|
+
add_templates("foo", "a.conf", "b.yml")
|
|
196
|
+
new_builder("foo", ["foo", "bar", "baz", "app42"],
|
|
197
|
+
["a.conf", "b.yml"], ["foo", "bar", "baz", "app42"])
|
|
198
|
+
}.should raise_error(Bosh::Cli::InvalidJob,
|
|
199
|
+
"Cannot find monit file for 'foo'")
|
|
200
|
+
|
|
201
|
+
add_monit("foo")
|
|
202
|
+
lambda {
|
|
203
|
+
new_builder("foo", ["foo", "bar", "baz", "app42"],
|
|
204
|
+
["a.conf", "b.yml"], ["foo", "bar", "baz", "app42"])
|
|
205
|
+
}.should_not raise_error
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "supports preparation script" do
|
|
209
|
+
spec = {
|
|
210
|
+
"name" => "foo",
|
|
211
|
+
"packages" => ["bar", "baz"],
|
|
212
|
+
"templates" => ["a.conf", "b.yml"]
|
|
213
|
+
}
|
|
214
|
+
spec_yaml = YAML.dump(spec)
|
|
215
|
+
|
|
216
|
+
script = <<-SCRIPT.gsub(/^\s*/, "")
|
|
217
|
+
#!/bin/sh
|
|
218
|
+
mkdir templates
|
|
219
|
+
touch templates/a.conf
|
|
220
|
+
touch templates/b.yml
|
|
221
|
+
echo '#{spec_yaml}' > spec
|
|
222
|
+
touch monit
|
|
223
|
+
SCRIPT
|
|
224
|
+
|
|
225
|
+
add_file("foo", "prepare", script)
|
|
226
|
+
script_path = File.join(@release_dir, "jobs", "foo", "prepare")
|
|
227
|
+
FileUtils.chmod(0755, script_path)
|
|
228
|
+
Bosh::Cli::JobBuilder.run_prepare_script(script_path)
|
|
229
|
+
|
|
230
|
+
builder = new_builder("foo", ["bar", "baz"], ["a.conf", "b.yml"],
|
|
231
|
+
["foo", "bar", "baz", "app42"], false)
|
|
232
|
+
builder.copy_files.should == 4
|
|
233
|
+
|
|
234
|
+
Dir.chdir(builder.build_dir) do
|
|
235
|
+
File.directory?("templates").should be_true
|
|
236
|
+
["templates/a.conf", "templates/b.yml"].each do |file|
|
|
237
|
+
File.file?(file).should be_true
|
|
238
|
+
end
|
|
239
|
+
File.file?("job.MF").should be_true
|
|
240
|
+
File.read("job.MF").should == File.read(
|
|
241
|
+
File.join(@release_dir, "jobs", "foo", "spec"))
|
|
242
|
+
File.exists?("monit").should be_true
|
|
243
|
+
File.exists?("prepare").should be_false
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "copies job files" do
|
|
248
|
+
add_templates("foo", "a.conf", "b.yml")
|
|
249
|
+
add_monit("foo")
|
|
250
|
+
builder = new_builder("foo", ["foo", "bar", "baz", "app42"],
|
|
251
|
+
["a.conf", "b.yml"], ["foo", "bar", "baz", "app42"])
|
|
252
|
+
|
|
253
|
+
builder.copy_files.should == 4
|
|
254
|
+
|
|
255
|
+
Dir.chdir(builder.build_dir) do
|
|
256
|
+
File.directory?("templates").should be_true
|
|
257
|
+
["templates/a.conf", "templates/b.yml"].each do |file|
|
|
258
|
+
File.file?(file).should be_true
|
|
259
|
+
end
|
|
260
|
+
File.file?("job.MF").should be_true
|
|
261
|
+
File.read("job.MF").should == File.read(
|
|
262
|
+
File.join(@release_dir, "jobs", "foo", "spec"))
|
|
263
|
+
File.exists?("monit").should be_true
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it "generates tarball" do
|
|
268
|
+
add_templates("foo", "bar", "baz")
|
|
269
|
+
add_monit("foo")
|
|
270
|
+
|
|
271
|
+
builder = new_builder("foo", ["p1", "p2"], ["bar", "baz"], ["p1", "p2"])
|
|
272
|
+
builder.generate_tarball.should be_true
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
it "supports versioning" do
|
|
276
|
+
add_templates("foo", "bar", "baz")
|
|
277
|
+
add_monit("foo")
|
|
278
|
+
|
|
279
|
+
builder = new_builder("foo", [], ["bar", "baz"], [])
|
|
280
|
+
|
|
281
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.1-dev.tgz").
|
|
282
|
+
should be_false
|
|
283
|
+
builder.build
|
|
284
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.1-dev.tgz").
|
|
285
|
+
should be_true
|
|
286
|
+
v1_fingerprint = builder.fingerprint
|
|
287
|
+
|
|
288
|
+
add_templates("foo", "zb.yml")
|
|
289
|
+
builder = new_builder("foo", [], ["bar", "baz", "zb.yml"], [])
|
|
290
|
+
builder.build
|
|
291
|
+
|
|
292
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.1-dev.tgz").
|
|
293
|
+
should be_true
|
|
294
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.2-dev.tgz").
|
|
295
|
+
should be_true
|
|
296
|
+
|
|
297
|
+
remove_templates("foo", "zb.yml")
|
|
298
|
+
|
|
299
|
+
builder = new_builder("foo", [], ["bar", "baz"], [])
|
|
300
|
+
builder.build
|
|
301
|
+
builder.version.should == "0.1-dev"
|
|
302
|
+
|
|
303
|
+
builder.fingerprint.should == v1_fingerprint
|
|
304
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.1-dev.tgz").
|
|
305
|
+
should be_true
|
|
306
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.2-dev.tgz").
|
|
307
|
+
should be_true
|
|
308
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.3-dev.tgz").
|
|
309
|
+
should be_false
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it "can point to either dev or a final version of a job" do
|
|
313
|
+
add_templates("foo", "bar", "baz")
|
|
314
|
+
add_monit("foo")
|
|
315
|
+
fingerprint = "34586bac103c208361ae5dcadd101807110c4546"
|
|
316
|
+
|
|
317
|
+
final_versions = Bosh::Cli::VersionsIndex.new(
|
|
318
|
+
File.join(@release_dir, ".final_builds", "jobs", "foo"))
|
|
319
|
+
dev_versions = Bosh::Cli::VersionsIndex.new(
|
|
320
|
+
File.join(@release_dir, ".dev_builds", "jobs", "foo"))
|
|
321
|
+
|
|
322
|
+
final_versions.add_version(fingerprint,
|
|
323
|
+
{ "version" => "4", "blobstore_id" => "12321" },
|
|
324
|
+
"payload")
|
|
325
|
+
dev_versions.add_version(fingerprint,
|
|
326
|
+
{ "version" => "0.7-dev" },
|
|
327
|
+
"dev_payload")
|
|
328
|
+
|
|
329
|
+
builder = new_builder("foo", [], ["bar", "baz"], [])
|
|
330
|
+
|
|
331
|
+
builder.fingerprint.should == fingerprint
|
|
332
|
+
|
|
333
|
+
builder.use_final_version
|
|
334
|
+
builder.version.should == "4"
|
|
335
|
+
builder.tarball_path.should == File.join(
|
|
336
|
+
@release_dir, ".final_builds", "jobs", "foo", "4.tgz")
|
|
337
|
+
|
|
338
|
+
builder.use_dev_version
|
|
339
|
+
builder.version.should == "0.7-dev"
|
|
340
|
+
builder.tarball_path.should == File.join(
|
|
341
|
+
@release_dir, ".dev_builds", "jobs", "foo", "0.7-dev.tgz")
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
it "bumps major dev version in sync with final version" do
|
|
345
|
+
add_templates("foo", "bar", "baz")
|
|
346
|
+
add_monit("foo")
|
|
347
|
+
|
|
348
|
+
builder = new_builder("foo", [], ["bar", "baz"], [])
|
|
349
|
+
builder.build
|
|
350
|
+
|
|
351
|
+
builder.version.should == "0.1-dev"
|
|
352
|
+
|
|
353
|
+
blobstore = mock("blobstore")
|
|
354
|
+
blobstore.should_receive(:create).and_return("object_id")
|
|
355
|
+
final_builder = new_builder("foo", [], ["bar", "baz"], [],
|
|
356
|
+
true, true, blobstore)
|
|
357
|
+
final_builder.build
|
|
358
|
+
|
|
359
|
+
final_builder.version.should == 1
|
|
360
|
+
|
|
361
|
+
add_templates("foo", "bzz")
|
|
362
|
+
builder2 = new_builder("foo", [], ["bar", "baz", "bzz"], [])
|
|
363
|
+
builder2.build
|
|
364
|
+
builder2.version.should == "1.1-dev"
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
it "whines on attempt to create final build if not matched " +
|
|
368
|
+
"by existing final or dev build" do
|
|
369
|
+
add_templates("foo", "bar", "baz")
|
|
370
|
+
add_monit("foo")
|
|
371
|
+
|
|
372
|
+
blobstore = mock("blobstore")
|
|
373
|
+
|
|
374
|
+
final_builder = new_builder("foo", [], ["bar", "baz"],
|
|
375
|
+
[], true, true, blobstore)
|
|
376
|
+
lambda {
|
|
377
|
+
final_builder.build
|
|
378
|
+
}.should raise_error(Bosh::Cli::CliExit)
|
|
379
|
+
|
|
380
|
+
dev_builder = new_builder("foo", [], ["bar", "baz"],
|
|
381
|
+
[], true, false, blobstore)
|
|
382
|
+
dev_builder.build
|
|
383
|
+
|
|
384
|
+
final_builder2 = new_builder("foo", [], ["bar", "baz"],
|
|
385
|
+
[], true, true, blobstore)
|
|
386
|
+
blobstore.should_receive(:create)
|
|
387
|
+
final_builder2.build
|
|
388
|
+
|
|
389
|
+
add_templates("foo", "bzz")
|
|
390
|
+
final_builder3 = new_builder("foo", [], ["bar", "baz", "bzz"],
|
|
391
|
+
[], true, true, blobstore)
|
|
392
|
+
|
|
393
|
+
lambda {
|
|
394
|
+
final_builder3.build
|
|
395
|
+
}.should raise_error(Bosh::Cli::CliExit)
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
it "allows template subdirectories" do
|
|
399
|
+
add_templates("foo", "foo/bar", "bar/baz")
|
|
400
|
+
add_monit("foo")
|
|
401
|
+
|
|
402
|
+
blobstore = mock("blobstore")
|
|
403
|
+
builder = new_builder("foo", [], ["foo/bar", "bar/baz"],
|
|
404
|
+
[], true, false, blobstore)
|
|
405
|
+
builder.build
|
|
406
|
+
|
|
407
|
+
Dir.chdir(builder.build_dir) do
|
|
408
|
+
File.directory?("templates").should be_true
|
|
409
|
+
["templates/foo/bar", "templates/bar/baz"].each do |file|
|
|
410
|
+
File.file?(file).should be_true
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
it "supports dry run" do
|
|
416
|
+
add_templates("foo", "bar", "baz")
|
|
417
|
+
add_monit("foo")
|
|
418
|
+
|
|
419
|
+
builder = new_builder("foo", [], ["bar", "baz"], [])
|
|
420
|
+
builder.dry_run = true
|
|
421
|
+
builder.build
|
|
422
|
+
|
|
423
|
+
builder.version.should == "0.1-dev"
|
|
424
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.1-dev.tgz").
|
|
425
|
+
should be_false
|
|
426
|
+
|
|
427
|
+
builder.dry_run = false
|
|
428
|
+
builder.reload.build
|
|
429
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.1-dev.tgz").
|
|
430
|
+
should be_true
|
|
431
|
+
|
|
432
|
+
blobstore = mock("blobstore")
|
|
433
|
+
blobstore.should_not_receive(:create)
|
|
434
|
+
final_builder = new_builder("foo", [], ["bar", "baz"], [],
|
|
435
|
+
true, true, blobstore)
|
|
436
|
+
final_builder.dry_run = true
|
|
437
|
+
final_builder.build
|
|
438
|
+
|
|
439
|
+
# Shouldn't be promoted during dry run:
|
|
440
|
+
final_builder.version.should == "0.1-dev"
|
|
441
|
+
File.exists?(@release_dir + "/.final_builds/jobs/foo/1.tgz").should be_false
|
|
442
|
+
|
|
443
|
+
add_templates("foo", "bzz")
|
|
444
|
+
builder2 = new_builder("foo", [], ["bar", "baz", "bzz"], [])
|
|
445
|
+
builder2.dry_run = true
|
|
446
|
+
builder2.build
|
|
447
|
+
builder2.version.should == "0.2-dev"
|
|
448
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.1-dev.tgz").
|
|
449
|
+
should be_true
|
|
450
|
+
File.exists?(@release_dir + "/.dev_builds/jobs/foo/0.2-dev.tgz").
|
|
451
|
+
should be_false
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
end
|