def test_delete_stacks_uses_the_correct_order(
            self, custom_resource_mock, stack_mock, template_loader_mock,
            dependency_resolver_mock, parameter_resolver_mock, cfn_mock):

        dependency_resolver_mock.return_value.get_stack_order.return_value = [
            'a', 'c'
        ]
        cfn_mock.return_value.get_stack_names.return_value = ['a', 'c']

        stack_a = CloudFormationStack('', [], 'a', '')
        stack_c = CloudFormationStack('', [], 'c', '')

        def stack_side_effect(*args, **kwargs):
            if args[2] == 'a':
                return stack_a
            if args[2] == 'c':
                return stack_c
            return None

        stack_mock.side_effect = stack_side_effect

        handler = StackActionHandler(Mock())
        handler.delete_stacks()

        expected_calls = [call(stack_c), call(stack_a)]
        six.assertCountEqual(self, expected_calls,
                             cfn_mock.return_value.delete_stack.mock_calls)
    def test_create_or_update_tests_exits_gracefully_if_preexisting_stack_disappears(
            self, custom_resource_mock, stack_mock, template_loader_mock,
            dependency_resolver_mock, parameter_resolver_mock, cfn_mock):

        dependency_resolver_mock.return_value.get_stack_order.return_value = [
            'a', 'c'
        ]
        cfn_mock.return_value.get_stack_names.return_value = ['a', 'd']

        stack_a = CloudFormationStack('', [], 'a', '')
        stack_c = CloudFormationStack('', [], 'c', '')

        def stack_side_effect(*args, **kwargs):
            if kwargs['name'] == 'a':
                return stack_a
            if kwargs['name'] == 'c':
                return stack_c
            return None

        stack_mock.side_effect = stack_side_effect

        handler = StackActionHandler(Mock())
        handler.create_or_update_stacks()

        # stack a needs update
        cfn_mock.return_value.validate_stack_is_ready_for_action.assert_called_once_with(
            stack_a)
        cfn_mock.return_value.update_stack.assert_called_once_with(stack_a)

        # stack c doesn't exist, must be created
        cfn_mock.return_value.create_stack.assert_called_once_with(stack_c)
    def test_delete_stacks(self, custom_resource_mock, stack_mock,
                           template_loader_mock, dependency_resolver_mock,
                           parameter_resolver_mock, cfn_mock):

        dependency_resolver_mock.return_value.get_stack_order.return_value = [
            'a', 'c'
        ]
        cfn_mock.return_value.get_stack_names.return_value = ['a', 'd']

        stack_a = CloudFormationStack('', [], 'a', '')
        stack_c = CloudFormationStack('', [], 'c', '')

        def stack_side_effect(*args, **kwargs):
            if args[2] == 'a':
                return stack_a
            if args[2] == 'c':
                return stack_c
            return None

        stack_mock.side_effect = stack_side_effect

        handler = StackActionHandler(Mock())
        handler.delete_stacks()

        cfn_mock.return_value.delete_stack.assert_called_once_with(stack_a)
Esempio n. 4
0
    def delete_stacks(self):
        existing_stacks = self.cfn.get_stack_names()
        stacks = self.config.stacks

        stack_processing_order = DependencyResolver().get_stack_order(stacks)
        stack_processing_order.reverse()

        self.logger.info(
            "Will delete stacks in the following order: {0}".format(
                ", ".join(stack_processing_order)))

        for stack_name in stack_processing_order:
            stack_config = self.config.stacks.get(stack_name)

            if stack_name in existing_stacks:
                stack = CloudFormationStack(
                    None,
                    None,
                    stack_name,
                    None,
                    None,
                    service_role=stack_config.service_role)
                self.cfn.validate_stack_is_ready_for_action(stack)
                self.cfn.delete_stack(stack)
            else:
                self.logger.info(
                    "Stack {0} is already deleted".format(stack_name))
    def test_process_post_resources(self, handle_sns_subscriptions_mock):
        custom_resource_yaml = dedent("""
        PostCustomResources:
            exposeTopicSubscription:
                Type: "Custom::SNS::Subscription"
                Properties:
                    TopicArn:
                      Ref: exposeTopicArn
                    QueueResourceName: exposeQueue
        """)

        custom_resource_dict = yaml.load(custom_resource_yaml)
        template = CloudFormationTemplate(custom_resource_dict, 'foo')
        stack = CloudFormationStack(template, {}, 'foo', 'foo-region')

        CustomResourceHandler.process_post_resources(stack)
        handle_sns_subscriptions_mock.assert_called_once_with(
            {
                'Type': 'Custom::SNS::Subscription',
                'Properties': {
                    'TopicArn': {
                        'Ref': 'exposeTopicArn'
                    },
                    'QueueResourceName': 'exposeQueue'
                }
            }, stack)
Esempio n. 6
0
    def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_rollback_complete_state(self, get_stack_mock):
        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "ROLLBACK_COMPLETE"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        cfn.validate_stack_is_ready_for_action(stack)
Esempio n. 7
0
    def test_validate_stack_is_ready_for_action_raises_exception_on_create_in_progress(self, get_stack_mock):
        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "CREATE_IN_PROGRESS"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnStackActionFailedException):
            cfn.validate_stack_is_ready_for_action(stack)
