bosh_cli 1.0.3 → 1.5.0.pre.1113

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.
Files changed (144) hide show
  1. data/bin/bosh +0 -9
  2. data/lib/cli.rb +69 -64
  3. data/lib/cli/backup_destination_path.rb +33 -0
  4. data/lib/cli/base_command.rb +57 -56
  5. data/lib/cli/blob_manager.rb +12 -12
  6. data/lib/cli/changeset_helper.rb +6 -7
  7. data/lib/cli/client/director.rb +724 -0
  8. data/lib/cli/command_handler.rb +6 -7
  9. data/lib/cli/commands/backup.rb +39 -0
  10. data/lib/cli/commands/biff.rb +42 -21
  11. data/lib/cli/commands/blob_management.rb +1 -1
  12. data/lib/cli/commands/cloudcheck.rb +11 -13
  13. data/lib/cli/commands/deployment.rb +53 -37
  14. data/lib/cli/commands/help.rb +3 -2
  15. data/lib/cli/commands/job_management.rb +67 -103
  16. data/lib/cli/commands/job_rename.rb +6 -8
  17. data/lib/cli/commands/log_management.rb +78 -55
  18. data/lib/cli/commands/maintenance.rb +36 -30
  19. data/lib/cli/commands/misc.rb +72 -51
  20. data/lib/cli/commands/package.rb +2 -2
  21. data/lib/cli/commands/property_management.rb +10 -12
  22. data/lib/cli/commands/release.rb +236 -133
  23. data/lib/cli/commands/snapshot.rb +93 -0
  24. data/lib/cli/commands/ssh.rb +216 -213
  25. data/lib/cli/commands/stemcell.rb +46 -34
  26. data/lib/cli/commands/task.rb +2 -2
  27. data/lib/cli/commands/user.rb +27 -3
  28. data/lib/cli/commands/vm.rb +28 -0
  29. data/lib/cli/commands/vms.rb +81 -23
  30. data/lib/cli/config.rb +6 -2
  31. data/lib/cli/core_ext.rb +31 -30
  32. data/lib/cli/deployment_helper.rb +134 -159
  33. data/lib/cli/deployment_manifest.rb +66 -0
  34. data/lib/cli/deployment_manifest_compiler.rb +0 -3
  35. data/lib/cli/event_log_renderer.rb +10 -10
  36. data/lib/cli/file_with_progress_bar.rb +52 -0
  37. data/lib/cli/job_builder.rb +1 -1
  38. data/lib/cli/job_command_args.rb +23 -0
  39. data/lib/cli/job_property_collection.rb +4 -7
  40. data/lib/cli/job_property_validator.rb +22 -12
  41. data/lib/cli/job_state.rb +54 -0
  42. data/lib/cli/line_wrap.rb +54 -0
  43. data/lib/cli/packaging_helper.rb +10 -10
  44. data/lib/cli/release.rb +18 -15
  45. data/lib/cli/release_builder.rb +9 -4
  46. data/lib/cli/release_compiler.rb +9 -9
  47. data/lib/cli/release_tarball.rb +3 -6
  48. data/lib/cli/resurrection.rb +31 -0
  49. data/lib/cli/runner.rb +56 -30
  50. data/lib/cli/stemcell.rb +25 -10
  51. data/lib/cli/task_log_renderer.rb +1 -1
  52. data/lib/cli/task_tracker.rb +10 -9
  53. data/lib/cli/validation.rb +3 -1
  54. data/lib/cli/version.rb +1 -1
  55. data/lib/cli/version_calc.rb +5 -18
  56. data/lib/cli/versions_index.rb +1 -1
  57. data/lib/cli/vm_state.rb +43 -0
  58. data/lib/cli/yaml_helper.rb +26 -35
  59. metadata +75 -208
  60. data/Rakefile +0 -56
  61. data/lib/cli/director.rb +0 -628
  62. data/spec/assets/biff/bad_gateway_config.yml +0 -28
  63. data/spec/assets/biff/good_simple_config.yml +0 -63
  64. data/spec/assets/biff/good_simple_golden_config.yml +0 -63
  65. data/spec/assets/biff/good_simple_template.erb +0 -69
  66. data/spec/assets/biff/ip_out_of_range.yml +0 -63
  67. data/spec/assets/biff/multiple_subnets_config.yml +0 -40
  68. data/spec/assets/biff/network_only_template.erb +0 -34
  69. data/spec/assets/biff/no_cc_config.yml +0 -27
  70. data/spec/assets/biff/no_range_config.yml +0 -27
  71. data/spec/assets/biff/no_subnet_config.yml +0 -16
  72. data/spec/assets/biff/ok_network_config.yml +0 -30
  73. data/spec/assets/biff/properties_template.erb +0 -6
  74. data/spec/assets/config/atmos/config/final.yml +0 -6
  75. data/spec/assets/config/atmos/config/private.yml +0 -4
  76. data/spec/assets/config/bad-providers/config/final.yml +0 -5
  77. data/spec/assets/config/bad-providers/config/private.yml +0 -4
  78. data/spec/assets/config/deprecation/config/final.yml +0 -5
  79. data/spec/assets/config/deprecation/config/private.yml +0 -2
  80. data/spec/assets/config/local/config/final.yml +0 -5
  81. data/spec/assets/config/local/config/private.yml +0 -1
  82. data/spec/assets/config/s3/config/final.yml +0 -5
  83. data/spec/assets/config/s3/config/private.yml +0 -5
  84. data/spec/assets/config/swift-hp/config/final.yml +0 -6
  85. data/spec/assets/config/swift-hp/config/private.yml +0 -7
  86. data/spec/assets/config/swift-rackspace/config/final.yml +0 -6
  87. data/spec/assets/config/swift-rackspace/config/private.yml +0 -6
  88. data/spec/assets/deployment.MF +0 -0
  89. data/spec/assets/plugins/bosh/cli/commands/echo.rb +0 -43
  90. data/spec/assets/plugins/bosh/cli/commands/ruby.rb +0 -24
  91. data/spec/assets/release/jobs/cacher.tgz +0 -0
  92. data/spec/assets/release/jobs/cacher/config/file1.conf +0 -0
  93. data/spec/assets/release/jobs/cacher/config/file2.conf +0 -0
  94. data/spec/assets/release/jobs/cacher/job.MF +0 -6
  95. data/spec/assets/release/jobs/cacher/monit +0 -1
  96. data/spec/assets/release/jobs/cleaner.tgz +0 -0
  97. data/spec/assets/release/jobs/cleaner/job.MF +0 -4
  98. data/spec/assets/release/jobs/cleaner/monit +0 -1
  99. data/spec/assets/release/jobs/sweeper.tgz +0 -0
  100. data/spec/assets/release/jobs/sweeper/config/test.conf +0 -1
  101. data/spec/assets/release/jobs/sweeper/job.MF +0 -5
  102. data/spec/assets/release/jobs/sweeper/monit +0 -1
  103. data/spec/assets/release/packages/mutator.tar.gz +0 -0
  104. data/spec/assets/release/packages/stuff.tgz +0 -0
  105. data/spec/assets/release/release.MF +0 -17
  106. data/spec/assets/release_invalid_checksum.tgz +0 -0
  107. data/spec/assets/release_invalid_jobs.tgz +0 -0
  108. data/spec/assets/release_no_name.tgz +0 -0
  109. data/spec/assets/release_no_version.tgz +0 -0
  110. data/spec/assets/stemcell/image +0 -1
  111. data/spec/assets/stemcell/stemcell.MF +0 -6
  112. data/spec/assets/stemcell_invalid_mf.tgz +0 -0
  113. data/spec/assets/stemcell_no_image.tgz +0 -0
  114. data/spec/assets/valid_release.tgz +0 -0
  115. data/spec/assets/valid_stemcell.tgz +0 -0
  116. data/spec/spec_helper.rb +0 -28
  117. data/spec/unit/base_command_spec.rb +0 -87
  118. data/spec/unit/biff_spec.rb +0 -172
  119. data/spec/unit/blob_manager_spec.rb +0 -288
  120. data/spec/unit/cache_spec.rb +0 -36
  121. data/spec/unit/cli_commands_spec.rb +0 -356
  122. data/spec/unit/config_spec.rb +0 -125
  123. data/spec/unit/core_ext_spec.rb +0 -81
  124. data/spec/unit/dependency_helper_spec.rb +0 -52
  125. data/spec/unit/deployment_manifest_compiler_spec.rb +0 -63
  126. data/spec/unit/deployment_manifest_spec.rb +0 -153
  127. data/spec/unit/director_spec.rb +0 -471
  128. data/spec/unit/director_task_spec.rb +0 -48
  129. data/spec/unit/event_log_renderer_spec.rb +0 -171
  130. data/spec/unit/hash_changeset_spec.rb +0 -73
  131. data/spec/unit/job_builder_spec.rb +0 -455
  132. data/spec/unit/job_property_collection_spec.rb +0 -111
  133. data/spec/unit/job_property_validator_spec.rb +0 -7
  134. data/spec/unit/job_rename_spec.rb +0 -200
  135. data/spec/unit/package_builder_spec.rb +0 -593
  136. data/spec/unit/release_builder_spec.rb +0 -120
  137. data/spec/unit/release_spec.rb +0 -173
  138. data/spec/unit/release_tarball_spec.rb +0 -29
  139. data/spec/unit/runner_spec.rb +0 -7
  140. data/spec/unit/ssh_spec.rb +0 -84
  141. data/spec/unit/stemcell_spec.rb +0 -17
  142. data/spec/unit/task_tracker_spec.rb +0 -131
  143. data/spec/unit/version_calc_spec.rb +0 -27
  144. data/spec/unit/versions_index_spec.rb +0 -144
