bosh_cli 0.16
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/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
data/lib/cli/core_ext.rb
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
module BoshExtensions
|
|
4
|
+
|
|
5
|
+
def say(message, sep = "\n")
|
|
6
|
+
return unless Bosh::Cli::Config.output && message
|
|
7
|
+
message = message.dup.to_s
|
|
8
|
+
sep = "" if message[-1..-1] == sep
|
|
9
|
+
Bosh::Cli::Config.output.print("#{$indent}#{message}#{sep}")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def with_indent(indent)
|
|
13
|
+
old_indent, $indent = $indent, old_indent.to_s + indent.to_s
|
|
14
|
+
yield
|
|
15
|
+
ensure
|
|
16
|
+
$indent = old_indent
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def header(message, filler = '-')
|
|
20
|
+
say("\n")
|
|
21
|
+
say(message)
|
|
22
|
+
say(filler.to_s * message.size)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def nl(count = 1)
|
|
26
|
+
say("\n" * count)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def err(message)
|
|
30
|
+
raise Bosh::Cli::CliExit.new message
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def quit(message = nil)
|
|
34
|
+
say(message)
|
|
35
|
+
raise Bosh::Cli::GracefulExit, message
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def blank?
|
|
39
|
+
self.to_s.blank?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def pretty_size(what, prec=1)
|
|
43
|
+
if what.is_a?(String) && File.exists?(what)
|
|
44
|
+
size = File.size(what)
|
|
45
|
+
else
|
|
46
|
+
size = what.to_i
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
return "NA" unless size
|
|
50
|
+
return "#{size}B" if size < 1024
|
|
51
|
+
return sprintf("%.#{prec}fK", size/1024.0) if size < (1024*1024)
|
|
52
|
+
if size < (1024*1024*1024)
|
|
53
|
+
return sprintf("%.#{prec}fM", size/(1024.0*1024.0))
|
|
54
|
+
end
|
|
55
|
+
sprintf("%.#{prec}fG", size/(1024.0*1024.0*1024.0))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def pluralize(number, singular, plural = nil)
|
|
59
|
+
plural = plural || "#{singular}s"
|
|
60
|
+
number == 1 ? "1 #{singular}" : "#{number} #{plural}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def format_time(time)
|
|
64
|
+
ts = time.to_i
|
|
65
|
+
sprintf("%02d:%02d:%02d", ts / 3600, (ts / 60) % 60, ts % 60);
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def load_yaml_file(path, expected_type = Hash)
|
|
69
|
+
err("Cannot find file `#{path}'") unless File.exists?(path)
|
|
70
|
+
yaml = YAML.load_file(path)
|
|
71
|
+
|
|
72
|
+
if expected_type && !yaml.is_a?(expected_type)
|
|
73
|
+
err("Incorrect file format in `#{path}', #{expected_type} expected")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
Bosh::Cli::YamlHelper.check_duplicate_keys(path)
|
|
77
|
+
|
|
78
|
+
yaml
|
|
79
|
+
rescue SystemCallError => e
|
|
80
|
+
err("Cannot load YAML file at `#{path}': #{e}")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def dump_yaml_to_file(obj, file)
|
|
84
|
+
yaml = YAML.dump(obj)
|
|
85
|
+
file.write(yaml.gsub(" \n", "\n"))
|
|
86
|
+
file.flush
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
module BoshStringExtensions
|
|
91
|
+
|
|
92
|
+
COLOR_CODES = {
|
|
93
|
+
:red => "\e[0m\e[31m",
|
|
94
|
+
:green => "\e[0m\e[32m",
|
|
95
|
+
:yellow => "\e[0m\e[33m"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
def red
|
|
99
|
+
colorize(:red)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def green
|
|
103
|
+
colorize(:green)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def yellow
|
|
107
|
+
colorize(:yellow)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def colorize(color_code)
|
|
111
|
+
if Bosh::Cli::Config.colorize && COLOR_CODES[color_code]
|
|
112
|
+
"#{COLOR_CODES[color_code]}#{self}\e[0m"
|
|
113
|
+
else
|
|
114
|
+
self
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def blank?
|
|
119
|
+
self =~ /^\s*$/
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def bosh_valid_id?
|
|
123
|
+
self =~ Bosh::Cli::Config::VALID_ID
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def truncate(limit = 30)
|
|
127
|
+
return "" if self.blank?
|
|
128
|
+
etc = "..."
|
|
129
|
+
stripped = self.strip[0..limit]
|
|
130
|
+
if stripped.length > limit
|
|
131
|
+
stripped.gsub(/\s+?(\S+)?$/, "") + etc
|
|
132
|
+
else
|
|
133
|
+
stripped
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
class Object
|
|
140
|
+
include BoshExtensions
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
class String
|
|
144
|
+
include BoshStringExtensions
|
|
145
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli
|
|
4
|
+
module DependencyHelper
|
|
5
|
+
|
|
6
|
+
# Expects package dependency graph
|
|
7
|
+
# { "A" => ["B", "C"], "B" => ["C", "D"] }
|
|
8
|
+
def tsort_packages(map)
|
|
9
|
+
resolved = Set.new
|
|
10
|
+
in_degree = { }
|
|
11
|
+
graph = { }
|
|
12
|
+
|
|
13
|
+
map.keys.sort.each do |package|
|
|
14
|
+
dependencies = map[package]
|
|
15
|
+
graph[package] ||= Set.new
|
|
16
|
+
in_degree[package] = dependencies.size
|
|
17
|
+
|
|
18
|
+
resolved << package if in_degree[package] == 0
|
|
19
|
+
|
|
20
|
+
# Reverse edges to avoid dfs
|
|
21
|
+
dependencies.each do |dependency|
|
|
22
|
+
unless map.has_key?(dependency)
|
|
23
|
+
raise MissingDependency, ("Package '%s' depends on " +
|
|
24
|
+
"missing package '%s'") % [package, dependency]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
graph[dependency] ||= Set.new
|
|
28
|
+
graph[dependency] << package
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
sorted = []
|
|
33
|
+
|
|
34
|
+
until resolved.empty?
|
|
35
|
+
p = resolved.first
|
|
36
|
+
resolved.delete(p)
|
|
37
|
+
|
|
38
|
+
sorted << p
|
|
39
|
+
|
|
40
|
+
graph[p].each do |v|
|
|
41
|
+
in_degree[v] -= 1
|
|
42
|
+
resolved << v if in_degree[v] == 0
|
|
43
|
+
end
|
|
44
|
+
graph[p].clear
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# each_pair gives different (correct) results in 1.8 in 1.9,
|
|
48
|
+
# stabilizing for tests
|
|
49
|
+
graph.keys.sort.each do |v|
|
|
50
|
+
e = graph[v]
|
|
51
|
+
unless e.empty?
|
|
52
|
+
raise CircularDependency, ("Cannot resolve dependencies for '%s': " +
|
|
53
|
+
"circular dependency with '%s'") % [v, e.first]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
sorted
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli
|
|
4
|
+
module DeploymentHelper
|
|
5
|
+
|
|
6
|
+
def prepare_deployment_manifest(options = {})
|
|
7
|
+
# TODO: extract to helper class
|
|
8
|
+
deployment_required
|
|
9
|
+
manifest_filename = deployment
|
|
10
|
+
|
|
11
|
+
unless File.exists?(manifest_filename)
|
|
12
|
+
err("Cannot find deployment manifest in `#{manifest_filename}'")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
manifest = load_yaml_file(manifest_filename)
|
|
16
|
+
manifest_yaml = File.read(manifest_filename)
|
|
17
|
+
|
|
18
|
+
if manifest["name"].blank?
|
|
19
|
+
err("Deployment name not found in the deployment manifest")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if manifest["target"]
|
|
23
|
+
err(manifest_target_upgrade_notice)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if options[:resolve_properties]
|
|
27
|
+
compiler = DeploymentManifestCompiler.new(manifest_yaml)
|
|
28
|
+
properties = {}
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
say("Getting deployment properties from director...")
|
|
32
|
+
properties = director.list_properties(manifest["name"])
|
|
33
|
+
rescue Bosh::Cli::DirectorError
|
|
34
|
+
say("Unable to get properties list from director, " +
|
|
35
|
+
"trying without it...")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
say("Compiling deployment manifest...")
|
|
39
|
+
compiler.properties = properties.inject({}) do |hash, property|
|
|
40
|
+
hash[property["name"]] = property["value"]
|
|
41
|
+
hash
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
manifest_yaml = compiler.result
|
|
45
|
+
manifest = YAML.load(manifest_yaml)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if manifest["name"].blank? || manifest["release"].blank? ||
|
|
49
|
+
manifest["director_uuid"].blank?
|
|
50
|
+
err("Invalid manifest `#{File.basename(deployment)}': " +
|
|
51
|
+
"name, release and director UUID are all required")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
options[:yaml] ? manifest_yaml : manifest
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Interactive walkthrough of deployment changes,
|
|
58
|
+
# expected to bail out of CLI using 'cancel_deployment'
|
|
59
|
+
# if something goes wrong, so it doesn't need to have
|
|
60
|
+
# a meaningful return value.
|
|
61
|
+
# @return Boolean Were there any changes in deployment manifest?
|
|
62
|
+
def inspect_deployment_changes(manifest, options = { })
|
|
63
|
+
show_empty_changeset = true
|
|
64
|
+
|
|
65
|
+
if options.has_key?(:show_empty_changeset)
|
|
66
|
+
show_empty_changeset = options[:show_empty_changeset]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
manifest = manifest.dup
|
|
70
|
+
current_deployment = director.get_deployment(manifest["name"])
|
|
71
|
+
|
|
72
|
+
# We cannot retrieve current manifest until there was at least one
|
|
73
|
+
# successful deployment. There used to be a warning about that
|
|
74
|
+
# but it turned out to be confusing to many users and thus has
|
|
75
|
+
# been removed.
|
|
76
|
+
return if current_deployment["manifest"].nil?
|
|
77
|
+
current_manifest = YAML.load(current_deployment["manifest"])
|
|
78
|
+
|
|
79
|
+
unless current_manifest.is_a?(Hash)
|
|
80
|
+
err("Current deployment manifest format is invalid, " +
|
|
81
|
+
"check if director works properly")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# TODO: validate new deployment manifest
|
|
85
|
+
diff = Bosh::Cli::HashChangeset.new
|
|
86
|
+
diff.add_hash(normalize_deployment_manifest(manifest), :new)
|
|
87
|
+
diff.add_hash(normalize_deployment_manifest(current_manifest), :old)
|
|
88
|
+
@_diff_key_visited = { "name" => 1, "director_uuid" => 1 }
|
|
89
|
+
|
|
90
|
+
say("Detecting changes in deployment...".green)
|
|
91
|
+
nl
|
|
92
|
+
|
|
93
|
+
if !diff.changed? && !show_empty_changeset
|
|
94
|
+
return false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
print_summary(diff, :release)
|
|
98
|
+
|
|
99
|
+
if diff[:release][:name].changed?
|
|
100
|
+
say("Release name has changed: %s -> %s".red % [
|
|
101
|
+
diff[:release][:name].old, diff[:release][:name].new])
|
|
102
|
+
unless confirmed?("This is very serious and potentially destructive " +
|
|
103
|
+
"change. ARE YOU SURE YOU WANT TO DO IT?")
|
|
104
|
+
cancel_deployment
|
|
105
|
+
end
|
|
106
|
+
elsif diff[:release][:version].changed?
|
|
107
|
+
say("Release version has changed: %s -> %s".yellow % [
|
|
108
|
+
diff[:release][:version].old, diff[:release][:version].new])
|
|
109
|
+
unless confirmed?("Are you sure you want to deploy this version?")
|
|
110
|
+
cancel_deployment
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
nl
|
|
114
|
+
|
|
115
|
+
print_summary(diff, :compilation)
|
|
116
|
+
nl
|
|
117
|
+
|
|
118
|
+
print_summary(diff, :update)
|
|
119
|
+
nl
|
|
120
|
+
|
|
121
|
+
print_summary(diff, :resource_pools)
|
|
122
|
+
|
|
123
|
+
old_stemcells = Set.new
|
|
124
|
+
new_stemcells = Set.new
|
|
125
|
+
|
|
126
|
+
diff[:resource_pools].each do |pool|
|
|
127
|
+
old_stemcells << {
|
|
128
|
+
:name => pool[:stemcell][:name].old,
|
|
129
|
+
:version => pool[:stemcell][:version].old
|
|
130
|
+
}
|
|
131
|
+
new_stemcells << {
|
|
132
|
+
:name => pool[:stemcell][:name].new,
|
|
133
|
+
:version => pool[:stemcell][:version].new
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
if old_stemcells != new_stemcells
|
|
138
|
+
unless confirmed?("Stemcell update has been detected. " +
|
|
139
|
+
"Are you sure you want to update stemcells?")
|
|
140
|
+
cancel_deployment
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
if old_stemcells.size != new_stemcells.size
|
|
145
|
+
say("Stemcell update seems to be inconsistent with current " +
|
|
146
|
+
"deployment. Please carefully review changes above.".red)
|
|
147
|
+
unless confirmed?("Are you sure this configuration is correct?")
|
|
148
|
+
cancel_deployment
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
nl
|
|
153
|
+
print_summary(diff, :networks)
|
|
154
|
+
nl
|
|
155
|
+
print_summary(diff, :jobs)
|
|
156
|
+
nl
|
|
157
|
+
print_summary(diff, :properties)
|
|
158
|
+
nl
|
|
159
|
+
|
|
160
|
+
diff.keys.each do |key|
|
|
161
|
+
unless @_diff_key_visited[key]
|
|
162
|
+
print_summary(diff, key)
|
|
163
|
+
nl
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
diff.changed?
|
|
168
|
+
rescue Bosh::Cli::DeploymentNotFound
|
|
169
|
+
say("Cannot get current deployment information from director, " +
|
|
170
|
+
"possibly a new deployment".red)
|
|
171
|
+
true
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private
|
|
175
|
+
|
|
176
|
+
def find_deployment(name)
|
|
177
|
+
if File.exists?(name)
|
|
178
|
+
File.expand_path(name)
|
|
179
|
+
else
|
|
180
|
+
File.expand_path(File.join(work_dir, "deployments", "#{name}.yml"))
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def cancel_deployment
|
|
185
|
+
quit("Deployment canceled".red)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def manifest_error(err)
|
|
189
|
+
err("Deployment manifest error: #{err}")
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def manifest_target_upgrade_notice
|
|
193
|
+
<<-EOS.gsub(/^\s*/, "").gsub(/\n$/, "")
|
|
194
|
+
Please upgrade your deployment manifest to use director UUID instead
|
|
195
|
+
of target. Just replace 'target' key with 'director_uuid' key in your
|
|
196
|
+
manifest. You can get your director UUID by targeting your director
|
|
197
|
+
with 'bosh target' and running 'bosh status' command afterwards.
|
|
198
|
+
EOS
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def print_summary(diff, key, title = nil)
|
|
202
|
+
title ||= key.to_s.gsub(/[-_]/, " ").capitalize
|
|
203
|
+
|
|
204
|
+
say(title.green)
|
|
205
|
+
summary = diff[key].summary
|
|
206
|
+
if summary.empty?
|
|
207
|
+
say("No changes")
|
|
208
|
+
else
|
|
209
|
+
say(summary.join("\n"))
|
|
210
|
+
end
|
|
211
|
+
@_diff_key_visited[key.to_s] = 1
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def normalize_deployment_manifest(manifest)
|
|
215
|
+
normalized = manifest.dup
|
|
216
|
+
|
|
217
|
+
%w(networks jobs resource_pools).each do |section|
|
|
218
|
+
unless normalized[section].kind_of?(Array)
|
|
219
|
+
manifest_error("#{section} is expected to be an array")
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
normalized[section] = normalized[section].inject({}) do |acc, e|
|
|
223
|
+
if e["name"].blank?
|
|
224
|
+
manifest_error("missing name for one of entries in '#{section}'")
|
|
225
|
+
end
|
|
226
|
+
if acc.has_key?(e["name"])
|
|
227
|
+
manifest_error("duplicate entry '#{e['name']}' in '#{section}'")
|
|
228
|
+
end
|
|
229
|
+
acc[e["name"]] = e
|
|
230
|
+
acc
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
normalized["networks"].each do |network_name, network|
|
|
235
|
+
# VIP and dynamic networks do not require subnet,
|
|
236
|
+
# but if it's there we can run some sanity checks
|
|
237
|
+
next unless network.has_key?("subnets")
|
|
238
|
+
|
|
239
|
+
unless network["subnets"].kind_of?(Array)
|
|
240
|
+
manifest_error("network subnets is expected to be an array")
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
subnets = network["subnets"].inject({}) do |acc, e|
|
|
244
|
+
if e["range"].blank?
|
|
245
|
+
manifest_error("missing range for one of subnets " +
|
|
246
|
+
"in '#{network_name}'")
|
|
247
|
+
end
|
|
248
|
+
if acc.has_key?(e["range"])
|
|
249
|
+
manifest_error("duplicate network range '#{e['range']}' " +
|
|
250
|
+
"in '#{network}'")
|
|
251
|
+
end
|
|
252
|
+
acc[e["range"]] = e
|
|
253
|
+
acc
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
normalized["networks"][network_name]["subnets"] = subnets
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
normalized
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
end
|
|
263
|
+
end
|