diff --git a/samcli/commands/_utils/parameters.py b/samcli/commands/_utils/parameters.py new file mode 100644 index 0000000000..d6f518ee2f --- /dev/null +++ b/samcli/commands/_utils/parameters.py @@ -0,0 +1,18 @@ +""" +Template parameters utilities shared by various commands +""" + +from typing import Dict, Union, Optional + + +def sanitize_parameter_overrides( + parameter_overrides: Dict[str, Union[Dict[str, str], str]] +) -> Dict[str, Optional[str]]: + """ + Get sanitized parameter override values based on if the workflow went via a guided deploy to set the + parameter overrides for deployment. If a guided deploy was followed the parameter overrides consists + of additional information such as if a given parameter's value is hidden or not. + :param parameter_overrides: dictionary of parameter key values. + :return: + """ + return {key: value.get("Value") if isinstance(value, dict) else value for key, value in parameter_overrides.items()} diff --git a/samcli/commands/deploy/command.py b/samcli/commands/deploy/command.py index 371dc61c4d..2ca436e090 100644 --- a/samcli/commands/deploy/command.py +++ b/samcli/commands/deploy/command.py @@ -20,7 +20,7 @@ signing_profiles_option, image_repositories_callback, ) -from samcli.commands.deploy.utils import sanitize_parameter_overrides +from samcli.commands._utils.parameters import sanitize_parameter_overrides from samcli.lib.telemetry.metric import track_command from samcli.lib.cli_validation.image_repository_validation import image_repository_validation from samcli.lib.utils import osutils diff --git a/samcli/commands/deploy/deploy_context.py b/samcli/commands/deploy/deploy_context.py index 3d4e7bc16a..d4b61fc52b 100644 --- a/samcli/commands/deploy/deploy_context.py +++ b/samcli/commands/deploy/deploy_context.py @@ -24,8 +24,8 @@ from samcli.commands.deploy import exceptions as deploy_exceptions from samcli.commands.deploy.auth_utils import auth_per_resource +from samcli.commands._utils.parameters import sanitize_parameter_overrides from samcli.commands.deploy.utils import ( - sanitize_parameter_overrides, print_deploy_args, hide_noecho_parameter_overrides, ) diff --git a/samcli/commands/deploy/guided_context.py b/samcli/commands/deploy/guided_context.py index dafdf0a331..dcbfe44d31 100644 --- a/samcli/commands/deploy/guided_context.py +++ b/samcli/commands/deploy/guided_context.py @@ -12,11 +12,7 @@ from click.types import FuncParamType from samcli.commands._utils.options import _space_separated_list_func_type -from samcli.commands._utils.template import ( - get_template_parameters, - get_template_artifacts_format, - get_template_function_resource_ids, -) +from samcli.commands._utils.template import get_template_parameters from samcli.commands.deploy.auth_utils import auth_per_resource from samcli.commands.deploy.code_signer_utils import ( signer_config_per_function, @@ -26,7 +22,7 @@ ) from samcli.commands.deploy.exceptions import GuidedDeployFailedError from samcli.commands.deploy.guided_config import GuidedConfig -from samcli.commands.deploy.utils import sanitize_parameter_overrides +from samcli.commands._utils.parameters import sanitize_parameter_overrides from samcli.lib.bootstrap.bootstrap import manage_stack from samcli.lib.config.samconfig import DEFAULT_ENV, DEFAULT_CONFIG_FILE_NAME from samcli.lib.intrinsic_resolver.intrinsics_symbol_table import IntrinsicsSymbolTable @@ -305,33 +301,36 @@ def prompt_image_repository(self, stacks: List[Stack]): A dictionary contains image function logical ID as key, image repository as value. """ image_repositories = {} - artifacts_format = get_template_artifacts_format(template_file=self.template_file) - if IMAGE in artifacts_format: - self.function_provider = SamFunctionProvider(stacks, ignore_code_extraction_warnings=True) - 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, + + self.function_provider = SamFunctionProvider(stacks, ignore_code_extraction_warnings=True) + + for resource_id in ( + resource_id + for resource_id, function in self.function_provider.functions.items() + if function.packagetype == IMAGE + ): + 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 resource_id not in image_repositories or not is_ecr_url(image_repositories[resource_id]): + raise GuidedDeployFailedError( + f"Invalid Image Repository ECR URI: {image_repositories.get(resource_id)}" ) - if resource_id not in image_repositories or not is_ecr_url(str(image_repositories[resource_id])): - raise GuidedDeployFailedError( - f"Invalid Image Repository ECR URI: {image_repositories.get(resource_id)}" - ) - for resource_id, function_prop in self.function_provider.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) + for resource_id, function_prop in self.function_provider.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 diff --git a/samcli/commands/deploy/utils.py b/samcli/commands/deploy/utils.py index c961eb710f..ddf3f43e65 100644 --- a/samcli/commands/deploy/utils.py +++ b/samcli/commands/deploy/utils.py @@ -77,17 +77,6 @@ def print_deploy_args( click.secho("\nInitiating deployment\n=====================", fg="yellow") -def sanitize_parameter_overrides(parameter_overrides): - """ - Get sanitized parameter override values based on if the workflow went via a guided deploy to set the - parameter overrides for deployment. If a guided deploy was followed the parameter overrides consists - of additional information such as if a given parameter's value is hidden or not. - :param parameter_overrides: dictionary of parameter key values. - :return: - """ - return {key: value.get("Value") if isinstance(value, dict) else value for key, value in parameter_overrides.items()} - - def hide_noecho_parameter_overrides(template_parameters, parameter_overrides): hidden_params = copy.deepcopy(parameter_overrides) params = template_parameters.get("Parameters", None) diff --git a/samcli/lib/cli_validation/image_repository_validation.py b/samcli/lib/cli_validation/image_repository_validation.py index 329e855019..66e6b0f352 100644 --- a/samcli/lib/cli_validation/image_repository_validation.py +++ b/samcli/lib/cli_validation/image_repository_validation.py @@ -5,8 +5,12 @@ import click from samcli.commands._utils.option_validator import Validator -from samcli.commands._utils.template import get_template_function_resource_ids, get_template_artifacts_format from samcli.lib.utils.packagetype import IMAGE +from samcli.lib.providers.sam_stack_provider import SamLocalStackProvider +from samcli.lib.providers.sam_function_provider import SamFunctionProvider +from samcli.lib.intrinsic_resolver.intrinsics_symbol_table import IntrinsicsSymbolTable +from samcli.commands._utils.parameters import sanitize_parameter_overrides +from samcli.cli.context import Context def image_repository_validation(func): @@ -22,22 +26,34 @@ def image_repository_validation(func): def wrapped(*args, **kwargs): ctx = click.get_current_context() + cli_ctx = Context.get_current_context() + guided = ctx.params.get("guided", False) or ctx.params.get("g", False) image_repository = ctx.params.get("image_repository", False) image_repositories = ctx.params.get("image_repositories", False) or {} template_file = ( ctx.params.get("t", False) or ctx.params.get("template_file", False) or ctx.params.get("template", False) ) + parameter_overrides = ctx.params.get("parameter_overrides", {}) + + stacks, _ = SamLocalStackProvider.get_stacks( + template_file, + parameter_overrides=sanitize_parameter_overrides(parameter_overrides), + global_parameter_overrides={IntrinsicsSymbolTable.AWS_REGION: cli_ctx.region} + if cli_ctx is not None + else {}, + ) # Check if `--image-repository` or `--image-repositories` are required by # looking for resources that have an IMAGE based packagetype. - required = any( - [ - _template_artifact == IMAGE - for _template_artifact in get_template_artifacts_format(template_file=template_file) - ] - ) + packageable_function_ids = [ + resource_id + for resource_id, function in SamFunctionProvider( + stacks, ignore_code_extraction_warnings=True + ).functions.items() + if function.packagetype == IMAGE + ] validators = [ Validator( @@ -50,7 +66,9 @@ def wrapped(*args, **kwargs): ), ), Validator( - validation_function=lambda: not guided and not (image_repository or image_repositories) and required, + validation_function=lambda: not guided + and not (image_repository or image_repositories) + and packageable_function_ids, exception=click.BadOptionUsage( option_name="--image-repositories", ctx=ctx, @@ -59,10 +77,7 @@ def wrapped(*args, **kwargs): ), Validator( validation_function=lambda: not guided - and ( - set(image_repositories.keys()) != set(get_template_function_resource_ids(template_file, IMAGE)) - and image_repositories - ), + and (set(image_repositories.keys()) != set(packageable_function_ids) and image_repositories), exception=click.BadOptionUsage( option_name="--image-repositories", ctx=ctx, diff --git a/samcli/lib/providers/sam_function_provider.py b/samcli/lib/providers/sam_function_provider.py index 6bffc4bf75..19676b4fa8 100644 --- a/samcli/lib/providers/sam_function_provider.py +++ b/samcli/lib/providers/sam_function_provider.py @@ -144,8 +144,10 @@ def _extract_functions( SamFunctionProvider._warn_code_extraction(resource_type, name, code_property_key) continue - if resource_package_type == IMAGE and SamBaseProvider._is_ecr_uri( - resource_properties.get(image_property_key) + if ( + resource_package_type == IMAGE + and ("DockerContext" not in (resource_metadata or {})) + and SamBaseProvider._is_ecr_uri(resource_properties.get(image_property_key)) ): # ImageUri can be an ECR uri, which is not supported if not ignore_code_extraction_warnings: