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.
@@ -6,6 +6,10 @@ module Bosh::Cli
6
6
  def self.create_for_log_type(log_type)
7
7
  if log_type == "event"
8
8
  EventLogRenderer.new
9
+ elsif log_type == "result"
10
+ # Null renderer doesn't output anything to screen, so it fits well
11
+ # in case we need to fetch task result log only, without rendering it
12
+ NullRenderer.new
9
13
  else
10
14
  TaskLogRenderer.new
11
15
  end
@@ -20,6 +24,11 @@ module Bosh::Cli
20
24
  @lock = Mutex.new
21
25
  @output = ""
22
26
  @time_adjustment = 0
27
+ @duration = nil
28
+ end
29
+
30
+ def duration_known?
31
+ false # TODO: make it available for basic renderer
23
32
  end
24
33
 
25
34
  def add_output(output)
@@ -0,0 +1,168 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh
4
+ module Cli
5
+ # This class is responsible for tracking director tasks
6
+ class TaskTracker
7
+
8
+ MAX_POLLS = nil # not limited
9
+ POLL_INTERVAL = 1 # second
10
+
11
+ attr_reader :output
12
+
13
+ # @param [Bosh::Cli::Director] director
14
+ # @param [Integer] task_id
15
+ # @param [Hash] options
16
+ def initialize(director, task_id, options = {})
17
+ @director = director
18
+ @task_id = task_id
19
+ @options = options
20
+
21
+ @log_type = options[:log_type] || "event"
22
+ @use_cache = options.key?(:use_cache) ? @options[:use_cache] : true
23
+
24
+ @output = nil
25
+ @cache = Config.cache
26
+ @task = Bosh::Cli::DirectorTask.new(@director, @task_id, @log_type)
27
+
28
+ if options[:raw_output]
29
+ @renderer = Bosh::Cli::TaskLogRenderer.new
30
+ else
31
+ @renderer = Bosh::Cli::TaskLogRenderer.create_for_log_type(@log_type)
32
+ end
33
+ end
34
+
35
+ # Tracks director task. Blocks until task is in one of the 'finished'
36
+ # states (done, error, cancelled). Handles Ctrl+C by prompting to cancel
37
+ # task.
38
+ # @return [Symbol] Task status
39
+ def track
40
+ nl
41
+ @renderer.time_adjustment = @director.get_time_difference
42
+ say("Director task #{@task_id.to_s.yellow}")
43
+
44
+ cached_output = get_cached_task_output
45
+
46
+ if cached_output
47
+ task_status = @task.state.to_sym
48
+ output_received(cached_output)
49
+ @renderer.refresh
50
+ else
51
+ task_status = poll
52
+ end
53
+
54
+ if task_status == :error && interactive? && @log_type != "debug"
55
+ prompt_for_debug_log
56
+ else
57
+ print_task_summary(task_status)
58
+ end
59
+
60
+ save_task_output unless cached_output
61
+ task_status
62
+ end
63
+
64
+ def poll
65
+ polls = 0
66
+
67
+ while true
68
+ polls += 1
69
+ state = @task.state
70
+ output = @task.output
71
+
72
+ output_received(output)
73
+ @renderer.refresh
74
+
75
+ if finished?(state)
76
+ return state.to_sym
77
+ elsif MAX_POLLS && polls >= MAX_POLLS
78
+ return :track_timeout
79
+ end
80
+
81
+ sleep(POLL_INTERVAL)
82
+ end
83
+
84
+ :unknown
85
+ rescue Interrupt # Local ctrl-c handler
86
+ prompt_for_task_cancel
87
+ end
88
+
89
+ def prompt_for_debug_log
90
+ return unless interactive?
91
+ nl
92
+ confirm = ask("The task has returned an error status, " +
93
+ "do you want to see debug log? [Yn]: ")
94
+ if confirm.empty? || confirm =~ /y(es)?/i
95
+ self.class.new(@director, @task_id,
96
+ @options.merge(:log_type => "debug")).track
97
+ else
98
+ say("Please use 'bosh task #{@task_id}' command ".red +
99
+ "to see the debug log".red)
100
+ end
101
+ end
102
+
103
+ def prompt_for_task_cancel
104
+ return unless interactive?
105
+ nl
106
+ confirm = ask("Do you want to cancel task #{@task_id}? [yN] " +
107
+ "(^C again to detach): ")
108
+
109
+ if confirm =~ /y(es)?/i
110
+ say("Cancelling task #{@task_id}...")
111
+ @director.cancel_task(@task_id)
112
+ end
113
+
114
+ poll
115
+ rescue Interrupt
116
+ nl
117
+ err("Task #{@task_id} is still running")
118
+ end
119
+
120
+ def print_task_summary(task_status)
121
+ output_received(@task.flush_output)
122
+ @renderer.finish(task_status)
123
+
124
+ nl
125
+ say("Task #{@task_id} #{task_status.to_s.yellow}")
126
+
127
+ if task_status == :done && @renderer.duration_known?
128
+ say("Started\t\t#{@renderer.started_at.utc.to_s}")
129
+ say("Finished\t#{@renderer.finished_at.utc.to_s}")
130
+ say("Duration\t#{format_time(@renderer.duration).yellow}")
131
+ end
132
+ end
133
+
134
+ private
135
+
136
+ # @param [String] output Output received from director task
137
+ def output_received(output)
138
+ return if output.nil?
139
+ @output ||= ""
140
+ @output << output
141
+ @renderer.add_output(output)
142
+ end
143
+
144
+ def finished?(state)
145
+ %(done error cancelled).include?(state)
146
+ end
147
+
148
+ def interactive?
149
+ Bosh::Cli::Config.interactive
150
+ end
151
+
152
+ def get_cached_task_output
153
+ return nil unless @use_cache
154
+ @cache.read(task_cache_key)
155
+ end
156
+
157
+ def save_task_output
158
+ return nil unless @output && @use_cache
159
+ @cache.write(task_cache_key, @output)
160
+ end
161
+
162
+ def task_cache_key
163
+ "task/#{@director.uuid}/#{@task_id}/#{@log_type}"
164
+ end
165
+
166
+ end
167
+ end
168
+ end
@@ -36,6 +36,7 @@ Job management
36
36
  <%= command_usage(:stop_job) %>