@@ -15,58 +15,53 @@ module Bosh::Cli::Command
15
15
  usage "status"
16
16
  desc "Show current status (current target, user, deployment info etc)"
17
17
  def status
18
- cpi = nil
19
- features = nil
20
-
21
- if config.target
22
- say("Updating director data...", " ")
18
+ say("Config".make_green)
19
+ print_value("", config.filename)
23
20
 
21
+ nl
22
+ say("Director".make_green)
23
+ if target.nil?
24
+ say(" not set".make_yellow)
25
+ else
24
26
  begin
25
27
  timeout(config.status_timeout || DEFAULT_STATUS_TIMEOUT) do
26
- director = Bosh::Cli::Director.new(config.target)
28
+ director = Bosh::Cli::Client::Director.new(target)
27
29
  status = director.get_status
28
30
 
29
- config.target_name = status["name"]
30
- config.target_version = status["version"]
31
- config.target_uuid = status["uuid"]
32
- cpi = status["cpi"]
33
- features = status["features"]
34
- config.save
35
- say("done".green)
31
+ print_value("Name", status["name"])
32
+ print_value("URL", target_url)
33
+ print_value("Version", status["version"])
34
+ print_value("User", username, "not logged in")
35
+ print_value("UUID", status["uuid"])
36
+ print_value("CPI", status["cpi"], "n/a")
37
+ print_feature_list(status["features"]) if status["features"]
38
+
39
+ unless options[:target]
40
+ config.target_name = status["name"]
41
+ config.target_version = status["version"]
42
+ config.target_uuid = status["uuid"]
43
+ config.save
44
+ end
36
45
  end
