bosh_cli 0.19.6 → 1.0.rc1

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.
Files changed (64) hide show
  1. data/bin/bosh +3 -0
  2. data/lib/cli.rb +15 -5
  3. data/lib/cli/{commands/base.rb → base_command.rb} +38 -44
  4. data/lib/cli/command_discovery.rb +40 -0
  5. data/lib/cli/command_handler.rb +135 -0
  6. data/lib/cli/commands/biff.rb +16 -12
  7. data/lib/cli/commands/blob_management.rb +10 -3
  8. data/lib/cli/commands/cloudcheck.rb +13 -11
  9. data/lib/cli/commands/complete.rb +29 -0
  10. data/lib/cli/commands/deployment.rb +137 -28
  11. data/lib/cli/commands/help.rb +96 -0
  12. data/lib/cli/commands/job.rb +4 -1
  13. data/lib/cli/commands/job_management.rb +36 -23
  14. data/lib/cli/commands/job_rename.rb +11 -12
  15. data/lib/cli/commands/log_management.rb +28 -32
  16. data/lib/cli/commands/maintenance.rb +6 -1
  17. data/lib/cli/commands/misc.rb +129 -87
  18. data/lib/cli/commands/package.rb +6 -65
  19. data/lib/cli/commands/property_management.rb +20 -8
  20. data/lib/cli/commands/release.rb +211 -206
  21. data/lib/cli/commands/ssh.rb +178 -188
  22. data/lib/cli/commands/stemcell.rb +114 -51
  23. data/lib/cli/commands/task.rb +74 -56
  24. data/lib/cli/commands/user.rb +6 -3
  25. data/lib/cli/commands/vms.rb +17 -15
  26. data/lib/cli/config.rb +27 -1
  27. data/lib/cli/core_ext.rb +27 -1
  28. data/lib/cli/deployment_helper.rb +47 -0
  29. data/lib/cli/director.rb +18 -9
  30. data/lib/cli/errors.rb +6 -0
  31. data/lib/cli/job_builder.rb +75 -23
  32. data/lib/cli/job_property_collection.rb +87 -0
  33. data/lib/cli/job_property_validator.rb +130 -0
  34. data/lib/cli/package_builder.rb +32 -5
  35. data/lib/cli/release.rb +2 -0
  36. data/lib/cli/release_builder.rb +9 -13
  37. data/lib/cli/release_compiler.rb +5 -34
  38. data/lib/cli/release_tarball.rb +4 -19
  39. data/lib/cli/runner.rb +118 -694
  40. data/lib/cli/version.rb +1 -1
  41. data/spec/assets/config/swift-hp/config/final.yml +6 -0
  42. data/spec/assets/config/swift-hp/config/private.yml +7 -0
  43. data/spec/assets/config/swift-rackspace/config/final.yml +6 -0
  44. data/spec/assets/config/swift-rackspace/config/private.yml +6 -0
  45. data/spec/spec_helper.rb +0 -5
  46. data/spec/unit/base_command_spec.rb +32 -37
  47. data/spec/unit/biff_spec.rb +11 -10
  48. data/spec/unit/cli_commands_spec.rb +96 -88
  49. data/spec/unit/core_ext_spec.rb +1 -1
  50. data/spec/unit/deployment_manifest_spec.rb +36 -0
  51. data/spec/unit/director_spec.rb +17 -3
  52. data/spec/unit/job_builder_spec.rb +2 -2
  53. data/spec/unit/job_property_collection_spec.rb +111 -0
  54. data/spec/unit/job_property_validator_spec.rb +7 -0
  55. data/spec/unit/job_rename_spec.rb +7 -6
  56. data/spec/unit/package_builder_spec.rb +2 -2
  57. data/spec/unit/release_builder_spec.rb +33 -0
  58. data/spec/unit/release_spec.rb +54 -0
  59. data/spec/unit/release_tarball_spec.rb +2 -7
  60. data/spec/unit/runner_spec.rb +1 -151
  61. data/spec/unit/ssh_spec.rb +15 -9
  62. metadata +41 -12
  63. data/lib/cli/command_definition.rb +0 -52
  64. data/lib/cli/templates/help_message.erb +0 -80
@@ -4,34 +4,34 @@ module Bosh::Cli::Command
4
4
  class JobRename < Base
5
5
  include Bosh::Cli::DeploymentHelper
6
6
 
