def test_get_stack_order_raises_exception_on_cyclic_dependency(self):
        stacks = {
            'app1':
            StackConfig({
                'template-url': 'horst.yml',
                'parameters': {
                    'a': '|Ref|app2.id'
                }
            }),
            'app2':
            StackConfig({
                'template-url': 'horst.yml',
                'parameters': {
                    'a': '|Ref|app3.id'
                }
            }),
            'app3':
            StackConfig({
                'template-url': 'horst.yml',
                'parameters': {
                    'a': '|Ref|app1.id'
                }
            })
        }

        with self.assertRaises(CyclicDependencyException):
            DependencyResolver.get_stack_order(stacks)
Beispiel #2
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_get_stack_order_raises_exception_on_cyclic_dependency(self):
        stacks = {
            "app1": StackConfig({"template-url": "horst.yml", "parameters": {"a": "|Ref|app2.id"}}),
            "app2": StackConfig({"template-url": "horst.yml", "parameters": {"a": "|Ref|app3.id"}}),
            "app3": StackConfig({"template-url": "horst.yml", "parameters": {"a": "|Ref|app1.id"}}),
        }

        with self.assertRaises(CyclicDependencyException):
            DependencyResolver.get_stack_order(stacks)
    def test_get_stack_order_raises_exception_on_cyclic_dependency(self):
        stacks = {
            'app1': StackConfig({'template-url': 'horst.yml', 'parameters': {'a': '|Ref|app2.id'}}),
            'app2': StackConfig({'template-url': 'horst.yml', 'parameters': {'a': '|Ref|app3.id'}}),
            'app3': StackConfig({'template-url': 'horst.yml', 'parameters': {'a': '|Ref|app1.id'}})
        }

        with self.assertRaises(CyclicDependencyException):
            DependencyResolver.get_stack_order(stacks)
Beispiel #5
0
    def _transform_value(value, suffix, managed_stack_names):
        result = value

        if DependencyResolver.is_parameter_reference(value):
            stack_name, output_name = DependencyResolver.parse_stack_reference_value(
                value)
            if stack_name in managed_stack_names:
                result = "|ref|{0}{1}.{2}".format(stack_name, suffix,
                                                  output_name)

        return result
    def resolve_parameter_values(self, stack_name, stack_config, cli_parameters=None):
        resolved_parameters = {}
        stack_outputs = self.cfn.get_stacks_outputs()

        for key, value in stack_config.parameters.items():

            if isinstance(value, list):
                self.logger.debug("List parameter found for {0}".format(key))
                for i, item in enumerate(value):
                    if DependencyResolver.is_parameter_reference(item):
                        referenced_stack, output_name = DependencyResolver.parse_stack_reference_value(item)
                        value[i] = str(self.get_output_value(stack_outputs, referenced_stack, output_name))

                value_string = self.convert_list_to_string(value)
                resolved_parameters[key] = value_string

            elif isinstance(value, str):

                if DependencyResolver.is_parameter_reference(value):
                    referenced_stack, output_name = DependencyResolver.parse_stack_reference_value(value)
                    resolved_parameters[key] = str(self.get_output_value(stack_outputs, referenced_stack, output_name))

                elif self.is_keep_value(value):
                    resolved_parameters[key] = str(self.get_latest_value(key, value, stack_name))

                elif self.is_taupage_ami_reference(value):
                    resolved_parameters[key] = str(self.ec2.get_latest_taupage_image_id())

                elif self.is_kms(value):
                    resolved_parameters[key] = str(self.kms.decrypt(value.split('|', 2)[2]))

                elif self.is_file(value):
                    url = value.split('|', 2)[2]
                    resolved_parameters[key] = FileLoader.get_file(url, stack_config.working_dir)

                else:
                    resolved_parameters[key] = value

            elif isinstance(value, bool):
                resolved_parameters[key] = str(value).lower()
            elif isinstance(value, (int, float)):
                resolved_parameters[key] = str(value)
            else:
                raise NotImplementedError("Cannot handle {0} type for key: {1}".format(type(value), key))

        if cli_parameters:
            return self.update_parameters_with_cli_parameters(resolved_parameters, cli_parameters, stack_name)
        else:
            return resolved_parameters