37
46
  rescue TimeoutError
38
- say("timed out".red)
47
+ say(" timed out fetching director status".make_red)
39
48
  rescue => e
40
- say("error: #{e.message}")
49
+ say(" error fetching director status: #{e.message}".make_red)
41
50
  end
42
- nl
43
- end
44
-
45
- say("Director".green)
46
- if target_url.nil?
47
- say(" not set".yellow)
48
- else
49
- print_value("Name", config.target_name)
50
- print_value("URL", target_url)
51
- print_value("Version", config.target_version)
52
- print_value("User", username, "not logged in")
53
- print_value("UUID", config.target_uuid)
54
- print_value("CPI", cpi, "n/a (update director)")
55
- print_feature_list(features) if features
56
51
  end
57
52
 
58
53
  nl
59
- say("Deployment".green)
54
+ say("Deployment".make_green)
60
55
 
61
56
  if deployment
62
57
  print_value("Manifest", deployment)
63
58
  else
64
- say(" not set".yellow)
59
+ say(" not set".make_yellow)
65
60
  end
66
61
 
67
62
  if in_release_dir?
68
63
  nl
69
- say("Release".green)
64
+ say("Release".make_green)
70
65
 
71
66
  dev_version = Bosh::Cli::VersionsIndex.new(
72
67
  File.join(work_dir, "dev_releases")).latest_version
@@ -87,7 +82,10 @@ module Bosh::Cli::Command
87
82
 
88
83
  # bosh login
89
84
  usage "login"
90
- desc "Log in to currently targeted director"
85
+ desc "Log in to currently targeted director. " +
86
+ "The username and password can also be " +
87
+ "set in the BOSH_USER and BOSH_PASSWORD " +
88
+ "environment variables."
91
89
  def login(username = nil, password = nil)
92
90
  target_required
93
91
 
@@ -106,16 +104,20 @@ module Bosh::Cli::Command
106
104
  end
107
105
  logged_in = false
108
106
 
107
+ #Converts HighLine::String to String
108
+ username = username.to_s
109
+ password = password.to_s
110
+
109
111
  director.user = username
110
112
  director.password = password
111
113
 
112
114
  if director.authenticated?
113
- say("Logged in as `#{username}'".green)
115
+ say("Logged in as `#{username}'".make_green)
114
116
  logged_in = true
115
117
  elsif non_interactive?
116
- err("Cannot log in as `#{username}'".red)
118
+ err("Cannot log in as `#{username}'".make_red)
117
119
  else
118
- say("Cannot log in as `#{username}', please try again".red)
120
+ say("Cannot log in as `#{username}', please try again".make_red)
119
121
  login(username)
120
122
  end
121
123
 
@@ -132,7 +134,7 @@ module Bosh::Cli::Command
132
134
  target_required
133
135
  config.set_credentials(target, nil, nil)
134
136
  config.save
135
- say("You are no longer logged in to `#{target}'".yellow)
137
+ say("You are no longer logged in to `#{target}'".make_yellow)
136
138
  end
137
139
 
138
140
  # bosh purge
@@ -143,7 +145,7 @@ module Bosh::Cli::Command
143
145
  err("Cache directory overriden, please remove manually")
144
146
  else
145
147
  FileUtils.rm_rf(cache.cache_dir)
146
- say("Purged cache".green)
148
+ say("Purged cache".make_green)
147
149
  end
148
150
  end
149
151
 
@@ -168,11 +170,11 @@ module Bosh::Cli::Command
168
170
 
169
171
  director_url = normalize_url(director_url)
170
172
  if target && director_url == normalize_url(target)
171
- say("Target already set to `#{target_name.green}'")
173
+ say("Target already set to `#{target_name.make_green}'")
172
174
  return
173
175
  end
174
176
 
175
- director = Bosh::Cli::Director.new(director_url)
177
+ director = Bosh::Cli::Client::Director.new(director_url)
176
178
 
177
179
  begin
178
180
  status = director.get_status
@@ -197,7 +199,7 @@ module Bosh::Cli::Command
197
199
  end
198
200
 
199
201
  config.save
200
- say("Target set to `#{target_name.green}'")
202
+ say("Target set to `#{target_name.make_green}'")
201
203
 
202
204
  if interactive? && !logged_in?
203
205
  redirect("login")
@@ -229,7 +231,7 @@ module Bosh::Cli::Command
229
231
  def set_alias(name, command)
230
232
  config.set_alias(:cli, name, command.to_s.strip)
231
233
  config.save
232
- say("Alias `#{name.green}' created for command `#{command.green}'")
234
+ say("Alias `#{name.make_green}' created for command `#{command.make_green}'")
233
235
  end
234
236
 
235
237
  # bosh aliases
@@ -255,9 +257,9 @@ module Bosh::Cli::Command
255
257
 
256
258
  def print_value(label, value, if_none = nil)
257
259
  if value
258
- message = label.ljust(10) + value.yellow
260
+ message = label.ljust(10) + ' ' + value.make_yellow
259
261
  else
260
- message = label.ljust(10) + (if_none || "n/a").yellow
262
+ message = label.ljust(10) + ' ' + (if_none || "n/a").make_yellow
261
263
  end
262
264
  say(message.indent(2))
263
265
  end
@@ -270,7 +272,7 @@ module Bosh::Cli::Command
270
272
  else
271
273
  name = config.target
272
274
  end
273
- say("Current target is #{name.green}")
275
+ say("Current target is #{name.make_green}")
274
276
  else