37
37
  <%= command_usage(:restart_job) %>
38
38
  <%= command_usage(:recreate_job) %>
39
+ <%= command_usage(:rename_job) %>
39
40
 
40
41
  Log management
41
42
  <%= command_usage(:fetch_logs) %>
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bosh
4
4
  module Cli
5
- VERSION = "0.18"
5
+ VERSION = "0.19"
6
6
  end
7
7
  end
@@ -38,14 +38,14 @@ module Bosh::Cli
38
38
 
39
39
  return nil if builds.empty?
40
40
 
41
- sorted = builds.sort { |build1, build2|
41
+ sorted = builds.sort do |build1, build2|
42
42
  cmp = version_cmp(build2["version"], build1["version"])
43
43
  if cmp == 0
44
44
  raise "There is a duplicate version `#{build1["version"]}' " +
45
- "in index `#{@index_file}'"
45
+ "in index `#{@index_file}'"
46
46
  end
47
47
  cmp
48
- }
48
+ end
49
49
 
50
50
  sorted[0]["version"]
51
51
  end
@@ -110,6 +110,11 @@ describe Bosh::Cli::Command::Biff do
110
110
  @biff.find_in("path1.path2.path3", obj).should == 3
111
111
  end
112
112
 
113
+ it "finds the object(boolean) path" do
114
+ obj = { "path1" => {"path2" => {"path3" => false}} }
115
+ @biff.find_in("path1.path2.path3", obj).should == false
116
+ end
117
+
113
118
  it "finds the object path in an array by the name key" do
114
119
  obj = { "by_key" => 1, "arr" => [{"name" => "by_name"}]}
115
120
  @biff.find_in("arr.by_name", obj).should == {"name" => "by_name"}
