bosh_cli 0.19.4 → 0.19.5
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/lib/cli/commands/base.rb +30 -12
- data/lib/cli/commands/biff.rb +19 -10
- data/lib/cli/commands/cloudcheck.rb +3 -3
- data/lib/cli/commands/deployment.rb +6 -6
- data/lib/cli/commands/misc.rb +48 -13
- data/lib/cli/commands/property_management.rb +6 -2
- data/lib/cli/commands/release.rb +20 -4
- data/lib/cli/commands/stemcell.rb +8 -1
- data/lib/cli/config.rb +33 -15
- data/lib/cli/deployment_helper.rb +61 -9
- data/lib/cli/director.rb +6 -8
- data/lib/cli/errors.rb +1 -1
- data/lib/cli/package_builder.rb +37 -19
- data/lib/cli/runner.rb +18 -2
- data/lib/cli/templates/help_message.erb +3 -0
- data/lib/cli/version.rb +1 -1
- data/spec/assets/biff/ip_out_of_range.yml +63 -0
- data/spec/unit/base_command_spec.rb +32 -6
- data/spec/unit/biff_spec.rb +16 -7
- data/spec/unit/cli_commands_spec.rb +11 -11
- data/spec/unit/config_spec.rb +6 -20
- data/spec/unit/deployment_manifest_spec.rb +117 -0
- data/spec/unit/package_builder_spec.rb +22 -4
- data/spec/unit/runner_spec.rb +17 -0
- metadata +8 -4
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Bosh::Cli
|
4
4
|
module DeploymentHelper
|
5
|
+
include VersionCalc
|
5
6
|
|
6
7
|
def prepare_deployment_manifest(options = {})
|
7
8
|
# TODO: extract to helper class
|
@@ -32,7 +33,7 @@ module Bosh::Cli
|
|
32
33
|
properties = director.list_properties(manifest["name"])
|
33
34
|
rescue Bosh::Cli::DirectorError
|
34
35
|
say("Unable to get properties list from director, " +
|
35
|
-
|
36
|
+
"trying without it...")
|
36
37
|
end
|
37
38
|
|
38
39
|
say("Compiling deployment manifest...")
|
@@ -41,33 +42,38 @@ module Bosh::Cli
|
|
41
42
|
hash
|
42
43
|
end
|
43
44
|
|
44
|
-
|
45
|
-
manifest = YAML.load(manifest_yaml)
|
45
|
+
manifest = YAML.load(compiler.result)
|
46
46
|
end
|
47
47
|
|
48
48
|
if manifest["name"].blank? || manifest["director_uuid"].blank?
|
49
49
|
err("Invalid manifest `#{File.basename(deployment)}': " +
|
50
|
-
|
50
|
+
"name and director UUID are required")
|
51
|
+
end
|
52
|
+
|
53
|
+
if director.uuid != manifest["director_uuid"]
|
54
|
+
err("Target director UUID doesn't match UUID from deployment manifest")
|
51
55
|
end
|
52
56
|
|
53
57
|
if manifest["release"].blank? && manifest["releases"].blank?
|
54
58
|
err("Deployment manifest doesn't have release information: '" +
|
55
|
-
|
59
|
+
"please add 'release' or 'releases' section")
|
56
60
|
end
|
57
61
|
|
58
|
-
|
62
|
+
resolve_release_aliases(manifest)
|
63
|
+
|
64
|
+
options[:yaml] ? YAML.dump(manifest) : manifest
|
59
65
|
end
|
60
66
|
|
61
67
|
# Check if the 2 deployments are different.
|
62
68
|
# Print out a summary if "show" is true.
|
63
|
-
def deployment_changed?(current_manifest, manifest, show=true)
|
69
|
+
def deployment_changed?(current_manifest, manifest, show = true)
|
64
70
|
diff = Bosh::Cli::HashChangeset.new
|
65
71
|
diff.add_hash(normalize_deployment_manifest(manifest), :new)
|
66
72
|
diff.add_hash(normalize_deployment_manifest(current_manifest), :old)
|
67
73
|
changed = diff.changed?
|
68
74
|
|
69
75
|
if changed && show
|
70
|
-
@_diff_key_visited = {
|
76
|
+
@_diff_key_visited = {}
|
71
77
|
diff.keys.each do |key|
|
72
78
|
unless @_diff_key_visited[key]
|
73
79
|
print_summary(diff, key)
|
@@ -186,7 +192,7 @@ module Bosh::Cli
|
|
186
192
|
end
|
187
193
|
|
188
194
|
diff.changed?
|
189
|
-
rescue Bosh::Cli::
|
195
|
+
rescue Bosh::Cli::ResourceNotFound
|
190
196
|
say("Cannot get current deployment information from director, " +
|
191
197
|
"possibly a new deployment".red)
|
192
198
|
true
|
@@ -299,5 +305,51 @@ module Bosh::Cli
|
|
299
305
|
end
|
300
306
|
end
|
301
307
|
|
308
|
+
# @param [Hash] manifest Deployment manifest (will be modified)
|
309
|
+
# @return [void]
|
310
|
+
def resolve_release_aliases(manifest)
|
311
|
+
if manifest["release"]
|
312
|
+
if manifest["releases"]
|
313
|
+
err("Manifest has both `release' and `releases', please fix it")
|
314
|
+
end
|
315
|
+
releases = [manifest["release"]]
|
316
|
+
else
|
317
|
+
releases = manifest["releases"]
|
318
|
+
end
|
319
|
+
|
320
|
+
unless releases.is_a?(Array)
|
321
|
+
err("Invalid release spec in the deployment manifest")
|
322
|
+
end
|
323
|
+
|
324
|
+
releases.each do |release|
|
325
|
+
if release["version"] == "latest"
|
326
|
+
latest_version = latest_releases[release["name"]]
|
327
|
+
if latest_version.nil?
|
328
|
+
err("Latest version for release `#{release["name"]}' is unknown")
|
329
|
+
end
|
330
|
+
# Avoiding Fixnum -> String noise in diff
|
331
|
+
if latest_version.to_s == latest_version.to_i.to_s
|
332
|
+
latest_version = latest_version.to_i
|
333
|
+
end
|
334
|
+
release["version"] = latest_version
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def latest_releases
|
340
|
+
@_latest_releases ||= begin
|
341
|
+
director.list_releases.inject({}) do |hash, release|
|
342
|
+
unless release["name"].is_a?(String) &&
|
343
|
+
release["versions"].is_a?(Array)
|
344
|
+
err("Invalid director release list format")
|
345
|
+
end
|
346
|
+
hash[release["name"]] = release["versions"].sort { |v1, v2|
|
347
|
+
version_cmp(v2, v1)
|
348
|
+
}.first
|
349
|
+
hash
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
302
354
|
end
|
303
355
|
end
|
data/lib/cli/director.rb
CHANGED
@@ -5,7 +5,7 @@ module Bosh
|
|
5
5
|
class Director
|
6
6
|
include Bosh::Cli::VersionCalc
|
7
7
|
|
8
|
-
DIRECTOR_HTTP_ERROR_CODES = [400, 403, 500]
|
8
|
+
DIRECTOR_HTTP_ERROR_CODES = [400, 403, 404, 500]
|
9
9
|
|
10
10
|
API_TIMEOUT = 86400 * 3
|
11
11
|
CONNECT_TIMEOUT = 30
|
@@ -108,17 +108,11 @@ module Bosh
|
|
108
108
|
|
109
109
|
def get_deployment(name)
|
110
110
|
status, body = get_json_with_status("/deployments/#{name}")
|
111
|
-
if status == 404
|
112
|
-
raise DeploymentNotFound, "Deployment `#{name}' not found"
|
113
|
-
end
|
114
111
|
body
|
115
112
|
end
|
116
113
|
|
117
114
|
def list_vms(name)
|
118
115
|
status, body = get_json_with_status("/deployments/#{name}/vms")
|
119
|
-
if status == 404
|
120
|
-
raise DeploymentNotFound, "Deployment `#{name}' not found"
|
121
|
-
end
|
122
116
|
body
|
123
117
|
end
|
124
118
|
|
@@ -488,7 +482,11 @@ module Bosh
|
|
488
482
|
end
|
489
483
|
|
490
484
|
if DIRECTOR_HTTP_ERROR_CODES.include?(response.code)
|
491
|
-
|
485
|
+
if response.code == 404
|
486
|
+
raise ResourceNotFound, parse_error_message(response.code, body)
|
487
|
+
else
|
488
|
+
raise DirectorError, parse_error_message(response.code, body)
|
489
|
+
end
|
492
490
|
end
|
493
491
|
|
494
492
|
headers = response.headers.inject({}) do |hash, (k, v)|
|
data/lib/cli/errors.rb
CHANGED
@@ -27,7 +27,7 @@ module Bosh::Cli
|
|
27
27
|
class AuthError < DirectorError; error_code(202); end
|
28
28
|
class MissingTask < DirectorError; error_code(203); end
|
29
29
|
class TaskTrackError < DirectorError; error_code(204); end
|
30
|
-
class
|
30
|
+
class ResourceNotFound < DirectorError; error_code(205); end
|
31
31
|
|
32
32
|
class CliExit < CliError; error_code(400); end
|
33
33
|
class GracefulExit < CliExit; error_code(401); end
|
data/lib/cli/package_builder.rb
CHANGED
@@ -216,28 +216,46 @@ module Bosh::Cli
|
|
216
216
|
|
217
217
|
# @return Array<GlobMatch>
|
218
218
|
def resolve_globs
|
219
|
-
|
219
|
+
all_matches = Set.new
|
220
220
|
|
221
221
|
@globs.each do |glob|
|
222
|
+
# Glob like core/dea/**/* might not yield anything in alt source even
|
223
|
+
# when `src_alt/core' exists. That's error prone, so we don't lookup
|
224
|
+
# in `src' if `src_alt' contains any part of the glob hierarchy.
|
225
|
+
top_dir = glob.split(File::SEPARATOR)[0]
|
226
|
+
alt_only = top_dir && File.exists?(File.join(@alt_sources_dir, top_dir))
|
227
|
+
|
228
|
+
matches = Set.new
|
222
229
|
# Alternative source dir completely shadows the source dir, there can be
|
223
230
|
# no partial match of a particular glob in both.
|
224
|
-
|
231
|
+
if File.directory?(@alt_sources_dir)
|
232
|
+
alt_matches = Dir.chdir(@alt_sources_dir) do
|
233
|
+
resolve_glob_in_cwd(glob)
|
234
|
+
end
|
235
|
+
else
|
236
|
+
alt_matches = []
|
237
|
+
end
|
225
238
|
|
226
|
-
|
227
|
-
|
239
|
+
if alt_matches.size > 0
|
240
|
+
matches += alt_matches.map do |path|
|
241
|
+
GlobMatch.new(@alt_sources_dir, path)
|
242
|
+
end
|
243
|
+
end
|
228
244
|
|
229
|
-
|
230
|
-
|
245
|
+
normal_matches = Dir.chdir(@sources_dir) do
|
246
|
+
resolve_glob_in_cwd(glob)
|
247
|
+
end
|
231
248
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
249
|
+
if alt_only && alt_matches.empty? && !normal_matches.empty?
|
250
|
+
raise InvalidPackage, "Package `#{name}' has a glob that " +
|
251
|
+
"doesn't match in `#{File.basename(@alt_sources_dir)}' " +
|
252
|
+
"but matches in `#{File.basename(@sources_dir)}'. " +
|
253
|
+
"However `#{File.basename(@alt_sources_dir)}/#{top_dir}' " +
|
254
|
+
"exists, so this might be an error."
|
255
|
+
end
|
239
256
|
|
240
|
-
|
257
|
+
matches += normal_matches.map do |path|
|
258
|
+
GlobMatch.new(@sources_dir, path)
|
241
259
|
end
|
242
260
|
|
243
261
|
# Blobs directory is a little bit different: whatever matches a blob
|
@@ -251,19 +269,19 @@ module Bosh::Cli
|
|
251
269
|
blob_matches.each do |path|
|
252
270
|
matches << GlobMatch.new(@blobs_dir, path)
|
253
271
|
end
|
254
|
-
|
255
|
-
found = true
|
256
272
|
end
|
257
273
|
end
|
258
274
|
end
|
259
275
|
|
260
|
-
|
261
|
-
raise InvalidPackage, "`#{name}' has a glob that " +
|
276
|
+
if matches.empty?
|
277
|
+
raise InvalidPackage, "Package `#{name}' has a glob that " +
|
262
278
|
"resolves to an empty file list: #{glob}"
|
263
279
|
end
|
280
|
+
|
281
|
+
all_matches += matches
|
264
282
|
end
|
265
283
|
|
266
|
-
|
284
|
+
all_matches.sort
|
267
285
|
end
|
268
286
|
|
269
287
|
def resolve_glob_in_cwd(glob)
|
data/lib/cli/runner.rb
CHANGED
@@ -192,6 +192,12 @@ module Bosh::Cli
|
|
192
192
|
route :misc, :set_alias
|
193
193
|
end
|
194
194
|
|
195
|
+
command :list_aliases do
|
196
|
+
usage "aliases"
|
197
|
+
desc "Show the list of available command aliases"
|
198
|
+
route :misc, :list_aliases
|
199
|
+
end
|
200
|
+
|
195
201
|
command :target do
|
196
202
|
usage "target [<name>] [<alias>]"
|
197
203
|
desc "Choose director to talk to (optionally creating an alias). " +
|
@@ -201,6 +207,12 @@ module Bosh::Cli
|
|
201
207
|
end
|
202
208
|
end
|
203
209
|
|
210
|
+
command :list_targets do
|
211
|
+
usage "targets"
|
212
|
+
desc "Show the list of available targets"
|
213
|
+
route :misc, :list_targets
|
214
|
+
end
|
215
|
+
|
204
216
|
command :deployment do
|
205
217
|
usage "deployment [<name>]"
|
206
218
|
desc "Choose deployment to work with " +
|
@@ -546,7 +558,7 @@ module Bosh::Cli
|
|
546
558
|
end
|
547
559
|
|
548
560
|
command :cloudcheck do
|
549
|
-
usage "cloudcheck"
|
561
|
+
usage "cloudcheck [<deployment>]"
|
550
562
|
desc "Cloud consistency check and interactive repair"
|
551
563
|
option "--auto", "resolve problems automatically " +
|
552
564
|
"(not recommended for production)"
|
@@ -615,7 +627,11 @@ module Bosh::Cli
|
|
615
627
|
@options[:colorize] = false
|
616
628
|
end
|
617
629
|
opts.on("-d", "--debug") { @options[:debug] = true }
|
618
|
-
opts.on("
|
630
|
+
opts.on("--target URL") { |target| @options[:target] = target }
|
631
|
+
opts.on("--user USER") { |user| @options[:username] = user }
|
632
|
+
opts.on("--password PASSWORD") { |pass| @options[:password] = pass }
|
633
|
+
opts.on("--deployment FILE") { |file| @options[:deployment] = file }
|
634
|
+
opts.on("-v", "--version") { dispatch(find_command(:version)) }
|
619
635
|
end
|
620
636
|
|
621
637
|
@args = opts_parser.order!(@args)
|
@@ -61,8 +61,11 @@ Misc
|
|
61
61
|
<%= command_usage(:status) %>
|
62
62
|
<%= command_usage(:list_vms) %>
|
63
63
|
<%= command_usage(:target) %>
|
64
|
+
<%= command_usage(:list_targets) %>
|
64
65
|
<%= command_usage(:login) %>
|
65
66
|
<%= command_usage(:logout) %>
|
67
|
+
<%= command_usage(:alias) %>
|
68
|
+
<%= command_usage(:list_aliases) %>
|
66
69
|
<%= command_usage(:purge) %>
|
67
70
|
|
68
71
|
Remote access
|
data/lib/cli/version.rb
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
---
|
2
|
+
name: ben-cloud
|
3
|
+
director_uuid: a8f335a4-30f0-4a92-ab88-5c3feb55577a
|
4
|
+
|
5
|
+
networks:
|
6
|
+
|
7
|
+
- name: default
|
8
|
+
subnets:
|
9
|
+
- reserved:
|
10
|
+
- 172.31.4.2 - 172.31.4.9
|
11
|
+
- 172.31.7.245 - 172.31.7.254
|
12
|
+
static:
|
13
|
+
- 172.31.4.10 - 172.31.4.11
|
14
|
+
range: 172.31.4.0/32
|
15
|
+
gateway: 172.31.4.1
|
16
|
+
dns:
|
17
|
+
- 172.30.22.153
|
18
|
+
- 172.30.22.154
|
19
|
+
cloud_properties:
|
20
|
+
name: VLAN2004
|
21
|
+
- name: lb
|
22
|
+
subnets:
|
23
|
+
- static:
|
24
|
+
- 172.28.6.241 - 172.28.6.242
|
25
|
+
range: 172.28.6.240/28
|
26
|
+
dns:
|
27
|
+
- 172.30.22.153
|
28
|
+
- 172.30.22.154
|
29
|
+
cloud_properties:
|
30
|
+
name: VLAN3121
|
31
|
+
|
32
|
+
jobs:
|
33
|
+
|
34
|
+
- name: debian_nfs_server
|
35
|
+
template: debian_nfs_server
|
36
|
+
instances: 1
|
37
|
+
resource_pool: infrastructure
|
38
|
+
persistent_disk: 8192
|
39
|
+
networks:
|
40
|
+
- name: default
|
41
|
+
static_ips:
|
42
|
+
- 172.31.4.10
|
43
|
+
|
44
|
+
- name: syslog_aggregator
|
45
|
+
template: syslog_aggregator
|
46
|
+
instances: 1
|
47
|
+
resource_pool: infrastructure
|
48
|
+
persistent_disk: 600
|
49
|
+
networks:
|
50
|
+
- name: default
|
51
|
+
static_ips:
|
52
|
+
- 172.31.4.13
|
53
|
+
|
54
|
+
properties:
|
55
|
+
domain: appcloud23.dev.mozycloud.com
|
56
|
+
|
57
|
+
nfs_server:
|
58
|
+
address: 172.31.4.10
|
59
|
+
network: 172.31.4.0/22
|
60
|
+
|
61
|
+
syslog_aggregator:
|
62
|
+
address: 172.31.4.13
|
63
|
+
port: 54321
|
@@ -5,7 +5,7 @@ require "spec_helper"
|
|
5
5
|
describe Bosh::Cli::Command::Base do
|
6
6
|
|
7
7
|
before :each do
|
8
|
-
@config
|
8
|
+
@config = File.join(Dir.mktmpdir, "bosh_config")
|
9
9
|
@cache_dir = Dir.mktmpdir
|
10
10
|
end
|
11
11
|
|
@@ -15,9 +15,9 @@ describe Bosh::Cli::Command::Base do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def make_command(options = {
|
19
|
-
Bosh::Cli::Command::Base.new({
|
20
|
-
|
18
|
+
def make_command(options = {})
|
19
|
+
Bosh::Cli::Command::Base.new({:config => @config,
|
20
|
+
:cache_dir => @cache_dir}.merge(options))
|
21
21
|
end
|
22
22
|
|
23
23
|
it "can access configuration and respects options" do
|
@@ -32,10 +32,36 @@ describe Bosh::Cli::Command::Base do
|
|
32
32
|
cmd.password.should == nil
|
33
33
|
end
|
34
34
|
|
35
|
+
it "looks up target, deployment and credentials in a right order" do
|
36
|
+
cmd = make_command
|
37
|
+
cmd.username.should be_nil
|
38
|
+
cmd.password.should be_nil
|
39
|
+
old_user = ENV["BOSH_USER"]
|
40
|
+
old_password = ENV["BOSH_PASSWORD"]
|
41
|
+
|
42
|
+
begin
|
43
|
+
ENV["BOSH_USER"] = "foo"
|
44
|
+
ENV["BOSH_PASSWORD"] = "bar"
|
45
|
+
cmd.username.should == "foo"
|
46
|
+
cmd.password.should == "bar"
|
47
|
+
other_cmd = make_command(:username => "new", :password => "baz")
|
48
|
+
other_cmd.username.should == "new"
|
49
|
+
other_cmd.password.should == "baz"
|
50
|
+
ensure
|
51
|
+
ENV["BOSH_USER"] = old_user
|
52
|
+
ENV["BOSH_PASSWORD"] = old_password
|
53
|
+
end
|
54
|
+
|
55
|
+
add_config("target" => "localhost:8080", "deployment" => "test")
|
56
|
+
cmd2 = make_command(:target => "foo", :deployment => "bar")
|
57
|
+
cmd2.target.should == "foo"
|
58
|
+
cmd2.deployment.should == "bar"
|
59
|
+
end
|
60
|
+
|
35
61
|
it "instantiates director when needed" do
|
36
62
|
add_config("target" => "localhost:8080", "deployment" => "test")
|
37
63
|
|
38
|
-
cmd = make_command
|
64
|
+
cmd = make_command
|
39
65
|
cmd.director.director_uri.should == "localhost:8080"
|
40
66
|
end
|
41
67
|
|
@@ -51,7 +77,7 @@ describe Bosh::Cli::Command::Base do
|
|
51
77
|
|
52
78
|
it "can redirect to other commands " +
|
53
79
|
"(effectively exiting after running them)" do
|
54
|
-
cmd
|
80
|
+
cmd = make_command
|
55
81
|
new_cmd = mock(Object)
|
56
82
|
|
57
83
|
Bosh::Cli::Command::Misc.should_receive(:new).and_return(new_cmd)
|