Beispiel #7
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:
            if stack_name in existing_stacks:
                stack = CloudFormationStack(None, None, stack_name, None, None)
                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_get_stack_order_includes_independent_stacks(self):
        stacks = {
            'default-sg':
            StackConfig({'template-url': 'horst.yml'}),
            'app1':
            StackConfig({
                'template-url': 'horst.yml',
                'parameters': {
                    'a': '|Ref|vpc.id',
                    'b': '|Ref|default-sg.id'
                }
            }),
            'app2':
            StackConfig({
                'template-url': 'horst.yml',
                'parameters': {
                    'a': '|Ref|vpc.id',
                    'b': '|Ref|default-sg.id',
                    'c': 'Ref::app1.id'
                }
            }),
            'vpc':
            StackConfig({
                'template-url': 'horst.yml',
                'parameters': {
                    'logBucketName': 'is24-cloudtrail-logs',
                    'includeGlobalServices': False
                }
            })
        }

        result = 4

        self.assertEqual(result,
                         len(DependencyResolver.get_stack_order(stacks)))
    def test_filter_unmanaged_stacks(self):
        stacks = ['a', 'b', 'c']
        managed_stacks = ['a', 'c']

        self.assertListEqual(
            managed_stacks,
            DependencyResolver.filter_unmanaged_stacks(managed_stacks, stacks))
Beispiel #10
0
    def resolve_parameter_value(self, key, value, stack_name, stack_config,
                                stack_outputs):
        if isinstance(value, list):
            self.logger.debug("List parameter found for {0}".format(key))
            for i, item in enumerate(value):
                value[i] = self.resolve_parameter_value(
                    key, item, stack_name, stack_config, stack_outputs)

            return self.convert_list_to_string(value)

        elif isinstance(value, string_types):

            if DependencyResolver.is_parameter_reference(value):
                referenced_stack, output_name = DependencyResolver.parse_stack_reference_value(
                    value)
                return str(
                    self.get_output_value(stack_outputs, referenced_stack,
                                          output_name))

            elif self.is_keep_value(value):
                return str(self.get_latest_value(key, value, stack_name))

            elif self.is_taupage_ami_reference(value):
                return str(self.ec2.get_latest_taupage_image_id())

            elif self.is_kms(value):
                return self.handle_kms_value(value)

            elif self.is_ssm(value):
                return self.handle_ssm_value(value)

            elif self.is_file(value):
                return self.handle_file_value(value, stack_config.working_dir)

            else:
                return value

        elif isinstance(value, bool):
            return str(value).lower()
        elif isinstance(value, (int, float)):
            return str(value)
        elif not value:
            raise CfnSphereException(
                "Parameter {0} does not seem to have a value".format(key))
        else:
            raise NotImplementedError(
                "Cannot handle {0} type for key: {1}".format(type(value), key))
    def test_get_stack_order_accepts_stacks_without_parameters_key(self):
        stacks = {'default-sg': {},
                  'app1': None,
                  'app2': {},
                  'vpc': {},
                  }

        result = 4

        self.assertEqual(result, len(DependencyResolver.get_stack_order(stacks)))
    def test_get_stack_order_accepts_stacks_without_parameters_key(self):
        stacks = {'default-sg': {},
                  'app1': None,
                  'app2': {},
                  'vpc': {},
                  }

        result = 4

        self.assertEqual(result, len(DependencyResolver.get_stack_order(stacks)))
    def resolve_parameter_values(self, parameters_dict, stack_name):
        parameters = {}

        for key, value in parameters_dict.items():

            if isinstance(value, list):

                self.logger.debug("List parameter found for {0}".format(key))
                for i, item in enumerate(value):
                    if DependencyResolver.is_parameter_reference(item):
                        referenced_stack, output_name = DependencyResolver.parse_stack_reference_value(item)
                        value[i] = str(self.get_output_value(referenced_stack + '.' + output_name))

                value_string = self.convert_list_to_string(value)
                parameters[key] = value_string

            elif isinstance(value, str):

                if DependencyResolver.is_parameter_reference(value):
                    referenced_stack, output_name = DependencyResolver.parse_stack_reference_value(value)
                    parameters[key] = str(self.get_output_value(referenced_stack + '.' + output_name))

                elif self.is_keep_value(value):
                    parameters[key] = str(self.get_latest_value(key, value, stack_name))

                elif self.is_taupage_ami_reference(value):
                    parameters[key] = str(self.ec2.get_latest_taupage_image_id())

                elif self.is_kms(value):
                    parameters[key] = str(self.kms.decrypt(value.split('|', 2)[2]))

                else:
                    parameters[key] = value

            elif isinstance(value, bool):
                parameters[key] = str(value).lower()
            elif isinstance(value, (int, float)):
                parameters[key] = str(value)
            else:
                raise NotImplementedError("Cannot handle {0} value for key: {1}".format(type(value), key))

        return parameters
