def setUp(self):
     self.ref = FindInMapAction()
class TestFindInMapCanResolveParameterRefs(TestCase):

    def setUp(self):
        self.ref = FindInMapAction()

    @patch.object(FindInMapAction, "can_handle")
    def test_cannot_handle(self, can_handle_mock):
        input = {
            "Fn::FindInMap": ["a", "b", "c"]
        }
        can_handle_mock.return_value = False
        output = self.ref.resolve_parameter_refs(input, {})

        self.assertEqual(input, output)

    def test_value_not_list(self):
        input = {
            "Fn::FindInMap": "a string"
        }
        with self.assertRaises(InvalidDocumentException):
            self.ref.resolve_parameter_refs(input, {})

    def test_value_not_list_of_length_three(self):
        input = {
            "Fn::FindInMap": ["a string"]
        }

        with self.assertRaises(InvalidDocumentException):
            self.ref.resolve_parameter_refs(input, {})

    def test_mapping_not_string(self):
        mappings = {
            "MapA":{
                "TopKey1": {
                   "SecondKey2": "value3"
               },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {
            "Fn::FindInMap": [["MapA"], "TopKey2", "SecondKey1"]
        }
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_top_level_key_not_string(self):
        mappings = {
            "MapA":{
                "TopKey1": {
                   "SecondKey2": "value3"
               },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {
            "Fn::FindInMap": ["MapA", ["TopKey2"], "SecondKey1"]
        }
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_second_level_key_not_string(self):
        mappings = {
            "MapA":{
                "TopKey1": {
                   "SecondKey2": "value3"
               },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {
            "Fn::FindInMap": ["MapA", "TopKey1", ["SecondKey2"]]
        }
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_mapping_not_found(self):
        mappings = {
            "MapA":{
                "TopKey1": {
                   "SecondKey2": "value3"
               },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {
            "Fn::FindInMap": ["MapB", "TopKey2", "SecondKey1"]
        }
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_top_level_key_not_found(self):
        mappings = {
            "MapA":{
                "TopKey1": {
                   "SecondKey2": "value3"
               },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {
            "Fn::FindInMap": ["MapA", "TopKey3", "SecondKey1"]
        }
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_second_level_key_not_found(self):
        mappings = {
            "MapA":{
                "TopKey1": {
                   "SecondKey2": "value3"
               },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {
            "Fn::FindInMap": ["MapA", "TopKey1", "SecondKey1"]
        }
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_one_level_find_in_mappings(self):
        mappings = {
            "MapA":{
                "TopKey1": {
                   "SecondKey2": "value3"
               },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {
            "Fn::FindInMap": ["MapA", "TopKey2", "SecondKey1"]
        }
        expected = "value4"
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(expected, output)

    def test_nested_find_in_mappings(self):
        mappings = {
            "MapA":{
                "TopKey1": {
                   "SecondKey2": "value3"
               },
                "TopKey2": {
                    "SecondKey1": "TopKey1"
                }
            }
        }
        input = {
            "Fn::FindInMap": ["MapA", {"Fn::FindInMap": ["MapA", "TopKey2", "SecondKey1"]}, "SecondKey2"]
        }
        expected = "value3"
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(expected, output)

    def test_nested_find_in_multiple_mappings(self):
        mappings = {
            "MapA":{
                "ATopKey1": {
                   "ASecondKey2": "value3"
               }
            },
            "MapB": {
                "BTopKey1": {
                    "BSecondKey2": "ATopKey1"
                }
            }
        }
        input = {
            "Fn::FindInMap": ["MapA", {"Fn::FindInMap": ["MapB", "BTopKey1", "BSecondKey2"]}, "ASecondKey2"]
        }
        expected = "value3"
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(expected, output)
    def translate(self, sam_template, parameter_values, feature_toggle=None):
        """Loads the SAM resources from the given SAM manifest, replaces them with their corresponding
        CloudFormation resources, and returns the resulting CloudFormation template.

        :param dict sam_template: the SAM manifest, as loaded by json.load() or yaml.load(), or as provided by \
                CloudFormation transforms.
        :param dict parameter_values: Map of template parameter names to their values. It is a required parameter that
                should at least be an empty map. By providing an empty map, the caller explicitly opts-into the idea
                that some functionality that relies on resolving parameter references might not work as expected
                (ex: auto-creating new Lambda Version when CodeUri contains reference to template parameter). This is
                why this parameter is required

        :returns: a copy of the template with SAM resources replaced with the corresponding CloudFormation, which may \
                be dumped into a valid CloudFormation JSON or YAML template
        """
        self.feature_toggle = (feature_toggle
                               if feature_toggle else FeatureToggle(
                                   FeatureToggleDefaultConfigProvider(),
                                   stage=None,
                                   account_id=None,
                                   region=None))
        self.function_names = dict()
        self.redeploy_restapi_parameters = dict()
        sam_parameter_values = SamParameterValues(parameter_values)
        sam_parameter_values.add_default_parameter_values(sam_template)
        sam_parameter_values.add_pseudo_parameter_values(self.boto_session)
        parameter_values = sam_parameter_values.parameter_values
        # Create & Install plugins
        sam_plugins = prepare_plugins(self.plugins, parameter_values)

        self.sam_parser.parse(sam_template=sam_template,
                              parameter_values=parameter_values,
                              sam_plugins=sam_plugins)

        template = copy.deepcopy(sam_template)
        macro_resolver = ResourceTypeResolver(sam_resources)
        intrinsics_resolver = IntrinsicsResolver(parameter_values)
        mappings_resolver = IntrinsicsResolver(
            template.get("Mappings", {}),
            {FindInMapAction.intrinsic_name: FindInMapAction()})
        deployment_preference_collection = DeploymentPreferenceCollection()
        supported_resource_refs = SupportedResourceReferences()
        shared_api_usage_plan = SharedApiUsagePlan()
        document_errors = []
        changed_logical_ids = {}
        for logical_id, resource_dict in self._get_resources_to_iterate(
                sam_template, macro_resolver):
            try:
                macro = macro_resolver.resolve_resource_type(
                    resource_dict).from_dict(logical_id,
                                             resource_dict,
                                             sam_plugins=sam_plugins)

                kwargs = macro.resources_to_link(sam_template["Resources"])
                kwargs["managed_policy_map"] = self.managed_policy_map
                kwargs["intrinsics_resolver"] = intrinsics_resolver
                kwargs["mappings_resolver"] = mappings_resolver
                kwargs[
                    "deployment_preference_collection"] = deployment_preference_collection
                kwargs["conditions"] = template.get("Conditions")
                # add the value of FunctionName property if the function is referenced with the api resource
                self.redeploy_restapi_parameters[
                    "function_names"] = self._get_function_names(
                        resource_dict, intrinsics_resolver)
                kwargs[
                    "redeploy_restapi_parameters"] = self.redeploy_restapi_parameters
                kwargs["shared_api_usage_plan"] = shared_api_usage_plan
                translated = macro.to_cloudformation(**kwargs)

                supported_resource_refs = macro.get_resource_references(
                    translated, supported_resource_refs)

                # Some resources mutate their logical ids. Track those to change all references to them:
                if logical_id != macro.logical_id:
                    changed_logical_ids[logical_id] = macro.logical_id

                del template["Resources"][logical_id]
                for resource in translated:
                    if verify_unique_logical_id(resource,
                                                sam_template["Resources"]):
                        template["Resources"].update(resource.to_dict())
                    else:
                        document_errors.append(
                            DuplicateLogicalIdException(
                                logical_id, resource.logical_id,
                                resource.resource_type))
            except (InvalidResourceException, InvalidEventException) as e:
                document_errors.append(e)

        if deployment_preference_collection.any_enabled():
            template["Resources"].update(deployment_preference_collection.
                                         codedeploy_application.to_dict())

            if not deployment_preference_collection.can_skip_service_role():
                template["Resources"].update(deployment_preference_collection.
                                             codedeploy_iam_role.to_dict())

            for logical_id in deployment_preference_collection.enabled_logical_ids(
            ):
                try:
                    template["Resources"].update(
                        deployment_preference_collection.deployment_group(
                            logical_id).to_dict())
                except InvalidResourceException as e:
                    document_errors.append(e)

        # Run the after-transform plugin target
        try:
            sam_plugins.act(LifeCycleEvents.after_transform_template, template)
        except (InvalidDocumentException, InvalidResourceException) as e:
            document_errors.append(e)

        # Cleanup
        if "Transform" in template:
            del template["Transform"]

        if len(document_errors) == 0:
            template = intrinsics_resolver.resolve_sam_resource_id_refs(
                template, changed_logical_ids)
            template = intrinsics_resolver.resolve_sam_resource_refs(
                template, supported_resource_refs)
            return template
        else:
            raise InvalidDocumentException(document_errors)
 def _get_intrinsic_resolvers(self, mappings):
     return [
         IntrinsicsResolver(self._parameters),
         IntrinsicsResolver(
             mappings, {FindInMapAction.intrinsic_name: FindInMapAction()}),
     ]
Beispiel #5
0
 def setUp(self):
     self.ref = FindInMapAction()
Beispiel #6
0
class TestFindInMapCanResolveParameterRefs(TestCase):
    def setUp(self):
        self.ref = FindInMapAction()

    @patch.object(FindInMapAction, "can_handle")
    def test_cannot_handle(self, can_handle_mock):
        input = {"Fn::FindInMap": ["a", "b", "c"]}
        can_handle_mock.return_value = False
        output = self.ref.resolve_parameter_refs(input, {})

        self.assertEqual(input, output)

    def test_value_not_list(self):
        input = {"Fn::FindInMap": "a string"}
        with self.assertRaises(InvalidDocumentException):
            self.ref.resolve_parameter_refs(input, {})

    def test_value_not_list_of_length_three(self):
        input = {"Fn::FindInMap": ["a string"]}

        with self.assertRaises(InvalidDocumentException):
            self.ref.resolve_parameter_refs(input, {})

    def test_mapping_not_string(self):
        mappings = {
            "MapA": {
                "TopKey1": {
                    "SecondKey2": "value3"
                },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {"Fn::FindInMap": [["MapA"], "TopKey2", "SecondKey1"]}
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_top_level_key_not_string(self):
        mappings = {
            "MapA": {
                "TopKey1": {
                    "SecondKey2": "value3"
                },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {"Fn::FindInMap": ["MapA", ["TopKey2"], "SecondKey1"]}
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_second_level_key_not_string(self):
        mappings = {
            "MapA": {
                "TopKey1": {
                    "SecondKey2": "value3"
                },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {"Fn::FindInMap": ["MapA", "TopKey1", ["SecondKey2"]]}
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_mapping_not_found(self):
        mappings = {
            "MapA": {
                "TopKey1": {
                    "SecondKey2": "value3"
                },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {"Fn::FindInMap": ["MapB", "TopKey2", "SecondKey1"]}
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_top_level_key_not_found(self):
        mappings = {
            "MapA": {
                "TopKey1": {
                    "SecondKey2": "value3"
                },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {"Fn::FindInMap": ["MapA", "TopKey3", "SecondKey1"]}
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_second_level_key_not_found(self):
        mappings = {
            "MapA": {
                "TopKey1": {
                    "SecondKey2": "value3"
                },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {"Fn::FindInMap": ["MapA", "TopKey1", "SecondKey1"]}
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(input, output)

    def test_one_level_find_in_mappings(self):
        mappings = {
            "MapA": {
                "TopKey1": {
                    "SecondKey2": "value3"
                },
                "TopKey2": {
                    "SecondKey1": "value4"
                }
            }
        }
        input = {"Fn::FindInMap": ["MapA", "TopKey2", "SecondKey1"]}
        expected = "value4"
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(expected, output)

    def test_nested_find_in_mappings(self):
        mappings = {
            "MapA": {
                "TopKey1": {
                    "SecondKey2": "value3"
                },
                "TopKey2": {
                    "SecondKey1": "TopKey1"
                }
            }
        }
        input = {
            "Fn::FindInMap": [
                "MapA", {
                    "Fn::FindInMap": ["MapA", "TopKey2", "SecondKey1"]
                }, "SecondKey2"
            ]
        }
        expected = "value3"
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(expected, output)

    def test_nested_find_in_multiple_mappings(self):
        mappings = {
            "MapA": {
                "ATopKey1": {
                    "ASecondKey2": "value3"
                }
            },
            "MapB": {
                "BTopKey1": {
                    "BSecondKey2": "ATopKey1"
                }
            }
        }
        input = {
            "Fn::FindInMap": [
                "MapA", {
                    "Fn::FindInMap": ["MapB", "BTopKey1", "BSecondKey2"]
                }, "ASecondKey2"
            ]
        }
        expected = "value3"
        output = self.ref.resolve_parameter_refs(input, mappings)

        self.assertEqual(expected, output)