Exemple #1
0
    def test_must_raise_if_file_does_not_exist(self):
        filename = "filename"

        with self.assertRaises(ValueError) as exception_ctx:
            get_template_data(filename)

        ex = exception_ctx.exception
        self.assertEquals(str(ex), "Template file not found at {}".format(filename))
Exemple #2
0
    def test_must_raise_if_file_does_not_exist(self):
        filename = "filename"

        with self.assertRaises(TemplateNotFoundException) as exception_ctx:
            get_template_data(filename)

        ex = exception_ctx.exception
        self.assertEqual(str(ex), "Template file not found at {}".format(filename))
    def _convert_cfn_stack_resource(
        template_file: str,
        stack_path: str,
        name: str,
        resource_properties: Dict,
        global_parameter_overrides: Optional[Dict] = None,
    ) -> Optional[Stack]:
        template_url = resource_properties.get("TemplateURL", "")

        if SamLocalStackProvider.is_remote_url(template_url):
            raise RemoteStackLocationNotSupported()
        if template_url.startswith("file://"):
            template_url = unquote(urlparse(template_url).path)
        else:
            template_url = SamLocalStackProvider.normalize_resource_path(
                template_file, template_url)

        return Stack(
            parent_stack_path=stack_path,
            name=name,
            location=template_url,
            parameters=SamLocalStackProvider.merge_parameter_overrides(
                resource_properties.get("Parameters", {}),
                global_parameter_overrides),
            template_dict=get_template_data(template_url),
        )
    def get_stacks(
        template_file: str,
        stack_path: str = "",
        name: str = "",
        parameter_overrides: Optional[Dict] = None,
    ) -> List[Stack]:
        template_dict = get_template_data(template_file)
        stacks = [Stack(stack_path, name, template_file, parameter_overrides, template_dict)]

        # Note(xinhol): recursive get_stacks is only enabled in tests by env var SAM_CLI_ENABLE_NESTED_STACK.
        # We will remove this env var and make this method recursive by default
        # for nested stack support in the future.
        if not os.environ.get(SamLocalStackProvider.ENV_SAM_CLI_ENABLE_NESTED_STACK, False):
            return stacks

        current = SamLocalStackProvider(template_file, stack_path, template_dict, parameter_overrides)
        for child_stack in current.get_all():
            stacks.extend(
                SamLocalStackProvider.get_stacks(
                    child_stack.location,
                    os.path.join(stack_path, name),
                    child_stack.name,
                    child_stack.parameters,
                )
            )
        return stacks
Exemple #5
0
    def prompt_image_repository(self, parameter_overrides):
        image_repository = None
        artifacts_format = get_template_artifacts_format(
            template_file=self.template_file)
        if IMAGE in artifacts_format:
            self.transformed_resources = transform_template(
                parameter_overrides=parameter_overrides,
                template_dict=get_template_data(
                    template_file=self.template_file),
            )
            image_repository = prompt(
                f"\t{self.start_bold}Image Repository{self.end_bold}",
                type=click.STRING,
                default=self.image_repository if self.image_repository else "",
            )
            for _, function_prop in self.transformed_resources.functions.items(
            ):
                if function_prop.packagetype == IMAGE:
                    image = function_prop.imageuri
                    try:
                        tag = tag_translation(image)
                    except NonLocalImageException:
                        pass
                    else:
                        click.secho(
                            f"\t{self.start_bold}Images that will be pushed:{self.end_bold}"
                        )
                        click.secho(f"\t  {image} to {image_repository}:{tag}")
            click.secho(nl=True)

        return image_repository
Exemple #6
0
    def _convert_cfn_stack_resource(
        template_directory: str,
        stack_path: str,
        name: str,
        resource_properties: Dict,
        global_parameter_overrides: Optional[Dict] = None,
    ) -> Optional[Stack]:
        template_url = resource_properties.get("TemplateURL", "")

        if SamLocalStackProvider.is_remote_url(template_url):
            LOG.warning(
                "Nested stack '%s' has specified S3 location for Location which is unsupported. "
                "Skipping resources inside this nested stack.",
                name,
            )
            return None
        if template_url.startswith("file://"):
            template_url = unquote(urlparse(template_url).path)
        elif not os.path.isabs(template_url):
            template_url = os.path.join(template_directory,
                                        os.path.relpath(template_url))

        return Stack(
            parent_stack_path=stack_path,
            name=name,
            location=template_url,
            parameters=SamLocalStackProvider.merge_parameter_overrides(
                resource_properties.get("Parameters", {}),
                global_parameter_overrides),
            template_dict=get_template_data(template_url),
        )
