CID-444: persist stacks even in case of failures

This commit is contained in:
Anton Chuchkalov 2016-03-23 18:35:05 +03:00
parent 48333a9097
commit 938616c489
3 changed files with 52 additions and 17 deletions

View File

@ -90,6 +90,8 @@ module Devops
def sync! def sync!
self.stack_status = provider_instance.stack_details(self)[:stack_status] self.stack_status = provider_instance.stack_details(self)[:stack_status]
self.events = provider_instance.stack_events(self) self.events = provider_instance.stack_events(self)
rescue Fog::AWS::CloudFormation::NotFound
self.stack_status = 'NOT_FOUND'
end end
def resources def resources

View File

@ -10,12 +10,20 @@ RSpec.describe StackSynchronizer, stubbed_connector: true do
allow(syncer).to receive(:sleep) allow(syncer).to receive(:sleep)
allow(stubbed_connector).to receive(:stack_update) allow(stubbed_connector).to receive(:stack_update)
lots_of_statuses = ['CREATE_IN_PROGRESS'] * 10 + ['CREATE_COMPLETE'] # lots_of_statuses = ['CREATE_IN_PROGRESS'] * 10 + ['CREATE_COMPLETE']
allow(stack).to receive(:stack_status).and_return(*lots_of_statuses) # allow(stack).to receive(:stack_status).and_return(*lots_of_statuses)
end end
describe '#sync' do def setup_statuses(statuses_array)
statuses = statuses_array.to_enum
allow(stack).to receive(:sync!) {
stack.stack_status = statuses.next
}
end
describe '#sync', stubbed_logger: true do
it 'waits for stack creating to be finished' do it 'waits for stack creating to be finished' do
setup_statuses(['CREATE_IN_PROGRESS'] * 10 + ['CREATE_COMPLETE'])
expect(syncer).to receive(:sleep).at_least(10).times expect(syncer).to receive(:sleep).at_least(10).times
expect(stack).to receive(:sync!).at_least(10).times expect(stack).to receive(:sync!).at_least(10).times
syncer.sync syncer.sync
@ -27,14 +35,23 @@ RSpec.describe StackSynchronizer, stubbed_connector: true do
event3 = {'event_id' => 3, 'timestamp' => 't3'} event3 = {'event_id' => 3, 'timestamp' => 't3'}
allow(stack).to receive(:events).and_return([event1], [event1, event2], [event1, event2, event3]) allow(stack).to receive(:events).and_return([event1], [event1, event2], [event1, event2, event3])
setup_statuses(['CREATE_IN_PROGRESS', 'CREATE_IN_PROGRESS', 'CREATE_IN_PROGRESS', 'CREATE_COMPLETE'])
syncer.sync syncer.sync
expect(out).to have_received(:puts).with(/t1/).once.ordered expect(out).to have_received(:puts).with(/t1/).once.ordered
expect(out).to have_received(:puts).with(/t2/).once.ordered expect(out).to have_received(:puts).with(/t2/).once.ordered
expect(out).to have_received(:puts).with(/t3/).once.ordered expect(out).to have_received(:puts).with(/t3/).once.ordered
end end
it 'updates stack when status is changed' do
setup_statuses(['CREATE_IN_PROGRESS', 'CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_COMPLETE'])
expect(stubbed_connector).to receive(:stack_update).exactly(3).times
syncer.sync
end
context 'when stack creating was successful' do context 'when stack creating was successful' do
it 'updates stack in DB when stack creating is finished and returns 0' do it 'updates stack in DB when stack creating is finished and returns 0' do
setup_statuses(['CREATE_COMPLETE'])
expect(stubbed_connector).to receive(:stack_update).with(stack) expect(stubbed_connector).to receive(:stack_update).with(stack)
expect(syncer.sync).to eq 0 expect(syncer.sync).to eq 0
end end
@ -42,14 +59,15 @@ RSpec.describe StackSynchronizer, stubbed_connector: true do
context 'when stack was rollbacked' do context 'when stack was rollbacked' do
it 'returns 1 (:stack_rolled_back)' do it 'returns 1 (:stack_rolled_back)' do
allow(stack).to receive(:stack_status).and_return('CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_COMPLETE') setup_statuses(['CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_COMPLETE'])
expect(syncer.sync).to eq 1 expect(syncer.sync).to eq 1
end end
end end
context 'when unkown stack status was found' do context 'when unkown stack status was found' do
it 'returns 2 (:unkown_status)' do it 'returns 2 (:unkown_status)' do
allow(stack).to receive(:stack_status).and_return('CREATE_IN_PROGRESS', 'unknown') setup_statuses(['CREATE_IN_PROGRESS', 'unknown'])
expect(syncer.sync).to eq 2 expect(syncer.sync).to eq 2
end end
end end
@ -63,7 +81,7 @@ RSpec.describe StackSynchronizer, stubbed_connector: true do
context 'when an error occured during syncing', stubbed_logger: true do context 'when an error occured during syncing', stubbed_logger: true do
it 'returns 5 (:error)' do it 'returns 5 (:error)' do
allow(stack).to receive(:stack_status).and_return('CREATE_IN_PROGRESS', 'CREATE_COMPLETE') setup_statuses(['CREATE_IN_PROGRESS', 'CREATE_COMPLETE'])
allow(stubbed_connector).to receive(:stack_update) { raise } allow(stubbed_connector).to receive(:stack_update) { raise }
expect(syncer.sync).to eq 5 expect(syncer.sync).to eq 5
end end