Esempio n. 8
0
    def test_validate_stack_is_ready_for_action_raises_exception_on_unknown_stack_state(self, get_stack_mock):
        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "FOO"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnStackActionFailedException):
            cfn.validate_stack_is_ready_for_action(stack)
Esempio n. 9
0
    def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error(self, get_stack_mock):
        get_stack_mock.side_effect = CfnSphereBotoError(None)

        stack_mock = Mock()
        stack_mock.stack_name = "my-stack"
        stack_mock.stack_status = "UPDATE_COMPLETE"
        get_stack_mock.return_value = stack_mock

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnSphereBotoError):
            cfn.validate_stack_is_ready_for_action(stack)
Esempio n. 10
0
    def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error(
            self, cloudformation_mock):
        cloudformation_mock.return_value.describe_stacks.side_effect = BotoServerError(
            '400', 'Bad Request')

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnSphereBotoError):
            cfn.validate_stack_is_ready_for_action(stack)

        cloudformation_mock.return_value.describe_stacks.assert_called_once_with(
            'my-stack')
Esempio n. 11
0
    def create_or_update_stacks(self):
        existing_stacks = self.cfn.get_stack_names()
        desired_stacks = self.config.stacks
        stack_processing_order = DependencyResolver().get_stack_order(
            desired_stacks)

        if len(stack_processing_order) > 1:
            self.logger.info(
                "Will process stacks in the following order: {0}".format(
                    ", ".join(stack_processing_order)))

        for stack_name in stack_processing_order:
            stack_config = self.config.stacks.get(stack_name)

            if stack_config.stack_policy_url:
                self.logger.info("Using stack policy from {0}".format(
                    stack_config.stack_policy_url))
                stack_policy = FileLoader.get_yaml_or_json_file(
                    stack_config.stack_policy_url, stack_config.working_dir)
            else:
                stack_policy = None

            template = TemplateHandler.get_template(stack_config.template_url,
                                                    stack_config.working_dir)
            parameters = self.parameter_resolver.resolve_parameter_values(
                stack_name, stack_config, self.cli_parameters)

            stack = CloudFormationStack(
                template=template,
                parameters=parameters,
                tags=stack_config.tags,
                name=stack_name,
                region=self.config.region,
                timeout=stack_config.timeout,
                service_role=stack_config.service_role,
                stack_policy=stack_policy,
                failure_action=stack_config.failure_action)

            if stack_name in existing_stacks:

                self.cfn.validate_stack_is_ready_for_action(stack)
                self.cfn.update_stack(stack)
            else:
                self.cfn.create_stack(stack)

            CustomResourceHandler.process_post_resources(stack)
Esempio n. 12
0
    def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_good_state(
            self, cloudformation_mock):
        describe_stack_mock = Mock()
        describe_stack_mock.stack_status = "UPDATE_COMPLETE"
        describe_stack_mock.stack_name = "my-stack"

        cloudformation_mock.return_value.describe_stacks.return_value = [
            describe_stack_mock
        ]

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        cfn.validate_stack_is_ready_for_action(stack)

        cloudformation_mock.return_value.describe_stacks.assert_called_once_with(
            'my-stack')
Esempio n. 13
0
    def test_validate_stack_is_ready_for_action_raises_exception_on_bad_stack_state(
            self, cloudformation_mock):
        describe_stack_mock = Mock()
        describe_stack_mock.stack_status = "UPDATE_IN_PROGRESS"
        describe_stack_mock.stack_name = "my-stack"

        cloudformation_mock.return_value.describe_stacks.return_value = [
            describe_stack_mock
        ]

        stack = CloudFormationStack('', [], 'my-stack', 'my-region')

        cfn = CloudFormation()
        with self.assertRaises(CfnStackActionFailedException):
            cfn.validate_stack_is_ready_for_action(stack)

        cloudformation_mock.return_value.describe_stacks.assert_called_once_with(
            'my-stack')
Esempio n. 14
0
    def create_or_update_stacks(self):
        existing_stacks = self.cfn.get_stack_names()
        desired_stacks = self.config.stacks
        stack_processing_order = DependencyResolver().get_stack_order(
            desired_stacks)

        if len(stack_processing_order) > 1:
            self.logger.info(
                "Will process stacks in the following order: {0}".format(
                    ", ".join(stack_processing_order)))

        for stack_name in stack_processing_order:
            stack_config = self.config.stacks.get(stack_name)

            raw_template = FileLoader.get_file_from_url(
                stack_config.template_url, stack_config.working_dir)
            template = CloudFormationTemplateTransformer.transform_template(
                raw_template)

            parameters = self.parameter_resolver.resolve_parameter_values(
                stack_config.parameters, stack_name)
            stack = CloudFormationStack(template=template,
                                        parameters=parameters,
                                        tags=(stack_config.tags),
                                        name=stack_name,
                                        region=self.region,
                                        timeout=stack_config.timeout)

            if stack_name in existing_stacks:

                self.cfn.validate_stack_is_ready_for_action(stack)
                self.cfn.update_stack(stack)
            else:
                self.cfn.create_stack(stack)

            CustomResourceHandler.process_post_resources(stack)