def do_cli(ctx, template, semantic_version):
    """Publish the application based on command line inputs."""
    try:
        template_data = get_template_data(template)
    except ValueError as ex:
        click.secho("Publish Failed", fg="red")
        raise UserException(str(ex))

    # Override SemanticVersion in template metadata when provided in command input
    if semantic_version and SERVERLESS_REPO_APPLICATION in template_data.get(
            METADATA, {}):
        template_data.get(METADATA).get(
            SERVERLESS_REPO_APPLICATION)[SEMANTIC_VERSION] = semantic_version

    try:
        publish_output = publish_application(template_data)
        click.secho("Publish Succeeded", fg="green")
        click.secho(_gen_success_message(publish_output))
    except InvalidS3UriError:
        click.secho("Publish Failed", fg="red")
        raise UserException(
            "Your SAM template contains invalid S3 URIs. Please make sure that you have uploaded application "
            "artifacts to S3 by packaging the template. See more details in {}"
            .format(SAM_PACKAGE_DOC))
    except ServerlessRepoError as ex:
        click.secho("Publish Failed", fg="red")
        LOG.debug("Failed to publish application to serverlessrepo",
                  exc_info=True)
        error_msg = "{}\nPlease follow the instructions in {}".format(
            str(ex), SAM_PUBLISH_DOC)
        raise UserException(error_msg)

    application_id = publish_output.get("application_id")
    _print_console_link(ctx.region, application_id)
    def _convert_sam_application_resource(
        template_file: str,
        stack_path: str,
        name: str,
        resource_properties: Dict,
        global_parameter_overrides: Optional[Dict] = None,
    ) -> Optional[Stack]:
        location = resource_properties.get("Location")

        if isinstance(location, dict):
            raise RemoteStackLocationNotSupported()

        location = cast(str, location)
        if SamLocalStackProvider.is_remote_url(location):
            raise RemoteStackLocationNotSupported()
        if location.startswith("file://"):
            location = unquote(urlparse(location).path)
        else:
            location = SamLocalStackProvider.normalize_resource_path(
                template_file, location)

        return Stack(
            parent_stack_path=stack_path,
            name=name,
            location=location,
            parameters=SamLocalStackProvider.merge_parameter_overrides(
                resource_properties.get("Parameters", {}),
                global_parameter_overrides),
            template_dict=get_template_data(location),
        )
Exemple #9
0
    def test_must_raise_on_parse_errors(self, exception, pathlib_mock, yaml_parse_mock):
        filename = "filename"
        file_data = "contents of the file"

        pathlib_mock.Path.return_value.exists.return_value = True  # Fake that the file exists

        m = mock_open(read_data=file_data)
        yaml_parse_mock.side_effect = exception

        with patch("samcli.commands._utils.template.open", m):

            with self.assertRaises(TemplateFailedParsingException) as ex_ctx:
                get_template_data(filename)

            actual_exception = ex_ctx.exception
            self.assertTrue(str(actual_exception).startswith("Failed to parse template: "))
    def handle_fn_transform(self, intrinsic_value, ignore_errors):
        """
        { "Fn::Transform" : { "Name" : macro name, "Parameters" : {key : value, ... } } }
        This intrinsic function will transform the data with the body provided

        This intrinsic function will resolve all the objects within the function's value and check their type.
        Parameter
        ----------
        intrinsic_value: list, dict
           This is the value of the object inside the Fn::Transform intrinsic function property

        Return
        -------
        A string with the resolved attributes
        """
        macro_name = intrinsic_value.get("Name")
        name = self.intrinsic_property_resolver(
            macro_name, ignore_errors, parent_function=IntrinsicResolver.FN_TRANSFORM
        )

        if name not in IntrinsicResolver.SUPPORTED_MACRO_TRANSFORMATIONS:
            raise InvalidIntrinsicException(
                "The type {} is not currently supported in {}".format(name, IntrinsicResolver.FN_TRANSFORM)
            )

        parameters = intrinsic_value.get("Parameters")
        verify_intrinsic_type_dict(
            parameters, IntrinsicResolver.FN_TRANSFORM, message=" Fn::Transform requires parameters section"
        )

        location = self.intrinsic_property_resolver(parameters.get("Location"), ignore_errors)
        location_data = get_template_data(location)

        return location_data
