diff --git a/samcli/commands/_utils/custom_options/hook_name_option.py b/samcli/commands/_utils/custom_options/hook_name_option.py index a2cb334157..57f53b9fc5 100644 --- a/samcli/commands/_utils/custom_options/hook_name_option.py +++ b/samcli/commands/_utils/custom_options/hook_name_option.py @@ -86,9 +86,10 @@ def _call_prepare_hook(self, iac_hook_wrapper, opts): aws_profile = opts.get("profile") aws_region = opts.get("region") skip_prepare_infra = opts.get("skip_prepare_infra", False) + plan_file = opts.get("terraform_plan_file") metadata_file = iac_hook_wrapper.prepare( - output_dir_path, iac_project_path, debug, aws_profile, aws_region, skip_prepare_infra + output_dir_path, iac_project_path, debug, aws_profile, aws_region, skip_prepare_infra, plan_file ) LOG.info("Prepare hook completed and metadata file generated at: %s", metadata_file) diff --git a/samcli/commands/_utils/options.py b/samcli/commands/_utils/options.py index aa57f65f2c..79397d00d4 100644 --- a/samcli/commands/_utils/options.py +++ b/samcli/commands/_utils/options.py @@ -789,6 +789,40 @@ def use_container_build_option(f): return use_container_build_click_option()(f) +def terraform_plan_file_callback(ctx, param, provided_value): + """ + Callback for --terraform-plan-file to check if --hook-name is also specified + + Parameters + ---------- + ctx: click.core.Context + Click context + param: click.Option + Parameter properties + provided_value: bool + True if option was provided + """ + is_option_provided = provided_value or ctx.default_map.get("terraform_plan_file") + is_hook_provided = ctx.params.get("hook_name") or ctx.default_map.get("hook_name") + + if is_option_provided and not is_hook_provided: + raise click.BadOptionUsage(option_name=param.name, ctx=ctx, message="Missing option --hook-name") + + +def terraform_plan_file_click_option(): + return click.option( + "--terraform-plan-file", + type=click.Path(), + required=False, + callback=terraform_plan_file_callback, + help="Used for passing a custom plan file when executing the Terraform hook.", + ) + + +def terraform_plan_file_option(f): + return terraform_plan_file_click_option()(f) + + def build_image_click_option(cls): return click.option( "--build-image", diff --git a/samcli/commands/build/command.py b/samcli/commands/build/command.py index 17c177a1e1..9e478f2aa8 100644 --- a/samcli/commands/build/command.py +++ b/samcli/commands/build/command.py @@ -22,6 +22,7 @@ use_container_build_option, build_image_option, hook_name_click_option, + terraform_plan_file_option, ) from samcli.commands._utils.option_value_processor import process_env_var, process_image_options from samcli.cli.main import pass_context, common_options as cli_framework_options, aws_creds_options, print_cmdline_args @@ -70,6 +71,7 @@ context_settings={"max_content_width": 120}, ) @configuration_option(provider=ConfigProvider(section="parameters")) +@terraform_plan_file_option @hook_name_click_option( force_prepare=True, invalid_coexist_options=["t", "template-file", "template", "parameter-overrides"], @@ -155,6 +157,7 @@ def cli( hook_name: Optional[str], skip_prepare_infra: bool, mount_with, + terraform_plan_file, ) -> None: """ `sam build` command entry point diff --git a/samcli/commands/build/core/options.py b/samcli/commands/build/core/options.py index 103808672c..2c525fdeca 100644 --- a/samcli/commands/build/core/options.py +++ b/samcli/commands/build/core/options.py @@ -37,6 +37,8 @@ TEMPLATE_OPTIONS: List[str] = ["parameter_overrides"] +TERRAFORM_HOOK_OPTIONS: List[str] = ["terraform_plan_file"] + ALL_OPTIONS: List[str] = ( REQUIRED_OPTIONS + TEMPLATE_OPTIONS @@ -47,6 +49,7 @@ + EXTENSION_OPTIONS + CONFIGURATION_OPTION_NAMES + ALL_COMMON_OPTIONS + + TERRAFORM_HOOK_OPTIONS ) OPTIONS_INFO: Dict[str, Dict] = { @@ -71,5 +74,6 @@ ), ], }, + "Terraform Hook Options": {"option_names": {opt: {"rank": idx} for idx, opt in enumerate(TERRAFORM_HOOK_OPTIONS)}}, } add_common_options_info(OPTIONS_INFO) diff --git a/samcli/commands/local/invoke/cli.py b/samcli/commands/local/invoke/cli.py index 88a3f72963..f8e87f4e27 100644 --- a/samcli/commands/local/invoke/cli.py +++ b/samcli/commands/local/invoke/cli.py @@ -11,7 +11,7 @@ from samcli.cli.main import common_options as cli_framework_options from samcli.commands._utils.experimental import ExperimentalFlag, is_experimental_enabled from samcli.commands._utils.option_value_processor import process_image_options -from samcli.commands._utils.options import hook_name_click_option, skip_prepare_infra_option +from samcli.commands._utils.options import hook_name_click_option, skip_prepare_infra_option, terraform_plan_file_option from samcli.commands.local.cli_common.options import invoke_common_options, local_common_options from samcli.commands.local.invoke.core.command import InvokeCommand from samcli.commands.local.lib.exceptions import InvalidIntermediateImageError @@ -44,6 +44,7 @@ context_settings={"max_content_width": 120}, ) @configuration_option(provider=ConfigProvider(section="parameters")) +@terraform_plan_file_option @hook_name_click_option( force_prepare=False, invalid_coexist_options=["t", "template-file", "template", "parameter-overrides"] ) @@ -91,6 +92,7 @@ def cli( invoke_image, hook_name, skip_prepare_infra, + terraform_plan_file, ): """ `sam local invoke` command entry point diff --git a/samcli/commands/local/invoke/core/options.py b/samcli/commands/local/invoke/core/options.py index b11f33eaa9..83bb246e3c 100644 --- a/samcli/commands/local/invoke/core/options.py +++ b/samcli/commands/local/invoke/core/options.py @@ -46,6 +46,8 @@ OTHER_OPTIONS: List[str] = ["debug"] +TERRAFORM_HOOK_OPTIONS: List[str] = ["terraform_plan_file"] + ALL_OPTIONS: List[str] = ( REQUIRED_OPTIONS + TEMPLATE_OPTIONS @@ -55,6 +57,7 @@ + EXTENSION_OPTIONS + CONFIGURATION_OPTION_NAMES + ALL_COMMON_OPTIONS + + TERRAFORM_HOOK_OPTIONS ) OPTIONS_INFO: Dict[str, Dict] = { @@ -78,6 +81,7 @@ ), ], }, + "Terraform Hook Options": {"option_names": {opt: {"rank": idx} for idx, opt in enumerate(TERRAFORM_HOOK_OPTIONS)}}, } add_common_options_info(OPTIONS_INFO) diff --git a/samcli/commands/local/start_api/cli.py b/samcli/commands/local/start_api/cli.py index 7c712c4561..9d3247eeef 100644 --- a/samcli/commands/local/start_api/cli.py +++ b/samcli/commands/local/start_api/cli.py @@ -15,6 +15,7 @@ generate_next_command_recommendation, hook_name_click_option, skip_prepare_infra_option, + terraform_plan_file_option, ) from samcli.commands.local.cli_common.options import ( invoke_common_options, @@ -59,6 +60,7 @@ context_settings={"max_content_width": 120}, ) @configuration_option(provider=ConfigProvider(section="parameters")) +@terraform_plan_file_option @hook_name_click_option( force_prepare=False, invalid_coexist_options=["t", "template-file", "template", "parameter-overrides"] ) @@ -109,6 +111,7 @@ def cli( invoke_image, hook_name, skip_prepare_infra, + terraform_plan_file, ): """ `sam local start-api` command entry point diff --git a/samcli/commands/local/start_api/core/options.py b/samcli/commands/local/start_api/core/options.py index 21bb1bf822..a71776269f 100644 --- a/samcli/commands/local/start_api/core/options.py +++ b/samcli/commands/local/start_api/core/options.py @@ -47,6 +47,8 @@ "static_dir", ] +TERRAFORM_HOOK_OPTIONS: List[str] = ["terraform_plan_file"] + ALL_OPTIONS: List[str] = ( REQUIRED_OPTIONS + TEMPLATE_OPTIONS @@ -56,6 +58,7 @@ + CONFIGURATION_OPTION_NAMES + ALL_COMMON_OPTIONS + EXTENSION_OPTIONS + + TERRAFORM_HOOK_OPTIONS ) OPTIONS_INFO: Dict[str, Dict] = { @@ -79,6 +82,7 @@ ), ], }, + "Terraform Hook Options": {"option_names": {opt: {"rank": idx} for idx, opt in enumerate(TERRAFORM_HOOK_OPTIONS)}}, } add_common_options_info(OPTIONS_INFO) diff --git a/samcli/commands/local/start_lambda/cli.py b/samcli/commands/local/start_lambda/cli.py index 744f7f8fe5..8dd78dc2f9 100644 --- a/samcli/commands/local/start_lambda/cli.py +++ b/samcli/commands/local/start_lambda/cli.py @@ -15,6 +15,7 @@ generate_next_command_recommendation, hook_name_click_option, skip_prepare_infra_option, + terraform_plan_file_option, ) from samcli.commands.local.cli_common.options import ( invoke_common_options, @@ -53,6 +54,7 @@ context_settings={"max_content_width": 120}, ) @configuration_option(provider=ConfigProvider(section="parameters")) +@terraform_plan_file_option @hook_name_click_option( force_prepare=False, invalid_coexist_options=["t", "template-file", "template", "parameter-overrides"] ) @@ -96,6 +98,7 @@ def cli( invoke_image, hook_name, skip_prepare_infra, + terraform_plan_file, ): """ `sam local start-lambda` command entry point diff --git a/samcli/commands/local/start_lambda/core/options.py b/samcli/commands/local/start_lambda/core/options.py index 6d6168c18f..5ff5d4e115 100644 --- a/samcli/commands/local/start_lambda/core/options.py +++ b/samcli/commands/local/start_lambda/core/options.py @@ -46,6 +46,8 @@ CONFIGURATION_OPTION_NAMES: List[str] = ["config_env", "config_file"] +TERRAFORM_HOOK_OPTIONS: List[str] = ["terraform_plan_file"] + ALL_OPTIONS: List[str] = ( REQUIRED_OPTIONS + TEMPLATE_OPTIONS @@ -55,6 +57,7 @@ + EXTENSION_OPTIONS + CONFIGURATION_OPTION_NAMES + ALL_COMMON_OPTIONS + + TERRAFORM_HOOK_OPTIONS ) OPTIONS_INFO: Dict[str, Dict] = { @@ -78,6 +81,7 @@ ), ], }, + "Terraform Hook Options": {"option_names": {opt: {"rank": idx} for idx, opt in enumerate(TERRAFORM_HOOK_OPTIONS)}}, } add_common_options_info(OPTIONS_INFO) diff --git a/samcli/hook_packages/terraform/hooks/prepare/hook.py b/samcli/hook_packages/terraform/hooks/prepare/hook.py index ccbd357f39..79553f3089 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/hook.py +++ b/samcli/hook_packages/terraform/hooks/prepare/hook.py @@ -55,49 +55,23 @@ def prepare(params: dict) -> dict: output_dir_path = os.path.normpath(os.path.join(terraform_application_dir, output_dir_path)) LOG.debug("The normalized OutputDirPath value is %s", output_dir_path) - skip_prepare_infra = params.get("SkipPrepareInfra") + skip_prepare_infra = params.get("SkipPrepareInfra", False) metadata_file_path = os.path.join(output_dir_path, TERRAFORM_METADATA_FILE) + plan_file = params.get("PlanFile") + if skip_prepare_infra and os.path.exists(metadata_file_path): LOG.info("Skipping preparation stage, the metadata file already exists at %s", metadata_file_path) else: - log_msg = ( - ( - "The option to skip infrastructure preparation was provided, but AWS SAM CLI could not find " - f"the metadata file. Preparing anyways.{os.linesep}Initializing Terraform application" - ) - if skip_prepare_infra - else "Initializing Terraform application" - ) + try: # initialize terraform application - LOG.info(log_msg) - invoke_subprocess_with_loading_pattern( - command_args={ - "args": ["terraform", "init", "-input=false"], - "cwd": terraform_application_dir, - } - ) - - # get json output of terraform plan - LOG.info("Creating terraform plan and getting JSON output") - with osutils.tempfile_platform_independent() as temp_file: - invoke_subprocess_with_loading_pattern( - # input false to avoid SAM CLI to stuck in case if the - # Terraform project expects input, and customer does not provide it. - command_args={ - "args": ["terraform", "plan", "-out", temp_file.name, "-input=false"], - "cwd": terraform_application_dir, - } - ) - - result = run( - ["terraform", "show", "-json", temp_file.name], - check=True, - capture_output=True, - cwd=terraform_application_dir, - ) - tf_json = json.loads(result.stdout) + if not plan_file: + tf_json = _generate_plan_file(skip_prepare_infra, terraform_application_dir) + else: + LOG.info(f"Using provided plan file: {plan_file}") + with open(plan_file, "r") as f: + tf_json = json.load(f) # convert terraform to cloudformation LOG.info("Generating metadata file") @@ -118,26 +92,7 @@ def prepare(params: dict) -> dict: LOG.info("Finished generating metadata file. Storing in %s", metadata_file_path) with open(metadata_file_path, "w+") as metadata_file: json.dump(cfn_dict, metadata_file) - except CalledProcessError as e: - stderr_output = str(e.stderr) - - # stderr can take on bytes or just be a plain string depending on terminal - if isinstance(e.stderr, bytes): - stderr_output = e.stderr.decode("utf-8") - - # one of the subprocess.run calls resulted in non-zero exit code or some OS error - LOG.debug( - "Error running terraform command: \n" "cmd: %s \n" "stdout: %s \n" "stderr: %s \n", - e.cmd, - e.stdout, - stderr_output, - ) - raise PrepareHookException( - f"There was an error while preparing the Terraform application.\n{stderr_output}" - ) from e - except LoadingPatternError as e: - raise PrepareHookException(f"Error occurred when invoking a process: {e}") from e except OSError as e: raise PrepareHookException(f"OSError: {e}") from e @@ -167,3 +122,78 @@ def _update_resources_paths(cfn_resources: Dict[str, Any], terraform_application original_path = resource.get("Properties", {}).get(attribute) if isinstance(original_path, str) and not os.path.isabs(original_path): resource["Properties"][attribute] = str(Path(terraform_application_dir).joinpath(original_path)) + + +def _generate_plan_file(skip_prepare_infra: bool, terraform_application_dir: str) -> dict: + """ + Call the relevant Terraform commands to generate, load and return the Terraform plan file + which the AWS SAM CLI will then parse to extract the fields required to run local emulators. + + Parameters + ---------- + skip_prepare_infra: bool + Flag to skip skip prepare hook if we already have the metadata file. Default is False. + terraform_application_dir: str + The path where the hook can find the TF application. + Returns + ------- + dict + The Terraform plan file in JSON format + """ + log_msg = ( + ( + "The option to skip infrastructure preparation was provided, but AWS SAM CLI could not find " + f"the metadata file. Preparing anyways.{os.linesep}Initializing Terraform application" + ) + if skip_prepare_infra + else "Initializing Terraform application" + ) + LOG.info(log_msg) + try: + invoke_subprocess_with_loading_pattern( + command_args={ + "args": ["terraform", "init", "-input=false"], + "cwd": terraform_application_dir, + } + ) + + # get json output of terraform plan + LOG.info("Creating terraform plan and getting JSON output") + with osutils.tempfile_platform_independent() as temp_file: + invoke_subprocess_with_loading_pattern( + # input false to avoid SAM CLI to stuck in case if the + # Terraform project expects input, and customer does not provide it. + command_args={ + "args": ["terraform", "plan", "-out", temp_file.name, "-input=false"], + "cwd": terraform_application_dir, + } + ) + + result = run( + ["terraform", "show", "-json", temp_file.name], + check=True, + capture_output=True, + cwd=terraform_application_dir, + ) + except CalledProcessError as e: + stderr_output = str(e.stderr) + + # stderr can take on bytes or just be a plain string depending on terminal + if isinstance(e.stderr, bytes): + stderr_output = e.stderr.decode("utf-8") + + # one of the subprocess.run calls resulted in non-zero exit code or some OS error + LOG.debug( + "Error running terraform command: \n" "cmd: %s \n" "stdout: %s \n" "stderr: %s \n", + e.cmd, + e.stdout, + stderr_output, + ) + + raise PrepareHookException( + f"There was an error while preparing the Terraform application.\n{stderr_output}" + ) from e + except LoadingPatternError as e: + raise PrepareHookException(f"Error occurred when invoking a process: {e}") from e + + return dict(json.loads(result.stdout)) diff --git a/samcli/lib/hook/hook_wrapper.py b/samcli/lib/hook/hook_wrapper.py index 2bcaa4bd2c..47b0a2fc25 100644 --- a/samcli/lib/hook/hook_wrapper.py +++ b/samcli/lib/hook/hook_wrapper.py @@ -51,6 +51,7 @@ def prepare( aws_profile: Optional[str] = None, aws_region: Optional[str] = None, skip_prepare_infra: bool = False, + plan_file: Optional[str] = None, ) -> str: """ Run the prepare hook to generate the IaC Metadata file. @@ -69,6 +70,8 @@ def prepare( AWS region to use. Default is None (use default region) skip_prepare_infra: bool Flag to skip skip prepare hook if we already have the metadata file. Default is False. + plan_file: Optional[str] + Provided plan file to use instead of generating one from the hook Returns ------- @@ -86,6 +89,8 @@ def prepare( params["Profile"] = aws_profile if aws_region: params["Region"] = aws_region + if plan_file: + params["PlanFile"] = plan_file output = self._execute("prepare", params) diff --git a/schema/samcli.json b/schema/samcli.json index cbe8ee4fce..f5daf84cbb 100644 --- a/schema/samcli.json +++ b/schema/samcli.json @@ -225,9 +225,14 @@ "properties": { "parameters": { "title": "Parameters for the build command", - "description": "Available parameters for the build command:\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* use_container:\nBuild functions within an AWS Lambda-like container.\n* container_env_var:\nEnvironment variables to be passed into build containers\nResource format (FuncName.VarName=Value) or Global format (VarName=Value).\n\n Example: --container-env-var Func1.VAR1=value1 --container-env-var VAR2=value2\n* container_env_var_file:\nEnvironment variables json file (e.g., env_vars.json) to be passed to build containers.\n* build_image:\nContainer image URIs for building functions/layers. You can specify for all functions/layers with just the image URI (--build-image public.ecr.aws/sam/build-nodejs18.x:latest). You can specify for each individual function with (--build-image FunctionLogicalID=public.ecr.aws/sam/build-nodejs18.x:latest). A combination of the two can be used. If a function does not have build image specified or an image URI for all functions, the default SAM CLI build images will be used.\n* exclude:\nName of the resource(s) to exclude from AWS SAM CLI build.\n* parallel:\nEnable parallel builds for AWS SAM template's functions and layers.\n* mount_with:\nSpecify mount mode for building functions/layers inside container. If it is mounted with write permissions, some files in source code directory may be changed/added by the build process. By default the source code directory is read only.\n* build_dir:\nDirectory to store build artifacts.Note: This directory will be first removed before starting a build.\n* cache_dir:\nDirectory to store cached artifacts. The default cache directory is .aws-sam/cache\n* base_dir:\nResolve relative paths to function's source code with respect to this directory. Use this if SAM template and source code are not in same enclosing folder. By default, relative paths are resolved with respect to the SAM template's location.\n* manifest:\nPath to a custom dependency manifest. Example: custom-package.json\n* cached:\nEnable cached builds.Reuse build artifacts that have not changed from previous builds. \n\nAWS SAM CLI evaluates if files in your project directory have changed. \n\nNote: AWS SAM CLI does not evaluate changes made to third party modules that the project depends on.Example: Python function includes a requirements.txt file with the following entry requests=1.x and the latest request module version changes from 1.1 to 1.2, AWS SAM CLI will not pull the latest version until a non-cached build is run.\n* template_file:\nAWS SAM template file.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)", + "description": "Available parameters for the build command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* use_container:\nBuild functions within an AWS Lambda-like container.\n* container_env_var:\nEnvironment variables to be passed into build containers\nResource format (FuncName.VarName=Value) or Global format (VarName=Value).\n\n Example: --container-env-var Func1.VAR1=value1 --container-env-var VAR2=value2\n* container_env_var_file:\nEnvironment variables json file (e.g., env_vars.json) to be passed to build containers.\n* build_image:\nContainer image URIs for building functions/layers. You can specify for all functions/layers with just the image URI (--build-image public.ecr.aws/sam/build-nodejs18.x:latest). You can specify for each individual function with (--build-image FunctionLogicalID=public.ecr.aws/sam/build-nodejs18.x:latest). A combination of the two can be used. If a function does not have build image specified or an image URI for all functions, the default SAM CLI build images will be used.\n* exclude:\nName of the resource(s) to exclude from AWS SAM CLI build.\n* parallel:\nEnable parallel builds for AWS SAM template's functions and layers.\n* mount_with:\nSpecify mount mode for building functions/layers inside container. If it is mounted with write permissions, some files in source code directory may be changed/added by the build process. By default the source code directory is read only.\n* build_dir:\nDirectory to store build artifacts.Note: This directory will be first removed before starting a build.\n* cache_dir:\nDirectory to store cached artifacts. The default cache directory is .aws-sam/cache\n* base_dir:\nResolve relative paths to function's source code with respect to this directory. Use this if SAM template and source code are not in same enclosing folder. By default, relative paths are resolved with respect to the SAM template's location.\n* manifest:\nPath to a custom dependency manifest. Example: custom-package.json\n* cached:\nEnable cached builds.Reuse build artifacts that have not changed from previous builds. \n\nAWS SAM CLI evaluates if files in your project directory have changed. \n\nNote: AWS SAM CLI does not evaluate changes made to third party modules that the project depends on.Example: Python function includes a requirements.txt file with the following entry requests=1.x and the latest request module version changes from 1.1 to 1.2, AWS SAM CLI will not pull the latest version until a non-cached build is run.\n* template_file:\nAWS SAM template file.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)", "type": "object", "properties": { + "terraform_plan_file": { + "title": "terraform_plan_file", + "type": "string", + "description": "Used for passing a custom plan file when executing the Terraform hook." + }, "hook_name": { "title": "hook_name", "type": "string", @@ -365,9 +370,14 @@ "properties": { "parameters": { "title": "Parameters for the local invoke command", - "description": "Available parameters for the local invoke command:\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* event:\nJSON file containing event data passed to the Lambda function during invoke. If this option is not specified, no event is assumed. Pass in the value '-' to input JSON via stdin\n* no_event:\nDEPRECATED: By default no event is assumed.\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing environment variables to be set within the container environment\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs14.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs14.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)", + "description": "Available parameters for the local invoke command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* event:\nJSON file containing event data passed to the Lambda function during invoke. If this option is not specified, no event is assumed. Pass in the value '-' to input JSON via stdin\n* no_event:\nDEPRECATED: By default no event is assumed.\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing environment variables to be set within the container environment\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs14.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs14.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)", "type": "object", "properties": { + "terraform_plan_file": { + "title": "terraform_plan_file", + "type": "string", + "description": "Used for passing a custom plan file when executing the Terraform hook." + }, "hook_name": { "title": "hook_name", "type": "string", @@ -516,9 +526,14 @@ "properties": { "parameters": { "title": "Parameters for the local start api command", - "description": "Available parameters for the local start api command:\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3000')\n* static_dir:\nAny static assets (e.g. CSS/Javascript/HTML) files located in this directory will be presented at /\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing environment variables to be set within the container environment\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs14.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs14.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)", + "description": "Available parameters for the local start api command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3000')\n* static_dir:\nAny static assets (e.g. CSS/Javascript/HTML) files located in this directory will be presented at /\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing environment variables to be set within the container environment\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs14.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs14.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)", "type": "object", "properties": { + "terraform_plan_file": { + "title": "terraform_plan_file", + "type": "string", + "description": "Used for passing a custom plan file when executing the Terraform hook." + }, "hook_name": { "title": "hook_name", "type": "string", @@ -703,9 +718,14 @@ "properties": { "parameters": { "title": "Parameters for the local start lambda command", - "description": "Available parameters for the local start lambda command:\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3001')\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing environment variables to be set within the container environment\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs14.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs14.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)", + "description": "Available parameters for the local start lambda command:\n* terraform_plan_file:\nUsed for passing a custom plan file when executing the Terraform hook.\n* hook_name:\nHook package id to extend AWS SAM CLI commands functionality. \n\nExample: `terraform` to extend AWS SAM CLI commands functionality to support terraform applications. \n\nAvailable Hook Names: ['terraform']\n* skip_prepare_infra:\nSkip preparation stage when there are no infrastructure changes. Only used in conjunction with --hook-name.\n* host:\nLocal hostname or IP address to bind to (default: '127.0.0.1')\n* port:\nLocal port number to listen on (default: '3001')\n* template_file:\nAWS SAM template which references built artifacts for resources in the template. (if applicable)\n* env_vars:\nJSON file containing values for Lambda function's environment variables.\n* parameter_overrides:\nString that contains AWS CloudFormation parameter overrides encoded as key=value pairs.\n* debug_port:\nWhen specified, Lambda function container will start in debug mode and will expose this port on localhost.\n* debugger_path:\nHost path to a debugger that will be mounted into the Lambda container.\n* debug_args:\nAdditional arguments to be passed to the debugger.\n* container_env_vars:\nJSON file containing environment variables to be set within the container environment\n* docker_volume_basedir:\nSpecify the location basedir where the SAM template exists. If Docker is running on a remote machine, Path of the SAM template must be mounted on the Docker machine and modified to match the remote machine.\n* log_file:\nFile to capture output logs.\n* layer_cache_basedir:\nSpecify the location basedir where the lambda layers used by the template will be downloaded to.\n* skip_pull_image:\nSkip pulling down the latest Docker image for Lambda runtime.\n* docker_network:\nName or ID of an existing docker network for AWS Lambda docker containers to connect to, along with the default bridge network. If not specified, the Lambda containers will only connect to the default bridge docker network.\n* force_image_build:\nForce rebuilding the image used for invoking functions with layers.\n* warm_containers:\nOptional. Specifies how AWS SAM CLI manages \ncontainers for each function.\nTwo modes are available:\nEAGER: Containers for all functions are \nloaded at startup and persist between \ninvocations.\nLAZY: Containers are only loaded when each \nfunction is first invoked. Those containers \npersist for additional invocations.\n* debug_function:\nOptional. Specifies the Lambda Function logicalId to apply debug options to when --warm-containers is specified. This parameter applies to --debug-port, --debugger-path, and --debug-args.\n* shutdown:\nEmulate a shutdown event after invoke completes, to test extension handling of shutdown behavior.\n* container_host:\nHost of locally emulated Lambda container. This option is useful when the container runs on a different host than AWS SAM CLI. For example, if one wants to run AWS SAM CLI in a Docker container on macOS, this option could specify `host.docker.internal`\n* container_host_interface:\nIP address of the host network interface that container ports should bind to. Use 0.0.0.0 to bind to all interfaces.\n* invoke_image:\nContainer image URIs for invoking functions or starting api and function. One can specify the image URI used for the local function invocation (--invoke-image public.ecr.aws/sam/build-nodejs14.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs14.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)", "type": "object", "properties": { + "terraform_plan_file": { + "title": "terraform_plan_file", + "type": "string", + "description": "Used for passing a custom plan file when executing the Terraform hook." + }, "hook_name": { "title": "hook_name", "type": "string", diff --git a/tests/unit/commands/_utils/custom_options/test_hook_package_id_option.py b/tests/unit/commands/_utils/custom_options/test_hook_package_id_option.py index 807d10c7f0..8e7c607d97 100644 --- a/tests/unit/commands/_utils/custom_options/test_hook_package_id_option.py +++ b/tests/unit/commands/_utils/custom_options/test_hook_package_id_option.py @@ -92,7 +92,7 @@ def test_valid_hook_package_with_only_hook_id_option( args = [] hook_name_option.handle_parse_result(ctx, opts, args) self.iac_hook_wrapper_instance_mock.prepare.assert_called_once_with( - os.path.join(self.cwd_path, ".aws-sam-iacs", "iacs_metadata"), self.cwd_path, False, None, None, False + os.path.join(self.cwd_path, ".aws-sam-iacs", "iacs_metadata"), self.cwd_path, False, None, None, False, None ) self.assertEqual(opts.get("template_file"), self.metadata_path) @@ -129,6 +129,7 @@ def test_valid_hook_package_with_other_options( "test", "us-east-1", False, + None, ) self.assertEqual(opts.get("template_file"), self.metadata_path) @@ -276,6 +277,7 @@ def test_valid_hook_package_with_beta_feature_option( None, None, False, + None, ) self.assertEqual(opts.get("template_file"), self.metadata_path) @@ -323,6 +325,7 @@ def test_valid_hook_package_with_beta_feature_option_in_sam_config( None, None, False, + None, ) self.assertEqual(opts.get("template_file"), metadata_path) @@ -375,6 +378,7 @@ def test_valid_hook_package_with_beta_feature_option_in_environment_variable( None, None, False, + None, ) self.assertEqual(opts.get("template_file"), metadata_path) @@ -410,7 +414,7 @@ def test_valid_hook_package_with_skipping_prepare_hook_and_built_path_does_not_e args = [] hook_name_option.handle_parse_result(ctx, opts, args) self.iac_hook_wrapper_instance_mock.prepare.assert_called_once_with( - os.path.join(self.cwd_path, ".aws-sam-iacs", "iacs_metadata"), self.cwd_path, False, None, None, False + os.path.join(self.cwd_path, ".aws-sam-iacs", "iacs_metadata"), self.cwd_path, False, None, None, False, None ) self.assertEqual(opts.get("template_file"), self.metadata_path) @@ -449,7 +453,7 @@ def test_valid_hook_package_with_use_container_and_build_image( args = [] hook_name_option.handle_parse_result(ctx, opts, args) self.iac_hook_wrapper_instance_mock.prepare.assert_called_once_with( - os.path.join(self.cwd_path, ".aws-sam-iacs", "iacs_metadata"), self.cwd_path, False, None, None, False + os.path.join(self.cwd_path, ".aws-sam-iacs", "iacs_metadata"), self.cwd_path, False, None, None, False, None ) self.assertEqual(opts.get("template_file"), self.metadata_path) @@ -525,6 +529,6 @@ def test_valid_hook_package_with_use_container_false_and_no_build_image( args = [] hook_name_option.handle_parse_result(ctx, opts, args) self.iac_hook_wrapper_instance_mock.prepare.assert_called_once_with( - os.path.join(self.cwd_path, ".aws-sam-iacs", "iacs_metadata"), self.cwd_path, False, None, None, False + os.path.join(self.cwd_path, ".aws-sam-iacs", "iacs_metadata"), self.cwd_path, False, None, None, False, None ) self.assertEqual(opts.get("template_file"), self.metadata_path) diff --git a/tests/unit/commands/buildcmd/core/test_command.py b/tests/unit/commands/buildcmd/core/test_command.py index 2869e4d0b2..0f2e191f7c 100644 --- a/tests/unit/commands/buildcmd/core/test_command.py +++ b/tests/unit/commands/buildcmd/core/test_command.py @@ -33,6 +33,7 @@ def test_get_options_build_command_text(self, mock_get_params): MockParams(rv=("--parameter-overrides", ""), name="parameter_overrides"), MockParams(rv=("--beta-features", ""), name="beta_features"), MockParams(rv=("--template-file", ""), name="template_file"), + MockParams(rv=("--terraform-plan-file", ""), name="terraform_plan_file"), ] cmd = BuildCommand(name="sync", requires_credentials=False, description=DESCRIPTION) @@ -41,6 +42,7 @@ def test_get_options_build_command_text(self, mock_get_params): "Container Options": [("", ""), ("--use-container", ""), ("", "")], "Configuration Options": [("", ""), ("--config-file", ""), ("", "")], "Extension Options": [("", ""), ("--hook-name", ""), ("", "")], + "Terraform Hook Options": [("", ""), ("--terraform-plan-file", ""), ("", "")], "Build Strategy Options": [("", ""), ("--parallel", ""), ("", "")], "Artifact Location Options": [("", ""), ("--build-dir", ""), ("", "")], "Template Options": [("", ""), ("--parameter-overrides", ""), ("", "")], diff --git a/tests/unit/commands/local/invoke/core/test_command.py b/tests/unit/commands/local/invoke/core/test_command.py index 78431388ac..a7d77e0a95 100644 --- a/tests/unit/commands/local/invoke/core/test_command.py +++ b/tests/unit/commands/local/invoke/core/test_command.py @@ -32,6 +32,7 @@ def test_get_options_local_invoke_command_text(self, mock_get_params): MockParams(rv=("--log-file", ""), name="log_file"), MockParams(rv=("--beta-features", ""), name="beta_features"), MockParams(rv=("--debug", ""), name="debug"), + MockParams(rv=("--terraform-plan-file", ""), name="terraform_plan_file"), ] cmd = InvokeCommand(name="local invoke", requires_credentials=False, description=DESCRIPTION) @@ -43,6 +44,7 @@ def test_get_options_local_invoke_command_text(self, mock_get_params): "Description": [(cmd.description + cmd.description_addendum, "")], "Examples": [], "Extension Options": [("", ""), ("--hook_name", ""), ("", "")], + "Terraform Hook Options": [("", ""), ("--terraform-plan-file", ""), ("", "")], "Beta Options": [("", ""), ("--beta-features", ""), ("", "")], "Invoke default lambda function with no event": [("", ""), ("$sam local invoke\x1b[0m", "")], "Invoke lambda function with stdin input": [ diff --git a/tests/unit/commands/local/start_api/core/test_command.py b/tests/unit/commands/local/start_api/core/test_command.py index 67eab70400..09a8a17edc 100644 --- a/tests/unit/commands/local/start_api/core/test_command.py +++ b/tests/unit/commands/local/start_api/core/test_command.py @@ -32,6 +32,7 @@ def test_get_options_local_start_api_command(self, mock_get_params): MockParams(rv=("--beta-features", ""), name="beta_features"), MockParams(rv=("--log-file", ""), name="log_file"), MockParams(rv=("--debug", ""), name="debug"), + MockParams(rv=("--terraform-plan-file", ""), name="terraform_plan_file"), ] cmd = InvokeAPICommand(name="local start-api", requires_credentials=False, description=DESCRIPTION) @@ -43,6 +44,7 @@ def test_get_options_local_start_api_command(self, mock_get_params): "Description": [(cmd.description + cmd.description_addendum, "")], "Examples": [("", ""), ("$sam local start-api\x1b[0m", "")], "Extension Options": [("", ""), ("--hook_name", ""), ("", "")], + "Terraform Hook Options": [("", ""), ("--terraform-plan-file", ""), ("", "")], "Other Options": [("", ""), ("--debug", ""), ("", "")], "Beta Options": [("", ""), ("--beta-features", ""), ("", "")], "Required Options": [("", ""), ("--template-file", ""), ("", "")], diff --git a/tests/unit/commands/local/start_lambda/core/test_command.py b/tests/unit/commands/local/start_lambda/core/test_command.py index 631c95c0f1..c0ef4fa826 100644 --- a/tests/unit/commands/local/start_lambda/core/test_command.py +++ b/tests/unit/commands/local/start_lambda/core/test_command.py @@ -32,6 +32,7 @@ def test_get_options_local_start_lambda_command(self, mock_get_params): MockParams(rv=("--log-file", ""), name="log_file"), MockParams(rv=("--beta-features", ""), name="beta_features"), MockParams(rv=("--debug", ""), name="debug"), + MockParams(rv=("--terraform-plan-file", ""), name="terraform_plan_file"), ] cmd = InvokeLambdaCommand(name="local start-api", requires_credentials=False, description=DESCRIPTION) @@ -42,6 +43,7 @@ def test_get_options_local_start_lambda_command(self, mock_get_params): "Container Options": [("", ""), ("--port", ""), ("", "")], "Description": [(cmd.description + cmd.description_addendum, "")], "Examples": [], + "Terraform Hook Options": [("", ""), ("--terraform-plan-file", ""), ("", "")], "Setup": [("", ""), ("Start the local lambda endpoint.", ""), ("$sam local start-lambda\x1b[0m", "")], "Template Options": [("", ""), ("--parameter-overrides", ""), ("", "")], "Using AWS CLI": [ diff --git a/tests/unit/hook_packages/terraform/hooks/prepare/test_hook.py b/tests/unit/hook_packages/terraform/hooks/prepare/test_hook.py index aabba14f3e..7b7902343b 100644 --- a/tests/unit/hook_packages/terraform/hooks/prepare/test_hook.py +++ b/tests/unit/hook_packages/terraform/hooks/prepare/test_hook.py @@ -1,6 +1,6 @@ """Test Terraform prepare hook""" from subprocess import CalledProcessError -from unittest.mock import Mock, call, patch, MagicMock +from unittest.mock import Mock, call, patch, MagicMock, ANY from parameterized import parameterized from tests.unit.hook_packages.terraform.hooks.prepare.prepare_base import PrepareHookUnitBase @@ -352,3 +352,32 @@ def test_skip_prepare_infra_with_metadata_file(self, run_mock, os_mock): prepare(self.prepare_params) run_mock.assert_not_called() + + @patch("samcli.hook_packages.terraform.hooks.prepare.hook.invoke_subprocess_with_loading_pattern") + @patch("samcli.hook_packages.terraform.hooks.prepare.hook._update_resources_paths") + @patch("samcli.hook_packages.terraform.hooks.prepare.hook.translate_to_cfn") + @patch("builtins.open") + @patch("samcli.hook_packages.terraform.hooks.prepare.hook.osutils.tempfile_platform_independent") + @patch("samcli.hook_packages.terraform.hooks.prepare.hook.os") + @patch("samcli.hook_packages.terraform.hooks.prepare.hook.json") + @patch("samcli.hook_packages.terraform.hooks.prepare.hook.run") + def test_uses_custom_plan_file( + self, + mock_subprocess_run, + mock_json, + mock_os, + named_temporary_file_mock, + mock_open, + mock_translate_to_cfn, + mock_update_resources_paths, + mock_subprocess_loader, + ): + self.prepare_params["PlanFile"] = "my-custom-plan.json" + + file_mock = Mock() + mock_open.return_value.__enter__.return_value = file_mock + + prepare(self.prepare_params) + + mock_open.assert_has_calls([call("my-custom-plan.json", "r"), ANY]) + mock_json.load.assert_called_once_with(file_mock)