Beispiel #14
0
    def resolve_parameter_values(self, parameters_dict, stack_name):
        parameters = {}

        for key, value in parameters_dict.items():

            if isinstance(value, list):

                self.logger.debug("List parameter found for {0}".format(key))
                value_string = self.convert_list_to_string(value)
                parameters[key] = value_string

            elif isinstance(value, str):

                if DependencyResolver.is_parameter_reference(value):
                    referenced_stack, output_name = DependencyResolver.parse_stack_reference_value(value)
                    parameters[key] = str(self.get_output_value(referenced_stack + '.' + output_name))

                elif self.is_keep_value(value):
                    parameters[key] = str(self.get_latest_value(key, value, stack_name))

                elif self.is_taupage_ami_reference(value):
                    parameters[key] = str(self.ec2.get_latest_taupage_image_id())

                elif self.is_kms(value):
                    parameters[key] = str(self.kms.decrypt(value.split('|', 2)[2]))

                else:
                    parameters[key] = value

            elif isinstance(value, bool):
                parameters[key] = str(value).lower()
            elif isinstance(value, int):
                parameters[key] = str(value)
            elif isinstance(value, float):
                parameters[key] = str(value)
            else:
                raise NotImplementedError("Cannot handle {0} value for key: {1}".format(type(value), key))

        return parameters
    def test_get_stack_order_returns_a_valid_order_from_ref_in_list(self):
        stacks = {'default-sg': StackConfig({'template-url': 'horst.yml', 'parameters': {'a': ['|Ref|vpc.id']}}),
                  'app1': StackConfig(
                      {'template-url': 'horst.yml', 'parameters': {'a': ['|Ref|vpc.id'], 'b': ['|Ref|default-sg.id']}}),
                  'app2': StackConfig({'template-url': 'horst.yml',
                                       'parameters': {'a': ['|Ref|vpc.id'], 'b': ['|Ref|default-sg.id'],
                                                      'c': ['|Ref|app1.id']}}),
                  'vpc': StackConfig({'template-url': 'horst.yml',
                                      'parameters': {'logBucketName': 'is24-cloudtrail-logs',
                                                     'includeGlobalServices': False}})
                  }

        expected = ['vpc', 'default-sg', 'app1', 'app2']

        self.assertEqual(expected, DependencyResolver.get_stack_order(stacks))
    def test_get_stack_order_includes_independent_stacks(self):
        stacks = {'default-sg': StackConfig({'template-url': 'horst.yml'}),
                  'app1': StackConfig(
                      {'template-url': 'horst.yml', 'parameters': {'a': '|Ref|vpc.id', 'b': '|Ref|default-sg.id'}}),
                  'app2': StackConfig({'template-url': 'horst.yml',
                                       'parameters': {'a': '|Ref|vpc.id', 'b': '|Ref|default-sg.id',
                                                      'c': 'Ref::app1.id'}}),
                  'vpc': StackConfig({'template-url': 'horst.yml',
                                      'parameters': {'logBucketName': 'is24-cloudtrail-logs',
                                                     'includeGlobalServices': False}})
                  }

        result = 4

        self.assertEqual(result, len(DependencyResolver.get_stack_order(stacks)))
    def test_get_stack_order_returns_a_valid_order(self):
        stacks = {'default-sg': StackConfig({'template-url': 'horst.yml', 'parameters': {'a': '|Ref|vpc.id'}}),
                  'app1': StackConfig(
                      {'template-url': 'horst.yml', 'parameters': {'a': '|Ref|vpc.id', 'b': '|Ref|default-sg.id'}}),
                  'app2': StackConfig({'template-url': 'horst.yml',
                                       'parameters': {'a': '|Ref|vpc.id', 'b': '|Ref|default-sg.id',
                                                      'c': '|Ref|app1.id'}}),
                  'vpc': StackConfig({'template-url': 'horst.yml',
                                      'parameters': {'logBucketName': 'is24-cloudtrail-logs',
                                                     'includeGlobalServices': False}})
                  }

        expected = ['vpc', 'default-sg', 'app1', 'app2']

        self.assertEqual(expected, DependencyResolver.get_stack_order(stacks))
