def setUp(self): """ Configure an empty stack. """ def termination(): return lambda: True def run_immediately(f): f() self.cooperator = Cooperator(terminationPredicateFactory=termination, scheduler=run_immediately) self.undo = InMemoryUndoStack(self.cooperator.coiterate)
def setUp(self): """ Configure an empty stack. """ def termination(): return lambda: True def run_immediately(f): f() self.cooperator = Cooperator( terminationPredicateFactory=termination, scheduler=run_immediately) self.undo = InMemoryUndoStack(self.cooperator.coiterate)
def execute_config(self, log, transaction_id, scaling_group, launch_config): """ see :meth:`ISupervisor.execute_config` """ log = log.bind(worker=launch_config['type'], tenant_id=scaling_group.tenant_id) assert launch_config['type'] == 'launch_server' undo = InMemoryUndoStack(self.coiterate) d = self._get_request_bag(log, scaling_group) def got_request_bag(request_bag): log.msg("Executing launch config.") return launch_server_v1.launch_server(log, request_bag, scaling_group, launch_config['args'], undo) d.addCallback(got_request_bag) def when_launch_server_completed(result): # XXX: Something should be done with this data. Currently only # enough to pass to the controller to store in the active state is # returned server_details, lb_info = result log.msg("Done executing launch config.", server_id=server_details['server']['id']) return { 'id': server_details['server']['id'], 'links': server_details['server']['links'], 'name': server_details['server']['name'], 'lb_info': lb_info } d.addCallback(when_launch_server_completed) def when_fails(result): log.msg("Encountered an error, rewinding {worker!r} job undo stack.", exc=result.value) ud = undo.rewind() ud.addCallback(lambda _: result) return ud d.addErrback(when_fails) return d
class InMemoryUndoStackTests(TestCase): """ Tests for the in memory undo stack. """ def setUp(self): """ Configure an empty stack. """ def termination(): return lambda: True def run_immediately(f): f() self.cooperator = Cooperator( terminationPredicateFactory=termination, scheduler=run_immediately) self.undo = InMemoryUndoStack(self.cooperator.coiterate) def test_provides_IUndoStack(self): """ InMemoryUndoStack provides IUndoStack. """ verifyObject(IUndoStack, self.undo) def test_push(self): """ push puts an operation and it's arguments onto the stack. """ calls = [] def op(*args, **kwargs): calls.append((args, kwargs)) self.undo.push(op, 'foo', bar='baz') self.undo.rewind() self.assertEqual(calls, [(('foo',), {'bar': 'baz'})]) def test_rewind_in_reverse_order(self): """ rewind invokes operations in the reverse of the order they are pushed onto the stack. (Because it is a stack.) """ calls = [] def op(*args, **kwargs): calls.append((args, kwargs)) self.undo.push(op, 'foo') self.undo.push(op, 'bar') self.undo.push(op, 'baz') self.undo.rewind() self.assertEqual(calls, [(('baz',), {}), (('bar',), {}), (('foo',), {})]) def test_rewind_handles_deferreds(self): """ rewind will wait on any deferreds returned by the undo operation function. """ def op(op_d): return op_d op_d = Deferred() self.undo.push(op, op_d) d = self.undo.rewind() self.assertNoResult(d) op_d.callback(None) self.successResultOf(d) def test_rewind_blocks_on_deferreds_returned_by_ops(self): """ rewind will serialize operations waiting on any deferreds returned before running the next operation. """ called = [0] def op(op_d): called[0] += 1 return op_d op_d1 = Deferred() self.undo.push(op, op_d1) op_d2 = Deferred() self.undo.push(op, op_d2) d = self.undo.rewind() self.assertEqual(called[0], 1) self.assertNoResult(d) op_d2.callback(None) self.assertEqual(called[0], 2) self.assertNoResult(d) op_d1.callback(None) self.successResultOf(d) def test_rewind_stops_on_error(self): """ rewind errbacks it's completion deferred when it encounters an error. """ called = [0] def op(op_d): called[0] += 1 return op_d self.undo.push(op, None) op_d1 = Deferred() self.undo.push(op, op_d1) d = self.undo.rewind() self.assertNoResult(d) class DummyOpFailure(Exception): pass op_d1.errback(DummyOpFailure()) self.assertEqual(called[0], 1) self.failureResultOf(d, DummyOpFailure) def test_rewind_empty_stack(self): """ rewind completes successfully if the stack is empty. """ self.successResultOf(self.undo.rewind())
def execute_config(self, log, transaction_id, scaling_group, launch_config): """ Executes a single launch config. :param log: Bound logger. :param str transaction_id: Transaction ID. :param callable auth_function: A 1-argument callable that takes a tenant_id, and returns a Deferred that fires with a 2-tuple of auth_token and service_catalog. :param IScalingGroup scaling_group: Scaling Group. :param dict launch_config: The launch config for the scaling group. :returns: A deferred that fires with a 3-tuple of job_id, completion deferred, and job_info (a dict) :rtype: ``Deferred`` """ job_id = generate_job_id(scaling_group.uuid) completion_d = Deferred() log = log.bind(job_id=job_id, worker=launch_config['type'], tenant_id=scaling_group.tenant_id) assert launch_config['type'] == 'launch_server' undo = InMemoryUndoStack(self.coiterate) def when_fails(result): log.msg( "Encountered an error, rewinding {worker!r} job undo stack.", exc=result.value) ud = undo.rewind() ud.addCallback(lambda _: result) return ud completion_d.addErrback(when_fails) log.msg("Authenticating for tenant") d = self.auth_function(scaling_group.tenant_id) def when_authenticated((auth_token, service_catalog)): log.msg("Executing launch config.") return launch_server_v1.launch_server(log, config_value('region'), scaling_group, service_catalog, auth_token, launch_config['args'], undo) d.addCallback(when_authenticated) def when_launch_server_completed(result): # XXX: Something should be done with this data. Currently only enough # to pass to the controller to store in the active state is returned server_details, lb_info = result log.msg("Done executing launch config.", instance_id=server_details['server']['id']) return { 'id': server_details['server']['id'], 'links': server_details['server']['links'], 'name': server_details['server']['name'], 'lb_info': lb_info } d.addCallback(when_launch_server_completed) d.chainDeferred(completion_d) return succeed((job_id, completion_d))
class InMemoryUndoStackTests(SynchronousTestCase): """ Tests for the in memory undo stack. """ def setUp(self): """ Configure an empty stack. """ def termination(): return lambda: True def run_immediately(f): f() self.cooperator = Cooperator(terminationPredicateFactory=termination, scheduler=run_immediately) self.undo = InMemoryUndoStack(self.cooperator.coiterate) def test_provides_IUndoStack(self): """ InMemoryUndoStack provides IUndoStack. """ verifyObject(IUndoStack, self.undo) def test_push(self): """ push puts an operation and it's arguments onto the stack. """ calls = [] def op(*args, **kwargs): calls.append((args, kwargs)) self.undo.push(op, 'foo', bar='baz') self.undo.rewind() self.assertEqual(calls, [(('foo', ), {'bar': 'baz'})]) def test_rewind_in_reverse_order(self): """ rewind invokes operations in the reverse of the order they are pushed onto the stack. (Because it is a stack.) """ calls = [] def op(*args, **kwargs): calls.append((args, kwargs)) self.undo.push(op, 'foo') self.undo.push(op, 'bar') self.undo.push(op, 'baz') self.undo.rewind() self.assertEqual(calls, [(('baz', ), {}), (('bar', ), {}), (('foo', ), {})]) def test_rewind_handles_deferreds(self): """ rewind will wait on any deferreds returned by the undo operation function. """ def op(op_d): return op_d op_d = Deferred() self.undo.push(op, op_d) d = self.undo.rewind() self.assertNoResult(d) op_d.callback(None) self.successResultOf(d) def test_rewind_blocks_on_deferreds_returned_by_ops(self): """ rewind will serialize operations waiting on any deferreds returned before running the next operation. """ called = [0] def op(op_d): called[0] += 1 return op_d op_d1 = Deferred() self.undo.push(op, op_d1) op_d2 = Deferred() self.undo.push(op, op_d2) d = self.undo.rewind() self.assertEqual(called[0], 1) self.assertNoResult(d) op_d2.callback(None) self.assertEqual(called[0], 2) self.assertNoResult(d) op_d1.callback(None) self.successResultOf(d) def test_rewind_stops_on_error(self): """ rewind errbacks it's completion deferred when it encounters an error. """ called = [0] def op(op_d): called[0] += 1 return op_d self.undo.push(op, None) op_d1 = Deferred() self.undo.push(op, op_d1) d = self.undo.rewind() self.assertNoResult(d) class DummyOpFailure(Exception): pass op_d1.errback(DummyOpFailure()) self.assertEqual(called[0], 1) self.failureResultOf(d, DummyOpFailure) def test_rewind_empty_stack(self): """ rewind completes successfully if the stack is empty. """ self.successResultOf(self.undo.rewind())