-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathcli.py
More file actions
296 lines (273 loc) · 9.38 KB
/
Copy pathcli.py
File metadata and controls
296 lines (273 loc) · 9.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
"""
CLI command for "local invoke" command
"""
import logging
import click
from samcli.cli.cli_config_file import ConfigProvider, configuration_option, save_params_option
from samcli.cli.main import aws_creds_options, pass_context, print_cmdline_args
from samcli.cli.main import common_options as cli_framework_options
from samcli.cli.types import TenantIdType
from samcli.commands._utils.option_value_processor import process_image_options
from samcli.commands._utils.options import (
hook_name_click_option,
mount_symlinks_option,
skip_prepare_infra_option,
terraform_plan_file_option,
)
from samcli.commands.init.init_flow_helpers import get_sorted_runtimes
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
from samcli.lib.telemetry.metric import track_command
from samcli.lib.utils.version_checker import check_newer_version
from samcli.local.common.runtime_template import INIT_RUNTIMES
from samcli.local.docker.exceptions import (
ContainerNotStartableException,
DockerContainerCreationFailedException,
PortAlreadyInUse,
)
LOG = logging.getLogger(__name__)
HELP_TEXT = """
Invoke AWS serverless functions locally.
"""
DESCRIPTION = """
Invoke lambda functions in a Lambda-like environment locally.
An event body can be passed using the -e (--event) parameter.
Logs from the Lambda function will be written to stdout.
"""
STDIN_FILE_NAME = "-"
@click.command(
"invoke",
cls=InvokeCommand,
help=HELP_TEXT,
description=DESCRIPTION,
requires_credentials=False,
short_help=HELP_TEXT,
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"]
)
@skip_prepare_infra_option
@click.option(
"--event",
"-e",
type=click.Path(),
help="JSON 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",
)
@click.option("--no-event", is_flag=True, default=True, help="DEPRECATED: By default no event is assumed.", hidden=True)
@click.option(
"-r",
"--runtime",
type=click.Choice(get_sorted_runtimes(INIT_RUNTIMES)),
help="Lambda runtime used to invoke the function."
+ click.style(f"\n\nRuntimes: {', '.join(get_sorted_runtimes(INIT_RUNTIMES))}", bold=True),
)
@click.option(
"--tenant-id",
type=TenantIdType(),
help="Tenant ID for multi-tenant Lambda functions. "
"Used to ensure compute isolation between different tenants. "
"Must be 1-256 characters, the allowed characters are a-z and A-Z, "
"numbers, spaces, and the characters _ . : / = + - @",
)
@mount_symlinks_option
@invoke_common_options
@local_common_options
@cli_framework_options
@aws_creds_options
@click.argument("function_logical_id", required=False)
@save_params_option
@pass_context
@track_command # pylint: disable=R0914
@check_newer_version
@print_cmdline_args
def cli(
ctx,
function_logical_id,
template_file,
event,
no_event,
env_vars,
debug_port,
debug_args,
debugger_path,
container_env_vars,
docker_volume_basedir,
docker_network,
log_file,
layer_cache_basedir,
skip_pull_image,
force_image_build,
shutdown,
parameter_overrides,
save_params,
config_file,
config_env,
container_host,
container_host_interface,
add_host,
invoke_image,
hook_name,
skip_prepare_infra,
terraform_plan_file,
runtime,
mount_symlinks,
no_memory_limit,
tenant_id,
):
"""
`sam local invoke` command entry point
"""
# All logic must be implemented in the ``do_cli`` method. This helps with easy unit testing
do_cli(
ctx,
function_logical_id,
template_file,
event,
no_event,
env_vars,
debug_port,
debug_args,
debugger_path,
container_env_vars,
docker_volume_basedir,
docker_network,
log_file,
layer_cache_basedir,
skip_pull_image,
force_image_build,
shutdown,
parameter_overrides,
container_host,
container_host_interface,
add_host,
invoke_image,
hook_name,
runtime,
mount_symlinks,
no_memory_limit,
tenant_id,
) # pragma: no cover
def do_cli( # pylint: disable=R0914
ctx,
function_identifier,
template,
event,
no_event,
env_vars,
debug_port,
debug_args,
debugger_path,
container_env_vars,
docker_volume_basedir,
docker_network,
log_file,
layer_cache_basedir,
skip_pull_image,
force_image_build,
shutdown,
parameter_overrides,
container_host,
container_host_interface,
add_host,
invoke_image,
hook_name,
runtime,
mount_symlinks,
no_mem_limit,
tenant_id,
):
"""
Implementation of the ``cli`` method, just separated out for unit testing purposes
"""
from samcli.commands.exceptions import UserException
from samcli.commands.local.cli_common.invoke_context import InvokeContext
from samcli.commands.local.lib.exceptions import NoPrivilegeException, OverridesNotWellDefinedError
from samcli.commands.validate.lib.exceptions import InvalidSamDocumentException
from samcli.lib.providers.exceptions import InvalidLayerReference
from samcli.lib.utils.name_utils import InvalidFunctionNameException
from samcli.local.docker.lambda_debug_settings import DebuggingNotSupported
from samcli.local.docker.manager import DockerImagePullFailedException
from samcli.local.lambdafn.exceptions import FunctionNotFound
LOG.debug("local invoke command is called")
if event:
event_data = _get_event(event, exception_class=UserException)
else:
event_data = "{}"
processed_invoke_images = process_image_options(invoke_image)
# Pass all inputs to setup necessary context to invoke function locally.
# Handler exception raised by the processor for invalid args and print errors
try:
with InvokeContext(
template_file=template,
function_identifier=function_identifier,
env_vars_file=env_vars,
docker_volume_basedir=docker_volume_basedir,
docker_network=docker_network,
log_file=log_file,
skip_pull_image=skip_pull_image,
debug_ports=debug_port,
debug_args=debug_args,
debugger_path=debugger_path,
container_env_vars_file=container_env_vars,
parameter_overrides=parameter_overrides,
layer_cache_basedir=layer_cache_basedir,
force_image_build=force_image_build,
aws_region=ctx.region,
aws_profile=ctx.profile,
shutdown=shutdown,
container_host=container_host,
container_host_interface=container_host_interface,
add_host=add_host,
invoke_images=processed_invoke_images,
mount_symlinks=mount_symlinks,
no_mem_limit=no_mem_limit,
) as context:
# Invoke the function
context.local_lambda_runner.invoke(
context.function_identifier,
event=event_data,
tenant_id=tenant_id,
stdout=context.stdout,
stderr=context.stderr,
override_runtime=runtime,
)
except FunctionNotFound as ex:
raise UserException(
"Function {} not found in template".format(function_identifier), wrapped_from=ex.__class__.__name__
) from ex
except (
InvalidSamDocumentException,
OverridesNotWellDefinedError,
InvalidFunctionNameException,
InvalidLayerReference,
InvalidIntermediateImageError,
DebuggingNotSupported,
NoPrivilegeException,
PortAlreadyInUse,
) as ex:
raise UserException(str(ex), wrapped_from=ex.__class__.__name__) from ex
except (DockerImagePullFailedException, DockerContainerCreationFailedException) as ex:
raise UserException(str(ex), wrapped_from=ex.__class__.__name__) from ex
except ContainerNotStartableException as ex:
raise UserException(str(ex), wrapped_from=ex.__class__.__name__) from ex
def _get_event(event_file_name, exception_class):
"""
Read the event JSON data from the given file. If no file is provided, read the event from stdin.
:param string event_file_name: Path to event file, or '-' for stdin
:return string: Contents of the event file or stdin
"""
if event_file_name == STDIN_FILE_NAME:
# If event is empty, listen to stdin for event data until EOF
LOG.info("Reading invoke payload from stdin (you can also pass it from file with --event)")
# click.open_file knows to open stdin when filename is '-'. This is safer than manually opening streams, and
# accidentally closing a standard stream
try:
with click.open_file(event_file_name, "r", encoding="utf-8") as fp:
return fp.read()
except FileNotFoundError as ex:
raise exception_class(str(ex), wrapped_from=ex.__class__.__name__) from ex