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
@@ -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