7
- def rename(*args)
7
+ # bosh rename
8
+ usage "rename job"
9
+ desc "Renames a job. NOTE, your deployment manifest must also be " +
10
+ "updated to reflect the new job name."
11
+ option "--force", "Ignore errors"
12
+ def rename(old_name, new_name)
8
13
  auth_required
9
14
  manifest_yaml = prepare_deployment_manifest(:yaml => true)
10
15
  manifest = YAML.load(manifest_yaml)
11
16
 
12
- args = args.dup
13
- force = args.delete("--force")
14
- old_name = args.shift
15
- new_name = args.shift
16
-
17
- say("You are about to rename #{old_name.green} to #{new_name.green}")
17
+ force = options[:force]
18
+ say("You are about to rename `#{old_name.green}' to `#{new_name.green}'")
18
19
 
19
20
  unless confirmed?
20
21
  nl
21
22
  say("Job rename canceled".green)
22
- return
23
+ exit(0)
23
24
  end
24
25
 
25
26
  sanity_check_job_rename(manifest_yaml, old_name, new_name)
26
27
 
27
- status, _ = director.rename_job(manifest["name"], manifest_yaml,
28
- old_name, new_name, force)
28
+ status, _ = director.rename_job(
29
+ manifest["name"], manifest_yaml, old_name, new_name, force)
29
30
 
30
31
  task_report(status, "Rename successful")
31
32
  end
32
33
 
33
34
  def sanity_check_job_rename(manifest_yaml, old_name, new_name)
34
-
35
35
  # Makes sure the new deployment manifest contains the renamed job
36
36
  manifest = YAML.load(manifest_yaml)
37
37
  new_jobs = manifest["jobs"].map { |job| job["name"] }
@@ -87,7 +87,6 @@ module Bosh::Cli::Command
87
87
  err("Manifest does not rename old job `#{old_name}'")
88
88
  end
89
89
 
90
-
91
90
  # Final sanity check, make sure that no
92
91
  # other properties or anything other than the names
93
92
  # have changed. So update current manifest with new name
@@ -4,55 +4,51 @@ module Bosh::Cli::Command
4
4
  class LogManagement < Base
5
5
  include Bosh::Cli::DeploymentHelper
6
6
 
7
- def fetch_logs(*args)
7
+ # bosh logs
8
+ usage "logs"
9
+ desc "Fetch job or agent logs from a BOSH-managed VM"
10
+ option "--agent", "fetch agent logs"
11
+ option "--job", "fetch job logs"
12
+ option "--only filter1,filter2,...", Array,
13
+ "only fetch logs that satisfy",
14
+ "given filters (defined in job spec)"
15
+ option "--all", "fetch all files in the job or agent log directory"
16
+ def fetch_logs(job, index)
8
17
  auth_required
9
18
  target_required
10
19
 
11
- job = args.shift
12
- index = args.shift
13
- filters = nil
14
- log_type = nil
15
-
16
- for_job = args.delete("--job")
17
- for_agent = args.delete("--agent")
20
+ if index !~ /^\d+$/
21
+ err("Job index is expected to be a positive integer")
22
+ end
18
23
 
19
- if for_job && for_agent
20
- err("Please specify which logs you want, job or agent")
21
- elsif for_agent
24
+ if options[:agent]
25
+ if options[:job]
26
+ err("You can't use --job and --agent together")
27
+ end
22
28
  log_type = "agent"
23
- else # default log type is 'job'
29
+ else
24
30
  log_type = "job"
25
31
  end
26
32
 
27
- if args.include?("--only")
28
- pos = args.index("--only")
29
- filters = args[pos+1]
30
- if filters.nil?
31
- err("Please provide a list of filters separated by comma")
33
+ if options[:only]
34
+ if options[:all]
35
+ err("You can't use --only and --all together")
32
36
  end
33
- args.delete("--only")
34
- args.delete(filters)
35
- elsif args.include?("--all")
36
- args.delete("--all")
37
+ filters = options[:only].join(",")
38
+ elsif options[:all]
37
39
  filters = "all"
40
+ else
41
+ filters = nil
38
42
  end
39
43
 
40
- if for_agent && !filters.nil? && filters != "all"
44
+ if options[:agent] && filters && filters != "all"
41
45
  err("Custom filtering is not supported for agent logs")
42
46
  end
43
47
 