Exemple #11
0
    def test_must_raise_on_parse_errors(self, exception, pathlib_mock, yaml_parse_mock):
        filename = "filename"
        file_data = "contents of the file"

        pathlib_mock.Path.return_value.exists.return_value = True  # Fake that the file exists

        m = mock_open(read_data=file_data)
        yaml_parse_mock.side_effect = exception

        with patch("samcli.commands._utils.template.open", m):

            with self.assertRaises(ValueError) as ex_ctx:
                get_template_data(filename)

            actual_exception = ex_ctx.exception
            self.assertTrue(str(actual_exception).startswith("Failed to parse template: "))
    def _convert_sam_application_resource(
        template_directory: str, stack_path: str, name: str, resource_properties: Dict
    ) -> Optional[Stack]:
        location = resource_properties.get("Location")

        if isinstance(location, dict):
            LOG.warning(
                "Nested application '%s' has specified an application published to the "
                "AWS Serverless Application Repository which is unsupported. "
                "Skipping resources inside this nested application.",
                name,
            )
            return None

        location = cast(str, location)
        if SamLocalStackProvider.is_remote_url(location):
            LOG.warning(
                "Nested application '%s' has specified S3 location for Location which is unsupported. "
                "Skipping resources inside this nested application.",
                name,
            )
            return None
        if location.startswith("file://"):
            location = unquote(urlparse(location).path)
        elif not os.path.isabs(location):
            location = os.path.join(template_directory, os.path.relpath(location))

        return Stack(
            parent_stack_path=stack_path,
            name=name,
            location=location,
            parameters=resource_properties.get("Parameters"),
            template_dict=get_template_data(location),
        )
Exemple #13
0
    def __enter__(self):
        self._template_dict = get_template_data(self._template_file)

        self._function_provider = SamFunctionProvider(
            self._template_dict, self._parameter_overrides)
        self._layer_provider = SamLayerProvider(self._template_dict,
                                                self._parameter_overrides)

        if not self._base_dir:
            # Base directory, if not provided, is the directory containing the template
            self._base_dir = str(
                pathlib.Path(self._template_file).resolve().parent)

        self._build_dir = self._setup_build_dir(self._build_dir, self._clean)

        if self._cached:
            cache_path = pathlib.Path(self._cache_dir)
            cache_path.mkdir(mode=self._BUILD_DIR_PERMISSIONS,
                             parents=True,
                             exist_ok=True)
            self._cache_dir = str(cache_path.resolve())

        if self._use_container:
            self._container_manager = ContainerManager(
                docker_network_id=self._docker_network,
                skip_pull_image=self._skip_pull_image)

        return self
    def deploy(
        self,
        stack_name,
        template_str,
        parameters,
        capabilities,
        no_execute_changeset,
        role_arn,
        notification_arns,
        s3_uploader,
        tags,
        region,
        fail_on_empty_changeset=True,
        confirm_changeset=False,
    ):

        auth_required_per_resource = auth_per_resource(
            sanitize_parameter_overrides(self.parameter_overrides),
            get_template_data(self.template_file))

        for resource, authorization_required in auth_required_per_resource:
            if not authorization_required:
                click.secho(f"{resource} may not have authorization defined.",
                            fg="yellow")

        try:
            result, changeset_type = self.deployer.create_and_wait_for_changeset(
                stack_name=stack_name,
                cfn_template=template_str,
                parameter_values=parameters,
                capabilities=capabilities,
                role_arn=role_arn,
                notification_arns=notification_arns,
                s3_uploader=s3_uploader,
                tags=tags,
            )
            click.echo(
                self.MSG_SHOWCASE_CHANGESET.format(changeset_id=result["Id"]))

            if no_execute_changeset:
                return

            if confirm_changeset:
                click.secho(self.MSG_CONFIRM_CHANGESET_HEADER, fg="yellow")
                click.secho("=" * len(self.MSG_CONFIRM_CHANGESET_HEADER),
                            fg="yellow")
                if not click.confirm(f"{self.MSG_CONFIRM_CHANGESET}",
                                     default=False):
                    return

            self.deployer.execute_changeset(result["Id"], stack_name)
            self.deployer.wait_for_execute(stack_name, changeset_type)
            click.echo(
                self.MSG_EXECUTE_SUCCESS.format(stack_name=stack_name,
                                                region=region))

        except deploy_exceptions.ChangeEmptyError as ex:
            if fail_on_empty_changeset:
                raise
            click.echo(str(ex))
