def outputs(self):
     result = []
     # first, fetch the outputs of nested child stacks
     for stack in self.nested_stacks:
         result.extend(stack.outputs)
     # now, fetch the outputs of this stack
     for k, details in self.template.get('Outputs', {}).items():
         value = None
         try:
             template_deployer.resolve_refs_recursively(
                 self.stack_name, details, self.resources)
             value = details['Value']
         except Exception as e:
             LOG.debug(
                 'Unable to resolve references in stack outputs: %s - %s' %
                 (details, e))
         exports = details.get('Export') or {}
         export = exports.get('Name')
         description = details.get('Description')
         entry = {
             'OutputKey': k,
             'OutputValue': value,
             'Description': description,
             'ExportName': export
         }
         result.append(entry)
     return result
Example #2
0
 def outputs(self):
     result = []
     # first, fetch the outputs of nested child stacks
     for stack in self.nested_stacks:
         result.extend(stack.outputs)
     # now, fetch the outputs of this stack
     for k, details in self.template.get("Outputs", {}).items():
         value = None
         try:
             template_deployer.resolve_refs_recursively(
                 self.stack_name, details, self.resources)
             value = details["Value"]
         except Exception as e:
             LOG.debug(
                 "Unable to resolve references in stack outputs: %s - %s" %
                 (details, e))
         exports = details.get("Export") or {}
         export = exports.get("Name")
         export = template_deployer.resolve_refs_recursively(
             self.stack_name, export, self.resources)
         description = details.get("Description")
         entry = {
             "OutputKey": k,
             "OutputValue": value,
             "Description": description,
             "ExportName": export,
         }
         result.append(entry)
     return result
Example #3
0
 def outputs_list(self) -> List[Dict]:
     """Returns a copy of the outputs of this stack."""
     result = []
     # first, fetch the outputs of nested child stacks
     for stack in self.nested_stacks:
         result.extend(stack.outputs_list())
     # now, fetch the outputs of this stack
     for k, details in self.outputs.items():
         value = None
         try:
             template_deployer.resolve_refs_recursively(self, details)
             value = details["Value"]
         except Exception as e:
             LOG.debug("Unable to resolve references in stack outputs: %s - %s", details, e)
         exports = details.get("Export") or {}
         export = exports.get("Name")
         export = template_deployer.resolve_refs_recursively(self, export)
         description = details.get("Description")
         entry = {
             "OutputKey": k,
             "OutputValue": value,
             "Description": description,
             "ExportName": export,
         }
         result.append(entry)
     return result
 def outputs(self):
     result = []
     for k, details in self.template.get('Outputs', {}).items():
         template_deployer.resolve_refs_recursively(self.stack_name, details, self.resources)
         export = details.get('Export', {}).get('Name')
         description = details.get('Description')
         entry = {'OutputKey': k, 'OutputValue': details['Value'], 'Description': description, 'ExportName': export}
         result.append(entry)
     return result
    def update_resource_id(resource, new_id, props, region_name, stack_name,
                           resource_map):
        """ Update and fix the ID(s) of the given resource. """

        # NOTE: this is a bit of a hack, which is required because
        # of the order of events when CloudFormation resources are created.
        # When we process a request to create a CF resource that's part of a
        # stack, say, an API Gateway Resource, then we (1) create the object
        # in memory in moto, which generates a random ID for the resource, and
        # (2) create the actual resource in the backend service using
        # template_deployer.deploy_resource(..) (see above).
        # The resource created in (2) now has a different ID than the resource
        # created in (1), which leads to downstream problems. Hence, we need
        # the logic below to reconcile the ids, i.e., apply IDs from (2) to (1).

        backend = apigw_models.apigateway_backends[region_name]
        if isinstance(resource, apigw_models.RestAPI):
            backend.apis.pop(resource.id, None)
            backend.apis[new_id] = resource
            # We also need to fetch the resources to replace the root resource
            # that moto automatically adds to newly created RestAPI objects
            client = aws_stack.connect_to_service('apigateway')
            resources = client.get_resources(restApiId=new_id,
                                             limit=500)['items']
            # make sure no resources have been added in addition to the root /
            assert len(resource.resources) == 1
            resource.resources = {}
            for res in resources:
                res_path_part = res.get('pathPart') or res.get('path')
                child = resource.add_child(res_path_part, res.get('parentId'))
                resource.resources.pop(child.id)
                child.id = res['id']
                child.api_id = new_id
                resource.resources[child.id] = child
            resource.id = new_id
        elif isinstance(resource, apigw_models.Resource):
            api_id = props['RestApiId']
            api_id = template_deployer.resolve_refs_recursively(
                stack_name, api_id, resource_map)
            backend.apis[api_id].resources.pop(resource.id, None)
            backend.apis[api_id].resources[new_id] = resource
            resource.id = new_id
        elif isinstance(resource, apigw_models.Deployment):
            api_id = props['RestApiId']
            api_id = template_deployer.resolve_refs_recursively(
                stack_name, api_id, resource_map)
            backend.apis[api_id].deployments.pop(resource['id'], None)
            backend.apis[api_id].deployments[new_id] = resource
            resource['id'] = new_id
        else:
            LOG.warning('Unexpected resource type when updating ID: %s' %
                        type(resource))
Example #6
0
    def resolve_refs_recursively(cls, stack_name, value, resources):
        # TODO: restructure code to avoid circular import here
        from localstack.services.cloudformation.provider import find_stack
        from localstack.utils.cloudformation.template_deployer import resolve_refs_recursively

        stack = find_stack(stack_name)
        return resolve_refs_recursively(stack, value)
