예제 #1
0
class TestDestroyAction(unittest.TestCase):
    def setUp(self):
        config = Config({
            "namespace":
            "namespace",
            "stacks": [
                {
                    "name": "vpc"
                },
                {
                    "name": "bastion",
                    "requires": ["vpc"]
                },
                {
                    "name": "instance",
                    "requires": ["vpc", "bastion"]
                },
                {
                    "name": "db",
                    "requires": ["instance", "vpc", "bastion"]
                },
                {
                    "name": "other",
                    "requires": ["db"]
                },
            ],
        })
        self.context = Context(config=config)
        self.action = destroy.Action(self.context, provider=mock.MagicMock())

    def test_generate_plan(self):
        plan = self.action._generate_plan()
        self.assertEqual(
            {
                'namespace-vpc':
                set([
                    'namespace-db', 'namespace-instance', 'namespace-bastion'
                ]),
                'namespace-other':
                set([]),
                'namespace-bastion':
                set(['namespace-instance', 'namespace-db']),
                'namespace-instance':
                set(['namespace-db']),
                'namespace-db':
                set(['namespace-other'])
            }, plan.graph.to_dict())

    def test_only_execute_plan_when_forced(self):
        with mock.patch.object(self.action, "_generate_plan") as \
                mock_generate_plan:
            self.action.run(force=False)
            self.assertEqual(mock_generate_plan().execute.call_count, 0)

    def test_execute_plan_when_forced(self):
        with mock.patch.object(self.action, "_generate_plan") as \
                mock_generate_plan:
            self.action.run(force=True)
            self.assertEqual(mock_generate_plan().execute.call_count, 1)

    def test_destroy_stack_complete_if_state_submitted(self):
        # Simulate the provider not being able to find the stack (a result of
        # it being successfully deleted)
        self.action.provider = mock.MagicMock()
        self.action.provider.get_stack.side_effect = StackDoesNotExist("mock")
        status = self.action._destroy_stack(MockStack("vpc"), status=PENDING)
        # if we haven't processed the step (ie. has never been SUBMITTED,
        # should be skipped)
        self.assertEqual(status, SKIPPED)
        status = self.action._destroy_stack(MockStack("vpc"), status=SUBMITTED)
        # if we have processed the step and then can't find the stack, it means
        # we successfully deleted it
        self.assertEqual(status, COMPLETE)

    def test_destroy_stack_step_statuses(self):
        mock_provider = mock.MagicMock()
        stacks_dict = self.context.get_stacks_dict()

        def get_stack(stack_name):
            return stacks_dict.get(stack_name)

        plan = self.action._generate_plan()
        step = plan.steps[0]
        # we need the AWS provider to generate the plan, but swap it for
        # the mock one to make the test easier
        self.action.provider = mock_provider

        # simulate stack doesn't exist and we haven't submitted anything for
        # deletion
        mock_provider.get_stack.side_effect = StackDoesNotExist("mock")
        status = step.run()
        self.assertEqual(status, SKIPPED)

        # simulate stack getting successfully deleted
        mock_provider.get_stack.side_effect = get_stack
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = False
        status = step._run_once()
        self.assertEqual(status, SUBMITTED)
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = True
        status = step._run_once()
        self.assertEqual(status, SUBMITTED)
        mock_provider.is_stack_destroyed.return_value = True
        mock_provider.is_stack_in_progress.return_value = False
        status = step._run_once()
        self.assertEqual(status, COMPLETE)
예제 #2
0
 def test_context_get_stacks_dict_use_fqn(self):
     context = Context(config=self.config)
     stacks_dict = context.get_stacks_dict()
     stack_names = sorted(stacks_dict.keys())
     self.assertEqual(stack_names[0], "namespace-stack1")
     self.assertEqual(stack_names[1], "namespace-stack2")
예제 #3
0
 def test_context_get_stacks_dict_use_fqn(self):
     context = Context(config=self.config)
     stacks_dict = context.get_stacks_dict()
     stack_names = sorted(stacks_dict.keys())
     self.assertEqual(stack_names[0], "namespace-stack1")
     self.assertEqual(stack_names[1], "namespace-stack2")
