localstack-core 4.10.1.dev42__py3-none-any.whl → 4.12.1.dev18__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.

Potentially problematic release.


This version of localstack-core might be problematic. Click here for more details.

Files changed (158) hide show
  1. localstack/aws/api/apigateway/__init__.py +42 -0
  2. localstack/aws/api/cloudformation/__init__.py +161 -0
  3. localstack/aws/api/ec2/__init__.py +1178 -12
  4. localstack/aws/api/iam/__init__.py +228 -0
  5. localstack/aws/api/kms/__init__.py +1 -0
  6. localstack/aws/api/lambda_/__init__.py +1034 -66
  7. localstack/aws/api/logs/__init__.py +500 -0
  8. localstack/aws/api/opensearch/__init__.py +100 -0
  9. localstack/aws/api/redshift/__init__.py +69 -0
  10. localstack/aws/api/resourcegroupstaggingapi/__init__.py +36 -0
  11. localstack/aws/api/route53/__init__.py +45 -0
  12. localstack/aws/api/route53resolver/__init__.py +1 -0
  13. localstack/aws/api/s3/__init__.py +64 -0
  14. localstack/aws/api/s3control/__init__.py +19 -0
  15. localstack/aws/api/secretsmanager/__init__.py +37 -23
  16. localstack/aws/api/stepfunctions/__init__.py +52 -10
  17. localstack/aws/api/sts/__init__.py +52 -0
  18. localstack/aws/connect.py +35 -15
  19. localstack/aws/handlers/logging.py +8 -4
  20. localstack/aws/handlers/service.py +11 -2
  21. localstack/aws/protocol/serializer.py +1 -1
  22. localstack/config.py +8 -0
  23. localstack/constants.py +3 -0
  24. localstack/deprecations.py +0 -6
  25. localstack/dev/kubernetes/__main__.py +39 -14
  26. localstack/runtime/analytics.py +11 -0
  27. localstack/services/acm/provider.py +17 -1
  28. localstack/services/apigateway/legacy/provider.py +28 -15
  29. localstack/services/cloudformation/engine/template_preparer.py +6 -2
  30. localstack/services/cloudformation/engine/v2/change_set_model.py +9 -0
  31. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +15 -1
  32. localstack/services/cloudformation/engine/v2/change_set_resource_support_checker.py +114 -0
  33. localstack/services/cloudformation/provider.py +26 -1
  34. localstack/services/cloudformation/provider_utils.py +20 -0
  35. localstack/services/cloudformation/resource_provider.py +5 -4
  36. localstack/services/cloudformation/scaffolding/__main__.py +94 -22
  37. localstack/services/cloudformation/v2/provider.py +41 -0
  38. localstack/services/cloudwatch/provider.py +10 -3
  39. localstack/services/cloudwatch/provider_v2.py +6 -3
  40. localstack/services/configservice/provider.py +5 -1
  41. localstack/services/dynamodb/provider.py +1 -0
  42. localstack/services/dynamodb/v2/provider.py +1 -0
  43. localstack/services/dynamodbstreams/provider.py +6 -0
  44. localstack/services/dynamodbstreams/v2/provider.py +6 -0
  45. localstack/services/ec2/provider.py +6 -0
  46. localstack/services/es/provider.py +6 -0
  47. localstack/services/events/provider.py +4 -0
  48. localstack/services/events/v1/provider.py +9 -0
  49. localstack/services/firehose/provider.py +5 -0
  50. localstack/services/iam/provider.py +4 -0
  51. localstack/services/kinesis/packages.py +1 -1
  52. localstack/services/kms/models.py +16 -22
  53. localstack/services/kms/provider.py +4 -0
  54. localstack/services/lambda_/analytics.py +11 -2
  55. localstack/services/lambda_/api_utils.py +37 -20
  56. localstack/services/lambda_/event_source_mapping/pollers/stream_poller.py +1 -1
  57. localstack/services/lambda_/invocation/assignment.py +4 -1
  58. localstack/services/lambda_/invocation/event_manager.py +15 -11
  59. localstack/services/lambda_/invocation/execution_environment.py +21 -2
  60. localstack/services/lambda_/invocation/lambda_models.py +31 -2
  61. localstack/services/lambda_/invocation/lambda_service.py +62 -3
  62. localstack/services/lambda_/invocation/models.py +9 -1
  63. localstack/services/lambda_/invocation/version_manager.py +18 -3
  64. localstack/services/lambda_/provider.py +307 -106
  65. localstack/services/lambda_/resource_providers/aws_lambda_function.py +33 -1
  66. localstack/services/lambda_/runtimes.py +3 -1
  67. localstack/services/logs/provider.py +9 -0
  68. localstack/services/opensearch/packages.py +34 -20
  69. localstack/services/opensearch/provider.py +53 -3
  70. localstack/services/resource_groups/provider.py +5 -1
  71. localstack/services/resourcegroupstaggingapi/provider.py +6 -1
  72. localstack/services/route53/provider.py +7 -0
  73. localstack/services/route53resolver/provider.py +5 -0
  74. localstack/services/s3/constants.py +5 -0
  75. localstack/services/s3/exceptions.py +9 -0
  76. localstack/services/s3/models.py +9 -1
  77. localstack/services/s3/provider.py +51 -43
  78. localstack/services/s3/utils.py +81 -15
  79. localstack/services/s3control/provider.py +107 -2
  80. localstack/services/s3control/validation.py +50 -0
  81. localstack/services/scheduler/provider.py +4 -2
  82. localstack/services/secretsmanager/provider.py +4 -0
  83. localstack/services/ses/provider.py +4 -0
  84. localstack/services/sns/constants.py +16 -1
  85. localstack/services/sns/provider.py +5 -0
  86. localstack/services/sns/publisher.py +15 -6
  87. localstack/services/sns/v2/models.py +9 -0
  88. localstack/services/sns/v2/provider.py +750 -19
  89. localstack/services/sns/v2/utils.py +12 -0
  90. localstack/services/sqs/constants.py +6 -0
  91. localstack/services/sqs/provider.py +9 -1
  92. localstack/services/sqs/resource_providers/aws_sqs_queue.py +61 -46
  93. localstack/services/ssm/provider.py +6 -0
  94. localstack/services/stepfunctions/asl/component/common/path/result_path.py +1 -1
  95. localstack/services/stepfunctions/asl/component/state/state_execution/execute_state.py +0 -1
  96. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +0 -1
  97. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/lambda_eval_utils.py +8 -8
  98. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/{mock_eval_utils.py → local_mock_eval_utils.py} +13 -9
  99. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service.py +6 -6
  100. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_callback.py +1 -1
  101. localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +4 -0
  102. localstack/services/stepfunctions/asl/component/test_state/state/base_mock.py +118 -0
  103. localstack/services/stepfunctions/asl/component/test_state/state/common.py +82 -0
  104. localstack/services/stepfunctions/asl/component/test_state/state/execution.py +139 -0
  105. localstack/services/stepfunctions/asl/component/test_state/state/map.py +77 -0
  106. localstack/services/stepfunctions/asl/component/test_state/state/task.py +44 -0
  107. localstack/services/stepfunctions/asl/eval/environment.py +30 -22
  108. localstack/services/stepfunctions/asl/eval/states.py +1 -1
  109. localstack/services/stepfunctions/asl/eval/test_state/environment.py +49 -9
  110. localstack/services/stepfunctions/asl/eval/test_state/program_state.py +22 -0
  111. localstack/services/stepfunctions/asl/jsonata/jsonata.py +5 -1
  112. localstack/services/stepfunctions/asl/parse/preprocessor.py +67 -24
  113. localstack/services/stepfunctions/asl/parse/test_state/asl_parser.py +5 -4
  114. localstack/services/stepfunctions/asl/parse/test_state/preprocessor.py +222 -31
  115. localstack/services/stepfunctions/asl/static_analyser/test_state/test_state_analyser.py +256 -22
  116. localstack/services/stepfunctions/backend/execution.py +10 -11
  117. localstack/services/stepfunctions/backend/execution_worker.py +5 -5
  118. localstack/services/stepfunctions/backend/test_state/execution.py +36 -0
  119. localstack/services/stepfunctions/backend/test_state/execution_worker.py +33 -1
  120. localstack/services/stepfunctions/backend/test_state/test_state_mock.py +127 -0
  121. localstack/services/stepfunctions/local_mocking/__init__.py +9 -0
  122. localstack/services/stepfunctions/{mocking → local_mocking}/mock_config.py +24 -17
  123. localstack/services/stepfunctions/provider.py +83 -25
  124. localstack/services/stepfunctions/test_state/mock_config.py +47 -0
  125. localstack/services/sts/provider.py +7 -0
  126. localstack/services/support/provider.py +5 -1
  127. localstack/services/swf/provider.py +5 -1
  128. localstack/services/transcribe/provider.py +7 -0
  129. localstack/testing/aws/lambda_utils.py +1 -1
  130. localstack/testing/aws/util.py +2 -1
  131. localstack/testing/config.py +1 -0
  132. localstack/testing/pytest/fixtures.py +28 -0
  133. localstack/testing/snapshots/transformer_utility.py +5 -0
  134. localstack/utils/analytics/publisher.py +37 -155
  135. localstack/utils/analytics/service_request_aggregator.py +6 -4
  136. localstack/utils/aws/arns.py +7 -0
  137. localstack/utils/aws/client_types.py +2 -4
  138. localstack/utils/batching.py +258 -0
  139. localstack/utils/bootstrap.py +2 -2
  140. localstack/utils/catalog/catalog.py +3 -2
  141. localstack/utils/collections.py +23 -11
  142. localstack/utils/container_utils/container_client.py +22 -13
  143. localstack/utils/container_utils/docker_cmd_client.py +6 -6
  144. localstack/version.py +2 -2
  145. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/METADATA +7 -7
  146. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/RECORD +155 -146
  147. localstack_core-4.12.1.dev18.dist-info/plux.json +1 -0
  148. localstack/services/stepfunctions/mocking/__init__.py +0 -0
  149. localstack/utils/batch_policy.py +0 -124
  150. localstack_core-4.10.1.dev42.dist-info/plux.json +0 -1
  151. /localstack/services/stepfunctions/{mocking → local_mocking}/mock_config_file.py +0 -0
  152. {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack +0 -0
  153. {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack-supervisor +0 -0
  154. {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack.bat +0 -0
  155. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/WHEEL +0 -0
  156. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/entry_points.txt +0 -0
  157. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/licenses/LICENSE.txt +0 -0
  158. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/top_level.txt +0 -0
@@ -27,6 +27,7 @@ from localstack.aws.api.lambda_ import (
27
27
  Arn,
28
28
  Blob,
29
29
  BlobStream,
30
+ CapacityProviderConfig,
30
31
  CodeSigningConfigArn,
31
32
  CodeSigningConfigNotFoundException,
32
33
  CodeSigningPolicies,
@@ -39,8 +40,10 @@ from localstack.aws.api.lambda_ import (
39
40
  CreateFunctionRequest,
40
41
  CreateFunctionUrlConfigResponse,
41
42
  DeleteCodeSigningConfigResponse,
43
+ DeleteFunctionResponse,
42
44
  Description,
43
45
  DestinationConfig,
46
+ DurableExecutionName,
44
47
  EventSourceMappingConfiguration,
45
48
  FunctionCodeLocation,
46
49
  FunctionConfiguration,
@@ -48,6 +51,7 @@ from localstack.aws.api.lambda_ import (
48
51
  FunctionName,
49
52
  FunctionUrlAuthType,
50
53
  FunctionUrlQualifier,
54
+ FunctionVersionLatestPublished,
51
55
  GetAccountSettingsResponse,
52
56
  GetCodeSigningConfigResponse,
53
57
  GetFunctionCodeSigningConfigResponse,
@@ -65,6 +69,7 @@ from localstack.aws.api.lambda_ import (
65
69
  InvokeAsyncResponse,
66
70
  InvokeMode,
67
71
  LambdaApi,
72
+ LambdaManagedInstancesCapacityProviderConfig,
68
73
  LastUpdateStatus,
69
74
  LayerName,
70
75
  LayerPermissionAllowedAction,
@@ -99,6 +104,7 @@ from localstack.aws.api.lambda_ import (
99
104
  MaxProvisionedConcurrencyConfigListItems,
100
105
  NamespacedFunctionName,
101
106
  NamespacedStatementId,
107
+ NumericLatestPublishedOrAliasQualifier,
102
108
  OnFailure,
103
109
  OnSuccess,
104
110
  OrganizationId,
@@ -130,6 +136,7 @@ from localstack.aws.api.lambda_ import (
130
136
  TaggableResource,
131
137
  TagKeyList,
132
138
  Tags,
139
+ TenantId,
133
140
  TracingMode,
134
141
  UnqualifiedFunctionName,
135
142
  UpdateCodeSigningConfigResponse,
@@ -137,7 +144,7 @@ from localstack.aws.api.lambda_ import (
137
144
  UpdateFunctionCodeRequest,
138
145
  UpdateFunctionConfigurationRequest,
139
146
  UpdateFunctionUrlConfigResponse,
140
- Version,
147
+ VersionWithLatestPublished,
141
148
  )
142
149
  from localstack.aws.api.lambda_ import FunctionVersion as FunctionVersionApi
143
150
  from localstack.aws.api.lambda_ import ServiceException as LambdaServiceException
@@ -151,6 +158,7 @@ from localstack.services.edge import ROUTER
151
158
  from localstack.services.lambda_ import api_utils
152
159
  from localstack.services.lambda_ import hooks as lambda_hooks
153
160
  from localstack.services.lambda_.analytics import (
161
+ FunctionInitializationType,
154
162
  FunctionOperation,
155
163
  FunctionStatus,
156
164
  function_counter,
@@ -209,6 +217,7 @@ from localstack.services.lambda_.invocation.lambda_service import (
209
217
  store_lambda_archive,
210
218
  store_s3_bucket_archive,
211
219
  )
220
+ from localstack.services.lambda_.invocation.models import CapacityProvider as CapacityProviderModel
212
221
  from localstack.services.lambda_.invocation.models import LambdaStore
213
222
  from localstack.services.lambda_.invocation.runtime_executor import get_runtime_executor
214
223
  from localstack.services.lambda_.lambda_utils import HINT_LOG
@@ -231,6 +240,7 @@ from localstack.services.plugins import ServiceLifecycleHook
231
240
  from localstack.state import StateVisitor
232
241
  from localstack.utils.aws.arns import (
233
242
  ArnData,
243
+ capacity_provider_arn,
234
244
  extract_resource_from_arn,
235
245
  extract_service_from_arn,
236
246
  get_partition,
@@ -239,7 +249,7 @@ from localstack.utils.aws.arns import (
239
249
  )
240
250
  from localstack.utils.aws.client_types import ServicePrincipal
241
251
  from localstack.utils.bootstrap import is_api_enabled
242
- from localstack.utils.collections import PaginatedList
252
+ from localstack.utils.collections import PaginatedList, merge_recursive
243
253
  from localstack.utils.event_matcher import validate_event_pattern
244
254
  from localstack.utils.strings import get_random_hex, short_uid, to_bytes, to_str
245
255
  from localstack.utils.sync import poll_condition
@@ -247,6 +257,7 @@ from localstack.utils.urls import localstack_host
247
257
 
248
258
  LOG = logging.getLogger(__name__)
249
259
 
260
+ CAPACITY_PROVIDER_ARN_NAME = "arn:aws[a-zA-Z-]*:lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:capacity-provider:[a-zA-Z0-9-_]+"
250
261
  LAMBDA_DEFAULT_TIMEOUT = 3
251
262
  LAMBDA_DEFAULT_MEMORY_SIZE = 128
252
263
 
@@ -296,19 +307,26 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
296
307
  for region_name, state in account_bundle.items():
297
308
  for fn in state.functions.values():
298
309
  for fn_version in fn.versions.values():
299
- # restore the "Pending" state for every function version and start it
300
310
  try:
301
- new_state = VersionState(
302
- state=State.Pending,
303
- code=StateReasonCode.Creating,
304
- reason="The function is being created.",
305
- )
306
- new_config = dataclasses.replace(fn_version.config, state=new_state)
307
- new_version = dataclasses.replace(fn_version, config=new_config)
308
- fn.versions[fn_version.id.qualifier] = new_version
309
- self.lambda_service.create_function_version(fn_version).result(
310
- timeout=5
311
+ # $LATEST is not invokable for Lambda functions with a capacity provider
312
+ # and has a different State (i.e., ActiveNonInvokable)
313
+ is_capacity_provider_latest = (
314
+ fn_version.config.CapacityProviderConfig
315
+ and fn_version.id.qualifier == "$LATEST"
311
316
  )
317
+ if not is_capacity_provider_latest:
318
+ # Restore the "Pending" state for the function version and start it
319
+ new_state = VersionState(
320
+ state=State.Pending,
321
+ code=StateReasonCode.Creating,
322
+ reason="The function is being created.",
323
+ )
324
+ new_config = dataclasses.replace(fn_version.config, state=new_state)
325
+ new_version = dataclasses.replace(fn_version, config=new_config)
326
+ fn.versions[fn_version.id.qualifier] = new_version
327
+ self.lambda_service.create_function_version(fn_version).result(
328
+ timeout=5
329
+ )
312
330
  except Exception:
313
331
  LOG.warning(
314
332
  "Failed to restore function version %s",
@@ -419,6 +437,23 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
419
437
  )
420
438
  return esm
421
439
 
440
+ @staticmethod
441
+ def _get_capacity_provider(
442
+ capacity_provider_name: str,
443
+ account_id: str,
444
+ region: str,
445
+ error_msg_template: str = "Capacity provider not found: {}",
446
+ ) -> CapacityProviderModel:
447
+ state = lambda_stores[account_id][region]
448
+ cp = state.capacity_providers.get(capacity_provider_name)
449
+ if not cp:
450
+ arn = capacity_provider_arn(capacity_provider_name, account_id, region)
451
+ raise ResourceNotFoundException(
452
+ error_msg_template.format(arn),
453
+ Type="User",
454
+ )
455
+ return cp
456
+
422
457
  @staticmethod
423
458
  def _validate_qualifier_expression(qualifier: str) -> None:
424
459
  if error_messages := api_utils.validate_qualifier(qualifier):
@@ -489,7 +524,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
489
524
  subnet_id = subnet_ids[0]
490
525
  if not bool(SUBNET_ID_REGEX.match(subnet_id)):
491
526
  raise ValidationException(
492
- f"1 validation error detected: Value '[{subnet_id}]' at 'vpcConfig.subnetIds' failed to satisfy constraint: Member must satisfy constraint: [Member must have length less than or equal to 1024, Member must have length greater than or equal to 0, Member must satisfy regular expression pattern: ^subnet-[0-9a-z]*$]"
527
+ f"1 validation error detected: Value '[{subnet_id}]' at 'vpcConfig.subnetIds' failed to satisfy constraint: Member must satisfy constraint: [Member must have length less than or equal to 1024, Member must have length greater than or equal to 0, Member must satisfy regular expression pattern: subnet-[0-9a-z]*]"
493
528
  )
494
529
 
495
530
  return VpcConfig(
@@ -506,6 +541,8 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
506
541
  description: str | None = None,
507
542
  revision_id: str | None = None,
508
543
  code_sha256: str | None = None,
544
+ publish_to: FunctionVersionLatestPublished | None = None,
545
+ is_active: bool = False,
509
546
  ) -> tuple[FunctionVersion, bool]:
510
547
  """
511
548
  Release a new version to the model if all restrictions are met.
@@ -568,38 +605,57 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
568
605
  ):
569
606
  return prev_version, False
570
607
  # TODO check if there was a change since last version
571
- next_version = str(function.next_version)
572
- function.next_version += 1
608
+ if publish_to == FunctionVersionLatestPublished.LATEST_PUBLISHED:
609
+ qualifier = "$LATEST.PUBLISHED"
610
+ else:
611
+ qualifier = str(function.next_version)
612
+ function.next_version += 1
573
613
  new_id = VersionIdentifier(
574
614
  function_name=function_name,
575
- qualifier=next_version,
615
+ qualifier=qualifier,
576
616
  region=region,
577
617
  account=account_id,
578
618
  )
579
- apply_on = current_latest_version.config.snap_start["ApplyOn"]
580
- optimization_status = SnapStartOptimizationStatus.Off
581
- if apply_on == SnapStartApplyOn.PublishedVersions:
582
- optimization_status = SnapStartOptimizationStatus.On
583
- snap_start = SnapStartResponse(
584
- ApplyOn=apply_on,
585
- OptimizationStatus=optimization_status,
619
+
620
+ if current_latest_version.config.CapacityProviderConfig:
621
+ # for lambda managed functions, snap start is not supported
622
+ snap_start = None
623
+ else:
624
+ apply_on = current_latest_version.config.snap_start["ApplyOn"]
625
+ optimization_status = SnapStartOptimizationStatus.Off
626
+ if apply_on == SnapStartApplyOn.PublishedVersions:
627
+ optimization_status = SnapStartOptimizationStatus.On
628
+ snap_start = SnapStartResponse(
629
+ ApplyOn=apply_on,
630
+ OptimizationStatus=optimization_status,
631
+ )
632
+
633
+ last_update = None
634
+ new_state = VersionState(
635
+ state=State.Pending,
636
+ code=StateReasonCode.Creating,
637
+ reason="The function is being created.",
586
638
  )
639
+ if publish_to == FunctionVersionLatestPublished.LATEST_PUBLISHED:
640
+ last_update = UpdateStatus(
641
+ status=LastUpdateStatus.InProgress,
642
+ code="Updating",
643
+ reason="The function is being updated.",
644
+ )
645
+ if is_active:
646
+ new_state = VersionState(state=State.Active)
587
647
  new_version = dataclasses.replace(
588
648
  current_latest_version,
589
649
  config=dataclasses.replace(
590
650
  current_latest_version.config,
591
- last_update=None, # versions never have a last update status
592
- state=VersionState(
593
- state=State.Pending,
594
- code=StateReasonCode.Creating,
595
- reason="The function is being created.",
596
- ),
651
+ last_update=last_update,
652
+ state=new_state,
597
653
  snap_start=snap_start,
598
654
  **changes,
599
655
  ),
600
656
  id=new_id,
601
657
  )
602
- function.versions[next_version] = new_version
658
+ function.versions[qualifier] = new_version
603
659
  return new_version, True
604
660
 
605
661
  def _publish_version_from_existing_version(
@@ -610,6 +666,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
610
666
  description: str | None = None,
611
667
  revision_id: str | None = None,
612
668
  code_sha256: str | None = None,
669
+ publish_to: FunctionVersionLatestPublished | None = None,
613
670
  ) -> FunctionVersion:
614
671
  """
615
672
  Publish version from an existing, already initialized LATEST
@@ -622,6 +679,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
622
679
  :param code_sha256: code sha (check if current code matches)
623
680
  :return: new version
624
681
  """
682
+ is_active = True if publish_to == FunctionVersionLatestPublished.LATEST_PUBLISHED else False
625
683
  new_version, changed = self._create_version_model(
626
684
  function_name=function_name,
627
685
  region=region,
@@ -629,18 +687,34 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
629
687
  description=description,
630
688
  revision_id=revision_id,
631
689
  code_sha256=code_sha256,
690
+ publish_to=publish_to,
691
+ is_active=is_active,
632
692
  )
633
693
  if not changed:
634
694
  return new_version
635
- self.lambda_service.publish_version(new_version)
695
+
696
+ if new_version.config.CapacityProviderConfig:
697
+ self.lambda_service.publish_version_async(new_version)
698
+ else:
699
+ self.lambda_service.publish_version(new_version)
636
700
  state = lambda_stores[account_id][region]
637
701
  function = state.functions.get(function_name)
702
+
703
+ # Update revision id for $LATEST version
638
704
  # TODO: re-evaluate data model to prevent this dirty hack just for bumping the revision id
639
705
  latest_version = function.versions["$LATEST"]
640
706
  function.versions["$LATEST"] = dataclasses.replace(
641
707
  latest_version, config=dataclasses.replace(latest_version.config)
642
708
  )
643
- return function.versions.get(new_version.id.qualifier)
709
+ if new_version.config.CapacityProviderConfig:
710
+ # publish_version happens async for functions with a capacity provider.
711
+ # Therefore, we return the new_version with State=Pending or LastUpdateStatus=InProgress ($LATEST.PUBLISHED)
712
+ return new_version
713
+ else:
714
+ # Regular functions yield an Active state modified during `publish_version` (sync).
715
+ # Therefore, we need to get the updated version from the store.
716
+ updated_version = function.versions.get(new_version.id.qualifier)
717
+ return updated_version
644
718
 
645
719
  def _publish_version_with_changes(
646
720
  self,
@@ -650,6 +724,8 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
650
724
  description: str | None = None,
651
725
  revision_id: str | None = None,
652
726
  code_sha256: str | None = None,
727
+ publish_to: FunctionVersionLatestPublished | None = None,
728
+ is_active: bool = False,
653
729
  ) -> FunctionVersion:
654
730
  """
655
731
  Publish version together with a new latest version (publish on create / update)
@@ -669,6 +745,8 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
669
745
  description=description,
670
746
  revision_id=revision_id,
671
747
  code_sha256=code_sha256,
748
+ publish_to=publish_to,
749
+ is_active=is_active,
672
750
  )
673
751
  if not changed:
674
752
  return new_version
@@ -720,7 +798,8 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
720
798
  if layer_version_str is None:
721
799
  raise ValidationException(
722
800
  f"1 validation error detected: Value '[{layer_version_arn}]'"
723
- + r" at 'layers' failed to satisfy constraint: Member must satisfy constraint: [Member must have length less than or equal to 140, Member must have length greater than or equal to 1, Member must satisfy regular expression pattern: (arn:[a-zA-Z0-9-]+:lambda:[a-z]{2}((-gov)|(-iso(b?)))?-[a-z]+-\d{1}:\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+)|(arn:[a-zA-Z0-9-]+:lambda:::awslayer:[a-zA-Z0-9-_]+), Member must not be null]",
801
+ + " at 'layers' failed to satisfy constraint: Member must satisfy constraint: [Member must have length less than or equal to 2048, Member must have length greater than or equal to 1, Member must satisfy regular expression pattern: "
802
+ + "(arn:(aws[a-zA-Z-]*)?:lambda:(eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1}:\\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+)|(arn:[a-zA-Z0-9-]+:lambda:::awslayer:[a-zA-Z0-9-_]+), Member must not be null]",
724
803
  )
725
804
 
726
805
  state = lambda_stores[layer_account_id][layer_region]
@@ -783,6 +862,30 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
783
862
  )
784
863
  visited_layers[layer_arn] = layer_version_arn
785
864
 
865
+ def _validate_capacity_provider_config(
866
+ self, capacity_provider_config: CapacityProviderConfig, context: RequestContext
867
+ ):
868
+ if not capacity_provider_config.get("LambdaManagedInstancesCapacityProviderConfig"):
869
+ raise ValidationException(
870
+ "1 validation error detected: Value null at 'capacityProviderConfig.lambdaManagedInstancesCapacityProviderConfig' failed to satisfy constraint: Member must not be null"
871
+ )
872
+
873
+ capacity_provider_arn = capacity_provider_config.get(
874
+ "LambdaManagedInstancesCapacityProviderConfig", {}
875
+ ).get("CapacityProviderArn")
876
+ if not capacity_provider_arn:
877
+ raise ValidationException(
878
+ "1 validation error detected: Value null at 'capacityProviderConfig.lambdaManagedInstancesCapacityProviderConfig.capacityProviderArn' failed to satisfy constraint: Member must not be null"
879
+ )
880
+
881
+ if not re.match(CAPACITY_PROVIDER_ARN_NAME, capacity_provider_arn):
882
+ raise ValidationException(
883
+ f"1 validation error detected: Value '{capacity_provider_arn}' at 'capacityProviderConfig.lambdaManagedInstancesCapacityProviderConfig.capacityProviderArn' failed to satisfy constraint: Member must satisfy regular expression pattern: {CAPACITY_PROVIDER_ARN_NAME}"
884
+ )
885
+
886
+ capacity_provider_name = capacity_provider_arn.split(":")[-1]
887
+ self.get_capacity_provider(context, capacity_provider_name)
888
+
786
889
  @staticmethod
787
890
  def map_layers(new_layers: list[str]) -> list[LayerVersion]:
788
891
  layers = []
@@ -941,11 +1044,13 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
941
1044
  account_id=context_account_id,
942
1045
  )
943
1046
  else:
944
- raise LambdaServiceException("Gotta have s3 bucket or zip file")
1047
+ raise LambdaServiceException("A ZIP file or S3 bucket is required")
945
1048
  elif package_type == PackageType.Image:
946
1049
  image = request_code.get("ImageUri")
947
1050
  if not image:
948
- raise LambdaServiceException("Gotta have an image when package type is image")
1051
+ raise LambdaServiceException(
1052
+ "An image is required when the package type is set to 'image'"
1053
+ )
949
1054
  image = create_image_code(image_uri=image)
950
1055
 
951
1056
  image_config_req = request.get("ImageConfig", {})
@@ -956,6 +1061,26 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
956
1061
  )
957
1062
  # Runtime management controls are not available when providing a custom image
958
1063
  runtime_version_config = None
1064
+
1065
+ capacity_provider_config = None
1066
+ memory_size = request.get("MemorySize", LAMBDA_DEFAULT_MEMORY_SIZE)
1067
+ if "CapacityProviderConfig" in request:
1068
+ capacity_provider_config = request["CapacityProviderConfig"]
1069
+ self._validate_capacity_provider_config(capacity_provider_config, context)
1070
+
1071
+ default_config = CapacityProviderConfig(
1072
+ LambdaManagedInstancesCapacityProviderConfig=LambdaManagedInstancesCapacityProviderConfig(
1073
+ ExecutionEnvironmentMemoryGiBPerVCpu=2.0,
1074
+ PerExecutionEnvironmentMaxConcurrency=16,
1075
+ )
1076
+ )
1077
+ capacity_provider_config = merge_recursive(default_config, capacity_provider_config)
1078
+ memory_size = 2048
1079
+ if request.get("LoggingConfig", {}).get("LogFormat") == LogFormat.Text:
1080
+ raise InvalidParameterValueException(
1081
+ 'LogLevel is not supported when LogFormat is set to "Text". Remove LogLevel from your request or change the LogFormat to "JSON" and try again.',
1082
+ Type="User",
1083
+ )
959
1084
  if "LoggingConfig" in request:
960
1085
  logging_config = request["LoggingConfig"]
961
1086
  LOG.warning(
@@ -979,11 +1104,25 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
979
1104
  | logging_config
980
1105
  )
981
1106
 
1107
+ elif capacity_provider_config:
1108
+ logging_config = LoggingConfig(
1109
+ LogFormat=LogFormat.JSON,
1110
+ LogGroup=f"/aws/lambda/{function_name}",
1111
+ ApplicationLogLevel="INFO",
1112
+ SystemLogLevel="INFO",
1113
+ )
982
1114
  else:
983
1115
  logging_config = LoggingConfig(
984
1116
  LogFormat=LogFormat.Text, LogGroup=f"/aws/lambda/{function_name}"
985
1117
  )
986
-
1118
+ snap_start = (
1119
+ None
1120
+ if capacity_provider_config
1121
+ else SnapStartResponse(
1122
+ ApplyOn=request.get("SnapStart", {}).get("ApplyOn", SnapStartApplyOn.None_),
1123
+ OptimizationStatus=SnapStartOptimizationStatus.Off,
1124
+ )
1125
+ )
987
1126
  version = FunctionVersion(
988
1127
  id=arn,
989
1128
  config=VersionFunctionConfiguration(
@@ -992,7 +1131,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
992
1131
  role=request["Role"],
993
1132
  timeout=request.get("Timeout", LAMBDA_DEFAULT_TIMEOUT),
994
1133
  runtime=request.get("Runtime"),
995
- memory_size=request.get("MemorySize", LAMBDA_DEFAULT_MEMORY_SIZE),
1134
+ memory_size=memory_size,
996
1135
  handler=request.get("Handler"),
997
1136
  package_type=package_type,
998
1137
  environment=env_vars,
@@ -1008,10 +1147,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1008
1147
  ephemeral_storage=LambdaEphemeralStorage(
1009
1148
  size=request.get("EphemeralStorage", {}).get("Size", 512)
1010
1149
  ),
1011
- snap_start=SnapStartResponse(
1012
- ApplyOn=request.get("SnapStart", {}).get("ApplyOn", SnapStartApplyOn.None_),
1013
- OptimizationStatus=SnapStartOptimizationStatus.Off,
1014
- ),
1150
+ snap_start=snap_start,
1015
1151
  runtime_version_config=runtime_version_config,
1016
1152
  dead_letter_arn=request.get("DeadLetterConfig", {}).get("TargetArn"),
1017
1153
  vpc_config=self._build_vpc_config(
@@ -1023,18 +1159,40 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1023
1159
  reason="The function is being created.",
1024
1160
  ),
1025
1161
  logging_config=logging_config,
1162
+ # TODO: might need something like **optional_kwargs if None
1163
+ # -> Test with regular GetFunction (i.e., without a capacity provider)
1164
+ CapacityProviderConfig=capacity_provider_config,
1026
1165
  ),
1027
1166
  )
1028
- fn.versions["$LATEST"] = version
1167
+ version_post_response = None
1168
+ if capacity_provider_config:
1169
+ version_post_response = dataclasses.replace(
1170
+ version,
1171
+ config=dataclasses.replace(
1172
+ version.config,
1173
+ last_update=UpdateStatus(status=LastUpdateStatus.Successful),
1174
+ state=VersionState(state=State.ActiveNonInvocable),
1175
+ ),
1176
+ )
1177
+ fn.versions["$LATEST"] = version_post_response or version
1029
1178
  state.functions[function_name] = fn
1179
+ initialization_type = (
1180
+ FunctionInitializationType.lambda_managed_instances
1181
+ if capacity_provider_config
1182
+ else FunctionInitializationType.on_demand
1183
+ )
1030
1184
  function_counter.labels(
1031
1185
  operation=FunctionOperation.create,
1032
1186
  runtime=runtime or "n/a",
1033
1187
  status=FunctionStatus.success,
1034
1188
  invocation_type="n/a",
1035
1189
  package_type=package_type,
1190
+ initialization_type=initialization_type,
1036
1191
  )
1037
- self.lambda_service.create_function_version(version)
1192
+ # TODO: consider potential other side effects of not having a function version for $LATEST
1193
+ # Provisioning happens upon publishing for functions using a capacity provider
1194
+ if not capacity_provider_config:
1195
+ self.lambda_service.create_function_version(version)
1038
1196
 
1039
1197
  if tags := request.get("Tags"):
1040
1198
  # This will check whether the function exists.
@@ -1042,7 +1200,10 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1042
1200
 
1043
1201
  if request.get("Publish"):
1044
1202
  version = self._publish_version_with_changes(
1045
- function_name=function_name, region=context_region, account_id=context_account_id
1203
+ function_name=function_name,
1204
+ region=context_region,
1205
+ account_id=context_account_id,
1206
+ publish_to=request.get("PublishTo"),
1046
1207
  )
1047
1208
 
1048
1209
  if config.LAMBDA_SYNCHRONOUS_CREATE:
@@ -1051,7 +1212,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1051
1212
  lambda: get_function_version(
1052
1213
  function_name, version.id.qualifier, version.id.account, version.id.region
1053
1214
  ).config.state.state
1054
- in [State.Active, State.Failed],
1215
+ in [State.Active, State.ActiveNonInvocable, State.Failed],
1055
1216
  timeout=10,
1056
1217
  ):
1057
1218
  LOG.warning(
@@ -1242,6 +1403,19 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1242
1403
  if new_mode:
1243
1404
  replace_kwargs["tracing_config_mode"] = new_mode
1244
1405
 
1406
+ if "CapacityProviderConfig" in request:
1407
+ if latest_version.config.CapacityProviderConfig and not request[
1408
+ "CapacityProviderConfig"
1409
+ ].get("LambdaManagedInstancesCapacityProviderConfig"):
1410
+ raise ValidationException(
1411
+ "1 validation error detected: Value null at 'capacityProviderConfig.lambdaManagedInstancesCapacityProviderConfig' failed to satisfy constraint: Member must not be null"
1412
+ )
1413
+ if not latest_version.config.CapacityProviderConfig:
1414
+ raise InvalidParameterValueException(
1415
+ "CapacityProviderConfig isn't supported for Lambda Default functions.",
1416
+ Type="User",
1417
+ )
1418
+
1245
1419
  new_latest_version = dataclasses.replace(
1246
1420
  latest_version,
1247
1421
  config=dataclasses.replace(
@@ -1327,7 +1501,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1327
1501
  code = None
1328
1502
  image = create_image_code(image_uri=image)
1329
1503
  else:
1330
- raise LambdaServiceException("Gotta have s3 bucket or zip file or image")
1504
+ raise LambdaServiceException("A ZIP file, S3 bucket, or image is required")
1331
1505
 
1332
1506
  old_function_version = function.versions.get("$LATEST")
1333
1507
  replace_kwargs = {"code": code} if code else {"image": image}
@@ -1365,7 +1539,12 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1365
1539
  self.lambda_service.update_version(new_version=function_version)
1366
1540
  if request.get("Publish"):
1367
1541
  function_version = self._publish_version_with_changes(
1368
- function_name=function_name, region=region, account_id=account_id
1542
+ function_name=function_name,
1543
+ region=region,
1544
+ account_id=account_id,
1545
+ # TODO: validations for PublishTo without Publish=True
1546
+ publish_to=request.get("PublishTo"),
1547
+ is_active=True,
1369
1548
  )
1370
1549
  return api_utils.map_config_out(
1371
1550
  function_version, return_qualified_arn=bool(request.get("Publish"))
@@ -1380,10 +1559,10 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1380
1559
  def delete_function(
1381
1560
  self,
1382
1561
  context: RequestContext,
1383
- function_name: FunctionName,
1384
- qualifier: Qualifier = None,
1562
+ function_name: NamespacedFunctionName,
1563
+ qualifier: NumericLatestPublishedOrAliasQualifier | None = None,
1385
1564
  **kwargs,
1386
- ) -> None:
1565
+ ) -> DeleteFunctionResponse:
1387
1566
  account_id, region = api_utils.get_account_and_region(function_name, context)
1388
1567
  function_name, qualifier = api_utils.get_name_and_qualifier(
1389
1568
  function_name, qualifier, context
@@ -1409,10 +1588,13 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1409
1588
  raise e
1410
1589
  function = store.functions.get(function_name)
1411
1590
 
1591
+ function_has_capacity_provider = False
1412
1592
  if qualifier:
1413
1593
  # delete a version of the function
1414
1594
  version = function.versions.pop(qualifier, None)
1415
1595
  if version:
1596
+ if version.config.CapacityProviderConfig:
1597
+ function_has_capacity_provider = True
1416
1598
  self.lambda_service.stop_version(version.id.qualified_arn())
1417
1599
  destroy_code_if_not_used(code=version.config.code, function=function)
1418
1600
  else:
@@ -1421,11 +1603,20 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1421
1603
  # the old version gets cleaned up in the internal lambda service.
1422
1604
  function = store.functions.pop(function_name)
1423
1605
  for version in function.versions.values():
1424
- self.lambda_service.stop_version(qualified_arn=version.id.qualified_arn())
1606
+ # Functions with a capacity provider do NOT have a version manager for $LATEST because only
1607
+ # published versions are invokable.
1608
+ if version.config.CapacityProviderConfig:
1609
+ function_has_capacity_provider = True
1610
+ if version.id.qualifier == "$LATEST":
1611
+ pass
1612
+ else:
1613
+ self.lambda_service.stop_version(qualified_arn=version.id.qualified_arn())
1425
1614
  # we can safely destroy the code here
1426
1615
  if version.config.code:
1427
1616
  version.config.code.destroy()
1428
1617
 
1618
+ return DeleteFunctionResponse(StatusCode=202 if function_has_capacity_provider else 204)
1619
+
1429
1620
  def list_functions(
1430
1621
  self,
1431
1622
  context: RequestContext,
@@ -1469,7 +1660,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1469
1660
  self,
1470
1661
  context: RequestContext,
1471
1662
  function_name: NamespacedFunctionName,
1472
- qualifier: Qualifier = None,
1663
+ qualifier: NumericLatestPublishedOrAliasQualifier | None = None,
1473
1664
  **kwargs,
1474
1665
  ) -> GetFunctionResponse:
1475
1666
  account_id, region = api_utils.get_account_and_region(function_name, context)
@@ -1540,7 +1731,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1540
1731
  self,
1541
1732
  context: RequestContext,
1542
1733
  function_name: NamespacedFunctionName,
1543
- qualifier: Qualifier = None,
1734
+ qualifier: NumericLatestPublishedOrAliasQualifier | None = None,
1544
1735
  **kwargs,
1545
1736
  ) -> FunctionConfiguration:
1546
1737
  account_id, region = api_utils.get_account_and_region(function_name, context)
@@ -1560,11 +1751,13 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1560
1751
  self,
1561
1752
  context: RequestContext,
1562
1753
  function_name: NamespacedFunctionName,
1563
- invocation_type: InvocationType = None,
1564
- log_type: LogType = None,
1565
- client_context: String = None,
1566
- payload: IO[Blob] = None,
1567
- qualifier: Qualifier = None,
1754
+ invocation_type: InvocationType | None = None,
1755
+ log_type: LogType | None = None,
1756
+ client_context: String | None = None,
1757
+ durable_execution_name: DurableExecutionName | None = None,
1758
+ payload: IO[Blob] | None = None,
1759
+ qualifier: NumericLatestPublishedOrAliasQualifier | None = None,
1760
+ tenant_id: TenantId | None = None,
1568
1761
  **kwargs,
1569
1762
  ) -> InvocationResponse:
1570
1763
  account_id, region = api_utils.get_account_and_region(function_name, context)
@@ -1634,9 +1827,10 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1634
1827
  self,
1635
1828
  context: RequestContext,
1636
1829
  function_name: FunctionName,
1637
- code_sha256: String = None,
1638
- description: Description = None,
1639
- revision_id: String = None,
1830
+ code_sha256: String | None = None,
1831
+ description: Description | None = None,
1832
+ revision_id: String | None = None,
1833
+ publish_to: FunctionVersionLatestPublished | None = None,
1640
1834
  **kwargs,
1641
1835
  ) -> FunctionConfiguration:
1642
1836
  account_id, region = api_utils.get_account_and_region(function_name, context)
@@ -1648,6 +1842,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1648
1842
  region=region,
1649
1843
  revision_id=revision_id,
1650
1844
  code_sha256=code_sha256,
1845
+ publish_to=publish_to,
1651
1846
  )
1652
1847
  return api_utils.map_config_out(new_version, return_qualified_arn=True)
1653
1848
 
@@ -1706,7 +1901,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1706
1901
  )
1707
1902
  if not api_utils.qualifier_is_version(key):
1708
1903
  raise ValidationException(
1709
- f"1 validation error detected: Value '{{{key}={value}}}' at 'routingConfig.additionalVersionWeights' failed to satisfy constraint: Map keys must satisfy constraint: [Member must have length less than or equal to 1024, Member must have length greater than or equal to 1, Member must satisfy regular expression pattern: [0-9]+, Member must not be null]"
1904
+ f"1 validation error detected: Value '{{{key}={value}}}' at 'routingConfig.additionalVersionWeights' failed to satisfy constraint: Map keys must satisfy constraint: [Member must have length less than or equal to 1024, Member must have length greater than or equal to 1, Member must satisfy regular expression pattern: [0-9]+]"
1710
1905
  )
1711
1906
 
1712
1907
  # checking if the version in the config exists
@@ -1723,7 +1918,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1723
1918
  context: RequestContext,
1724
1919
  function_name: FunctionName,
1725
1920
  name: Alias,
1726
- function_version: Version,
1921
+ function_version: VersionWithLatestPublished,
1727
1922
  description: Description = None,
1728
1923
  routing_config: AliasRoutingConfiguration = None,
1729
1924
  **kwargs,
@@ -1774,7 +1969,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1774
1969
  self,
1775
1970
  context: RequestContext,
1776
1971
  function_name: FunctionName,
1777
- function_version: Version = None,
1972
+ function_version: VersionWithLatestPublished = None,
1778
1973
  marker: String = None,
1779
1974
  max_items: MaxListItems = None,
1780
1975
  **kwargs,
@@ -1842,7 +2037,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1842
2037
  context: RequestContext,
1843
2038
  function_name: FunctionName,
1844
2039
  name: Alias,
1845
- function_version: Version = None,
2040
+ function_version: VersionWithLatestPublished = None,
1846
2041
  description: Description = None,
1847
2042
  routing_config: AliasRoutingConfiguration = None,
1848
2043
  revision_id: String = None,
@@ -2069,6 +2264,9 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
2069
2264
  raise Exception("unknown version") # TODO: cover via test
2070
2265
  elif qualifier == "$LATEST":
2071
2266
  pass
2267
+ elif qualifier == "$LATEST.PUBLISHED":
2268
+ if fn.versions.get(qualifier):
2269
+ pass
2072
2270
  else:
2073
2271
  raise Exception("invalid functionname") # TODO: cover via test
2074
2272
  fn_arn = api_utils.qualified_lambda_arn(function_name, qualifier, account, region)
@@ -2591,7 +2789,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
2591
2789
  if revision_id != fn_revision_id:
2592
2790
  raise PreconditionFailedException(
2593
2791
  "The Revision Id provided does not match the latest Revision Id. "
2594
- "Call the GetFunction/GetAlias API to retrieve the latest Revision Id",
2792
+ "Call the GetPolicy API to retrieve the latest Revision Id",
2595
2793
  Type="User",
2596
2794
  )
2597
2795
 
@@ -2649,10 +2847,10 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
2649
2847
  def remove_permission(
2650
2848
  self,
2651
2849
  context: RequestContext,
2652
- function_name: FunctionName,
2850
+ function_name: NamespacedFunctionName,
2653
2851
  statement_id: NamespacedStatementId,
2654
- qualifier: Qualifier = None,
2655
- revision_id: String = None,
2852
+ qualifier: NumericLatestPublishedOrAliasQualifier | None = None,
2853
+ revision_id: String | None = None,
2656
2854
  **kwargs,
2657
2855
  ) -> None:
2658
2856
  account_id, region = api_utils.get_account_and_region(function_name, context)
@@ -2716,7 +2914,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
2716
2914
  self,
2717
2915
  context: RequestContext,
2718
2916
  function_name: NamespacedFunctionName,
2719
- qualifier: Qualifier = None,
2917
+ qualifier: NumericLatestPublishedOrAliasQualifier | None = None,
2720
2918
  **kwargs,
2721
2919
  ) -> GetPolicyResponse:
2722
2920
  account_id, region = api_utils.get_account_and_region(function_name, context)
@@ -2758,9 +2956,9 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
2758
2956
  self,
2759
2957
  context: RequestContext,
2760
2958
  allowed_publishers: AllowedPublishers,
2761
- description: Description = None,
2762
- code_signing_policies: CodeSigningPolicies = None,
2763
- tags: Tags = None,
2959
+ description: Description | None = None,
2960
+ code_signing_policies: CodeSigningPolicies | None = None,
2961
+ tags: Tags | None = None,
2764
2962
  **kwargs,
2765
2963
  ) -> CreateCodeSigningConfigResponse:
2766
2964
  account = context.account_id
@@ -2785,7 +2983,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
2785
2983
  self,
2786
2984
  context: RequestContext,
2787
2985
  code_signing_config_arn: CodeSigningConfigArn,
2788
- function_name: FunctionName,
2986
+ function_name: NamespacedFunctionName,
2789
2987
  **kwargs,
2790
2988
  ) -> PutFunctionCodeSigningConfigResponse:
2791
2989
  account_id, region = api_utils.get_account_and_region(function_name, context)
@@ -2852,7 +3050,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
2852
3050
  return GetCodeSigningConfigResponse(CodeSigningConfig=api_utils.map_csc(csc))
2853
3051
 
2854
3052
  def get_function_code_signing_config(
2855
- self, context: RequestContext, function_name: FunctionName, **kwargs
3053
+ self, context: RequestContext, function_name: NamespacedFunctionName, **kwargs
2856
3054
  ) -> GetFunctionCodeSigningConfigResponse:
2857
3055
  account_id, region = api_utils.get_account_and_region(function_name, context)
2858
3056
  state = lambda_stores[account_id][region]
@@ -2870,7 +3068,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
2870
3068
  return GetFunctionCodeSigningConfigResponse()
2871
3069
 
2872
3070
  def delete_function_code_signing_config(
2873
- self, context: RequestContext, function_name: FunctionName, **kwargs
3071
+ self, context: RequestContext, function_name: NamespacedFunctionName, **kwargs
2874
3072
  ) -> None:
2875
3073
  account_id, region = api_utils.get_account_and_region(function_name, context)
2876
3074
  state = lambda_stores[account_id][region]
@@ -3280,7 +3478,8 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
3280
3478
  raise ValidationException(
3281
3479
  "1 validation error detected: Value '"
3282
3480
  + destination_arn
3283
- + r"' at 'destinationConfig.onFailure.destination' failed to satisfy constraint: Member must satisfy regular expression pattern: ^$|arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\-])+:([a-z]{2}((-gov)|(-iso(b?)))?-[a-z]+-\d{1})?:(\d{12})?:(.*)"
3481
+ + "' at 'destinationConfig.onFailure.destination' failed to satisfy constraint: Member must satisfy regular expression pattern: "
3482
+ + "$|kafka://([^.]([a-zA-Z0-9\\-_.]{0,248}))|arn:(aws[a-zA-Z0-9-]*):([a-zA-Z0-9\\-])+:((eusc-)?[a-z]{2}((-gov)|(-iso([a-z]?)))?-[a-z]+-\\d{1})?:(\\d{12})?:(.*)"
3284
3483
  )
3285
3484
 
3286
3485
  match destination_arn.split(":")[2]:
@@ -3330,7 +3529,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
3330
3529
  self,
3331
3530
  context: RequestContext,
3332
3531
  function_name: FunctionName,
3333
- qualifier: Qualifier = None,
3532
+ qualifier: NumericLatestPublishedOrAliasQualifier = None,
3334
3533
  maximum_retry_attempts: MaximumRetryAttempts = None,
3335
3534
  maximum_event_age_in_seconds: MaximumEventAgeInSeconds = None,
3336
3535
  destination_config: DestinationConfig = None,
@@ -3412,7 +3611,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
3412
3611
  self,
3413
3612
  context: RequestContext,
3414
3613
  function_name: FunctionName,
3415
- qualifier: Qualifier = None,
3614
+ qualifier: NumericLatestPublishedOrAliasQualifier | None = None,
3416
3615
  **kwargs,
3417
3616
  ) -> FunctionEventInvokeConfig:
3418
3617
  account_id, region = api_utils.get_account_and_region(function_name, context)
@@ -3489,7 +3688,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
3489
3688
  self,
3490
3689
  context: RequestContext,
3491
3690
  function_name: FunctionName,
3492
- qualifier: Qualifier = None,
3691
+ qualifier: NumericLatestPublishedOrAliasQualifier | None = None,
3493
3692
  **kwargs,
3494
3693
  ) -> None:
3495
3694
  account_id, region = api_utils.get_account_and_region(function_name, context)
@@ -3517,7 +3716,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
3517
3716
  self,
3518
3717
  context: RequestContext,
3519
3718
  function_name: FunctionName,
3520
- qualifier: Qualifier = None,
3719
+ qualifier: NumericLatestPublishedOrAliasQualifier = None,
3521
3720
  maximum_retry_attempts: MaximumRetryAttempts = None,
3522
3721
  maximum_event_age_in_seconds: MaximumEventAgeInSeconds = None,
3523
3722
  destination_config: DestinationConfig = None,
@@ -3608,10 +3807,10 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
3608
3807
  context: RequestContext,
3609
3808
  layer_name: LayerName,
3610
3809
  content: LayerVersionContentInput,
3611
- description: Description = None,
3612
- compatible_runtimes: CompatibleRuntimes = None,
3613
- license_info: LicenseInfo = None,
3614
- compatible_architectures: CompatibleArchitectures = None,
3810
+ description: Description | None = None,
3811
+ compatible_runtimes: CompatibleRuntimes | None = None,
3812
+ license_info: LicenseInfo | None = None,
3813
+ compatible_architectures: CompatibleArchitectures | None = None,
3615
3814
  **kwargs,
3616
3815
  ) -> PublishLayerVersionResponse:
