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
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli::Command
4
+ class Complete < Base
5
+
6
+ # bosh complete: Bash-compatible command line completion
7
+ usage "complete"
8
+ desc "Command completion options"
9
+ def complete(*args)
10
+ unless ENV.has_key?("COMP_LINE")
11
+ err("COMP_LINE must be set when calling bosh complete")
12
+ end
13
+ line = ENV["COMP_LINE"].gsub(/^\S*bosh\s*/, "")
14
+ say(completions(line).join("\n"))
15
+ end
16
+
17
+ private
18
+
19
+ # @param [String] line
20
+ def completions(line)
21
+ if runner.nil?
22
+ err("Command runner is not instantiated")
23
+ end
24
+
25
+ runner.find_completions(line.split(/\s+/))
26
+ end
27
+
28
+ end
29
+ end
@@ -4,17 +4,19 @@ module Bosh::Cli::Command
4
4
  class Deployment < Base
5
5
  include Bosh::Cli::DeploymentHelper
6
6
 
7
- def show_current
8
- say(deployment ?
9
- "Current deployment is `#{deployment.green}'" :
10
- "Deployment not set".red)
11
- end
7
+ # bosh deployment
8
+ usage "deployment"
9
+ desc "Get/set current deployment"
10
+ def set_current(filename = nil)
11
+ if filename.nil?
12
+ show_current
13
+ return
14
+ end
12
15
 
13
- def set_current(name)
14
- manifest_filename = find_deployment(name)
16
+ manifest_filename = find_deployment(filename)
15
17
 
16
18
  unless File.exists?(manifest_filename)
17
- err("Missing manifest for #{name} (tried `#{manifest_filename}')")
19
+ err("Missing manifest for `#{filename}'")
18
20
  end
19
21
 
20
22
  manifest = load_yaml_file(manifest_filename)
@@ -45,13 +47,14 @@ module Bosh::Cli::Command
45
47
 
46
48
  if new_target_url.blank?
47
49
  err("This manifest references director with UUID " +
48
- "#{new_director_uuid}.\n" +
49
- "You've never targeted it before.\n" +
50
- "Please find your director IP or hostname and target it first.")
50
+ "#{new_director_uuid}.\n" +
51
+ "You've never targeted it before.\n" +
52
+ "Please find your director IP or hostname and target it first.")
51
53
  end
52
54
 
53
- new_director = Bosh::Cli::Director.new(new_target_url,
54
- username, password)
55
+ new_director = Bosh::Cli::Director.new(
56
+ new_target_url, username, password)
57
+
55
58
  status = new_director.get_status
56
59
 
57
60
  config.target = new_target_url
@@ -67,21 +70,25 @@ module Bosh::Cli::Command
67
70
  config.save
68
71
  end
69
72
 
73
+ # bosh edit deployment
74
+ usage "edit deployment"
75
+ desc "Edit current deployment manifest"
70
76
  def edit
71
- unless deployment
72
- quit("Deployment not set".red)
73
- end
74
-
77
+ deployment_required
75
78
  editor = ENV['EDITOR'] || "vi"
76
79
  system("#{editor} #{deployment}")
77
80
  end
78
81
 
79
- def perform(*options)
82
+ # bosh deploy
83
+ usage "deploy"
84
+ desc "Deploy according to the currently selected deployment manifest"
85
+ option "--recreate", "recreate all VMs in deployment"
86
+ def perform
80
87
  auth_required
81
- recreate = options.include?("--recreate")
88
+ recreate = !!options[:recreate]
82
89
 
83
- manifest_yaml =
84
- prepare_deployment_manifest(:yaml => true, :resolve_properties => true)
90
+ manifest_yaml = prepare_deployment_manifest(
91
+ :yaml => true, :resolve_properties => true)
85
92
 
86
93
  if interactive?
87
94
  inspect_deployment_changes(YAML.load(manifest_yaml))
@@ -99,11 +106,16 @@ module Bosh::Cli::Command
99
106
  task_report(status, "Deployed #{desc}")
100
107
  end
101
108
 
