Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 76 additions & 2 deletions samcli/commands/local/cli_common/invoke_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from samcli.local.docker.exceptions import PortAlreadyInUse
from samcli.local.docker.lambda_image import LambdaImage
from samcli.local.docker.manager import ContainerManager
from samcli.local.lambdafn.exceptions import FunctionNotFound
from samcli.local.lambdafn.runtime import LambdaRuntime, WarmLambdaRuntime
from samcli.local.layers.layer_downloader import LayerDownloader

Expand Down Expand Up @@ -98,6 +99,7 @@ def __init__(
invoke_images: Optional[str] = None,
mount_symlinks: Optional[bool] = False,
no_mem_limit: Optional[bool] = False,
function_logical_ids: Optional[Tuple[str, ...]] = None,
) -> None:
"""
Initialize the context
Expand All @@ -107,7 +109,7 @@ def __init__(
template_file str
Name or path to template
function_identifier str
Identifier of the function to invoke
Identifier of the single function to invoke (used by 'sam local invoke' command)
env_vars_file str
Path to a file containing values for environment variables
docker_volume_basedir str
Expand Down Expand Up @@ -154,10 +156,27 @@ def __init__(
Optional. A dictionary that defines the custom invoke image URI of each function
mount_symlinks bool
Optional. Indicates if symlinks should be mounted inside the container
function_logical_ids tuple(str)
Optional. Tuple of function logical IDs to filter and make available for local execution.
Used by 'sam local start-api' and 'sam local start-lambda' commands to limit which
functions from the template are exposed. If not provided, all functions are available.
"""

self._template_file = template_file
self._function_identifier = function_identifier
self._function_logical_ids = function_logical_ids

# Validate that function_identifier and function_logical_ids aren't both provided
# function_identifier is for 'sam local invoke' (single function)
# function_logical_ids is for 'sam local start-lambda' commands (multiple function filter)
if self._function_identifier and self._function_logical_ids:
LOG.warning(
"Both function_identifier and function_logical_ids were provided. "
"function_identifier is used for 'sam local invoke' to specify a single function, "
"while function_logical_ids is used for 'sam local start-lambda' to filter functions. "
"function_identifier will take precedence for single function invocation."
)

self._env_vars_file = env_vars_file
self._docker_volume_basedir = docker_volume_basedir
self._docker_network = docker_network
Expand Down Expand Up @@ -241,10 +260,18 @@ def __enter__(self) -> "InvokeContext":
if self._docker_volume_basedir:
_function_providers_args[self._containers_mode].append(True)

_function_providers_kwargs: Dict[str, Any] = {}

if self._function_logical_ids:
_function_providers_kwargs["function_logical_ids"] = self._function_logical_ids

self._function_provider = _function_providers_class[self._containers_mode](
*_function_providers_args[self._containers_mode]
*_function_providers_args[self._containers_mode], **_function_providers_kwargs
)

# Validate function logical IDs after provider is initialized
self._validate_function_logical_ids()

self._env_vars_value = self._get_env_vars_value(self._env_vars_file)
self._container_env_vars_value = self._get_env_vars_value(self._container_env_vars_file)
self._log_file_handle = self._setup_log_file(self._log_file)
Expand Down Expand Up @@ -448,6 +475,38 @@ def _clean_running_containers_and_related_resources(self) -> None:
cast(WarmLambdaRuntime, self.lambda_runtime).clean_running_containers_and_related_resources()
cast(RefreshableSamFunctionProvider, self._function_provider).stop_observer()

def _validate_function_logical_ids(self) -> None:
"""
Validates that all provided function logical IDs exist in the template.
Raises FunctionNotFound with helpful error message if validation fails.
"""
if not self._function_logical_ids:
return # No filtering requested

# Get all available function names/IDs from the provider
all_functions_set = set()
for func in self._function_provider.get_all():
all_functions_set.add(func.name)
all_functions_set.add(func.function_id)
Comment thread
vicheey marked this conversation as resolved.
if func.functionname:
all_functions_set.add(func.functionname)

# Check for invalid IDs
invalid_ids = set(self._function_logical_ids) - all_functions_set

if invalid_ids:
# Get all available function full paths, matching sam local invoke pattern
all_function_full_paths = [f.full_path for f in self._function_provider.get_all()]

# Format message to match sam local invoke pattern exactly
invalid_functions_str = ", ".join(sorted(invalid_ids))
available_function_message = "{} not found. Possible options in your template: {}".format(
invalid_functions_str, all_function_full_paths
)
LOG.info(available_function_message)

raise FunctionNotFound("Unable to find Function(s) with name(s) '{}'".format(invalid_functions_str))

def _add_account_id_to_global(self) -> None:
"""
Attempts to get the Account ID from the current session
Expand Down Expand Up @@ -556,6 +615,21 @@ def initialized_container_ids(self) -> List[str]:
"""
return self._initialized_container_ids