275
277
  say(config.target)
276
278
  end
@@ -310,7 +312,7 @@ module Bosh::Cli::Command
310
312
  t << [name, dev_version.gsub(/\-dev$/, "").rjust(8),
311
313
  final_version.to_s.rjust(8)]
312
314
  rescue Bosh::Cli::InvalidIndex => e
313
- say("Problem with #{entity} index for `#{name}': #{e.message}".red)
315
+ say("Problem with #{entity} index for `#{name}': #{e.message}".make_red)
314
316
  end
315
317
  else
316
318
  say("Spec file `#{spec_file}' is invalid")
@@ -322,22 +324,41 @@ module Bosh::Cli::Command
322
324
 
323
325
  def print_feature_list(features)
324
326
  if features.respond_to?(:each)
325
- features.each do |feature, status|
326
- print_value(feature, format_feature_status(status))
327
+ features.each do |feature, info|
328
+ # Old director only returns status as a Boolean
329
+ if info.kind_of?(Hash)
330
+ status = info["status"]
331
+ extras = info["extras"]
332
+ else
333
+ status = info
334
+ extras = nil
335
+ end
336
+ print_value(feature, format_feature_status(status, extras))
327
337
  end
328
338
  else
329
- say("Unknown feature list: #{features.inspect}".red)
339
+ say("Unknown feature list: #{features.inspect}".make_red)
330
340
  end
331
341
  end
332
342
 
333
- def format_feature_status(status)
343
+ def format_feature_status(status, extras)
334
344
  if status.nil?
335
345
  "n/a"
336
346
  elsif status
337
- "enabled"
347
+ "enabled #{format_feature_extras(extras)}"
338
348
  else
339
349
  "disabled"
340
350
  end
341
351
  end
352
+
353
+ def format_feature_extras(extras)
354
+ return "" if extras.nil? || extras.empty?
355
+
356
+ result = []
357
+ extras.each do |name, value|
358
+ result << "#{name}: #{value}"
359
+ end
360
+
361
+ "(#{result.join(", ")})"
362
+ end
342
363
  end
343
364
  end
@@ -22,12 +22,12 @@ module Bosh::Cli::Command
22
22
  FileUtils.mkdir_p(package_dir)
23
23
 
24
24
  generate_file(package_dir, "packaging") do
25
- "# abort script on any command that exit " +
25
+ "# abort script on any command that exits " +
26
26
  "with a non zero value\nset -e\n"
27
27
  end
28
28
 
29
29
  generate_file(package_dir, "pre_packaging") do
30
- "# abort script on any command that exit " +
30
+ "# abort script on any command that exits " +
31
31
  "with a non zero value\nset -e\n"
32
32
  end
33
33
 
@@ -2,8 +2,6 @@
2
2
 
3
3
  module Bosh::Cli::Command
4
4
  class PropertyManagement < Base
5
- include Bosh::Cli::DeploymentHelper
6
-
7
5
  # bosh set property
8
6
  usage "set property"
9
7
  desc "Set deployment property"
@@ -19,14 +17,14 @@ module Bosh::Cli::Command
19
17
  end
20
18
 
21
19
  if existing_property
22
- say("Current `#{name.green}' value is " +
23
- "`#{format_property(body["value"]).green}'")
20
+ say("Current `#{name.make_green}' value is " +
21
+ "`#{format_property(body["value"]).make_green}'")
24
22
  else
25
23
  say("This will be a new property")
26
24
  end
27
25
 
28
26
  prompt = "Are you sure you want to set property" +
29
- " `#{name.green}' to `#{format_property(value).green}'?"
27
+ " `#{name.make_green}' to `#{format_property(value).make_green}'?"
30
28
 
31
29
  unless confirmed?(prompt)
32
30
  err("Canceled")
@@ -39,7 +37,7 @@ module Bosh::Cli::Command
39
37
  end
40
38
 
41
39
  if status == 204
42
- say("Property `#{name.green}' set to `#{value.green}'")
40
+ say("Property `#{name.make_green}' set to `#{value.make_green}'")
43
41
  else
44
42
  err(director.parse_error_message(status, body))
45
43
  end
@@ -53,7 +51,7 @@ module Bosh::Cli::Command
53
51
  show_header
54
52
 
55
53
  prompt = "Are you sure you want to unset property " +
56
- "`#{name.green}'?"
54
+ "`#{name.make_green}'?"
57
55
 
58
56
  unless confirmed?(prompt)
59
57
  err("Canceled")
@@ -62,7 +60,7 @@ module Bosh::Cli::Command
62
60
  status, body = director.delete_property(@deployment_name, name)
63
61
 
64
62
  if status == 204
65
- say("Property `#{name.green}' has been unset")
63
+ say("Property `#{name.make_green}' has been unset")
66
64
  else
67
65
  err(director.parse_error_message(status, body))
68
66
  end
@@ -77,8 +75,8 @@ module Bosh::Cli::Command
77
75
 
78
76
  status, body = director.get_property(@deployment_name, name)
79
77
  if status == 200
80
- say("Property `#{name.green}' value is " +
81
- "`#{format_property(body["value"]).green}'")
78
+ say("Property `#{name.make_green}' value is " +
79
+ "`#{format_property(body["value"]).make_green}'")
82
80
  else
83
81
  err(director.parse_error_message(status, body))
84
82
  end
@@ -128,8 +126,8 @@ module Bosh::Cli::Command
128
126
  end
129
127
 
130
128
  def show_header
131
- say("Target #{target_name.green}")
132
- say("Deployment #{@deployment_name.green}")
129
+ say("Target #{target_name.make_green}")
130
+ say("Deployment #{@deployment_name.make_green}")
133
131
  nl