Beispiel #18
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)
    def test_get_stack_order_returns_a_valid_order(self):
        stacks = {
            "default-sg": StackConfig({"template-url": "horst.yml", "parameters": {"a": "|Ref|vpc.id"}}),
            "app1": StackConfig(
                {"template-url": "horst.yml", "parameters": {"a": "|Ref|vpc.id", "b": "|Ref|default-sg.id"}}
            ),
            "app2": StackConfig(
                {
                    "template-url": "horst.yml",
                    "parameters": {"a": "|Ref|vpc.id", "b": "|Ref|default-sg.id", "c": "|Ref|app1.id"},
                }
            ),
            "vpc": StackConfig(
                {
                    "template-url": "horst.yml",
                    "parameters": {"logBucketName": "is24-cloudtrail-logs", "includeGlobalServices": False},
                }
            ),
        }

        expected = ["vpc", "default-sg", "app1", "app2"]

        self.assertEqual(expected, DependencyResolver.get_stack_order(stacks))
    def test_get_stack_order_includes_independent_stacks(self):
        stacks = {
            "default-sg": StackConfig({"template-url": "horst.yml"}),
            "app1": StackConfig(
                {"template-url": "horst.yml", "parameters": {"a": "|Ref|vpc.id", "b": "|Ref|default-sg.id"}}
            ),
            "app2": StackConfig(
                {
                    "template-url": "horst.yml",
                    "parameters": {"a": "|Ref|vpc.id", "b": "|Ref|default-sg.id", "c": "Ref::app1.id"},
                }
            ),
            "vpc": StackConfig(
                {
                    "template-url": "horst.yml",
                    "parameters": {"logBucketName": "is24-cloudtrail-logs", "includeGlobalServices": False},
                }
            ),
        }

        result = 4

        self.assertEqual(result, len(DependencyResolver.get_stack_order(stacks)))
Beispiel #21
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)
 def test_is_parameter_reference_returns_true_for_lowercase_ref(self):
     self.assertTrue(
         DependencyResolver.is_parameter_reference("|ref|vpc.id"))
 def test_parse_stack_reference_raises_exception_on_empty_reference(self):
     with self.assertRaises(CfnSphereException):
         DependencyResolver.parse_stack_reference_value('|ref|')
    def test_get_stack_order_accepts_stacks_without_parameters_key(self):
        stacks = {"default-sg": {}, "app1": None, "app2": {}, "vpc": {}}

        result = 4

        self.assertEqual(result, len(DependencyResolver.get_stack_order(stacks)))
 def test_parse_stack_reference_value_returns_stack_and_output_name_tuple(self):
     self.assertEqual(("stack", "output"), DependencyResolver.parse_stack_reference_value("|ref|stack.output"))
