localstack-core 4.11.2.dev14__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 (77) hide show
  1. localstack/aws/api/ec2/__init__.py +13 -0
  2. localstack/aws/api/iam/__init__.py +1 -0
  3. localstack/aws/api/lambda_/__init__.py +616 -0
  4. localstack/aws/api/logs/__init__.py +188 -0
  5. localstack/aws/api/opensearch/__init__.py +11 -0
  6. localstack/aws/api/route53/__init__.py +3 -0
  7. localstack/aws/api/s3/__init__.py +2 -0
  8. localstack/aws/api/s3control/__init__.py +19 -0
  9. localstack/aws/api/secretsmanager/__init__.py +9 -0
  10. localstack/aws/connect.py +35 -15
  11. localstack/config.py +8 -0
  12. localstack/constants.py +3 -0
  13. localstack/dev/kubernetes/__main__.py +39 -14
  14. localstack/runtime/analytics.py +11 -0
  15. localstack/services/acm/provider.py +13 -1
  16. localstack/services/cloudformation/engine/v2/change_set_model.py +9 -0
  17. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +3 -1
  18. localstack/services/cloudformation/engine/v2/change_set_resource_support_checker.py +114 -0
  19. localstack/services/cloudformation/provider.py +26 -1
  20. localstack/services/cloudformation/provider_utils.py +20 -0
  21. localstack/services/cloudformation/resource_provider.py +5 -4
  22. localstack/services/cloudformation/scaffolding/__main__.py +94 -22
  23. localstack/services/cloudformation/v2/provider.py +41 -0
  24. localstack/services/kinesis/packages.py +1 -1
  25. localstack/services/kms/models.py +6 -2
  26. localstack/services/lambda_/analytics.py +11 -2
  27. localstack/services/lambda_/invocation/event_manager.py +15 -11
  28. localstack/services/lambda_/invocation/lambda_models.py +4 -0
  29. localstack/services/lambda_/invocation/lambda_service.py +11 -0
  30. localstack/services/lambda_/provider.py +70 -13
  31. localstack/services/opensearch/packages.py +34 -20
  32. localstack/services/route53/provider.py +7 -0
  33. localstack/services/route53resolver/provider.py +5 -0
  34. localstack/services/s3/constants.py +5 -0
  35. localstack/services/s3/exceptions.py +9 -0
  36. localstack/services/s3/models.py +9 -1
  37. localstack/services/s3/provider.py +25 -30
  38. localstack/services/s3/utils.py +46 -1
  39. localstack/services/s3control/provider.py +6 -0
  40. localstack/services/scheduler/provider.py +4 -2
  41. localstack/services/secretsmanager/provider.py +4 -0
  42. localstack/services/ses/provider.py +4 -0
  43. localstack/services/sns/constants.py +13 -0
  44. localstack/services/sns/provider.py +5 -0
  45. localstack/services/sns/v2/models.py +3 -0
  46. localstack/services/sns/v2/provider.py +100 -0
  47. localstack/services/sqs/constants.py +6 -0
  48. localstack/services/sqs/provider.py +9 -1
  49. localstack/services/sqs/resource_providers/aws_sqs_queue.py +61 -46
  50. localstack/services/ssm/provider.py +6 -0
  51. localstack/services/stepfunctions/asl/static_analyser/test_state/test_state_analyser.py +193 -107
  52. localstack/services/stepfunctions/backend/execution.py +4 -5
  53. localstack/services/stepfunctions/provider.py +21 -14
  54. localstack/services/sts/provider.py +7 -0
  55. localstack/services/support/provider.py +5 -1
  56. localstack/services/swf/provider.py +5 -1
  57. localstack/services/transcribe/provider.py +7 -0
  58. localstack/testing/aws/lambda_utils.py +1 -1
  59. localstack/testing/aws/util.py +2 -1
  60. localstack/testing/config.py +1 -0
  61. localstack/utils/aws/client_types.py +2 -4
  62. localstack/utils/bootstrap.py +2 -2
  63. localstack/utils/catalog/catalog.py +3 -2
  64. localstack/utils/container_utils/container_client.py +22 -13
  65. localstack/utils/container_utils/docker_cmd_client.py +6 -6
  66. localstack/version.py +2 -2
  67. {localstack_core-4.11.2.dev14.dist-info → localstack_core-4.12.1.dev18.dist-info}/METADATA +6 -6
  68. {localstack_core-4.11.2.dev14.dist-info → localstack_core-4.12.1.dev18.dist-info}/RECORD +76 -75
  69. localstack_core-4.12.1.dev18.dist-info/plux.json +1 -0
  70. localstack_core-4.11.2.dev14.dist-info/plux.json +0 -1
  71. {localstack_core-4.11.2.dev14.data → localstack_core-4.12.1.dev18.data}/scripts/localstack +0 -0
  72. {localstack_core-4.11.2.dev14.data → localstack_core-4.12.1.dev18.data}/scripts/localstack-supervisor +0 -0
  73. {localstack_core-4.11.2.dev14.data → localstack_core-4.12.1.dev18.data}/scripts/localstack.bat +0 -0
  74. {localstack_core-4.11.2.dev14.dist-info → localstack_core-4.12.1.dev18.dist-info}/WHEEL +0 -0
  75. {localstack_core-4.11.2.dev14.dist-info → localstack_core-4.12.1.dev18.dist-info}/entry_points.txt +0 -0
  76. {localstack_core-4.11.2.dev14.dist-info → localstack_core-4.12.1.dev18.dist-info}/licenses/LICENSE.txt +0 -0
  77. {localstack_core-4.11.2.dev14.dist-info → localstack_core-4.12.1.dev18.dist-info}/top_level.txt +0 -0