44
- if index !~ /^\d+$/
45
- err("Job index is expected to be a positive integer")
46
- end
47
-
48
- if args.size > 0
49
- err("Unknown arguments: #{args.join(", ")}")
50
- end
51
-
52
48
  manifest = prepare_deployment_manifest
53
49
 
54
- resource_id = director.fetch_logs(manifest["name"], job, index,
55
- log_type, filters)
50
+ resource_id = director.fetch_logs(
51
+ manifest["name"], job, index, log_type, filters)
56
52
 
57
53
  if resource_id.nil?
58
54
  err("Error retrieving logs")
@@ -7,6 +7,9 @@ module Bosh::Cli::Command
7
7
  RELEASES_TO_KEEP = 2
8
8
  STEMCELLS_TO_KEEP = 2
9
9
 
10
+ # bosh cleanup
11
+ usage "cleanup"
12
+ desc "Cleanup releases and stemcells"
10
13
  def cleanup
11
14
  target_required
12
15
  auth_required
@@ -28,7 +31,9 @@ module Bosh::Cli::Command
28
31
  Releases and stemcells that are in use will not be affected.
29
32
  EOS
30
33
 
31
- say("\n#{desc}\n")
34
+ nl
35
+ say(desc)
36
+ nl
32
37
 
33
38
  err("Cleanup canceled") unless confirmed?
34
39
 
@@ -4,12 +4,18 @@ module Bosh::Cli::Command
4
4
  class Misc < Base
5
5
  DEFAULT_STATUS_TIMEOUT = 3 # seconds
6
6
 
7
+ # bosh version
8
+ usage "version"
9
+ desc "Show version"
7
10
  def version
8
11
  say("BOSH %s" % [Bosh::Cli::VERSION])
9
12
  end
10
13
 
14
+ # bosh status
15
+ usage "status"
16
+ desc "Show current status (current target, user, deployment info etc)"
11
17
  def status
12
- if config.target && options[:director_checks]
18
+ if config.target
13
19
  say("Updating director data...", " ")
14
20
 
15
21
  begin
@@ -26,58 +32,59 @@ module Bosh::Cli::Command
26
32
  rescue TimeoutError
27
33
  say("timed out".red)
28
34
  rescue => e
29
- say("error".red)
35
+ say("error: #{e.message}")
30
36
  end
31
37
  nl
32
38
  end
33
39
 
34
- target_name = full_target_name ? full_target_name.green : "not set".red
35
- target_uuid = config.target_uuid ? config.target_uuid.green : "n/a".red
36
- user = logged_in? ? username.green : "not set".red
37
- deployment = config.deployment ? config.deployment.green : "not set".red
40
+ say("Director".green)
41
+ if target_url.nil?
42
+ say(" not set".yellow)
43
+ else
44
+ print_value("Name", config.target_name)
45
+ print_value("URL", target_url)
46
+ print_value("Version", config.target_version)
47
+ print_value("User", username, "not logged in")
48
+ print_value("UUID", config.target_uuid)
49
+ end
50
+
51
+ nl
52
+ say("Deployment".green)
38
53
 
39
- say("Target".ljust(15) + target_name)
40
- say("UUID".ljust(15) + target_uuid)
41
- say("User".ljust(15) + user)
42
- say("Deployment".ljust(15) + deployment)
54
+ if deployment
55
+ print_value("Manifest", deployment)
56
+ else
57
+ say(" not set".yellow)
58
+ end
43
59
 
44
60
  if in_release_dir?
45
- header("You are in release directory")
61
+ nl
62
+ say("Release".green)
46
63
 
47
- dev_name = release.dev_name
48
64
  dev_version = Bosh::Cli::VersionsIndex.new(
49
- File.join(work_dir, "dev_releases")).latest_version
65
+ File.join(work_dir, "dev_releases")).latest_version
50
66
 
51
- final_name = release.final_name
52
67
  final_version = Bosh::Cli::VersionsIndex.new(
53
- File.join(work_dir, "releases")).latest_version
54
-
55
- say("Dev name: %s" % [dev_name ? dev_name.green : "not set".red])
56
- say("Dev version: %s" % [dev_version ?
57
- dev_version.to_s.green :
58
- "no versions yet".red])
59
- say("\n")
60
- say("Final name: %s" % [final_name ?
61
- final_name.green :
62
- "not set".red])
63
- say("Final version: %s" % [final_version ?
64
- final_version.to_s.green :
65
- "no versions yet".red])
66
-
67
- say("\n")
68
- say("Packages")
69
- print_specs("package", "packages")
70
-
71
- say("\n")
72
- say("Jobs")
73
- print_specs("job", "jobs")
68
+ File.join(work_dir, "releases")).latest_version
69
+
70
+ dev = release.dev_name
71
+ dev += "/#{dev_version}" if dev && dev_version
72
+
73
+ final = release.final_name
74
+ final += "/#{final_version}" if final && final_version
75
+
76
+ print_value("dev", dev)
77
+ print_value("final", final)
74
78
  end
