bosh_cli 0.16
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -0
- data/Rakefile +55 -0
- data/bin/bosh +17 -0
- data/lib/cli.rb +76 -0
- data/lib/cli/cache.rb +44 -0
- data/lib/cli/changeset_helper.rb +142 -0
- data/lib/cli/command_definition.rb +52 -0
- data/lib/cli/commands/base.rb +245 -0
- data/lib/cli/commands/biff.rb +300 -0
- data/lib/cli/commands/blob.rb +125 -0
- data/lib/cli/commands/cloudcheck.rb +169 -0
- data/lib/cli/commands/deployment.rb +147 -0
- data/lib/cli/commands/job.rb +42 -0
- data/lib/cli/commands/job_management.rb +117 -0
- data/lib/cli/commands/log_management.rb +81 -0
- data/lib/cli/commands/maintenance.rb +131 -0
- data/lib/cli/commands/misc.rb +240 -0
- data/lib/cli/commands/package.rb +112 -0
- data/lib/cli/commands/property_management.rb +125 -0
- data/lib/cli/commands/release.rb +469 -0
- data/lib/cli/commands/ssh.rb +271 -0
- data/lib/cli/commands/stemcell.rb +184 -0
- data/lib/cli/commands/task.rb +213 -0
- data/lib/cli/commands/user.rb +28 -0
- data/lib/cli/commands/vms.rb +53 -0
- data/lib/cli/config.rb +154 -0
- data/lib/cli/core_ext.rb +145 -0
- data/lib/cli/dependency_helper.rb +62 -0
- data/lib/cli/deployment_helper.rb +263 -0
- data/lib/cli/deployment_manifest_compiler.rb +28 -0
- data/lib/cli/director.rb +633 -0
- data/lib/cli/director_task.rb +64 -0
- data/lib/cli/errors.rb +48 -0
- data/lib/cli/event_log_renderer.rb +351 -0
- data/lib/cli/job_builder.rb +226 -0
- data/lib/cli/package_builder.rb +254 -0
- data/lib/cli/packaging_helper.rb +248 -0
- data/lib/cli/release.rb +176 -0
- data/lib/cli/release_builder.rb +215 -0
- data/lib/cli/release_compiler.rb +178 -0
- data/lib/cli/release_tarball.rb +272 -0
- data/lib/cli/runner.rb +771 -0
- data/lib/cli/stemcell.rb +83 -0
- data/lib/cli/task_log_renderer.rb +40 -0
- data/lib/cli/templates/help_message.erb +75 -0
- data/lib/cli/validation.rb +42 -0
- data/lib/cli/version.rb +7 -0
- data/lib/cli/version_calc.rb +48 -0
- data/lib/cli/versions_index.rb +126 -0
- data/lib/cli/yaml_helper.rb +62 -0
- data/spec/assets/biff/bad_gateway_config.yml +28 -0
- data/spec/assets/biff/good_simple_config.yml +63 -0
- data/spec/assets/biff/good_simple_golden_config.yml +63 -0
- data/spec/assets/biff/good_simple_template.erb +69 -0
- data/spec/assets/biff/multiple_subnets_config.yml +40 -0
- data/spec/assets/biff/network_only_template.erb +34 -0
- data/spec/assets/biff/no_cc_config.yml +27 -0
- data/spec/assets/biff/no_range_config.yml +27 -0
- data/spec/assets/biff/no_subnet_config.yml +16 -0
- data/spec/assets/biff/ok_network_config.yml +30 -0
- data/spec/assets/biff/properties_template.erb +6 -0
- data/spec/assets/deployment.MF +0 -0
- data/spec/assets/plugins/bosh/cli/commands/echo.rb +43 -0
- data/spec/assets/plugins/bosh/cli/commands/ruby.rb +24 -0
- data/spec/assets/release/jobs/cacher.tgz +0 -0
- data/spec/assets/release/jobs/cacher/config/file1.conf +0 -0
- data/spec/assets/release/jobs/cacher/config/file2.conf +0 -0
- data/spec/assets/release/jobs/cacher/job.MF +6 -0
- data/spec/assets/release/jobs/cacher/monit +1 -0
- data/spec/assets/release/jobs/cleaner.tgz +0 -0
- data/spec/assets/release/jobs/cleaner/job.MF +4 -0
- data/spec/assets/release/jobs/cleaner/monit +1 -0
- data/spec/assets/release/jobs/sweeper.tgz +0 -0
- data/spec/assets/release/jobs/sweeper/config/test.conf +1 -0
- data/spec/assets/release/jobs/sweeper/job.MF +5 -0
- data/spec/assets/release/jobs/sweeper/monit +1 -0
- data/spec/assets/release/packages/mutator.tar.gz +0 -0
- data/spec/assets/release/packages/stuff.tgz +0 -0
- data/spec/assets/release/release.MF +17 -0
- data/spec/assets/release_invalid_checksum.tgz +0 -0
- data/spec/assets/release_invalid_jobs.tgz +0 -0
- data/spec/assets/release_no_name.tgz +0 -0
- data/spec/assets/release_no_version.tgz +0 -0
- data/spec/assets/stemcell/image +1 -0
- data/spec/assets/stemcell/stemcell.MF +6 -0
- data/spec/assets/stemcell_invalid_mf.tgz +0 -0
- data/spec/assets/stemcell_no_image.tgz +0 -0
- data/spec/assets/valid_release.tgz +0 -0
- data/spec/assets/valid_stemcell.tgz +0 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/unit/base_command_spec.rb +66 -0
- data/spec/unit/biff_spec.rb +135 -0
- data/spec/unit/cache_spec.rb +36 -0
- data/spec/unit/cli_commands_spec.rb +481 -0
- data/spec/unit/config_spec.rb +139 -0
- data/spec/unit/core_ext_spec.rb +77 -0
- data/spec/unit/dependency_helper_spec.rb +52 -0
- data/spec/unit/deployment_manifest_compiler_spec.rb +63 -0
- data/spec/unit/director_spec.rb +511 -0
- data/spec/unit/director_task_spec.rb +48 -0
- data/spec/unit/event_log_renderer_spec.rb +171 -0
- data/spec/unit/hash_changeset_spec.rb +73 -0
- data/spec/unit/job_builder_spec.rb +454 -0
- data/spec/unit/package_builder_spec.rb +567 -0
- data/spec/unit/release_builder_spec.rb +65 -0
- data/spec/unit/release_spec.rb +66 -0
- data/spec/unit/release_tarball_spec.rb +33 -0
- data/spec/unit/runner_spec.rb +140 -0
- data/spec/unit/ssh_spec.rb +78 -0
- data/spec/unit/stemcell_spec.rb +17 -0
- data/spec/unit/version_calc_spec.rb +27 -0
- data/spec/unit/versions_index_spec.rb +132 -0
- metadata +338 -0
@@ -0,0 +1,469 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Cli::Command
|
4
|
+
class Release < Base
|
5
|
+
DEFAULT_RELEASE_NAME = "bosh-release"
|
6
|
+
|
7
|
+
include Bosh::Cli::DependencyHelper
|
8
|
+
include Bosh::Cli::VersionCalc
|
9
|
+
|
10
|
+
def init(base=nil, *options)
|
11
|
+
flags = options.inject({}) { |h, option| h[option] = true; h }
|
12
|
+
git = flags.delete("--git")
|
13
|
+
|
14
|
+
if base
|
15
|
+
FileUtils.mkdir_p(base) unless Dir.exist?(base)
|
16
|
+
Dir.chdir(base)
|
17
|
+
end
|
18
|
+
|
19
|
+
err("Release already initialized") if in_release_dir?
|
20
|
+
|
21
|
+
git_init if git
|
22
|
+
|
23
|
+
%w[jobs packages src blobs].each do |dir|
|
24
|
+
FileUtils.mkdir(dir)
|
25
|
+
end
|
26
|
+
|
27
|
+
# initialize an empty blob_index file
|
28
|
+
blobs = {}
|
29
|
+
File.open("blob_index.yml", "w") do |f|
|
30
|
+
YAML.dump(blobs, f)
|
31
|
+
end
|
32
|
+
|
33
|
+
say("Release directory initialized".green)
|
34
|
+
end
|
35
|
+
|
36
|
+
def git_init
|
37
|
+
out = %x{git init 2>&1}
|
38
|
+
if $? != 0
|
39
|
+
say("error running 'git init':\n#{out}")
|
40
|
+
else
|
41
|
+
File.open(".gitignore", "w") do |f|
|
42
|
+
f.puts("config/dev.yml\nconfig/private.yml")
|
43
|
+
f.puts("blobs\nreleases/*.tgz")
|
44
|
+
f.puts("dev_releases\n.dev_builds")
|
45
|
+
f.puts(".final_builds/jobs/**/*.tgz")
|
46
|
+
f.puts(".final_builds/packages/**/*.tgz")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
rescue Errno::ENOENT
|
50
|
+
say("Unable to run 'git init'".red)
|
51
|
+
end
|
52
|
+
|
53
|
+
def verify(tarball_path, *options)
|
54
|
+
tarball = Bosh::Cli::ReleaseTarball.new(tarball_path)
|
55
|
+
|
56
|
+
say("\nVerifying release...")
|
57
|
+
tarball.validate
|
58
|
+
nl
|
59
|
+
|
60
|
+
if tarball.valid?
|
61
|
+
say("'%s' is a valid release" % [tarball_path] )
|
62
|
+
else
|
63
|
+
say("'%s' is not a valid release:" % [tarball_path] )
|
64
|
+
for error in tarball.errors
|
65
|
+
say("- %s" % [error])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def upload(release_file = nil)
|
71
|
+
auth_required
|
72
|
+
|
73
|
+
if release_file.nil?
|
74
|
+
check_if_release_dir
|
75
|
+
release_file = release.latest_release_filename
|
76
|
+
if release_file.nil?
|
77
|
+
err("The information about latest generated release is missing, " +
|
78
|
+
"please provide release filename")
|
79
|
+
end
|
80
|
+
unless confirmed?("Upload release " +
|
81
|
+
"`#{File.basename(release_file).green}' " +
|
82
|
+
"to `#{target_name.green}'")
|
83
|
+
err("Canceled upload")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
file_type = `file --mime-type -b '#{release_file}'`
|
88
|
+
|
89
|
+
if file_type =~ /text\/(plain|yaml)/
|
90
|
+
upload_manifest(release_file)
|
91
|
+
else # Just assume tarball
|
92
|
+
upload_tarball(release_file)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def upload_manifest(manifest_path)
|
97
|
+
manifest = load_yaml_file(manifest_path)
|
98
|
+
remote_release = get_remote_release(manifest["name"]) rescue nil
|
99
|
+
blobstore = release.blobstore
|
100
|
+
tmpdir = Dir.mktmpdir
|
101
|
+
|
102
|
+
at_exit { FileUtils.rm_rf(tmpdir) }
|
103
|
+
|
104
|
+
compiler = Bosh::Cli::ReleaseCompiler.new(manifest_path,
|
105
|
+
blobstore, remote_release)
|
106
|
+
need_repack = true
|
107
|
+
|
108
|
+
unless compiler.exists?
|
109
|
+
compiler.tarball_path = File.join(tmpdir, "release.tgz")
|
110
|
+
compiler.compile
|
111
|
+
need_repack = false
|
112
|
+
end
|
113
|
+
upload_tarball(compiler.tarball_path, :repack => need_repack)
|
114
|
+
end
|
115
|
+
|
116
|
+
def upload_tarball(tarball_path, options = {})
|
117
|
+
tarball = Bosh::Cli::ReleaseTarball.new(tarball_path)
|
118
|
+
# Trying to repack release by default
|
119
|
+
repack = options.has_key?(:repack) ? !!options[:repack] : true
|
120
|
+
|
121
|
+
say("\nVerifying release...")
|
122
|
+
tarball.validate(:allow_sparse => true)
|
123
|
+
nl
|
124
|
+
|
125
|
+
unless tarball.valid?
|
126
|
+
err("Release is invalid, please fix, verify and upload again")
|
127
|
+
end
|
128
|
+
|
129
|
+
begin
|
130
|
+
remote_release = get_remote_release(tarball.release_name)
|
131
|
+
if remote_release["versions"].include?(tarball.version)
|
132
|
+
err("This release version has already been uploaded")
|
133
|
+
end
|
134
|
+
|
135
|
+
if repack
|
136
|
+
say("Checking if can repack release for faster upload...")
|
137
|
+
repacked_path = tarball.repack(remote_release)
|
138
|
+
if repacked_path.nil?
|
139
|
+
say("Uploading the whole release".green)
|
140
|
+
else
|
141
|
+
say("Release repacked " +
|
142
|
+
"(new size is #{pretty_size(repacked_path)})".green)
|
143
|
+
tarball_path = repacked_path
|
144
|
+
end
|
145
|
+
end
|
146
|
+
rescue Bosh::Cli::DirectorError
|
147
|
+
# It's OK for director to choke on getting
|
148
|
+
# a release info (think new releases)
|
149
|
+
end
|
150
|
+
|
151
|
+
say("\nUploading release...\n")
|
152
|
+
status, message = director.upload_release(tarball_path)
|
153
|
+
|
154
|
+
responses = {
|
155
|
+
:done => "Release uploaded and updated",
|
156
|
+
:non_trackable => "Uploaded release but director at #{target} " +
|
157
|
+
"doesn't support update tracking",
|
158
|
+
:track_timeout => "Uploaded release but timed out out " +
|
159
|
+
"while tracking status",
|
160
|
+
:error => "Uploaded release but received an error while tracking status"
|
161
|
+
}
|
162
|
+
|
163
|
+
say(responses[status] || "Cannot upload release: #{message}")
|
164
|
+
end
|
165
|
+
|
166
|
+
def create(*options)
|
167
|
+
check_if_release_dir
|
168
|
+
if options.size == 1 && File.file?(options[0])
|
169
|
+
create_from_manifest(options[0])
|
170
|
+
release_filename = options[0]
|
171
|
+
else
|
172
|
+
release_filename = create_from_spec(*options)
|
173
|
+
end
|
174
|
+
|
175
|
+
if release_filename
|
176
|
+
release.latest_release_filename = release_filename
|
177
|
+
release.save_config
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def create_from_manifest(manifest_file)
|
182
|
+
say("Recreating release from the manifest")
|
183
|
+
Bosh::Cli::ReleaseCompiler.compile(manifest_file, release.blobstore)
|
184
|
+
end
|
185
|
+
|
186
|
+
def create_from_spec(*options)
|
187
|
+
flags = options.inject({}) { |h, option| h[option] = true; h }
|
188
|
+
|
189
|
+
final = flags.delete("--final")
|
190
|
+
force = flags.delete("--force")
|
191
|
+
manifest_only = !flags.delete("--with-tarball")
|
192
|
+
dry_run = flags.delete("--dry-run")
|
193
|
+
|
194
|
+
if final && !release.has_blobstore_secret?
|
195
|
+
say("Can't create final release without blobstore secret".red)
|
196
|
+
exit(1)
|
197
|
+
end
|
198
|
+
|
199
|
+
if flags.size > 0
|
200
|
+
say("Unknown flags: #{flags.keys.join(", ")}".red)
|
201
|
+
show_usage
|
202
|
+
exit(1)
|
203
|
+
end
|
204
|
+
|
205
|
+
if !force
|
206
|
+
check_dirty_blobs
|
207
|
+
check_if_dirty_state
|
208
|
+
end
|
209
|
+
|
210
|
+
confirmation = "Are you sure you want to " +
|
211
|
+
"generate #{'final'.red} version? "
|
212
|
+
|
213
|
+
if final && !dry_run && !confirmed?(confirmation)
|
214
|
+
say("Canceled release generation".green)
|
215
|
+
exit(1)
|
216
|
+
end
|
217
|
+
|
218
|
+
packages = []
|
219
|
+
jobs = []
|
220
|
+
|
221
|
+
if final
|
222
|
+
header("Building FINAL release".green)
|
223
|
+
release_name = release.final_name
|
224
|
+
else
|
225
|
+
release_name = release.dev_name
|
226
|
+
header("Building DEV release".green)
|
227
|
+
end
|
228
|
+
|
229
|
+
if version_greater(release.min_cli_version, Bosh::Cli::VERSION)
|
230
|
+
err("You should use CLI >= #{release.min_cli_version} " +
|
231
|
+
"with this release, you have #{Bosh::Cli::VERSION}")
|
232
|
+
end
|
233
|
+
|
234
|
+
if release_name.blank?
|
235
|
+
confirmation = "Please enter %s release name: " % [
|
236
|
+
final ? "final" : "development"]
|
237
|
+
name = interactive? ? ask(confirmation).to_s : DEFAULT_RELEASE_NAME
|
238
|
+
err("Canceled release creation, no name given") if name.blank?
|
239
|
+
if final
|
240
|
+
release.final_name = name
|
241
|
+
else
|
242
|
+
release.dev_name = name
|
243
|
+
end
|
244
|
+
release.save_config
|
245
|
+
end
|
246
|
+
|
247
|
+
header("Building packages")
|
248
|
+
Dir[File.join(work_dir, "packages", "*", "spec")].each do |package_spec|
|
249
|
+
package = Bosh::Cli::PackageBuilder.new(package_spec, work_dir,
|
250
|
+
final, release.blobstore)
|
251
|
+
package.dry_run = dry_run
|
252
|
+
say("Building #{package.name.green}...")
|
253
|
+
package.build
|
254
|
+
packages << package
|
255
|
+
nl
|
256
|
+
end
|
257
|
+
|
258
|
+
if packages.size > 0
|
259
|
+
package_index = packages.inject({}) do |index, package|
|
260
|
+
index[package.name] = package.dependencies
|
261
|
+
index
|
262
|
+
end
|
263
|
+
sorted_packages = tsort_packages(package_index)
|
264
|
+
header("Resolving dependencies")
|
265
|
+
say("Dependencies resolved, correct build order is:")
|
266
|
+
for package_name in sorted_packages
|
267
|
+
say("- %s" % [package_name])
|
268
|
+
end
|
269
|
+
nl
|
270
|
+
end
|
271
|
+
|
272
|
+
built_package_names = packages.map { |package| package.name }
|
273
|
+
|
274
|
+
header("Building jobs")
|
275
|
+
Dir[File.join(work_dir, "jobs", "*")].each do |job_dir|
|
276
|
+
next unless File.directory?(job_dir)
|
277
|
+
prepare_script = File.join(job_dir, "prepare")
|
278
|
+
job_spec = File.join(job_dir, "spec")
|
279
|
+
|
280
|
+
if File.exists?(prepare_script)
|
281
|
+
say("Found prepare script in `#{File.basename(job_dir)}'")
|
282
|
+
Bosh::Cli::JobBuilder.run_prepare_script(prepare_script)
|
283
|
+
end
|
284
|
+
|
285
|
+
job = Bosh::Cli::JobBuilder.new(job_spec, work_dir, final,
|
286
|
+
release.blobstore, built_package_names)
|
287
|
+
job.dry_run = dry_run
|
288
|
+
say("Building #{job.name.green}...")
|
289
|
+
job.build
|
290
|
+
jobs << job
|
291
|
+
nl
|
292
|
+
end
|
293
|
+
|
294
|
+
builder = Bosh::Cli::ReleaseBuilder.new(release, packages,
|
295
|
+
jobs, :final => final)
|
296
|
+
|
297
|
+
unless dry_run
|
298
|
+
if manifest_only
|
299
|
+
builder.build(:generate_tarball => false)
|
300
|
+
else
|
301
|
+
builder.build(:generate_tarball => true)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
header("Release summary")
|
306
|
+
show_summary(builder)
|
307
|
+
nl
|
308
|
+
|
309
|
+
return nil if dry_run
|
310
|
+
|
311
|
+
say("Release version: #{builder.version.to_s.green}")
|
312
|
+
say("Release manifest: #{builder.manifest_path.green}")
|
313
|
+
|
314
|
+
unless manifest_only
|
315
|
+
say("Release tarball (#{pretty_size(builder.tarball_path)}): " +
|
316
|
+
builder.tarball_path.green)
|
317
|
+
end
|
318
|
+
|
319
|
+
release.min_cli_version = Bosh::Cli::VERSION
|
320
|
+
release.save_config
|
321
|
+
|
322
|
+
builder.manifest_path
|
323
|
+
end
|
324
|
+
|
325
|
+
def reset
|
326
|
+
check_if_release_dir
|
327
|
+
|
328
|
+
say("Your dev release environment will be completely reset".red)
|
329
|
+
if confirmed?
|
330
|
+
say("Removing dev_builds index...")
|
331
|
+
FileUtils.rm_rf(".dev_builds")
|
332
|
+
say("Clearing dev name...")
|
333
|
+
release.dev_name = nil
|
334
|
+
release.save_config
|
335
|
+
say("Removing dev tarballs...")
|
336
|
+
FileUtils.rm_rf("dev_releases")
|
337
|
+
|
338
|
+
say("Release has been reset".green)
|
339
|
+
else
|
340
|
+
say("Canceled")
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def list
|
345
|
+
auth_required
|
346
|
+
releases = director.list_releases.sort do |r1, r2|
|
347
|
+
r1["name"] <=> r2["name"]
|
348
|
+
end
|
349
|
+
|
350
|
+
err("No releases") if releases.empty?
|
351
|
+
|
352
|
+
releases_table = table do |t|
|
353
|
+
t.headings = "Name", "Versions"
|
354
|
+
releases.each do |r|
|
355
|
+
versions = r["versions"].sort do |v1, v2|
|
356
|
+
version_cmp(v1, v2)
|
357
|
+
end
|
358
|
+
|
359
|
+
t << [r["name"], versions.join(", ")]
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
nl
|
364
|
+
say(releases_table)
|
365
|
+
nl
|
366
|
+
say("Releases total: %d" % releases.size)
|
367
|
+
end
|
368
|
+
|
369
|
+
def delete(name, *options)
|
370
|
+
auth_required
|
371
|
+
force = options.include?("--force")
|
372
|
+
options.delete("--force")
|
373
|
+
version = options.shift
|
374
|
+
|
375
|
+
desc = "release `#{name}'"
|
376
|
+
desc << " version #{version}" if version
|
377
|
+
|
378
|
+
if force
|
379
|
+
say("Deleting #{desc} (FORCED DELETE, WILL IGNORE ERRORS)".red)
|
380
|
+
elsif options.size > 0
|
381
|
+
err("Unknown option, currently only '--force' is supported")
|
382
|
+
else
|
383
|
+
say("Deleting #{desc}".red)
|
384
|
+
end
|
385
|
+
|
386
|
+
if confirmed?
|
387
|
+
status, body = director.delete_release(name, :force => force,
|
388
|
+
:version => version)
|
389
|
+
responses = {
|
390
|
+
:done => "Deleted #{desc}",
|
391
|
+
:non_trackable => "Started deleting release but director " +
|
392
|
+
"at '#{target}' doesn't support " +
|
393
|
+
"deployment tracking",
|
394
|
+
:track_timeout => "Started deleting release but timed out out " +
|
395
|
+
"while tracking status",
|
396
|
+
:error => "Started deleting release but received an error " +
|
397
|
+
"while tracking status",
|
398
|
+
}
|
399
|
+
|
400
|
+
say(responses[status] || "Cannot delete release: #{body}")
|
401
|
+
else
|
402
|
+
say("Canceled deleting release".green)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
private
|
407
|
+
|
408
|
+
def show_summary(builder)
|
409
|
+
packages_table = table do |t|
|
410
|
+
t.headings = %w(Name Version Notes Fingerprint)
|
411
|
+
builder.packages.each do |package|
|
412
|
+
t << artefact_summary(package)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
jobs_table = table do |t|
|
417
|
+
t.headings = %w(Name Version Notes Fingerprint)
|
418
|
+
builder.jobs.each do |job|
|
419
|
+
t << artefact_summary(job)
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
say("Packages")
|
424
|
+
say(packages_table)
|
425
|
+
nl
|
426
|
+
say("Jobs")
|
427
|
+
say(jobs_table)
|
428
|
+
|
429
|
+
affected_jobs = builder.affected_jobs
|
430
|
+
|
431
|
+
if affected_jobs.size > 0
|
432
|
+
nl
|
433
|
+
say("Jobs affected by changes in this release")
|
434
|
+
|
435
|
+
affected_jobs_table = table do |t|
|
436
|
+
t.headings = %w(Name Version)
|
437
|
+
affected_jobs.each do |job|
|
438
|
+
t << [job.name, job.version]
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
say(affected_jobs_table)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def artefact_summary(artefact)
|
447
|
+
result = []
|
448
|
+
result << artefact.name
|
449
|
+
result << artefact.version
|
450
|
+
result << artefact.notes.join(", ")
|
451
|
+
result << artefact.fingerprint
|
452
|
+
result
|
453
|
+
end
|
454
|
+
|
455
|
+
def get_remote_release(name)
|
456
|
+
release = director.get_release(name)
|
457
|
+
|
458
|
+
unless release.is_a?(Hash) &&
|
459
|
+
release.has_key?("jobs") &&
|
460
|
+
release.has_key?("packages")
|
461
|
+
raise Bosh::Cli::DirectorError,
|
462
|
+
"Cannot find version, jobs and packages info " +
|
463
|
+
"in the director response, maybe old director?"
|
464
|
+
end
|
465
|
+
|
466
|
+
release
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|