Example #7
0
 def test_resolve_references(self):
     ref = {
         "Fn::Join": [
             "",
             [
                 "arn:",
                 {
                     "Ref": "AWS::Partition"
                 },
                 ":apigateway:",
                 {
                     "Ref": "AWS::Region"
                 },
                 ":lambda:path/2015-03-31/functions/",
                 "test:lambda:arn",
                 "/invocations",
             ],
         ]
     }
     stack_name = "test"
     resources = {}
     result = template_deployer.resolve_refs_recursively(
         stack_name, ref, resources)
     pattern = (
         r"arn:aws:apigateway:.*:lambda:path/2015-03-31/functions/test:lambda:arn/invocations"
     )
     self.assertTrue(re.match(pattern, result))
def _resolve_refs_in_template(template, stack_params: Dict = None):
    stack = Stack({"StackName": "test"})
    stack.stack_parameters()
    stack_params = stack_params or {}
    stack_params = [{
        "ParameterKey": k,
        "ParameterValue": v
    } for k, v in stack_params.items()]
    stack.metadata["Parameters"].extend(stack_params)
    return template_deployer.resolve_refs_recursively(stack, template)
Example #9
0
    def _post_create(resource_id, resources, resource_type, func, stack_name):
        """attaches managed policies from the template to the role"""
        from localstack.utils.cloudformation.template_deployer import (
            find_stack,
            resolve_refs_recursively,
        )

        iam = aws_stack.connect_to_service("iam")
        resource = resources[resource_id]
        props = resource["Properties"]
        role_name = props["RoleName"]

        # attach managed policies
        policy_arns = props.get("ManagedPolicyArns", [])
        for arn in policy_arns:
            iam.attach_role_policy(RoleName=role_name, PolicyArn=arn)

        # TODO: to be removed once we change the method signature to pass in the stack object directly!
        stack = find_stack(stack_name)

        # add inline policies
        inline_policies = props.get("Policies", [])
        for policy in inline_policies:
            assert not isinstance(
                policy,
                list)  # remove if this doesn't make any problems for a while
            if policy == PLACEHOLDER_AWS_NO_VALUE:
                continue
            if not isinstance(policy, dict):
                LOG.info('Invalid format of policy for IAM role "%s": %s',
                         props.get("RoleName"), policy)
                continue
            pol_name = policy.get("PolicyName")

            # get policy document - make sure we're resolving references in the policy doc
            doc = dict(policy["PolicyDocument"])
            doc = resolve_refs_recursively(stack, doc)

            doc["Version"] = doc.get("Version") or IAM_POLICY_VERSION
            statements = ensure_list(doc["Statement"])
            for statement in statements:
                if isinstance(statement.get("Resource"), list):
                    # filter out empty resource strings
                    statement["Resource"] = [
                        r for r in statement["Resource"] if r
                    ]
            doc = json.dumps(doc)
            iam.put_role_policy(
                RoleName=props["RoleName"],
                PolicyName=pol_name,
                PolicyDocument=doc,
            )
Example #10
0
 def outputs(self):
     result = []
     for k, details in self.template.get('Outputs', {}).items():
         value = None
         try:
             template_deployer.resolve_refs_recursively(
                 self.stack_name, details, self.resources)
             value = details['Value']
         except Exception as e:
             LOG.debug(
                 'Unable to resolve references in stack outputs: %s - %s' %
                 (details, e))
         export = details.get('Export', {}).get('Name')
         description = details.get('Description')
         entry = {
             'OutputKey': k,
             'OutputValue': value,
             'Description': description,
             'ExportName': export
         }
         result.append(entry)
     return result
Example #11
0
 def test_resolve_references(self):
     ref = {
         'Fn::Join': [
             '',
             [
                 'arn:', {
                     'Ref': 'AWS::Partition'
                 }, ':apigateway:', {
                     'Ref': 'AWS::Region'
                 }, ':lambda:path/2015-03-31/functions/', 'test:lambda:arn',
                 '/invocations'
             ]
         ]
     }
     stack_name = 'test'
     resources = {}
     result = template_deployer.resolve_refs_recursively(
         stack_name, ref, resources)
     pattern = r'arn:aws:apigateway:.*:lambda:path/2015-03-31/functions/test:lambda:arn/invocations'
     self.assertTrue(re.match(pattern, result))
Example #12
0
def test_resolve_references():
    ref = {
        "Fn::Join": [
            "",
            [
                "arn:",
                {
                    "Ref": "AWS::Partition"
                },
                ":apigateway:",
                {
                    "Ref": "AWS::Region"
                },
                ":lambda:path/2015-03-31/functions/",
                "test:lambda:arn",
                "/invocations",
            ],
        ]
    }
    stack_name = "test"
    stack = Stack({"StackName": stack_name})
    result = template_deployer.resolve_refs_recursively(stack, ref)
    pattern = r"arn:aws:apigateway:.*:lambda:path/2015-03-31/functions/test:lambda:arn/invocations"
    assert re.match(pattern, result)
Example #13
0
 def resolve_refs_recursively(self, stack_name, value, resources):
     # TODO: restructure code to avoid circular import here
     from localstack.utils.cloudformation.template_deployer import resolve_refs_recursively
     return resolve_refs_recursively(stack_name, value, resources)