diff --git a/samcli/commands/local/cli_common/options.py b/samcli/commands/local/cli_common/options.py index a154481421..85552b6a87 100644 --- a/samcli/commands/local/cli_common/options.py +++ b/samcli/commands/local/cli_common/options.py @@ -212,6 +212,13 @@ def invoke_common_options(f): help="Specify the location basedir where the lambda layers used by the template will be downloaded to.", default=get_default_layer_cache_dir(), ), + click.option( + "--filesystem", + type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True), + help="Local directory to mount as EFS filesystem for Lambda functions. " + "Maps to the LocalMountPath specified in the function's FileSystemConfigs. " + "If no FileSystemConfigs is specified, defaults to /mnt/efs", + ), ] + docker_click_options() + [ diff --git a/samcli/commands/local/invoke/cli.py b/samcli/commands/local/invoke/cli.py index 60a9c4580c..dd39eb52e0 100644 --- a/samcli/commands/local/invoke/cli.py +++ b/samcli/commands/local/invoke/cli.py @@ -100,6 +100,7 @@ def cli( docker_network, log_file, layer_cache_basedir, + filesystem, skip_pull_image, force_image_build, shutdown, @@ -138,6 +139,7 @@ def cli( docker_network, log_file, layer_cache_basedir, + filesystem, skip_pull_image, force_image_build, shutdown, @@ -168,6 +170,7 @@ def do_cli( # pylint: disable=R0914 docker_network, log_file, layer_cache_basedir, + filesystem, skip_pull_image, force_image_build, shutdown, diff --git a/samcli/commands/local/invoke/core/options.py b/samcli/commands/local/invoke/core/options.py index a0b4b4f6cd..0f689954ca 100644 --- a/samcli/commands/local/invoke/core/options.py +++ b/samcli/commands/local/invoke/core/options.py @@ -36,6 +36,7 @@ "add_host", "invoke_image", "runtime", + "filesystem", "mount_symlinks", "no_memory_limit", ] diff --git a/samcli/commands/local/start_api/cli.py b/samcli/commands/local/start_api/cli.py index bd9d36ead5..bbee1f46b9 100644 --- a/samcli/commands/local/start_api/cli.py +++ b/samcli/commands/local/start_api/cli.py @@ -123,6 +123,7 @@ def cli( docker_network, log_file, layer_cache_basedir, + filesystem, skip_pull_image, force_image_build, parameter_overrides, @@ -164,6 +165,7 @@ def cli( docker_network, log_file, layer_cache_basedir, + filesystem, skip_pull_image, force_image_build, parameter_overrides, @@ -197,6 +199,7 @@ def do_cli( # pylint: disable=R0914 docker_network, log_file, layer_cache_basedir, + filesystem, skip_pull_image, force_image_build, parameter_overrides, @@ -256,6 +259,7 @@ def do_cli( # pylint: disable=R0914 container_host_interface=container_host_interface, invoke_images=processed_invoke_images, add_host=add_host, + filesystem_dir=filesystem, no_mem_limit=no_mem_limit, ) as invoke_context: ssl_context = (ssl_cert_file, ssl_key_file) if ssl_cert_file else None diff --git a/samcli/commands/local/start_api/core/options.py b/samcli/commands/local/start_api/core/options.py index 750b8c6d15..470af0a21d 100644 --- a/samcli/commands/local/start_api/core/options.py +++ b/samcli/commands/local/start_api/core/options.py @@ -34,6 +34,7 @@ "docker_volume_basedir", "skip_pull_image", "docker_network", + "filesystem", "force_image_build", "no_memory_limit", "warm_containers", diff --git a/samcli/commands/local/start_lambda/cli.py b/samcli/commands/local/start_lambda/cli.py index f4df9156a0..96329bf41e 100644 --- a/samcli/commands/local/start_lambda/cli.py +++ b/samcli/commands/local/start_lambda/cli.py @@ -85,6 +85,7 @@ def cli( docker_network, log_file, layer_cache_basedir, + filesystem, skip_pull_image, force_image_build, parameter_overrides, @@ -122,6 +123,7 @@ def cli( docker_network, log_file, layer_cache_basedir, + filesystem, skip_pull_image, force_image_build, parameter_overrides, @@ -151,6 +153,7 @@ def do_cli( # pylint: disable=R0914 docker_network, log_file, layer_cache_basedir, + filesystem, skip_pull_image, force_image_build, parameter_overrides, @@ -208,6 +211,7 @@ def do_cli( # pylint: disable=R0914 container_host_interface=container_host_interface, add_host=add_host, invoke_images=processed_invoke_images, + filesystem_dir=filesystem, no_mem_limit=no_mem_limit, ) as invoke_context: service = LocalLambdaService(lambda_invoke_context=invoke_context, port=port, host=host) diff --git a/samcli/commands/local/start_lambda/core/options.py b/samcli/commands/local/start_lambda/core/options.py index 4eecf90483..2d75465760 100644 --- a/samcli/commands/local/start_lambda/core/options.py +++ b/samcli/commands/local/start_lambda/core/options.py @@ -31,6 +31,7 @@ "docker_volume_basedir", "skip_pull_image", "docker_network", + "filesystem", "force_image_build", "shutdown", "container_host", diff --git a/schema/samcli.json b/schema/samcli.json index 5203d60bc7..6e421aebf8 100644 --- a/schema/samcli.json +++ b/schema/samcli.json @@ -408,7 +408,7 @@ "properties": { "parameters": { "title": "Parameters for the local invoke command", - "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* runtime:\nLambda runtime used to invoke the function.\n\nRuntimes: dotnet8, dotnet6, go1.x, java21, java17, java11, java8.al2, nodejs22.x, nodejs20.x, nodejs18.x, nodejs16.x, provided, provided.al2, provided.al2023, python3.9, python3.8, python3.13, python3.12, python3.11, python3.10, ruby3.4, ruby3.3, ruby3.2\n* mount_symlinks:\nSpecify if symlinks at the top level of the code should be mounted inside the container. Activating this flag could allow access to locations outside of your workspace by using a symbolic link. By default symlinks are not mounted.\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 additional environment variables to be set within the container when used in a debugging session locally.\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* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\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-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\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)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", + "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* runtime:\nLambda runtime used to invoke the function.\n\nRuntimes: dotnet8, dotnet6, go1.x, java21, java17, java11, java8.al2, nodejs22.x, nodejs20.x, nodejs18.x, nodejs16.x, provided, provided.al2, provided.al2023, python3.9, python3.8, python3.13, python3.12, python3.11, python3.10, ruby3.4, ruby3.3, ruby3.2\n* mount_symlinks:\nSpecify if symlinks at the top level of the code should be mounted inside the container. Activating this flag could allow access to locations outside of your workspace by using a symbolic link. By default symlinks are not mounted.\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 additional environment variables to be set within the container when used in a debugging session locally.\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* filesystem:\nLocal directory to mount as EFS filesystem for Lambda functions. Maps to the LocalMountPath specified in the function's FileSystemConfigs. If no FileSystemConfigs is specified, defaults to /mnt/efs\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* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\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-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\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)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", "type": "object", "properties": { "terraform_plan_file": { @@ -529,6 +529,11 @@ "type": "string", "description": "Specify the location basedir where the lambda layers used by the template will be downloaded to." }, + "filesystem": { + "title": "filesystem", + "type": "string", + "description": "Local directory to mount as EFS filesystem for Lambda functions. Maps to the LocalMountPath specified in the function's FileSystemConfigs. If no FileSystemConfigs is specified, defaults to /mnt/efs" + }, "skip_pull_image": { "title": "skip_pull_image", "type": "boolean", @@ -617,7 +622,7 @@ "properties": { "parameters": { "title": "Parameters for the local start api command", - "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* disable_authorizer:\nDisable custom Lambda Authorizers from being parsed and invoked.\n* ssl_cert_file:\nPath to SSL certificate file (default: None)\n* ssl_key_file:\nPath to SSL key file (default: None)\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 additional environment variables to be set within the container when used in a debugging session locally.\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* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\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-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\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)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", + "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* disable_authorizer:\nDisable custom Lambda Authorizers from being parsed and invoked.\n* ssl_cert_file:\nPath to SSL certificate file (default: None)\n* ssl_key_file:\nPath to SSL key file (default: None)\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 additional environment variables to be set within the container when used in a debugging session locally.\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* filesystem:\nLocal directory to mount as EFS filesystem for Lambda functions. Maps to the LocalMountPath specified in the function's FileSystemConfigs. If no FileSystemConfigs is specified, defaults to /mnt/efs\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* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\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-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\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)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", "type": "object", "properties": { "terraform_plan_file": { @@ -725,6 +730,11 @@ "type": "string", "description": "Specify the location basedir where the lambda layers used by the template will be downloaded to." }, + "filesystem": { + "title": "filesystem", + "type": "string", + "description": "Local directory to mount as EFS filesystem for Lambda functions. Maps to the LocalMountPath specified in the function's FileSystemConfigs. If no FileSystemConfigs is specified, defaults to /mnt/efs" + }, "skip_pull_image": { "title": "skip_pull_image", "type": "boolean", @@ -842,7 +852,7 @@ "properties": { "parameters": { "title": "Parameters for the local start lambda command", - "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 additional environment variables to be set within the container when used in a debugging session locally.\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* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\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-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\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)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", + "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 additional environment variables to be set within the container when used in a debugging session locally.\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* filesystem:\nLocal directory to mount as EFS filesystem for Lambda functions. Maps to the LocalMountPath specified in the function's FileSystemConfigs. If no FileSystemConfigs is specified, defaults to /mnt/efs\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* add_host:\nPasses a hostname to IP address mapping to the Docker container's host file. This parameter can be passed multiple times.Example:--add-host example.com:127.0.0.1\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-nodejs20.x:latest). One can also specify for each individual function with (--invoke-image Function1=public.ecr.aws/sam/build-nodejs20.x:latest). If a function does not have invoke image specified, the default AWS SAM CLI emulation image will be used.\n* no_memory_limit:\nRemoves the Memory limit during emulation. With this parameter, the underlying container will run without a --memory parameter\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)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", "type": "object", "properties": { "terraform_plan_file": { @@ -929,6 +939,11 @@ "type": "string", "description": "Specify the location basedir where the lambda layers used by the template will be downloaded to." }, + "filesystem": { + "title": "filesystem", + "type": "string", + "description": "Local directory to mount as EFS filesystem for Lambda functions. Maps to the LocalMountPath specified in the function's FileSystemConfigs. If no FileSystemConfigs is specified, defaults to /mnt/efs" + }, "skip_pull_image": { "title": "skip_pull_image", "type": "boolean", diff --git a/tests/integration/local/start_api/test_filesystem_mount.py b/tests/integration/local/start_api/test_filesystem_mount.py new file mode 100644 index 0000000000..07d731cde3 --- /dev/null +++ b/tests/integration/local/start_api/test_filesystem_mount.py @@ -0,0 +1,94 @@ +""" +Integration tests for --filesystem flag with start-api command +Tests that the --filesystem flag correctly mounts EFS directories and allows Lambda functions +to interact with the mounted filesystem. +""" + +import os +import shutil +import tempfile +from pathlib import Path + +import pytest +import requests + +from tests.integration.local.start_api.start_api_integ_base import StartApiIntegBaseClass + + +class TestFilesystemMountWithStartApi(StartApiIntegBaseClass): + """ + Test that --filesystem flag correctly mounts EFS directories for start-api + """ + + template_path = "/testdata/start_api/filesystem/template.yaml" + + def setUp(self): + self.url = f"http://127.0.0.1:{self.port}" + + # Create a temporary filesystem directory with test files + self.test_fs_dir = tempfile.mkdtemp() + self.test_file = Path(self.test_fs_dir) / "test.txt" + self.test_file.write_text("Hello from EFS!") + + def tearDown(self): + # Clean up test filesystem + if os.path.exists(self.test_fs_dir): + shutil.rmtree(self.test_fs_dir) + super().tearDown() + + @property + def command_list(self): + command_list = super().command_list + # Add filesystem flag pointing to our test directory + command_list.extend(["--filesystem", self.test_fs_dir]) + return command_list + + @pytest.mark.flaky(reruns=3) + @pytest.mark.timeout(timeout=600, method="thread") + def test_lambda_can_read_from_mounted_filesystem(self): + """ + Test that Lambda function can read files from mounted EFS directory + """ + response = requests.get(f"{self.url}/read-file", params={"filename": "test.txt"}, timeout=300) + + self.assertEqual(response.status_code, 200) + data = response.json() + self.assertEqual(data.get("content"), "Hello from EFS!") + self.assertEqual(data.get("success"), True) + self.assertEqual(data.get("filename"), "test.txt") + + @pytest.mark.flaky(reruns=3) + @pytest.mark.timeout(timeout=600, method="thread") + def test_lambda_can_write_to_mounted_filesystem(self): + """ + Test that Lambda function can write files to mounted EFS directory + and verify changes persist on host filesystem + """ + write_content = "Written from Lambda!" + response = requests.post( + f"{self.url}/write-file", json={"filename": "written.txt", "content": write_content}, timeout=300 + ) + + self.assertEqual(response.status_code, 200) + data = response.json() + self.assertEqual(data.get("success"), True) + self.assertEqual(data.get("filename"), "written.txt") + + # Verify file actually exists on host filesystem + written_file = Path(self.test_fs_dir) / "written.txt" + self.assertTrue(written_file.exists(), "File should exist on host filesystem after Lambda write") + self.assertEqual(written_file.read_text(), write_content, "File content should match what Lambda wrote") + + @pytest.mark.flaky(reruns=3) + @pytest.mark.timeout(timeout=600, method="thread") + def test_lambda_can_list_mounted_filesystem(self): + """ + Test that Lambda function can list directory contents of mounted EFS + """ + response = requests.get(f"{self.url}/list-files", timeout=300) + + self.assertEqual(response.status_code, 200) + data = response.json() + self.assertEqual(data.get("success"), True) + self.assertIn("test.txt", data.get("files", []), "test.txt should be visible to Lambda in mounted filesystem") + self.assertEqual(data.get("mount_path"), "/mnt/efs", "Mount path should be /mnt/efs") diff --git a/tests/integration/local/start_api/testdata/filesystem/code/main.py b/tests/integration/local/start_api/testdata/filesystem/code/main.py new file mode 100644 index 0000000000..e0bf4bd2a2 --- /dev/null +++ b/tests/integration/local/start_api/testdata/filesystem/code/main.py @@ -0,0 +1,50 @@ +import json +import os + +EFS_MOUNT = "/mnt/efs" + + +def handler(event, context): + """ + Lambda handler for testing EFS filesystem mounting + """ + path = event.get("path", "") + + if path == "/read-file": + # Read a file from mounted EFS + filename = event.get("queryStringParameters", {}).get("filename", "") + try: + filepath = os.path.join(EFS_MOUNT, filename) + with open(filepath, "r") as f: + content = f.read() + return { + "statusCode": 200, + "body": json.dumps({"success": True, "content": content, "filename": filename}), + } + except Exception as e: + return {"statusCode": 500, "body": json.dumps({"success": False, "error": str(e)})} + + elif path == "/write-file": + # Write a file to mounted EFS + try: + body = json.loads(event.get("body", "{}")) + filename = body.get("filename", "") + content = body.get("content", "") + + filepath = os.path.join(EFS_MOUNT, filename) + with open(filepath, "w") as f: + f.write(content) + + return {"statusCode": 200, "body": json.dumps({"success": True, "filename": filename})} + except Exception as e: + return {"statusCode": 500, "body": json.dumps({"success": False, "error": str(e)})} + + elif path == "/list-files": + # List files in mounted EFS + try: + files = os.listdir(EFS_MOUNT) + return {"statusCode": 200, "body": json.dumps({"success": True, "files": files, "mount_path": EFS_MOUNT})} + except Exception as e: + return {"statusCode": 500, "body": json.dumps({"success": False, "error": str(e)})} + + return {"statusCode": 404, "body": json.dumps({"error": "Not found"})} diff --git a/tests/integration/local/start_api/testdata/filesystem/template.yaml b/tests/integration/local/start_api/testdata/filesystem/template.yaml new file mode 100644 index 0000000000..88df825761 --- /dev/null +++ b/tests/integration/local/start_api/testdata/filesystem/template.yaml @@ -0,0 +1,30 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 + +Resources: + FileSystemFunction: + Type: AWS::Serverless::Function + Properties: + Handler: main.handler + Runtime: python3.11 + CodeUri: code/ + Timeout: 30 + FileSystemConfigs: + - Arn: "arn:aws:elasticfilesystem:us-east-1:123456789012:access-point/fsap-test" + LocalMountPath: "/mnt/efs" + Events: + ReadFile: + Type: Api + Properties: + Path: /read-file + Method: GET + WriteFile: + Type: Api + Properties: + Path: /write-file + Method: POST + ListFiles: + Type: Api + Properties: + Path: /list-files + Method: GET diff --git a/tests/unit/commands/local/cli_common/test_invoke_context.py b/tests/unit/commands/local/cli_common/test_invoke_context.py index cec05c9d8a..64c6a26761 100644 --- a/tests/unit/commands/local/cli_common/test_invoke_context.py +++ b/tests/unit/commands/local/cli_common/test_invoke_context.py @@ -1401,7 +1401,12 @@ def test_must_pass_custom_region(self, add_account_id_to_global_mock, get_stacks class TestInvokeContext_add_account_id_to_global(TestCase): - def test_must_work_with_no_token(self): + @patch("samcli.commands.local.cli_common.invoke_context.get_boto_client_provider_with_config") + def test_must_work_with_no_token(self, get_boto_client_provider_with_config_mock): + # Mock to return no credentials + get_boto_client_provider_with_config_mock.return_value.return_value.get_caller_identity.side_effect = Exception( + "No credentials" + ) invoke_context = InvokeContext("template_file") invoke_context._add_account_id_to_global() self.assertIsNone(invoke_context._global_parameter_overrides) diff --git a/tests/unit/commands/local/invoke/test_cli.py b/tests/unit/commands/local/invoke/test_cli.py index d18eae519e..f4f0a1ed82 100644 --- a/tests/unit/commands/local/invoke/test_cli.py +++ b/tests/unit/commands/local/invoke/test_cli.py @@ -53,6 +53,7 @@ def setUp(self): self.overide_runtime = None self.mount_symlinks = False self.no_mem_limit = False + self.filesystem = None self.ctx_mock = Mock() self.ctx_mock.region = self.region_name @@ -73,9 +74,10 @@ def call_cli(self): docker_volume_basedir=self.docker_volume_basedir, docker_network=self.docker_network, log_file=self.log_file, + layer_cache_basedir=self.layer_cache_basedir, + filesystem=self.filesystem, skip_pull_image=self.skip_pull_image, parameter_overrides=self.parameter_overrides, - layer_cache_basedir=self.layer_cache_basedir, force_image_build=self.force_image_build, shutdown=self.shutdown, container_host=self.container_host, @@ -161,9 +163,9 @@ def test_cli_must_invoke_with_no_event(self, get_event_mock, InvokeContextMock): parameter_overrides=self.parameter_overrides, layer_cache_basedir=self.layer_cache_basedir, force_image_build=self.force_image_build, - shutdown=self.shutdown, aws_region=self.region_name, aws_profile=self.profile, + shutdown=self.shutdown, container_host=self.container_host, container_host_interface=self.container_host_interface, add_host=self.add_host, diff --git a/tests/unit/commands/local/start_api/test_cli.py b/tests/unit/commands/local/start_api/test_cli.py index 58b8d28a0f..380ee3c31a 100644 --- a/tests/unit/commands/local/start_api/test_cli.py +++ b/tests/unit/commands/local/start_api/test_cli.py @@ -98,6 +98,7 @@ def test_cli_must_setup_context_and_start_service(self, local_api_service_mock, container_host_interface=self.container_host_interface, add_host=self.add_host, invoke_images={}, + filesystem_dir=None, no_mem_limit=self.no_mem_limit, ) @@ -200,7 +201,21 @@ def test_must_raise_user_exception_on_invalid_imageuri(self, invoke_context_mock expected = "invalid imageuri" self.assertEqual(msg, expected) - def call_cli(self): + @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext") + @patch("samcli.commands.local.lib.local_api_service.LocalApiService") + def test_cli_with_filesystem_parameter(self, local_api_service_mock, invoke_context_mock): + """ + Test that filesystem parameter is correctly passed to InvokeContext + """ + # Mock the __enter__ method to return a object inside a context manager + context_mock = Mock() + invoke_context_mock.return_value.__enter__.return_value = context_mock + + service_mock = Mock() + local_api_service_mock.return_value = service_mock + + filesystem_path = "/path/to/efs" + start_api_cli( ctx=self.ctx_mock, host=self.host, @@ -215,10 +230,74 @@ def call_cli(self): docker_volume_basedir=self.docker_volume_basedir, docker_network=self.docker_network, log_file=self.log_file, + layer_cache_basedir=self.layer_cache_basedir, + filesystem=filesystem_path, skip_pull_image=self.skip_pull_image, parameter_overrides=self.parameter_overrides, + force_image_build=self.force_image_build, + warm_containers=self.warm_containers, + debug_function=self.debug_function, + shutdown=self.shutdown, + container_host=self.container_host, + container_host_interface=self.container_host_interface, + invoke_image=self.invoke_image, + hook_name=self.hook_name, + ssl_cert_file=self.ssl_cert_file, + ssl_key_file=self.ssl_key_file, + disable_authorizer=self.disable_authorizer, + add_host=self.add_host, + no_mem_limit=self.no_mem_limit, + ) + + # Assert filesystem_dir was passed to InvokeContext + invoke_context_mock.assert_called_with( + template_file=self.template, + function_identifier=None, + env_vars_file=self.env_vars, + docker_volume_basedir=self.docker_volume_basedir, + docker_network=self.docker_network, + log_file=self.log_file, + skip_pull_image=self.skip_pull_image, + debug_ports=self.debug_ports, + debug_args=self.debug_args, + debugger_path=self.debugger_path, + container_env_vars_file=self.container_env_vars, + parameter_overrides=self.parameter_overrides, layer_cache_basedir=self.layer_cache_basedir, force_image_build=self.force_image_build, + aws_region=self.region_name, + aws_profile=self.profile, + warm_container_initialization_mode=self.warm_containers, + debug_function=self.debug_function, + shutdown=self.shutdown, + container_host=self.container_host, + container_host_interface=self.container_host_interface, + add_host=self.add_host, + invoke_images={}, + filesystem_dir=filesystem_path, + no_mem_limit=self.no_mem_limit, + ) + + def call_cli(self): + start_api_cli( + ctx=self.ctx_mock, + host=self.host, + port=self.port, + static_dir=self.static_dir, + template=self.template, + env_vars=self.env_vars, + debug_port=self.debug_ports, + debug_args=self.debug_args, + debugger_path=self.debugger_path, + container_env_vars=self.container_env_vars, + docker_volume_basedir=self.docker_volume_basedir, + docker_network=self.docker_network, + log_file=self.log_file, + layer_cache_basedir=self.layer_cache_basedir, + filesystem=None, + skip_pull_image=self.skip_pull_image, + parameter_overrides=self.parameter_overrides, + force_image_build=self.force_image_build, warm_containers=self.warm_containers, debug_function=self.debug_function, shutdown=self.shutdown, diff --git a/tests/unit/commands/local/start_lambda/test_cli.py b/tests/unit/commands/local/start_lambda/test_cli.py index 75531ba255..434c836346 100644 --- a/tests/unit/commands/local/start_lambda/test_cli.py +++ b/tests/unit/commands/local/start_lambda/test_cli.py @@ -45,6 +45,7 @@ def setUp(self): self.add_host = {} self.invoke_image = () self.hook_name = None + self.filesystem = None self.no_mem_limit = False @patch("samcli.commands.local.cli_common.invoke_context.InvokeContext") @@ -85,6 +86,7 @@ def test_cli_must_setup_context_and_start_service(self, local_lambda_service_moc container_host_interface=self.container_host_interface, add_host=self.add_host, invoke_images={}, + filesystem_dir=self.filesystem, no_mem_limit=self.no_mem_limit, ) @@ -178,6 +180,7 @@ def call_cli(self): skip_pull_image=self.skip_pull_image, parameter_overrides=self.parameter_overrides, layer_cache_basedir=self.layer_cache_basedir, + filesystem=self.filesystem, force_image_build=self.force_image_build, warm_containers=self.warm_containers, debug_function=self.debug_function, diff --git a/tests/unit/commands/samconfig/test_samconfig.py b/tests/unit/commands/samconfig/test_samconfig.py index 666a54d0e3..e162ba2b77 100644 --- a/tests/unit/commands/samconfig/test_samconfig.py +++ b/tests/unit/commands/samconfig/test_samconfig.py @@ -573,6 +573,7 @@ def test_local_invoke(self, do_cli_mock): "mynetwork", "logfile", "basedir", + None, True, True, True, @@ -641,6 +642,7 @@ def test_local_invoke_with_runtime_params(self, do_cli_mock): "mynetwork", "logfile", "basedir", + None, True, True, True, @@ -710,6 +712,7 @@ def test_local_start_api(self, do_cli_mock): "mynetwork", "logfile", "basedir", + None, True, True, {"Key": "Value", "Key2": "Value2"}, @@ -777,6 +780,7 @@ def test_local_start_lambda(self, do_cli_mock): "mynetwork", "logfile", "basedir", + None, True, True, {"Key": "Value"}, @@ -1669,6 +1673,7 @@ def test_override_with_cli_params(self, do_cli_mock): "othernetwork", "otherlogfile", "otherbasedir", + None, True, True, {"A": "123", "C": "D", "E": "F12!", "G": "H"}, @@ -1768,6 +1773,7 @@ def test_override_with_cli_params_and_envvars(self, do_cli_mock): "envnetwork", "otherlogfile", "envlayercache", + None, False, True, {"A": "123", "C": "D", "E": "F12!", "G": "H"},