bosh_cli 0.18 → 0.19

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 CHANGED
@@ -11,7 +11,10 @@ end
11
11
  $:.unshift(File.expand_path("../../lib", __FILE__))
12
12
  require "cli"
13
13
 
14
- trap("INT") { puts "\nExiting..."; exit(1) }
15
-
16
- Thread.abort_on_exception = true
17
- Bosh::Cli::Runner.run(ARGV.dup)
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"
@@ -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 = Cache.new(cache_dir)
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
@@ -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
- f.write(str1)
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
- if old_director_uuid != manifest["director_uuid"]
42
- new_target_url = config.resolve_alias(:target,
43
- manifest["director_uuid"])
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("Cannot find director url for " +
46
- "UUID '#{manifest["director_uuid"]}'")
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 |name, value|
118
- if name != PUBLIC_STEMCELL_INDEX
119
- t << [name, value["url"]]
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
@@ -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>'. Check parse_flags to see what flags can be
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
- err("Task id must be a positive integer") unless task_id.to_i > 0
16
+ track_options = {
17
+ :log_type => log_type,
18
+ :use_cache => no_cache ? false : true,
19
+ :raw_output => raw_output
20
+ }
17
21
 
18
- task = Bosh::Cli::DirectorTask.new(director, task_id, log_type)
19
- say("Task state: #{task.state}")
22
+ if task_id.to_i <= 0
23
+ err("Task id must be a positive integer")
24
+ end
20
25
 
21
- cached_output = get_cached_task_output(task_id, log_type) unless no_cache
26
+ tracker = Bosh::Cli::TaskTracker.new(director, task_id, track_options)
27
+ tracker.track
28
+ end
22
29
 
23
- if raw_output
24
- renderer = Bosh::Cli::TaskLogRenderer.new
25
- else
26
- renderer = Bosh::Cli::TaskLogRenderer.create_for_log_type(log_type)
27
- renderer.time_adjustment = director.get_time_difference
28
- end
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
- say("Task log:")
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
- if cached_output
33
- renderer.add_output(cached_output)
34
- # renderer.finish calls render which prints the output.
35
- renderer.finish(task.state)
36
- else
37
- # This calls renderer.finish which calls render and prints the output.
38
- fetch_print_and_save_output(task, task_id, log_type, renderer)
39
- end
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
- nl
65
+ log_type = get_log_type(flags)
66
+ no_cache = flags.include?("--no-cache")
67
+ raw_output = flags.include?("--raw")
42
68
 
43
- print_task_state_and_timing(task, task_id, renderer)
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? || ["last", "latest"].include?(task_id)
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?("--event")
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