/
tail-cf-events
executable file
·90 lines (74 loc) · 2.77 KB
/
tail-cf-events
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env ngs
# {
# "StackId": "arn:aws:cloudformation:eu-west-1:XXX:stack/sher-test-blah-static-ip/YYY",
# "EventId": "EcsSubnet1-025b44a1-1b75-47db-a0c9-1428f3f716fc",
# "StackName": "sher-test-blah-static-ip",
# "LogicalResourceId": "EcsSubnet1",
# "PhysicalResourceId": "subnet-0d9c800d3f5a6b162",
# "ResourceType": "AWS::EC2::Subnet",
# "Timestamp": "2020-02-07T11:03:06.420Z",
# "ResourceStatus": "DELETE_IN_PROGRESS"
# }
F normalize_time_str(s:Str) s.split('.')[0] + '.000Z'
F normalize_time_str(s:Str) {
# Special case - no dot: 2020-02-11T10:55:19Z
# Thanks AWS for consistency once again!
guard '.' not in s
s[0..-1] + '.000Z'
}
F epoch_time(s:Str) normalize_time_str(s).Time().Int() # NGS doesn't handle fractions of seconds yet
F get_events(stack_name:Str, interval:Int) {
events = null
while not(events) {
try {
events = ``aws cloudformation describe-stack-events --stack-name $stack_name 2>${true}``.reverse()
} catch(e:ProcessFail) {
guard e.process.stderr ~ /Stack.*does not exist$/i # We don't know how to handle any other error
error("Stack does not exist. Retrying in ${interval} seconds.")
$(sleep $interval)
}
}
events
}
F main(stack_name:Str, interval:Int=2, start_limit:Int=20, empty_line_seconds:Int=60) {
last_seen_event = null
last_update_start_event = null
while true {
events = get_events(stack_name, interval)
start = if last_seen_event in events {
# TODO: Fix "index() is returning null" when new stack is created but last_seen_event is from previous
events.index(X==last_seen_event) + 1
} else {
max(len(events) - start_limit, 0)
}
new_events = events[start..null]
for e in new_events {
if last_seen_event {
if e.Timestamp.epoch_time() - last_seen_event.Timestamp.epoch_time() > empty_line_seconds {
echo("")
}
}
reason = ' ' +? e.get('ResourceStatusReason', '')
echo("${e.Timestamp} ${e.ResourceType.Str(50)} ${e.LogicalResourceId.Str(30)} ${e.ResourceStatus}${reason}")
last_seen_event = e
if e.ResourceType == 'AWS::CloudFormation::Stack' {
if e.ResourceStatus in %[CREATE_IN_PROGRESS UPDATE_IN_PROGRESS] {
last_update_start_event = e
}
# TODO: only print outputs for last UPDATE_COMPLETE
if e.ResourceStatus.ends_with('_COMPLETE') {
outputs = ``aws cloudformation describe-stacks --stack-name $stack_name``.the_one().get('Outputs', []).sort('OutputKey')
echo("")
for o in outputs {
echo("[output] ${o.OutputKey} ${o.OutputValue} - ${o.get('Description', '(no description)')}")
}
if last_update_start_event {
echo("[time] Last create/update/delete took ${e.Timestamp.epoch_time() - last_update_start_event.Timestamp.epoch_time()} seconds")
}
echo("")
}
}
}
$(sleep $interval)
}
}