Exemple #15
0
    def test_must_read_file_with_non_utf8_encoding(self, pathlib_mock, yaml_parse_mock):
        filename = "filename"
        file_data = "utf-8 😐"
        parse_result = "parse result"
        default_locale_encoding = "cp932"

        pathlib_mock.Path.return_value.exists.return_value = True  # Fake that the file exists

        yaml_parse_mock.return_value = parse_result

        # mock open with a different default encoding
        def mock_encoding_open(
            file, mode="r", buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None
        ):
            if encoding is None:
                encoding = default_locale_encoding
            mock_file = MagicMock()

            def mock_read():
                return file_data.encode("utf-8").decode(encoding)

            # __enter__ is used for with open(...) PEP343
            mock_file.__enter__.return_value = mock_file
            mock_file.read = mock_read
            return mock_file

        with patch("samcli.commands._utils.template.open", mock_encoding_open):
            result = get_template_data(filename)
            self.assertEqual(result, parse_result)

        yaml_parse_mock.assert_called_with(file_data)
Exemple #16
0
    def get_stacks(
        template_file: str,
        stack_path: str = "",
        name: str = "",
        parameter_overrides: Optional[Dict] = None,
        global_parameter_overrides: Optional[Dict] = None,
    ) -> List[Stack]:
        """
        Recursively extract stacks from a template file.

        Parameters
        ----------
        template_file: str
            the file path of the template to extract stacks from
        stack_path: str
            the stack path of the parent stack, for root stack, it is ""
        name: str
            the name of the stack associated with the template_file, for root stack, it is ""
        parameter_overrides: Optional[Dict]
            Optional dictionary of values for SAM template parameters that might want
            to get substituted within the template
        global_parameter_overrides: Optional[Dict]
            Optional dictionary of values for SAM template global parameters
            that might want to get substituted within the template and its child templates

        Returns
        -------
        stacks: List[Stack]
            The list of stacks extracted from template_file
        """
        template_dict = get_template_data(template_file)
        stacks = [
            Stack(
                stack_path,
                name,
                template_file,
                SamLocalStackProvider.merge_parameter_overrides(
                    parameter_overrides, global_parameter_overrides),
                template_dict,
            )
        ]

        current = SamLocalStackProvider(template_file, stack_path,
                                        template_dict, parameter_overrides,
                                        global_parameter_overrides)
        for child_stack in current.get_all():
            stacks.extend(
                SamLocalStackProvider.get_stacks(
                    child_stack.location,
                    os.path.join(stack_path, name),
                    child_stack.name,
                    child_stack.parameters,
                    global_parameter_overrides,
                ))
        return stacks
