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