@@ -13,30 +13,30 @@ describe Bosh::Cli::Director do
13
13
  describe "fetching status" do
14
14
  it "tells if user is authenticated" do
15
15
  @director.should_receive(:get).with("/info", "application/json").
16
- and_return([200, JSON.generate("user" => "adam")])
16
+ and_return([200, JSON.generate("user" => "adam")])
17
17
  @director.authenticated?.should == true
18
18
  end
19
19
 
20
20
  it "tells if user not authenticated" do
21
21
  @director.should_receive(:get).with("/info", "application/json").
22
- and_return([403, "Forbidden"])
22
+ and_return([403, "Forbidden"])
23
23
  @director.authenticated?.should == false
24
24
 
25
25
  @director.should_receive(:get).with("/info", "application/json").
26
- and_return([500, "Error"])
26
+ and_return([500, "Error"])
27
27
  @director.authenticated?.should == false
28
28
 
29
29
  @director.should_receive(:get).with("/info", "application/json").
30
- and_return([404, "Not Found"])
30
+ and_return([404, "Not Found"])
31
31
  @director.authenticated?.should == false
32
32
 
33
33
  @director.should_receive(:get).with("/info", "application/json").
34
- and_return([200, JSON.generate("user" => nil, "version" => 1)])
34
+ and_return([200, JSON.generate("user" => nil, "version" => 1)])
35
35
  @director.authenticated?.should == false
36
36
 
37
37
  # Backward compatibility
38
38
  @director.should_receive(:get).with("/info", "application/json").
39
- and_return([200, JSON.generate("status" => "ZB")])
39
+ and_return([200, JSON.generate("status" => "ZB")])
40
40
  @director.authenticated?.should == true
41
41
  end
42
42
  end
@@ -53,157 +53,144 @@ describe Bosh::Cli::Director do
53
53
  describe "API calls" do
54
54
  it "creates user" do
55
55
  @director.should_receive(:post).
56
- with("/users", "application/json",
57
- JSON.generate("username" => "joe", "password" => "pass")).
58
- and_return(true)
56
+ with("/users", "application/json",
57
+ JSON.generate("username" => "joe", "password" => "pass")).
58
+ and_return(true)
59
59
  @director.create_user("joe", "pass")
60
60
  end
61
61
 
62
62
  it "uploads stemcell" do
63
63
  @director.should_receive(:upload_and_track).
64
- with("/stemcells", "application/x-compressed",
65
- "/path", :log_type=>"event").and_return(true)
64
+ with("/stemcells", "application/x-compressed", "/path").
65
+ and_return(true)
66
66
  @director.upload_stemcell("/path")
67
67
  end
68
68
 
69
69
  it "lists stemcells" do
70
70
  @director.should_receive(:get).with("/stemcells", "application/json").
71
- and_return([200, JSON.generate([]), {}])
71
+ and_return([200, JSON.generate([]), {}])
72
72
  @director.list_stemcells
73
73
  end
74
74
 
75
75
  it "lists releases" do
76
76
  @director.should_receive(:get).with("/releases", "application/json").
77
- and_return([200, JSON.generate([]), {}])
77
+ and_return([200, JSON.generate([]), {}])
78
78
  @director.list_releases
79
79
  end
80
80
 
81
81
  it "lists deployments" do
82
82
  @director.should_receive(:get).with("/deployments", "application/json").
83
- and_return([200, JSON.generate([]), {}])
83
+ and_return([200, JSON.generate([]), {}])
84
84
  @director.list_deployments
85
85
  end
86
86
 
87
87
  it "lists currently running tasks (director version < 0.3.5)" do
88
88
  @director.should_receive(:get).with("/info", "application/json").
89
- and_return([200, JSON.generate({ :version => "0.3.2"})])
89
+ and_return([200, JSON.generate({ :version => "0.3.2"})])
90
90
  @director.should_receive(:get).
91
- with("/tasks?state=processing", "application/json").
92
- and_return([200, JSON.generate([]), {}])
91
+ with("/tasks?state=processing", "application/json").
92
+ and_return([200, JSON.generate([]), {}])
93
93
  @director.list_running_tasks
