예제 #1
0
    def is_valid(self):
        """
        Runs the SAM Translator to determine if the template provided is valid. This is similar to running a
        ChangeSet in CloudFormation for a SAM Template

        Raises
        -------
        InvalidSamDocumentException
             If the template is not valid, an InvalidSamDocumentException is raised
        """
        managed_policy_map = self.managed_policy_loader.load()

        sam_translator = Translator(managed_policy_map=managed_policy_map,
                                    sam_parser=self.sam_parser,
                                    plugins=[])

        self._replace_local_codeuri()

        try:
            template = sam_translator.translate(sam_template=self.sam_template,
                                                parameter_values={})
            LOG.debug("Translated template is:\n%s", yaml_dump(template))
        except InvalidDocumentException as e:
            raise InvalidSamDocumentException(
                functools.reduce(
                    lambda message, error: message + " " + str(error),
                    e.causes, str(e))) from e
예제 #2
0
    def test_parse_yaml_preserve_elements_order(self):
        input_template = ("B_Resource:\n"
                          "  Key2:\n"
                          "    Name: name2\n"
                          "  Key1:\n"
                          "    Name: name1\n"
                          "A_Resource:\n"
                          "  Key2:\n"
                          "    Name: name2\n"
                          "  Key1:\n"
                          "    Name: name1\n")
        output_dict = yaml_parse(input_template)
        expected_dict = OrderedDict([
            ("B_Resource",
             OrderedDict([("Key2", {
                 "Name": "name2"
             }), ("Key1", {
                 "Name": "name1"
             })])),
            ("A_Resource",
             OrderedDict([("Key2", {
                 "Name": "name2"
             }), ("Key1", {
                 "Name": "name1"
             })])),
        ])
        self.assertEqual(expected_dict, output_dict)

        output_template = yaml_dump(output_dict)
        self.assertEqual(input_template, output_template)
예제 #3
0
    def test_yaml_with_tags(self):
        output = yaml_parse(self.yaml_with_tags)
        self.assertEquals(self.parsed_yaml_dict, output)

        # Make sure formatter and parser work well with each other
        formatted_str = yaml_dump(output)
        output_again = yaml_parse(formatted_str)
        self.assertEquals(output, output_again)
예제 #4
0
    def test_yaml_with_tags(self):
        output = yaml_parse(self.yaml_with_tags)
        self.assertEquals(self.parsed_yaml_dict, output)

        # Make sure formatter and parser work well with each other
        formatted_str = yaml_dump(output)
        output_again = yaml_parse(formatted_str)
        self.assertEquals(output, output_again)
예제 #5
0
    def test_yaml_dumps(self):
        input_yaml_dict = {"Resource": {"Key7": "012345678"}}

        expected_output = "Resource:\n  Key7: '012345678'\n"

        output = yaml_dump(input_yaml_dict)

        self.assertEqual(output, expected_output)
예제 #6
0
    def _export(self, template_path, use_json):
        template = Template(template_path, os.getcwd(), self.s3_uploader)
        exported_template = template.export()

        if use_json:
            exported_str = json.dumps(exported_template, indent=4, ensure_ascii=False)
        else:
            exported_str = yaml_dump(exported_template)

        return exported_str
