# Polling stack status until it's completed or failed. class StackSynchronizer include PutsAndFlush attr_reader :out, :stack class SyncResult < Devops::Helpers::ResultObject set_result_codes( ok: 0, stack_rolled_back: 1, unkown_status: 2, timeout: 3, error: 5, stack_deleted: 6, stack_not_found: 7 ) end def initialize(stack, out) @stack, @out = stack, out @printed_events = [] @sync_result = nil end def sync puts_and_flush "Syncing stack '#{stack.id}'..." sleep_times.detect do |sleep_time| sleep sleep_time stack.sync! print_new_events update_stack_status if stack_status_changed? stack_is_already_created_or_failed? end @sync_result ||= result(:timeout) print_result_message @sync_result rescue StandardError => e DevopsLogger.logger.error e.message puts_and_flush "Error: #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}" result(:error) end private def stack_is_already_created_or_failed? case stack.stack_status when 'CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS', 'DELETE_IN_PROGRESS' false when 'CREATE_COMPLETE', 'ROLLBACK_COMPLETE', 'DELETE_COMPLETE', 'CREATE_FAILED', 'NOT_FOUND' @sync_result = result_for_provider_status(stack.stack_status) else @sync_result = result(:unkown_status) end end def sleep_times [5]*5 + [10]*400 end def result(reason) SyncResult.from_reason(reason) end def result_for_provider_status(status) provider_status_mapping = { 'CREATE_COMPLETE' => :ok, 'ROLLBACK_COMPLETE' => :stack_rolled_back, 'DELETE_COMPLETE' => :stack_deleted, 'NOT_FOUND' => :stack_not_found } result(provider_status_mapping.fetch(status)) end def print_result_message puts_and_flush Devops::Messages.t("stack_synchronizer.result.#{@sync_result.reason}", stack_id: stack.id, status: stack.stack_status, seconds: sleep_times.inject(&:+)) end def update_stack_status ::Devops::Db.connector.stack_update(stack) @last_status = stack.stack_status end def stack_status_changed? @last_status != stack.stack_status end def print_new_events stack.events.each do |event| unless @printed_events.include?(event["event_id"]) @printed_events << event["event_id"] out.puts "#{event["timestamp"]} - #{event["status"]}: #{event["reason"]}" end end out.flush end end