94
94
  end
95
95
 
96
96
  it "lists currently running tasks (director version >= 0.3.5)" do
97
97
  @director.should_receive(:get).
98
- with("/info", "application/json").
99
- and_return([200, JSON.generate({ :version => "0.3.5"})])
98
+ with("/info", "application/json").
99
+ and_return([200, JSON.generate({ :version => "0.3.5"})])
100
100
  @director.should_receive(:get).
101
- with("/tasks?state=processing,cancelling,queued", "application/json").
102
- and_return([200, JSON.generate([]), {}])
101
+ with("/tasks?state=processing,cancelling,queued", "application/json").
102
+ and_return([200, JSON.generate([]), {}])
103
103
  @director.list_running_tasks
104
104
  end
105
105
 
106
106
  it "lists recent tasks" do
107
107
  @director.should_receive(:get).
108
- with("/tasks?limit=30", "application/json").
109
- and_return([200, JSON.generate([]), {}])
108
+ with("/tasks?limit=30", "application/json").
109
+ and_return([200, JSON.generate([]), {}])
110
110
  @director.list_recent_tasks
111
111
 
112
112
  @director.should_receive(:get).
113
- with("/tasks?limit=100", "application/json").
114
- and_return([200, JSON.generate([]), {}])
113
+ with("/tasks?limit=100", "application/json").
114
+ and_return([200, JSON.generate([]), {}])
115
115
  @director.list_recent_tasks(100000)
116
116
  end
117
117
 
118
118
  it "uploads release" do
119
119
  @director.should_receive(:upload_and_track).
120
- with("/releases", "application/x-compressed",
121
- "/path", :log_type => "event").
122
- and_return(true)
120
+ with("/releases", "application/x-compressed", "/path").
121
+ and_return(true)
123
122
  @director.upload_release("/path")
124
123
  end
125
124
 
126
125
  it "gets release info" do
127
126
  @director.should_receive(:get).
128
- with("/releases/foo", "application/json").
129
- and_return([200, JSON.generate([]), { }])
127
+ with("/releases/foo", "application/json").
128
+ and_return([200, JSON.generate([]), { }])
130
129
  @director.get_release("foo")
131
130
  end
132
131
 
133
132
  it "gets deployment info" do
134
133
  @director.should_receive(:get).
135
- with("/deployments/foo", "application/json").
136
- and_return([200, JSON.generate([]), { }])
134
+ with("/deployments/foo", "application/json").
135
+ and_return([200, JSON.generate([]), { }])
137
136
  @director.get_deployment("foo")
138
137
  end
139
138
 
140
139
  it "deletes stemcell" do
141
140
  @director.should_receive(:request_and_track).
142
- with(:delete, "/stemcells/ubuntu/123",
143
- nil, nil, :log_type => "event").
144
- and_return(true)
141
+ with(:delete, "/stemcells/ubuntu/123").and_return(true)
145
142
  @director.delete_stemcell("ubuntu", "123")
146
143
  end
147
144
 
148
145
  it "deletes deployment" do
149
146
  @director.should_receive(:request_and_track).
150
- with(:delete, "/deployments/foo",
151
- nil, nil, :log_type => "event").
152
- and_return(true)
147
+ with(:delete, "/deployments/foo").and_return(true)
153
148
  @director.delete_deployment("foo")
154
149
  end
155
150
 
156
151
  it "deletes release (non-force)" do
157
152
  @director.should_receive(:request_and_track).
158
- with(:delete, "/releases/za",
159
- nil, nil, :log_type => "event").
160
- and_return(true)
153
+ with(:delete, "/releases/za").and_return(true)
161
154
  @director.delete_release("za")
162
155
  end
163
156
 
164
157
  it "deletes release (force)" do
165
158
  @director.should_receive(:request_and_track).
166
- with(:delete, "/releases/zb?force=true",
167
- nil, nil, :log_type => "event").
168
- and_return(true)
159
+ with(:delete, "/releases/zb?force=true").and_return(true)
169
160
  @director.delete_release("zb", :force => true)