예제 #7
0
    def is_valid(self):
        """
        Runs the SAM Translator to determine if the template provided is valid. This is similar to running a
        ChangeSet in CloudFormation for a SAM Template

        Raises
        -------
        InvalidSamDocumentException
             If the template is not valid, an InvalidSamDocumentException is raised
        """
        managed_policy_map = self.managed_policy_loader.load()

        sam_translator = Translator(managed_policy_map=managed_policy_map,
                                    sam_parser=self.sam_parser,
                                    plugins=[])

        self._replace_local_codeuri()

        # In the Paser class, within the SAM Translator, they log a warning for when the template
        # does not match the schema. The logger they use is the root logger instead of one scoped to
        # their module. Currently this does not cause templates to fail, so we will suppress this
        # by patching the logging.warning method that is used in that class.
        class WarningSuppressLogger(object):
            def __init__(self, obj_to_patch):
                self.obj_to_patch = obj_to_patch

            def __enter__(self):
                self.obj_to_patch.warning = self.warning

            def __exit__(self, exc_type, exc_val, exc_tb):
                self.obj_to_patch.warning = self.obj_to_patch.warning

            def warning(self, message):
                pass

        try:
            with WarningSuppressLogger(parser.logging):
                template = sam_translator.translate(
                    sam_template=self.sam_template, parameter_values={})
                LOG.debug("Translated template is:\n%s", yaml_dump(template))
        except InvalidDocumentException as e:
            raise InvalidSamDocumentException(
                functools.reduce(
                    lambda message, error: message + ' ' + str(error),
                    e.causes, str(e)))
예제 #8
0
    def is_valid(self):
        """
        Runs the SAM Translator to determine if the template provided is valid. This is similar to running a
        ChangeSet in CloudFormation for a SAM Template

        Raises
        -------
        InvalidSamDocumentException
             If the template is not valid, an InvalidSamDocumentException is raised
        """
        managed_policy_map = self.managed_policy_loader.load()

        sam_translator = Translator(managed_policy_map=managed_policy_map,
                                    sam_parser=self.sam_parser,
                                    plugins=[])

        self._replace_local_codeuri()

        # In the Paser class, within the SAM Translator, they log a warning for when the template
        # does not match the schema. The logger they use is the root logger instead of one scoped to
        # their module. Currently this does not cause templates to fail, so we will suppress this
        # by patching the logging.warning method that is used in that class.
        class WarningSuppressLogger(object):

            def __init__(self, obj_to_patch):
                self.obj_to_patch = obj_to_patch

            def __enter__(self):
                self.obj_to_patch.warning = self.warning

            def __exit__(self, exc_type, exc_val, exc_tb):
                self.obj_to_patch.warning = self.obj_to_patch.warning

            def warning(self, message):
                pass

        try:
            with WarningSuppressLogger(parser.logging):
                template = sam_translator.translate(sam_template=self.sam_template,
                                                    parameter_values={})
                LOG.debug("Translated template is:\n%s", yaml_dump(template))
        except InvalidDocumentException as e:
            raise InvalidSamDocumentException(
                functools.reduce(lambda message, error: message + ' ' + str(error), e.causes, str(e)))
예제 #9
0
    def do_export(self, resource_id, resource_dict, parent_dir):
        """
        If the nested stack template is valid, this method will
        export on the nested template, upload the exported template to S3
        and set property to URL of the uploaded S3 template
        """

        template_path = resource_dict.get(self.PROPERTY_NAME, None)

        if (template_path is None or is_s3_url(template_path)
                or template_path.startswith(self.uploader.s3.meta.endpoint_url)
                or template_path.startswith("https://s3.amazonaws.com/")):
            # Nothing to do
            return

        abs_template_path = make_abs_path(parent_dir, template_path)
        if not is_local_file(abs_template_path):
            raise exceptions.InvalidTemplateUrlParameterError(
                property_name=self.PROPERTY_NAME,
                resource_id=resource_id,
                template_path=abs_template_path)

        exported_template_dict = Template(template_path, parent_dir,
                                          self.uploaders,
                                          self.code_signer).export()

        exported_template_str = yaml_dump(exported_template_dict)

        with mktempfile() as temporary_file:
            temporary_file.write(exported_template_str)
            temporary_file.flush()

            url = self.uploader.upload_with_dedup(temporary_file.name,
                                                  "template")

            # TemplateUrl property requires S3 URL to be in path-style format
            parts = S3Uploader.parse_s3_url(url, version_property="Version")
            s3_path_url = self.uploader.to_path_style_s3_url(
                parts["Key"], parts.get("Version", None))
            set_value_from_jmespath(resource_dict, self.PROPERTY_NAME,
                                    s3_path_url)