예제 #4
0
class TestDestroyAction(unittest.TestCase):

    def setUp(self):
        self.context = Context({'namespace': 'namespace'})
        self.context.config = {
            'stacks': [
                {'name': 'vpc'},
                {'name': 'bastion', 'requires': ['vpc']},
                {'name': 'instance', 'requires': ['vpc', 'bastion']},
                {'name': 'db', 'requires': ['instance', 'vpc', 'bastion']},
                {'name': 'other', 'requires': ['db']},
            ],
        }
        self.action = destroy.Action(self.context, provider=mock.MagicMock())

    def test_generate_plan(self):
        plan = self.action._generate_plan()
        self.assertEqual(
            [self.context.get_fqn(s) for s in ['other', 'db', 'instance', 'bastion', 'vpc']],
            plan.keys(),
        )

    def test_only_execute_plan_when_forced(self):
        with mock.patch.object(self.action, '_generate_plan') as mock_generate_plan:
            self.action.run(force=False)
            self.assertEqual(mock_generate_plan().execute.call_count, 0)

    def test_execute_plan_when_forced(self):
        with mock.patch.object(self.action, '_generate_plan') as mock_generate_plan:
            self.action.run(force=True)
            self.assertEqual(mock_generate_plan().execute.call_count, 1)

    def test_destroy_stack_complete_if_state_submitted(self):
        # Simulate the provider not being able to find the stack (a result of
        # it being successfully deleted)
        self.action.provider = mock.MagicMock()
        self.action.provider.get_stack.side_effect = StackDoesNotExist('mock')
        status = self.action._destroy_stack(MockStack('vpc'), status=PENDING)
        # if we haven't processed the step (ie. has never been SUBMITTED, should be skipped)
        self.assertEqual(status, SKIPPED)
        status = self.action._destroy_stack(MockStack('vpc'), status=SUBMITTED)
        # if we have processed the step and then can't find the stack, it means
        # we successfully deleted it
        self.assertEqual(status, COMPLETE)

    def test_destroy_stack_in_parallel(self):
        count = {'_count': 0}
        mock_provider = mock.MagicMock()
        self.context.config = {
            'stacks': [
                {'name': 'vpc'},
                {'name': 'bastion', 'requires': ['vpc']},
                {'name': 'instance', 'requires': ['vpc']},
                {'name': 'db', 'requies': ['vpc']},
            ],
        }
        stacks_dict = self.context.get_stacks_dict()

        def get_stack(stack_name):
            return stacks_dict.get(stack_name)

        def get_stack_staggered(stack_name):
            count['_count'] += 1
            if not count['_count'] % 2:
                raise StackDoesNotExist(stack_name)
            return stacks_dict.get(stack_name)

        def wait_func(*args):
            # we want "get_stack" above to return staggered results for the stack
            # being "deleted" to simulate waiting on stacks to complete
            mock_provider.get_stack.side_effect = get_stack_staggered

        plan = self.action._generate_plan()
        plan._wait_func = wait_func

        # swap for mock provider
        plan.provider = mock_provider
        self.action.provider = mock_provider

        # we want "get_stack" to return the mock stacks above on the first
        # pass. "wait_func" will simulate the stack being deleted every second
        # pass
        mock_provider.get_stack.side_effect = get_stack
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = True

        independent_stacks = filter(lambda x: x.name != 'vpc', self.context.get_stacks())
        while not plan._single_run():
            # vpc should be the last stack that is deleted
            if plan['namespace-vpc'].completed:
                self.assertFalse(plan.list_pending())

            # all of the independent stacks should be submitted at the same time
            if any([plan[stack.fqn].submitted for stack in independent_stacks]):
                self.assertTrue(all([plan[stack.fqn].submitted for stack in independent_stacks]))
            wait_func()

    def test_destroy_stack_step_statuses(self):
        mock_provider = mock.MagicMock()
        stacks_dict = self.context.get_stacks_dict()

        def get_stack(stack_name):
            return stacks_dict.get(stack_name)

        plan = self.action._generate_plan()
        _, step = plan.list_pending()[0]
        # we need the AWS provider to generate the plan, but swap it for
        # the mock one to make the test easier
        self.action.provider = mock_provider

        # simulate stack doesn't exist and we haven't submitted anything for deletion
        mock_provider.get_stack.side_effect = StackDoesNotExist('mock')
        status = step.run()
        self.assertEqual(status, SKIPPED)

        # simulate stack getting successfully deleted
        mock_provider.get_stack.side_effect = get_stack
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = False
        status = step.run()
        self.assertEqual(status, SUBMITTED)
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = True
        status = step.run()
        self.assertEqual(status, SUBMITTED)
        mock_provider.is_stack_destroyed.return_value = True
        mock_provider.is_stack_in_progress.return_value = False
        status = step.run()
        self.assertEqual(status, COMPLETE)