Exemple #17
0
def get_or_default_template_file_name(ctx, param, provided_value,
                                      include_build):
    """
    Default value for the template file name option is more complex than what Click can handle.
    This method either returns user provided file name or one of the two default options (template.yaml/template.yml)
    depending on the file that exists

    :param ctx: Click Context
    :param param: Param name
    :param provided_value: Value provided by Click. It could either be the default value or provided by user.
    :return: Actual value to be used in the CLI
    """

    original_template_path = os.path.abspath(provided_value)

    search_paths = ["template.yaml", "template.yml"]

    if include_build:
        search_paths.insert(0,
                            os.path.join(".aws-sam", "build", "template.yaml"))

    if provided_value == _TEMPLATE_OPTION_DEFAULT_VALUE:
        # "--template" is an alias of "--template-file", however, only the first option name "--template-file" in
        # ctx.default_map is used as default value of provided value. Here we add "--template"'s value as second
        # default value in this option, so that the command line paramerters from config file can load it.
        if ctx and ctx.default_map.get("template", None):
            provided_value = ctx.default_map.get("template")
        else:
            # Default value was used. Value can either be template.yaml or template.yml. Decide based on which file exists
            # .yml is the default, even if it does not exist.
            provided_value = "template.yml"

            for option in search_paths:
                if os.path.exists(option):
                    provided_value = option
                    break
    result = os.path.abspath(provided_value)

    if ctx:
        # sam configuration file should always be relative to the supplied original template and should not to be set
        # to be .aws-sam/build/
        setattr(ctx, "samconfig_dir", os.path.dirname(original_template_path))
        try:
            # FIX-ME: figure out a way to insert this directly to sam-cli context and not use click context.
            template_data = get_template_data(result)
            setattr(ctx, "template_dict", template_data)
        except TemplateNotFoundException:
            # Ignoring because there are certain cases where template file will not be available, eg: --help
            pass

    LOG.debug("Using SAM Template at %s", result)
    return result
    def _get_template_data(template_file):
        """
        Read the template file, parse it as JSON/YAML and return the template as a dictionary.

        :param string template_file: Path to the template to read
        :return dict: Template data as a dictionary
        :raises InvokeContextException: If template file was not found or the data was not a JSON/YAML
        """

        try:
            return get_template_data(template_file)
        except ValueError as ex:
            raise InvokeContextException(str(ex))
Exemple #19
0
    def _get_template_data(template_file):
        """
        Read the template file, parse it as JSON/YAML and return the template as a dictionary.

        :param string template_file: Path to the template to read
        :return dict: Template data as a dictionary
        :raises InvokeContextException: If template file was not found or the data was not a JSON/YAML
        """

        try:
            return get_template_data(template_file)
        except ValueError as ex:
            raise InvokeContextException(str(ex))
    def prompt_authorization(self, parameter_overrides):
        auth_required_per_resource = auth_per_resource(
            parameter_overrides, get_template_data(self.template_file))

        for resource, authorization_required in auth_required_per_resource:
            if not authorization_required:
                auth_confirm = confirm(
                    f"\t{self.start_bold}{resource} may not have authorization defined, Is this okay?{self.end_bold}",
                    default=False,
                )
                if not auth_confirm:
                    raise GuidedDeployFailedError(
                        msg="Security Constraints Not Satisfied!")
Exemple #21
0
    def test_must_read_file_and_parse(self, pathlib_mock, yaml_parse_mock):
        filename = "filename"
        file_data = "contents of the file"
        parse_result = "parse result"

        pathlib_mock.Path.return_value.exists.return_value = True  # Fake that the file exists

        m = mock_open(read_data=file_data)
        yaml_parse_mock.return_value = parse_result

        with patch("samcli.commands._utils.template.open", m):
            result = get_template_data(filename)

            self.assertEqual(result, parse_result)

        m.assert_called_with(filename, "r")
        yaml_parse_mock.assert_called_with(file_data)
Exemple #22
0
    def test_must_read_file_and_parse(self, pathlib_mock, yaml_parse_mock):
        filename = "filename"
        file_data = "contents of the file"
        parse_result = "parse result"

        pathlib_mock.Path.return_value.exists.return_value = True  # Fake that the file exists

        m = mock_open(read_data=file_data)
        yaml_parse_mock.return_value = parse_result

        with patch("samcli.commands._utils.template.open", m):
            result = get_template_data(filename)

            self.assertEquals(result, parse_result)

        m.assert_called_with(filename, 'r')
        yaml_parse_mock.assert_called_with(file_data)