@@ -103,6 +103,9 @@ from localstack.services.cloudformation.engine.v2.change_set_model_transform imp
103
103
  from localstack.services.cloudformation.engine.v2.change_set_model_validator import (
104
104
  ChangeSetModelValidator,
105
105
  )
106
+ from localstack.services.cloudformation.engine.v2.change_set_resource_support_checker import (
107
+ ChangeSetResourceSupportChecker,
108
+ )
106
109
  from localstack.services.cloudformation.engine.validations import ValidationError
107
110
  from localstack.services.cloudformation.provider import (
108
111
  ARN_CHANGESET_REGEX,
@@ -222,6 +225,12 @@ def find_stack_instance(stack_set: StackSet, account: str, region: str) -> Stack
222
225
 
223
226
  class CloudformationProviderV2(CloudformationProvider, ServiceLifecycleHook):
224
227
  def on_before_start(self):
228
+ # TODO: make sure to bring `_validate_config` from the base class when removing it
229
+ # as this ensures we have a valid CFN_NO_WAIT_ITERATIONS value
230
+ super().on_before_start()
231
+ self._log_create_issue_info()
232
+
233
+ def _log_create_issue_info(self):
225
234
  base = "https://github.com/localstack/localstack/issues/new"
226
235
  query_args = {
227
236
  "template": "bug-report.yml",
@@ -417,6 +426,38 @@ class CloudformationProviderV2(CloudformationProvider, ServiceLifecycleHook):
417
426
  update_model.node_template.change_type = ChangeType.MODIFIED
418
427
  change_set.processed_template = transformed_after_template
419
428
 
429
+ if not config.CFN_IGNORE_UNSUPPORTED_RESOURCE_TYPES:
430
+ support_visitor = ChangeSetResourceSupportChecker()
431
+ support_visitor.visit(change_set.update_model.node_template)
432
+ failure_messages = support_visitor.failure_messages
433
+ if failure_messages:
434
+ reason_suffix = ", ".join(failure_messages)
435
+ status_reason = f"{ChangeSetResourceSupportChecker.TITLE_MESSAGE} {reason_suffix}"
436
+
437
+ change_set.status_reason = status_reason
438
+ change_set.set_change_set_status(ChangeSetStatus.FAILED)
439
+ failure_transitions = {
440
+ ChangeSetType.CREATE: (
441
+ StackStatus.ROLLBACK_IN_PROGRESS,
442
+ StackStatus.CREATE_FAILED,
443
+ ),
444
+ ChangeSetType.UPDATE: (
445
+ StackStatus.UPDATE_ROLLBACK_IN_PROGRESS,
446
+ StackStatus.UPDATE_ROLLBACK_FAILED,
447
+ ),
448
+ ChangeSetType.IMPORT: (
449
+ StackStatus.IMPORT_ROLLBACK_IN_PROGRESS,
450
+ StackStatus.IMPORT_ROLLBACK_FAILED,
451
+ ),
452
+ }
453
+ transitions = failure_transitions.get(change_set.change_set_type)
454
+ if transitions:
455
+ first_status, *remaining_statuses = transitions
456
+ change_set.stack.set_stack_status(first_status, status_reason)
457
+ for status in remaining_statuses:
458
+ change_set.stack.set_stack_status(status)
459
+ return
460
+
420
461
  @handler("CreateChangeSet", expand=False)
421
462
  def create_change_set(
422
463
  self, context: RequestContext, request: CreateChangeSetInput
@@ -7,7 +7,7 @@ from localstack.packages import InstallTarget, Package
7
7
  from localstack.packages.core import GitHubReleaseInstaller, NodePackageInstaller
8
8
  from localstack.packages.java import JavaInstallerMixin, java_package
9
9
 
10
- _KINESIS_MOCK_VERSION = os.environ.get("KINESIS_MOCK_VERSION") or "0.5.1"
10
+ _KINESIS_MOCK_VERSION = os.environ.get("KINESIS_MOCK_VERSION") or "0.5.2"
11
11
 
12
12
 
13
13
  class KinesisMockEngine(StrEnum):
@@ -232,7 +232,10 @@ class KmsCryptoKey:
232
232
 
233
233
  if key_spec.startswith("RSA"):
234
234
  key_size = RSA_CRYPTO_KEY_LENGTHS.get(key_spec)
235
- key = rsa.generate_private_key(public_exponent=65537, key_size=key_size)
235
+ if key_material:
236
+ key = crypto_serialization.load_der_private_key(key_material, password=None)
237
+ else:
238
+ key = rsa.generate_private_key(public_exponent=65537, key_size=key_size)
236
239
  elif key_spec.startswith("ECC"):
237
240
  curve = ECC_CURVES.get(key_spec)
238
241
  if key_material:
@@ -636,7 +639,8 @@ class KmsKey:
636
639
  # https://docs.aws.amazon.com/kms/latest/APIReference/API_TagResource.html
637
640
  # "To edit a tag, specify an existing tag key and a new tag value."
638
641
  for i, tag in enumerate(tags, start=1):
639
- validate_tag(i, tag)
642
+ if tag.get("TagKey") != TAG_KEY_CUSTOM_KEY_MATERIAL:
643
+ validate_tag(i, tag)
640
644
  self.tags[tag.get("TagKey")] = tag.get("TagValue")
641
645
 
642
646
  def schedule_key_deletion(self, pending_window_in_days: int) -> None:
@@ -14,9 +14,10 @@ function_counter = LabeledCounter(
14
14
  "status",
15
15
  "runtime",
16
16
  "package_type",
17
- # only for operation "invoke"
18
- "invocation_type",
17
+ "invocation_type", # only for operation "invoke", otherwise "n/a"
18
+ "initialization_type",
19
19
  ],
20
+ schema_version=2,
20
21
  )
21
22
 
22
23
 
@@ -38,6 +39,14 @@ class FunctionStatus(StrEnum):
38
39
  invocation_error = "invocation_error"
39
40
 
40
41
 
42
+ class FunctionInitializationType(StrEnum):
43
+ # Maps to the Lambda environment variable AWS_LAMBDA_INITIALIZATION_TYPE
44
+ on_demand = "on-demand"
45
+ lambda_managed_instances = "lambda-managed-instances"
46
+ # Only applies to the operation "invoke" because provisioned concurrency is not configured on "create"
47
+ provisioned_concurrency = "provisioned-concurrency"
48
+
49
+
41
50
  esm_counter = LabeledCounter(namespace=NAMESPACE, name="esm", labels=["source", "status"])
42
51
 
43
52
 
@@ -13,6 +13,7 @@ from botocore.config import Config
13
13
  from localstack import config
14
14
  from localstack.aws.api.lambda_ import InvocationType, TooManyRequestsException
15
15
  from localstack.services.lambda_.analytics import (
16
+ FunctionInitializationType,
16
17
  FunctionOperation,
17
18
  FunctionStatus,
18
19
  function_counter,
@@ -198,22 +199,22 @@ class Poller:
198
199
  def handle_message(self, message: dict) -> None:
199
200
  failure_cause = None
200
201
  qualifier = self.version_manager.function_version.id.qualifier
202
+ function_config = self.version_manager.function_version.config
201
203
  event_invoke_config = self.version_manager.function.event_invoke_configs.get(qualifier)
202
204
  runtime = None
203
205
  status = None
206
+ # TODO: handle initialization_type provisioned-concurrency, which requires enriching invocation_result
207
+ initialization_type = (
208
+ FunctionInitializationType.lambda_managed_instances
209
+ if function_config.CapacityProviderConfig
210
+ else FunctionInitializationType.on_demand
211
+ )
204
212
  try:
205
213
  sqs_invocation = SQSInvocation.decode(message["Body"])
206
214
  invocation = sqs_invocation.invocation
207
215
  try:
208
216
  invocation_result = self.version_manager.invoke(invocation=invocation)
209
- function_config = self.version_manager.function_version.config
210
- function_counter.labels(
211
- operation=FunctionOperation.invoke,
212
- runtime=function_config.runtime or "n/a",
213
- status=FunctionStatus.success,
214
- invocation_type=InvocationType.Event,
215
- package_type=function_config.package_type,
216
- ).increment()
217
+ status = FunctionStatus.success
217
218
  except Exception as e:
218
219
  # Reserved concurrency == 0
219
220
  if self.version_manager.function.reserved_concurrent_executions == 0:
@@ -223,6 +224,7 @@ class Poller:
223
224
  elif not has_enough_time_for_retry(sqs_invocation, event_invoke_config):
224
225
  failure_cause = "EventAgeExceeded"
225
226
  status = FunctionStatus.event_age_exceeded_error
227
+
226
228
  if failure_cause:
227
229
  invocation_result = InvocationResult(
228
230
  is_error=True, request_id=invocation.request_id, payload=None, logs=None
@@ -240,14 +242,14 @@ class Poller:
240
242
  sqs_client.delete_message(
241
243
  QueueUrl=self.event_queue_url, ReceiptHandle=message["ReceiptHandle"]
242
244
  )
243
- # status MUST be set before returning
244
- package_type = self.version_manager.function_version.config.package_type
245
+ assert status, "status MUST be set before returning"
245
246
  function_counter.labels(
246
247
  operation=FunctionOperation.invoke,
247
248
  runtime=runtime or "n/a",
248
249
  status=status,
249
250
  invocation_type=InvocationType.Event,
250
- package_type=package_type,
251
+ package_type=function_config.package_type,
252
+ initialization_type=initialization_type,
251
253
  ).increment()
252
254
 
253
255
  # Good summary blogpost: https://haithai91.medium.com/aws-lambdas-retry-behaviors-edff90e1cf1b
@@ -257,6 +259,8 @@ class Poller:
257
259
  if event_invoke_config and event_invoke_config.maximum_retry_attempts is not None:
258
260
  max_retry_attempts = event_invoke_config.maximum_retry_attempts
259
261
 
262
+ assert invocation_result, "Invocation result MUST exist if we are not returning before"
263
+
260
264
  # An invocation error either leads to a terminal failure or to a scheduled retry
261
265
  if invocation_result.is_error: # invocation error
262
266
  failure_cause = None
@@ -30,6 +30,7 @@ from localstack.aws.api.lambda_ import (
30
30
  CodeSigningPolicies,
31
31
  Cors,
32
32
  DestinationConfig,
33
+ FunctionScalingConfig,
33
34
  FunctionUrlAuthType,
34
35
  InstanceRequirements,
35
36
  InvocationType,
@@ -625,6 +626,9 @@ class Function:
625
626
  provisioned_concurrency_configs: dict[str, ProvisionedConcurrencyConfiguration] = (
626
627
  dataclasses.field(default_factory=dict)
627
628
  )
629
+ function_scaling_configs: dict[str, FunctionScalingConfig] = dataclasses.field(
630
+ default_factory=dict
631
+ )
628
632
 
629
633
  lock: threading.RLock = dataclasses.field(default_factory=threading.RLock)
630
634
  next_version: int = 1
@@ -29,6 +29,7 @@ from localstack.aws.connect import connect_to
29
29
  from localstack.constants import AWS_REGION_US_EAST_1
30
30
  from localstack.services.lambda_ import hooks as lambda_hooks
31
31
  from localstack.services.lambda_.analytics import (
32
+ FunctionInitializationType,
32
33
  FunctionOperation,
33
34
  FunctionStatus,
34
35
  function_counter,
@@ -313,6 +314,12 @@ class LambdaService:
313
314
  raise ResourceNotFoundException(f"Function not found: {invoked_arn}", Type="User")
314
315
  runtime = version.config.runtime or "n/a"
315
316
  package_type = version.config.package_type
317
+ # Not considering provisioned concurrency for such early errors
318
+ initialization_type = (
319
+ FunctionInitializationType.lambda_managed_instances
320
+ if version.config.CapacityProviderConfig
321
+ else FunctionInitializationType.on_demand
322
+ )
316
323
  if version.config.CapacityProviderConfig and qualifier == "$LATEST":
317
324
  if function.versions.get("$LATEST.PUBLISHED"):
318
325
  raise InvalidParameterValueException(
@@ -355,6 +362,7 @@ class LambdaService:
355
362
  status=status,
356
363
  invocation_type=invocation_type,
357
364
  package_type=package_type,
365
+ initialization_type=initialization_type,
358
366
  ).increment()
359
367
  raise ResourceConflictException(
360
368
  f"The operation cannot be performed at this time. The function is currently in the following state: {state}"
@@ -373,6 +381,7 @@ class LambdaService:
373
381
  status=FunctionStatus.invalid_payload_error,
374
382
  invocation_type=invocation_type,
375
383
  package_type=package_type,
384
+ initialization_type=initialization_type,
376
385
  ).increment()
377
386
  # MAYBE: improve parity of detailed exception message (quite cumbersome)
378
387
  raise InvalidRequestContentException(
@@ -417,12 +426,14 @@ class LambdaService:
417
426
  if invocation_result.is_error
418
427
  else FunctionStatus.success
419
428
  )
429
+ # TODO: handle initialization_type provisioned-concurrency, requires enriching invocation_result
420
430
  function_counter.labels(
421
431
  operation=FunctionOperation.invoke,
422
432
  runtime=runtime,
423
433
  status=status,
424
434
  invocation_type=invocation_type,
425
435
  package_type=package_type,
436
+ initialization_type=initialization_type,
426
437
  ).increment()
427
438
  return invocation_result
428
439
 
@@ -43,6 +43,7 @@ from localstack.aws.api.lambda_ import (
43
43
  DeleteFunctionResponse,
44
44
  Description,
45
45
  DestinationConfig,
46
+ DurableExecutionName,
46
47
  EventSourceMappingConfiguration,
47
48
  FunctionCodeLocation,
48
49
  FunctionConfiguration,
@@ -157,6 +158,7 @@ from localstack.services.edge import ROUTER
157
158
  from localstack.services.lambda_ import api_utils
158
159
  from localstack.services.lambda_ import hooks as lambda_hooks
159
160
  from localstack.services.lambda_.analytics import (
161
+ FunctionInitializationType,
160
162
  FunctionOperation,
161
163
  FunctionStatus,
162
164
  function_counter,
@@ -255,6 +257,7 @@ from localstack.utils.urls import localstack_host
255
257
 
256
258
  LOG = logging.getLogger(__name__)
257
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-_]+"
258
261
  LAMBDA_DEFAULT_TIMEOUT = 3
259
262
  LAMBDA_DEFAULT_MEMORY_SIZE = 128
260
263
 
@@ -304,20 +307,26 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
304
307
  for region_name, state in account_bundle.items():
305
308
  for fn in state.functions.values():
306
309
  for fn_version in fn.versions.values():
307
- # restore the "Pending" state for every function version and start it
308
310
  try:
309
- new_state = VersionState(
310
- state=State.Pending,
311
- code=StateReasonCode.Creating,
312
- reason="The function is being created.",
313
- )
314
- new_config = dataclasses.replace(fn_version.config, state=new_state)
315
- new_version = dataclasses.replace(fn_version, config=new_config)
316
- fn.versions[fn_version.id.qualifier] = new_version
317
- # TODO: consider skipping this for $LATEST versions of functions with a capacity provider
318
- self.lambda_service.create_function_version(fn_version).result(
319
- 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"
320
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
+ )
321
330
  except Exception:
322
331
  LOG.warning(
323
332
  "Failed to restore function version %s",
@@ -853,6 +862,30 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
853
862
  )
854
863
  visited_layers[layer_arn] = layer_version_arn
855
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
+
856
889
  @staticmethod
857
890
  def map_layers(new_layers: list[str]) -> list[LayerVersion]:
858
891
  layers = []
@@ -1029,11 +1062,12 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1029
1062
  # Runtime management controls are not available when providing a custom image
1030
1063
  runtime_version_config = None
1031
1064
 
1032
- # TODO: validations and figure out in which order
1033
1065
  capacity_provider_config = None
1034
1066
  memory_size = request.get("MemorySize", LAMBDA_DEFAULT_MEMORY_SIZE)
1035
1067
  if "CapacityProviderConfig" in request:
1036
1068
  capacity_provider_config = request["CapacityProviderConfig"]
1069
+ self._validate_capacity_provider_config(capacity_provider_config, context)
1070
+
1037
1071
  default_config = CapacityProviderConfig(
1038
1072
  LambdaManagedInstancesCapacityProviderConfig=LambdaManagedInstancesCapacityProviderConfig(
1039
1073
  ExecutionEnvironmentMemoryGiBPerVCpu=2.0,
@@ -1142,12 +1176,18 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1142
1176
  )
1143
1177
  fn.versions["$LATEST"] = version_post_response or version
1144
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
+ )
1145
1184
  function_counter.labels(
1146
1185
  operation=FunctionOperation.create,
1147
1186
  runtime=runtime or "n/a",
1148
1187
  status=FunctionStatus.success,
1149
1188
  invocation_type="n/a",
1150
1189
  package_type=package_type,
1190
+ initialization_type=initialization_type,
1151
1191
  )
1152
1192
  # TODO: consider potential other side effects of not having a function version for $LATEST
1153
1193
  # Provisioning happens upon publishing for functions using a capacity provider
@@ -1363,6 +1403,19 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1363
1403
  if new_mode:
1364
1404
  replace_kwargs["tracing_config_mode"] = new_mode
1365
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
+
1366
1419
  new_latest_version = dataclasses.replace(
1367
1420
  latest_version,
1368
1421
  config=dataclasses.replace(
@@ -1701,6 +1754,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
1701
1754
  invocation_type: InvocationType | None = None,
1702
1755
  log_type: LogType | None = None,
1703
1756
  client_context: String | None = None,
1757
+ durable_execution_name: DurableExecutionName | None = None,
1704
1758
  payload: IO[Blob] | None = None,
1705
1759
  qualifier: NumericLatestPublishedOrAliasQualifier | None = None,
1706
1760
  tenant_id: TenantId | None = None,
@@ -2210,6 +2264,9 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
2210
2264
  raise Exception("unknown version") # TODO: cover via test
2211
2265
  elif qualifier == "$LATEST":
2212
2266
  pass
2267
+ elif qualifier == "$LATEST.PUBLISHED":
2268
+ if fn.versions.get(qualifier):
2269
+ pass
2213
2270
  else:
2214
2271
  raise Exception("invalid functionname") # TODO: cover via test
2215
2272
  fn_arn = api_utils.qualified_lambda_arn(function_name, qualifier, account, region)
@@ -107,19 +107,31 @@ class OpensearchPackageInstaller(PackageInstaller):
107
107
  # setup security based on the version
108
108
  self._setup_security(install_dir, parsed_version)
109
109
 
110
+ # Determine network configuration to use for plugin downloads
111
+ sys_props = {
112
+ **java_system_properties_proxy(),
113
+ **java_system_properties_ssl(
114
+ os.path.join(install_dir, "jdk", "bin", "keytool"),
115
+ {"JAVA_HOME": os.path.join(install_dir, "jdk")},
116
+ ),
117
+ }
118
+ java_opts = system_properties_to_cli_args(sys_props)
119
+
120
+ keystore_binary = os.path.join(install_dir, "bin", "opensearch-keystore")
121
+ if os.path.exists(keystore_binary):
122
+ # initialize and create the keystore. Concurrent starts of ES will all try to create it at the same
123
+ # time, and fail with a race condition. Creating once when installing solves the issue without
124
+ # the need to lock the starts
125
+ # Ultimately, each cluster should have its own `config` file and maybe not share the same one
126
+ output = run(
127
+ [keystore_binary, "create"],
128
+ env_vars={"OPENSEARCH_JAVA_OPTS": " ".join(java_opts)},
129
+ )
130
+ LOG.debug("Keystore init output: %s", output)
131
+
110
132
  # install other default plugins for opensearch 1.1+
111
133
  # https://forum.opensearch.org/t/ingest-attachment-cannot-be-installed/6494/12
112
134
  if parsed_version >= "1.1.0":
113
- # Determine network configuration to use for plugin downloads
114
- sys_props = {
115
- **java_system_properties_proxy(),
116
- **java_system_properties_ssl(
117
- os.path.join(install_dir, "jdk", "bin", "keytool"),
118
- {"JAVA_HOME": os.path.join(install_dir, "jdk")},
119
- ),
120
- }
121
- java_opts = system_properties_to_cli_args(sys_props)
122
-
123
135
  for plugin in OPENSEARCH_PLUGIN_LIST:
124
136
  plugin_binary = os.path.join(install_dir, "bin", "opensearch-plugin")
125
137
  plugin_dir = os.path.join(install_dir, "plugins", plugin)
@@ -322,6 +334,18 @@ class ElasticsearchPackageInstaller(PackageInstaller):
322
334
  if not os.environ.get("IGNORE_ES_DOWNLOAD_ERRORS"):
323
335
  raise
324
336
 
337
+ keystore_binary = os.path.join(install_dir, "bin", "elasticsearch-keystore")
338
+ if os.path.exists(keystore_binary):
339
+ # initialize and create the keystore. Concurrent starts of ES will all try to create it at the same
340
+ # time, and fail with a race condition. Creating once when installing solves the issue without
341
+ # the need to lock the starts
342
+ # Ultimately, each cluster should have its own `config` file and maybe not share the same one
343
+ output = run(
344
+ [keystore_binary, "create"],
345
+ env_vars={"ES_JAVA_OPTS": " ".join(java_opts)},
346
+ )
347
+ LOG.debug("Keystore init output: %s", output)
348
+
325
349
  # delete some plugins to free up space
326
350
  for plugin in ELASTICSEARCH_DELETE_MODULES:
327
351
  module_dir = os.path.join(install_dir, "modules", plugin)
@@ -341,16 +365,6 @@ class ElasticsearchPackageInstaller(PackageInstaller):
341
365
  if jvm_options != jvm_options_replaced:
342
366
  save_file(jvm_options_file, jvm_options_replaced)
343
367
 
344
- # patch JVM options file - replace hardcoded heap size settings
345
- jvm_options_file = os.path.join(install_dir, "config", "jvm.options")
346
- if os.path.exists(jvm_options_file):
347
- jvm_options = load_file(jvm_options_file)
348
- jvm_options_replaced = re.sub(
349
- r"(^-Xm[sx][a-zA-Z0-9.]+$)", r"# \1", jvm_options, flags=re.MULTILINE
350
- )
351
- if jvm_options != jvm_options_replaced:
352
- save_file(jvm_options_file, jvm_options_replaced)
353
-
354
368
  def _get_install_marker_path(self, install_dir: str) -> str:
355
369
  return os.path.join(install_dir, "bin", "elasticsearch")
356
370
 
@@ -26,9 +26,16 @@ from localstack.aws.api.route53 import (
26
26
  from localstack.aws.connect import connect_to
27
27
  from localstack.services.moto import call_moto
28
28
  from localstack.services.plugins import ServiceLifecycleHook
29
+ from localstack.state import StateVisitor
29
30
 
30
31
 
31
32
  class Route53Provider(Route53Api, ServiceLifecycleHook):
33
+ def accept_state_visitor(self, visitor: StateVisitor):
34
+ from localstack.services.route53.models import route53_stores
35
+
36
+ visitor.visit(route53_backends)
37
+ visitor.visit(route53_stores)
38
+
32
39
  def create_hosted_zone(
33
40
  self,
34
41
  context: RequestContext,
@@ -100,6 +100,7 @@ from localstack.services.route53resolver.utils import (
100
100
  validate_mutation_protection,
101
101
  validate_priority,
102
102
  )
103
+ from localstack.state import StateVisitor
103
104
  from localstack.utils.aws import arns
104
105
  from localstack.utils.aws.arns import extract_account_id_from_arn, extract_region_from_arn
105
106
  from localstack.utils.collections import select_from_typed_dict
@@ -107,6 +108,10 @@ from localstack.utils.patch import patch
107
108
 
108
109
 
109
110
  class Route53ResolverProvider(Route53ResolverApi):
111
+ def accept_state_visitor(self, visitor: StateVisitor):
112
+ visitor.visit(route53resolver_backends)
113
+ visitor.visit(route53resolver_stores)
114
+
110
115
  @staticmethod
111
116
  def get_store(account_id: str, region: str) -> Route53ResolverStore:
112
117
  return route53resolver_stores[account_id][region]
@@ -1,4 +1,5 @@
1
1
  from localstack.aws.api.s3 import (
2
+ BucketLocationConstraint,
2
3
  ChecksumAlgorithm,
3
4
  Grantee,
4
5
  Permission,
@@ -66,6 +67,10 @@ CHECKSUM_ALGORITHMS: list[ChecksumAlgorithm] = [
66
67
  ChecksumAlgorithm.CRC64NVME,
67
68
  ]
68
69
 
70
+ BUCKET_LOCATION_CONSTRAINTS = [constraint.value for constraint in BucketLocationConstraint]
71
+
72
+ EU_WEST_1_LOCATION_CONSTRAINTS = [BucketLocationConstraint.EU, BucketLocationConstraint.eu_west_1]
73
+
69
74
  # response header overrides the client may request
70
75
  ALLOWED_HEADER_OVERRIDES = {
71
76
  "ResponseContentType": "ContentType",
@@ -51,3 +51,12 @@ class InvalidBucketOwnerAWSAccountID(CommonServiceException):
51
51
  class TooManyConfigurations(CommonServiceException):
52
52
  def __init__(self, message=None) -> None:
53
53
  super().__init__("TooManyConfigurations", status_code=400, message=message)
54
+
55
+
56
+ class IllegalLocationConstraintException(CommonServiceException):
57
+ def __init__(self, location_constraint: str):
58
+ super().__init__(
59
+ "IllegalLocationConstraintException",
60
+ status_code=400,
61
+ message=f"The {location_constraint} location constraint is incompatible for the region specific endpoint this request was sent to.",
62
+ )
@@ -16,6 +16,7 @@ from localstack.aws.api.s3 import (
16
16
  BadDigest,
17
17
  BucketAccelerateStatus,
18
18
  BucketKeyEnabled,
19
+ BucketLocationConstraint,
19
20
  BucketName,
20
21
  BucketRegion,
21
22
  BucketVersioningStatus,
@@ -76,7 +77,11 @@ from localstack.services.s3.constants import (
76
77
  S3_UPLOAD_PART_MIN_SIZE,
77
78
  )
78
79
  from localstack.services.s3.exceptions import InvalidRequest
79
- from localstack.services.s3.utils import CombinedCrcHash, get_s3_checksum, rfc_1123_datetime
80
+ from localstack.services.s3.utils import (
81
+ CombinedCrcHash,
82
+ get_s3_checksum,
83
+ rfc_1123_datetime,
84
+ )
80
85
  from localstack.services.stores import (
81
86
  AccountRegionBundle,
82
87
  BaseStore,
@@ -101,6 +106,7 @@ class S3Bucket:
101
106
  name: BucketName
102
107
  bucket_account_id: AccountId
103
108
  bucket_region: BucketRegion
109
+ location_constraint: BucketLocationConstraint | Literal[""]
104
110
  creation_date: datetime
105
111
  multiparts: dict[MultipartUploadId, "S3Multipart"]
106
112
  objects: Union["KeyStore", "VersionedKeyStore"]
@@ -137,10 +143,12 @@ class S3Bucket:
137
143
  acl: AccessControlPolicy = None,
138
144
  object_ownership: ObjectOwnership = None,
139
145
  object_lock_enabled_for_bucket: bool = None,
146
+ location_constraint: BucketLocationConstraint | Literal[""] = "",
140
147
  ):
141
148
  self.name = name
142
149
  self.bucket_account_id = account_id
143
150
  self.bucket_region = bucket_region
151
+ self.location_constraint = location_constraint
144
152
  # If ObjectLock is enabled, it forces the bucket to be versioned as well
145
153
  self.versioning_status = None if not object_lock_enabled_for_bucket else "Enabled"
146
154
  self.objects = KeyStore() if not object_lock_enabled_for_bucket else VersionedKeyStore()