@property
def function_logical_ids(self) -> Optional[Tuple[str, ...]]:
"""
Returns the tuple of function logical IDs to filter, if provided.

This is used by 'sam local start-lambda' commands
to limit which functions from the template are made available for local execution.
This is different from function_identifier which is used by 'sam local invoke'
to specify a single function to invoke.

Returns:
Optional[Tuple[str, ...]]: Tuple of function logical IDs or None if no filter
"""
return self._function_logical_ids

@property
def stdout(self) -> StreamWriter:
"""
Expand Down
41 changes: 30 additions & 11 deletions samcli/commands/local/start_api/core/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,36 @@ class CustomFormatterContext(Context):
@staticmethod
def format_examples(ctx: Context, formatter: InvokeStartAPICommandHelpTextFormatter):
with formatter.indented_section(name="Examples", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name=style(f"$ {ctx.command_path}"),
extra_row_modifiers=[ShowcaseRowModifier()],
),
]
)
with formatter.indented_section(name="Setup", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name="Start the local lambda with Amazon API Gateway endpoint",
),
RowDefinition(
name=style(f"$ {ctx.command_path}"),
extra_row_modifiers=[ShowcaseRowModifier()],
),
]
)
with formatter.indented_section(name="Invoke local Lambda endpoint", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name="Invoke Lambda function locally using curl",
),
RowDefinition(
name=style("$ curl http://127.0.0.1:3000/hello"),
extra_row_modifiers=[ShowcaseRowModifier()],
),
]
)

def format_options(self, ctx: Context, formatter: InvokeStartAPICommandHelpTextFormatter) -> None: # type:ignore
# NOTE(sriram-mv): `ignore` is put in place here for mypy even though it is the correct behavior,
Expand Down
5 changes: 5 additions & 0 deletions samcli/commands/local/start_lambda/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
requires_credentials=False,
context_settings={"max_content_width": 120},
)
@click.argument("function_logical_ids", nargs=-1, required=False)
Comment thread
vicheey marked this conversation as resolved.
@configuration_option(provider=ConfigProvider(section="parameters"))
@terraform_plan_file_option
@hook_name_click_option(
Expand All @@ -71,6 +72,7 @@
@print_cmdline_args
def cli(
ctx, # pylint: disable=R0914
function_logical_ids,
# start-lambda Specific Options
host,
port,
Expand Down Expand Up @@ -110,6 +112,7 @@ def cli(

do_cli(
ctx,
function_logical_ids,
host,
port,
template_file,
Expand Down Expand Up @@ -139,6 +142,7 @@ def cli(

def do_cli( # pylint: disable=R0914
ctx,
function_logical_ids,
host,
port,
template,
Expand Down Expand Up @@ -208,6 +212,7 @@ def do_cli( # pylint: disable=R0914
container_host_interface=container_host_interface,
add_host=add_host,
invoke_images=processed_invoke_images,
function_logical_ids=function_logical_ids,
no_mem_limit=no_mem_limit,
) as invoke_context:
service = LocalLambdaService(lambda_invoke_context=invoke_context, port=port, host=host)
Expand Down
35 changes: 31 additions & 4 deletions samcli/commands/local/start_lambda/core/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,50 @@ def format_examples(ctx: Context, formatter: InvokeStartLambdaCommandHelpTextFor
text="\n",
),
RowDefinition(
name="Start the local lambda endpoint.",
name="Start the local lambda endpoint for all functions",
),
RowDefinition(
name=style(f"$ {ctx.command_path}"),
extra_row_modifiers=[ShowcaseRowModifier()],
),
]
)
with formatter.indented_section(name="Using AWS CLI", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name="Invoke Lambda function locally using the AWS CLI.",
name="Start the local lambda endpoint for one function",
),
RowDefinition(
name=style(f"$ {ctx.command_path} HelloWorldFunction"),
extra_row_modifiers=[ShowcaseRowModifier()],
),
]
)
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name="Start the local lambda endpoint for multiple functions",
),
RowDefinition(
name=style(f"$ {ctx.command_path} HelloWorldFunctionOne HelloWorldFunctionTwo"),
extra_row_modifiers=[ShowcaseRowModifier()],
),
]
)
with formatter.indented_section(name="Invoke local Lambda endpoint", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
text="\n",
),
RowDefinition(
name="Use the AWS CLI.",
),
RowDefinition(
name=style(
Expand All @@ -63,7 +91,6 @@ def format_examples(ctx: Context, formatter: InvokeStartLambdaCommandHelpTextFor
),
]
)
with formatter.indented_section(name="Using AWS SDK", extra_indents=1):
formatter.write_rd(
[
RowDefinition(
Expand Down
Loading
Loading