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.
- data/bin/bosh +3 -0
- data/lib/cli.rb +15 -5
- data/lib/cli/{commands/base.rb → base_command.rb} +38 -44
- data/lib/cli/command_discovery.rb +40 -0
- data/lib/cli/command_handler.rb +135 -0
- data/lib/cli/commands/biff.rb +16 -12
- data/lib/cli/commands/blob_management.rb +10 -3
- data/lib/cli/commands/cloudcheck.rb +13 -11
- data/lib/cli/commands/complete.rb +29 -0
- data/lib/cli/commands/deployment.rb +137 -28
- data/lib/cli/commands/help.rb +96 -0
- data/lib/cli/commands/job.rb +4 -1
- data/lib/cli/commands/job_management.rb +36 -23
- data/lib/cli/commands/job_rename.rb +11 -12
- data/lib/cli/commands/log_management.rb +28 -32
- data/lib/cli/commands/maintenance.rb +6 -1
- data/lib/cli/commands/misc.rb +129 -87
- data/lib/cli/commands/package.rb +6 -65
- data/lib/cli/commands/property_management.rb +20 -8
- data/lib/cli/commands/release.rb +211 -206
- data/lib/cli/commands/ssh.rb +178 -188
- data/lib/cli/commands/stemcell.rb +114 -51
- data/lib/cli/commands/task.rb +74 -56
- data/lib/cli/commands/user.rb +6 -3
- data/lib/cli/commands/vms.rb +17 -15
- data/lib/cli/config.rb +27 -1
- data/lib/cli/core_ext.rb +27 -1
- data/lib/cli/deployment_helper.rb +47 -0
- data/lib/cli/director.rb +18 -9
- data/lib/cli/errors.rb +6 -0
- data/lib/cli/job_builder.rb +75 -23
- data/lib/cli/job_property_collection.rb +87 -0
- data/lib/cli/job_property_validator.rb +130 -0
- data/lib/cli/package_builder.rb +32 -5
- data/lib/cli/release.rb +2 -0
- data/lib/cli/release_builder.rb +9 -13
- data/lib/cli/release_compiler.rb +5 -34
- data/lib/cli/release_tarball.rb +4 -19
- data/lib/cli/runner.rb +118 -694
- data/lib/cli/version.rb +1 -1
- data/spec/assets/config/swift-hp/config/final.yml +6 -0
- data/spec/assets/config/swift-hp/config/private.yml +7 -0
- data/spec/assets/config/swift-rackspace/config/final.yml +6 -0
- data/spec/assets/config/swift-rackspace/config/private.yml +6 -0
- data/spec/spec_helper.rb +0 -5
- data/spec/unit/base_command_spec.rb +32 -37
- data/spec/unit/biff_spec.rb +11 -10
- data/spec/unit/cli_commands_spec.rb +96 -88
- data/spec/unit/core_ext_spec.rb +1 -1
- data/spec/unit/deployment_manifest_spec.rb +36 -0
- data/spec/unit/director_spec.rb +17 -3
- data/spec/unit/job_builder_spec.rb +2 -2
- data/spec/unit/job_property_collection_spec.rb +111 -0
- data/spec/unit/job_property_validator_spec.rb +7 -0
- data/spec/unit/job_rename_spec.rb +7 -6
- data/spec/unit/package_builder_spec.rb +2 -2
- data/spec/unit/release_builder_spec.rb +33 -0
- data/spec/unit/release_spec.rb +54 -0
- data/spec/unit/release_tarball_spec.rb +2 -7
- data/spec/unit/runner_spec.rb +1 -151
- data/spec/unit/ssh_spec.rb +15 -9
- metadata +41 -12
- data/lib/cli/command_definition.rb +0 -52
- 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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
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
|
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
|
-
|
49
|
-
|
50
|
-
|
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(
|
54
|
-
|
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
|
-
|
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
|
-
|
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
|
88
|
+
recreate = !!options[:recreate]
|
82
89
|
|
83
|
-
manifest_yaml =
|
84
|
-
|
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
|
-
|
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
|
115
|
+
force = !!options[:force]
|
105
116
|
|
106
|
-
say("\nYou are going to delete deployment `#{name}'
|
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.
|
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
|
-
|
204
|
+
nl
|
134
205
|
say(deployments_table)
|
135
|
-
|
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
|
data/lib/cli/commands/job.rb
CHANGED
@@ -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
|
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
|
-
|
8
|
-
|
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
|
-
|
12
|
-
|
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
|
-
|
16
|
-
|
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
|
-
|
20
|
-
|
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,
|
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
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
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
|
-
|
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
|