102
- def delete(name, *options)
109
+ # bosh delete deployment
110
+ usage "delete deployment"
111
+ desc "Delete deployment"
112
+ option "--force", "ignore errors while deleting"
113
+ def delete(name)
103
114
  auth_required
104
- force = options.include?("--force")
115
+ force = !!options[:force]
105
116
 
106
- say("\nYou are going to delete deployment `#{name}'.\n\n")
117
+ say("\nYou are going to delete deployment `#{name}'.".red)
118
+ nl
107
119
  say("THIS IS A VERY DESTRUCTIVE OPERATION AND IT CANNOT BE UNDONE!\n".red)
108
120
 
109
121
  unless confirmed?
@@ -116,12 +128,71 @@ module Bosh::Cli::Command
116
128
  task_report(status, "Deleted deployment `#{name}'")
117
129
  end
118
130
 
131
+ # bosh validate jobs
132
+ usage "validate jobs"
133
+ desc "Validates all jobs in the current release using current " +
134
+ "deployment manifest as the source of properties"
135
+ def validate_jobs
136
+ check_if_release_dir
137
+ manifest = prepare_deployment_manifest(:resolve_properties => true)
138
+
139
+ nl
140
+ say("Analyzing release directory...".yellow)
141
+
142
+ say(" - discovering packages")
143
+ packages = Bosh::Cli::PackageBuilder.discover(
144
+ work_dir,
145
+ :dry_run => true,
146
+ :final => false
147
+ )
148
+
149
+ say(" - discovering jobs")
150
+ jobs = Bosh::Cli::JobBuilder.discover(
151
+ work_dir,
152
+ :dry_run => true,
153
+ :final => false,
154
+ :package_names => packages.map {|package| package.name}
155
+ )
156
+
157
+ say(" - validating properties")
158
+ validator = Bosh::Cli::JobPropertyValidator.new(jobs, manifest)
159
+ validator.validate
160
+
161
+ unless validator.jobs_without_properties.empty?
162
+ nl
163
+ say("Legacy jobs (no properties defined): ".yellow)
164
+ validator.jobs_without_properties.sort { |a, b|
165
+ a.name <=> b.name
166
+ }.each do |job|
167
+ say(" - #{job.name}")
168
+ end
169
+ end
170
+
171
+ if validator.template_errors.empty?
172
+ nl
173
+ say("No template errors found".green)
174
+ else
175
+ nl
176
+ say("Template errors: ".yellow)
177
+ validator.template_errors.each do |error|
178
+ nl
179
+ path = Pathname.new(error.template_path)
180
+ rel_path = path.relative_path_from(Pathname.new(release.dir))
181
+
182
+ say(" - #{rel_path}:")
183
+ say(" line #{error.line}:".yellow + " #{error.exception.to_s}")
184
+ end
185
+ end
186
+ end
187
+
188
+ # bosh deployments
189
+ usage "deployments"
190
+ desc "Show the list of available deployments"
119
191
  def list
120
192
  auth_required
121
-
122
193
  deployments = director.list_deployments
123
194
 
124
- err("No deployments") if deployments.size == 0
195
+ err("No deployments") if deployments.empty?
125
196
 
126
197
  deployments_table = table do |t|
127
198
  t.headings = %w(Name)
@@ -130,10 +201,48 @@ module Bosh::Cli::Command
130
201
  end
131
202
  end
132
203
 
133
- say("\n")
204
+ nl
134
205
  say(deployments_table)
135
- say("\n")
206
+ nl
136
207
  say("Deployments total: %d" % deployments.size)
137
208
  end
209
+
210
+ # bosh download manifest
211
+ usage "download manifest"
212
+ desc "Download deployment manifest locally"
213
+ def download_manifest(deployment_name, save_as = nil)
214
+ auth_required
215
+
216
+ if save_as && File.exists?(save_as) &&
217
+ !confirmed?("Overwrite `#{save_as}'?")
218
+ err("Please choose another file to save the manifest to")
219
+ end
220
+
221
+ deployment = director.get_deployment(deployment_name)
222
+
223
+ if save_as
224
+ File.open(save_as, "w") do |f|
225
+ f.write(deployment["manifest"])
226
+ end
227
+ say("Deployment manifest saved to `#{save_as}'".green)
228
+ else
229
+ say(deployment["manifest"])
230
+ end
231
+ end
232
+
233
+ private
234
+
235
+ def show_current
236
+ if deployment
237
+ if interactive?
238
+ say("Current deployment is `#{deployment.green}'")
239
+ else
240
+ say(deployment)
241
+ end
242
+ else
243
+ err("Deployment not set")
244
+ end
245
+ end
246
+
138
247
  end
