bosh_cli 0.19.6 → 1.0.rc1

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