bosh_cli 0.18 → 0.19
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/bosh +7 -4
- data/lib/cli.rb +2 -0
- data/lib/cli/commands/base.rb +1 -18
- data/lib/cli/commands/biff.rb +3 -7
- data/lib/cli/commands/cloudcheck.rb +1 -0
- data/lib/cli/commands/deployment.rb +10 -6
- data/lib/cli/commands/job_rename.rb +116 -0
- data/lib/cli/commands/stemcell.rb +3 -3
- data/lib/cli/commands/task.rb +52 -143
- data/lib/cli/commands/vms.rb +2 -2
- data/lib/cli/config.rb +1 -0
- data/lib/cli/deployment_helper.rb +62 -22
- data/lib/cli/director.rb +85 -196
- data/lib/cli/director_task.rb +4 -4
- data/lib/cli/event_log_renderer.rb +5 -1
- data/lib/cli/null_renderer.rb +19 -0
- data/lib/cli/package_builder.rb +91 -62
- data/lib/cli/packaging_helper.rb +1 -1
- data/lib/cli/release_builder.rb +47 -13
- data/lib/cli/runner.rb +21 -39
- data/lib/cli/task_log_renderer.rb +9 -0
- data/lib/cli/task_tracker.rb +168 -0
- data/lib/cli/templates/help_message.erb +1 -0
- data/lib/cli/version.rb +1 -1
- data/lib/cli/versions_index.rb +3 -3
- data/spec/unit/biff_spec.rb +5 -0
- data/spec/unit/director_spec.rb +96 -192
- data/spec/unit/job_rename_spec.rb +195 -0
- data/spec/unit/package_builder_spec.rb +188 -186
- data/spec/unit/release_builder_spec.rb +27 -9
- data/spec/unit/runner_spec.rb +0 -25
- data/spec/unit/task_tracker_spec.rb +154 -0
- metadata +11 -4
data/bin/bosh
CHANGED
@@ -11,7 +11,10 @@ end
|
|
11
11
|
$:.unshift(File.expand_path("../../lib", __FILE__))
|
12
12
|
require "cli"
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
begin
|
15
|
+
Thread.abort_on_exception = true
|
16
|
+
Bosh::Cli::Runner.run(ARGV.dup)
|
17
|
+
rescue Interrupt
|
18
|
+
puts "\nExiting..."
|
19
|
+
exit(1)
|
20
|
+
end
|
data/lib/cli.rb
CHANGED
@@ -55,8 +55,10 @@ require "cli/packaging_helper"
|
|
55
55
|
require "cli/package_builder"
|
56
56
|
require "cli/job_builder"
|
57
57
|
require "cli/changeset_helper"
|
58
|
+
require "cli/task_tracker"
|
58
59
|
require "cli/task_log_renderer"
|
59
60
|
require "cli/event_log_renderer"
|
61
|
+
require "cli/null_renderer"
|
60
62
|
require "cli/deployment_manifest_compiler"
|
61
63
|
|
62
64
|
require "cli/release"
|
data/lib/cli/commands/base.rb
CHANGED
@@ -13,9 +13,8 @@ module Bosh::Cli
|
|
13
13
|
@options = options.dup
|
14
14
|
@work_dir = Dir.pwd
|
15
15
|
config_file = @options[:config] || Bosh::Cli::DEFAULT_CONFIG_PATH
|
16
|
-
cache_dir = @options[:cache_dir] || Bosh::Cli::DEFAULT_CACHE_DIR
|
17
16
|
@config = Config.new(config_file)
|
18
|
-
@cache =
|
17
|
+
@cache = Config.cache
|
19
18
|
end
|
20
19
|
|
21
20
|
class << self
|
@@ -107,22 +106,6 @@ module Bosh::Cli
|
|
107
106
|
ret + " %s" % target_version if ret
|
108
107
|
end
|
109
108
|
|
110
|
-
##
|
111
|
-
# Returns whether there is currently a task running. A wrapper for the
|
112
|
-
# director.rb method.
|
113
|
-
#
|
114
|
-
# @return [Boolean] Whether there is a task currently running.
|
115
|
-
def task_running?
|
116
|
-
director.has_current?
|
117
|
-
end
|
118
|
-
|
119
|
-
##
|
120
|
-
# Cancels the task currently running. A wrapper for the director.rb
|
121
|
-
# method.
|
122
|
-
def cancel_current_task
|
123
|
-
director.cancel_current
|
124
|
-
end
|
125
|
-
|
126
109
|
protected
|
127
110
|
|
128
111
|
def auth_required
|
data/lib/cli/commands/biff.rb
CHANGED
@@ -52,12 +52,8 @@ module Bosh::Cli::Command
|
|
52
52
|
# @param [String] str1 The first string to diff.
|
53
53
|
# @param [String] str2 The string to diff against.
|
54
54
|
def print_string_diff(str1, str2)
|
55
|
-
File.open(@temp_file_path_1, "w") { |f|
|
56
|
-
|
57
|
-
}
|
58
|
-
File.open(@temp_file_path_2, "w") { |f|
|
59
|
-
f.write(str2)
|
60
|
-
}
|
55
|
+
File.open(@temp_file_path_1, "w") { |f| f.write(str1) }
|
56
|
+
File.open(@temp_file_path_2, "w") { |f| f.write(str2) }
|
61
57
|
|
62
58
|
@diff_works = true
|
63
59
|
cmd = "#{DIFF_COMMAND} #{@temp_file_path_1} #{@temp_file_path_2} 2>&1"
|
@@ -123,7 +119,7 @@ module Bosh::Cli::Command
|
|
123
119
|
found = true
|
124
120
|
end
|
125
121
|
end
|
126
|
-
elsif obj[path_part]
|
122
|
+
elsif !obj[path_part].nil?
|
127
123
|
obj = obj[path_part]
|
128
124
|
found = true
|
129
125
|
end
|
@@ -31,6 +31,7 @@ module Bosh::Cli::Command
|
|
31
31
|
status, body = director.perform_cloud_scan(deployment_name)
|
32
32
|
scan_failed(status, body) if status != :done
|
33
33
|
|
34
|
+
nl
|
34
35
|
say("Scan is complete, checking if any problems found...")
|
35
36
|
@problems = director.list_problems(deployment_name)
|
36
37
|
|
@@ -38,12 +38,16 @@ module Bosh::Cli::Command
|
|
38
38
|
old_director_uuid = nil
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
new_director_uuid = manifest["director_uuid"]
|
42
|
+
|
43
|
+
if old_director_uuid != new_director_uuid
|
44
|
+
new_target_url = config.resolve_alias(:target, new_director_uuid)
|
45
|
+
|
44
46
|
if new_target_url.blank?
|
45
|
-
err("
|
46
|
-
|
47
|
+
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.")
|
47
51
|
end
|
48
52
|
|
49
53
|
new_director = Bosh::Cli::Director.new(new_target_url,
|
@@ -54,7 +58,7 @@ module Bosh::Cli::Command
|
|
54
58
|
config.target_name = status["name"]
|
55
59
|
config.target_version = status["version"]
|
56
60
|
config.target_uuid = status["uuid"]
|
57
|
-
say("#{"WARNING!".red} Your target has been" +
|
61
|
+
say("#{"WARNING!".red} Your target has been " +
|
58
62
|
"changed to `#{target.red}'!")
|
59
63
|
end
|
60
64
|
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
module Bosh::Cli::Command
|
4
|
+
class JobRename < Base
|
5
|
+
include Bosh::Cli::DeploymentHelper
|
6
|
+
|
7
|
+
def rename(*args)
|
8
|
+
auth_required
|
9
|
+
manifest_yaml = prepare_deployment_manifest(:yaml => true)
|
10
|
+
manifest = YAML.load(manifest_yaml)
|
11
|
+
|
12
|
+
args = args.dup
|
13
|
+
force = args.delete("--force")
|
14
|
+
old_name = args.shift
|
15
|
+
new_name = args.shift
|
16
|
+
|
17
|
+
sanity_check_job_rename(manifest_yaml, old_name, new_name)
|
18
|
+
|
19
|
+
say("You are about to rename #{old_name.green} to " +
|
20
|
+
"#{new_name.green} #{force}")
|
21
|
+
|
22
|
+
status, body = director.rename_job(manifest["name"], manifest_yaml,
|
23
|
+
old_name, new_name, force)
|
24
|
+
responses = {
|
25
|
+
:done => "Rename successful",
|
26
|
+
:non_trackable => "Started deployment but director at '#{target}' " +
|
27
|
+
"doesn't support deployment tracking",
|
28
|
+
:track_timeout => "Started deployment but timed out out "+
|
29
|
+
"while tracking status",
|
30
|
+
:error => "Started deployment but received an error " +
|
31
|
+
"while tracking status",
|
32
|
+
:invalid => "Deployment is invalid, please fix it and deploy again"
|
33
|
+
}
|
34
|
+
|
35
|
+
say(responses[status] || "Cannot deploy: #{body}")
|
36
|
+
end
|
37
|
+
|
38
|
+
def sanity_check_job_rename(manifest_yaml, old_name, new_name)
|
39
|
+
|
40
|
+
# Makes sure the new deployment manifest contains the renamed job
|
41
|
+
manifest = YAML.load(manifest_yaml)
|
42
|
+
new_jobs = manifest["jobs"].map { |job| job["name"] }
|
43
|
+
unless new_jobs.include?(new_name)
|
44
|
+
err "Please update your deployment manifest to include the " +
|
45
|
+
"new job name #{new_name.green}"
|
46
|
+
end
|
47
|
+
|
48
|
+
if new_jobs.include?(old_name)
|
49
|
+
err "Old name #{old_name.green} is still being used in the " +
|
50
|
+
"deployment file"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Make sure that the old deployment manifest contains the old job
|
54
|
+
current_deployment = director.get_deployment(manifest["name"])
|
55
|
+
if current_deployment["manifest"].nil?
|
56
|
+
err "Director could not find manifest for deployment " +
|
57
|
+
"#{manifest["name"]}"
|
58
|
+
end
|
59
|
+
|
60
|
+
current_manifest = YAML.load(current_deployment["manifest"])
|
61
|
+
jobs = current_manifest["jobs"].map { |job| job["name"] }
|
62
|
+
unless jobs.include?(old_name)
|
63
|
+
err "Trying to rename a non existent job #{old_name}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Technically we could allow this
|
67
|
+
if jobs.include?(new_name)
|
68
|
+
err "Trying to reuse an existing job name #{new_name} " +
|
69
|
+
"to rename job #{old_name}"
|
70
|
+
end
|
71
|
+
|
72
|
+
# Make sure that only one job has been renamed
|
73
|
+
added_jobs = new_jobs - jobs
|
74
|
+
|
75
|
+
if added_jobs.size > 1
|
76
|
+
err "Cannot rename more than one job, you are trying to " +
|
77
|
+
"add #{added_jobs.inspect}"
|
78
|
+
end
|
79
|
+
|
80
|
+
if added_jobs.first != new_name
|
81
|
+
err "Manifest does not include new job #{new_name}"
|
82
|
+
end
|
83
|
+
|
84
|
+
renamed_jobs = jobs - new_jobs
|
85
|
+
|
86
|
+
if renamed_jobs.size > 1
|
87
|
+
err "Cannot rename more than one job, you have changes to " +
|
88
|
+
"#{renamed_jobs}"
|
89
|
+
end
|
90
|
+
|
91
|
+
if renamed_jobs.first != old_name
|
92
|
+
err "Manifest does not rename old job #{old_name}"
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
# Final sanity check, make sure that no
|
97
|
+
# other properties or anything other than the names
|
98
|
+
# have changed. So update current manifest with new name
|
99
|
+
# and check that it matches with the old manifest
|
100
|
+
current_manifest["jobs"].each do |job|
|
101
|
+
if job["name"] == old_name
|
102
|
+
job["name"] = new_name
|
103
|
+
break
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Now the manifests should be the same
|
108
|
+
manifest = YAML.load(manifest_yaml)
|
109
|
+
if deployment_changed?(current_manifest.dup, manifest.dup)
|
110
|
+
err "You cannot have any other changes to your manifest during " +
|
111
|
+
"rename. Please revert the above changes and retry."
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
@@ -114,9 +114,9 @@ module Bosh::Cli::Command
|
|
114
114
|
yaml = get_public_stemcell_list
|
115
115
|
stemcells_table = table do |t|
|
116
116
|
t.headings = "Name", "Url"
|
117
|
-
yaml.each do |
|
118
|
-
if
|
119
|
-
t << [
|
117
|
+
yaml.keys.sort.each do |key|
|
118
|
+
if key != PUBLIC_STEMCELL_INDEX
|
119
|
+
t << [key, yaml[key]["url"]]
|
120
120
|
end
|
121
121
|
end
|
122
122
|
end
|
data/lib/cli/commands/task.rb
CHANGED
@@ -4,7 +4,7 @@ module Bosh::Cli::Command
|
|
4
4
|
class Task < Base
|
5
5
|
|
6
6
|
# Tracks a running task or outputs the logs from an old task. Triggered
|
7
|
-
# with 'bosh task <task_num>'.
|
7
|
+
# with 'bosh task <task_num>'. Check parse_flags to see what flags can be
|
8
8
|
# used with this.
|
9
9
|
#
|
10
10
|
# @param [Array] args The arguments from the command line command.
|
@@ -13,34 +13,60 @@ module Bosh::Cli::Command
|
|
13
13
|
|
14
14
|
task_id, log_type, no_cache, raw_output = parse_flags(args)
|
15
15
|
|
16
|
-
|
16
|
+
track_options = {
|
17
|
+
:log_type => log_type,
|
18
|
+
:use_cache => no_cache ? false : true,
|
19
|
+
:raw_output => raw_output
|
20
|
+
}
|
17
21
|
|
18
|
-
|
19
|
-
|
22
|
+
if task_id.to_i <= 0
|
23
|
+
err("Task id must be a positive integer")
|
24
|
+
end
|
20
25
|
|
21
|
-
|
26
|
+
tracker = Bosh::Cli::TaskTracker.new(director, task_id, track_options)
|
27
|
+
tracker.track
|
28
|
+
end
|
22
29
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
def list_running
|
31
|
+
auth_required
|
32
|
+
tasks = director.list_running_tasks
|
33
|
+
err("No running tasks") if tasks.empty?
|
34
|
+
show_tasks_table(tasks.sort_by { |t| t["id"].to_i * -1 })
|
35
|
+
say("Total tasks running now: %d" % [tasks.size])
|
36
|
+
end
|
37
|
+
|
38
|
+
def list_recent(count = 30)
|
39
|
+
auth_required
|
40
|
+
tasks = director.list_recent_tasks(count)
|
41
|
+
err("No recent tasks") if tasks.empty?
|
42
|
+
show_tasks_table(tasks)
|
43
|
+
say("Showing %d recent %s" % [tasks.size,
|
44
|
+
tasks.size == 1 ? "task" : "tasks"])
|
45
|
+
end
|
29
46
|
|
30
|
-
|
47
|
+
def cancel(task_id)
|
48
|
+
auth_required
|
49
|
+
task = Bosh::Cli::DirectorTask.new(director, task_id)
|
50
|
+
task.cancel
|
51
|
+
say("Cancelling task #{task_id}")
|
52
|
+
end
|
31
53
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
54
|
+
private
|
55
|
+
|
56
|
+
# Parses the command line args to see what options have been specified.
|
57
|
+
#
|
58
|
+
# @param [Array] flags The args that were passed in from the command line.
|
59
|
+
# @return [String, String, Boolean, Boolean] The task id, the type of log
|
60
|
+
# output, whether to use cache or not, whether to output the raw log.
|
61
|
+
def parse_flags(flags)
|
62
|
+
task_id = flags.shift
|
63
|
+
task_id = get_last_task_id if asking_for_last_task?(task_id)
|
40
64
|
|
41
|
-
|
65
|
+
log_type = get_log_type(flags)
|
66
|
+
no_cache = flags.include?("--no-cache")
|
67
|
+
raw_output = flags.include?("--raw")
|
42
68
|
|
43
|
-
|
69
|
+
[task_id, log_type, no_cache, raw_output]
|
44
70
|
end
|
45
71
|
|
46
72
|
# Whether the bosh user has asked for the last (most recently run) task.
|
@@ -49,7 +75,7 @@ module Bosh::Cli::Command
|
|
49
75
|
# number as a string or it could be "last" or "latest".
|
50
76
|
# @return [Boolean] Whether the user is asking for the most recent task.
|
51
77
|
def asking_for_last_task?(task_id)
|
52
|
-
task_id.nil? ||
|
78
|
+
task_id.nil? || %w(last latest).include?(task_id)
|
53
79
|
end
|
54
80
|
|
55
81
|
# Returns the task id of the most recently run task.
|
@@ -71,117 +97,13 @@ module Bosh::Cli::Command
|
|
71
97
|
def get_log_type(flags)
|
72
98
|
if flags.include?("--soap")
|
73
99
|
"soap"
|
74
|
-
elsif flags.include?("--
|
75
|
-
"event"
|
76
|
-
else
|
100
|
+
elsif flags.include?("--debug")
|
77
101
|
"debug"
|
102
|
+
else
|
103
|
+
"event"
|
78
104
|
end
|
79
105
|
end
|
80
106
|
|
81
|
-
# Parses the command line args to see what options have been specified.
|
82
|
-
#
|
83
|
-
# @param [Array] flags The args that were passed in from the command line.
|
84
|
-
# @return [String, String, Boolean, Boolean] The task id, the type of log
|
85
|
-
# output, whether to use cache or not, whether to output the raw log.
|
86
|
-
def parse_flags(flags)
|
87
|
-
task_id = flags.shift
|
88
|
-
task_id = get_last_task_id if asking_for_last_task?(task_id)
|
89
|
-
|
90
|
-
log_type = get_log_type(flags)
|
91
|
-
|
92
|
-
no_cache = flags.include?("--no-cache")
|
93
|
-
|
94
|
-
raw_output = flags.include?("--raw")
|
95
|
-
|
96
|
-
[task_id, log_type, no_cache, raw_output]
|
97
|
-
end
|
98
|
-
|
99
|
-
# Grabs the log output for a task, prints it, then saves it to the cache.
|
100
|
-
#
|
101
|
-
# @param [Bosh::Cli::DirectorTask] task The director task that has all of
|
102
|
-
# the methods for retrieving/parsing the director's task JSON.
|
103
|
-
# @param [String] task_id The ID of the task to get logs on.
|
104
|
-
# @param [String] log_type The type of log output.
|
105
|
-
# @param [Bosh::Cli::TaskLogRenderer] renderer The renderer that renders the
|
106
|
-
# parsed task JSON.
|
107
|
-
def fetch_print_and_save_output(task, task_id, log_type, renderer)
|
108
|
-
complete_output = ""
|
109
|
-
|
110
|
-
begin
|
111
|
-
state, output = task.state, task.output
|
112
|
-
|
113
|
-
if output
|
114
|
-
renderer.add_output(output)
|
115
|
-
complete_output << output
|
116
|
-
end
|
117
|
-
|
118
|
-
renderer.refresh
|
119
|
-
sleep(0.5)
|
120
|
-
|
121
|
-
end while ["queued", "processing", "cancelling"].include?(state)
|
122
|
-
|
123
|
-
final_out = task.flush_output
|
124
|
-
|
125
|
-
if final_out
|
126
|
-
renderer.add_output(final_out)
|
127
|
-
complete_output << final_out << "\n"
|
128
|
-
end
|
129
|
-
|
130
|
-
renderer.finish(state)
|
131
|
-
save_task_output(task_id, log_type, complete_output)
|
132
|
-
end
|
133
|
-
|
134
|
-
# Prints the task state and timing information at the end of the output.
|
135
|
-
#
|
136
|
-
# @param [Bosh::Cli::DirectorTask] task The director task that has all of
|
137
|
-
# the methods for retrieving/parsing the director's task JSON.
|
138
|
-
# @param [String] task_id The ID of the task to get logs on.
|
139
|
-
# @param [Bosh::Cli::TaskLogRenderer] renderer The renderer that renders the
|
140
|
-
# parsed task JSON.
|
141
|
-
def print_task_state_and_timing(task, task_id, renderer)
|
142
|
-
final_state = task.state
|
143
|
-
color = {
|
144
|
-
"done" => :green,
|
145
|
-
"error" => :red,
|
146
|
-
}[final_state] || :yellow
|
147
|
-
status = "Task %s state is %s" %
|
148
|
-
[task_id.to_s.green, final_state.colorize(color)]
|
149
|
-
|
150
|
-
duration = renderer.duration
|
151
|
-
if final_state == "done" && duration && duration.kind_of?(Numeric)
|
152
|
-
status += ", started: %s, ended: %s (%s)" %
|
153
|
-
[renderer.started_at.to_s.green, renderer.finished_at.to_s.green,
|
154
|
-
format_time(duration).green]
|
155
|
-
end
|
156
|
-
|
157
|
-
say(status)
|
158
|
-
end
|
159
|
-
|
160
|
-
def list_running
|
161
|
-
auth_required
|
162
|
-
tasks = director.list_running_tasks
|
163
|
-
err("No running tasks") if tasks.empty?
|
164
|
-
show_tasks_table(tasks.sort_by { |t| t["id"].to_i * -1 })
|
165
|
-
say("Total tasks running now: %d" % [tasks.size])
|
166
|
-
end
|
167
|
-
|
168
|
-
def list_recent(count = 30)
|
169
|
-
auth_required
|
170
|
-
tasks = director.list_recent_tasks(count)
|
171
|
-
err("No recent tasks") if tasks.empty?
|
172
|
-
show_tasks_table(tasks)
|
173
|
-
say("Showing %d recent %s" % [tasks.size,
|
174
|
-
tasks.size == 1 ? "task" : "tasks"])
|
175
|
-
end
|
176
|
-
|
177
|
-
def cancel(task_id)
|
178
|
-
task = Bosh::Cli::DirectorTask.new(director, task_id)
|
179
|
-
task.cancel
|
180
|
-
say("Cancelling task #{task_id}")
|
181
|
-
end
|
182
|
-
|
183
|
-
private
|
184
|
-
|
185
107
|
def show_tasks_table(tasks)
|
186
108
|
return if tasks.empty?
|
187
109
|
tasks_table = table do |t|
|
@@ -196,18 +118,5 @@ module Bosh::Cli::Command
|
|
196
118
|
say(tasks_table)
|
197
119
|
say("\n")
|
198
120
|
end
|
199
|
-
|
200
|
-
def get_cached_task_output(task_id, log_type)
|
201
|
-
cache.read(task_cache_key(task_id, log_type))
|
202
|
-
end
|
203
|
-
|
204
|
-
def save_task_output(task_id, log_type, output)
|
205
|
-
cache.write(task_cache_key(task_id, log_type), output)
|
206
|
-
end
|
207
|
-
|
208
|
-
def task_cache_key(task_id, log_type)
|
209
|
-
"task/#{target}/#{task_id}/#{log_type}"
|
210
|
-
end
|
211
|
-
|
212
121
|
end
|
213
122
|
end
|