bosh_cli 0.16
Sign up to get free protection for your applications and to get access to all the features.
- 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
|