170
161
  end
171
162
 
172
163
  it "deploys" do
173
164
  @director.should_receive(:request_and_track).
174
- with(:post, "/deployments", "text/yaml",
175
- "manifest", :log_type => "event").
176
- and_return(true)
165
+ with(:post, "/deployments", "text/yaml", "manifest").and_return(true)
177
166
  @director.deploy("manifest")
178
167
  end
179
168
 
180
169
  it "changes job state" do
181
170
  @director.should_receive(:request_and_track).
182
- with(:put, "/deployments/foo/jobs/dea?state=stopped",
183
- "text/yaml", "manifest", :log_type => "event").
184
- and_return(true)
171
+ with(:put, "/deployments/foo/jobs/dea?state=stopped",
172
+ "text/yaml", "manifest").and_return(true)
185
173
  @director.change_job_state("foo", "manifest", "dea", nil, "stopped")
186
174
  end
187
175
 
188
176
  it "changes job instance state" do
189
177
  @director.should_receive(:request_and_track).
190
- with(:put, "/deployments/foo/jobs/dea/0?state=detached",
191
- "text/yaml", "manifest", :log_type => "event").
192
- and_return(true)
178
+ with(:put, "/deployments/foo/jobs/dea/0?state=detached",
179
+ "text/yaml", "manifest").and_return(true)
193
180
  @director.change_job_state("foo", "manifest", "dea", 0, "detached")
194
181
  end
195
182
 
196
183
  it "gets task state" do
197
184
  @director.should_receive(:get).
198
- with("/tasks/232").
199
- and_return([200, JSON.generate({ "state" => "done" })])
185
+ with("/tasks/232").
186
+ and_return([200, JSON.generate({ "state" => "done" })])
200
187
  @director.get_task_state(232).should == "done"
201
188
  end
202
189
 
203
190
  it "whines on missing task" do
204
191
  @director.should_receive(:get).
205
- with("/tasks/232").
206
- and_return([404, "Not Found"])
192
+ with("/tasks/232").
193
+ and_return([404, "Not Found"])
207
194
  lambda {
208
195
  @director.get_task_state(232).should
209
196
  }.should raise_error(Bosh::Cli::MissingTask)
@@ -211,17 +198,17 @@ describe Bosh::Cli::Director do
211
198
 
212
199
  it "gets task output" do
213
200
  @director.should_receive(:get).
214
- with("/tasks/232/output", nil,
215
- nil, { "Range" => "bytes=42-" }).
216
- and_return([206, "test", { :content_range => "bytes 42-56/100" }])
201
+ with("/tasks/232/output", nil,
202
+ nil, { "Range" => "bytes=42-" }).
203
+ and_return([206, "test", { :content_range => "bytes 42-56/100" }])
217
204
  @director.get_task_output(232, 42).should == ["test", 57]
218
205
  end
219
206
 
220
207
  it "doesn't set task output new offset if it wasn't a partial response" do
221
208
  @director.should_receive(:get).
222
- with("/tasks/232/output", nil, nil,
223
- { "Range" => "bytes=42-" }).
224
- and_return([200, "test"])
209
+ with("/tasks/232/output", nil, nil,
210
+ { "Range" => "bytes=42-" }).
211
+ and_return([200, "test"])
225
212
  @director.get_task_output(232, 42).should == ["test", nil]
226
213
  end
227
214
 
@@ -231,8 +218,8 @@ describe Bosh::Cli::Director do
231
218
  Time.stub!(:now).and_return(now)
232
219
 
233
220
  @director.should_receive(:get).with("/info").
234
- and_return([200, JSON.generate("version" => 1),
235
- { :date => server_time.rfc822 }])
221
+ and_return([200, JSON.generate("version" => 1),
222
+ { :date => server_time.rfc822 }])
236
223
  @director.get_time_difference.to_i.should == 100
237
224
  end
238
225
 
@@ -241,59 +228,65 @@ describe Bosh::Cli::Director do
241
228
  describe "checking status" do
242
229
  it "considers target valid if it responds with 401 (for compatibility)" do