View File

@ -17,18 +17,14 @@ class StackSynchronizer
sleep sleep_time sleep sleep_time
stack.sync! stack.sync!
print_new_events print_new_events
update_stack_status if stack_status_changed?
case stack.stack_status case stack.stack_status
when 'CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS', 'DELETE_IN_PROGRESS' when 'CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS', 'DELETE_IN_PROGRESS'
when 'CREATE_COMPLETE' when 'CREATE_COMPLETE', 'ROLLBACK_COMPLETE', 'DELETE_COMPLETE', 'CREATE_FAILED', 'NOT_FOUND'
::Devops::Db.connector.stack_update(stack)
puts_and_flush "Stack '#{stack.id}' status is now #{stack.stack_status}" puts_and_flush "Stack '#{stack.id}' status is now #{stack.stack_status}"
return 0 return code_for_status(stack.stack_status)
when 'ROLLBACK_COMPLETE'
puts_and_flush "Stack '#{stack.id}' status is rolled back"
return error_code(:stack_rolled_back)
when 'DELETE_COMPLETE'
puts_and_flush "Stack '#{stack.id}' status is deleted"
return error_code(:stack_deleted)
else else
puts_and_flush "Unknown stack status: '#{stack.stack_status}'" puts_and_flush "Unknown stack status: '#{stack.stack_status}'"
return error_code(:unkown_status) return error_code(:unkown_status)
@ -38,7 +34,7 @@ class StackSynchronizer
error_code(:timeout) error_code(:timeout)
rescue StandardError => e rescue StandardError => e
DevopsLogger.logger.error e.message DevopsLogger.logger.error e.message
puts_and_flush "Error: #{e.message}\n#{e.backtrace.join("\n")}" puts_and_flush "Error: #{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
error_code(:error) error_code(:error)
end end
@ -48,6 +44,24 @@ class StackSynchronizer
private private
def code_for_status(status)
{
'CREATE_COMPLETE' => 0,
'ROLLBACK_COMPLETE' => error_code(:stack_rolled_back),
'DELETE_COMPLETE' => error_code(:stack_deleted),
'NOT_FOUND' => error_code(:stack_not_found)
}.fetch(status)
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 error_code(reason) def error_code(reason)
error_codes.fetch(reason) error_codes.fetch(reason)
end end
@ -58,7 +72,8 @@ class StackSynchronizer
unkown_status: 2, unkown_status: 2,
timeout: 3, timeout: 3,
error: 5, error: 5,
stack_deleted: 6 stack_deleted: 6,
stack_not_found: 7
} }
end end