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