243
230
  @director.stub(:get).
244
- with("/info", "application/json").
245
- and_return([401, "Not authorized"])
231
+ with("/info", "application/json").
232
+ and_return([401, "Not authorized"])
246
233
  @director.exists?.should be_true
247
234
  end
248
235
 
249
236
  it "considers target valid if it responds with 200" do
250
237
  @director.stub(:get).
251
- with("/info", "application/json").
252
- and_return([200, JSON.generate("name" => "Director is your friend")])
238
+ with("/info", "application/json").
239
+ and_return([200, JSON.generate("name" => "Director is your friend")])
253
240
  @director.exists?.should be_true
254
241
  end
255
242
  end
256
243
 
257
244
  describe "tracking request" do
258
245
  it "starts polling task if request responded with a redirect to task URL" do
246
+ options = { :arg1 => 1, :arg2 => 2 }
247
+
259
248
  @director.should_receive(:request).
260
- with(:get, "/stuff", "text/plain", "abc").
261
- and_return([302, "body", { :location => "/tasks/502" }])
262
- @director.should_receive(:poll_task).
263
- with("502", :arg1 => 1, :arg2 => 2).
264
- and_return("polling result")
249
+ with(:get, "/stuff", "text/plain", "abc").
250
+ and_return([302, "body", { :location => "/tasks/502" }])
251
+
252
+ tracker = mock("tracker", :track => "polling result", :output => "foo")
253
+
254
+ Bosh::Cli::TaskTracker.should_receive(:new).
255
+ with(@director, "502", options).
256
+ and_return(tracker)
257
+
265
258
  @director.request_and_track(:get, "/stuff", "text/plain",
266
- "abc", :arg1 => 1, :arg2 => 2).
267
- should == ["polling result", "502"]
259
+ "abc", options).
260
+ should == ["polling result", "502", "foo"]
268
261
  end
269
262
 
270
- it "considers all reponses but 302 a failure" do
263
+ it "considers all responses but 302 a failure" do
271
264
  [200, 404, 403].each do |code|
272
265
  @director.should_receive(:request).
273
- with(:get, "/stuff", "text/plain", "abc").
274
- and_return([code, "body", { }])
266
+ with(:get, "/stuff", "text/plain", "abc").
267
+ and_return([code, "body", {}])
275
268
  @director.request_and_track(:get, "/stuff", "text/plain",
276
269
  "abc", :arg1 => 1, :arg2 => 2).
277
- should == [:failed, nil]
270
+ should == [:failed, nil, nil]
278
271
  end
279
272
  end
280
273
 
281
- it "reports task as non trackable if its URL is unfamiliar" do
274
+ it "reports task as non-trackable if its URL is unfamiliar" do
282
275
  @director.should_receive(:request).
283
- with(:get, "/stuff", "text/plain", "abc").
284
- and_return([302, "body", { :location => "/track-task/502" }])
276
+ with(:get, "/stuff", "text/plain", "abc").
277
+ and_return([302, "body", { :location => "/track-task/502" }])
285
278
  @director.request_and_track(:get, "/stuff", "text/plain",
286
279
  "abc", :arg1 => 1, :arg2 => 2).
287
- should == [:non_trackable, nil]
280
+ should == [:non_trackable, nil, nil]
288
281
  end
289
282
 
290
- it "suppports uploading with progress bar" do
283
+ it "supports uploading with progress bar" do
291
284
  file = spec_asset("valid_release.tgz")
292
285
  f = Bosh::Cli::FileWithProgressBar.open(file, "r")
293
286
 
294
287
  Bosh::Cli::FileWithProgressBar.stub!(:open).with(file, "r").and_return(f)
295
288
  @director.should_receive(:request_and_track).
296
- with(:post, "/stuff", "application/x-compressed", f, { })
289
+ with(:post, "/stuff", "application/x-compressed", f, { })
297
290
  @director.upload_and_track("/stuff", "application/x-compressed", file)
298
291
  f.progress_bar.finished?.should be_true
299
292
  end
