bosh_cli 0.19.4 → 0.19.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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