예제 #10
0
def move_template(src_template_path, dest_template_path, template_dict):
    """
    Move the SAM/CloudFormation template from ``src_template_path`` to ``dest_template_path``. For convenience, this
    method accepts a dictionary of template data ``template_dict`` that will be written to the destination instead of
    reading from the source file.

    SAM/CloudFormation template can contain certain properties whose value is a relative path to a local file/folder.
    This path is always relative to the template's location. Before writing the template to ``dest_template_path`,
    we will update these paths to be relative to the new location.

    This methods updates resource properties supported by ``aws cloudformation package`` command:
    https://docs.aws.amazon.com/cli/latest/reference/cloudformation/package.html

    You must use this method if you are reading a template from one location, modifying it, and writing it back to a
    different location.

    Parameters
    ----------
    src_template_path : str
        Path to the original location of the template

    dest_template_path : str
        Path to the destination location where updated template should be written to

    template_dict : dict
        Dictionary containing template contents. This dictionary will be updated & written to ``dest`` location.
    """

    original_root = os.path.dirname(src_template_path)
    new_root = os.path.dirname(dest_template_path)

    # Next up, we will be writing the template to a different location. Before doing so, we should
    # update any relative paths in the template to be relative to the new location.
    modified_template = _update_relative_paths(template_dict, original_root,
                                               new_root)

    # if a stack only has image functions, the directory for that directory won't be created.
    # here we make sure the directory the destination template file to write to exists.
    os.makedirs(os.path.dirname(dest_template_path), exist_ok=True)
    with open(dest_template_path, "w") as fp:
        fp.write(yaml_dump(modified_template))
예제 #11
0
def move_template(src_template_path,
                  dest_template_path,
                  template_dict):
    """
    Move the SAM/CloudFormation template from ``src_template_path`` to ``dest_template_path``. For convenience, this
    method accepts a dictionary of template data ``template_dict`` that will be written to the destination instead of
    reading from the source file.

    SAM/CloudFormation template can contain certain properties whose value is a relative path to a local file/folder.
    This path is always relative to the template's location. Before writing the template to ``dest_template_path`,
    we will update these paths to be relative to the new location.

    This methods updates resource properties supported by ``aws cloudformation package`` command:
    https://docs.aws.amazon.com/cli/latest/reference/cloudformation/package.html

    You must use this method if you are reading a template from one location, modifying it, and writing it back to a
    different location.

    Parameters
    ----------
    src_template_path : str
        Path to the original location of the template

    dest_template_path : str
        Path to the destination location where updated template should be written to

    template_dict : dict
        Dictionary containing template contents. This dictionary will be updated & written to ``dest`` location.
    """

    original_root = os.path.dirname(src_template_path)
    new_root = os.path.dirname(dest_template_path)

    # Next up, we will be writing the template to a different location. Before doing so, we should
    # update any relative paths in the template to be relative to the new location.
    modified_template = _update_relative_paths(template_dict,
                                               original_root,
                                               new_root)

    with open(dest_template_path, "w") as fp:
        fp.write(yaml_dump(modified_template))
예제 #12
0
    def test_unroll_yaml_anchors(self):
        properties = {"Foo": "bar", "Spam": "eggs"}
        template = {
            "Resources": {
                "Resource1": {
                    "Properties": properties
                },
                "Resource2": {
                    "Properties": properties
                }
            }
        }

        expected = ("Resources:\n"
                    "  Resource1:\n"
                    "    Properties:\n"
                    "      Foo: bar\n"
                    "      Spam: eggs\n"
                    "  Resource2:\n"
                    "    Properties:\n"
                    "      Foo: bar\n"
                    "      Spam: eggs\n")
        actual = yaml_dump(template)
        self.assertEqual(actual, expected)