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.
- localstack/aws/api/apigateway/__init__.py +42 -0
- localstack/aws/api/cloudformation/__init__.py +161 -0
- localstack/aws/api/ec2/__init__.py +1178 -12
- localstack/aws/api/iam/__init__.py +228 -0
- localstack/aws/api/kms/__init__.py +1 -0
- localstack/aws/api/lambda_/__init__.py +1034 -66
- localstack/aws/api/logs/__init__.py +500 -0
- localstack/aws/api/opensearch/__init__.py +100 -0
- localstack/aws/api/redshift/__init__.py +69 -0
- localstack/aws/api/resourcegroupstaggingapi/__init__.py +36 -0
- localstack/aws/api/route53/__init__.py +45 -0
- localstack/aws/api/route53resolver/__init__.py +1 -0
- localstack/aws/api/s3/__init__.py +64 -0
- localstack/aws/api/s3control/__init__.py +19 -0
- localstack/aws/api/secretsmanager/__init__.py +37 -23
- localstack/aws/api/stepfunctions/__init__.py +52 -10
- localstack/aws/api/sts/__init__.py +52 -0
- localstack/aws/connect.py +35 -15
- localstack/aws/handlers/logging.py +8 -4
- localstack/aws/handlers/service.py +11 -2
- localstack/aws/protocol/serializer.py +1 -1
- localstack/config.py +8 -0
- localstack/constants.py +3 -0
- localstack/deprecations.py +0 -6
- localstack/dev/kubernetes/__main__.py +39 -14
- localstack/runtime/analytics.py +11 -0
- localstack/services/acm/provider.py +17 -1
- 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.py +9 -0
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +15 -1
- localstack/services/cloudformation/engine/v2/change_set_resource_support_checker.py +114 -0
- localstack/services/cloudformation/provider.py +26 -1
- localstack/services/cloudformation/provider_utils.py +20 -0
- localstack/services/cloudformation/resource_provider.py +5 -4
- localstack/services/cloudformation/scaffolding/__main__.py +94 -22
- localstack/services/cloudformation/v2/provider.py +41 -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/kinesis/packages.py +1 -1
- localstack/services/kms/models.py +16 -22
- localstack/services/kms/provider.py +4 -0
- localstack/services/lambda_/analytics.py +11 -2
- 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/event_manager.py +15 -11
- localstack/services/lambda_/invocation/execution_environment.py +21 -2
- localstack/services/lambda_/invocation/lambda_models.py +31 -2
- localstack/services/lambda_/invocation/lambda_service.py +62 -3
- localstack/services/lambda_/invocation/models.py +9 -1
- localstack/services/lambda_/invocation/version_manager.py +18 -3
- localstack/services/lambda_/provider.py +307 -106
- 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/packages.py +34 -20
- localstack/services/opensearch/provider.py +53 -3
- localstack/services/resource_groups/provider.py +5 -1
- localstack/services/resourcegroupstaggingapi/provider.py +6 -1
- localstack/services/route53/provider.py +7 -0
- localstack/services/route53resolver/provider.py +5 -0
- localstack/services/s3/constants.py +5 -0
- localstack/services/s3/exceptions.py +9 -0
- localstack/services/s3/models.py +9 -1
- localstack/services/s3/provider.py +51 -43
- localstack/services/s3/utils.py +81 -15
- localstack/services/s3control/provider.py +107 -2
- localstack/services/s3control/validation.py +50 -0
- localstack/services/scheduler/provider.py +4 -2
- localstack/services/secretsmanager/provider.py +4 -0
- localstack/services/ses/provider.py +4 -0
- localstack/services/sns/constants.py +16 -1
- localstack/services/sns/provider.py +5 -0
- localstack/services/sns/publisher.py +15 -6
- localstack/services/sns/v2/models.py +9 -0
- localstack/services/sns/v2/provider.py +750 -19
- localstack/services/sns/v2/utils.py +12 -0
- localstack/services/sqs/constants.py +6 -0
- localstack/services/sqs/provider.py +9 -1
- localstack/services/sqs/resource_providers/aws_sqs_queue.py +61 -46
- localstack/services/ssm/provider.py +6 -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 +256 -22
- localstack/services/stepfunctions/backend/execution.py +10 -11
- 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 +83 -25
- localstack/services/stepfunctions/test_state/mock_config.py +47 -0
- localstack/services/sts/provider.py +7 -0
- localstack/services/support/provider.py +5 -1
- localstack/services/swf/provider.py +5 -1
- localstack/services/transcribe/provider.py +7 -0
- localstack/testing/aws/lambda_utils.py +1 -1
- localstack/testing/aws/util.py +2 -1
- localstack/testing/config.py +1 -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/aws/client_types.py +2 -4
- localstack/utils/batching.py +258 -0
- localstack/utils/bootstrap.py +2 -2
- localstack/utils/catalog/catalog.py +3 -2
- localstack/utils/collections.py +23 -11
- localstack/utils/container_utils/container_client.py +22 -13
- localstack/utils/container_utils/docker_cmd_client.py +6 -6
- localstack/version.py +2 -2
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/METADATA +7 -7
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/RECORD +155 -146
- localstack_core-4.12.1.dev18.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.12.1.dev18.data}/scripts/localstack +0 -0
- {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/WHEEL +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/top_level.txt +0 -0
|
@@ -19,11 +19,24 @@ from localstack.utils.strings import is_base64, to_bytes
|
|
|
19
19
|
from localstack.utils.testutil import create_zip_file
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
class LambdaManagedInstancesCapacityProviderConfig(TypedDict):
|
|
23
|
+
CapacityProviderArn: str | None
|
|
24
|
+
PerExecutionEnvironmentMaxConcurrency: int | None
|
|
25
|
+
ExecutionEnvironmentMemoryGiBPerVCpu: float | None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CapacityProviderConfig(TypedDict):
|
|
29
|
+
LambdaManagedInstancesCapacityProviderConfig: (
|
|
30
|
+
LambdaManagedInstancesCapacityProviderConfig | None
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
22
34
|
class LambdaFunctionProperties(TypedDict):
|
|
23
35
|
Code: Code | None
|
|
24
36
|
Role: str | None
|
|
25
37
|
Architectures: list[str] | None
|
|
26
38
|
Arn: str | None
|
|
39
|
+
CapacityProviderConfig: CapacityProviderConfig | None
|
|
27
40
|
CodeSigningConfigArn: str | None
|
|
28
41
|
DeadLetterConfig: DeadLetterConfig | None
|
|
29
42
|
Description: str | None
|
|
@@ -297,6 +310,7 @@ def _transform_function_to_model(function):
|
|
|
297
310
|
"Arn",
|
|
298
311
|
"EphemeralStorage",
|
|
299
312
|
"Architectures",
|
|
313
|
+
"CapacityProviderConfig",
|
|
300
314
|
]
|
|
301
315
|
response_model = util.select_attributes(function, model_properties)
|
|
302
316
|
response_model["Arn"] = function["FunctionArn"]
|
|
@@ -387,6 +401,7 @@ class LambdaFunctionProvider(ResourceProvider[LambdaFunctionProperties]):
|
|
|
387
401
|
"TracingConfig",
|
|
388
402
|
"VpcConfig",
|
|
389
403
|
"LoggingConfig",
|
|
404
|
+
"CapacityProviderConfig",
|
|
390
405
|
],
|
|
391
406
|
)
|
|
392
407
|
if "Timeout" in kwargs:
|
|
@@ -408,11 +423,27 @@ class LambdaFunctionProvider(ResourceProvider[LambdaFunctionProperties]):
|
|
|
408
423
|
}
|
|
409
424
|
|
|
410
425
|
kwargs["Code"] = _get_lambda_code_param(model)
|
|
426
|
+
|
|
427
|
+
# For managed instance lambdas, we publish them immediately
|
|
428
|
+
if "CapacityProviderConfig" in kwargs:
|
|
429
|
+
kwargs["Publish"] = True
|
|
430
|
+
kwargs["PublishTo"] = "LATEST_PUBLISHED"
|
|
431
|
+
|
|
411
432
|
create_response = lambda_client.create_function(**kwargs)
|
|
433
|
+
# TODO: if version is in the schema, just put it in the model instead of the custom context
|
|
434
|
+
request.custom_context["Version"] = create_response["Version"] # $LATEST.PUBLISHED
|
|
412
435
|
model["Arn"] = create_response["FunctionArn"]
|
|
413
436
|
|
|
414
|
-
|
|
437
|
+
if request.custom_context.get("Version") == "$LATEST.PUBLISHED":
|
|
438
|
+
# for managed instance lambdas, we need to wait until the version is published & active
|
|
439
|
+
get_fn_response = lambda_client.get_function(
|
|
440
|
+
FunctionName=model["FunctionName"], Qualifier=request.custom_context["Version"]
|
|
441
|
+
)
|
|
442
|
+
else:
|
|
443
|
+
get_fn_response = lambda_client.get_function(FunctionName=model["Arn"])
|
|
444
|
+
|
|
415
445
|
match get_fn_response["Configuration"]["State"]:
|
|
446
|
+
# TODO: explicitly handle new ActiveNonInvocable state?
|
|
416
447
|
case "Pending":
|
|
417
448
|
return ProgressEvent(
|
|
418
449
|
status=OperationStatus.IN_PROGRESS,
|
|
@@ -541,6 +572,7 @@ class LambdaFunctionProvider(ResourceProvider[LambdaFunctionProperties]):
|
|
|
541
572
|
"TracingConfig",
|
|
542
573
|
"VpcConfig",
|
|
543
574
|
"LoggingConfig",
|
|
575
|
+
"CapacityProviderConfig",
|
|
544
576
|
]
|
|
545
577
|
update_config_props = util.select_attributes(request.desired_state, config_keys)
|
|
546
578
|
function_name = request.previous_state["FunctionName"]
|
|
@@ -34,6 +34,7 @@ from localstack.aws.api.lambda_ import Runtime
|
|
|
34
34
|
# => Synchronize the order with the "Supported runtimes" under "AWS Lambda runtimes" (a)
|
|
35
35
|
# => Add comments for deprecated runtimes using <Deprecation date> => <Block function create> => <Block function update>
|
|
36
36
|
IMAGE_MAPPING: dict[Runtime, str] = {
|
|
37
|
+
Runtime.nodejs24_x: "nodejs:24",
|
|
37
38
|
Runtime.nodejs22_x: "nodejs:22",
|
|
38
39
|
Runtime.nodejs20_x: "nodejs:20",
|
|
39
40
|
Runtime.nodejs18_x: "nodejs:18",
|
|
@@ -112,6 +113,7 @@ ALL_RUNTIMES: list[Runtime] = list(IMAGE_MAPPING.keys())
|
|
|
112
113
|
# => Remove deprecated runtimes from this testing list
|
|
113
114
|
RUNTIMES_AGGREGATED = {
|
|
114
115
|
"nodejs": [
|
|
116
|
+
Runtime.nodejs24_x,
|
|
115
117
|
Runtime.nodejs22_x,
|
|
116
118
|
Runtime.nodejs20_x,
|
|
117
119
|
Runtime.nodejs18_x,
|
|
@@ -166,6 +168,6 @@ SNAP_START_SUPPORTED_RUNTIMES = [
|
|
|
166
168
|
]
|
|
167
169
|
|
|
168
170
|
# An ordered list of all Lambda runtimes considered valid by AWS. Matching snapshots in test_create_lambda_exceptions
|
|
169
|
-
VALID_RUNTIMES: str = "[nodejs20.x, python3.14, provided.al2023, python3.12, python3.13, nodejs22.x, java17, nodejs16.x, java25, dotnet8, python3.10, java11, python3.11, dotnet6, java21, nodejs18.x, provided.al2, ruby3.3, ruby3.4, java8.al2, ruby3.2, python3.8, python3.9]"
|
|
171
|
+
VALID_RUNTIMES: str = "[nodejs20.x, python3.14, provided.al2023, python3.12, python3.13, nodejs24.x, nodejs22.x, java17, nodejs16.x, java25, dotnet8, python3.10, java11, python3.11, dotnet6, java21, nodejs18.x, provided.al2, ruby3.3, ruby3.4, java8.al2, ruby3.2, python3.8, python3.9]"
|
|
170
172
|
# An ordered list of all Lambda runtimes for layers considered valid by AWS. Matching snapshots in test_layer_exceptions
|
|
171
173
|
VALID_LAYER_RUNTIMES: str = "[ruby3.5, ruby2.6, dotnetcore1.0, python3.7, nodejs8.10, nasa, ruby2.7, python2.7-greengrass, dotnetcore2.0, python3.8, java21, dotnet6, dotnetcore2.1, python3.9, java11, nodejs6.10, provided, dotnetcore3.1, dotnet8, java25, java17, nodejs, nodejs4.3, java8.al2, go1.x, dotnet10, nodejs20.x, go1.9, byol, nodejs10.x, provided.al2023, nodejs22.x, python3.10, java8, nodejs12.x, python3.11, nodejs24.x, nodejs8.x, python3.12, nodejs14.x, nodejs8.9, nodejs26.x, python3.13, python3.14, nodejs16.x, python3.15, provided.al2, nodejs4.3-edge, nodejs18.x, ruby3.2, python3.4, ruby3.3, ruby3.4, ruby2.5, python3.6, python2.7]"
|
|
@@ -14,6 +14,7 @@ from moto.logs.models import LogStream as MotoLogStream
|
|
|
14
14
|
from localstack.aws.api import CommonServiceException, RequestContext, handler
|
|
15
15
|
from localstack.aws.api.logs import (
|
|
16
16
|
AmazonResourceName,
|
|
17
|
+
DeletionProtectionEnabled,
|
|
17
18
|
DescribeLogGroupsRequest,
|
|
18
19
|
DescribeLogGroupsResponse,
|
|
19
20
|
DescribeLogStreamsRequest,
|
|
@@ -43,6 +44,7 @@ from localstack.services import moto
|
|
|
43
44
|
from localstack.services.logs.models import get_moto_logs_backend, logs_stores
|
|
44
45
|
from localstack.services.moto import call_moto
|
|
45
46
|
from localstack.services.plugins import ServiceLifecycleHook
|
|
47
|
+
from localstack.state import StateVisitor
|
|
46
48
|
from localstack.utils.aws import arns
|
|
47
49
|
from localstack.utils.aws.client_types import ServicePrincipal
|
|
48
50
|
from localstack.utils.bootstrap import is_api_enabled
|
|
@@ -57,6 +59,12 @@ class LogsProvider(LogsApi, ServiceLifecycleHook):
|
|
|
57
59
|
super().__init__()
|
|
58
60
|
self.cw_client = connect_to().cloudwatch
|
|
59
61
|
|
|
62
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
63
|
+
from moto.logs.models import logs_backends
|
|
64
|
+
|
|
65
|
+
visitor.visit(logs_backends)
|
|
66
|
+
visitor.visit(logs_stores)
|
|
67
|
+
|
|
60
68
|
def put_log_events(
|
|
61
69
|
self,
|
|
62
70
|
context: RequestContext,
|
|
@@ -163,6 +171,7 @@ class LogsProvider(LogsApi, ServiceLifecycleHook):
|
|
|
163
171
|
kms_key_id: KmsKeyId | None = None,
|
|
164
172
|
tags: Tags | None = None,
|
|
165
173
|
log_group_class: LogGroupClass | None = None,
|
|
174
|
+
deletion_protection_enabled: DeletionProtectionEnabled | None = None,
|
|
166
175
|
**kwargs,
|
|
167
176
|
) -> None:
|
|
168
177
|
call_moto(context)
|
|
@@ -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
|
|
|
@@ -116,6 +116,11 @@ DEFAULT_OPENSEARCH_DOMAIN_ENDPOINT_OPTIONS = DomainEndpointOptions(
|
|
|
116
116
|
CustomEndpointEnabled=False,
|
|
117
117
|
)
|
|
118
118
|
|
|
119
|
+
DEFAULT_AUTOTUNE_OPTIONS = AutoTuneOptionsOutput(
|
|
120
|
+
State=AutoTuneState.ENABLED,
|
|
121
|
+
UseOffPeakWindow=False,
|
|
122
|
+
)
|
|
123
|
+
|
|
119
124
|
|
|
120
125
|
def cluster_manager() -> ClusterManager:
|
|
121
126
|
global __CLUSTER_MANAGER
|
|
@@ -203,6 +208,13 @@ def _status_to_config(status: DomainStatus) -> DomainConfig:
|
|
|
203
208
|
cluster_cfg = status.get("ClusterConfig") or {}
|
|
204
209
|
default_cfg = DEFAULT_OPENSEARCH_CLUSTER_CONFIG
|
|
205
210
|
config_status = get_domain_config_status()
|
|
211
|
+
autotune_options = status.get("AutoTuneOptions") or DEFAULT_AUTOTUNE_OPTIONS
|
|
212
|
+
autotune_state = autotune_options.get("State") or AutoTuneState.ENABLED
|
|
213
|
+
desired_state = (
|
|
214
|
+
AutoTuneDesiredState.ENABLED
|
|
215
|
+
if autotune_state == AutoTuneState.ENABLED
|
|
216
|
+
else AutoTuneDesiredState.DISABLED
|
|
217
|
+
)
|
|
206
218
|
return DomainConfig(
|
|
207
219
|
AccessPolicies=AccessPoliciesStatus(
|
|
208
220
|
Options=status.get("AccessPolicies", ""),
|
|
@@ -275,15 +287,16 @@ def _status_to_config(status: DomainStatus) -> DomainConfig:
|
|
|
275
287
|
),
|
|
276
288
|
AutoTuneOptions=AutoTuneOptionsStatus(
|
|
277
289
|
Options=AutoTuneOptions(
|
|
278
|
-
DesiredState=
|
|
290
|
+
DesiredState=desired_state,
|
|
279
291
|
RollbackOnDisable=RollbackOnDisable.NO_ROLLBACK,
|
|
280
292
|
MaintenanceSchedules=[],
|
|
293
|
+
UseOffPeakWindow=autotune_options.get("UseOffPeakWindow", False),
|
|
281
294
|
),
|
|
282
295
|
Status=AutoTuneStatus(
|
|
283
296
|
CreationDate=config_status.get("CreationDate"),
|
|
284
297
|
UpdateDate=config_status.get("UpdateDate"),
|
|
285
298
|
UpdateVersion=config_status.get("UpdateVersion"),
|
|
286
|
-
State=
|
|
299
|
+
State=autotune_state,
|
|
287
300
|
PendingDeletion=config_status.get("PendingDeletion"),
|
|
288
301
|
),
|
|
289
302
|
),
|
|
@@ -315,6 +328,22 @@ def get_domain_status(
|
|
|
315
328
|
stored_status.update(request)
|
|
316
329
|
default_cfg.update(request.get("ClusterConfig", {}))
|
|
317
330
|
|
|
331
|
+
autotune_options = stored_status.get("AutoTuneOptions") or deepcopy(DEFAULT_AUTOTUNE_OPTIONS)
|
|
332
|
+
if request and (request_options := request.get("AutoTuneOptions")):
|
|
333
|
+
desired_state = request_options.get("DesiredState") or AutoTuneDesiredState.ENABLED
|
|
334
|
+
state = (
|
|
335
|
+
AutoTuneState.ENABLED
|
|
336
|
+
if desired_state == AutoTuneDesiredState.ENABLED
|
|
337
|
+
else AutoTuneState.DISABLED
|
|
338
|
+
)
|
|
339
|
+
autotune_options = AutoTuneOptionsOutput(
|
|
340
|
+
State=state,
|
|
341
|
+
UseOffPeakWindow=request_options.get(
|
|
342
|
+
"UseOffPeakWindow", autotune_options.get("UseOffPeakWindow", False)
|
|
343
|
+
),
|
|
344
|
+
)
|
|
345
|
+
stored_status["AutoTuneOptions"] = autotune_options
|
|
346
|
+
|
|
318
347
|
domain_processing_status = stored_status.get("DomainProcessingStatus", None)
|
|
319
348
|
processing = stored_status.get("Processing", True)
|
|
320
349
|
if deleted:
|
|
@@ -377,7 +406,10 @@ def get_domain_status(
|
|
|
377
406
|
AdvancedSecurityOptions=AdvancedSecurityOptions(
|
|
378
407
|
Enabled=False, InternalUserDatabaseEnabled=False
|
|
379
408
|
),
|
|
380
|
-
AutoTuneOptions=AutoTuneOptionsOutput(
|
|
409
|
+
AutoTuneOptions=AutoTuneOptionsOutput(
|
|
410
|
+
State=stored_status.get("AutoTuneOptions", {}).get("State"),
|
|
411
|
+
UseOffPeakWindow=autotune_options.get("UseOffPeakWindow", False),
|
|
412
|
+
),
|
|
381
413
|
)
|
|
382
414
|
return new_status
|
|
383
415
|
|
|
@@ -582,6 +614,24 @@ class OpensearchProvider(OpensearchApi, ServiceLifecycleHook):
|
|
|
582
614
|
if domain_status is None:
|
|
583
615
|
raise ResourceNotFoundException(f"Domain not found: {domain_key.domain_name}")
|
|
584
616
|
|
|
617
|
+
if payload.get("AutoTuneOptions"):
|
|
618
|
+
auto_request = payload.pop("AutoTuneOptions")
|
|
619
|
+
desired_state = auto_request.get("DesiredState") or AutoTuneDesiredState.ENABLED
|
|
620
|
+
|
|
621
|
+
state = (
|
|
622
|
+
AutoTuneState.ENABLED
|
|
623
|
+
if desired_state == AutoTuneDesiredState.ENABLED
|
|
624
|
+
else AutoTuneState.DISABLED
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
current_autotune = domain_status.get("AutoTuneOptions", {})
|
|
628
|
+
domain_status["AutoTuneOptions"] = AutoTuneOptionsOutput(
|
|
629
|
+
State=state,
|
|
630
|
+
UseOffPeakWindow=auto_request.get(
|
|
631
|
+
"UseOffPeakWindow", current_autotune.get("UseOffPeakWindow", False)
|
|
632
|
+
),
|
|
633
|
+
)
|
|
634
|
+
|
|
585
635
|
status_update: dict = _update_domain_config_request_to_status(payload)
|
|
586
636
|
domain_status.update(status_update)
|
|
587
637
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
from localstack.aws.api.resource_groups import ResourceGroupsApi
|
|
2
|
+
from localstack.state import StateVisitor
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class ResourceGroupsProvider(ResourceGroupsApi):
|
|
5
|
-
|
|
6
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
7
|
+
from moto.resourcegroups.models import resourcegroups_backends
|
|
8
|
+
|
|
9
|
+
visitor.visit(resourcegroups_backends)
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
2
|
|
|
3
3
|
from localstack.aws.api.resourcegroupstaggingapi import ResourcegroupstaggingapiApi
|
|
4
|
+
from localstack.state import StateVisitor
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class ResourcegroupstaggingapiProvider(ResourcegroupstaggingapiApi, ABC):
|
|
7
|
-
|
|
8
|
+
def accept_state_visitor(self, visitor: StateVisitor):
|
|
9
|
+
# currently, Moto resourcegroupstaggingapi stores all tags into the other services backend, so their backend
|
|
10
|
+
# does not hold any state and is not worth saving. It only holds direct references to other services
|
|
11
|
+
# It only holds pagination tokens that are not worth keeping
|
|
12
|
+
pass
|
|
@@ -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
|
+
)
|
localstack/services/s3/models.py
CHANGED
|
@@ -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
|
|
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()
|
|
@@ -33,6 +33,7 @@ from localstack.aws.api.s3 import (
|
|
|
33
33
|
BucketAlreadyOwnedByYou,
|
|
34
34
|
BucketCannedACL,
|
|
35
35
|
BucketLifecycleConfiguration,
|
|
36
|
+
BucketLocationConstraint,
|
|
36
37
|
BucketLoggingStatus,
|
|
37
38
|
BucketName,
|
|
38
39
|
BucketNotEmpty,
|
|
@@ -117,7 +118,6 @@ from localstack.aws.api.s3 import (
|
|
|
117
118
|
InvalidArgument,
|
|
118
119
|
InvalidBucketName,
|
|
119
120
|
InvalidDigest,
|
|
120
|
-
InvalidLocationConstraint,
|
|
121
121
|
InvalidObjectState,
|
|
122
122
|
InvalidPartNumber,
|
|
123
123
|
InvalidPartOrder,
|
|
@@ -229,7 +229,7 @@ from localstack.aws.handlers import (
|
|
|
229
229
|
preprocess_request,
|
|
230
230
|
serve_custom_service_request_handlers,
|
|
231
231
|
)
|
|
232
|
-
from localstack.constants import AWS_REGION_US_EAST_1
|
|
232
|
+
from localstack.constants import AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1
|
|
233
233
|
from localstack.services.edge import ROUTER
|
|
234
234
|
from localstack.services.plugins import ServiceLifecycleHook
|
|
235
235
|
from localstack.services.s3.codec import AwsChunkedDecoder
|
|
@@ -278,6 +278,7 @@ from localstack.services.s3.utils import (
|
|
|
278
278
|
etag_to_base_64_content_md5,
|
|
279
279
|
extract_bucket_key_version_id_from_copy_source,
|
|
280
280
|
generate_safe_version_id,
|
|
281
|
+
get_bucket_location_xml,
|
|
281
282
|
get_canned_acl,
|
|
282
283
|
get_class_attrs_from_spec_class,
|
|
283
284
|
get_failed_precondition_copy_source,
|
|
@@ -304,6 +305,7 @@ from localstack.services.s3.utils import (
|
|
|
304
305
|
validate_dict_fields,
|
|
305
306
|
validate_failed_precondition,
|
|
306
307
|
validate_kms_key_id,
|
|
308
|
+
validate_location_constraint,
|
|
307
309
|
validate_tag_set,
|
|
308
310
|
)
|
|
309
311
|
from localstack.services.s3.validation import (
|
|
@@ -323,6 +325,7 @@ from localstack.services.s3.validation import (
|
|
|
323
325
|
from localstack.services.s3.website_hosting import register_website_hosting_routes
|
|
324
326
|
from localstack.state import AssetDirectory, StateVisitor
|
|
325
327
|
from localstack.utils.aws.arns import s3_bucket_name
|
|
328
|
+
from localstack.utils.aws.aws_stack import get_valid_regions_for_service
|
|
326
329
|
from localstack.utils.collections import select_from_typed_dict
|
|
327
330
|
from localstack.utils.strings import short_uid, to_bytes, to_str
|
|
328
331
|
|
|
@@ -491,34 +494,20 @@ class S3Provider(S3Api, ServiceLifecycleHook):
|
|
|
491
494
|
if not is_bucket_name_valid(bucket_name):
|
|
492
495
|
raise InvalidBucketName("The specified bucket is not valid.", BucketName=bucket_name)
|
|
493
496
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
)
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
if bucket_region in ("us-east-1", "aws-global"):
|
|
506
|
-
raise InvalidLocationConstraint(
|
|
507
|
-
"The specified location-constraint is not valid",
|
|
508
|
-
LocationConstraint=bucket_region,
|
|
509
|
-
)
|
|
510
|
-
elif context.region != bucket_region:
|
|
511
|
-
raise CommonServiceException(
|
|
512
|
-
code="IllegalLocationConstraintException",
|
|
513
|
-
message=f"The {bucket_region} location constraint is incompatible for the region specific endpoint this request was sent to.",
|
|
514
|
-
)
|
|
515
|
-
else:
|
|
497
|
+
create_bucket_configuration = request.get("CreateBucketConfiguration") or {}
|
|
498
|
+
|
|
499
|
+
bucket_tags = create_bucket_configuration.get("Tags")
|
|
500
|
+
if bucket_tags:
|
|
501
|
+
validate_tag_set(bucket_tags, type_set="create-bucket")
|
|
502
|
+
|
|
503
|
+
location_constraint = create_bucket_configuration.get("LocationConstraint", "")
|
|
504
|
+
validate_location_constraint(context.region, location_constraint)
|
|
505
|
+
|
|
506
|
+
bucket_region = location_constraint
|
|
507
|
+
if not location_constraint:
|
|
516
508
|
bucket_region = AWS_REGION_US_EAST_1
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
code="IllegalLocationConstraintException",
|
|
520
|
-
message="The unspecified location constraint is incompatible for the region specific endpoint this request was sent to.",
|
|
521
|
-
)
|
|
509
|
+
if location_constraint == BucketLocationConstraint.EU:
|
|
510
|
+
bucket_region = AWS_REGION_EU_WEST_1
|
|
522
511
|
|
|
523
512
|
store = self.get_store(context.account_id, bucket_region)
|
|
524
513
|
|
|
@@ -527,8 +516,9 @@ class S3Provider(S3Api, ServiceLifecycleHook):
|
|
|
527
516
|
if existing_bucket_owner != context.account_id:
|
|
528
517
|
raise BucketAlreadyExists()
|
|
529
518
|
|
|
530
|
-
# if the existing bucket has the same owner, the behaviour will depend on the region
|
|
531
|
-
|
|
519
|
+
# if the existing bucket has the same owner, the behaviour will depend on the region and if the request has
|
|
520
|
+
# tags
|
|
521
|
+
if bucket_region != AWS_REGION_US_EAST_1 or bucket_tags:
|
|
532
522
|
raise BucketAlreadyOwnedByYou(
|
|
533
523
|
"Your previous request to create the named bucket succeeded and you already own it.",
|
|
534
524
|
BucketName=bucket_name,
|
|
@@ -547,6 +537,7 @@ class S3Provider(S3Api, ServiceLifecycleHook):
|
|
|
547
537
|
# see https://docs.aws.amazon.com/AmazonS3/latest/API/API_Owner.html
|
|
548
538
|
owner = get_owner_for_account_id(context.account_id)
|
|
549
539
|
acl = get_access_control_policy_for_new_resource_request(request, owner=owner)
|
|
540
|
+
|
|
550
541
|
s3_bucket = S3Bucket(
|
|
551
542
|
name=bucket_name,
|
|
552
543
|
account_id=context.account_id,
|
|
@@ -555,10 +546,16 @@ class S3Provider(S3Api, ServiceLifecycleHook):
|
|
|
555
546
|
acl=acl,
|
|
556
547
|
object_ownership=request.get("ObjectOwnership"),
|
|
557
548
|
object_lock_enabled_for_bucket=request.get("ObjectLockEnabledForBucket"),
|
|
549
|
+
location_constraint=location_constraint,
|
|
558
550
|
)
|
|
559
551
|
|
|
560
552
|
store.buckets[bucket_name] = s3_bucket
|
|
561
553
|
store.global_bucket_map[bucket_name] = s3_bucket.bucket_account_id
|
|
554
|
+
if bucket_tags:
|
|
555
|
+
store.TAGS.tag_resource(
|
|
556
|
+
arn=s3_bucket.bucket_arn,
|
|
557
|
+
tags=bucket_tags,
|
|
558
|
+
)
|
|
562
559
|
self._cors_handler.invalidate_cache()
|
|
563
560
|
self._storage_backend.create_bucket(bucket_name)
|
|
564
561
|
|
|
@@ -607,6 +604,13 @@ class S3Provider(S3Api, ServiceLifecycleHook):
|
|
|
607
604
|
bucket_region: BucketRegion = None,
|
|
608
605
|
**kwargs,
|
|
609
606
|
) -> ListBucketsOutput:
|
|
607
|
+
if bucket_region and not config.ALLOW_NONSTANDARD_REGIONS:
|
|
608
|
+
if bucket_region not in get_valid_regions_for_service(self.service):
|
|
609
|
+
raise InvalidArgument(
|
|
610
|
+
f"Argument value {bucket_region} is not a valid AWS Region",
|
|
611
|
+
ArgumentName="bucket-region",
|
|
612
|
+
)
|
|
613
|
+
|
|
610
614
|
owner = get_owner_for_account_id(context.account_id)
|
|
611
615
|
store = self.get_store(context.account_id, context.region)
|
|
612
616
|
|
|
@@ -698,16 +702,18 @@ class S3Provider(S3Api, ServiceLifecycleHook):
|
|
|
698
702
|
"""
|
|
699
703
|
store, s3_bucket = self._get_cross_account_bucket(context, bucket)
|
|
700
704
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
)
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
705
|
+
# TODO: Remove usage of getattr once persistence mechanism is updated.
|
|
706
|
+
# If the stored constraint is None the bucket was made before the storage of location_constraint.
|
|
707
|
+
# The EU location constraint wasn't supported before this point so we can safely default to the region.
|
|
708
|
+
location_constraint = getattr(s3_bucket, "location_constraint", None)
|
|
709
|
+
if location_constraint is None:
|
|
710
|
+
location_constraint = (
|
|
711
|
+
s3_bucket.bucket_region if s3_bucket.bucket_region != "us-east-1" else ""
|
|
712
|
+
)
|
|
708
713
|
|
|
709
|
-
|
|
710
|
-
|
|
714
|
+
return GetBucketLocationOutput(
|
|
715
|
+
LocationConstraint=get_bucket_location_xml(location_constraint)
|
|
716
|
+
)
|
|
711
717
|
|
|
712
718
|
@handler("PutObject", expand=False)
|
|
713
719
|
def put_object(
|
|
@@ -1189,12 +1195,14 @@ class S3Provider(S3Api, ServiceLifecycleHook):
|
|
|
1189
1195
|
response["ChecksumType"] = checksum_type
|
|
1190
1196
|
|
|
1191
1197
|
add_encryption_to_response(response, s3_object=s3_object)
|
|
1198
|
+
object_tags = store.TAGS.tags.get(
|
|
1199
|
+
get_unique_key_id(bucket_name, object_key, s3_object.version_id)
|
|
1200
|
+
)
|
|
1201
|
+
if object_tags:
|
|
1202
|
+
response["TagCount"] = len(object_tags)
|
|
1192
1203
|
|
|
1193
1204
|
# if you specify the VersionId, AWS won't return the Expiration header, even if that's the current version
|
|
1194
1205
|
if not version_id and s3_bucket.lifecycle_rules:
|
|
1195
|
-
object_tags = store.TAGS.tags.get(
|
|
1196
|
-
get_unique_key_id(bucket_name, object_key, s3_object.version_id)
|
|
1197
|
-
)
|
|
1198
1206
|
if expiration_header := self._get_expiration_header(
|
|
1199
1207
|
s3_bucket.lifecycle_rules,
|
|
1200
1208
|
bucket_name,
|