tools-cf-plugin 1.0.0
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/Rakefile +8 -0
- data/lib/tools-cf-plugin/plugin.rb +1 -0
- data/lib/tools-cf-plugin/version.rb +3 -0
- data/lib/tools-cf-plugin/watch.rb +360 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/watch_spec.rb +871 -0
- metadata +245 -0
data/spec/watch_spec.rb
ADDED
@@ -0,0 +1,871 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CFTools::Watch do
|
4
|
+
let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dir" }
|
5
|
+
|
6
|
+
stub_home_dir_with { fake_home_dir }
|
7
|
+
|
8
|
+
let(:app) { fake :app, :name => "myapp", :guid => "myappguid" }
|
9
|
+
|
10
|
+
let(:client) { fake_client :apps => [app] }
|
11
|
+
|
12
|
+
before { stub_client }
|
13
|
+
|
14
|
+
before do
|
15
|
+
stub(app).exists? { true }
|
16
|
+
end
|
17
|
+
|
18
|
+
before do
|
19
|
+
stub(NATS).start(anything) { |_, blk| blk.call }
|
20
|
+
stub(NATS).subscribe
|
21
|
+
end
|
22
|
+
|
23
|
+
it "subscribes all messages on NATS" do
|
24
|
+
mock(NATS).subscribe(">")
|
25
|
+
cf %W[watch]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "turns off output buffering" do
|
29
|
+
# this is dumb. i know. exercise for the reader. - AS
|
30
|
+
any_instance_of(StringIO) do |io|
|
31
|
+
mock(io).sync = true
|
32
|
+
end
|
33
|
+
|
34
|
+
cf %W[watch]
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when no application is given" do
|
38
|
+
around { |example| Timecop.freeze(&example) }
|
39
|
+
|
40
|
+
it "prints a timestamp, message, and raw body" do
|
41
|
+
stub(NATS).subscribe(">") do |_, block|
|
42
|
+
block.call("some-message", nil, "some.subject")
|
43
|
+
end
|
44
|
+
|
45
|
+
cf %W[watch]
|
46
|
+
|
47
|
+
expect(output).to say(/#{Time.now.strftime("%r")}\s*some.subject\s*some-message/)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when no NATS server info is specified" do
|
52
|
+
it "connects on nats:nats@localhost:4222" do
|
53
|
+
mock(NATS).start(hash_including(
|
54
|
+
:uri => "nats://nats:nats@localhost:4222"))
|
55
|
+
|
56
|
+
cf %W[watch]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when NATS server info is specified" do
|
61
|
+
it "connects to the given location using the given credentials" do
|
62
|
+
mock(NATS).start(hash_including(
|
63
|
+
:uri => "nats://someuser:somepass@example.com:4242"))
|
64
|
+
|
65
|
+
cf %W[watch -h example.com -P 4242 -u someuser -p somepass]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when a malformed message comes in" do
|
70
|
+
it "prints an error message and keeps on truckin'" do
|
71
|
+
stub(NATS).subscribe(">") do |_, block|
|
72
|
+
block.call("foo", nil, "some.subject")
|
73
|
+
end
|
74
|
+
|
75
|
+
any_instance_of described_class do |cli|
|
76
|
+
stub(cli).process_message { raise "hell" }
|
77
|
+
end
|
78
|
+
|
79
|
+
cf %W[watch]
|
80
|
+
|
81
|
+
expect(output).to say(
|
82
|
+
"couldn't deal w/ some.subject 'foo': RuntimeError: hell")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "when a message comes in with a reply channel, followed by a reply" do
|
87
|
+
it "registers it in #requests" do
|
88
|
+
stub(NATS).subscribe(">") do |_, block|
|
89
|
+
block.call("foo", "some-reply", "some.subject")
|
90
|
+
block.call("some-response", nil, "some-reply")
|
91
|
+
end
|
92
|
+
|
93
|
+
cf %W[watch]
|
94
|
+
|
95
|
+
expect(output).to say("some.subject (1)\tfoo")
|
96
|
+
expect(output).to say("`- reply to some.subject (1)\tsome-response")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when an application is given" do
|
101
|
+
context "and it cannot be found" do
|
102
|
+
it "prints a failure message" do
|
103
|
+
cf %W[watch some-bogus-app]
|
104
|
+
expect(error_output).to say("Unknown app 'some-bogus-app'")
|
105
|
+
end
|
106
|
+
|
107
|
+
it "exits with a non-zero status" do
|
108
|
+
expect(cf %W[watch some-bogus-app]).to_not eq(0)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "and a message containing the app's GUID is seen" do
|
113
|
+
around { |example| Timecop.freeze(&example) }
|
114
|
+
|
115
|
+
it "prints a timestamp, message, and raw body" do
|
116
|
+
stub(NATS).subscribe(">") do |_, block|
|
117
|
+
block.call("some-message-mentioning-#{app.guid}", nil, "some.subject")
|
118
|
+
end
|
119
|
+
|
120
|
+
cf %W[watch myapp]
|
121
|
+
|
122
|
+
expect(output).to say(/#{Time.now.strftime("%r")}\s*some.subject\s*some-message-mentioning-#{app.guid}/)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when a message NOT containing the app's GUID is seen" do
|
127
|
+
it "does not print it" do
|
128
|
+
stub(NATS).subscribe(">") do |_, block|
|
129
|
+
block.call("some-irrelevant-message", nil, "some.subject")
|
130
|
+
end
|
131
|
+
|
132
|
+
cf %W[watch myapp]
|
133
|
+
|
134
|
+
expect(output).to_not say("some.subject")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "when a message is seen with subject droplet.exited" do
|
140
|
+
let(:payload) { <<PAYLOAD }
|
141
|
+
{
|
142
|
+
"exit_description": "",
|
143
|
+
"exit_status": -1,
|
144
|
+
"reason": "STOPPED",
|
145
|
+
"index": 0,
|
146
|
+
"instance": "2e2b8ca31e87dd3a26cee0ddba01e84e",
|
147
|
+
"version": "aaca113b-3ff9-4c04-8e69-28f8dc9d8cc0",
|
148
|
+
"droplet": "#{app.guid}",
|
149
|
+
"cc_partition": "default"
|
150
|
+
}
|
151
|
+
PAYLOAD
|
152
|
+
|
153
|
+
it "pretty-prints the message body" do
|
154
|
+
stub(NATS).subscribe(">") do |_, block|
|
155
|
+
block.call(payload, nil, "droplet.exited")
|
156
|
+
end
|
157
|
+
|
158
|
+
cf %W[watch]
|
159
|
+
|
160
|
+
expect(output).to say("app: myapp, reason: STOPPED, index: 0")
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "when a message is seen with subject dea.heartbeat" do
|
165
|
+
let(:payload) { <<PAYLOAD }
|
166
|
+
{
|
167
|
+
"prod": false,
|
168
|
+
"dea": "1-4b293b726167fbc895af5a7927c0973a",
|
169
|
+
"droplets": [
|
170
|
+
{
|
171
|
+
"state_timestamp": 1369251231.3436642,
|
172
|
+
"state": "RUNNING",
|
173
|
+
"index": 0,
|
174
|
+
"instance": "some app instance",
|
175
|
+
"version": "5c0e0e10-8384-4a35-915e-872fe91ffb95",
|
176
|
+
"droplet": "#{app.guid}",
|
177
|
+
"cc_partition": "default"
|
178
|
+
},
|
179
|
+
{
|
180
|
+
"state_timestamp": 1369251231.3436642,
|
181
|
+
"state": "CRASHED",
|
182
|
+
"index": 1,
|
183
|
+
"instance": "some other app instance",
|
184
|
+
"version": "5c0e0e10-8384-4a35-915e-872fe91ffb95",
|
185
|
+
"droplet": "#{app.guid}",
|
186
|
+
"cc_partition": "default"
|
187
|
+
},
|
188
|
+
{
|
189
|
+
"state_timestamp": 1369251225.2800167,
|
190
|
+
"state": "RUNNING",
|
191
|
+
"index": 0,
|
192
|
+
"instance": "some other other app instance",
|
193
|
+
"version": "bdc3b7d7-5a55-455d-ac66-ba82a9ad43e7",
|
194
|
+
"droplet": "eaebd610-0e15-4935-9784-b676d7d8495e",
|
195
|
+
"cc_partition": "default"
|
196
|
+
}
|
197
|
+
]
|
198
|
+
}
|
199
|
+
PAYLOAD
|
200
|
+
|
201
|
+
context "and an application is given" do
|
202
|
+
it "prints only the application's entry" do
|
203
|
+
stub(NATS).subscribe(">") do |_, block|
|
204
|
+
block.call(payload, nil, "dea.heartbeat")
|
205
|
+
end
|
206
|
+
|
207
|
+
cf %W[watch myapp]
|
208
|
+
|
209
|
+
expect(output).to say("dea: 1, running: 1, crashed: 1")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context "when a message is seen with subject dea.advertise" do
|
215
|
+
context "and app is given" do
|
216
|
+
it "prints nothing" do
|
217
|
+
stub(NATS).subscribe(">") do |_, block|
|
218
|
+
block.call("whatever-#{app.guid}", nil, "dea.advertise")
|
219
|
+
end
|
220
|
+
|
221
|
+
cf %W[watch myapp]
|
222
|
+
|
223
|
+
expect(output).to_not say("dea.advertise")
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context "and app is NOT given" do
|
228
|
+
let(:other_app) { fake :app, :name => "otherapp", :guid => "otherguid" }
|
229
|
+
|
230
|
+
let(:client) { fake_client :apps => [app, other_app] }
|
231
|
+
|
232
|
+
let(:payload) { <<PAYLOAD }
|
233
|
+
{
|
234
|
+
"app_id_to_count": {
|
235
|
+
"#{app.guid}": 1,
|
236
|
+
"#{other_app.guid}": 2
|
237
|
+
},
|
238
|
+
"available_memory": 30000,
|
239
|
+
"stacks": [
|
240
|
+
"lucid64"
|
241
|
+
],
|
242
|
+
"prod": false,
|
243
|
+
"id": "1-f158dcd026d1589853846a3859faf0ea"
|
244
|
+
}
|
245
|
+
PAYLOAD
|
246
|
+
|
247
|
+
before do
|
248
|
+
stub(app).exists? { true }
|
249
|
+
stub(other_app).exists? { false }
|
250
|
+
end
|
251
|
+
|
252
|
+
it "prints the dea, its stacks, available memory, and apps" do
|
253
|
+
stub(NATS).subscribe(">") do |_, block|
|
254
|
+
block.call(payload, nil, "dea.advertise")
|
255
|
+
end
|
256
|
+
|
257
|
+
cf %W[watch]
|
258
|
+
|
259
|
+
expect(output).to say("dea: 1, stacks: lucid64, available mem: 29G, apps: 1 x myapp, 2 x unknown")
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context "when a message is seen with subject staging.advertise" do
|
265
|
+
let(:payload) { <<PAYLOAD }
|
266
|
+
{
|
267
|
+
"available_memory": 27264,
|
268
|
+
"stacks": [
|
269
|
+
"lucid64"
|
270
|
+
],
|
271
|
+
"id": "1-7b56cfd786123e56423cf700103f54a8"
|
272
|
+
}
|
273
|
+
PAYLOAD
|
274
|
+
|
275
|
+
it "prints the dea, its stacks, and its available memory" do
|
276
|
+
stub(NATS).subscribe(">") do |_, block|
|
277
|
+
block.call(payload, nil, "staging.advertise")
|
278
|
+
end
|
279
|
+
|
280
|
+
cf %W[watch]
|
281
|
+
|
282
|
+
expect(output).to say("dea: 1, stacks: lucid64, available mem: 27G")
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context "when a message is seen with subject router.start" do
|
287
|
+
let(:payload) { <<PAYLOAD }
|
288
|
+
{
|
289
|
+
"hosts": [
|
290
|
+
"10.10.16.15"
|
291
|
+
],
|
292
|
+
"id": "11bb18232f3afdc5cad2b583f8d000f5"
|
293
|
+
}
|
294
|
+
PAYLOAD
|
295
|
+
|
296
|
+
it "prints the hosts" do
|
297
|
+
stub(NATS).subscribe(">") do |_, block|
|
298
|
+
block.call(payload, nil, "router.start")
|
299
|
+
end
|
300
|
+
|
301
|
+
cf %W[watch]
|
302
|
+
|
303
|
+
expect(output).to say("hosts: 10.10.16.15")
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
context "when a message is seen with subject router.register" do
|
308
|
+
context "when there's an associated DEA" do
|
309
|
+
let(:payload) { <<PAYLOAD }
|
310
|
+
{
|
311
|
+
"private_instance_id": "e4a5ee2330c81fd7611eba7dbedbb499a00ae1b79f97f40a3603c8bff6fbcc6f",
|
312
|
+
"tags": {},
|
313
|
+
"port": 61111,
|
314
|
+
"host": "192.0.43.10",
|
315
|
+
"uris": [
|
316
|
+
"my-app.com",
|
317
|
+
"my-app-2.com"
|
318
|
+
],
|
319
|
+
"app": "#{app.guid}",
|
320
|
+
"dea": "1-4b293b726167fbc895af5a7927c0973a"
|
321
|
+
}
|
322
|
+
PAYLOAD
|
323
|
+
|
324
|
+
it "prints the uris, host, and port" do
|
325
|
+
stub(NATS).subscribe(">") do |_, block|
|
326
|
+
block.call(payload, nil, "router.register")
|
327
|
+
end
|
328
|
+
|
329
|
+
cf %W[watch]
|
330
|
+
|
331
|
+
expect(output).to say("app: myapp, dea: 1, uris: my-app.com, my-app-2.com, host: 192.0.43.10, port: 61111")
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
context "when there's NOT an associated DEA" do
|
336
|
+
let(:payload) { <<PAYLOAD }
|
337
|
+
{
|
338
|
+
"private_instance_id": "e4a5ee2330c81fd7611eba7dbedbb499a00ae1b79f97f40a3603c8bff6fbcc6f",
|
339
|
+
"tags": {},
|
340
|
+
"port": 61111,
|
341
|
+
"host": "192.0.43.10",
|
342
|
+
"uris": [
|
343
|
+
"my-app.com",
|
344
|
+
"my-app-2.com"
|
345
|
+
]
|
346
|
+
}
|
347
|
+
PAYLOAD
|
348
|
+
it "prints the uris, host, and port" do
|
349
|
+
stub(NATS).subscribe(">") do |_, block|
|
350
|
+
block.call(payload, nil, "router.register")
|
351
|
+
end
|
352
|
+
|
353
|
+
cf %W[watch]
|
354
|
+
|
355
|
+
expect(output).to say("uris: my-app.com, my-app-2.com, host: 192.0.43.10, port: 61111")
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
context "when a message is seen with subject router.unregister" do
|
361
|
+
context "when there's an associated DEA" do
|
362
|
+
let(:payload) { <<PAYLOAD }
|
363
|
+
{
|
364
|
+
"private_instance_id": "9ade4a089b26c3aa179edec08db65f47c8379ba2c4f4da625d5180ca97c3ef04",
|
365
|
+
"tags": {},
|
366
|
+
"port": 61111,
|
367
|
+
"host": "192.0.43.10",
|
368
|
+
"uris": [
|
369
|
+
"my-app.com"
|
370
|
+
],
|
371
|
+
"app": "#{app.guid}",
|
372
|
+
"dea": "5-029eb34eef489818abbc08413e4a70d9"
|
373
|
+
}
|
374
|
+
PAYLOAD
|
375
|
+
|
376
|
+
it "prints the dea, uris, host, and port" do
|
377
|
+
stub(NATS).subscribe(">") do |_, block|
|
378
|
+
block.call(payload, nil, "router.unregister")
|
379
|
+
end
|
380
|
+
|
381
|
+
cf %W[watch]
|
382
|
+
|
383
|
+
expect(output).to say("app: myapp, dea: 5, uris: my-app.com, host: 192.0.43.10, port: 61111")
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
context "when no app is specified and there's NOT an associated DEA" do
|
388
|
+
let(:payload) { <<PAYLOAD }
|
389
|
+
{
|
390
|
+
"private_instance_id": "9ade4a089b26c3aa179edec08db65f47c8379ba2c4f4da625d5180ca97c3ef04",
|
391
|
+
"tags": {},
|
392
|
+
"port": 61111,
|
393
|
+
"host": "192.0.43.10",
|
394
|
+
"uris": [
|
395
|
+
"my-app.com"
|
396
|
+
]
|
397
|
+
}
|
398
|
+
PAYLOAD
|
399
|
+
|
400
|
+
it "prints the uris, host, and port" do
|
401
|
+
stub(NATS).subscribe(">") do |_, block|
|
402
|
+
block.call(payload, nil, "router.unregister")
|
403
|
+
end
|
404
|
+
|
405
|
+
cf %W[watch]
|
406
|
+
|
407
|
+
expect(output).to say("uris: my-app.com, host: 192.0.43.10, port: 61111")
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
context "when a message is seen with subject dea.*.start" do
|
413
|
+
let(:payload) { <<PAYLOAD }
|
414
|
+
{
|
415
|
+
"index": 2,
|
416
|
+
"debug": null,
|
417
|
+
"console": true,
|
418
|
+
"env": [],
|
419
|
+
"cc_partition": "default",
|
420
|
+
"limits": {
|
421
|
+
"fds": 16384,
|
422
|
+
"disk": 1024,
|
423
|
+
"mem": 128
|
424
|
+
},
|
425
|
+
"services": [],
|
426
|
+
"droplet": "#{app.guid}",
|
427
|
+
"name": "hello-sinatra",
|
428
|
+
"uris": [
|
429
|
+
"myapp.com"
|
430
|
+
],
|
431
|
+
"prod": false,
|
432
|
+
"sha1": "9c8f36ee81b535a7d9b4efcd9d629e8cf8a2645f",
|
433
|
+
"executableFile": "deprecated",
|
434
|
+
"executableUri": "https://a1-cf-app-com-cc-droplets.s3.amazonaws.com/ac/cf/accf1078-e7e1-439a-bd32-77296390c406?AWSAccessKeyId=AKIAIMGCF7E5F6M5RV3A&Signature=1lyIotK3cZ2VUyK3H8YrlT82B8c%3D&Expires=1369259081",
|
435
|
+
"version": "ce1da6af-59b1-4fea-9e39-64c19440a671"
|
436
|
+
}
|
437
|
+
PAYLOAD
|
438
|
+
|
439
|
+
it "filters the uuid from the subject" do
|
440
|
+
stub(NATS).subscribe(">") do |_, block|
|
441
|
+
block.call(payload, nil, "dea.42-deadbeef.start")
|
442
|
+
end
|
443
|
+
|
444
|
+
cf %W[watch]
|
445
|
+
|
446
|
+
expect(output).to say("dea.42.start")
|
447
|
+
end
|
448
|
+
|
449
|
+
it "prints the uris, host, and port" do
|
450
|
+
stub(NATS).subscribe(">") do |_, block|
|
451
|
+
block.call(payload, nil, "dea.42-deadbeef.start")
|
452
|
+
end
|
453
|
+
|
454
|
+
cf %W[watch]
|
455
|
+
|
456
|
+
expect(output).to say("app: myapp, dea: 42, index: 2, uris: myapp.com")
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
context "when a message is seen with subject droplet.updated" do
|
461
|
+
let(:payload) { <<PAYLOAD }
|
462
|
+
{
|
463
|
+
"cc_partition": "default",
|
464
|
+
"droplet": "#{app.guid}"
|
465
|
+
}
|
466
|
+
|
467
|
+
PAYLOAD
|
468
|
+
|
469
|
+
it "prints a blank message" do
|
470
|
+
stub(NATS).subscribe(">") do |_, block|
|
471
|
+
block.call(payload, nil, "droplet.updated")
|
472
|
+
end
|
473
|
+
|
474
|
+
cf %W[watch]
|
475
|
+
|
476
|
+
expect(output).to say("droplet.updated\tapp: myapp")
|
477
|
+
expect(output).to_not say("cc_partition")
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
context "when a message is seen with subject dea.stop" do
|
482
|
+
context "and it's stopping particular indices" do
|
483
|
+
let(:payload) { <<PAYLOAD }
|
484
|
+
{
|
485
|
+
"indices": [
|
486
|
+
1,
|
487
|
+
2
|
488
|
+
],
|
489
|
+
"version": "ce1da6af-59b1-4fea-9e39-64c19440a671",
|
490
|
+
"droplet": "#{app.guid}"
|
491
|
+
}
|
492
|
+
PAYLOAD
|
493
|
+
|
494
|
+
it "prints that it's scaling down, and the affected indices" do
|
495
|
+
stub(NATS).subscribe(">") do |_, block|
|
496
|
+
block.call(payload, nil, "dea.stop")
|
497
|
+
end
|
498
|
+
|
499
|
+
cf %W[watch]
|
500
|
+
|
501
|
+
expect(output).to say("app: myapp, scaling down indices: 1, 2")
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
context "when it's specifying instances (i.e. from HM)" do
|
506
|
+
let(:payload) { <<PAYLOAD }
|
507
|
+
{
|
508
|
+
"instances": [
|
509
|
+
"a",
|
510
|
+
"b",
|
511
|
+
"c"
|
512
|
+
],
|
513
|
+
"droplet": "#{app.guid}"
|
514
|
+
}
|
515
|
+
PAYLOAD
|
516
|
+
it "prints that it's killing extra instances" do
|
517
|
+
stub(NATS).subscribe(">") do |_, block|
|
518
|
+
block.call(payload, nil, "dea.stop")
|
519
|
+
end
|
520
|
+
|
521
|
+
cf %W[watch]
|
522
|
+
|
523
|
+
expect(output).to say("app: myapp, killing extra instances: a, b, c")
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
context "when it's stopping the entire application" do
|
528
|
+
let(:payload) { <<PAYLOAD }
|
529
|
+
{
|
530
|
+
"droplet": "#{app.guid}"
|
531
|
+
}
|
532
|
+
PAYLOAD
|
533
|
+
|
534
|
+
it "prints that it's killing extra instances" do
|
535
|
+
stub(NATS).subscribe(">") do |_, block|
|
536
|
+
block.call(payload, nil, "dea.stop")
|
537
|
+
end
|
538
|
+
|
539
|
+
cf %W[watch]
|
540
|
+
|
541
|
+
expect(output).to say("app: myapp, stopping application")
|
542
|
+
end
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
context "when a message is seen with subject dea.update" do
|
547
|
+
let(:payload) { <<PAYLOAD }
|
548
|
+
{
|
549
|
+
"uris": [
|
550
|
+
"myapp.com",
|
551
|
+
"myotherroute.com"
|
552
|
+
],
|
553
|
+
"droplet": "#{app.guid}"
|
554
|
+
}
|
555
|
+
PAYLOAD
|
556
|
+
|
557
|
+
it "prints the index being stopped" do
|
558
|
+
stub(NATS).subscribe(">") do |_, block|
|
559
|
+
block.call(payload, nil, "dea.update")
|
560
|
+
end
|
561
|
+
|
562
|
+
cf %W[watch]
|
563
|
+
|
564
|
+
expect(output).to say("app: myapp, uris: myapp.com, myotherroute.com")
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
context "when a message is seen with subject dea.find.droplet" do
|
569
|
+
let(:payload) { <<PAYLOAD }
|
570
|
+
{
|
571
|
+
"version": "878318bf-64a0-4055-b79b-46871292ceb8",
|
572
|
+
"states": [
|
573
|
+
"STARTING",
|
574
|
+
"RUNNING"
|
575
|
+
],
|
576
|
+
"droplet": "#{app.guid}"
|
577
|
+
}
|
578
|
+
PAYLOAD
|
579
|
+
|
580
|
+
let(:response_payload) { <<PAYLOAD }
|
581
|
+
{
|
582
|
+
"console_port": 61016,
|
583
|
+
"console_ip": "10.10.17.1",
|
584
|
+
"staged": "/7cc4f4fe64c7a0fbfaacf71e9e222a35",
|
585
|
+
"credentials": [
|
586
|
+
"8a3890704d0d08e7bc291a0d11801c4e",
|
587
|
+
"ba7e9e6d09170c4d3e794033fa76be97"
|
588
|
+
],
|
589
|
+
"dea": "1-c0d2928b36c524153cdc8cfb51d80f75",
|
590
|
+
"droplet": "#{app.guid}",
|
591
|
+
"version": "c75b3e45-0cf4-403d-a54d-1c0970dca50d",
|
592
|
+
"instance": "7cc4f4fe64c7a0fbfaacf71e9e222a35",
|
593
|
+
"index": 0,
|
594
|
+
"state": "RUNNING",
|
595
|
+
"state_timestamp": 1369262704.3337305,
|
596
|
+
"file_uri": "http://10.10.17.1:12345/instances"
|
597
|
+
}
|
598
|
+
PAYLOAD
|
599
|
+
|
600
|
+
it "prints the states being queried" do
|
601
|
+
stub(NATS).subscribe(">") do |_, block|
|
602
|
+
block.call(payload, nil, "dea.find.droplet")
|
603
|
+
end
|
604
|
+
|
605
|
+
cf %W[watch]
|
606
|
+
|
607
|
+
expect(output).to say("app: myapp, querying states: starting, running")
|
608
|
+
end
|
609
|
+
|
610
|
+
context "and we see the response" do
|
611
|
+
it "pretty-prints the response" do
|
612
|
+
stub(NATS).subscribe(">") do |_, block|
|
613
|
+
block.call(payload, "some-inbox", "dea.find.droplet")
|
614
|
+
block.call(response_payload, nil, "some-inbox")
|
615
|
+
end
|
616
|
+
|
617
|
+
cf %W[watch]
|
618
|
+
|
619
|
+
expect(output).to say("reply to dea.find.droplet (1)\tdea: 1, index: 0, state: running, since: 2013-05-22 15:45:04 -0700")
|
620
|
+
end
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
context "when a message is seen with subject healthmanager.status" do
|
625
|
+
let(:payload) { <<PAYLOAD }
|
626
|
+
{
|
627
|
+
"version": "50512eed-674e-4991-9ada-a583633c0cd4",
|
628
|
+
"state": "FLAPPING",
|
629
|
+
"droplet": "#{app.guid}"
|
630
|
+
}
|
631
|
+
PAYLOAD
|
632
|
+
|
633
|
+
let(:response_payload) { <<PAYLOAD }
|
634
|
+
{
|
635
|
+
"indices": [
|
636
|
+
1,
|
637
|
+
2
|
638
|
+
]
|
639
|
+
}
|
640
|
+
PAYLOAD
|
641
|
+
|
642
|
+
it "prints the states being queried" do
|
643
|
+
stub(NATS).subscribe(">") do |_, block|
|
644
|
+
block.call(payload, nil, "healthmanager.status")
|
645
|
+
end
|
646
|
+
|
647
|
+
cf %W[watch]
|
648
|
+
|
649
|
+
expect(output).to say("app: myapp, querying states: flapping")
|
650
|
+
end
|
651
|
+
|
652
|
+
context "and we see the response" do
|
653
|
+
it "pretty-prints the response" do
|
654
|
+
stub(NATS).subscribe(">") do |_, block|
|
655
|
+
block.call(payload, "some-inbox", "healthmanager.status")
|
656
|
+
block.call(response_payload, nil, "some-inbox")
|
657
|
+
end
|
658
|
+
|
659
|
+
cf %W[watch]
|
660
|
+
|
661
|
+
expect(output).to say("reply to healthmanager.status (1)\tindices: 1, 2")
|
662
|
+
end
|
663
|
+
end
|
664
|
+
end
|
665
|
+
|
666
|
+
context "when a message is seen with subject healthmanager.health" do
|
667
|
+
let(:other_app) { fake :app, :name => "otherapp" }
|
668
|
+
let(:client) { fake_client :apps => [app, other_app] }
|
669
|
+
|
670
|
+
let(:payload) { <<PAYLOAD }
|
671
|
+
{
|
672
|
+
"droplets": [
|
673
|
+
{
|
674
|
+
"version": "some-version",
|
675
|
+
"droplet": "#{app.guid}"
|
676
|
+
},
|
677
|
+
{
|
678
|
+
"version": "some-other-version",
|
679
|
+
"droplet": "#{other_app.guid}"
|
680
|
+
}
|
681
|
+
]
|
682
|
+
}
|
683
|
+
PAYLOAD
|
684
|
+
|
685
|
+
let(:response_payload) { <<PAYLOAD }
|
686
|
+
{
|
687
|
+
"healthy": 2,
|
688
|
+
"version": "some-version",
|
689
|
+
"droplet": "#{app.guid}"
|
690
|
+
}
|
691
|
+
PAYLOAD
|
692
|
+
|
693
|
+
let(:other_response_payload) { <<PAYLOAD }
|
694
|
+
{
|
695
|
+
"healthy": 3,
|
696
|
+
"version": "some-version",
|
697
|
+
"droplet": "#{other_app.guid}"
|
698
|
+
}
|
699
|
+
PAYLOAD
|
700
|
+
|
701
|
+
before { stub(other_app).exists? { true } }
|
702
|
+
|
703
|
+
it "prints the apps whose health being queried" do
|
704
|
+
stub(NATS).subscribe(">") do |_, block|
|
705
|
+
block.call(payload, nil, "healthmanager.health")
|
706
|
+
end
|
707
|
+
|
708
|
+
cf %W[watch]
|
709
|
+
|
710
|
+
expect(output).to say("querying health for: myapp, otherapp")
|
711
|
+
end
|
712
|
+
|
713
|
+
context "and we see the response" do
|
714
|
+
it "pretty-prints the response" do
|
715
|
+
stub(NATS).subscribe(">") do |_, block|
|
716
|
+
block.call(payload, "some-inbox", "healthmanager.health")
|
717
|
+
block.call(response_payload, nil, "some-inbox")
|
718
|
+
block.call(other_response_payload, nil, "some-inbox")
|
719
|
+
end
|
720
|
+
|
721
|
+
cf %W[watch]
|
722
|
+
|
723
|
+
expect(output).to say("reply to healthmanager.health (1)\tapp: myapp, healthy: 2")
|
724
|
+
expect(output).to say("reply to healthmanager.health (1)\tapp: otherapp, healthy: 3")
|
725
|
+
end
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
context "when a message is seen with subject dea.shutdown" do
|
730
|
+
let(:other_app) { fake :app, :name => "otherapp" }
|
731
|
+
let(:client) { fake_client :apps => [app, other_app] }
|
732
|
+
|
733
|
+
let(:payload) { <<PAYLOAD }
|
734
|
+
{
|
735
|
+
"app_id_to_count": {
|
736
|
+
"#{app.guid}": 1,
|
737
|
+
"#{other_app.guid}": 2
|
738
|
+
},
|
739
|
+
"version": "0.0.1",
|
740
|
+
"ip": "1.2.3.4",
|
741
|
+
"id": "0-deadbeef"
|
742
|
+
}
|
743
|
+
PAYLOAD
|
744
|
+
|
745
|
+
context "and the apps still exist" do
|
746
|
+
before do
|
747
|
+
stub(app).exists? { true }
|
748
|
+
stub(other_app).exists? { true }
|
749
|
+
end
|
750
|
+
|
751
|
+
it "prints the DEA and affected applications" do
|
752
|
+
stub(NATS).subscribe(">") do |_, block|
|
753
|
+
block.call(payload, nil, "dea.shutdown")
|
754
|
+
end
|
755
|
+
|
756
|
+
cf %W[watch]
|
757
|
+
|
758
|
+
expect(output).to say(
|
759
|
+
"dea: 0, apps: 1 x myapp (#{app.guid}), 2 x otherapp (#{other_app.guid})")
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
context "and an app no longer exists" do
|
764
|
+
before do
|
765
|
+
stub(app).exists? { true }
|
766
|
+
stub(other_app).exists? { false }
|
767
|
+
end
|
768
|
+
|
769
|
+
it "prints the DEA and affected applications" do
|
770
|
+
stub(NATS).subscribe(">") do |_, block|
|
771
|
+
block.call(payload, nil, "dea.shutdown")
|
772
|
+
end
|
773
|
+
|
774
|
+
cf %W[watch]
|
775
|
+
|
776
|
+
expect(output).to say(
|
777
|
+
"dea: 0, apps: 1 x myapp (#{app.guid}), 2 x unknown (#{other_app.guid})")
|
778
|
+
end
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
context "when a message is seen with subject cloudcontrollers.hm.requests.*" do
|
783
|
+
let(:last_updated) { Time.now }
|
784
|
+
|
785
|
+
context "and it is a STOP operation" do
|
786
|
+
let(:payload) { <<PAYLOAD }
|
787
|
+
{
|
788
|
+
"instances": [
|
789
|
+
"some-instance",
|
790
|
+
"some-other-instance"
|
791
|
+
],
|
792
|
+
"last_updated": #{last_updated.to_i},
|
793
|
+
"op": "STOP",
|
794
|
+
"droplet": "#{app.guid}"
|
795
|
+
}
|
796
|
+
PAYLOAD
|
797
|
+
|
798
|
+
it "trims the subject" do
|
799
|
+
stub(NATS).subscribe(">") do |_, block|
|
800
|
+
block.call(payload, nil, "cloudcontrollers.hm.requests.default")
|
801
|
+
end
|
802
|
+
|
803
|
+
cf %W[watch]
|
804
|
+
|
805
|
+
expect(output).to say("\thm.request\t")
|
806
|
+
end
|
807
|
+
|
808
|
+
it "prints the operation, last updated timestamp, and instances" do
|
809
|
+
stub(NATS).subscribe(">") do |_, block|
|
810
|
+
block.call(payload, nil, "cloudcontrollers.hm.requests.default")
|
811
|
+
end
|
812
|
+
|
813
|
+
cf %W[watch]
|
814
|
+
|
815
|
+
expect(output).to say(
|
816
|
+
"app: myapp, operation: stop, app last updated: #{last_updated}, instances: some-instance, some-other-instance")
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
context "when a message is seen with subject *.announce" do
|
821
|
+
let(:payload) { <<PAYLOAD }
|
822
|
+
{
|
823
|
+
"supported_versions": [
|
824
|
+
"n/a"
|
825
|
+
],
|
826
|
+
"plan": "1dolla",
|
827
|
+
"id": "quarters_as_a_service",
|
828
|
+
"capacity_unit": 1,
|
829
|
+
"max_capacity": 100,
|
830
|
+
"available_capacity": 100
|
831
|
+
}
|
832
|
+
PAYLOAD
|
833
|
+
|
834
|
+
it "prints the dea, its stacks, and its available memory" do
|
835
|
+
stub(NATS).subscribe(">") do |_, block|
|
836
|
+
block.call(payload, nil, "QaaS.announce")
|
837
|
+
end
|
838
|
+
|
839
|
+
cf %W[watch]
|
840
|
+
|
841
|
+
expect(output).to say("id: quarters_as_a_service, plan: 1dolla, supported versions: n/a, capacity: (available: 100, max: 100, unit: 1)")
|
842
|
+
end
|
843
|
+
end
|
844
|
+
|
845
|
+
context "and it is a START operation" do
|
846
|
+
let(:payload) { <<PAYLOAD }
|
847
|
+
{
|
848
|
+
"indices": [
|
849
|
+
1,
|
850
|
+
3
|
851
|
+
],
|
852
|
+
"version": "some-version",
|
853
|
+
"last_updated": #{last_updated.to_i},
|
854
|
+
"op": "START",
|
855
|
+
"droplet": "#{app.guid}"
|
856
|
+
}
|
857
|
+
PAYLOAD
|
858
|
+
|
859
|
+
it "prints the operation, last updated timestamp, and instances" do
|
860
|
+
stub(NATS).subscribe(">") do |_, block|
|
861
|
+
block.call(payload, nil, "cloudcontrollers.hm.requests.default")
|
862
|
+
end
|
863
|
+
|
864
|
+
cf %W[watch]
|
865
|
+
|
866
|
+
expect(output).to say(
|
867
|
+
"app: myapp, operation: start, app last updated: #{last_updated}, indices: 1, 3")
|
868
|
+
end
|
869
|
+
end
|
870
|
+
end
|
871
|
+
end
|