@@ -308,16 +301,16 @@ describe Bosh::Cli::Director do
308
301
 
309
302
  client = mock("httpclient")
310
303
  client.should_receive(:send_timeout=).
311
- with(Bosh::Cli::Director::API_TIMEOUT)
304
+ with(Bosh::Cli::Director::API_TIMEOUT)
312
305
  client.should_receive(:receive_timeout=).
313
- with(Bosh::Cli::Director::API_TIMEOUT)
306
+ with(Bosh::Cli::Director::API_TIMEOUT)
314
307
  client.should_receive(:connect_timeout=).
315
- with(Bosh::Cli::Director::CONNECT_TIMEOUT)
308
+ with(Bosh::Cli::Director::CONNECT_TIMEOUT)
316
309
  HTTPClient.stub!(:new).and_return(client)
317
310
 
318
311
  client.should_receive(:request).
319
- with(:get, "http://target/stuff", :body => "payload",
320
- :header => headers.merge("Authorization" => auth))
312
+ with(:get, "http://target/stuff", :body => "payload",
313
+ :header => headers.merge("Authorization" => auth))
321
314
  @director.send(:perform_http_request, :get,
322
315
  "http://target/stuff", "payload", headers)
323
316
  end
@@ -351,7 +344,7 @@ describe Bosh::Cli::Director do
351
344
  :headers => {})
352
345
 
353
346
  @director.should_receive(:perform_http_request).
354
- and_return(mock_response)
347
+ and_return(mock_response)
355
348
  @director.request(:get, "/stuff", "application/octet-stream",
356
349
  "payload", { :hdr1 => "a", :hdr2 => "b"})
357
350
  }.should raise_error(Bosh::Cli::DirectorError,
@@ -363,7 +356,7 @@ describe Bosh::Cli::Director do
363
356
  :body => "error message goes here",
364
357
  :headers => {})
365
358
  @director.should_receive(:perform_http_request).
366
- and_return(mock_response)
359
+ and_return(mock_response)
367
360
  @director.request(:get, "/stuff", "application/octet-stream",
368
361
  "payload", { :hdr1 => "a", :hdr2 => "b"})
369
362
  }.should raise_error(Bosh::Cli::DirectorError,
@@ -376,7 +369,7 @@ describe Bosh::Cli::Director do
376
369
  :body => '{"c":"d","a":"b"}',
377
370
  :headers => {})
378
371
  @director.should_receive(:perform_http_request).
379
- and_return(mock_response)
372
+ and_return(mock_response)
380
373
  @director.request(:get, "/stuff", "application/octet-stream",
381
374
  "payload", { :hdr1 => "a", :hdr2 => "b"})
