Class: CfnStatus
- Inherits:
-
Object
show all
- Includes:
- AwsService
- Defined in:
- lib/cfn_status.rb,
lib/cfn_status/version.rb,
lib/cfn_status/aws_service.rb,
lib/cfn_status/rollback_stack.rb more...
Defined Under Namespace
Modules: AwsService
Classes: Error, RollbackStack
Constant Summary
collapse
- VERSION =
"0.6.1"
Instance Attribute Summary collapse
Instance Method Summary
collapse
Methods included from AwsService
#aws_options, #cfn, #find_stack, #rollback_complete?, #stack_exists?
Constructor Details
#initialize(stack_name, options = {}) ⇒ CfnStatus
Returns a new instance of CfnStatus.
[View source]
11
12
13
14
15
16
17
18
|
# File 'lib/cfn_status.rb', line 11
def initialize(stack_name, options = {})
@stack_name = stack_name
@options = options
@cfn = options[:cfn] resp = cfn.describe_stacks(stack_name: @stack_name)
@stack = resp.stacks.first
reset
end
|
Instance Attribute Details
#events ⇒ Object
Returns the value of attribute events.
10
11
12
|
# File 'lib/cfn_status.rb', line 10
def events
@events
end
|
#stack ⇒ Object
Returns the value of attribute stack.
10
11
12
|
# File 'lib/cfn_status.rb', line 10
def stack
@stack
end
|
Instance Method Details
#add_events_pages(resp, index_method) ⇒ Object
Examples:
add_events_pages(:start_index)
add_events_pages(:last_shown_index)
if index_method is start_index
loops add_events_pagess through describe_stack_events until "User Initiated" is found
if index_method is last_shown_index
loops add_events_pagess through describe_stack_events until last_shown_index is found
[View source]
186
187
188
189
190
191
192
193
|
# File 'lib/cfn_status.rb', line 186
def add_events_pages(resp, index_method)
found = !!send(index_method)
until found
resp = cfn.describe_stack_events(stack_name: @stack_name, next_token: resp["next_token"])
@events += resp["stack_events"]
found = !!send(index_method)
end
end
|
#completed? ⇒ Boolean
[View source]
99
100
101
102
103
|
# File 'lib/cfn_status.rb', line 99
def completed?
last_event_status =~ /(_COMPLETE|_FAILED)$/ &&
@events.dig(0, "logical_resource_id") == @stack_name &&
@events.dig(0, "resource_type") == "AWS::CloudFormation::Stack"
end
|
#event_time(timestamp) ⇒ Object
[View source]
147
148
149
|
# File 'lib/cfn_status.rb', line 147
def event_time(timestamp)
Time.parse(timestamp.to_s).localtime.strftime("%I:%M:%S%p")
end
|
#find_update_failed_event ⇒ Object
[View source]
231
232
233
234
235
236
237
238
239
240
|
# File 'lib/cfn_status.rb', line 231
def find_update_failed_event
i = @events.find_index do |event|
event["resource_type"] == "AWS::CloudFormation::Stack" &&
event["resource_status_reason"] == "User Initiated"
end
@events[0..i].reverse.find do |e|
e["resource_status"] == "UPDATE_FAILED"
end
end
|
#in_progress? ⇒ Boolean
[View source]
41
42
43
44
|
# File 'lib/cfn_status.rb', line 41
def in_progress?
in_progress = stack.stack_status =~ /_IN_PROGRESS$/
!!in_progress
end
|
#last_event_status ⇒ Object
[View source]
105
106
107
|
# File 'lib/cfn_status.rb', line 105
def last_event_status
@events.dig(0, "resource_status")
end
|
#last_shown_index ⇒ Object
[View source]
208
209
210
211
212
|
# File 'lib/cfn_status.rb', line 208
def last_shown_index
@events.find_index do |event|
event["event_id"] == @last_shown_event_id
end
end
|
#messages_map ⇒ Object
[View source]
258
259
260
261
262
263
264
|
# File 'lib/cfn_status.rb', line 258
def messages_map
{
/CloudFormation cannot update a stack when a custom-named resource requires replacing/ => "A workaround is to run ufo again with STATIC_NAME=0 and to switch to dynamic names for resources. Then run ufo again with STATIC_NAME=1 to get back to statically name resources. Note, there are caveats with the workaround.",
/cannot be associated with more than one load balancer/ => "There's was an issue updating the stack. Target groups can only be associated with one load balancer at a time. The workaround for this is to use UFO_FORCE_TARGET_GROUP=1 and run the command again. This will force the recreation of the target group resource.",
/SetSubnets is not supported for load balancers of type/ => "Changing subnets for Network Load Balancers is currently not supported. You can try workarouding this with UFO_FORCE_ELB=1 and run the command again. This will force the recreation of the elb resource."
}
end
|
#pretty_time(total_seconds) ⇒ Object
[View source]
267
268
269
270
271
272
273
274
275
|
# File 'lib/cfn_status.rb', line 267
def pretty_time(total_seconds)
minutes = (total_seconds / 60) % 60
seconds = total_seconds % 60
if total_seconds < 60
"#{seconds.to_i}s"
else
"#{minutes.to_i}m #{seconds.to_i}s"
end
end
|
#print_event(e) ⇒ Object
[View source]
134
135
136
137
138
139
140
141
142
143
144
|
# File 'lib/cfn_status.rb', line 134
def print_event(e)
message = [
event_time(e["timestamp"]),
e["resource_status"],
e["resource_type"],
e["logical_resource_id"],
e["resource_status_reason"]
].join(" ")
message = message.color(:red) if /_FAILED/.match?(e["resource_status"])
puts message
end
|
#print_events(i) ⇒ Object
[View source]
125
126
127
128
129
130
131
132
|
# File 'lib/cfn_status.rb', line 125
def print_events(i)
@events[0..i].reverse_each do |e|
print_event(e)
end
@last_shown_event_id = @events.dig(0, "event_id")
end
|
#refresh_events ⇒ Object
Refreshes the @events in memory.
[View source]
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
# File 'lib/cfn_status.rb', line 153
def refresh_events
resp = cfn.describe_stack_events(stack_name: @stack_name)
@events = resp["stack_events"]
if @last_shown_event_id
add_events_pages(resp, :last_shown_index)
else
add_events_pages(resp, :start_index)
end
rescue Aws::CloudFormation::Errors::ValidationError => e
if /Stack .* does not exis/.match?(e.message)
@stack_deletion_completed = true
else
raise
end
end
|
#reset ⇒ Object
[View source]
46
47
48
49
50
|
# File 'lib/cfn_status.rb', line 46
def reset
@events = [] @last_shown_event_id = nil
@stack_deletion_completed = nil
end
|
#rollback_error_message ⇒ Object
[View source]
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
# File 'lib/cfn_status.rb', line 242
def rollback_error_message
return unless update_rollback?
event = find_update_failed_event
return unless event
reason = event["resource_status_reason"]
messages_map.each do |pattern, message|
if reason&.match?(pattern)
return message
end
end
reason end
|
#run ⇒ Object
[View source]
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
# File 'lib/cfn_status.rb', line 20
def run
unless stack_exists?(@stack_name)
puts "The stack #{@stack_name.color(:green)} does not exist."
return true
end
puts "Stack #{@stack_name.color(:green)} Status: #{stack.stack_status.color(:green)}"
if in_progress?
puts "Stack events (tailing):"
@hide_time_took = true
wait
else
puts "Stack events:"
refresh_events
show_events(final: true)
end
success?
end
|
#show_events(final: false, quiet: false) ⇒ Object
[View source]
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
# File 'lib/cfn_status.rb', line 110
def show_events(final: false, quiet: false)
if @last_shown_event_id.nil?
i = start_index
print_events(i) unless quiet
else
i = last_shown_index
print_events(i - 1) unless i == 0 || quiet
end
return if final
sleep 5 unless ENV["TEST"]
refresh_events
end
|
#show_took(start_time) ⇒ Object
[View source]
91
92
93
94
95
96
97
|
# File 'lib/cfn_status.rb', line 91
def show_took(start_time)
return if @hide_time_took
return unless @options[:show_took]
took = Time.now - start_time
puts "Time took: #{pretty_time(took).color(:green)}"
end
|
#start_index ⇒ Object
Should always find a “User Initiated” stack event when @last_shown_index is not set
[View source]
196
197
198
199
200
201
202
203
204
205
206
|
# File 'lib/cfn_status.rb', line 196
def start_index
start_index_before_delete = @options[:start_index_before_delete]
@events.find_index do |event|
skip = start_index_before_delete && event["resource_status"] == "DELETE_IN_PROGRESS"
event["resource_type"] == "AWS::CloudFormation::Stack" &&
event["resource_status_reason"] == "User Initiated" &&
!skip
end
end
|
#success? ⇒ Boolean
[View source]
214
215
216
217
218
219
220
221
222
223
224
225
|
# File 'lib/cfn_status.rb', line 214
def success?
resource_status = @events.dig(0, "resource_status")
if resource_status.nil? resp = cfn.describe_stacks(stack_name: @stack_name)
status = resp.stacks.first.stack_status
else
status = resource_status
end
successes = %w[CREATE_COMPLETE UPDATE_COMPLETE]
successes.include?(status)
end
|
#update_rollback? ⇒ Boolean
[View source]
227
228
229
|
# File 'lib/cfn_status.rb', line 227
def update_rollback?
@events.dig(0, "resource_status") == "UPDATE_ROLLBACK_COMPLETE"
end
|
#wait(quiet: false) ⇒ Object
check for /(_COMPLETE|_FAILED)$/ status
[View source]
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
|
# File 'lib/cfn_status.rb', line 53
def wait(quiet: false)
return success? unless in_progress?
puts "Waiting for stack to complete" unless quiet
start_time = Time.now
refresh_events
until completed? || @stack_deletion_completed
show_events(final: false, quiet: quiet)
end
show_events(final: true, quiet: quiet)
if @stack_deletion_completed
puts "Stack #{@stack_name} deleted." unless quiet
show_took(start_time)
return true
end
if /_FAILED/.match?(last_event_status)
puts "Stack failed: #{last_event_status}".color(:red)
puts "Stack reason #{@events.dig(0, "resource_status_reason")}".color(:red)
elsif /_ROLLBACK_/.match?(last_event_status)
puts "Stack rolled back: #{last_event_status}".color(:red)
else puts "Stack success status: #{last_event_status}" unless quiet
end
show_took(start_time)
success?
end
|