3617
3816
  """
@@ -3749,10 +3948,10 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
3749
3948
  def list_layers(
3750
3949
  self,
3751
3950
  context: RequestContext,
3752
- compatible_runtime: Runtime = None,
3753
- marker: String = None,
3754
- max_items: MaxLayerListItems = None,
3755
- compatible_architecture: Architecture = None,
3951
+ compatible_runtime: Runtime | None = None,
3952
+ marker: String | None = None,
3953
+ max_items: MaxLayerListItems | None = None,
3954
+ compatible_architecture: Architecture | None = None,
3756
3955
  **kwargs,
3757
3956
  ) -> ListLayersResponse:
3758
3957
  validation_errors = []
@@ -3804,10 +4003,10 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
3804
4003
  self,
3805
4004
  context: RequestContext,
3806
4005
  layer_name: LayerName,
3807
- compatible_runtime: Runtime = None,
3808
- marker: String = None,
3809
- max_items: MaxLayerListItems = None,
3810
- compatible_architecture: Architecture = None,
4006
+ compatible_runtime: Runtime | None = None,
4007
+ marker: String | None = None,
4008
+ max_items: MaxLayerListItems | None = None,
4009
+ compatible_architecture: Architecture | None = None,
3811
4010
  **kwargs,
3812
4011
  ) -> ListLayerVersionsResponse:
3813
4012
  validation_errors = api_utils.validate_layer_runtimes_and_architectures(
@@ -4155,7 +4354,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
4155
4354
 
4156
4355
  def fetch_lambda_store_for_tagging(self, resource: TaggableResource) -> LambdaStore:
4157
4356
  """
