localstack-core 4.10.1.dev42__py3-none-any.whl → 4.11.2.dev14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- localstack/aws/api/apigateway/__init__.py +42 -0
- localstack/aws/api/cloudformation/__init__.py +161 -0
- localstack/aws/api/ec2/__init__.py +1165 -12
- localstack/aws/api/iam/__init__.py +227 -0
- localstack/aws/api/kms/__init__.py +1 -0
- localstack/aws/api/lambda_/__init__.py +418 -66
- localstack/aws/api/logs/__init__.py +312 -0
- localstack/aws/api/opensearch/__init__.py +89 -0
- localstack/aws/api/redshift/__init__.py +69 -0
- localstack/aws/api/resourcegroupstaggingapi/__init__.py +36 -0
- localstack/aws/api/route53/__init__.py +42 -0
- localstack/aws/api/route53resolver/__init__.py +1 -0
- localstack/aws/api/s3/__init__.py +62 -0
- localstack/aws/api/secretsmanager/__init__.py +28 -23
- localstack/aws/api/stepfunctions/__init__.py +52 -10
- localstack/aws/api/sts/__init__.py +52 -0
- localstack/aws/handlers/logging.py +8 -4
- localstack/aws/handlers/service.py +11 -2
- localstack/aws/protocol/serializer.py +1 -1
- localstack/deprecations.py +0 -6
- localstack/services/acm/provider.py +4 -0
- localstack/services/apigateway/legacy/provider.py +28 -15
- localstack/services/cloudformation/engine/template_preparer.py +6 -2
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +12 -0
- localstack/services/cloudwatch/provider.py +10 -3
- localstack/services/cloudwatch/provider_v2.py +6 -3
- localstack/services/configservice/provider.py +5 -1
- localstack/services/dynamodb/provider.py +1 -0
- localstack/services/dynamodb/v2/provider.py +1 -0
- localstack/services/dynamodbstreams/provider.py +6 -0
- localstack/services/dynamodbstreams/v2/provider.py +6 -0
- localstack/services/ec2/provider.py +6 -0
- localstack/services/es/provider.py +6 -0
- localstack/services/events/provider.py +4 -0
- localstack/services/events/v1/provider.py +9 -0
- localstack/services/firehose/provider.py +5 -0
- localstack/services/iam/provider.py +4 -0
- localstack/services/kms/models.py +10 -20
- localstack/services/kms/provider.py +4 -0
- localstack/services/lambda_/api_utils.py +37 -20
- localstack/services/lambda_/event_source_mapping/pollers/stream_poller.py +1 -1
- localstack/services/lambda_/invocation/assignment.py +4 -1
- localstack/services/lambda_/invocation/execution_environment.py +21 -2
- localstack/services/lambda_/invocation/lambda_models.py +27 -2
- localstack/services/lambda_/invocation/lambda_service.py +51 -3
- localstack/services/lambda_/invocation/models.py +9 -1
- localstack/services/lambda_/invocation/version_manager.py +18 -3
- localstack/services/lambda_/provider.py +239 -95
- localstack/services/lambda_/resource_providers/aws_lambda_function.py +33 -1
- localstack/services/lambda_/runtimes.py +3 -1
- localstack/services/logs/provider.py +9 -0
- localstack/services/opensearch/provider.py +53 -3
- localstack/services/resource_groups/provider.py +5 -1
- localstack/services/resourcegroupstaggingapi/provider.py +6 -1
- localstack/services/s3/provider.py +28 -15
- localstack/services/s3/utils.py +35 -14
- localstack/services/s3control/provider.py +101 -2
- localstack/services/s3control/validation.py +50 -0
- localstack/services/sns/constants.py +3 -1
- localstack/services/sns/publisher.py +15 -6
- localstack/services/sns/v2/models.py +6 -0
- localstack/services/sns/v2/provider.py +650 -19
- localstack/services/sns/v2/utils.py +12 -0
- localstack/services/stepfunctions/asl/component/common/path/result_path.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/execute_state.py +0 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +0 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/lambda_eval_utils.py +8 -8
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/{mock_eval_utils.py → local_mock_eval_utils.py} +13 -9
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service.py +6 -6
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_callback.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +4 -0
- localstack/services/stepfunctions/asl/component/test_state/state/base_mock.py +118 -0
- localstack/services/stepfunctions/asl/component/test_state/state/common.py +82 -0
- localstack/services/stepfunctions/asl/component/test_state/state/execution.py +139 -0
- localstack/services/stepfunctions/asl/component/test_state/state/map.py +77 -0
- localstack/services/stepfunctions/asl/component/test_state/state/task.py +44 -0
- localstack/services/stepfunctions/asl/eval/environment.py +30 -22
- localstack/services/stepfunctions/asl/eval/states.py +1 -1
- localstack/services/stepfunctions/asl/eval/test_state/environment.py +49 -9
- localstack/services/stepfunctions/asl/eval/test_state/program_state.py +22 -0
- localstack/services/stepfunctions/asl/jsonata/jsonata.py +5 -1
- localstack/services/stepfunctions/asl/parse/preprocessor.py +67 -24
- localstack/services/stepfunctions/asl/parse/test_state/asl_parser.py +5 -4
- localstack/services/stepfunctions/asl/parse/test_state/preprocessor.py +222 -31
- localstack/services/stepfunctions/asl/static_analyser/test_state/test_state_analyser.py +170 -22
- localstack/services/stepfunctions/backend/execution.py +6 -6
- localstack/services/stepfunctions/backend/execution_worker.py +5 -5
- localstack/services/stepfunctions/backend/test_state/execution.py +36 -0
- localstack/services/stepfunctions/backend/test_state/execution_worker.py +33 -1
- localstack/services/stepfunctions/backend/test_state/test_state_mock.py +127 -0
- localstack/services/stepfunctions/local_mocking/__init__.py +9 -0
- localstack/services/stepfunctions/{mocking → local_mocking}/mock_config.py +24 -17
- localstack/services/stepfunctions/provider.py +78 -27
- localstack/services/stepfunctions/test_state/mock_config.py +47 -0
- localstack/testing/pytest/fixtures.py +28 -0
- localstack/testing/snapshots/transformer_utility.py +5 -0
- localstack/utils/analytics/publisher.py +37 -155
- localstack/utils/analytics/service_request_aggregator.py +6 -4
- localstack/utils/aws/arns.py +7 -0
- localstack/utils/batching.py +258 -0
- localstack/utils/collections.py +23 -11
- localstack/version.py +2 -2
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/METADATA +5 -5
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/RECORD +113 -105
- localstack_core-4.11.2.dev14.dist-info/plux.json +1 -0
- localstack/services/stepfunctions/mocking/__init__.py +0 -0
- localstack/utils/batch_policy.py +0 -124
- localstack_core-4.10.1.dev42.dist-info/plux.json +0 -1
- /localstack/services/stepfunctions/{mocking → local_mocking}/mock_config_file.py +0 -0
- {localstack_core-4.10.1.dev42.data → localstack_core-4.11.2.dev14.data}/scripts/localstack +0 -0
- {localstack_core-4.10.1.dev42.data → localstack_core-4.11.2.dev14.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.10.1.dev42.data → localstack_core-4.11.2.dev14.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/WHEEL +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.11.2.dev14.dist-info}/top_level.txt +0 -0
|
@@ -45,6 +45,7 @@ from localstack.services.events.scheduler import JobScheduler
|
|
|
45
45
|
from localstack.services.events.v1.models import EventsStore, events_stores
|
|
46
46
|
from localstack.services.moto import call_moto
|
|
47
47
|
from localstack.services.plugins import ServiceLifecycleHook
|
|
48
|
+
from localstack.state import StateVisitor
|
|
48
49
|
from localstack.utils.aws.arns import event_bus_arn, parse_arn
|
|
49
50
|
from localstack.utils.aws.client_types import ServicePrincipal
|
|
50
51
|
from localstack.utils.aws.message_forwarding import send_event_to_target
|
|
@@ -83,6 +84,14 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
83
84
|
def on_before_stop(self):
|
|
84
85
|
JobScheduler.shutdown()
|
|
85
86
|
|
|
87
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
88
|
+
from moto.events.models import events_backends
|
|
89
|
+
|
|
90
|
+
from localstack.services.events.v1.models import events_stores
|
|
91
|
+
|
|
92
|
+
visitor.visit(events_backends)
|
|
93
|
+
visitor.visit(events_stores)
|
|
94
|
+
|
|
86
95
|
@route("/_aws/events/rules/<path:rule_arn>/trigger")
|
|
87
96
|
def trigger_scheduled_rule(self, request: Request, rule_arn: str):
|
|
88
97
|
"""Developer endpoint to trigger a scheduled rule."""
|
|
@@ -94,6 +94,7 @@ from localstack.services.firehose.mappers import (
|
|
|
94
94
|
convert_source_config_to_desc,
|
|
95
95
|
)
|
|
96
96
|
from localstack.services.firehose.models import FirehoseStore, firehose_stores
|
|
97
|
+
from localstack.state import StateVisitor
|
|
97
98
|
from localstack.utils.aws.arns import (
|
|
98
99
|
extract_account_id_from_arn,
|
|
99
100
|
extract_region_from_arn,
|
|
@@ -251,8 +252,12 @@ class FirehoseProvider(FirehoseApi):
|
|
|
251
252
|
|
|
252
253
|
def __init__(self) -> None:
|
|
253
254
|
super().__init__()
|
|
255
|
+
# TODO: stop/restart the kinesis listeners when stopping the service / reset the state / restore the state
|
|
254
256
|
self.kinesis_listeners = {}
|
|
255
257
|
|
|
258
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
259
|
+
visitor.visit(firehose_stores)
|
|
260
|
+
|
|
256
261
|
@staticmethod
|
|
257
262
|
def get_store(account_id: str, region_name: str) -> FirehoseStore:
|
|
258
263
|
return firehose_stores[account_id][region_name]
|
|
@@ -75,6 +75,7 @@ from localstack.services.iam.resources.policy_simulator import (
|
|
|
75
75
|
)
|
|
76
76
|
from localstack.services.iam.resources.service_linked_roles import SERVICE_LINKED_ROLES
|
|
77
77
|
from localstack.services.moto import call_moto
|
|
78
|
+
from localstack.state import StateVisitor
|
|
78
79
|
from localstack.utils.aws.request_context import extract_access_key_id_from_auth_header
|
|
79
80
|
|
|
80
81
|
LOG = logging.getLogger(__name__)
|
|
@@ -110,6 +111,9 @@ class IamProvider(IamApi):
|
|
|
110
111
|
apply_iam_patches()
|
|
111
112
|
self.policy_simulator = BasicIAMPolicySimulator()
|
|
112
113
|
|
|
114
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
115
|
+
visitor.visit(iam_backends)
|
|
116
|
+
|
|
113
117
|
@handler("CreateRole", expand=False)
|
|
114
118
|
def create_role(
|
|
115
119
|
self, context: RequestContext, request: CreateRoleRequest
|
|
@@ -20,7 +20,6 @@ from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
|
|
|
20
20
|
from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15
|
|
21
21
|
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
|
22
22
|
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
|
|
23
|
-
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
|
24
23
|
from cryptography.hazmat.primitives.serialization import load_der_public_key
|
|
25
24
|
|
|
26
25
|
from localstack.aws.api.kms import (
|
|
@@ -442,17 +441,15 @@ class KmsKey:
|
|
|
442
441
|
|
|
443
442
|
def derive_shared_secret(self, public_key: bytes) -> bytes:
|
|
444
443
|
key_spec = self.metadata.get("KeySpec")
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
f"{self.metadata['Arn']} key usage is {self.metadata['KeyUsage']} which is not valid for DeriveSharedSecret."
|
|
455
|
-
)
|
|
444
|
+
if key_spec not in (
|
|
445
|
+
KeySpec.ECC_NIST_P256,
|
|
446
|
+
KeySpec.ECC_SECG_P256K1,
|
|
447
|
+
KeySpec.ECC_NIST_P384,
|
|
448
|
+
KeySpec.ECC_NIST_P521,
|
|
449
|
+
):
|
|
450
|
+
raise InvalidKeyUsageException(
|
|
451
|
+
f"{self.metadata['Arn']} key usage is {self.metadata['KeyUsage']} which is not valid for DeriveSharedSecret."
|
|
452
|
+
)
|
|
456
453
|
|
|
457
454
|
# Deserialize public key from DER encoded data to EllipticCurvePublicKey.
|
|
458
455
|
try:
|
|
@@ -460,14 +457,7 @@ class KmsKey:
|
|
|
460
457
|
except (UnsupportedAlgorithm, ValueError):
|
|
461
458
|
raise ValidationException("")
|
|
462
459
|
shared_secret = self.crypto_key.key.exchange(ec.ECDH(), pub_key)
|
|
463
|
-
|
|
464
|
-
return HKDF(
|
|
465
|
-
algorithm=algorithm,
|
|
466
|
-
salt=None,
|
|
467
|
-
info=b"",
|
|
468
|
-
length=algorithm.digest_size,
|
|
469
|
-
backend=default_backend(),
|
|
470
|
-
).derive(shared_secret)
|
|
460
|
+
return shared_secret
|
|
471
461
|
|
|
472
462
|
# This method gets called when a key is replicated to another region. It's meant to populate the required metadata
|
|
473
463
|
# fields in a new replica key.
|
|
@@ -138,6 +138,7 @@ from localstack.services.kms.utils import (
|
|
|
138
138
|
validate_alias_name,
|
|
139
139
|
)
|
|
140
140
|
from localstack.services.plugins import ServiceLifecycleHook
|
|
141
|
+
from localstack.state import StateVisitor
|
|
141
142
|
from localstack.utils.aws.arns import get_partition, kms_alias_arn, parse_arn
|
|
142
143
|
from localstack.utils.collections import PaginatedList
|
|
143
144
|
from localstack.utils.common import select_attributes
|
|
@@ -201,6 +202,9 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
201
202
|
- VerifyMac
|
|
202
203
|
"""
|
|
203
204
|
|
|
205
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
206
|
+
visitor.visit(kms_stores)
|
|
207
|
+
|
|
204
208
|
#
|
|
205
209
|
# Helpers
|
|
206
210
|
#
|
|
@@ -3,6 +3,8 @@ Everything related to behavior or implicit functionality goes into `lambda_utils
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import datetime
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
6
8
|
import random
|
|
7
9
|
import re
|
|
8
10
|
import string
|
|
@@ -62,13 +64,14 @@ DESTINATION_ARN_PATTERN = re.compile(
|
|
|
62
64
|
r"^$|arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\-])+:([a-z]{2}(-gov)?-[a-z]+-\d{1})?:(\d{12})?:(.*)"
|
|
63
65
|
)
|
|
64
66
|
|
|
67
|
+
# TODO: what's the difference between AWS_FUNCTION_NAME_REGEX and FUNCTION_NAME_REGEX? Can we unify?
|
|
65
68
|
AWS_FUNCTION_NAME_REGEX = re.compile(
|
|
66
|
-
"^(arn:(aws[a-zA-Z-]*)?:lambda:)?([a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_.]+)(:(\\$LATEST
|
|
69
|
+
"^(arn:(aws[a-zA-Z-]*)?:lambda:)?([a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_.]+)(:(\\$LATEST(\\.PUBLISHED)?|[a-zA-Z0-9-_]+))?$"
|
|
67
70
|
)
|
|
68
71
|
|
|
69
72
|
# Pattern for extracting various attributes from a full or partial ARN or just a function name.
|
|
70
73
|
FUNCTION_NAME_REGEX = re.compile(
|
|
71
|
-
r"(arn:(aws[a-zA-Z-]*):lambda:)?((?P<region>[a-z]{2}(-gov)?-[a-z]+-\d{1}):)?(?:(?P<account>\d{12}):)?(function:)?(?P<name>[a-zA-Z0-9-_\.]+)(:(?P<qualifier>\$LATEST
|
|
74
|
+
r"(arn:(aws[a-zA-Z-]*):lambda:)?((?P<region>[a-z]{2}(-gov)?-[a-z]+-\d{1}):)?(?:(?P<account>\d{12}):)?(function:)?(?P<name>[a-zA-Z0-9-_\.]+)(:(?P<qualifier>\$LATEST(\.PUBLISHED)?|[a-zA-Z0-9-_]+))?"
|
|
72
75
|
) # also length 1-170 incl.
|
|
73
76
|
# Pattern for a lambda function handler
|
|
74
77
|
HANDLER_REGEX = re.compile(r"[^\s]+")
|
|
@@ -86,9 +89,11 @@ SIGNING_JOB_ARN_REGEX = re.compile(
|
|
|
86
89
|
SIGNING_PROFILE_VERSION_ARN_REGEX = re.compile(
|
|
87
90
|
r"arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\-])+:([a-z]{2}(-gov)?-[a-z]+-\d{1})?:(\d{12})?:(.*)"
|
|
88
91
|
)
|
|
89
|
-
# Combined pattern for alias and version based on AWS error using "(|[a-zA-Z0-9$
|
|
90
|
-
|
|
92
|
+
# Combined pattern for alias and version based on AWS error using "\\$(LATEST(\\.PUBLISHED)?)|[a-zA-Z0-9-_$]+"
|
|
93
|
+
# This regex is based on the snapshotted validation message, just removing the double \\ before $LATEST
|
|
94
|
+
QUALIFIER_REGEX = re.compile(r"^\$(LATEST(\\.PUBLISHED)?)|[a-zA-Z0-9-_$]+$")
|
|
91
95
|
# Pattern for a version qualifier
|
|
96
|
+
# TODO: do we need to consider $LATEST.PUBLISHED here?
|
|
92
97
|
VERSION_REGEX = re.compile(r"^[0-9]+$")
|
|
93
98
|
# Pattern for an alias qualifier
|
|
94
99
|
# Rules: https://docs.aws.amazon.com/lambda/latest/dg/API_CreateAlias.html#SSS-CreateAlias-request-Name
|
|
@@ -107,36 +112,42 @@ LAMBDA_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%f+0000"
|
|
|
107
112
|
# An unordered list of all Lambda CPU architectures supported by LocalStack.
|
|
108
113
|
ARCHITECTURES = [Architecture.arm64, Architecture.x86_64]
|
|
109
114
|
|
|
110
|
-
# ARN
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
ARN_NAME_PATTERN_VALIDATION_TEMPLATE = "(arn:(aws[a-zA-Z-]*)?:lambda:)?([a-z]{{2}}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{{1}}:)?(\\d{{12}}:)?(function:)?([a-zA-Z0-9-_{0}]+)(:(\\$LATEST|[a-zA-Z0-9-_]+))?"
|
|
115
|
+
# ARN patterns returned in validation exception messages
|
|
116
|
+
ARN_NAME_PATTERN_GET = r"(arn:(aws[a-zA-Z-]*)?:lambda:)?((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_\.]+)(:(\$LATEST(\.PUBLISHED)?|[a-zA-Z0-9-_]+))?"
|
|
117
|
+
ARN_NAME_PATTERN_CREATE = r"(arn:(aws[a-zA-Z-]*)?:lambda:)?((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?"
|
|
114
118
|
|
|
115
119
|
# AWS response when invalid ARNs are used in Tag operations.
|
|
116
|
-
TAGGABLE_RESOURCE_ARN_PATTERN = "arn:(aws[a-zA-Z-]*):lambda:[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:(function:[a-zA-Z0-9-_]+(:(\\$LATEST|[a-zA-Z0-9-_]+))?|layer:([a-zA-Z0-9-_]+)|code-signing-config:csc-[a-z0-9]{17}|event-source-mapping:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})"
|
|
120
|
+
TAGGABLE_RESOURCE_ARN_PATTERN = "arn:(aws[a-zA-Z-]*):lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:(function:[a-zA-Z0-9-_]+(:(\\$LATEST|[a-zA-Z0-9-_]+))?|layer:([a-zA-Z0-9-_]+)|code-signing-config:csc-[a-z0-9]{17}|event-source-mapping:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|capacity-provider:[a-zA-Z0-9-_]+)"
|
|
117
121
|
|
|
118
122
|
|
|
119
123
|
def validate_function_name(function_name_or_arn: str, operation_type: str):
|
|
120
124
|
function_name, *_ = function_locators_from_arn(function_name_or_arn)
|
|
121
|
-
arn_name_pattern =
|
|
125
|
+
arn_name_pattern = ARN_NAME_PATTERN_CREATE
|
|
122
126
|
max_length = 170
|
|
123
127
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
if operation_type == "GetFunction" or operation_type == "Invoke":
|
|
129
|
+
arn_name_pattern = ARN_NAME_PATTERN_GET
|
|
130
|
+
elif operation_type == "CreateFunction":
|
|
131
|
+
# https://docs.aws.amazon.com/lambda/latest/api/API_CreateFunction.html#lambda-CreateFunction-request-FunctionName
|
|
132
|
+
if function_name == function_name_or_arn: # only a function name
|
|
128
133
|
max_length = 64
|
|
129
|
-
|
|
134
|
+
else: # full or partial ARN
|
|
130
135
|
max_length = 140
|
|
136
|
+
elif operation_type == "DeleteFunction":
|
|
137
|
+
max_length = 140
|
|
138
|
+
arn_name_pattern = ARN_NAME_PATTERN_GET
|
|
131
139
|
|
|
132
140
|
validations = []
|
|
133
|
-
if
|
|
134
|
-
constraint = f"Member must
|
|
141
|
+
if not AWS_FUNCTION_NAME_REGEX.match(function_name_or_arn) or not function_name:
|
|
142
|
+
constraint = f"Member must satisfy regular expression pattern: {arn_name_pattern}"
|
|
135
143
|
validation_msg = f"Value '{function_name_or_arn}' at 'functionName' failed to satisfy constraint: {constraint}"
|
|
136
144
|
validations.append(validation_msg)
|
|
145
|
+
if not operation_type == "CreateFunction":
|
|
146
|
+
# Immediately raises rather than summarizing all validations, except for CreateFunction
|
|
147
|
+
return validations
|
|
137
148
|
|
|
138
|
-
if
|
|
139
|
-
constraint = f"Member must
|
|
149
|
+
if len(function_name_or_arn) > max_length:
|
|
150
|
+
constraint = f"Member must have length less than or equal to {max_length}"
|
|
140
151
|
validation_msg = f"Value '{function_name_or_arn}' at 'functionName' failed to satisfy constraint: {constraint}"
|
|
141
152
|
validations.append(validation_msg)
|
|
142
153
|
|
|
@@ -154,7 +165,7 @@ def validate_qualifier(qualifier: str):
|
|
|
154
165
|
validations.append(validation_msg)
|
|
155
166
|
|
|
156
167
|
if not QUALIFIER_REGEX.match(qualifier):
|
|
157
|
-
constraint = "Member must satisfy regular expression pattern: (|[a-zA-Z0-9$
|
|
168
|
+
constraint = "Member must satisfy regular expression pattern: \\$(LATEST(\\.PUBLISHED)?)|[a-zA-Z0-9-_$]+"
|
|
158
169
|
validation_msg = (
|
|
159
170
|
f"Value '{qualifier}' at 'qualifier' failed to satisfy constraint: {constraint}"
|
|
160
171
|
)
|
|
@@ -571,6 +582,12 @@ def map_config_out(
|
|
|
571
582
|
optional_kwargs["CodeSize"] = 0
|
|
572
583
|
optional_kwargs["CodeSha256"] = version.config.image.code_sha256
|
|
573
584
|
|
|
585
|
+
if version.config.CapacityProviderConfig:
|
|
586
|
+
optional_kwargs["CapacityProviderConfig"] = version.config.CapacityProviderConfig
|
|
587
|
+
data = json.dumps(version.config.CapacityProviderConfig, sort_keys=True).encode("utf-8")
|
|
588
|
+
config_sha_256 = hashlib.sha256(data).hexdigest()
|
|
589
|
+
optional_kwargs["ConfigSha256"] = config_sha_256
|
|
590
|
+
|
|
574
591
|
# output for an alias qualifier is completely the same except for the returned ARN
|
|
575
592
|
if alias_name:
|
|
576
593
|
function_arn = f"{':'.join(version.id.qualified_arn().split(':')[:-1])}:{alias_name}"
|
|
@@ -36,7 +36,7 @@ from localstack.services.lambda_.event_source_mapping.senders.sender_utils impor
|
|
|
36
36
|
)
|
|
37
37
|
from localstack.utils.aws.arns import parse_arn, s3_bucket_name
|
|
38
38
|
from localstack.utils.backoff import ExponentialBackoff
|
|
39
|
-
from localstack.utils.
|
|
39
|
+
from localstack.utils.batching import Batcher
|
|
40
40
|
from localstack.utils.strings import long_uid
|
|
41
41
|
|
|
42
42
|
LOG = logging.getLogger(__name__)
|
|
@@ -88,9 +88,12 @@ class AssignmentService(OtherServiceEndpoint):
|
|
|
88
88
|
self, version_manager_id: str, function_version: FunctionVersion
|
|
89
89
|
) -> ExecutionEnvironment:
|
|
90
90
|
LOG.debug("Starting new environment")
|
|
91
|
+
initialization_type = "on-demand"
|
|
92
|
+
if function_version.config.CapacityProviderConfig:
|
|
93
|
+
initialization_type = "lambda-managed-instances"
|
|
91
94
|
execution_environment = ExecutionEnvironment(
|
|
92
95
|
function_version=function_version,
|
|
93
|
-
initialization_type=
|
|
96
|
+
initialization_type=initialization_type,
|
|
94
97
|
on_timeout=self.on_timeout,
|
|
95
98
|
version_manager_id=version_manager_id,
|
|
96
99
|
)
|
|
@@ -8,6 +8,7 @@ from enum import Enum, auto
|
|
|
8
8
|
from threading import RLock, Timer
|
|
9
9
|
|
|
10
10
|
from localstack import config
|
|
11
|
+
from localstack.aws.api.lambda_ import LogFormat
|
|
11
12
|
from localstack.aws.connect import connect_to
|
|
12
13
|
from localstack.services.lambda_.invocation.lambda_models import (
|
|
13
14
|
Credentials,
|
|
@@ -149,6 +150,22 @@ class ExecutionEnvironment:
|
|
|
149
150
|
# LOCALSTACK_USER conditionally added below
|
|
150
151
|
}
|
|
151
152
|
# Conditionally added environment variables
|
|
153
|
+
# Lambda advanced logging controls:
|
|
154
|
+
# https://aws.amazon.com/blogs/compute/introducing-advanced-logging-controls-for-aws-lambda-functions/
|
|
155
|
+
logging_config = self.function_version.config.logging_config
|
|
156
|
+
if logging_config.get("LogFormat") == LogFormat.JSON:
|
|
157
|
+
env_vars["AWS_LAMBDA_LOG_FORMAT"] = logging_config["LogFormat"]
|
|
158
|
+
# TODO: test this (currently not implemented in LocalStack)
|
|
159
|
+
env_vars["AWS_LAMBDA_LOG_LEVEL"] = logging_config["ApplicationLogLevel"].capitalize()
|
|
160
|
+
# Lambda Managed Instances
|
|
161
|
+
if capacity_provider_config := self.function_version.config.CapacityProviderConfig:
|
|
162
|
+
# Disable dropping privileges for parity
|
|
163
|
+
# TODO: implement mixed permissions (maybe in RIE)
|
|
164
|
+
# env_vars["LOCALSTACK_USER"] = "root"
|
|
165
|
+
env_vars["AWS_LAMBDA_MAX_CONCURRENCY"] = capacity_provider_config[
|
|
166
|
+
"LambdaManagedInstancesCapacityProviderConfig"
|
|
167
|
+
]["PerExecutionEnvironmentMaxConcurrency"]
|
|
168
|
+
env_vars["TZ"] = ":/etc/localtime"
|
|
152
169
|
if not config.LAMBDA_DISABLE_AWS_ENDPOINT_URL:
|
|
153
170
|
env_vars["AWS_ENDPOINT_URL"] = (
|
|
154
171
|
f"http://{self.runtime_executor.get_endpoint_from_executor()}:{config.GATEWAY_LISTEN[0].port}"
|
|
@@ -159,8 +176,6 @@ class ExecutionEnvironment:
|
|
|
159
176
|
# Will be overridden by the runtime itself unless it is a provided runtime
|
|
160
177
|
if self.function_version.config.runtime:
|
|
161
178
|
env_vars["AWS_EXECUTION_ENV"] = "AWS_Lambda_rapid"
|
|
162
|
-
if self.function_version.config.environment:
|
|
163
|
-
env_vars.update(self.function_version.config.environment)
|
|
164
179
|
if config.LAMBDA_INIT_DEBUG:
|
|
165
180
|
# Disable dropping privileges because it breaks debugging
|
|
166
181
|
env_vars["LOCALSTACK_USER"] = "root"
|
|
@@ -175,6 +190,10 @@ class ExecutionEnvironment:
|
|
|
175
190
|
env_vars["LOCALSTACK_MAX_PAYLOAD_SIZE"] = int(
|
|
176
191
|
config.LAMBDA_LIMITS_MAX_FUNCTION_PAYLOAD_SIZE_BYTES
|
|
177
192
|
)
|
|
193
|
+
|
|
194
|
+
# Let users overwrite any environment variable at last (if they want so)
|
|
195
|
+
if self.function_version.config.environment:
|
|
196
|
+
env_vars.update(self.function_version.config.environment)
|
|
178
197
|
return env_vars
|
|
179
198
|
|
|
180
199
|
# Lifecycle methods
|
|
@@ -12,7 +12,7 @@ import threading
|
|
|
12
12
|
from abc import ABCMeta, abstractmethod
|
|
13
13
|
from datetime import datetime
|
|
14
14
|
from pathlib import Path
|
|
15
|
-
from typing import IO, Literal, TypedDict
|
|
15
|
+
from typing import IO, Literal, Optional, TypedDict
|
|
16
16
|
|
|
17
17
|
import boto3
|
|
18
18
|
from botocore.exceptions import ClientError
|
|
@@ -22,12 +22,19 @@ from localstack.aws.api import CommonServiceException
|
|
|
22
22
|
from localstack.aws.api.lambda_ import (
|
|
23
23
|
AllowedPublishers,
|
|
24
24
|
Architecture,
|
|
25
|
+
CapacityProviderArn,
|
|
26
|
+
CapacityProviderConfig,
|
|
27
|
+
CapacityProviderPermissionsConfig,
|
|
28
|
+
CapacityProviderScalingConfig,
|
|
29
|
+
CapacityProviderVpcConfig,
|
|
25
30
|
CodeSigningPolicies,
|
|
26
31
|
Cors,
|
|
27
32
|
DestinationConfig,
|
|
28
33
|
FunctionUrlAuthType,
|
|
34
|
+
InstanceRequirements,
|
|
29
35
|
InvocationType,
|
|
30
36
|
InvokeMode,
|
|
37
|
+
KMSKeyArn,
|
|
31
38
|
LastUpdateStatus,
|
|
32
39
|
LoggingConfig,
|
|
33
40
|
PackageType,
|
|
@@ -38,12 +45,14 @@ from localstack.aws.api.lambda_ import (
|
|
|
38
45
|
SnapStartResponse,
|
|
39
46
|
State,
|
|
40
47
|
StateReasonCode,
|
|
48
|
+
Timestamp,
|
|
41
49
|
TracingMode,
|
|
42
50
|
)
|
|
43
51
|
from localstack.aws.connect import connect_to
|
|
44
52
|
from localstack.constants import AWS_REGION_US_EAST_1, INTERNAL_AWS_SECRET_ACCESS_KEY
|
|
45
53
|
from localstack.services.lambda_.api_utils import qualified_lambda_arn, unqualified_lambda_arn
|
|
46
54
|
from localstack.utils.archives import unzip
|
|
55
|
+
from localstack.utils.files import chmod_r
|
|
47
56
|
from localstack.utils.strings import long_uid, short_uid
|
|
48
57
|
|
|
49
58
|
LOG = logging.getLogger(__name__)
|
|
@@ -212,6 +221,7 @@ class S3Code(ArchiveCode):
|
|
|
212
221
|
with tempfile.NamedTemporaryFile() as file:
|
|
213
222
|
self._download_archive_to_file(file)
|
|
214
223
|
unzip(file.name, str(target_path))
|
|
224
|
+
chmod_r(str(target_path), 0o755)
|
|
215
225
|
|
|
216
226
|
def destroy_cached(self) -> None:
|
|
217
227
|
"""
|
|
@@ -331,7 +341,7 @@ class VpcConfig:
|
|
|
331
341
|
@dataclasses.dataclass(frozen=True)
|
|
332
342
|
class UpdateStatus:
|
|
333
343
|
status: LastUpdateStatus | None
|
|
334
|
-
code: str | None = None
|
|
344
|
+
code: str | None = None
|
|
335
345
|
reason: str | None = None
|
|
336
346
|
|
|
337
347
|
|
|
@@ -568,6 +578,9 @@ class VersionFunctionConfiguration:
|
|
|
568
578
|
vpc_config: VpcConfig | None = None
|
|
569
579
|
|
|
570
580
|
logging_config: LoggingConfig = dataclasses.field(default_factory=dict)
|
|
581
|
+
# TODO: why does `CapacityProviderConfig | None = None` fail with on Python 3.13.9:
|
|
582
|
+
# TypeError: unsupported operand type(s) for |: 'NoneType' and 'NoneType'
|
|
583
|
+
CapacityProviderConfig: Optional[CapacityProviderConfig] = None # noqa
|
|
571
584
|
|
|
572
585
|
|
|
573
586
|
@dataclasses.dataclass(frozen=True)
|
|
@@ -580,6 +593,18 @@ class FunctionVersion:
|
|
|
580
593
|
return self.id.qualified_arn()
|
|
581
594
|
|
|
582
595
|
|
|
596
|
+
@dataclasses.dataclass
|
|
597
|
+
class CapacityProvider:
|
|
598
|
+
CapacityProviderArn: CapacityProviderArn
|
|
599
|
+
# State is determined dynamically
|
|
600
|
+
VpcConfig: CapacityProviderVpcConfig
|
|
601
|
+
PermissionsConfig: CapacityProviderPermissionsConfig
|
|
602
|
+
InstanceRequirements: InstanceRequirements
|
|
603
|
+
CapacityProviderScalingConfig: CapacityProviderScalingConfig
|
|
604
|
+
LastModified: Timestamp
|
|
605
|
+
KmsKeyArn: KMSKeyArn | None = None
|
|
606
|
+
|
|
607
|
+
|
|
583
608
|
@dataclasses.dataclass
|
|
584
609
|
class Function:
|
|
585
610
|
function_name: str
|
|
@@ -5,6 +5,7 @@ import io
|
|
|
5
5
|
import logging
|
|
6
6
|
import os.path
|
|
7
7
|
import random
|
|
8
|
+
import time
|
|
8
9
|
import uuid
|
|
9
10
|
from concurrent.futures import Executor, Future, ThreadPoolExecutor
|
|
10
11
|
from datetime import datetime
|
|
@@ -19,6 +20,7 @@ from localstack.aws.api.lambda_ import (
|
|
|
19
20
|
InvalidRequestContentException,
|
|
20
21
|
InvocationType,
|
|
21
22
|
LastUpdateStatus,
|
|
23
|
+
NoPublishedVersionException,
|
|
22
24
|
ResourceConflictException,
|
|
23
25
|
ResourceNotFoundException,
|
|
24
26
|
State,
|
|
@@ -190,6 +192,9 @@ class LambdaService:
|
|
|
190
192
|
lambda_hooks.create_function_version.run(function_version.qualified_arn)
|
|
191
193
|
return self.task_executor.submit(self._start_lambda_version, version_manager)
|
|
192
194
|
|
|
195
|
+
def publish_version_async(self, function_version: FunctionVersion):
|
|
196
|
+
self.task_executor.submit(self.publish_version, function_version)
|
|
197
|
+
|
|
193
198
|
def publish_version(self, function_version: FunctionVersion):
|
|
194
199
|
"""
|
|
195
200
|
Synchronously create a function version (manager)
|
|
@@ -200,6 +205,14 @@ class LambdaService:
|
|
|
200
205
|
|
|
201
206
|
:param function_version: Function Version to create
|
|
202
207
|
"""
|
|
208
|
+
# HACK: trying to match the AWS timing behavior of Lambda Managed Instances for the operation
|
|
209
|
+
# publish_version followed by get_function because transitioning LastUpdateStatus from InProgress to
|
|
210
|
+
# Successful happens too fast on LocalStack (thanks to caching in prepare_version).
|
|
211
|
+
# Without this hack, test_latest_published_update_config fails at get_function_response_postpublish
|
|
212
|
+
# and test_lifecycle_invoke is flaky, sometimes not triggering the ResourceConflictException
|
|
213
|
+
# Increasing this sleep too much (e.g., 10s) shouldn't cause any side effects apart from slow responsiveness
|
|
214
|
+
if function_version.config.CapacityProviderConfig:
|
|
215
|
+
time.sleep(0.1)
|
|
203
216
|
with self.lambda_version_manager_lock:
|
|
204
217
|
qualified_arn = function_version.id.qualified_arn()
|
|
205
218
|
version_manager = self.lambda_starting_versions.get(qualified_arn)
|
|
@@ -225,7 +238,7 @@ class LambdaService:
|
|
|
225
238
|
def invoke(
|
|
226
239
|
self,
|
|
227
240
|
function_name: str,
|
|
228
|
-
qualifier: str,
|
|
241
|
+
qualifier: str | None,
|
|
229
242
|
region: str,
|
|
230
243
|
account_id: str,
|
|
231
244
|
invocation_type: InvocationType | None,
|
|
@@ -258,13 +271,27 @@ class LambdaService:
|
|
|
258
271
|
account=account_id,
|
|
259
272
|
region=region,
|
|
260
273
|
)
|
|
261
|
-
qualifier = qualifier or "$LATEST"
|
|
262
274
|
state = lambda_stores[account_id][region]
|
|
263
275
|
function = state.functions.get(function_name)
|
|
264
276
|
|
|
265
277
|
if function is None:
|
|
278
|
+
if not qualifier:
|
|
279
|
+
invoked_arn += ":$LATEST"
|
|
266
280
|
raise ResourceNotFoundException(f"Function not found: {invoked_arn}", Type="User")
|
|
267
281
|
|
|
282
|
+
# A provided qualifier always takes precedence, but the default depends on whether $LATEST.PUBLISHED exists
|
|
283
|
+
version_latest_published = function.versions.get("$LATEST.PUBLISHED")
|
|
284
|
+
if version_latest_published:
|
|
285
|
+
qualifier = qualifier or "$LATEST.PUBLISHED"
|
|
286
|
+
invoked_arn = lambda_arn(
|
|
287
|
+
function_name=function_name,
|
|
288
|
+
qualifier=qualifier,
|
|
289
|
+
account=account_id,
|
|
290
|
+
region=region,
|
|
291
|
+
)
|
|
292
|
+
else:
|
|
293
|
+
qualifier = qualifier or "$LATEST"
|
|
294
|
+
|
|
268
295
|
if qualifier_is_alias(qualifier):
|
|
269
296
|
alias = function.aliases.get(qualifier)
|
|
270
297
|
if not alias:
|
|
@@ -282,8 +309,21 @@ class LambdaService:
|
|
|
282
309
|
# Need the qualified arn to exactly get the target lambda
|
|
283
310
|
qualified_arn = qualified_lambda_arn(function_name, version_qualifier, account_id, region)
|
|
284
311
|
version = function.versions.get(version_qualifier)
|
|
312
|
+
if version is None:
|
|
313
|
+
raise ResourceNotFoundException(f"Function not found: {invoked_arn}", Type="User")
|
|
285
314
|
runtime = version.config.runtime or "n/a"
|
|
286
315
|
package_type = version.config.package_type
|
|
316
|
+
if version.config.CapacityProviderConfig and qualifier == "$LATEST":
|
|
317
|
+
if function.versions.get("$LATEST.PUBLISHED"):
|
|
318
|
+
raise InvalidParameterValueException(
|
|
319
|
+
"Functions configured with capacity provider configuration can't be invoked with $LATEST qualifier. To invoke this function, specify a published version qualifier or $LATEST.PUBLISHED.",
|
|
320
|
+
Type="User",
|
|
321
|
+
)
|
|
322
|
+
else:
|
|
323
|
+
raise NoPublishedVersionException(
|
|
324
|
+
"The function can't be invoked because no published version exists. For functions with capacity provider configuration, either publish a version to $LATEST.PUBLISHED, or specify a published version qualifier.",
|
|
325
|
+
Type="User",
|
|
326
|
+
)
|
|
287
327
|
try:
|
|
288
328
|
version_manager = self.get_lambda_version_manager(qualified_arn)
|
|
289
329
|
event_manager = self.get_lambda_event_manager(qualified_arn)
|
|
@@ -393,7 +433,10 @@ class LambdaService:
|
|
|
393
433
|
|
|
394
434
|
:param new_version: New version (with the same qualifier as an older one)
|
|
395
435
|
"""
|
|
396
|
-
if
|
|
436
|
+
if (
|
|
437
|
+
new_version.qualified_arn not in self.lambda_running_versions
|
|
438
|
+
and not new_version.config.CapacityProviderConfig
|
|
439
|
+
):
|
|
397
440
|
raise ValueError(
|
|
398
441
|
f"Version {new_version.qualified_arn} cannot be updated if an old one is not running"
|
|
399
442
|
)
|
|
@@ -437,6 +480,11 @@ class LambdaService:
|
|
|
437
480
|
elif new_state.state == State.Failed:
|
|
438
481
|
update_status = UpdateStatus(status=LastUpdateStatus.Failed)
|
|
439
482
|
self.task_executor.submit(new_version_manager.stop)
|
|
483
|
+
elif (
|
|
484
|
+
new_state.state == State.ActiveNonInvocable
|
|
485
|
+
and function_version.config.CapacityProviderConfig
|
|
486
|
+
):
|
|
487
|
+
update_status = UpdateStatus(status=LastUpdateStatus.Successful)
|
|
440
488
|
else:
|
|
441
489
|
# TODO what to do if state pending or inactive is supported?
|
|
442
490
|
self.task_executor.submit(new_version_manager.stop)
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
from localstack.aws.api.lambda_ import EventSourceMappingConfiguration
|
|
2
|
-
from localstack.services.lambda_.invocation.lambda_models import
|
|
2
|
+
from localstack.services.lambda_.invocation.lambda_models import (
|
|
3
|
+
CapacityProvider,
|
|
4
|
+
CodeSigningConfig,
|
|
5
|
+
Function,
|
|
6
|
+
Layer,
|
|
7
|
+
)
|
|
3
8
|
from localstack.services.stores import AccountRegionBundle, BaseStore, LocalAttribute
|
|
4
9
|
from localstack.utils.tagging import TaggingService
|
|
5
10
|
|
|
@@ -17,6 +22,9 @@ class LambdaStore(BaseStore):
|
|
|
17
22
|
# maps layer names to Layers
|
|
18
23
|
layers: dict[str, Layer] = LocalAttribute(default=dict)
|
|
19
24
|
|
|
25
|
+
# maps capacity provider names to respective CapacityProvider
|
|
26
|
+
capacity_providers: dict[str, CapacityProvider] = LocalAttribute(default=dict)
|
|
27
|
+
|
|
20
28
|
# maps resource ARNs for EventSourceMappings and CodeSigningConfiguration to tags
|
|
21
29
|
TAGS = LocalAttribute(default=TaggingService)
|
|
22
30
|
|
|
@@ -100,11 +100,26 @@ class LambdaVersionManager:
|
|
|
100
100
|
|
|
101
101
|
# code and reason not set for success scenario because only failed states provide this field:
|
|
102
102
|
# https://docs.aws.amazon.com/lambda/latest/dg/API_GetFunctionConfiguration.html#SSS-GetFunctionConfiguration-response-LastUpdateStatusReasonCode
|
|
103
|
-
|
|
103
|
+
new_state = State.Active
|
|
104
|
+
if (
|
|
105
|
+
self.function_version.config.CapacityProviderConfig
|
|
106
|
+
and self.function_version.id.qualifier == "$LATEST"
|
|
107
|
+
):
|
|
108
|
+
new_state = State.ActiveNonInvocable
|
|
109
|
+
# HACK: trying to match the AWS timing behavior of Lambda Managed Instances for the operation
|
|
110
|
+
# update_function_configuration followed by get_function because transitioning LastUpdateStatus from
|
|
111
|
+
# InProgress to Successful happens too fast on LocalStack (thanks to caching in prepare_version).
|
|
112
|
+
# Without this hack, test_latest_published_update_config fails at get_function_response_postupdate_latest
|
|
113
|
+
# TODO: this sleep has side-effects and we should be looking into alternatives
|
|
114
|
+
# Increasing this sleep too much (e.g., 3s) could cause the side effect that a created function is not
|
|
115
|
+
# ready for updates (i.e., rejected with a ResourceConflictException) and failing other tests
|
|
116
|
+
# time.sleep(0.1)
|
|
117
|
+
self.state = VersionState(state=new_state)
|
|
104
118
|
LOG.debug(
|
|
105
|
-
"Changing Lambda %s (id %s) to
|
|
119
|
+
"Changing Lambda %s (id %s) to %s",
|
|
106
120
|
self.function_arn,
|
|
107
121
|
self.function_version.config.internal_revision,
|
|
122
|
+
new_state,
|
|
108
123
|
)
|
|
109
124
|
except Exception as e:
|
|
110
125
|
self.state = VersionState(
|
|
@@ -113,7 +128,7 @@ class LambdaVersionManager:
|
|
|
113
128
|
reason=f"Error while creating lambda: {e}",
|
|
114
129
|
)
|
|
115
130
|
LOG.debug(
|
|
116
|
-
"Changing Lambda %s (id %s) to
|
|
131
|
+
"Changing Lambda %s (id %s) to Failed. Reason: %s",
|
|
117
132
|
self.function_arn,
|
|
118
133
|
self.function_version.config.internal_revision,
|
|
119
134
|
e,
|