Class: Convection::Control::Stack
- Inherits:
-
Object
- Object
- Convection::Control::Stack
- Defined in:
- lib/convection/control/stack.rb
Overview
The Stack class provides a state wrapper for CloudFormation Stacks. It tracks the state of the managed stack, and creates/updates accordingly. Stack is also region-aware, and can be used within a template to define resources that depend upon availability-zones or other region-specific neuances that cannot be represented as maps or require iteration.
Constant Summary collapse
- CREATE_COMPLETE =
Represents a stack that has successfully been converged.
'CREATE_COMPLETE'.freeze
- CREATE_FAILED =
Represents a stack that has not successfully been converged.
'CREATE_FAILED'.freeze
- CREATE_IN_PROGRESS =
Represents a stack that is currently being converged for the first time.
'CREATE_IN_PROGRESS'.freeze
- DELETE_COMPLETE =
Represents a stack that has successfully been deleted.
'DELETE_COMPLETE'.freeze
- DELETE_FAILED =
Represents a stack that has not successfully been deleted.
'DELETE_FAILED'.freeze
- DELETE_IN_PROGRESS =
Represents a stack that is currently being deleted.
'DELETE_IN_PROGRESS'.freeze
- ROLLBACK_COMPLETE =
Represents a stack that has successfully been rolled back.
'ROLLBACK_COMPLETE'.freeze
- ROLLBACK_FAILED =
Represents a stack that has not successfully been rolled back.
'ROLLBACK_FAILED'.freeze
- ROLLBACK_IN_PROGRESS =
Represents a stack that is currently being rolled back.
'ROLLBACK_IN_PROGRESS'.freeze
- UPDATE_COMPLETE =
Represents a stack that has successfully been updated (re-converged).
'UPDATE_COMPLETE'.freeze
- UPDATE_COMPLETE_CLEANUP_IN_PROGRESS =
Represents a stack that is currently performing post-update cleanup.
'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS'.freeze
- UPDATE_FAILED =
Represents a stack that has not successfully been updated.
'UPDATE_FAILED'.freeze
- UPDATE_IN_PROGRESS =
Represents a stack that is currently being updated (re-converged).
'UPDATE_IN_PROGRESS'.freeze
- UPDATE_ROLLBACK_COMPLETE =
Represents a stack that has successfully rolled back an update (re-converge).
'UPDATE_ROLLBACK_COMPLETE'.freeze
- UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS =
Represents a stack that is currently performing post-update-rollback cleanup.
'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS'.freeze
- UPDATE_ROLLBACK_FAILED =
Represents a stack that has successfully been rolled back after an update.
'UPDATE_ROLLBACK_FAILED'.freeze
- UPDATE_ROLLBACK_IN_PROGRESS =
Represents a stack that is currently performing a update rollback.
'UPDATE_ROLLBACK_IN_PROGRESS'.freeze
- NOT_CREATED =
Represents a stack that has not been created. The default state for a convection stack before getting its status.
'NOT_CREATED'.freeze
- TASK_COMPLETE =
Represents a stack task being completed.
'TASK_COMPLETE'.freeze
- TASK_FAILED =
Represents a stack task having failed.
'TASK_FAILED'.freeze
- TASK_IN_PROGRESS =
Represents a stack task that is currently in progress.
'TASK_IN_PROGRESS'.freeze
Instance Attribute Summary collapse
-
#attribute_mapping_values ⇒ Object
readonly
Returns the value of attribute attribute_mapping_values.
-
#attributes ⇒ Object
readonly
Returns the value of attribute attributes.
-
#capabilities ⇒ Object
readonly
Returns the value of attribute capabilities.
-
#cloud ⇒ Object
Returns the value of attribute cloud.
-
#credentials ⇒ Object
Returns the value of attribute credentials.
-
#current_template ⇒ Object
readonly
Returns the value of attribute current_template.
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
-
#exclude_availability_zones ⇒ Object
Returns the value of attribute exclude_availability_zones.
-
#exist ⇒ Boolean
(also: #exist?)
readonly
Whether the stack exists and has a status other than DELETED.
-
#id ⇒ Object
readonly
Returns the value of attribute id.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#on_failure ⇒ Object
Returns the value of attribute on_failure.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#outputs ⇒ Object
readonly
Returns the value of attribute outputs.
-
#parameters ⇒ Object
readonly
Returns the value of attribute parameters.
-
#region ⇒ Object
AWS-SDK.
-
#resources ⇒ Object
readonly
Returns the value of attribute resources.
-
#status ⇒ String
readonly
CloudFormation Stack status.
-
#tags ⇒ Object
readonly
Returns the value of attribute tags.
-
#tasks ⇒ Object
readonly
Returns the value of attribute tasks.
-
#template ⇒ Object
Returns the value of attribute template.
Attribute accessors collapse
- #[](key) ⇒ Object
- #[]=(key, value) ⇒ Object
- #fetch(*args) ⇒ Object
- #get(*args) ⇒ Object
-
#include?(stack, key = nil) ⇒ Boolean
Whether the stack includes the specified key.
Stack state methods collapse
-
#complete? ⇒ Boolean
Whether the CloudFormation Stack is in one of the several *_COMPLETE states.
-
#credential_error? ⇒ Boolean
Whether a credential error occurred is the reason accessing the CloudFormation stack failed.
-
#delete_complete? ⇒ Boolean
Whether the CloudFormation Stack is in one of the several *_COMPLETE states.
-
#delete_success? ⇒ Boolean
Whether the Stack state is now #delete_complete? (with no errors present).
-
#error? ⇒ Boolean
Whether any errors occurred modifying the stack.
-
#fail? ⇒ Boolean
Whether the CloudFormation Stack is in one of the several *_FAILED states.
-
#in_progress? ⇒ Boolean
Whether or not the CloudFormation Stack is in one of several *IN_PROGRESS states.
-
#success? ⇒ Boolean
Whether the Stack state is now #complete? (with no errors present).
Render/diff methods collapse
-
#diff ⇒ Hash
A set of differences between the current template (in CloudFormation) and the state of the rendered template (what would be converged).
- #render ⇒ Object
-
#resource_changes? ⇒ Boolean
Whether the Resources section of the rendered template has any changes compared to the current template (in CloudFormation).
-
#resource_dependent_changes? ⇒ Boolean
Whether the any template sections dependent on the Resources section of the rendered template has any changes compared to the current template (in CloudFormation).
-
#to_json(pretty = false) ⇒ Object
The renedered CloudFormation Template JSON.
Controllers collapse
-
#apply(&block) ⇒ Object
Render the CloudFormation template and apply the Stack using the CloudFormation client.
-
#delete(&block) ⇒ Object
Delete the CloudFormation Stack using the CloudFormation client.
Task methods collapse
-
#after_create_task(task) ⇒ Object
Register a given task to run after creation of a stack.
-
#after_delete_task(task) ⇒ Object
Register a given task to run after deletion of a stack.
-
#after_update_task(task) ⇒ Object
Register a given task to run after an update of a stack.
-
#before_create_task(task) ⇒ Object
Register a given task to run before creation of a stack.
-
#before_delete_task(task) ⇒ Object
Register a given task to run before deletion of a stack.
-
#before_update_task(task) ⇒ Object
Register a given task to run before an update of a stack.
Instance Method Summary collapse
-
#availability_zones(&block) ⇒ Array<String>
The list of availability zones found by the call to ec2 client’s describe availability zones.
-
#cloud_name ⇒ Object
rubocop:enable Metrics/LineLength.
-
#initialize(name, template, options = {}, &block) ⇒ Stack
constructor
rubocop:disable Metrics/LineLength.
- #load_template_info ⇒ Object
- #template_status ⇒ Object
-
#validate ⇒ Object
Validates a rendered template against the CloudFormation API.
-
#watch(poll = 2, &block) ⇒ Object
Loops through current events until the CloudFormation Stack has finished being modified.
Constructor Details
#initialize(name, template, options = {}, &block) ⇒ Stack
rubocop:disable Metrics/LineLength
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/convection/control/stack.rb', line 104 def initialize(name, template, = {}, &block) @name = name @template = template.clone(self) @errors = [] @cloud = .delete(:cloud) @cloud_name = .delete(:cloud_name) @region = .delete(:region) { |_| 'us-east-1' } @exclude_availability_zones = .delete(:exclude_availability_zones) { |_| [] } # Default empty Array @credentials = .delete(:credentials) @parameters = .delete(:parameters) { |_| {} } # Default empty hash @tags = .delete(:tags) { |_| {} } # Default empty hash .delete(:disable_rollback) # There can be only one... @on_failure = .delete(:on_failure) { |_| 'DELETE' } @capabilities = .delete(:capabilities) { |_| %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM) } @attributes = .delete(:attributes) { |_| Model::Attributes.new } @options = @retry_limit = [:retry_limit] || 7 = {}.tap do |opt| opt[:region] = @region opt[:credentials] = @credentials unless @credentials.nil? opt[:retry_limit] = @retry_limit end @ec2_client = Aws::EC2::Client.new() @cf_client = Aws::CloudFormation::Client.new() ## Remote state @exist = false @status = NOT_CREATED @id = nil @outputs = {} @resources = {} @tasks = { after_create: [], after_delete: [], after_update: [], before_create: [], before_delete: [], before_update: [] } instance_exec(&block) if block @current_template = {} @last_event_seen = nil # First pass evaluation of stack # This is important because it: # * Catches syntax errors before starting a converge # * Builds a list of all resources that allows stacks early in # the dependency tree to know about later stacks. Some # clouds use this, for example, to create security groups early # in the dependency tree to avoid the chicken-and-egg problem. @template.execute rescue Aws::Errors::ServiceError => e @errors << e end |
Instance Attribute Details
#attribute_mapping_values ⇒ Object (readonly)
Returns the value of attribute attribute_mapping_values.
31 32 33 |
# File 'lib/convection/control/stack.rb', line 31 def attribute_mapping_values @attribute_mapping_values end |
#attributes ⇒ Object (readonly)
Returns the value of attribute attributes.
27 28 29 |
# File 'lib/convection/control/stack.rb', line 27 def attributes @attributes end |
#capabilities ⇒ Object (readonly)
Returns the value of attribute capabilities.
39 40 41 |
# File 'lib/convection/control/stack.rb', line 39 def capabilities @capabilities end |
#cloud ⇒ Object
Returns the value of attribute cloud.
38 39 40 |
# File 'lib/convection/control/stack.rb', line 38 def cloud @cloud end |
#credentials ⇒ Object
Returns the value of attribute credentials.
40 41 42 |
# File 'lib/convection/control/stack.rb', line 40 def credentials @credentials end |
#current_template ⇒ Object (readonly)
Returns the value of attribute current_template.
25 26 27 |
# File 'lib/convection/control/stack.rb', line 25 def current_template @current_template end |
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
28 29 30 |
# File 'lib/convection/control/stack.rb', line 28 def errors @errors end |
#exclude_availability_zones ⇒ Object
Returns the value of attribute exclude_availability_zones.
37 38 39 |
# File 'lib/convection/control/stack.rb', line 37 def exclude_availability_zones @exclude_availability_zones end |
#exist ⇒ Boolean (readonly) Also known as: exist?
Returns whether the stack exists and has a status other than DELETED.
20 21 22 |
# File 'lib/convection/control/stack.rb', line 20 def exist @exist end |
#id ⇒ Object (readonly)
Returns the value of attribute id.
15 16 17 |
# File 'lib/convection/control/stack.rb', line 15 def id @id end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
16 17 18 |
# File 'lib/convection/control/stack.rb', line 16 def name @name end |
#on_failure ⇒ Object
Returns the value of attribute on_failure.
43 44 45 |
# File 'lib/convection/control/stack.rb', line 43 def on_failure @on_failure end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
29 30 31 |
# File 'lib/convection/control/stack.rb', line 29 def @options end |
#outputs ⇒ Object (readonly)
Returns the value of attribute outputs.
32 33 34 |
# File 'lib/convection/control/stack.rb', line 32 def outputs @outputs end |
#parameters ⇒ Object (readonly)
Returns the value of attribute parameters.
41 42 43 |
# File 'lib/convection/control/stack.rb', line 41 def parameters @parameters end |
#region ⇒ Object
AWS-SDK
36 37 38 |
# File 'lib/convection/control/stack.rb', line 36 def region @region end |
#resources ⇒ Object (readonly)
Returns the value of attribute resources.
30 31 32 |
# File 'lib/convection/control/stack.rb', line 30 def resources @resources end |
#status ⇒ String (readonly)
Returns CloudFormation Stack status.
22 23 24 |
# File 'lib/convection/control/stack.rb', line 22 def status @status end |
#tags ⇒ Object (readonly)
Returns the value of attribute tags.
42 43 44 |
# File 'lib/convection/control/stack.rb', line 42 def @tags end |
#tasks ⇒ Object (readonly)
Returns the value of attribute tasks.
33 34 35 |
# File 'lib/convection/control/stack.rb', line 33 def tasks @tasks end |
#template ⇒ Object
Returns the value of attribute template.
17 18 19 |
# File 'lib/convection/control/stack.rb', line 17 def template @template end |
Instance Method Details
#[](key) ⇒ Object
192 193 194 |
# File 'lib/convection/control/stack.rb', line 192 def [](key) @attributes.get(name, key) end |
#[]=(key, value) ⇒ Object
197 198 199 |
# File 'lib/convection/control/stack.rb', line 197 def []=(key, value) @attributes.set(name, key, value) end |
#after_create_task(task) ⇒ Object
Register a given task to run after creation of a stack.
465 466 467 |
# File 'lib/convection/control/stack.rb', line 465 def after_create_task(task) @tasks[:after_create] << task end |
#after_delete_task(task) ⇒ Object
Register a given task to run after deletion of a stack.
470 471 472 |
# File 'lib/convection/control/stack.rb', line 470 def after_delete_task(task) @tasks[:after_delete] << task end |
#after_update_task(task) ⇒ Object
Register a given task to run after an update of a stack.
475 476 477 |
# File 'lib/convection/control/stack.rb', line 475 def after_update_task(task) @tasks[:after_update] << task end |
#apply(&block) ⇒ Object
Render the CloudFormation template and apply the Stack using the CloudFormation client.
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/convection/control/stack.rb', line 327 def apply(&block) = @options.clone.tap do |o| o[:template_body] = to_json o[:parameters] = cf_parameters o[:capabilities] = capabilities end # Get the state of existence before creation existing_stack = exist? if existing_stack if diff.empty? ## No Changes. Just get resources and move on block.call(Model::Event.new(:complete, "Stack #{ name } has no changes", :info)) if block get_status return elsif !resource_changes? && resource_dependent_changes? = "Stack #{ name } has no convergable changes (you must update Resources to update Conditions, Metadata, or Outputs)" block.call(Model::Event.new(UPDATE_FAILED, , :warn)) if block get_status return end ## Execute before update tasks @tasks[:before_update].delete_if do |task| run_task(:before_update, task, &block) end ## Update @cf_client.update_stack(.tap do |o| o[:stack_name] = id end) else ## Execute before create tasks @tasks[:before_create].delete_if do |task| run_task(:before_create, task, &block) end ## Create @cf_client.create_stack(.tap do |o| o[:stack_name] = cloud_name o[:tags] = o[:on_failure] = on_failure end) get_status(cloud_name) # Get ID of new stack end watch(&block) if block # Block execution on stack status ## Execute after create tasks after_task_type = existing_stack ? :after_update : :after_create @tasks[after_task_type].delete_if do |task| run_task(after_task_type, task, &block) end rescue Aws::Errors::ServiceError => e @errors << e end |
#availability_zones(&block) ⇒ Array<String>
Returns the list of availability zones found by the call to ec2 client’s describe availability zones.
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 |
# File 'lib/convection/control/stack.rb', line 437 def availability_zones(&block) @availability_zones ||= @ec2_client.describe_availability_zones.availability_zones.map(&:zone_name) unless @exclude_availability_zones.empty? @availability_zones.reject! { |az| @exclude_availability_zones.include?(az) } end if @availability_zones.empty? && block fail 'There are no AvailabilityZones, check exclude_availability_zones in the Cloudfile.' end @availability_zones.sort! @availability_zones.each_with_index(&block) if block @availability_zones end |
#before_create_task(task) ⇒ Object
Register a given task to run before creation of a stack.
480 481 482 |
# File 'lib/convection/control/stack.rb', line 480 def before_create_task(task) @tasks[:before_create] << task end |
#before_delete_task(task) ⇒ Object
Register a given task to run before deletion of a stack.
485 486 487 |
# File 'lib/convection/control/stack.rb', line 485 def before_delete_task(task) @tasks[:before_delete] << task end |
#before_update_task(task) ⇒ Object
Register a given task to run before an update of a stack.
490 491 492 |
# File 'lib/convection/control/stack.rb', line 490 def before_update_task(task) @tasks[:before_update] << task end |
#cloud_name ⇒ Object
rubocop:enable Metrics/LineLength
172 173 174 175 176 |
# File 'lib/convection/control/stack.rb', line 172 def cloud_name return @cloud_name unless @cloud_name.nil? return name if cloud.nil? "#{ cloud }-#{ name }" end |
#complete? ⇒ Boolean
Returns whether the CloudFormation Stack is in one of the several *_COMPLETE states.
226 227 228 |
# File 'lib/convection/control/stack.rb', line 226 def complete? [CREATE_COMPLETE, ROLLBACK_COMPLETE, UPDATE_COMPLETE, UPDATE_ROLLBACK_COMPLETE].include?(status) end |
#credential_error? ⇒ Boolean
Returns whether a credential error occurred is the reason accessing the CloudFormation stack failed.
232 233 234 |
# File 'lib/convection/control/stack.rb', line 232 def credential_error? error? && errors.all? { |e| e.class == Aws::EC2::Errors::RequestExpired } end |
#delete(&block) ⇒ Object
Delete the CloudFormation Stack using the CloudFormation client.
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 |
# File 'lib/convection/control/stack.rb', line 389 def delete(&block) ## Execute before delete tasks @tasks[:before_delete].delete_if do |task| run_task(:before_delete, task, &block) end @cf_client.delete_stack( :stack_name => id ) ## Block execution on stack status watch(&block) if block get_status ## Execute after delete tasks @tasks[:after_delete].delete_if do |task| run_task(:after_delete, task, &block) end rescue Aws::Errors::ServiceError => e @errors << e end |
#delete_complete? ⇒ Boolean
Returns whether the CloudFormation Stack is in one of the several *_COMPLETE states.
238 239 240 |
# File 'lib/convection/control/stack.rb', line 238 def delete_complete? DELETE_COMPLETE == status end |
#delete_success? ⇒ Boolean
Returns whether the Stack state is now #delete_complete? (with no errors present).
243 244 245 |
# File 'lib/convection/control/stack.rb', line 243 def delete_success? !error? && delete_complete? end |
#diff ⇒ Hash
Returns a set of differences between the current template (in CloudFormation) and the state of the rendered template (what would be converged).
284 285 286 |
# File 'lib/convection/control/stack.rb', line 284 def diff @template.diff(@current_template) end |
#error? ⇒ Boolean
Returns whether any errors occurred modifying the stack.
255 256 257 |
# File 'lib/convection/control/stack.rb', line 255 def error? !errors.empty? end |
#fail? ⇒ Boolean
Returns whether the CloudFormation Stack is in one of the several *_FAILED states.
249 250 251 |
# File 'lib/convection/control/stack.rb', line 249 def fail? [CREATE_FAILED, ROLLBACK_FAILED, DELETE_FAILED, UPDATE_ROLLBACK_FAILED].include?(status) end |
#fetch(*args) ⇒ Object
202 203 204 |
# File 'lib/convection/control/stack.rb', line 202 def fetch(*args) @attributes.fetch(*args) end |
#get(*args) ⇒ Object
207 208 209 |
# File 'lib/convection/control/stack.rb', line 207 def get(*args) @attributes.get(*args) end |
#in_progress? ⇒ Boolean
Returns whether or not the CloudFormation Stack is in one of several *IN_PROGRESS states.
217 218 219 220 221 222 |
# File 'lib/convection/control/stack.rb', line 217 def in_progress? [CREATE_IN_PROGRESS, ROLLBACK_IN_PROGRESS, DELETE_IN_PROGRESS, UPDATE_IN_PROGRESS, UPDATE_COMPLETE_CLEANUP_IN_PROGRESS, UPDATE_ROLLBACK_IN_PROGRESS, UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS].include?(status) end |
#include?(key) ⇒ Boolean #include?(stack_name, key) ⇒ Boolean
Returns whether the stack includes the specified key.
186 187 188 189 |
# File 'lib/convection/control/stack.rb', line 186 def include?(stack, key = nil) return @attributes.include?(name, stack) if key.nil? @attributes.include?(stack, key) end |
#load_template_info ⇒ Object
162 163 164 165 166 167 168 169 |
# File 'lib/convection/control/stack.rb', line 162 def load_template_info get_resources get_template resource_attributes get_events(1) # Get the latest page of events (Set @last_event_seen before starting) rescue Aws::Errors::ServiceError => e @errors << e end |
#render ⇒ Object
269 270 271 |
# File 'lib/convection/control/stack.rb', line 269 def render @template.render end |
#resource_changes? ⇒ Boolean
Returns whether the Resources section of the rendered template has any changes compared to the current template (in CloudFormation).
291 292 293 294 295 296 |
# File 'lib/convection/control/stack.rb', line 291 def resource_changes? ours = { 'Resources' => @template.all_resources.map(&:render) } thiers = { 'Resources' => @current_template['Resources'] } ours.diff(thiers).any? end |
#resource_dependent_changes? ⇒ Boolean
Returns whether the any template sections dependent on the Resources section of the rendered template has any changes compared to the current template (in CloudFormation). For example Conditions, Metadata, and Outputs depend on changes to resources to be able to converge. See also rapid7/convection#140.
305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/convection/control/stack.rb', line 305 def resource_dependent_changes? ours = { 'Conditions' => @template.conditions.map(&:render), 'Outputs' => @template.outputs.map(&:render) } theirs = { 'Conditions' => @current_template['Conditions'], 'Outputs' => @current_template['Outputs'] } ours.diff(theirs).any? end |
#success? ⇒ Boolean
Returns whether the Stack state is now #complete? (with no errors present).
260 261 262 |
# File 'lib/convection/control/stack.rb', line 260 def success? !error? && complete? end |
#template_status ⇒ Object
156 157 158 159 160 |
# File 'lib/convection/control/stack.rb', line 156 def template_status get_status(cloud_name) rescue Aws::Errors::ServiceError => e @errors << e end |
#to_json(pretty = false) ⇒ Object
Returns the renedered CloudFormation Template JSON.
276 277 278 |
# File 'lib/convection/control/stack.rb', line 276 def to_json(pretty = false) @template.to_json(nil, pretty) end |
#validate ⇒ Object
Validates a rendered template against the CloudFormation API.
456 457 458 459 460 |
# File 'lib/convection/control/stack.rb', line 456 def validate result = @cf_client.validate_template(:template_body => template.to_json) fail result.context.http_response.inspect unless result.successful? puts "\nTemplate validated successfully" end |
#watch(poll = 2, &block) ⇒ Object
Loops through current events until the CloudFormation Stack has finished being modified.
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 |
# File 'lib/convection/control/stack.rb', line 416 def watch(poll = 2, &block) get_status loop do get_events.reverse_each do |event| block.call(Model::Event.from_cf(event)) end if block break unless in_progress? sleep poll get_status end rescue Aws::Errors::ServiceError => e @errors << e end |