134
132
  end
135
133
 
@@ -26,16 +26,15 @@ module Bosh::Cli::Command
26
26
 
27
27
  # Initialize an empty blobs index
28
28
  File.open(File.join("config", "blobs.yml"), "w") do |f|
29
- YAML.dump({}, f)
29
+ Psych.dump({}, f)
30
30
  end
31
31
 
32
- say("Release directory initialized".green)
32
+ say("Release directory initialized".make_green)
33
33
  end
34
34
 
35
35
  # bosh create release
36
36
  usage "create release"
37
- desc "Create release (assumes current directory " +
38
- "to be a release repository)"
37
+ desc "Create release (assumes current directory to be a release repository)"
39
38
  option "--force", "bypass git dirty state check"
40
39
  option "--final", "create final release"
41
40
  option "--with-tarball", "create release tarball"
@@ -67,54 +66,57 @@ module Bosh::Cli::Command
67
66
  nl
68
67
 
69
68
  if tarball.valid?
70
- say("`#{tarball_path}' is a valid release".green)
69
+ say("`#{tarball_path}' is a valid release".make_green)
71
70
  else
72
- say("Validation errors:".red)
71
+ say("Validation errors:".make_red)
73
72
  tarball.errors.each do |error|
74
73
  say("- #{error}")
75
74
  end
76
- err("`#{tarball_path}' is not a valid release".red)
75
+ err("`#{tarball_path}' is not a valid release".make_red)
77
76
  end
78
77
  end
79
78
 
80
79
  usage "upload release"
81
- desc "Upload release"
80
+ desc "Upload release (release_file can be a local file or a remote URI)"
82
81
  option "--rebase",
83
82
  "Rebases this release onto the latest version",
84
83
  "known by director (discards local job/package",
85
84
  "versions in favor of versions assigned by director)"
85
+ option "--skip-if-exists", "skips upload if release already exists"
86
86
  def upload(release_file = nil)
87
87
  auth_required
88
88
 
89
89
  upload_options = {
90
90
  :rebase => options[:rebase],
91
- :repack => true
91
+ :repack => true,
92
+ :skip_if_exists => options[:skip_if_exists],
92
93
  }
93
94
 
94
95
  if release_file.nil?
95
96
  check_if_release_dir
96
97
  release_file = release.latest_release_filename
97
98
  if release_file.nil?
98
- err("The information about latest generated release is missing, " +
99
- "please provide release filename")
99
+ err("The information about latest generated release is missing, please provide release filename")
100
100
  end
101
- unless confirmed?("Upload release " +
102
- "`#{File.basename(release_file).green}' " +
103
- "to `#{target_name.green}'")
101
+ unless confirmed?("Upload release `#{File.basename(release_file).make_green}' to `#{target_name.make_green}'")
104
102
  err("Canceled upload")
105
103
  end
106
104
  end
107
105
 
108
- unless File.exist?(release_file)
109
- err("Release file doesn't exist")
110
- end
106
+ if release_file =~ /^#{URI::regexp}$/
107
+ upload_remote_release(release_file, upload_options)
108
+ else
109
+ unless File.exist?(release_file)
110
+ err("Release file doesn't exist")
111
+ end
111
112
 
112
- file_type = `file --mime-type -b '#{release_file}'`
113
+ file_type = `file --mime-type -b '#{release_file}'`
113
114
 
114
- if file_type =~ /text\/(plain|yaml)/
115
- upload_manifest(release_file, upload_options)
116
- else
117
- upload_tarball(release_file, upload_options)
115
+ if file_type =~ /text\/(plain|yaml)/
116
+ upload_manifest(release_file, upload_options)
117
+ else
118
+ upload_tarball(release_file, upload_options)
119
+ end
118
120
  end
119
121
  end
120
122
 
@@ -123,7 +125,7 @@ module Bosh::Cli::Command
123
125
  def reset
124
126
  check_if_release_dir
125
127
 
126
- say("Your dev release environment will be completely reset".red)
128
+ say("Your dev release environment will be completely reset".make_red)
127
129
  if confirmed?
128
130
  say("Removing dev_builds index...")
129
131
  FileUtils.rm_rf(".dev_builds")
@@ -133,7 +135,7 @@ module Bosh::Cli::Command
133
135
  say("Removing dev tarballs...")
134
136
  FileUtils.rm_rf("dev_releases")
135
137
 
136
- say("Release has been reset".green)
138
+ say("Release has been reset".make_green)
137
139
  else
138
140
  say("Canceled")
139
141
  end
@@ -141,6 +143,7 @@ module Bosh::Cli::Command
141
143
 
142
144
  usage "releases"
143
145
  desc "Show the list of available releases"
146
+ option "--jobs", "include job templates"
144
147
  def list
145
148
  auth_required
146
149
  releases = director.list_releases.sort do |r1, r2|
@@ -149,20 +152,16 @@ module Bosh::Cli::Command
149
152
 
150
153
  err("No releases") if releases.empty?
151
154
 
152
- releases_table = table do |t|
153
- t.headings = "Name", "Versions"
154
- releases.each do |r|
155
- versions = r["versions"].sort { |v1, v2|
156
- version_cmp(v1, v2)
157
- }.map { |v| ((r["in_use"] || []).include?(v)) ? "#{v}*" : v }
158
-
159
- t << [r["name"], versions.join(", ")]
160
- end
155
+ if releases.first.has_key? "release_versions"
156
+ releases_table = build_releases_table(releases, options)
157
+ elsif releases.first.has_key? "versions"
158
+ releases_table = build_releases_table_for_old_director(releases)
161
159
  end