139
248
  end
@@ -0,0 +1,96 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli::Command
4
+ class Help < Base
5
+
6
+ # bosh help: shows either a high level help message or drills down to a
7
+ # specific area (release, deployment etc)
8
+ usage "help"
9
+ desc "Show help message"
10
+ option "--all", "Show help for all BOSH commands"
11
+ # @param [Array] keywords What specific kind of help is requested
12
+ def help(*keywords)
13
+ if runner.nil?
14
+ err("Cannot show help message, command runner is not instantiated")
15
+ end
16
+
17
+ keywords = "all" if keywords.empty? && options[:all]
18
+
19
+ if keywords.empty?
20
+ generic_help
21
+ else
22
+ keyword_help(keywords)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def generic_help
29
+ message = <<-HELP.gsub(/^\s*\|/, "")
30
+ |BOSH CLI helps you manage your BOSH deployments and releases.
31
+ |
32
+ |#{runner.usage}
33
+ |
34
+ |The most commonly used BOSH commands are:
35
+ | target Point CLI to BOSH Director
36
+ | deployment Set deployment
37
+ | status Current status
38
+ | create release Create new release
39
+ | upload release Upload release
40
+ | upload stemcell Upload stemcell image
41
+ | deploy Perform deployment
42
+ | task <task_id> Track task / show task log
43
+ | tasks List running tasks
44
+ | tasks recent List recent tasks
45
+ | cloudcheck Find and resolve deployment problems
46
+ | deployments List deployments
47
+ | releases List releases
48
+ | start,restart,recreate,stop Job management
49
+ | add blob Add large binary file to release
50
+ |
51
+ |You can run 'bosh help <keywords...>' to see different commands,
52
+ |i.e. 'bosh help release', 'bosh help cloudcheck'.
53
+ |Or run 'bosh help --all' to see all available BOSH commands
54
+ HELP
55
+
56
+ say message
57
+ end
58
+
59
+ # @param [Array] keywords
60
+ def keyword_help(keywords)
61
+ matches = Bosh::Cli::Config.commands.values
62
+
63
+ if keywords == "all"
64
+ good_matches = matches.sort { |a, b| a.usage <=> b.usage }
65
+ else
66
+ good_matches = []
67
+ matches.each do |command|
68
+ common_keywords = command.keywords & keywords
69
+ if common_keywords.size > 0
70
+ good_matches << command
71
+ end
72
+
73
+ good_matches.sort! do |a, b|
74
+ cmp = (b.keywords & keywords).size <=> (a.keywords & keywords).size
75
+ cmp = (a.usage <=> b.usage) if cmp == 0
76
+ cmp
77
+ end
78
+ end
79
+ end
80
+
81
+ help_column_width = terminal_width - 5
82
+ help_indent = 4
83
+
84
+ good_matches.each_with_index do |command, i|
85
+ nl if i > 0
86
+ margin = command.usage.size + 1
87
+ say("#{command.usage_with_params.columnize(70, margin).green}")
88
+ say(command.desc.columnize(help_column_width).indent(help_indent))
89
+ if command.has_options?
90
+ say(command.options_summary.indent(help_indent))
91
+ end
92
+ end
93
+ end
94
+
95
+ end
96
+ end
@@ -3,11 +3,14 @@
3
3
  module Bosh::Cli::Command
4
4
  class Job < Base
5
5
 
6
+ # bosh generate job
7
+ usage "generate job"
8
+ desc "Generate job template"
6
9
  def generate(name)
7
10
  check_if_release_dir
8
11
 
9
12
  unless name.bosh_valid_id?