Beispiel #26
0
    def resolve_parameter_values(self,
                                 stack_name,
                                 stack_config,
                                 cli_parameters=None):
        resolved_parameters = {}
        stack_outputs = self.cfn.get_stacks_outputs()

        for key, value in stack_config.parameters.items():

            if isinstance(value, list):
                self.logger.debug("List parameter found for {0}".format(key))
                for i, item in enumerate(value):
                    if DependencyResolver.is_parameter_reference(item):
                        referenced_stack, output_name = DependencyResolver.parse_stack_reference_value(
                            item)
                        value[i] = str(
                            self.get_output_value(stack_outputs,
                                                  referenced_stack,
                                                  output_name))

                value_string = self.convert_list_to_string(value)
                resolved_parameters[key] = value_string

            elif isinstance(value, str):

                if DependencyResolver.is_parameter_reference(value):
                    referenced_stack, output_name = DependencyResolver.parse_stack_reference_value(
                        value)
                    resolved_parameters[key] = str(
                        self.get_output_value(stack_outputs, referenced_stack,
                                              output_name))

                elif self.is_keep_value(value):
                    resolved_parameters[key] = str(
                        self.get_latest_value(key, value, stack_name))

                elif self.is_taupage_ami_reference(value):
                    resolved_parameters[key] = str(
                        self.ec2.get_latest_taupage_image_id())

                elif self.is_kms(value):
                    resolved_parameters[key] = str(
                        self.kms.decrypt(value.split('|', 2)[2]))

                elif self.is_file(value):
                    resolved_parameters[key] = self.handle_file_value(
                        value, stack_config.working_dir)

                else:
                    resolved_parameters[key] = value

            elif isinstance(value, bool):
                resolved_parameters[key] = str(value).lower()
            elif isinstance(value, (int, float)):
                resolved_parameters[key] = str(value)
            else:
                raise NotImplementedError(
                    "Cannot handle {0} type for key: {1}".format(
                        type(value), key))

        if cli_parameters:
            return self.update_parameters_with_cli_parameters(
                resolved_parameters, cli_parameters, stack_name)
        else:
            return resolved_parameters
    def test_filter_unmanaged_stacks(self):
        stacks = ["a", "b", "c"]
        managed_stacks = ["a", "c"]

        self.assertListEqual(managed_stacks, DependencyResolver.filter_unmanaged_stacks(managed_stacks, stacks))
    def test_filter_unmanaged_stacks(self):
        stacks = ['a', 'b', 'c']
        managed_stacks = ['a', 'c']

        self.assertListEqual(managed_stacks, DependencyResolver.filter_unmanaged_stacks(managed_stacks, stacks))
 def test_parse_stack_reference_value_returns_none_for_non_reference(self):
     self.assertEqual((None, None), DependencyResolver.parse_stack_reference_value('foo'))
 def test_is_parameter_reference_returns_true_for_uppercase_ref(self):
     self.assertTrue(DependencyResolver.is_parameter_reference("|Ref|vpc.id"))
 def test_parse_stack_reference_value_returns_stack_and_output_name_tuple(self):
     self.assertEqual(('stack', 'output'), DependencyResolver.parse_stack_reference_value('|ref|stack.output'))
 def test_parse_stack_reference_raises_exception_on_missing_dot(self):
     with self.assertRaises(CfnSphereException):
         DependencyResolver.parse_stack_reference_value('|ref|foo')
 def test_parse_stack_reference_value_returns_none_for_non_reference(self):
     self.assertEqual((None, None),
                      DependencyResolver.parse_stack_reference_value('foo'))
 def test_parse_stack_reference_raises_exception_on_missing_dot(self):
     with self.assertRaises(CfnSphereException):
         DependencyResolver.parse_stack_reference_value('|ref|foo')
 def test_is_parameter_reference_returns_false_for_single_separator(self):
     self.assertFalse(
         DependencyResolver.is_parameter_reference("Ref|vpc.id"))
 def test_is_parameter_reference_returns_false_for_boolean_values(self):
     self.assertFalse(DependencyResolver.is_parameter_reference(True))
 def test_parse_stack_reference_value_returns_stack_and_output_name_tuple(
         self):
     self.assertEqual(('stack', 'output'),
                      DependencyResolver.parse_stack_reference_value(
                          '|ref|stack.output'))
 def test_is_parameter_reference_returns_false_for_single_separator(self):
     self.assertFalse(DependencyResolver.is_parameter_reference("Ref|vpc.id"))
 def test_parse_stack_reference_raises_exception_on_empty_reference(self):
     with self.assertRaises(CfnSphereException):
         DependencyResolver.parse_stack_reference_value('|ref|')
 def test_is_parameter_reference_returns_false_for_simple_string(self):
     self.assertFalse(DependencyResolver.is_parameter_reference("vpc.id"))
 def test_is_parameter_reference_returns_false_for_simple_string(self):
     self.assertFalse(DependencyResolver.is_parameter_reference("vpc.id"))
 def test_is_parameter_reference_returns_false_for_boolean_values(self):
     self.assertFalse(DependencyResolver.is_parameter_reference(True))
 def test_is_parameter_reference_returns_true_on_empty_reference(self):
     self.assertTrue(DependencyResolver.is_parameter_reference('|ref|'))
 def test_is_parameter_reference_returns_true_on_empty_reference(self):
     self.assertTrue(DependencyResolver.is_parameter_reference('|ref|'))