162
160
 
163
161
  nl
164
- say(releases_table)
162
+ say(releases_table.render)
165
163
  say("(*) Currently deployed") if releases_table.to_s =~ /\*/
164
+ say("(+) Uncommitted changes") if releases_table.to_s =~ /^\|.*\+{1}.*\|$/
166
165
  nl
167
166
  say("Releases total: %d" % releases.size)
168
167
  end
@@ -178,20 +177,20 @@ module Bosh::Cli::Command
178
177
  desc << "/#{version}" if version
179
178
 
180
179
  if force
181
- say("Deleting `#{desc}' (FORCED DELETE, WILL IGNORE ERRORS)".red)
180
+ say("Deleting `#{desc}' (FORCED DELETE, WILL IGNORE ERRORS)".make_red)
182
181
  else
183
- say("Deleting `#{desc}'".red)
182
+ say("Deleting `#{desc}'".make_red)
184
183
  end
185
184
 
186
185
  if confirmed?
187
- status, task_id = director.delete_release(
188
- name, :force => force, :version => version)
186
+ status, task_id = director.delete_release(name, force: force, version: version)
189
187
  task_report(status, task_id, "Deleted `#{desc}'")
190
188
  else
191
- say("Canceled deleting release".green)
189
+ say("Canceled deleting release".make_green)
192
190
  end
193
191
  end
194
192
 
193
+
195
194
  protected
196
195
 
197
196
  def upload_manifest(manifest_path, upload_options = {})
@@ -202,8 +201,7 @@ module Bosh::Cli::Command
202
201
  blobstore = release.blobstore
203
202
  tmpdir = Dir.mktmpdir
204
203
 
205
- compiler = Bosh::Cli::ReleaseCompiler.new(
206
- manifest_path, blobstore, package_matches)
204
+ compiler = Bosh::Cli::ReleaseCompiler.new(manifest_path, blobstore, package_matches)
207
205
  need_repack = true
208
206
 
209
207
  unless compiler.exists?
@@ -230,24 +228,29 @@ module Bosh::Cli::Command
230
228
  err("Release is invalid, please fix, verify and upload again")
231
229
  end
232
230
 
233
- begin
234
- remote_release = get_remote_release(tarball.release_name) rescue nil
235
-
236
- if remote_release && !rebase &&
237
- remote_release["versions"].include?(tarball.version)
238
- err("This release version has already been uploaded")
231
+ remote_release = get_remote_release(tarball.release_name) rescue nil
232
+ if remote_release && !rebase
233
+ if remote_release["versions"].include?(tarball.version)
234
+ if upload_options[:skip_if_exists]
235
+ say("Release `#{tarball.release_name}/#{tarball.version}' already exists. Skipping upload.")
236
+ return
237
+ else
238
+ err("This release version has already been uploaded")
239
+ end
239
240
  end
241
+ end
240
242
 
243
+ begin
241
244
  if repack
242
245
  package_matches = match_remote_packages(tarball.manifest)
243
246
 
244
247
  say("Checking if can repack release for faster upload...")
245
248
  repacked_path = tarball.repack(package_matches)
249
+
246
250
  if repacked_path.nil?
247
- say("Uploading the whole release".green)
251
+ say("Uploading the whole release".make_green)
248
252
  else
249
- say("Release repacked " +
250
- "(new size is #{pretty_size(repacked_path)})".green)
253
+ say("Release repacked (new size is #{pretty_size(repacked_path)})".make_green)
251
254
  tarball_path = repacked_path
252
255
  end
253
256
  end
@@ -257,7 +260,7 @@ module Bosh::Cli::Command
257
260
  end
258
261
 
259
262
  if rebase
260
- say("Uploading release (#{"will be rebased".yellow})")
263
+ say("Uploading release (#{"will be rebased".make_yellow})")
261
264
  status, task_id = director.rebase_release(tarball_path)
262
265
  task_report(status, task_id, "Release rebased")
263
266
  else
@@ -267,6 +270,19 @@ module Bosh::Cli::Command
267
270
  end
268
271
  end
269
272
 
273
+ def upload_remote_release(release_location, upload_options = {})
274
+ nl
275
+ if upload_options[:rebase]
276
+ say("Using remote release `#{release_location}' (#{"will be rebased".make_yellow})")
277
+ status, task_id = director.rebase_remote_release(release_location)
278
+ task_report(status, task_id, "Release rebased")
279
+ else
280
+ say("Using remote release `#{release_location}'")
281
+ status, task_id = director.upload_remote_release(release_location)
282
+ task_report(status, task_id, "Release uploaded")
283
+ end
284
+ end
285
+
270
286
  def create_from_manifest(manifest_file)
271
287
  say("Recreating release from the manifest")
272
288
  Bosh::Cli::ReleaseCompiler.compile(manifest_file, release.blobstore)
@@ -279,67 +295,84 @@ module Bosh::Cli::Command
279
295
  manifest_only = !options[:with_tarball]
280
296
  dry_run = options[:dry_run]
281
297
 
282
- if final && !release.has_blobstore_secret?
283
- err("Can't create final release without blobstore secret")
298
+ err("Can't create final release without blobstore secret") if final && !release.has_blobstore_secret?
299
+
300
+ dirty_blob_check(force)
301
+
302
+ raise_dirty_state_error if dirty_state? && !force
303
+
304
+ if final
305
+ confirm_final_release(dry_run)
306
+ save_final_release_name if release.final_name.blank?
307
+ header("Building FINAL release".make_green)
308
+ else
309
+ save_dev_release_name if release.dev_name.blank?
310
+ header("Building DEV release".make_green)
284
311
  end
