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.
- data/bin/bosh +0 -9
- data/lib/cli.rb +69 -64
- data/lib/cli/backup_destination_path.rb +33 -0
- data/lib/cli/base_command.rb +57 -56
- data/lib/cli/blob_manager.rb +12 -12
- data/lib/cli/changeset_helper.rb +6 -7
- data/lib/cli/client/director.rb +724 -0
- data/lib/cli/command_handler.rb +6 -7
- data/lib/cli/commands/backup.rb +39 -0
- data/lib/cli/commands/biff.rb +42 -21
- data/lib/cli/commands/blob_management.rb +1 -1
- data/lib/cli/commands/cloudcheck.rb +11 -13
- data/lib/cli/commands/deployment.rb +53 -37
- data/lib/cli/commands/help.rb +3 -2
- data/lib/cli/commands/job_management.rb +67 -103
- data/lib/cli/commands/job_rename.rb +6 -8
- data/lib/cli/commands/log_management.rb +78 -55
- data/lib/cli/commands/maintenance.rb +36 -30
- data/lib/cli/commands/misc.rb +72 -51
- data/lib/cli/commands/package.rb +2 -2
- data/lib/cli/commands/property_management.rb +10 -12
- data/lib/cli/commands/release.rb +236 -133
- data/lib/cli/commands/snapshot.rb +93 -0
- data/lib/cli/commands/ssh.rb +216 -213
- data/lib/cli/commands/stemcell.rb +46 -34
- data/lib/cli/commands/task.rb +2 -2
- data/lib/cli/commands/user.rb +27 -3
- data/lib/cli/commands/vm.rb +28 -0
- data/lib/cli/commands/vms.rb +81 -23
- data/lib/cli/config.rb +6 -2
- data/lib/cli/core_ext.rb +31 -30
- data/lib/cli/deployment_helper.rb +134 -159
- data/lib/cli/deployment_manifest.rb +66 -0
- data/lib/cli/deployment_manifest_compiler.rb +0 -3
- data/lib/cli/event_log_renderer.rb +10 -10
- data/lib/cli/file_with_progress_bar.rb +52 -0
- data/lib/cli/job_builder.rb +1 -1
- data/lib/cli/job_command_args.rb +23 -0
- data/lib/cli/job_property_collection.rb +4 -7
- data/lib/cli/job_property_validator.rb +22 -12
- data/lib/cli/job_state.rb +54 -0
- data/lib/cli/line_wrap.rb +54 -0
- data/lib/cli/packaging_helper.rb +10 -10
- data/lib/cli/release.rb +18 -15
- data/lib/cli/release_builder.rb +9 -4
- data/lib/cli/release_compiler.rb +9 -9
- data/lib/cli/release_tarball.rb +3 -6
- data/lib/cli/resurrection.rb +31 -0
- data/lib/cli/runner.rb +56 -30
- data/lib/cli/stemcell.rb +25 -10
- data/lib/cli/task_log_renderer.rb +1 -1
- data/lib/cli/task_tracker.rb +10 -9
- data/lib/cli/validation.rb +3 -1
- data/lib/cli/version.rb +1 -1
- data/lib/cli/version_calc.rb +5 -18
- data/lib/cli/versions_index.rb +1 -1
- data/lib/cli/vm_state.rb +43 -0
- data/lib/cli/yaml_helper.rb +26 -35
- metadata +75 -208
- data/Rakefile +0 -56
- data/lib/cli/director.rb +0 -628
- data/spec/assets/biff/bad_gateway_config.yml +0 -28
- data/spec/assets/biff/good_simple_config.yml +0 -63
- data/spec/assets/biff/good_simple_golden_config.yml +0 -63
- data/spec/assets/biff/good_simple_template.erb +0 -69
- data/spec/assets/biff/ip_out_of_range.yml +0 -63
- data/spec/assets/biff/multiple_subnets_config.yml +0 -40
- data/spec/assets/biff/network_only_template.erb +0 -34
- data/spec/assets/biff/no_cc_config.yml +0 -27
- data/spec/assets/biff/no_range_config.yml +0 -27
- data/spec/assets/biff/no_subnet_config.yml +0 -16
- data/spec/assets/biff/ok_network_config.yml +0 -30
- data/spec/assets/biff/properties_template.erb +0 -6
- data/spec/assets/config/atmos/config/final.yml +0 -6
- data/spec/assets/config/atmos/config/private.yml +0 -4
- data/spec/assets/config/bad-providers/config/final.yml +0 -5
- data/spec/assets/config/bad-providers/config/private.yml +0 -4
- data/spec/assets/config/deprecation/config/final.yml +0 -5
- data/spec/assets/config/deprecation/config/private.yml +0 -2
- data/spec/assets/config/local/config/final.yml +0 -5
- data/spec/assets/config/local/config/private.yml +0 -1
- data/spec/assets/config/s3/config/final.yml +0 -5
- data/spec/assets/config/s3/config/private.yml +0 -5
- data/spec/assets/config/swift-hp/config/final.yml +0 -6
- data/spec/assets/config/swift-hp/config/private.yml +0 -7
- data/spec/assets/config/swift-rackspace/config/final.yml +0 -6
- data/spec/assets/config/swift-rackspace/config/private.yml +0 -6
- data/spec/assets/deployment.MF +0 -0
- data/spec/assets/plugins/bosh/cli/commands/echo.rb +0 -43
- data/spec/assets/plugins/bosh/cli/commands/ruby.rb +0 -24
- 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 +0 -6
- data/spec/assets/release/jobs/cacher/monit +0 -1
- data/spec/assets/release/jobs/cleaner.tgz +0 -0
- data/spec/assets/release/jobs/cleaner/job.MF +0 -4
- data/spec/assets/release/jobs/cleaner/monit +0 -1
- data/spec/assets/release/jobs/sweeper.tgz +0 -0
- data/spec/assets/release/jobs/sweeper/config/test.conf +0 -1
- data/spec/assets/release/jobs/sweeper/job.MF +0 -5
- data/spec/assets/release/jobs/sweeper/monit +0 -1
- 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 +0 -17
- 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 +0 -1
- data/spec/assets/stemcell/stemcell.MF +0 -6
- 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 +0 -28
- data/spec/unit/base_command_spec.rb +0 -87
- data/spec/unit/biff_spec.rb +0 -172
- data/spec/unit/blob_manager_spec.rb +0 -288
- data/spec/unit/cache_spec.rb +0 -36
- data/spec/unit/cli_commands_spec.rb +0 -356
- data/spec/unit/config_spec.rb +0 -125
- data/spec/unit/core_ext_spec.rb +0 -81
- data/spec/unit/dependency_helper_spec.rb +0 -52
- data/spec/unit/deployment_manifest_compiler_spec.rb +0 -63
- data/spec/unit/deployment_manifest_spec.rb +0 -153
- data/spec/unit/director_spec.rb +0 -471
- data/spec/unit/director_task_spec.rb +0 -48
- data/spec/unit/event_log_renderer_spec.rb +0 -171
- data/spec/unit/hash_changeset_spec.rb +0 -73
- data/spec/unit/job_builder_spec.rb +0 -455
- data/spec/unit/job_property_collection_spec.rb +0 -111
- data/spec/unit/job_property_validator_spec.rb +0 -7
- data/spec/unit/job_rename_spec.rb +0 -200
- data/spec/unit/package_builder_spec.rb +0 -593
- data/spec/unit/release_builder_spec.rb +0 -120
- data/spec/unit/release_spec.rb +0 -173
- data/spec/unit/release_tarball_spec.rb +0 -29
- data/spec/unit/runner_spec.rb +0 -7
- data/spec/unit/ssh_spec.rb +0 -84
- data/spec/unit/stemcell_spec.rb +0 -17
- data/spec/unit/task_tracker_spec.rb +0 -131
- data/spec/unit/version_calc_spec.rb +0 -27
- data/spec/unit/versions_index_spec.rb +0 -144
|
@@ -5,7 +5,6 @@ module Bosh::Cli
|
|
|
5
5
|
include VersionCalc
|
|
6
6
|
|
|
7
7
|
def prepare_deployment_manifest(options = {})
|
|
8
|
-
# TODO: extract to helper class
|
|
9
8
|
deployment_required
|
|
10
9
|
manifest_filename = deployment
|
|
11
10
|
|
|
@@ -16,11 +15,11 @@ module Bosh::Cli
|
|
|
16
15
|
manifest = load_yaml_file(manifest_filename)
|
|
17
16
|
manifest_yaml = File.read(manifest_filename)
|
|
18
17
|
|
|
19
|
-
if manifest[
|
|
20
|
-
err(
|
|
18
|
+
if manifest['name'].blank?
|
|
19
|
+
err('Deployment name not found in the deployment manifest')
|
|
21
20
|
end
|
|
22
21
|
|
|
23
|
-
if manifest[
|
|
22
|
+
if manifest['target']
|
|
24
23
|
err(manifest_target_upgrade_notice)
|
|
25
24
|
end
|
|
26
25
|
|
|
@@ -29,40 +28,40 @@ module Bosh::Cli
|
|
|
29
28
|
properties = {}
|
|
30
29
|
|
|
31
30
|
begin
|
|
32
|
-
say(
|
|
33
|
-
properties = director.list_properties(manifest[
|
|
31
|
+
say('Getting deployment properties from director...')
|
|
32
|
+
properties = director.list_properties(manifest['name'])
|
|
34
33
|
rescue Bosh::Cli::DirectorError
|
|
35
|
-
say(
|
|
36
|
-
|
|
34
|
+
say('Unable to get properties list from director, ' +
|
|
35
|
+
'trying without it...')
|
|
37
36
|
end
|
|
38
37
|
|
|
39
|
-
say(
|
|
38
|
+
say('Compiling deployment manifest...')
|
|
40
39
|
compiler.properties = properties.inject({}) do |hash, property|
|
|
41
|
-
hash[property[
|
|
40
|
+
hash[property['name']] = property['value']
|
|
42
41
|
hash
|
|
43
42
|
end
|
|
44
43
|
|
|
45
|
-
manifest =
|
|
44
|
+
manifest = Psych.load(compiler.result)
|
|
46
45
|
end
|
|
47
46
|
|
|
48
|
-
if manifest[
|
|
47
|
+
if manifest['name'].blank? || manifest['director_uuid'].blank?
|
|
49
48
|
err("Invalid manifest `#{File.basename(deployment)}': " +
|
|
50
|
-
|
|
49
|
+
'name and director UUID are required')
|
|
51
50
|
end
|
|
52
51
|
|
|
53
|
-
if director.uuid != manifest[
|
|
52
|
+
if director.uuid != manifest['director_uuid']
|
|
54
53
|
err("Target director UUID doesn't match UUID from deployment manifest")
|
|
55
54
|
end
|
|
56
55
|
|
|
57
|
-
if manifest[
|
|
56
|
+
if manifest['release'].blank? && manifest['releases'].blank?
|
|
58
57
|
err("Deployment manifest doesn't have release information: '" +
|
|
59
|
-
|
|
58
|
+
"please add 'release' or 'releases' section")
|
|
60
59
|
end
|
|
61
60
|
|
|
62
61
|
resolve_release_aliases(manifest)
|
|
63
62
|
resolve_stemcell_aliases(manifest)
|
|
64
63
|
|
|
65
|
-
options[:yaml] ?
|
|
64
|
+
options[:yaml] ? Psych.dump(manifest) : manifest
|
|
66
65
|
end
|
|
67
66
|
|
|
68
67
|
# Check if the 2 deployments are different.
|
|
@@ -91,7 +90,7 @@ module Bosh::Cli
|
|
|
91
90
|
# if something goes wrong, so it doesn't need to have
|
|
92
91
|
# a meaningful return value.
|
|
93
92
|
# @return Boolean Were there any changes in deployment manifest?
|
|
94
|
-
def inspect_deployment_changes(manifest, options = {
|
|
93
|
+
def inspect_deployment_changes(manifest, options = {})
|
|
95
94
|
show_empty_changeset = true
|
|
96
95
|
|
|
97
96
|
if options.has_key?(:show_empty_changeset)
|
|
@@ -99,27 +98,26 @@ module Bosh::Cli
|
|
|
99
98
|
end
|
|
100
99
|
|
|
101
100
|
manifest = manifest.dup
|
|
102
|
-
current_deployment = director.get_deployment(manifest[
|
|
101
|
+
current_deployment = director.get_deployment(manifest['name'])
|
|
103
102
|
|
|
104
103
|
# We cannot retrieve current manifest until there was at least one
|
|
105
104
|
# successful deployment. There used to be a warning about that
|
|
106
105
|
# but it turned out to be confusing to many users and thus has
|
|
107
106
|
# been removed.
|
|
108
|
-
return if current_deployment[
|
|
109
|
-
current_manifest =
|
|
107
|
+
return if current_deployment['manifest'].nil?
|
|
108
|
+
current_manifest = Psych.load(current_deployment['manifest'])
|
|
110
109
|
|
|
111
110
|
unless current_manifest.is_a?(Hash)
|
|
112
|
-
err(
|
|
113
|
-
|
|
111
|
+
err('Current deployment manifest format is invalid, ' +
|
|
112
|
+
'check if director works properly')
|
|
114
113
|
end
|
|
115
114
|
|
|
116
|
-
# TODO: validate new deployment manifest
|
|
117
115
|
diff = Bosh::Cli::HashChangeset.new
|
|
118
116
|
diff.add_hash(normalize_deployment_manifest(manifest), :new)
|
|
119
117
|
diff.add_hash(normalize_deployment_manifest(current_manifest), :old)
|
|
120
|
-
@_diff_key_visited = {
|
|
118
|
+
@_diff_key_visited = { 'name' => 1, 'director_uuid' => 1 }
|
|
121
119
|
|
|
122
|
-
say(
|
|
120
|
+
say('Detecting changes in deployment...'.make_green)
|
|
123
121
|
nl
|
|
124
122
|
|
|
125
123
|
if !diff.changed? && !show_empty_changeset
|
|
@@ -163,16 +161,16 @@ module Bosh::Cli
|
|
|
163
161
|
end
|
|
164
162
|
|
|
165
163
|
if old_stemcells != new_stemcells
|
|
166
|
-
unless confirmed?(
|
|
167
|
-
|
|
164
|
+
unless confirmed?('Stemcell update has been detected. ' +
|
|
165
|
+
'Are you sure you want to update stemcells?')
|
|
168
166
|
cancel_deployment
|
|
169
167
|
end
|
|
170
168
|
end
|
|
171
169
|
|
|
172
170
|
if old_stemcells.size != new_stemcells.size
|
|
173
|
-
say(
|
|
174
|
-
|
|
175
|
-
unless confirmed?(
|
|
171
|
+
say('Stemcell update seems to be inconsistent with current '.make_red +
|
|
172
|
+
'deployment. Please carefully review changes above.'.make_red)
|
|
173
|
+
unless confirmed?('Are you sure this configuration is correct?')
|
|
176
174
|
cancel_deployment
|
|
177
175
|
end
|
|
178
176
|
end
|
|
@@ -194,31 +192,102 @@ module Bosh::Cli
|
|
|
194
192
|
|
|
195
193
|
diff.changed?
|
|
196
194
|
rescue Bosh::Cli::ResourceNotFound
|
|
197
|
-
say(
|
|
198
|
-
|
|
195
|
+
say('Cannot get current deployment information from director, ' +
|
|
196
|
+
'possibly a new deployment'.make_red)
|
|
199
197
|
true
|
|
200
198
|
end
|
|
201
199
|
|
|
202
|
-
|
|
200
|
+
def latest_release_versions
|
|
201
|
+
@_latest_release_versions ||= begin
|
|
202
|
+
director.list_releases.inject({}) do |hash, release|
|
|
203
|
+
name = release['name']
|
|
204
|
+
versions = release['versions'] || release['release_versions'].map { |release_version| release_version['version'] }
|
|
205
|
+
latest_version = versions.map { |v| Bosh::Common::VersionNumber.new(v) }.max
|
|
206
|
+
hash[name] = latest_version.to_s
|
|
207
|
+
hash
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
203
211
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
# @param [Hash] manifest Deployment manifest (will be modified)
|
|
213
|
+
# @return [void]
|
|
214
|
+
def resolve_release_aliases(manifest)
|
|
215
|
+
releases = manifest['releases'] || [manifest['release']]
|
|
216
|
+
|
|
217
|
+
releases.each do |release|
|
|
218
|
+
if release['version'] == 'latest'
|
|
219
|
+
latest_release_version = latest_release_versions[release['name']]
|
|
220
|
+
unless latest_release_version
|
|
221
|
+
err("Release '#{release['name']}' not found on director. Unable to resolve 'latest' alias in manifest.")
|
|
222
|
+
end
|
|
223
|
+
release['version'] = latest_release_version
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
if release['version'].to_i.to_s == release['version']
|
|
227
|
+
release['version'] = release['version'].to_i
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def job_unique_in_deployment?(job_name)
|
|
233
|
+
job = find_job(job_name)
|
|
234
|
+
job.fetch('instances') == 1 if job
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def job_exists_in_deployment?(job_name)
|
|
238
|
+
!!find_job(job_name)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def job_must_exist_in_deployment(job)
|
|
242
|
+
err("Job `#{job}' doesn't exist") unless job_exists_in_deployment?(job)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def prompt_for_job_and_index
|
|
246
|
+
jobs_list = jobs_and_indexes
|
|
247
|
+
|
|
248
|
+
return jobs_list.first if jobs_list.size == 1
|
|
249
|
+
|
|
250
|
+
choose do |menu|
|
|
251
|
+
menu.prompt = 'Choose an instance: '
|
|
252
|
+
jobs_list.each do |job_name, index|
|
|
253
|
+
menu.choice("#{job_name}/#{index}") { [job_name, index] }
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def jobs_and_indexes
|
|
259
|
+
jobs = prepare_deployment_manifest.fetch('jobs')
|
|
260
|
+
|
|
261
|
+
jobs.inject([]) do |jobs_and_indexes, job|
|
|
262
|
+
job_name = job.fetch('name')
|
|
263
|
+
0.upto(job.fetch('instances').to_i - 1) do |index|
|
|
264
|
+
jobs_and_indexes << [job_name, index]
|
|
265
|
+
end
|
|
266
|
+
jobs_and_indexes
|
|
209
267
|
end
|
|
210
268
|
end
|
|
211
269
|
|
|
212
270
|
def cancel_deployment
|
|
213
|
-
quit(
|
|
271
|
+
quit('Deployment canceled'.make_red)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
private
|
|
275
|
+
|
|
276
|
+
def find_job(job_name)
|
|
277
|
+
jobs = prepare_deployment_manifest.fetch('jobs')
|
|
278
|
+
jobs.find { |job| job.fetch('name') == job_name }
|
|
214
279
|
end
|
|
215
280
|
|
|
216
|
-
def
|
|
217
|
-
|
|
281
|
+
def find_deployment(name)
|
|
282
|
+
if File.exists?(name)
|
|
283
|
+
File.expand_path(name)
|
|
284
|
+
else
|
|
285
|
+
File.expand_path(File.join(work_dir, 'deployments', "#{name}.yml"))
|
|
286
|
+
end
|
|
218
287
|
end
|
|
219
288
|
|
|
220
289
|
def manifest_target_upgrade_notice
|
|
221
|
-
<<-EOS.gsub(/^\s*/,
|
|
290
|
+
<<-EOS.gsub(/^\s*/, '').gsub(/\n$/, '')
|
|
222
291
|
Please upgrade your deployment manifest to use director UUID instead
|
|
223
292
|
of target. Just replace 'target' key with 'director_uuid' key in your
|
|
224
293
|
manifest. You can get your director UUID by targeting your director
|
|
@@ -227,130 +296,53 @@ module Bosh::Cli
|
|
|
227
296
|
end
|
|
228
297
|
|
|
229
298
|
def print_summary(diff, key, title = nil)
|
|
230
|
-
title ||= key.to_s.gsub(/[-_]/,
|
|
299
|
+
title ||= key.to_s.gsub(/[-_]/, ' ').capitalize
|
|
231
300
|
|
|
232
|
-
say(title.
|
|
301
|
+
say(title.make_green)
|
|
233
302
|
summary = diff[key].summary
|
|
234
303
|
if summary.empty?
|
|
235
|
-
say(
|
|
304
|
+
say('No changes')
|
|
236
305
|
else
|
|
237
306
|
say(summary.join("\n"))
|
|
238
307
|
end
|
|
239
308
|
@_diff_key_visited[key.to_s] = 1
|
|
240
309
|
end
|
|
241
310
|
|
|
242
|
-
def normalize_deployment_manifest(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
%w(releases networks jobs resource_pools).each do |section|
|
|
246
|
-
normalized[section] ||= []
|
|
247
|
-
|
|
248
|
-
unless normalized[section].kind_of?(Array)
|
|
249
|
-
manifest_error("#{section} is expected to be an array")
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
normalized[section] = normalized[section].inject({}) do |acc, e|
|
|
253
|
-
if e["name"].blank?
|
|
254
|
-
manifest_error("missing name for one of entries in '#{section}'")
|
|
255
|
-
end
|
|
256
|
-
if acc.has_key?(e["name"])
|
|
257
|
-
manifest_error("duplicate entry '#{e['name']}' in '#{section}'")
|
|
258
|
-
end
|
|
259
|
-
acc[e["name"]] = e
|
|
260
|
-
acc
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
normalized["networks"].each do |network_name, network|
|
|
265
|
-
# VIP and dynamic networks do not require subnet,
|
|
266
|
-
# but if it's there we can run some sanity checks
|
|
267
|
-
next unless network.has_key?("subnets")
|
|
268
|
-
|
|
269
|
-
unless network["subnets"].kind_of?(Array)
|
|
270
|
-
manifest_error("network subnets is expected to be an array")
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
subnets = network["subnets"].inject({}) do |acc, e|
|
|
274
|
-
if e["range"].blank?
|
|
275
|
-
manifest_error("missing range for one of subnets " +
|
|
276
|
-
"in '#{network_name}'")
|
|
277
|
-
end
|
|
278
|
-
if acc.has_key?(e["range"])
|
|
279
|
-
manifest_error("duplicate network range '#{e['range']}' " +
|
|
280
|
-
"in '#{network}'")
|
|
281
|
-
end
|
|
282
|
-
acc[e["range"]] = e
|
|
283
|
-
acc
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
normalized["networks"][network_name]["subnets"] = subnets
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
normalized
|
|
311
|
+
def normalize_deployment_manifest(manifest_hash)
|
|
312
|
+
DeploymentManifest.new(manifest_hash).normalize
|
|
290
313
|
end
|
|
291
314
|
|
|
292
315
|
def warn_about_release_changes(release_diff)
|
|
293
316
|
if release_diff[:name].changed?
|
|
294
|
-
say(
|
|
317
|
+
say('Release name has changed: %s -> %s'.make_red % [
|
|
295
318
|
release_diff[:name].old, release_diff[:name].new])
|
|
296
|
-
unless confirmed?(
|
|
297
|
-
|
|
319
|
+
unless confirmed?('This is very serious and potentially destructive ' +
|
|
320
|
+
'change. ARE YOU SURE YOU WANT TO DO IT?')
|
|
298
321
|
cancel_deployment
|
|
299
322
|
end
|
|
300
323
|
elsif release_diff[:version].changed?
|
|
301
|
-
say(
|
|
324
|
+
say('Release version has changed: %s -> %s'.make_yellow % [
|
|
302
325
|
release_diff[:version].old, release_diff[:version].new])
|
|
303
|
-
unless confirmed?(
|
|
326
|
+
unless confirmed?('Are you sure you want to deploy this version?')
|
|
304
327
|
cancel_deployment
|
|
305
328
|
end
|
|
306
329
|
end
|
|
307
330
|
end
|
|
308
331
|
|
|
309
|
-
# @param [Hash] manifest Deployment manifest (will be modified)
|
|
310
|
-
# @return [void]
|
|
311
|
-
def resolve_release_aliases(manifest)
|
|
312
|
-
if manifest["release"]
|
|
313
|
-
if manifest["releases"]
|
|
314
|
-
err("Manifest has both `release' and `releases', please fix it")
|
|
315
|
-
end
|
|
316
|
-
releases = [manifest["release"]]
|
|
317
|
-
else
|
|
318
|
-
releases = manifest["releases"]
|
|
319
|
-
end
|
|
320
|
-
|
|
321
|
-
unless releases.is_a?(Array)
|
|
322
|
-
err("Invalid release spec in the deployment manifest")
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
releases.each do |release|
|
|
326
|
-
if release["version"] == "latest"
|
|
327
|
-
latest_version = latest_releases[release["name"]]
|
|
328
|
-
if latest_version.nil?
|
|
329
|
-
err("Latest version for release `#{release["name"]}' is unknown")
|
|
330
|
-
end
|
|
331
|
-
# Avoiding Fixnum -> String noise in diff
|
|
332
|
-
if latest_version.to_s == latest_version.to_i.to_s
|
|
333
|
-
latest_version = latest_version.to_i
|
|
334
|
-
end
|
|
335
|
-
release["version"] = latest_version
|
|
336
|
-
end
|
|
337
|
-
end
|
|
338
|
-
end
|
|
339
|
-
|
|
340
332
|
# @param [Hash] manifest Deployment manifest (will be modified)
|
|
341
333
|
# @return [void]
|
|
342
334
|
def resolve_stemcell_aliases(manifest)
|
|
343
|
-
return if manifest[
|
|
335
|
+
return if manifest['resource_pools'].nil?
|
|
344
336
|
|
|
345
|
-
manifest[
|
|
346
|
-
stemcell = rp[
|
|
337
|
+
manifest['resource_pools'].each do |rp|
|
|
338
|
+
stemcell = rp['stemcell']
|
|
347
339
|
unless stemcell.is_a?(Hash)
|
|
348
|
-
err(
|
|
340
|
+
err('Invalid stemcell spec in the deployment manifest')
|
|
349
341
|
end
|
|
350
|
-
if stemcell[
|
|
351
|
-
latest_version = latest_stemcells[stemcell[
|
|
342
|
+
if stemcell['version'] == 'latest'
|
|
343
|
+
latest_version = latest_stemcells[stemcell['name']]
|
|
352
344
|
if latest_version.nil?
|
|
353
|
-
err("Latest version for stemcell `#{stemcell[
|
|
345
|
+
err("Latest version for stemcell `#{stemcell['name']}' is unknown")
|
|
354
346
|
end
|
|
355
347
|
# Avoiding {Float,Fixnum} -> String noise in diff
|
|
356
348
|
if latest_version.to_s == latest_version.to_f.to_s
|
|
@@ -358,7 +350,7 @@ module Bosh::Cli
|
|
|
358
350
|
elsif latest_version.to_s == latest_version.to_i.to_s
|
|
359
351
|
latest_version = latest_version.to_i
|
|
360
352
|
end
|
|
361
|
-
stemcell[
|
|
353
|
+
stemcell['version'] = latest_version
|
|
362
354
|
end
|
|
363
355
|
end
|
|
364
356
|
end
|
|
@@ -367,11 +359,11 @@ module Bosh::Cli
|
|
|
367
359
|
def latest_stemcells
|
|
368
360
|
@_latest_stemcells ||= begin
|
|
369
361
|
stemcells = director.list_stemcells.inject({}) do |hash, stemcell|
|
|
370
|
-
unless stemcell.is_a?(Hash) && stemcell[
|
|
371
|
-
err(
|
|
362
|
+
unless stemcell.is_a?(Hash) && stemcell['name'] && stemcell['version']
|
|
363
|
+
err('Invalid director stemcell list format')
|
|
372
364
|
end
|
|
373
|
-
hash[stemcell[
|
|
374
|
-
hash[stemcell[
|
|
365
|
+
hash[stemcell['name']] ||= []
|
|
366
|
+
hash[stemcell['name']] << stemcell['version']
|
|
375
367
|
hash
|
|
376
368
|
end
|
|
377
369
|
|
|
@@ -381,22 +373,5 @@ module Bosh::Cli
|
|
|
381
373
|
end
|
|
382
374
|
end
|
|
383
375
|
end
|
|
384
|
-
|
|
385
|
-
# @return [Array]
|
|
386
|
-
def latest_releases
|
|
387
|
-
@_latest_releases ||= begin
|
|
388
|
-
director.list_releases.inject({}) do |hash, release|
|
|
389
|
-
unless release["name"].is_a?(String) &&
|
|
390
|
-
release["versions"].is_a?(Array)
|
|
391
|
-
err("Invalid director release list format")
|
|
392
|
-
end
|
|
393
|
-
hash[release["name"]] = release["versions"].sort { |v1, v2|
|
|
394
|
-
version_cmp(v2, v1)
|
|
395
|
-
}.first
|
|
396
|
-
hash
|
|
397
|
-
end
|
|
398
|
-
end
|
|
399
|
-
end
|
|
400
|
-
|
|
401
376
|
end
|
|
402
377
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'common/deep_copy'
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli
|
|
4
|
+
class DeploymentManifest
|
|
5
|
+
def initialize(manifest_hash)
|
|
6
|
+
@manifest_hash = manifest_hash
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def normalize
|
|
10
|
+
normalized = Bosh::Common::DeepCopy.copy(manifest_hash)
|
|
11
|
+
|
|
12
|
+
%w(releases networks jobs resource_pools).each do |section|
|
|
13
|
+
normalized[section] ||= []
|
|
14
|
+
|
|
15
|
+
unless normalized[section].kind_of?(Array)
|
|
16
|
+
manifest_error("#{section} is expected to be an array")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
normalized[section] = normalized[section].inject({}) do |acc, e|
|
|
20
|
+
if e["name"].blank?
|
|
21
|
+
manifest_error("missing name for one of entries in '#{section}'")
|
|
22
|
+
end
|
|
23
|
+
if acc.has_key?(e["name"])
|
|
24
|
+
manifest_error("duplicate entry '#{e['name']}' in '#{section}'")
|
|
25
|
+
end
|
|
26
|
+
acc[e["name"]] = e
|
|
27
|
+
acc
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
normalized["networks"].each do |network_name, network|
|
|
32
|
+
# VIP and dynamic networks do not require subnet,
|
|
33
|
+
# but if it's there we can run some sanity checks
|
|
34
|
+
next unless network.has_key?("subnets")
|
|
35
|
+
|
|
36
|
+
unless network["subnets"].kind_of?(Array)
|
|
37
|
+
manifest_error("network subnets is expected to be an array")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
subnets = network["subnets"].inject({}) do |acc, e|
|
|
41
|
+
if e["range"].blank?
|
|
42
|
+
manifest_error("missing range for one of subnets " +
|
|
43
|
+
"in '#{network_name}'")
|
|
44
|
+
end
|
|
45
|
+
if acc.has_key?(e["range"])
|
|
46
|
+
manifest_error("duplicate network range '#{e['range']}' " +
|
|
47
|
+
"in '#{network}'")
|
|
48
|
+
end
|
|
49
|
+
acc[e["range"]] = e
|
|
50
|
+
acc
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
normalized["networks"][network_name]["subnets"] = subnets
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
normalized
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
attr_reader :manifest_hash
|
|
61
|
+
|
|
62
|
+
def manifest_error(err)
|
|
63
|
+
err("Deployment manifest error: #{err}")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|