bosh_cli 1.0.3 → 1.5.0.pre.1113

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