bosh_cli 0.19.4 → 0.19.5
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/lib/cli/commands/base.rb +30 -12
- data/lib/cli/commands/biff.rb +19 -10
- data/lib/cli/commands/cloudcheck.rb +3 -3
- data/lib/cli/commands/deployment.rb +6 -6
- data/lib/cli/commands/misc.rb +48 -13
- data/lib/cli/commands/property_management.rb +6 -2
- data/lib/cli/commands/release.rb +20 -4
- data/lib/cli/commands/stemcell.rb +8 -1
- data/lib/cli/config.rb +33 -15
- data/lib/cli/deployment_helper.rb +61 -9
- data/lib/cli/director.rb +6 -8
- data/lib/cli/errors.rb +1 -1
- data/lib/cli/package_builder.rb +37 -19
- data/lib/cli/runner.rb +18 -2
- data/lib/cli/templates/help_message.erb +3 -0
- data/lib/cli/version.rb +1 -1
- data/spec/assets/biff/ip_out_of_range.yml +63 -0
- data/spec/unit/base_command_spec.rb +32 -6
- data/spec/unit/biff_spec.rb +16 -7
- data/spec/unit/cli_commands_spec.rb +11 -11
- data/spec/unit/config_spec.rb +6 -20
- data/spec/unit/deployment_manifest_spec.rb +117 -0
- data/spec/unit/package_builder_spec.rb +22 -4
- data/spec/unit/runner_spec.rb +17 -0
- metadata +8 -4
data/lib/cli/commands/base.rb
CHANGED
@@ -3,12 +3,11 @@
|
|
3
3
|
module Bosh::Cli
|
4
4
|
module Command
|
5
5
|
class Base
|
6
|
-
BLOBS_DIR = "blobs"
|
7
|
-
BLOBS_INDEX_FILE = "blob_index.yml"
|
8
|
-
|
9
6
|
attr_reader :cache, :config, :options, :work_dir
|
10
7
|
attr_accessor :out, :usage
|
11
8
|
|
9
|
+
DEFAULT_DIRECTOR_PORT = 25555
|
10
|
+
|
12
11
|
def initialize(options = {})
|
13
12
|
@options = options.dup
|
14
13
|
@work_dir = Dir.pwd
|
@@ -16,6 +15,8 @@ module Bosh::Cli
|
|
16
15
|
@config = Config.new(config_file)
|
17
16
|
@cache = Config.cache
|
18
17
|
@exit_code = 0
|
18
|
+
@out = nil
|
19
|
+
@usage = nil
|
19
20
|
end
|
20
21
|
|
21
22
|
class << self
|
@@ -81,17 +82,31 @@ module Bosh::Cli
|
|
81
82
|
|
82
83
|
def confirmed?(question = "Are you sure?")
|
83
84
|
non_interactive? ||
|
84
|
-
|
85
|
+
ask("#{question} (type 'yes' to continue): ") == "yes"
|
85
86
|
end
|
86
87
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
# @return [String] Target director URL
|
89
|
+
def target
|
90
|
+
url = options[:target] || config.target
|
91
|
+
config.resolve_alias(:target, url) || url
|
91
92
|
end
|
92
|
-
|
93
93
|
alias_method :target_url, :target
|
94
94
|
|
95
|
+
# @return [String] Deployment manifest path
|
96
|
+
def deployment
|
97
|
+
options[:deployment] || config.deployment
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [String] Director username
|
101
|
+
def username
|
102
|
+
options[:username] || ENV["BOSH_USER"] || config.username(target)
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [String] Director password
|
106
|
+
def password
|
107
|
+
options[:password] || ENV["BOSH_PASSWORD"] || config.password(target)
|
108
|
+
end
|
109
|
+
|
95
110
|
def target_name
|
96
111
|
config.target_name || target_url
|
97
112
|
end
|
@@ -175,8 +190,8 @@ module Bosh::Cli
|
|
175
190
|
|
176
191
|
def in_release_dir?
|
177
192
|
File.directory?("packages") &&
|
178
|
-
|
179
|
-
|
193
|
+
File.directory?("jobs") &&
|
194
|
+
File.directory?("src")
|
180
195
|
end
|
181
196
|
|
182
197
|
def dirty_state?
|
@@ -186,8 +201,11 @@ module Bosh::Cli
|
|
186
201
|
end
|
187
202
|
|
188
203
|
def normalize_url(url)
|
204
|
+
had_port = url.to_s =~ /:\d+$/
|
189
205
|
url = "http://#{url}" unless url.match(/^https?/)
|
190
|
-
URI.parse(url)
|
206
|
+
uri = URI.parse(url)
|
207
|
+
uri.port = DEFAULT_DIRECTOR_PORT unless had_port
|
208
|
+
uri.to_s.strip.gsub(/\/$/, "")
|
191
209
|
end
|
192
210
|
|
193
211
|
end
|
data/lib/cli/commands/biff.rb
CHANGED
@@ -244,8 +244,17 @@ module Bosh::Cli::Command
|
|
244
244
|
def ip_range(range, netw_name)
|
245
245
|
netw_cidr = get_helper(netw_name)
|
246
246
|
first, last = get_first_last_from_range(range, netw_cidr)
|
247
|
-
|
248
|
-
|
247
|
+
raise_range_err = false
|
248
|
+
begin
|
249
|
+
unless netw_cidr[first] and netw_cidr[last]
|
250
|
+
raise_range_err = true
|
251
|
+
end
|
252
|
+
rescue NetAddr::BoundaryError => e
|
253
|
+
raise_range_err = true
|
254
|
+
end
|
255
|
+
if raise_range_err
|
256
|
+
err("IP range '#{range}' is not within the bounds of network " +
|
257
|
+
"'#{netw_name}', which only has #{netw_cidr.size} IPs.")
|
249
258
|
end
|
250
259
|
first == last ? "#{netw_cidr[first].ip}" :
|
251
260
|
"#{netw_cidr[first].ip} - #{netw_cidr[last].ip}"
|
@@ -303,7 +312,7 @@ module Bosh::Cli::Command
|
|
303
312
|
helper = {}
|
304
313
|
netw_arr = find("networks")
|
305
314
|
if netw_arr.nil?
|
306
|
-
|
315
|
+
err("Must have a network section.")
|
307
316
|
end
|
308
317
|
netw_arr.each do |netw|
|
309
318
|
subnets = netw["subnets"]
|
@@ -322,18 +331,18 @@ module Bosh::Cli::Command
|
|
322
331
|
# @param [Array] subnets The subnets in the network.
|
323
332
|
def check_valid_network_config(netw, subnets)
|
324
333
|
if subnets.nil?
|
325
|
-
|
334
|
+
err("You must have subnets in #{netw["name"]}")
|
326
335
|
end
|
327
336
|
unless subnets.length == 1
|
328
|
-
|
329
|
-
|
337
|
+
err("Biff doesn't know how to deal with anything other than one " +
|
338
|
+
"subnet in #{netw["name"]}")
|
330
339
|
end
|
331
340
|
if subnets.first["range"].nil? || subnets.first["dns"].nil?
|
332
|
-
|
341
|
+
err("Biff requires each network to have range and dns entries.")
|
333
342
|
end
|
334
343
|
if subnets.first["gateway"] && subnets.first["gateway"].match(/.*\.1$/).nil?
|
335
|
-
|
336
|
-
|
344
|
+
err("Biff only supports configurations where the gateway is the " +
|
345
|
+
"first IP (e.g. 172.31.196.1).")
|
337
346
|
end
|
338
347
|
end
|
339
348
|
|
@@ -355,7 +364,7 @@ module Bosh::Cli::Command
|
|
355
364
|
def setup(template)
|
356
365
|
@template_file = template
|
357
366
|
@deployment_file = deployment
|
358
|
-
|
367
|
+
err("Deployment not set.") if @deployment_file.nil?
|
359
368
|
@deployment_obj = load_yaml_file(@deployment_file)
|
360
369
|
@template_obj = load_template_as_yaml
|
361
370
|
@ip_helper = create_ip_helper
|
@@ -6,6 +6,8 @@ module Bosh::Cli::Command
|
|
6
6
|
|
7
7
|
def perform(*options)
|
8
8
|
auth_required
|
9
|
+
# TODO: introduce option helpers
|
10
|
+
deployment_name = options.shift unless options[0] =~ /^--/
|
9
11
|
|
10
12
|
@auto_mode = options.delete("--auto")
|
11
13
|
@report_mode = options.delete("--report")
|
@@ -24,9 +26,7 @@ module Bosh::Cli::Command
|
|
24
26
|
end
|
25
27
|
|
26
28
|
say("Performing cloud check...")
|
27
|
-
|
28
|
-
manifest = prepare_deployment_manifest
|
29
|
-
deployment_name = manifest["name"]
|
29
|
+
deployment_name ||= prepare_deployment_manifest["name"]
|
30
30
|
|
31
31
|
status, _ = director.perform_cloud_scan(deployment_name)
|
32
32
|
|
@@ -6,15 +6,15 @@ module Bosh::Cli::Command
|
|
6
6
|
|
7
7
|
def show_current
|
8
8
|
say(deployment ?
|
9
|
-
|
10
|
-
|
9
|
+
"Current deployment is `#{deployment.green}'" :
|
10
|
+
"Deployment not set".red)
|
11
11
|
end
|
12
12
|
|
13
13
|
def set_current(name)
|
14
14
|
manifest_filename = find_deployment(name)
|
15
15
|
|
16
16
|
unless File.exists?(manifest_filename)
|
17
|
-
err("Missing manifest for #{name} (tried
|
17
|
+
err("Missing manifest for #{name} (tried `#{manifest_filename}')")
|
18
18
|
end
|
19
19
|
|
20
20
|
manifest = load_yaml_file(manifest_filename)
|
@@ -62,7 +62,7 @@ module Bosh::Cli::Command
|
|
62
62
|
"changed to `#{target.red}'!")
|
63
63
|
end
|
64
64
|
|
65
|
-
say("Deployment set to
|
65
|
+
say("Deployment set to `#{manifest_filename.green}'")
|
66
66
|
config.set_deployment(manifest_filename)
|
67
67
|
config.save
|
68
68
|
end
|
@@ -71,8 +71,8 @@ module Bosh::Cli::Command
|
|
71
71
|
auth_required
|
72
72
|
recreate = options.include?("--recreate")
|
73
73
|
|
74
|
-
manifest_yaml =
|
75
|
-
|
74
|
+
manifest_yaml =
|
75
|
+
prepare_deployment_manifest(:yaml => true, :resolve_properties => true)
|
76
76
|
|
77
77
|
if interactive?
|
78
78
|
inspect_deployment_changes(YAML.load(manifest_yaml))
|
data/lib/cli/commands/misc.rb
CHANGED
@@ -96,10 +96,10 @@ module Bosh::Cli::Command
|
|
96
96
|
director = Bosh::Cli::Director.new(target, username, password)
|
97
97
|
|
98
98
|
if director.authenticated?
|
99
|
-
say("Logged in as
|
99
|
+
say("Logged in as `#{username}'")
|
100
100
|
logged_in = true
|
101
101
|
else
|
102
|
-
say("Cannot log in as
|
102
|
+
say("Cannot log in as `#{username}', please try again")
|
103
103
|
unless options[:non_interactive]
|
104
104
|
redirect(:misc, :login, username, nil)
|
105
105
|
end
|
@@ -121,7 +121,7 @@ module Bosh::Cli::Command
|
|
121
121
|
|
122
122
|
def purge_cache
|
123
123
|
if cache.cache_dir != Bosh::Cli::DEFAULT_CACHE_DIR
|
124
|
-
say("Cache directory
|
124
|
+
say("Cache directory `#{@cache.cache_dir}' differs from default, " +
|
125
125
|
"please remove manually")
|
126
126
|
else
|
127
127
|
FileUtils.rm_rf(cache.cache_dir)
|
@@ -131,14 +131,14 @@ module Bosh::Cli::Command
|
|
131
131
|
|
132
132
|
def show_target
|
133
133
|
say(target ?
|
134
|
-
"Current target is
|
135
|
-
"Target not set")
|
134
|
+
"Current target is `#{full_target_name.green}'" :
|
135
|
+
"Target not set".red)
|
136
136
|
end
|
137
137
|
|
138
138
|
def set_target(director_url, name = nil)
|
139
139
|
if name.nil?
|
140
|
-
director_url =
|
141
|
-
|
140
|
+
director_url =
|
141
|
+
config.resolve_alias(:target, director_url) || director_url
|
142
142
|
end
|
143
143
|
|
144
144
|
if director_url.blank?
|
@@ -146,8 +146,8 @@ module Bosh::Cli::Command
|
|
146
146
|
end
|
147
147
|
|
148
148
|
director_url = normalize_url(director_url)
|
149
|
-
if director_url == target
|
150
|
-
say("Target already set to
|
149
|
+
if target && director_url == normalize_url(target)
|
150
|
+
say("Target already set to `#{full_target_name.green}'")
|
151
151
|
return
|
152
152
|
end
|
153
153
|
|
@@ -159,11 +159,11 @@ module Bosh::Cli::Command
|
|
159
159
|
rescue Bosh::Cli::AuthError
|
160
160
|
status = {}
|
161
161
|
rescue Bosh::Cli::DirectorError
|
162
|
-
err("Cannot talk to director at
|
162
|
+
err("Cannot talk to director at `#{director_url}', " +
|
163
163
|
"please set correct target")
|
164
164
|
end
|
165
165
|
else
|
166
|
-
status = {
|
166
|
+
status = {"name" => "Unknown Director", "version" => "n/a"}
|
167
167
|
end
|
168
168
|
|
169
169
|
config.target = director_url
|
@@ -180,19 +180,54 @@ module Bosh::Cli::Command
|
|
180
180
|
end
|
181
181
|
|
182
182
|
config.save
|
183
|
-
say("Target set to
|
183
|
+
say("Target set to `#{full_target_name.green}'")
|
184
184
|
|
185
|
-
if interactive? &&
|
185
|
+
if interactive? && !logged_in?
|
186
186
|
redirect(:misc, :login)
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
|
+
def list_targets
|
191
|
+
# TODO: Bonus point will be checking each director status
|
192
|
+
# (maybe an --status option?)
|
193
|
+
targets = config.aliases(:target) || {}
|
194
|
+
|
195
|
+
err("No targets found") if targets.size == 0
|
196
|
+
|
197
|
+
targets_table = table do |t|
|
198
|
+
t.headings = [ "Name", "Director URL" ]
|
199
|
+
targets.each { |row| t << [row[0], row[1]] }
|
200
|
+
end
|
201
|
+
|
202
|
+
say("\n")
|
203
|
+
say(targets_table)
|
204
|
+
say("\n")
|
205
|
+
say("Targets total: %d" % targets.size)
|
206
|
+
end
|
207
|
+
|
190
208
|
def set_alias(name, value)
|
191
209
|
config.set_alias(:cli, name, value.to_s.strip)
|
192
210
|
config.save
|
193
211
|
say("Alias `#{name.green}' created for command `#{value.green}'")
|
194
212
|
end
|
195
213
|
|
214
|
+
def list_aliases
|
215
|
+
aliases = config.aliases(:cli) || {}
|
216
|
+
|
217
|
+
err("No aliases found") if aliases.size == 0
|
218
|
+
|
219
|
+
sorted = aliases.sort_by { |name, value| name }
|
220
|
+
aliases_table = table do |t|
|
221
|
+
t.headings = [ "Alias", "Command" ]
|
222
|
+
sorted.each { |row| t << [row[0], row[1]] }
|
223
|
+
end
|
224
|
+
|
225
|
+
say("\n")
|
226
|
+
say(aliases_table)
|
227
|
+
say("\n")
|
228
|
+
say("Aliases total: %d" % aliases.size)
|
229
|
+
end
|
230
|
+
|
196
231
|
private
|
197
232
|
|
198
233
|
def print_specs(entity, dir)
|
@@ -8,8 +8,12 @@ module Bosh::Cli::Command
|
|
8
8
|
prepare
|
9
9
|
show_header
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
begin
|
12
|
+
status, body = director.get_property(@deployment_name, name)
|
13
|
+
existing_property = status == 200
|
14
|
+
rescue Bosh::Cli::DirectorError
|
15
|
+
existing_property = false
|
16
|
+
end
|
13
17
|
|
14
18
|
if existing_property
|
15
19
|
say("Current `#{name.green}' value is " +
|
data/lib/cli/commands/release.rb
CHANGED
@@ -264,10 +264,20 @@ module Bosh::Cli::Command
|
|
264
264
|
end
|
265
265
|
|
266
266
|
header("Building packages")
|
267
|
-
Dir[File.join(work_dir, "packages", "*"
|
267
|
+
Dir[File.join(work_dir, "packages", "*")].each do |package_dir|
|
268
|
+
next unless File.directory?(package_dir)
|
269
|
+
package_dirname = File.basename(package_dir)
|
270
|
+
package_spec = load_yaml_file(File.join(package_dir, "spec"))
|
271
|
+
|
272
|
+
if package_spec["name"] != package_dirname
|
273
|
+
err("Found `#{package_spec["name"]}' package in " +
|
274
|
+
"`#{package_dirname}' directory, please fix it")
|
275
|
+
end
|
276
|
+
|
268
277
|
package = Bosh::Cli::PackageBuilder.new(package_spec, work_dir,
|
269
278
|
final, release.blobstore)
|
270
|
-
package.dry_run = dry_run
|
279
|
+
package.dry_run = true if dry_run
|
280
|
+
|
271
281
|
say("Building #{package.name.green}...")
|
272
282
|
package.build
|
273
283
|
packages << package
|
@@ -293,14 +303,20 @@ module Bosh::Cli::Command
|
|
293
303
|
header("Building jobs")
|
294
304
|
Dir[File.join(work_dir, "jobs", "*")].each do |job_dir|
|
295
305
|
next unless File.directory?(job_dir)
|
296
|
-
|
297
|
-
job_spec = File.join(job_dir, "spec")
|
306
|
+
job_dirname = File.basename(job_dir)
|
298
307
|
|
308
|
+
prepare_script = File.join(job_dir, "prepare")
|
299
309
|
if File.exists?(prepare_script)
|
300
310
|
say("Found prepare script in `#{File.basename(job_dir)}'")
|
301
311
|
Bosh::Cli::JobBuilder.run_prepare_script(prepare_script)
|
302
312
|
end
|
303
313
|
|
314
|
+
job_spec = load_yaml_file(File.join(job_dir, "spec"))
|
315
|
+
if job_spec["name"] != job_dirname
|
316
|
+
err("Found `#{job_spec["name"]}' job in " +
|
317
|
+
"`#{job_dirname}' directory, please fix it")
|
318
|
+
end
|
319
|
+
|
304
320
|
job = Bosh::Cli::JobBuilder.new(job_spec, work_dir, final,
|
305
321
|
release.blobstore, built_package_names)
|
306
322
|
job.dry_run = dry_run
|
@@ -137,6 +137,7 @@ module Bosh::Cli::Command
|
|
137
137
|
|
138
138
|
url = yaml[stemcell_name]["url"]
|
139
139
|
size = yaml[stemcell_name]["size"]
|
140
|
+
sha1 = yaml[stemcell_name]["sha"]
|
140
141
|
progress_bar = ProgressBar.new(stemcell_name, size)
|
141
142
|
progress_bar.file_transfer_mode
|
142
143
|
File.open("#{stemcell_name}", "w") { |file|
|
@@ -146,7 +147,13 @@ module Bosh::Cli::Command
|
|
146
147
|
end
|
147
148
|
}
|
148
149
|
progress_bar.finish
|
149
|
-
|
150
|
+
file_sha1 = Digest::SHA1.file(stemcell_name).hexdigest
|
151
|
+
if file_sha1 != sha1
|
152
|
+
err("The downloaded file sha1 '#{file_sha1}' does not match the " +
|
153
|
+
"expected sha1 '#{sha1}'.")
|
154
|
+
else
|
155
|
+
puts("Download complete.")
|
156
|
+
end
|
150
157
|
end
|
151
158
|
|
152
159
|
def delete(name, version)
|
data/lib/cli/config.rb
CHANGED
@@ -23,33 +23,47 @@ module Bosh::Cli
|
|
23
23
|
@config_file = load_yaml_file(@filename, nil)
|
24
24
|
|
25
25
|
unless @config_file.is_a?(Hash)
|
26
|
-
@config_file = {
|
26
|
+
@config_file = {} # Just ignore it if it's malformed
|
27
27
|
end
|
28
28
|
|
29
29
|
rescue SystemCallError => e
|
30
30
|
raise ConfigError, "Cannot read config file: #{e.message}"
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
# @return [Hash] Director credentials
|
34
|
+
def credentials_for(target)
|
35
|
+
if @config_file["auth"].is_a?(Hash) && @config_file["auth"][target]
|
35
36
|
@config_file["auth"][target]
|
36
37
|
else
|
37
|
-
|
38
|
+
{
|
39
|
+
"username" => nil,
|
40
|
+
"password" => nil
|
41
|
+
}
|
38
42
|
end
|
39
43
|
end
|
40
44
|
|
41
45
|
def set_credentials(target, username, password)
|
42
|
-
@config_file["auth"] ||= {
|
43
|
-
@config_file["auth"][target] = {
|
44
|
-
|
46
|
+
@config_file["auth"] ||= {}
|
47
|
+
@config_file["auth"][target] = {
|
48
|
+
"username" => username,
|
49
|
+
"password" => password
|
50
|
+
}
|
45
51
|
end
|
46
52
|
|
47
53
|
def set_alias(category, alias_name, value)
|
48
|
-
@config_file["aliases"] ||= {
|
49
|
-
@config_file["aliases"][category.to_s] ||= {
|
54
|
+
@config_file["aliases"] ||= {}
|
55
|
+
@config_file["aliases"][category.to_s] ||= {}
|
50
56
|
@config_file["aliases"][category.to_s][alias_name] = value
|
51
57
|
end
|
52
58
|
|
59
|
+
def aliases(category)
|
60
|
+
if @config_file.has_key?("aliases") && @config_file["aliases"].is_a?(Hash)
|
61
|
+
@config_file["aliases"][category.to_s]
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
53
67
|
def resolve_alias(category, alias_name)
|
54
68
|
category = category.to_s
|
55
69
|
|
@@ -64,16 +78,20 @@ module Bosh::Cli
|
|
64
78
|
end
|
65
79
|
end
|
66
80
|
|
67
|
-
|
68
|
-
|
81
|
+
# @param [String] target Target director url
|
82
|
+
# @return [String] Username associated with target
|
83
|
+
def username(target)
|
84
|
+
credentials_for(target)["username"]
|
69
85
|
end
|
70
86
|
|
71
|
-
|
72
|
-
|
87
|
+
# @param [String] target Target director url
|
88
|
+
# @return [String] Password associated with target
|
89
|
+
def password(target)
|
90
|
+
credentials_for(target)["password"]
|
73
91
|
end
|
74
92
|
|
75
93
|
# Deployment used to be a string that was only stored for your
|
76
|
-
# current target.
|
94
|
+
# current target. As soon as you switched targets, the deployment
|
77
95
|
# was erased. If the user has the old config we convert it to the
|
78
96
|
# new config.
|
79
97
|
#
|
@@ -106,7 +124,7 @@ module Bosh::Cli
|
|
106
124
|
# @param [String] deployment_file_path The string path to the
|
107
125
|
# deployment file.
|
108
126
|
def set_deployment(deployment_file_path)
|
109
|
-
raise MissingTarget, "Must have a target set
|
127
|
+
raise MissingTarget, "Must have a target set" if target.nil?
|
110
128
|
@config_file["deployment"] = {} if is_old_deployment_config?
|
111
129
|
@config_file["deployment"] ||= {}
|
112
130
|
@config_file["deployment"][target] = deployment_file_path
|