Exemple #23
0
    def __enter__(self):
        try:
            self._template_dict = get_template_data(self._template_file)
        except ValueError as ex:
            raise UserException(str(ex))

        self._function_provider = SamFunctionProvider(self._template_dict, self._parameter_overrides)

        if not self._base_dir:
            # Base directory, if not provided, is the directory containing the template
            self._base_dir = str(pathlib.Path(self._template_file).resolve().parent)

        self._build_dir = self._setup_build_dir(self._build_dir, self._clean)

        if self._use_container:
            self._container_manager = ContainerManager(docker_network_id=self._docker_network,
                                                       skip_pull_image=self._skip_pull_image)

        return self
    def __enter__(self):
        self._template_dict = get_template_data(self._template_file)

        self._function_provider = SamFunctionProvider(
            self._template_dict, self._parameter_overrides)

        if not self._base_dir:
            # Base directory, if not provided, is the directory containing the template
            self._base_dir = str(
                pathlib.Path(self._template_file).resolve().parent)

        self._build_dir = self._setup_build_dir(self._build_dir, self._clean)

        if self._use_container:
            self._container_manager = ContainerManager(
                docker_network_id=self._docker_network,
                skip_pull_image=self._skip_pull_image)

        return self
Exemple #25
0
    def test_package_template_with_image_function_in_nested_application(
            self, template_file):
        template_path = self.test_data_path.joinpath(template_file)

        # when image function is not in main template, erc_repo_name does not show up in stdout
        # here we download the nested application template file and verify its content
        with tempfile.NamedTemporaryFile(
        ) as packaged_file, tempfile.TemporaryFile() as packaged_nested_file:
            # https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile
            # Closes the NamedTemporaryFile as on Windows NT or later, NamedTemporaryFile cannot be opened twice.
            packaged_file.close()

            command_list = self.get_command_list(
                image_repository=self.ecr_repo_name,
                template=template_path,
                resolve_s3=True,
                output_template_file=packaged_file.name,
            )

            process = Popen(command_list, stdout=PIPE, stderr=PIPE)
            try:
                process.communicate(timeout=TIMEOUT)
            except TimeoutExpired:
                process.kill()
                raise

            self.assertEqual(0, process.returncode)

            # download the root template and locate nested template url
            template_dict = get_template_data(packaged_file.name)
            nested_app_template_uri = (template_dict.get("Resources", {}).get(
                "myApp", {}).get("Properties").get("Location"))

            # extract bucket name and object key from the url
            parsed = urlparse(nested_app_template_uri)
            bucket_name, key = parsed.path.lstrip("/").split("/")

            # download and verify it contains ecr_repo_name
            s3 = boto3.resource("s3")
            s3.Object(bucket_name, key).download_fileobj(packaged_nested_file)
            packaged_nested_file.seek(0)
            self.assertIn(f"{self.ecr_repo_name}",
                          packaged_nested_file.read().decode())
Exemple #26
0
    def prompt_image_repository(self, parameter_overrides):
        image_repositories = {}
        artifacts_format = get_template_artifacts_format(
            template_file=self.template_file)
        if IMAGE in artifacts_format:
            self.transformed_resources = transform_template(
                parameter_overrides=parameter_overrides,
                template_dict=get_template_data(
                    template_file=self.template_file),
            )
            function_resources = get_template_function_resource_ids(
                template_file=self.template_file, artifact=IMAGE)
            for resource_id in function_resources:
                image_repositories[resource_id] = prompt(
                    f"\t{self.start_bold}Image Repository for {resource_id}{self.end_bold}",
                    default=self.image_repositories.get(resource_id, "")
                    if isinstance(self.image_repositories, dict) else ""
                    or self.image_repository,
                )
                if not is_ecr_url(image_repositories.get(resource_id)):
                    raise GuidedDeployFailedError(
                        f"Invalid Image Repository ECR URI: {image_repositories.get(resource_id)}"
                    )
            for resource_id, function_prop in self.transformed_resources.functions.items(
            ):
                if function_prop.packagetype == IMAGE:
                    image = function_prop.imageuri
                    try:
                        tag = tag_translation(image)
                    except NonLocalImageException:
                        pass
                    except NoImageFoundException as ex:
                        raise GuidedDeployFailedError(
                            "No images found to deploy, try running sam build"
                        ) from ex
                    else:
                        click.secho(
                            f"\t  {image} to be pushed to {image_repositories.get(resource_id)}:{tag}"
                        )
            click.secho(nl=True)

        return image_repositories