4158
- Takes a resource ARN for a TaggableResource (Lambda Function, Event Source Mapping, or Code Signing Config) and returns a corresponding
4357
+ Takes a resource ARN for a TaggableResource (Lambda Function, Event Source Mapping, Code Signing Config, or Capacity Provider) and returns a corresponding
4159
4358
  LambdaStore for its region and account.
4160
4359
 
4161
4360
  In addition, this function validates that the ARN is a valid TaggableResource type, and that the TaggableResource exists.
@@ -4190,9 +4389,8 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
4190
4389
  _raise_validation_exception()
4191
4390
 
4192
4391
  resource_type, resource_identifier, *qualifier = parts
4193
- if resource_type not in {"event-source-mapping", "code-signing-config", "function"}:
4194
- _raise_validation_exception()
4195
4392
 
4393
+ # Qualifier validation raises before checking for NotFound
4196
4394
  if qualifier:
4197
4395
  if resource_type == "function":
4198
4396
  raise InvalidParameterValueException(
@@ -4201,15 +4399,18 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
4201
4399
  )
4202
4400
  _raise_validation_exception()
4203
4401
 
4204
- match resource_type:
4205
- case "event-source-mapping":
4206
- self._get_esm(resource_identifier, account_id, region)
4207
- case "code-signing-config":
4208
- raise NotImplementedError("Resource tagging on CSC not yet implemented.")
4209
- case "function":
4210
- self._get_function(
4211
- function_name=resource_identifier, account_id=account_id, region=region
4212
- )
4402
+ if resource_type == "event-source-mapping":
4403
+ self._get_esm(resource_identifier, account_id, region)
4404
+ elif resource_type == "code-signing-config":
4405
+ raise NotImplementedError("Resource tagging on CSC not yet implemented.")
4406
+ elif resource_type == "function":
4407
+ self._get_function(
4408
+ function_name=resource_identifier, account_id=account_id, region=region
4409
+ )
4410
+ elif resource_type == "capacity-provider":
4411
+ self._get_capacity_provider(resource_identifier, account_id, region)
4412
+ else:
4413
+ _raise_validation_exception()
4213
4414
 
4214
4415
  # If no exceptions are raised, assume ARN and referenced resource is valid for tag operations
4215
4416
  return lambda_stores[account_id][region]