285
312
 
286
- blob_manager.sync
287
- if blob_manager.dirty?
288
- blob_manager.print_status
289
- if force
290
- say("Proceeding with dirty blobs as '--force' is given".red)
291
- else
292
- err("Please use '--force' or upload new blobs")
293
- end
313
+ if version_greater(release.min_cli_version, Bosh::Cli::VERSION)
314
+ err("You should use CLI >= #{release.min_cli_version} with this release, you have #{Bosh::Cli::VERSION}")
294
315
  end
295
316
 
296
- check_if_dirty_state unless force
317
+ header("Building packages")
318
+ packages = build_packages(dry_run, final)
297
319
 
298
- confirmation = "Are you sure you want to " +
299
- "generate #{'final'.red} version? "
320
+ header("Building jobs")
321
+ jobs = build_jobs(packages.map(&:name), dry_run, final)
300
322
 
301
- if final && !dry_run && !confirmed?(confirmation)
302
- say("Canceled release generation".green)
303
- exit(1)
304
- end
323
+ header("Building release")
324
+ release_builder = build_release(dry_run, final, jobs, manifest_only, packages)
305
325
 
306
- if final
307
- header("Building FINAL release".green)
308
- release_name = release.final_name
309
- else
310
- release_name = release.dev_name
311
- header("Building DEV release".green)
326
+ header("Release summary")
327
+ show_summary(release_builder)
328
+ nl
329
+
330
+ return nil if dry_run
331
+
332
+ say("Release version: #{release_builder.version.to_s.make_green}")
333
+ say("Release manifest: #{release_builder.manifest_path.make_green}")
334
+
335
+ unless manifest_only
336
+ say("Release tarball (#{pretty_size(release_builder.tarball_path)}): " +
337
+ release_builder.tarball_path.make_green)
312
338
  end
313
339
 
314
- if version_greater(release.min_cli_version, Bosh::Cli::VERSION)
315
- err("You should use CLI >= #{release.min_cli_version} " +
316
- "with this release, you have #{Bosh::Cli::VERSION}")
340
+ release.min_cli_version = Bosh::Cli::VERSION
341
+ release.save_config
342
+
343
+ release_builder.manifest_path
344
+ end
345
+
346
+ def confirm_final_release(dry_run)
347
+ confirmed = non_interactive? || agree("Are you sure you want to generate #{'final'.make_red} version? ")
348
+ if !dry_run && !confirmed
349
+ say("Canceled release generation".make_green)
350
+ exit(1)
317
351
  end
352
+ end
318
353
 
319
- if release_name.blank?
320
- confirmation = "Please enter %s release name: " % [
321
- final ? "final" : "development"]
322
- name = interactive? ? ask(confirmation).to_s : DEFAULT_RELEASE_NAME
323
- err("Canceled release creation, no name given") if name.blank?
324
- if final
325
- release.final_name = name
354
+ def dirty_blob_check(force)
355
+ blob_manager.sync
356
+ if blob_manager.dirty?
357
+ blob_manager.print_status
358
+ if force
359
+ say("Proceeding with dirty blobs as '--force' is given".make_red)
326
360
  else
327
- release.dev_name = name
361
+ err("Please use '--force' or upload new blobs")
328
362
  end
329
- release.save_config
330
363
  end
364
+ end
331
365
 
332
- header("Building packages")
333
-
366
+ def build_packages(dry_run, final)
334
367
  packages = Bosh::Cli::PackageBuilder.discover(
335
- work_dir,
336
- :final => final,
337
- :blobstore => release.blobstore,
338
- :dry_run => dry_run
368
+ work_dir,
369
+ :final => final,
370
+ :blobstore => release.blobstore,
371
+ :dry_run => dry_run
339
372
  )
340
373
 
341
374
  packages.each do |package|
342
- say("Building #{package.name.green}...")
375
+ say("Building #{package.name.make_green}...")
343
376
  package.build
344
377
  nl
345
378
  end
@@ -358,52 +391,58 @@ module Bosh::Cli::Command
358
391
  nl
359
392
  end
360
393
 
361
- built_package_names = packages.map { |package| package.name }
362
-
363
- header("Building jobs")
364
- jobs = Bosh::Cli::JobBuilder.discover(
365
- work_dir,
366
- :final => final,
367
- :blobstore => release.blobstore,
368
- :dry_run => dry_run,
369
- :package_names => built_package_names
370
- )
371
-
372
- jobs.each do |job|
373
- say("Building #{job.name.green}...")
374
- job.build
375
- nl
376
- end
394
+ packages
395
+ end
377
396
 
378
- builder = Bosh::Cli::ReleaseBuilder.new(release, packages,
379
- jobs, :final => final)
397
+ def build_release(dry_run, final, jobs, manifest_only, packages)
398
+ release_builder = Bosh::Cli::ReleaseBuilder.new(release, packages, jobs, final: final,
399
+ commit_hash: commit_hash, uncommitted_changes: dirty_state?)
380
400
 
381
401
  unless dry_run
382
402
  if manifest_only
383
- builder.build(:generate_tarball => false)
403
+ release_builder.build(:generate_tarball => false)
384
404
  else
385
- builder.build(:generate_tarball => true)
405
+ release_builder.build(:generate_tarball => true)
386
406
  end
387
407
  end
388
408
 
389
- header("Release summary")
390
- show_summary(builder)
391
- nl
392
-
393
- return nil if dry_run
409
+ release_builder
410
+ end
394
411
 
