예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
    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
예제 #4
0
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())
예제 #5
0
    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))
예제 #6
0
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())