예제 #5
0
class TestDestroyAction(unittest.TestCase):
    def setUp(self):
        self.context = Context({'namespace': 'namespace'})
        self.context.config = {
            'stacks': [
                {
                    'name': 'vpc'
                },
                {
                    'name': 'bastion',
                    'requires': ['vpc']
                },
                {
                    'name': 'instance',
                    'requires': ['vpc', 'bastion']
                },
                {
                    'name': 'db',
                    'requires': ['instance', 'vpc', 'bastion']
                },
                {
                    'name': 'other',
                    'requires': ['db']
                },
            ],
        }
        self.action = destroy.Action(self.context, provider=mock.MagicMock())

    def test_generate_plan(self):
        plan = self.action._generate_plan()
        self.assertEqual(
            [
                self.context.get_fqn(s)
                for s in ['other', 'db', 'instance', 'bastion', 'vpc']
            ],
            plan.keys(),
        )

    def test_only_execute_plan_when_forced(self):
        with mock.patch.object(self.action,
                               '_generate_plan') as mock_generate_plan:
            self.action.run(force=False)
            self.assertEqual(mock_generate_plan().execute.call_count, 0)

    def test_execute_plan_when_forced(self):
        with mock.patch.object(self.action,
                               '_generate_plan') as mock_generate_plan:
            self.action.run(force=True)
            self.assertEqual(mock_generate_plan().execute.call_count, 1)

    def test_destroy_stack_complete_if_state_submitted(self):
        # Simulate the provider not being able to find the stack (a result of
        # it being successfully deleted)
        self.action.provider = mock.MagicMock()
        self.action.provider.get_stack.side_effect = StackDoesNotExist('mock')
        status = self.action._destroy_stack(MockStack('vpc'), status=PENDING)
        # if we haven't processed the step (ie. has never been SUBMITTED, should be skipped)
        self.assertEqual(status, SKIPPED)
        status = self.action._destroy_stack(MockStack('vpc'), status=SUBMITTED)
        # if we have processed the step and then can't find the stack, it means
        # we successfully deleted it
        self.assertEqual(status, COMPLETE)

    def test_destroy_stack_in_parallel(self):
        count = {'_count': 0}
        mock_provider = mock.MagicMock()
        self.context.config = {
            'stacks': [
                {
                    'name': 'vpc'
                },
                {
                    'name': 'bastion',
                    'requires': ['vpc']
                },
                {
                    'name': 'instance',
                    'requires': ['vpc']
                },
                {
                    'name': 'db',
                    'requies': ['vpc']
                },
            ],
        }
        stacks_dict = self.context.get_stacks_dict()

        def get_stack(stack_name):
            return stacks_dict.get(stack_name)

        def get_stack_staggered(stack_name):
            count['_count'] += 1
            if not count['_count'] % 2:
                raise StackDoesNotExist(stack_name)
            return stacks_dict.get(stack_name)

        def wait_func(*args):
            # we want "get_stack" above to return staggered results for the stack
            # being "deleted" to simulate waiting on stacks to complete
            mock_provider.get_stack.side_effect = get_stack_staggered

        plan = self.action._generate_plan()
        plan._wait_func = wait_func

        # swap for mock provider
        plan.provider = mock_provider
        self.action.provider = mock_provider

        # we want "get_stack" to return the mock stacks above on the first
        # pass. "wait_func" will simulate the stack being deleted every second
        # pass
        mock_provider.get_stack.side_effect = get_stack
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = True

        independent_stacks = filter(lambda x: x.name != 'vpc',
                                    self.context.get_stacks())
        while not plan._single_run():
            # vpc should be the last stack that is deleted
            if plan['namespace-vpc'].completed:
                self.assertFalse(plan.list_pending())

            # all of the independent stacks should be submitted at the same time
            if any([plan[stack.fqn].submitted
                    for stack in independent_stacks]):
                self.assertTrue(
                    all([
                        plan[stack.fqn].submitted
                        for stack in independent_stacks
                    ]))
            wait_func()

    def test_destroy_stack_step_statuses(self):
        mock_provider = mock.MagicMock()
        stacks_dict = self.context.get_stacks_dict()

        def get_stack(stack_name):
            return stacks_dict.get(stack_name)

        plan = self.action._generate_plan()
        _, step = plan.list_pending()[0]
        # we need the AWS provider to generate the plan, but swap it for
        # the mock one to make the test easier
        self.action.provider = mock_provider

        # simulate stack doesn't exist and we haven't submitted anything for deletion
        mock_provider.get_stack.side_effect = StackDoesNotExist('mock')
        status = step.run()
        self.assertEqual(status, SKIPPED)

        # simulate stack getting successfully deleted
        mock_provider.get_stack.side_effect = get_stack
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = False
        status = step.run()
        self.assertEqual(status, SUBMITTED)
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = True
        status = step.run()
        self.assertEqual(status, SUBMITTED)
        mock_provider.is_stack_destroyed.return_value = True
        mock_provider.is_stack_in_progress.return_value = False
        status = step.run()
        self.assertEqual(status, COMPLETE)