Exemple #27
0
def do_cli(ctx, template):
    """Publish the application based on command line inputs."""
    try:
        template_data = get_template_data(template)
    except ValueError as ex:
        click.secho("Publish Failed", fg='red')
        raise UserException(str(ex))

    try:
        publish_output = publish_application(template_data)
        click.secho("Publish Succeeded", fg="green")
        click.secho(_gen_success_message(publish_output), fg="yellow")
    except ServerlessRepoError as ex:
        click.secho("Publish Failed", fg='red')
        raise UserException(str(ex))
    except ClientError as ex:
        click.secho("Publish Failed", fg='red')
        raise _wrap_s3_uri_exception(ex)

    application_id = publish_output.get('application_id')
    _print_console_link(ctx.region, application_id)
    def _convert_cfn_stack_resource(
            stack_path: str, name: str,
            resource_properties: Dict) -> Optional[Stack]:
        template_url = resource_properties.get("TemplateURL", "")

        if SamLocalStackProvider.is_remote_url(template_url):
            LOG.warning(
                "Nested stack '%s' has specified S3 location for Location which is unsupported. "
                "Skipping resources inside this nested stack.",
                name,
            )
            return None
        if template_url.startswith("file://"):
            template_url = unquote(urlparse(template_url).path)

        return Stack(
            parent_stack_path=stack_path,
            name=name,
            location=template_url,
            parameters=resource_properties.get("Parameters"),
            template_dict=get_template_data(template_url),
        )
    def prompt_code_signing_settings(self, parameter_overrides):
        (functions_with_code_sign,
         layers_with_code_sign) = signer_config_per_function(
             parameter_overrides, get_template_data(self.template_file))

        # if no function or layer definition found with code signing, skip it
        if not functions_with_code_sign and not layers_with_code_sign:
            LOG.debug(
                "No function or layer definition found with code sign config, skipping"
            )
            return

        click.echo(
            "\n\t#Found code signing configurations in your function definitions"
        )
        sign_functions = confirm(
            f"\t{self.start_bold}Do you want to sign your code?{self.end_bold}",
            default=True,
        )

        if not sign_functions:
            LOG.debug(
                "User skipped code signing, continuing rest of the process")
            self.signing_profiles = None
            return

        if not self.signing_profiles:
            self.signing_profiles = {}

        click.echo(
            "\t#Please provide signing profile details for the following functions & layers"
        )

        for function_name in functions_with_code_sign:
            (profile_name,
             profile_owner) = extract_profile_name_and_owner_from_existing(
                 function_name, self.signing_profiles)

            click.echo(
                f"\t#Signing profile details for function '{function_name}'")
            profile_name = prompt_profile_name(profile_name, self.start_bold,
                                               self.end_bold)
            profile_owner = prompt_profile_owner(profile_owner,
                                                 self.start_bold,
                                                 self.end_bold)
            self.signing_profiles[function_name] = {
                "profile_name": profile_name,
                "profile_owner": profile_owner
            }
            self.signing_profiles[function_name][
                "profile_owner"] = "" if not profile_owner else profile_owner

        for layer_name, functions_use_this_layer in layers_with_code_sign.items(
        ):
            (profile_name,
             profile_owner) = extract_profile_name_and_owner_from_existing(
                 layer_name, self.signing_profiles)
            click.echo(
                f"\t#Signing profile details for layer '{layer_name}', "
                f"which is used by functions {functions_use_this_layer}")
            profile_name = prompt_profile_name(profile_name, self.start_bold,
                                               self.end_bold)
            profile_owner = prompt_profile_owner(profile_owner,
                                                 self.start_bold,
                                                 self.end_bold)
            self.signing_profiles[layer_name] = {
                "profile_name": profile_name,
                "profile_owner": profile_owner
            }
            self.signing_profiles[layer_name][
                "profile_owner"] = "" if not profile_owner else profile_owner

        LOG.debug("Signing profile names and owners %s", self.signing_profiles)