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.
@@ -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
- "trying without it...")
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
- manifest_yaml = compiler.result
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
- "name and director UUID are required")
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
- "please add 'release' or 'releases' section")
59
+ "please add 'release' or 'releases' section")
56
60
  end
57
61
 
58
- options[:yaml] ? manifest_yaml : manifest
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::DeploymentNotFound
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
- raise DirectorError, parse_error_message(response.code, body)
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 DeploymentNotFound < DirectorError; error_code(205); end
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
@@ -216,28 +216,46 @@ module Bosh::Cli
216
216
 
217
217
  # @return Array<GlobMatch>
218
218
  def resolve_globs
219
- matches = Set.new
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
- found = false
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
- [@alt_sources_dir, @sources_dir].each do |dir|
227
- next unless File.directory?(dir)
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
- Dir.chdir(dir) do
230
- dir_matches = resolve_glob_in_cwd(glob)
245
+ normal_matches = Dir.chdir(@sources_dir) do
246
+ resolve_glob_in_cwd(glob)
247
+ end
231
248
 
232
- unless dir_matches.empty?
233
- matches += dir_matches.map do |path|
234
- GlobMatch.new(dir, path)
235
- end
236
- found = true
237
- end
238
- end
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
- break if found
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
- unless found
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
- matches.sort
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("-v", "--version") { dispatch(find_command(:version)); }
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bosh
4
4
  module Cli
5
- VERSION = "0.19.4"
5
+ VERSION = "0.19.5"
6
6
  end
7
7
  end
@@ -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 = File.join(Dir.mktmpdir, "bosh_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({ :config => @config,
20
- :cache_dir => @cache_dir }.merge(options))
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 = make_command
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)