382
375
  }.should raise_error(Bosh::Cli::DirectorError,
@@ -388,13 +381,15 @@ describe Bosh::Cli::Director do
388
381
  it "wraps director access exceptions" do
389
382
  [URI::Error, SocketError, Errno::ECONNREFUSED].each do |err|
390
383
  @director.should_receive(:perform_http_request).
391
- and_raise(err.new("err message"))
384
+ and_raise(err.new("err message"))
392
385
  lambda {
393
386
  @director.request(:get, "/stuff", "app/zb", "payload", { })
394
387
  }.should raise_error(Bosh::Cli::DirectorInaccessible)
395
388
  end
389
+
396
390
  @director.should_receive(:perform_http_request).
397
- and_raise(SystemCallError.new("err message"))
391
+ and_raise(SystemCallError.new("err message", 22))
392
+
398
393
  lambda {
399
394
  @director.request(:get, "/stuff", "app/zb", "payload", { })
400
395
  }.should raise_error Bosh::Cli::DirectorError
@@ -404,11 +399,12 @@ describe Bosh::Cli::Director do
404
399
  mock_response = mock("response", :code => 200,
405
400
  :body => "test body", :headers => { })
406
401
  @director.should_receive(:perform_http_request).
407
- and_yield("test body").and_return(mock_response)
402
+ and_yield("test body").and_return(mock_response)
408
403
 
409
- code, filename, headers = @director.request(:get,
410
- "/files/foo", nil, nil,
411
- { }, { :file => true })
404
+ code, filename, headers =
405
+ @director.request(:get,
406
+ "/files/foo", nil, nil,
407
+ { }, { :file => true })
412
408
 
413
409
  code.should == 200
414
410
  File.read(filename).should == "test body"
@@ -416,96 +412,4 @@ describe Bosh::Cli::Director do
416
412
  end
417
413
  end
418
414
 
419
- describe "polling jobs" do
420
- it "polls until success" do
421
- n_calls = 0
422
-
423
- @director.stub!(:get_time_difference).and_return(0)
424
- @director.should_receive(:get).
425
- with("/tasks/1").exactly(5).times.
426
- and_return {
427
- n_calls += 1;
428
- [200,
429
- JSON.generate("state" => n_calls == 5 ? "done" : "processing")
430
- ]
431
- }
432
- @director.should_receive(:get).
433
- with("/tasks/1/output",
434
- nil, nil, "Range" => "bytes=0-").
435
- exactly(5).times.and_return(nil)
436
-
437
- @director.poll_task(1, :poll_interval => 0, :max_polls => 1000).
438
- should == :done
439
- end
440
-
441
- it "respects max polls setting" do
442
- @director.stub!(:get_time_difference).and_return(0)
443
- @director.should_receive(:get).with("/tasks/1").
444
- exactly(10).times.
445
- and_return [200, JSON.generate("state" => "processing")]
446
- @director.should_receive(:get).
447
- with("/tasks/1/output",
448
- nil, nil, "Range" => "bytes=0-").
449
- exactly(10).times.and_return(nil)
450
-
451
- @director.poll_task(1, :poll_interval => 0, :max_polls => 10).
452
- should == :track_timeout
453
- end
454
-
455
- it "respects poll interval setting" do
456
- @director.stub(:get).and_return([200, "processing"])
457
-
458
- @director.should_receive(:get).with("/tasks/1").
459
- exactly(10).times.
460
- and_return([200, JSON.generate("state" => "processing")])
461
- @director.should_receive(:get).
462
- with("/tasks/1/output", nil, nil,
463
- "Range" => "bytes=0-").
464
- exactly(10).times.and_return(nil)
465
- @director.should_receive(:sleep).with(5).exactly(9).times.and_return(nil)
466
-
467
- @director.poll_task(1, :poll_interval => 5, :max_polls => 10).
468
- should == :track_timeout
469
- end
470
-
471
- it "stops polling and returns error if status is not HTTP 200" do
472
- @director.stub!(:get_time_difference).and_return(0)
473
-
474
- @director.should_receive(:get).
475
- with("/tasks/1").
476
- and_return([500, JSON.generate("state" => "processing")])
477
-
478
- lambda {
479
- @director.poll_task(1, :poll_interval => 0, :max_polls => 10)
480
- }.should raise_error(Bosh::Cli::TaskTrackError,
481
- "Got HTTP 500 while tracking task state")
482
- end
483
-
484
- it "stops polling and returns error if task state is error" do
485
- @director.stub!(:get_time_difference).and_return(0)
486
-
487
- @director.stub(:get).
488
- with("/tasks/1/output", nil, nil,
489
- "Range" => "bytes=0-").
490
- and_return([200, ""])
491
-
492
- @director.stub(:get).
493
- with("/tasks/1").
494
- and_return([200, JSON.generate("state" => "error")])
495
-
496
- @director.should_receive(:get).exactly(2).times
497
-
498
- @director.poll_task(1, :poll_interval => 0, :max_polls => 10).
499
- should == :error
500
- end
501
- end
502
-
503
- it "calls cancel_task on the current task when cancel_current is called" do
504
- task_num = 1
505
- @director.stub(:cancel_task).and_return(["body", 200])
506
- @director.should_receive(:cancel_task).once.with(task_num)
507
- @director.should_receive(:say).once.with("Cancelling task ##{task_num}.")
508
- @director.current_running_task = task_num
509
- @director.cancel_current
510
- end
511
415
  end