예제 #6
0
class TestDestroyAction(unittest.TestCase):

    def setUp(self):
        config = Config({
            "namespace": "namespace",
            "stacks": [
                {"name": "vpc"},
                {"name": "bastion", "requires": ["vpc"]},
                {"name": "instance", "requires": ["vpc", "bastion"]},
                {"name": "db", "requires": ["instance", "vpc", "bastion"]},
                {"name": "other", "requires": ["db"]},
            ],
        })
        self.context = Context(config=config)
        self.action = destroy.Action(self.context,
                                     cancel=MockThreadingEvent())

    def test_generate_plan(self):
        plan = self.action._generate_plan()
        self.assertEqual(
            {
                'vpc': set(
                    ['db', 'instance', 'bastion']),
                'other': set([]),
                'bastion': set(
                    ['instance', 'db']),
                'instance': set(
                    ['db']),
                'db': set(
                    ['other'])},
            plan.graph.to_dict()
        )

    def test_only_execute_plan_when_forced(self):
        with mock.patch.object(self.action, "_generate_plan") as \
                mock_generate_plan:
            self.action.run(force=False)
            self.assertEqual(mock_generate_plan().execute.call_count, 0)

    def test_execute_plan_when_forced(self):
        with mock.patch.object(self.action, "_generate_plan") as \
                mock_generate_plan:
            self.action.run(force=True)
            self.assertEqual(mock_generate_plan().execute.call_count, 1)

    def test_destroy_stack_complete_if_state_submitted(self):
        # Simulate the provider not being able to find the stack (a result of
        # it being successfully deleted)
        provider = mock.MagicMock()
        provider.get_stack.side_effect = StackDoesNotExist("mock")
        self.action.provider_builder = MockProviderBuilder(provider)
        status = self.action._destroy_stack(MockStack("vpc"), status=PENDING)
        # if we haven't processed the step (ie. has never been SUBMITTED,
        # should be skipped)
        self.assertEqual(status, SKIPPED)
        status = self.action._destroy_stack(MockStack("vpc"), status=SUBMITTED)
        # if we have processed the step and then can't find the stack, it means
        # we successfully deleted it
        self.assertEqual(status, COMPLETE)

    def test_destroy_stack_step_statuses(self):
        mock_provider = mock.MagicMock()
        stacks_dict = self.context.get_stacks_dict()

        def get_stack(stack_name):
            return stacks_dict.get(stack_name)

        plan = self.action._generate_plan()
        step = plan.steps[0]
        # we need the AWS provider to generate the plan, but swap it for
        # the mock one to make the test easier
        self.action.provider_builder = MockProviderBuilder(mock_provider)

        # simulate stack doesn't exist and we haven't submitted anything for
        # deletion
        mock_provider.get_stack.side_effect = StackDoesNotExist("mock")

        step.run()
        self.assertEqual(step.status, SKIPPED)

        # simulate stack getting successfully deleted
        mock_provider.get_stack.side_effect = get_stack
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = False

        step._run_once()
        self.assertEqual(step.status, SUBMITTED)
        mock_provider.is_stack_destroyed.return_value = False
        mock_provider.is_stack_in_progress.return_value = True

        step._run_once()
        self.assertEqual(step.status, SUBMITTED)
        mock_provider.is_stack_destroyed.return_value = True
        mock_provider.is_stack_in_progress.return_value = False

        step._run_once()
        self.assertEqual(step.status, COMPLETE)