75
79
  end
76
80
 
81
+ # bosh login
82
+ usage "login"
83
+ desc "Log in to currently targeted director"
77
84
  def login(username = nil, password = nil)
78
85
  target_required
79
86
 
80
- unless options[:non_interactive]
87
+ if interactive?
81
88
  username = ask("Your username: ").to_s if username.blank?
82
89
 
83
90
  password_retries = 0
@@ -92,50 +99,57 @@ module Bosh::Cli::Command
92
99
  end
93
100
  logged_in = false
94
101
 
95
- if options[:director_checks]
96
- director = Bosh::Cli::Director.new(target, username, password)
102
+ director.user = username
103
+ director.password = password
97
104
 
98
- if director.authenticated?
99
- say("Logged in as `#{username}'")
100
- logged_in = true
101
- else
102
- say("Cannot log in as `#{username}', please try again")
103
- unless options[:non_interactive]
104
- redirect(:misc, :login, username, nil)
105
- end
106
- end
105
+ if director.authenticated?
106
+ say("Logged in as `#{username}'".green)
107
+ logged_in = true
108
+ elsif non_interactive?
109
+ err("Cannot log in as `#{username}'".red)
110
+ else
111
+ say("Cannot log in as `#{username}', please try again".red)
112
+ login(username)
107
113
  end
108
114
 
109
- if logged_in || !options[:director_checks]
115
+ if logged_in
110
116
  config.set_credentials(target, username, password)
111
117
  config.save
112
118
  end
113
119
  end
114
120
 
121
+ # bosh logout
122
+ usage "logout"
123
+ desc "Forget saved credentials for targeted director"
115
124
  def logout
116
125
  target_required
117
126
  config.set_credentials(target, nil, nil)
118
127
  config.save
119
- say("You are no longer logged in to '#{target}'")
128
+ say("You are no longer logged in to `#{target}'".yellow)
120
129
  end
121
130
 
131
+ # bosh purge
132
+ usage "purge"
133
+ desc "Purge local manifest cache"
122
134
  def purge_cache
123
135
  if cache.cache_dir != Bosh::Cli::DEFAULT_CACHE_DIR
124
- say("Cache directory `#{@cache.cache_dir}' differs from default, " +
125
- "please remove manually")
136
+ err("Cache directory overriden, please remove manually")
126
137
  else
127
138
  FileUtils.rm_rf(cache.cache_dir)
128
- say("Purged cache")
139
+ say("Purged cache".green)
129
140
  end
130
141
  end
131
142
 
132
- def show_target
133
- say(target ?
134
- "Current target is `#{full_target_name.green}'" :
135
- "Target not set".red)
136
- end
143
+ # bosh target
144
+ usage "target"
145
+ desc "Choose director to talk to (optionally creating an alias). " +
146
+ "If no arguments given, show currently targeted director"
147
+ def set_target(director_url = nil, name = nil)
148
+ if director_url.nil?
149
+ show_target
150
+ return
151
+ end
137
152
 
138
- def set_target(director_url, name = nil)
139
153
  if name.nil?
140
154
  director_url =
141
155
  config.resolve_alias(:target, director_url) || director_url
@@ -147,23 +161,19 @@ module Bosh::Cli::Command
147
161
 
148
162
  director_url = normalize_url(director_url)
149
163
  if target && director_url == normalize_url(target)
150
- say("Target already set to `#{full_target_name.green}'")
164
+ say("Target already set to `#{target_name.green}'")
151
165
  return
152
166
  end
153
167
 
154
168
  director = Bosh::Cli::Director.new(director_url)
155
169
 
