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.
@@ -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
- ask("#{question} (type 'yes' to continue): ") == "yes"
85
+ ask("#{question} (type 'yes' to continue): ") == "yes"
85
86
  end
86
87
 
87
- [:username, :password, :target, :deployment].each do |attr_name|
88
- define_method attr_name do
89
- config.send(attr_name)
90
- end
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
- File.directory?("jobs") &&
179
- File.directory?("src")
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).to_s
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
@@ -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
- unless netw_cidr[first] and netw_cidr[last]
248
- raise "Ip range '#{range}' not in #{netw_name}."
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
- raise "Must have a network section."
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
- raise "You must have subnets in #{netw["name"]}"
334
+ err("You must have subnets in #{netw["name"]}")
326
335
  end
327
336
  unless subnets.length == 1
328
- raise "Biff doesn't know how to deal with anything other than one " +
329
- "subnet in #{netw["name"]}"
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
- raise "Biff requires each network to have range and dns entries."
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
- raise "Biff only supports configurations where the gateway is the " +
336
- "first IP (e.g. 172.31.196.1)."
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
- raise "Deployment not set." if @deployment_file.nil?
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
- "Current deployment is '#{deployment.green}'" :
10
- "Deployment not set")
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 '#{manifest_filename}')")
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 '#{manifest_filename.green}'")
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 = prepare_deployment_manifest(:yaml => true,
75
- :resolve_properties => true)
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))
@@ -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 '#{username}'")
99
+ say("Logged in as `#{username}'")
100
100
  logged_in = true
101
101
  else
102
- say("Cannot log in as '#{username}', please try again")
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 '#{@cache.cache_dir}' differs from default, " +
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 '#{full_target_name.green}'" :
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 = config.resolve_alias(:target, director_url) ||
141
- director_url
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 '#{full_target_name.green}'")
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 '#{director_url}', " +
162
+ err("Cannot talk to director at `#{director_url}', " +
163
163
  "please set correct target")
164
164
  end
165
165
  else
166
- status = { "name" => "Unknown Director", "version" => "n/a" }
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 '#{full_target_name.green}'")
183
+ say("Target set to `#{full_target_name.green}'")
184
184
 
185
- if interactive? && (config.username.blank? || config.password.blank?)
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
- status, body = director.get_property(@deployment_name, name)
12
- existing_property = status == 200
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 " +
@@ -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", "*", "spec")].each do |package_spec|
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
- prepare_script = File.join(job_dir, "prepare")
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
- puts("Download complete.")
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 = { } # Just ignore it if it's malformed
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
- def auth
34
- if @config_file.has_key?("auth") && @config_file["auth"].is_a?(Hash)
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
- nil
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] = { "username" => username,
44
- "password" => password }
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
- def username
68
- auth ? auth["username"] : nil
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
- def password
72
- auth ? auth["password"] : nil
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. As soon as you switched targets, the deployment
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." if target.nil?
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