395
- say("Release version: #{builder.version.to_s.green}")
396
- say("Release manifest: #{builder.manifest_path.green}")
412
+ def build_jobs(built_package_names, dry_run, final)
413
+ jobs = Bosh::Cli::JobBuilder.discover(
414
+ work_dir,
415
+ :final => final,
416
+ :blobstore => release.blobstore,
417
+ :dry_run => dry_run,
418
+ :package_names => built_package_names
419
+ )
397
420
 
398
- unless manifest_only
399
- say("Release tarball (#{pretty_size(builder.tarball_path)}): " +
400
- builder.tarball_path.green)
421
+ jobs.each do |job|
422
+ say("Building #{job.name.make_green}...")
423
+ job.build
424
+ nl
401
425
  end
402
426
 
403
- release.min_cli_version = Bosh::Cli::VERSION
427
+ jobs
428
+ end
429
+
430
+ def save_final_release_name
431
+ release.final_name = DEFAULT_RELEASE_NAME
432
+ if interactive?
433
+ release.final_name = ask("Please enter final release name: ").to_s
434
+ err("Canceled release creation, no name given") if release.final_name.blank?
435
+ end
404
436
  release.save_config
437
+ end
405
438
 
406
- builder.manifest_path
439
+ def save_dev_release_name
440
+ release.dev_name = DEFAULT_RELEASE_NAME
441
+ if interactive?
442
+ release.dev_name = ask("Please enter development release name: ").to_s
443
+ err("Canceled release creation, no name given") if release.dev_name.blank?
444
+ end
445
+ release.save_config
407
446
  end
408
447
 
409
448
  def git_init
@@ -432,9 +471,10 @@ module Bosh::Cli::Command
432
471
  end
433
472
  end
434
473
  rescue Errno::ENOENT
435
- say("Unable to run 'git init'".red)
474
+ say("Unable to run 'git init'".make_red)
436
475
  end
437
476
 
477
+
438
478
  private
439
479
 
440
480
  # if we aren't already in a release directory, try going up two levels
@@ -503,8 +543,7 @@ module Bosh::Cli::Command
503
543
  release.has_key?("jobs") &&
504
544
  release.has_key?("packages")
505
545
  raise Bosh::Cli::DirectorError,
506
- "Cannot find version, jobs and packages info " +
507
- "in the director response, maybe old director?"
546
+ "Cannot find version, jobs and packages info in the director response, maybe old director?"
508
547
  end
509
548
 
510
549
  release
@@ -514,12 +553,76 @@ module Bosh::Cli::Command
514
553
  director.match_packages(manifest_yaml)
515
554
  rescue Bosh::Cli::DirectorError
516
555
  msg = "You are using CLI >= 0.20 with director that doesn't support " +
517
- "package matches.\nThis will result in uploading all packages " +
518
- "and jobs to your director.\nIt is recommended to update your " +
519
- "director or downgrade your CLI to 0.19.6"
556
+ "package matches.\nThis will result in uploading all packages " +
557
+ "and jobs to your director.\nIt is recommended to update your " +
558
+ "director or downgrade your CLI to 0.19.6"
520
559
 
521
- say(msg.yellow)
560
+ say(msg.make_yellow)
522
561
  exit(1) unless confirmed?
523
562
  end
563
+
564
+ def build_releases_table_for_old_director(releases)
565
+ table do |t|
566
+ t.headings = "Name", "Versions"
567
+ releases.each do |release|
568
+ versions = release["versions"].sort { |v1, v2|
569
+ version_cmp(v1, v2)
570
+ }.map { |v| ((release["in_use"] || []).include?(v)) ? "#{v}*" : v }
571
+
572
+ t << [release["name"], versions.join(", ")]
573
+ end
574
+ end
575
+ end
576
+
577
+ # Builds table of release information
578
+ # Default headings: "Name", "Versions", "Commit Hash"
579
+ # Extra headings: options[:job] => "Jobs"
580
+ def build_releases_table(releases, options = {})
581
+ show_jobs = options[:jobs]
582
+ table do |t|
583
+ t.headings = "Name", "Versions", "Commit Hash"
584
+ t.headings << "Jobs" if show_jobs
585
+ releases.each do |release|
586
+ versions, commit_hashes = formatted_versions(release).transpose
587
+ row = [release["name"], versions.join("\n"), commit_hashes.join("\n")]
588
+ if show_jobs
589
+ jobs = formatted_jobs(release).transpose
590
+ row << jobs.join("\n")
591
+ end
592
+ t << row
593
+ end
594
+ end
595
+ end
596
+
597
+ def formatted_versions(release)
598
+ sort_versions(release["release_versions"]).map { |v| formatted_version_and_commit_hash(v) }
599
+ end
600
+
601
+ def sort_versions(versions)
602
+ versions.sort { |v1, v2| version_cmp(v1["version"], v2["version"]) }
603
+ end
604
+
605
+ def formatted_version_and_commit_hash(version)
606
+ version_number = version["version"] + (version["currently_deployed"] ? "*" : "")
607
+ commit_hash = version["commit_hash"] + (version["uncommitted_changes"] ? "+" : "")
608
+ [version_number, commit_hash]
609
+ end
610
+
611
+ def formatted_jobs(release)
612
+ sort_versions(release["release_versions"]).map do |v|
613
+ if job_names = v["job_names"]
614
+ [job_names.join(", ")]
615
+ else
616
+ ["n/a "] # with enough whitespace to match "Jobs" header
617
+ end
618
+ end
619
+ end
620
+
621
+ def commit_hash
622
+ status = Bosh::Exec.sh('git show-ref --head --hash=8 2> /dev/null')
623
+ status.output.split.first
624
+ rescue Bosh::Exec::Error => e
625
+ '00000000'
626
+ end
524
627
  end
525
- end
628
+ end