156
- if options[:director_checks]
157
- begin
158
- status = director.get_status
159
- rescue Bosh::Cli::AuthError
160
- status = {}
161
- rescue Bosh::Cli::DirectorError
162
- err("Cannot talk to director at `#{director_url}', " +
163
- "please set correct target")
164
- end
165
- else
166
- status = {"name" => "Unknown Director", "version" => "n/a"}
170
+ begin
171
+ status = director.get_status
172
+ rescue Bosh::Cli::AuthError
173
+ status = {}
174
+ rescue Bosh::Cli::DirectorError
175
+ err("Cannot talk to director at `#{director_url}', " +
176
+ "please set correct target")
167
177
  end
168
178
 
169
179
  config.target = director_url
@@ -180,56 +190,88 @@ module Bosh::Cli::Command
180
190
  end
181
191
 
182
192
  config.save
183
- say("Target set to `#{full_target_name.green}'")
193
+ say("Target set to `#{target_name.green}'")
184
194
 
185
195
  if interactive? && !logged_in?
186
- redirect(:misc, :login)
196
+ redirect("login")
187
197
  end
188
198
  end
189
199
 
200
+ # bosh targets
201
+ usage "targets"
202
+ desc "Show the list of available targets"
190
203
  def list_targets
191
- # TODO: Bonus point will be checking each director status
192
- # (maybe an --status option?)
193
204
  targets = config.aliases(:target) || {}
194
205
 
195
- err("No targets found") if targets.size == 0
206
+ err("No targets found") if targets.empty?
196
207
 
197
208
  targets_table = table do |t|
198
209
  t.headings = [ "Name", "Director URL" ]
199
210
  targets.each { |row| t << [row[0], row[1]] }
200
211
  end
201
212
 
202
- say("\n")
213
+ nl
203
214
  say(targets_table)
204
- say("\n")
215
+ nl
205
216
  say("Targets total: %d" % targets.size)
206
217
  end
207
218
 
208
- def set_alias(name, value)
209
- config.set_alias(:cli, name, value.to_s.strip)
219
+ # bosh alias
220
+ usage "alias"
221
+ desc "Create an alias <name> for command <command>"
222
+ def set_alias(name, command)
223
+ config.set_alias(:cli, name, command.to_s.strip)
210
224
  config.save
211
- say("Alias `#{name.green}' created for command `#{value.green}'")
225
+ say("Alias `#{name.green}' created for command `#{command.green}'")
212
226
  end
213
227
 
228
+ # bosh aliases
229
+ usage "aliases"
230
+ desc "Show the list of available command aliases"
214
231
  def list_aliases
215
232
  aliases = config.aliases(:cli) || {}
233
+ err("No aliases found") if aliases.empty?
216
234
 
217
- err("No aliases found") if aliases.size == 0
218
-
219
- sorted = aliases.sort_by { |name, value| name }
235
+ sorted = aliases.sort_by { |name, _| name }
220
236
  aliases_table = table do |t|
221
- t.headings = [ "Alias", "Command" ]
237
+ t.headings = %w(Alias Command)
222
238
  sorted.each { |row| t << [row[0], row[1]] }
223
239
  end
224
240
 
225
- say("\n")
241
+ nl
226
242
  say(aliases_table)
227
- say("\n")
243
+ nl
228
244
  say("Aliases total: %d" % aliases.size)
229
245
  end
230
246
 
231
247
  private
232
248
 
249
+ def print_value(label, value, if_none = nil)
250
+ if value
251
+ message = label.ljust(10) + value.yellow
252
+ else
253
+ message = label.ljust(10) + (if_none || "n/a").yellow
254
+ end
255
+ say(message.indent(2))
256
+ end
257
+
258
+ def show_target
259
+ if config.target
260
+ if interactive?
261
+ if config.target_name
262
+ name = "#{config.target} (#{config.target_name})"
263
+ else
264
+ name = config.target
265
+ end
266
+ say("Current target is #{name.green}")
267
+ else
268
+ say(config.target)
269
+ end
270
+ else
271
+ err("Target not set")
272
+ end
273
+ end
274
+
233
275
  def print_specs(entity, dir)
234
276
  specs = Dir[File.join(work_dir, dir, "*", "spec")]
235
277
 
@@ -237,7 +279,7 @@ module Bosh::Cli::Command
237
279
  say("No #{entity} specs found")
238
280
  end
239
281
 
240
- t = table ["Name", "Dev", "Final"]
282
+ t = table %w(Name Dev Final)
241
283
 
242
284
  specs.each do |spec_file|
243
285
  if spec_file.is_a?(String) && File.file?(spec_file)