10
- err("`#{name}' is not a vaild BOSH id")
13
+ err("`#{name}' is not a valid BOSH id")
11
14
  end
12
15
 
13
16
  job_dir = File.join("jobs", name)
@@ -4,23 +4,43 @@ module Bosh::Cli::Command
4
4
  class JobManagement < Base
5
5
  include Bosh::Cli::DeploymentHelper
6
6
 
7
- def start_job(*args)
8
- change_job_state(:start, *args)
7
+ FORCE = "Proceed even when there are other manifest changes"
8
+
9
+ # bosh start
10
+ usage "start"
11
+ desc "Start job/instance"
12
+ option "--force", FORCE
13
+ def start_job(job, index = nil)
14
+ change_job_state(:start, job, index)
9
15
  end
10
16
 
11
- def stop_job(*args)
12
- change_job_state(:stop, *args)
17
+ # bosh stop
18
+ usage "stop"
19
+ desc "Stop job/instance"
20
+ option "--soft", "Stop process only"
21
+ option "--hard", "Power off VM"
22
+ option "--force", FORCE
23
+ def stop_job(job, index = nil)
24
+ change_job_state(:stop, job, index)
13
25
  end
14
26
 
15
- def restart_job(*args)
16
- change_job_state(:restart, *args)
27
+ # bosh restart
28
+ usage "restart"
29
+ desc "Restart job/instance (soft stop + start)"
30
+ option "--force", FORCE
31
+ def restart_job(job, index = nil)
32
+ change_job_state(:restart, job, index)
17
33
  end
18
34
 
19
- def recreate_job(*args)
20
- change_job_state(:recreate, *args)
35
+ # bosh recreate
36
+ usage "recreate"
37
+ desc "Recreate job/instance (hard stop + start)"
38
+ option "--force", FORCE
39
+ def recreate_job(job, index = nil)
40
+ change_job_state(:recreate, job, index)
21
41
  end
22
42
 
23
- def change_job_state(operation, *args)
43
+ def change_job_state(operation, job, index)
24
44
  auth_required
25
45
  manifest_yaml = prepare_deployment_manifest(:yaml => true)
26
46
  manifest = YAML.load(manifest_yaml)
@@ -30,10 +50,9 @@ module Bosh::Cli::Command
30
50
  "`start', `stop', `restart', `recreate'")
31
51
  end
32
52
 
33
- args = args.dup
34
- hard = args.delete("--hard")
35
- soft = args.delete("--soft")
36
- force = args.delete("--force")
53
+ hard = options[:hard]
54
+ soft = options[:soft]
55
+ force = options[:force]
37
56
 
38
57
  if hard && soft
39
58
  err("Cannot handle both --hard and --soft options, please choose one")
@@ -43,9 +62,7 @@ module Bosh::Cli::Command
43
62
  err("--hard and --soft options only make sense for `stop' operation")
44
63
  end
45
64
 
46
- job = args.shift
47
- index = args.shift
48
- job_desc = index ? "#{job}(#{index})" : "#{job}"
65
+ job_desc = index ? "#{job}/#{index}" : "#{job}"
49
66
 
50
67
  op_desc = nil
51
68
  new_state = nil
@@ -83,8 +100,6 @@ module Bosh::Cli::Command
83
100
  say("You are about to #{op_desc.green}")
84
101
 
85
102
  if interactive?
86
- # TODO: refactor inspect_deployment_changes
87
- # to decouple changeset structure and rendering
88
103
  other_changes_present = inspect_deployment_changes(
89
104
  manifest, :show_empty_changeset => false)
90
105
 
@@ -96,14 +111,12 @@ module Bosh::Cli::Command
96
111
  cancel_deployment
97
112
  end
98
113
  end
99
- nl
100
114
 
115
+ nl
101
116
  say("Performing `#{op_desc}'...")
102
117
 
103
- status, _ =
104
- director.change_job_state(manifest["name"],
105
- manifest_yaml,
106
- job, index, new_state)
118
+ status, _ = director.change_job_state(
119
+ manifest["name"], manifest_yaml, job, index, new_state)
107
120
 
108
121
  task_report(status, completion_desc)
109
122
  end