localstack-core 4.7.1.dev49__py3-none-any.whl → 4.10.1.dev12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- localstack/aws/api/cloudformation/__init__.py +18 -4
- localstack/aws/api/cloudwatch/__init__.py +41 -1
- localstack/aws/api/config/__init__.py +4 -0
- localstack/aws/api/core.py +6 -2
- localstack/aws/api/dynamodb/__init__.py +30 -0
- localstack/aws/api/ec2/__init__.py +1522 -65
- localstack/aws/api/iam/__init__.py +7 -0
- localstack/aws/api/kinesis/__init__.py +19 -0
- localstack/aws/api/kms/__init__.py +6 -0
- localstack/aws/api/lambda_/__init__.py +13 -0
- localstack/aws/api/logs/__init__.py +15 -0
- localstack/aws/api/redshift/__init__.py +9 -3
- localstack/aws/api/route53/__init__.py +5 -0
- localstack/aws/api/s3/__init__.py +12 -0
- localstack/aws/api/s3control/__init__.py +54 -0
- localstack/aws/api/ssm/__init__.py +2 -0
- localstack/aws/api/transcribe/__init__.py +17 -0
- localstack/aws/client.py +7 -2
- localstack/aws/forwarder.py +52 -5
- localstack/aws/handlers/analytics.py +1 -1
- localstack/aws/handlers/internal_requests.py +6 -1
- localstack/aws/handlers/logging.py +12 -2
- localstack/aws/handlers/metric_handler.py +41 -1
- localstack/aws/handlers/service.py +40 -20
- localstack/aws/mocking.py +2 -2
- localstack/aws/patches.py +2 -2
- localstack/aws/protocol/parser.py +459 -32
- localstack/aws/protocol/serializer.py +689 -69
- localstack/aws/protocol/service_router.py +120 -20
- localstack/aws/protocol/validate.py +1 -1
- localstack/aws/scaffold.py +1 -1
- localstack/aws/skeleton.py +4 -2
- localstack/aws/spec-patches.json +58 -0
- localstack/aws/spec.py +37 -16
- localstack/cli/exceptions.py +1 -1
- localstack/cli/localstack.py +6 -6
- localstack/cli/lpm.py +3 -4
- localstack/cli/plugins.py +1 -1
- localstack/cli/profiles.py +1 -2
- localstack/config.py +25 -18
- localstack/constants.py +4 -29
- localstack/dev/kubernetes/__main__.py +130 -7
- localstack/dev/run/configurators.py +1 -4
- localstack/dev/run/paths.py +1 -1
- localstack/dns/plugins.py +5 -1
- localstack/dns/server.py +13 -4
- localstack/logging/format.py +3 -3
- localstack/packages/api.py +9 -8
- localstack/packages/core.py +2 -2
- localstack/packages/plugins.py +0 -8
- localstack/runtime/analytics.py +3 -0
- localstack/runtime/hooks.py +1 -1
- localstack/runtime/init.py +2 -2
- localstack/runtime/main.py +5 -5
- localstack/runtime/patches.py +2 -2
- localstack/services/apigateway/helpers.py +1 -4
- localstack/services/apigateway/legacy/helpers.py +7 -8
- localstack/services/apigateway/legacy/integration.py +4 -3
- localstack/services/apigateway/legacy/invocations.py +6 -5
- localstack/services/apigateway/legacy/provider.py +148 -68
- localstack/services/apigateway/legacy/templates.py +1 -1
- localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
- localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +1 -2
- localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +3 -0
- localstack/services/apigateway/next_gen/execute_api/integrations/http.py +3 -3
- localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
- localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
- localstack/services/apigateway/next_gen/provider.py +5 -0
- localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +1 -1
- localstack/services/cloudformation/api_utils.py +4 -8
- localstack/services/cloudformation/cfn_utils.py +1 -1
- localstack/services/cloudformation/engine/entities.py +14 -4
- localstack/services/cloudformation/engine/template_deployer.py +6 -4
- localstack/services/cloudformation/engine/transformers.py +6 -4
- localstack/services/cloudformation/engine/v2/change_set_model.py +201 -13
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +52 -3
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +117 -76
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +205 -52
- localstack/services/cloudformation/engine/v2/change_set_model_transform.py +350 -116
- localstack/services/cloudformation/engine/v2/change_set_model_validator.py +56 -14
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +1 -0
- localstack/services/cloudformation/engine/v2/resolving.py +7 -5
- localstack/services/cloudformation/engine/yaml_parser.py +9 -2
- localstack/services/cloudformation/provider.py +7 -5
- localstack/services/cloudformation/resource_provider.py +7 -1
- localstack/services/cloudformation/resources.py +24149 -0
- localstack/services/cloudformation/service_models.py +2 -2
- localstack/services/cloudformation/v2/entities.py +19 -9
- localstack/services/cloudformation/v2/provider.py +336 -106
- localstack/services/cloudformation/v2/types.py +13 -7
- localstack/services/cloudformation/v2/utils.py +4 -1
- localstack/services/cloudwatch/alarm_scheduler.py +4 -1
- localstack/services/cloudwatch/provider.py +18 -13
- localstack/services/cloudwatch/provider_v2.py +25 -28
- localstack/services/dynamodb/packages.py +2 -1
- localstack/services/dynamodb/provider.py +42 -0
- localstack/services/dynamodb/server.py +2 -2
- localstack/services/dynamodb/v2/provider.py +42 -0
- localstack/services/ecr/resource_providers/aws_ecr_repository.py +5 -2
- localstack/services/edge.py +1 -1
- localstack/services/es/provider.py +2 -2
- localstack/services/events/event_rule_engine.py +31 -13
- localstack/services/events/models.py +4 -5
- localstack/services/events/provider.py +17 -14
- localstack/services/events/target.py +17 -9
- localstack/services/events/v1/provider.py +5 -5
- localstack/services/firehose/provider.py +14 -4
- localstack/services/iam/provider.py +11 -116
- localstack/services/iam/resources/policy_simulator.py +133 -0
- localstack/services/kinesis/models.py +15 -2
- localstack/services/kinesis/provider.py +86 -3
- localstack/services/kms/provider.py +14 -5
- localstack/services/lambda_/api_utils.py +6 -3
- localstack/services/lambda_/invocation/docker_runtime_executor.py +1 -1
- localstack/services/lambda_/invocation/event_manager.py +1 -1
- localstack/services/lambda_/invocation/internal_sqs_queue.py +5 -9
- localstack/services/lambda_/invocation/lambda_models.py +10 -7
- localstack/services/lambda_/invocation/lambda_service.py +5 -1
- localstack/services/lambda_/packages.py +1 -1
- localstack/services/lambda_/provider.py +4 -3
- localstack/services/lambda_/provider_utils.py +1 -1
- localstack/services/logs/provider.py +36 -19
- localstack/services/moto.py +2 -1
- localstack/services/opensearch/cluster.py +15 -7
- localstack/services/opensearch/packages.py +26 -7
- localstack/services/opensearch/provider.py +8 -2
- localstack/services/opensearch/versions.py +56 -7
- localstack/services/plugins.py +11 -7
- localstack/services/providers.py +10 -2
- localstack/services/redshift/provider.py +0 -21
- localstack/services/s3/constants.py +5 -2
- localstack/services/s3/cors.py +4 -4
- localstack/services/s3/models.py +1 -1
- localstack/services/s3/notifications.py +55 -39
- localstack/services/s3/presigned_url.py +35 -54
- localstack/services/s3/provider.py +73 -15
- localstack/services/s3/utils.py +42 -22
- localstack/services/s3/validation.py +46 -32
- localstack/services/s3/website_hosting.py +4 -2
- localstack/services/ses/provider.py +18 -8
- localstack/services/sns/constants.py +7 -1
- localstack/services/sns/executor.py +9 -2
- localstack/services/sns/provider.py +8 -5
- localstack/services/sns/publisher.py +31 -16
- localstack/services/sns/v2/models.py +167 -0
- localstack/services/sns/v2/provider.py +867 -0
- localstack/services/sns/v2/utils.py +130 -0
- localstack/services/sqs/constants.py +1 -1
- localstack/services/sqs/developer_api.py +205 -0
- localstack/services/sqs/models.py +48 -5
- localstack/services/sqs/provider.py +38 -311
- localstack/services/sqs/query_api.py +6 -2
- localstack/services/sqs/utils.py +121 -2
- localstack/services/ssm/provider.py +1 -1
- localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
- localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
- localstack/services/stepfunctions/asl/eval/environment.py +1 -1
- localstack/services/stepfunctions/asl/jsonata/jsonata.py +1 -1
- localstack/services/stepfunctions/backend/execution.py +2 -1
- localstack/services/stores.py +1 -1
- localstack/services/transcribe/provider.py +6 -1
- localstack/state/codecs.py +61 -0
- localstack/state/core.py +11 -5
- localstack/state/pickle.py +10 -49
- localstack/testing/aws/cloudformation_utils.py +1 -1
- localstack/testing/pytest/cloudformation/fixtures.py +3 -3
- localstack/testing/pytest/cloudformation/transformers.py +0 -0
- localstack/testing/pytest/container.py +4 -5
- localstack/testing/pytest/fixtures.py +33 -31
- localstack/testing/pytest/in_memory_localstack.py +0 -4
- localstack/testing/pytest/marking.py +38 -11
- localstack/testing/pytest/stepfunctions/utils.py +4 -3
- localstack/testing/pytest/util.py +1 -1
- localstack/testing/pytest/validation_tracking.py +1 -2
- localstack/testing/snapshots/transformer_utility.py +6 -1
- localstack/utils/analytics/events.py +2 -2
- localstack/utils/analytics/metadata.py +6 -4
- localstack/utils/analytics/metrics/counter.py +8 -15
- localstack/utils/analytics/publisher.py +1 -2
- localstack/utils/analytics/service_providers.py +19 -0
- localstack/utils/analytics/service_request_aggregator.py +2 -2
- localstack/utils/archives.py +11 -11
- localstack/utils/asyncio.py +2 -2
- localstack/utils/aws/arns.py +24 -29
- localstack/utils/aws/aws_responses.py +8 -8
- localstack/utils/aws/aws_stack.py +2 -3
- localstack/utils/aws/dead_letter_queue.py +1 -5
- localstack/utils/aws/message_forwarding.py +1 -2
- localstack/utils/aws/request_context.py +4 -5
- localstack/utils/aws/resources.py +1 -1
- localstack/utils/aws/templating.py +1 -1
- localstack/utils/batch_policy.py +3 -3
- localstack/utils/bootstrap.py +21 -13
- localstack/utils/catalog/catalog.py +139 -0
- localstack/utils/catalog/catalog_loader.py +119 -0
- localstack/utils/catalog/common.py +58 -0
- localstack/utils/catalog/plugins.py +28 -0
- localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
- localstack/utils/collections.py +7 -8
- localstack/utils/config_listener.py +1 -1
- localstack/utils/container_networking.py +2 -3
- localstack/utils/container_utils/container_client.py +135 -136
- localstack/utils/container_utils/docker_cmd_client.py +85 -69
- localstack/utils/container_utils/docker_sdk_client.py +69 -66
- localstack/utils/crypto.py +10 -10
- localstack/utils/diagnose.py +3 -4
- localstack/utils/docker_utils.py +9 -5
- localstack/utils/files.py +33 -13
- localstack/utils/functions.py +4 -3
- localstack/utils/http.py +11 -11
- localstack/utils/json.py +20 -6
- localstack/utils/kinesis/kinesis_connector.py +2 -1
- localstack/utils/net.py +15 -9
- localstack/utils/no_exit_argument_parser.py +2 -2
- localstack/utils/numbers.py +9 -2
- localstack/utils/objects.py +7 -6
- localstack/utils/patch.py +10 -3
- localstack/utils/run.py +12 -11
- localstack/utils/scheduler.py +11 -11
- localstack/utils/server/tcp_proxy.py +2 -2
- localstack/utils/serving.py +3 -4
- localstack/utils/strings.py +15 -16
- localstack/utils/sync.py +126 -1
- localstack/utils/tagging.py +8 -6
- localstack/utils/testutil.py +8 -8
- localstack/utils/threads.py +2 -2
- localstack/utils/time.py +12 -4
- localstack/utils/urls.py +1 -3
- localstack/utils/xray/traceid.py +1 -1
- localstack/version.py +16 -3
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -14
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +248 -239
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
- localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
- localstack/packages/terraform.py +0 -46
- localstack/services/cloudformation/deploy.html +0 -144
- localstack/services/cloudformation/deploy_ui.py +0 -47
- localstack/services/cloudformation/plugins.py +0 -12
- localstack_core-4.7.1.dev49.dist-info/plux.json +0 -1
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import logging
|
|
2
3
|
import os
|
|
4
|
+
import re
|
|
3
5
|
import time
|
|
4
6
|
from random import random
|
|
5
7
|
|
|
@@ -8,14 +10,18 @@ from localstack.aws.api import RequestContext
|
|
|
8
10
|
from localstack.aws.api.kinesis import (
|
|
9
11
|
ConsumerARN,
|
|
10
12
|
Data,
|
|
13
|
+
GetResourcePolicyOutput,
|
|
11
14
|
HashKey,
|
|
12
15
|
KinesisApi,
|
|
13
16
|
PartitionKey,
|
|
17
|
+
Policy,
|
|
14
18
|
ProvisionedThroughputExceededException,
|
|
15
19
|
PutRecordOutput,
|
|
16
20
|
PutRecordsOutput,
|
|
17
21
|
PutRecordsRequestEntryList,
|
|
18
22
|
PutRecordsResultEntry,
|
|
23
|
+
ResourceARN,
|
|
24
|
+
ResourceNotFoundException,
|
|
19
25
|
SequenceNumber,
|
|
20
26
|
ShardId,
|
|
21
27
|
StartingPosition,
|
|
@@ -24,6 +30,7 @@ from localstack.aws.api.kinesis import (
|
|
|
24
30
|
SubscribeToShardEvent,
|
|
25
31
|
SubscribeToShardEventStream,
|
|
26
32
|
SubscribeToShardOutput,
|
|
33
|
+
ValidationException,
|
|
27
34
|
)
|
|
28
35
|
from localstack.aws.connect import connect_to
|
|
29
36
|
from localstack.constants import LOCALHOST
|
|
@@ -39,6 +46,13 @@ LOG = logging.getLogger(__name__)
|
|
|
39
46
|
MAX_SUBSCRIPTION_SECONDS = 300
|
|
40
47
|
SERVER_STARTUP_TIMEOUT = 120
|
|
41
48
|
|
|
49
|
+
DATA_STREAM_ARN_REGEX = re.compile(
|
|
50
|
+
r"^arn:aws(?:-[a-z]+)*:kinesis:[a-z0-9-]+:\d{12}:stream\/[a-zA-Z0-9_.\-]+$"
|
|
51
|
+
)
|
|
52
|
+
CONSUMER_ARN_REGEX = re.compile(
|
|
53
|
+
r"^arn:aws(?:-[a-z]+)*:kinesis:[a-z0-9-]+:\d{12}:stream\/[a-zA-Z0-9_.\-]+\/consumer\/[a-zA-Z0-9_.\-]+:\d+$"
|
|
54
|
+
)
|
|
55
|
+
|
|
42
56
|
|
|
43
57
|
def find_stream_for_consumer(consumer_arn):
|
|
44
58
|
account_id = extract_account_id_from_arn(consumer_arn)
|
|
@@ -49,7 +63,12 @@ def find_stream_for_consumer(consumer_arn):
|
|
|
49
63
|
for cons in kinesis.list_stream_consumers(StreamARN=stream_arn)["Consumers"]:
|
|
50
64
|
if cons["ConsumerARN"] == consumer_arn:
|
|
51
65
|
return stream_name
|
|
52
|
-
raise Exception("Unable to find stream for stream consumer
|
|
66
|
+
raise Exception(f"Unable to find stream for stream consumer {consumer_arn}")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def is_valid_kinesis_arn(resource_arn: ResourceARN) -> bool:
|
|
70
|
+
"""Check if the provided ARN is a valid Kinesis ARN."""
|
|
71
|
+
return bool(CONSUMER_ARN_REGEX.match(resource_arn) or DATA_STREAM_ARN_REGEX.match(resource_arn))
|
|
53
72
|
|
|
54
73
|
|
|
55
74
|
class KinesisProvider(KinesisApi, ServiceLifecycleHook):
|
|
@@ -81,6 +100,64 @@ class KinesisProvider(KinesisApi, ServiceLifecycleHook):
|
|
|
81
100
|
def get_store(account_id: str, region_name: str) -> KinesisStore:
|
|
82
101
|
return kinesis_stores[account_id][region_name]
|
|
83
102
|
|
|
103
|
+
def put_resource_policy(
|
|
104
|
+
self,
|
|
105
|
+
context: RequestContext,
|
|
106
|
+
resource_arn: ResourceARN,
|
|
107
|
+
policy: Policy,
|
|
108
|
+
**kwargs,
|
|
109
|
+
) -> None:
|
|
110
|
+
if not is_valid_kinesis_arn(resource_arn):
|
|
111
|
+
raise ValidationException(f"invalid kinesis arn {resource_arn}")
|
|
112
|
+
|
|
113
|
+
kinesis = connect_to(
|
|
114
|
+
aws_access_key_id=context.account_id, region_name=context.region
|
|
115
|
+
).kinesis
|
|
116
|
+
try:
|
|
117
|
+
kinesis.describe_stream_summary(StreamARN=resource_arn)
|
|
118
|
+
except kinesis.exceptions.ResourceNotFoundException:
|
|
119
|
+
raise ResourceNotFoundException(f"Stream with ARN {resource_arn} not found")
|
|
120
|
+
|
|
121
|
+
store = self.get_store(context.account_id, context.region)
|
|
122
|
+
store.resource_policies[resource_arn] = policy
|
|
123
|
+
|
|
124
|
+
def get_resource_policy(
|
|
125
|
+
self,
|
|
126
|
+
context: RequestContext,
|
|
127
|
+
resource_arn: ResourceARN,
|
|
128
|
+
**kwargs,
|
|
129
|
+
) -> GetResourcePolicyOutput:
|
|
130
|
+
if not is_valid_kinesis_arn(resource_arn):
|
|
131
|
+
raise ValidationException(f"invalid kinesis arn {resource_arn}")
|
|
132
|
+
|
|
133
|
+
kinesis = connect_to(
|
|
134
|
+
aws_access_key_id=context.account_id, region_name=context.region
|
|
135
|
+
).kinesis
|
|
136
|
+
try:
|
|
137
|
+
kinesis.describe_stream_summary(StreamARN=resource_arn)
|
|
138
|
+
except kinesis.exceptions.ResourceNotFoundException:
|
|
139
|
+
raise ResourceNotFoundException(f"Stream with ARN {resource_arn} not found")
|
|
140
|
+
|
|
141
|
+
store = self.get_store(context.account_id, context.region)
|
|
142
|
+
policy = store.resource_policies.get(resource_arn, json.dumps({}))
|
|
143
|
+
return GetResourcePolicyOutput(Policy=policy)
|
|
144
|
+
|
|
145
|
+
def delete_resource_policy(
|
|
146
|
+
self,
|
|
147
|
+
context: RequestContext,
|
|
148
|
+
resource_arn: ResourceARN,
|
|
149
|
+
**kwargs,
|
|
150
|
+
) -> None:
|
|
151
|
+
if not is_valid_kinesis_arn(resource_arn):
|
|
152
|
+
raise ValidationException(f"invalid kinesis arn {resource_arn}")
|
|
153
|
+
|
|
154
|
+
store = self.get_store(context.account_id, context.region)
|
|
155
|
+
if resource_arn not in store.resource_policies:
|
|
156
|
+
raise ResourceNotFoundException(
|
|
157
|
+
f"No resource policy found for resource ARN {resource_arn}"
|
|
158
|
+
)
|
|
159
|
+
del store.resource_policies[resource_arn]
|
|
160
|
+
|
|
84
161
|
def subscribe_to_shard(
|
|
85
162
|
self,
|
|
86
163
|
context: RequestContext,
|
|
@@ -125,7 +202,13 @@ class KinesisProvider(KinesisApi, ServiceLifecycleHook):
|
|
|
125
202
|
raise
|
|
126
203
|
shard_iterator = result.get("NextShardIterator")
|
|
127
204
|
records = result.get("Records", [])
|
|
128
|
-
if
|
|
205
|
+
if records:
|
|
206
|
+
# Update the last sequence number to the last record's sequence number
|
|
207
|
+
# TODO: This will suffice for now but does not properly capture checkpointing when
|
|
208
|
+
# no data is written to a shard. See AWS docs:
|
|
209
|
+
# https://docs.aws.amazon.com/kinesis/latest/APIReference/API_SubscribeToShardEvent.html#API_SubscribeToShardEvent_Contents
|
|
210
|
+
last_sequence_number = records[-1].get("SequenceNumber", last_sequence_number)
|
|
211
|
+
else:
|
|
129
212
|
# On AWS there is *at least* 1 event every 5 seconds
|
|
130
213
|
# but this is not possible in this structure.
|
|
131
214
|
# In order to avoid a 5-second blocking call, we make the compromise of 3 seconds.
|
|
@@ -136,7 +219,7 @@ class KinesisProvider(KinesisApi, ServiceLifecycleHook):
|
|
|
136
219
|
Records=records,
|
|
137
220
|
ContinuationSequenceNumber=str(last_sequence_number),
|
|
138
221
|
MillisBehindLatest=0,
|
|
139
|
-
ChildShards=
|
|
222
|
+
ChildShards=None, # TODO: Include shard children info
|
|
140
223
|
)
|
|
141
224
|
)
|
|
142
225
|
|
|
@@ -85,6 +85,7 @@ from localstack.aws.api.kms import (
|
|
|
85
85
|
MacAlgorithmSpec,
|
|
86
86
|
MarkerType,
|
|
87
87
|
MultiRegionKey,
|
|
88
|
+
MultiRegionKeyType,
|
|
88
89
|
NotFoundException,
|
|
89
90
|
NullableBooleanType,
|
|
90
91
|
OriginType,
|
|
@@ -490,24 +491,32 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
490
491
|
self, context: RequestContext, request: ReplicateKeyRequest
|
|
491
492
|
) -> ReplicateKeyResponse:
|
|
492
493
|
account_id = context.account_id
|
|
493
|
-
|
|
494
|
-
key_id =
|
|
495
|
-
|
|
494
|
+
primary_key = self._get_kms_key(account_id, context.region, request.get("KeyId"))
|
|
495
|
+
key_id = primary_key.metadata.get("KeyId")
|
|
496
|
+
key_arn = primary_key.metadata.get("Arn")
|
|
497
|
+
if not primary_key.metadata.get("MultiRegion"):
|
|
496
498
|
raise UnsupportedOperationException(
|
|
497
499
|
f"Unable to replicate a non-MultiRegion key {key_id}"
|
|
498
500
|
)
|
|
499
501
|
replica_region = request.get("ReplicaRegion")
|
|
500
502
|
replicate_to_store = kms_stores[account_id][replica_region]
|
|
503
|
+
|
|
504
|
+
if (
|
|
505
|
+
primary_key.metadata.get("MultiRegionConfiguration", {}).get("MultiRegionKeyType")
|
|
506
|
+
!= MultiRegionKeyType.PRIMARY
|
|
507
|
+
):
|
|
508
|
+
raise UnsupportedOperationException(f"{key_arn} is not a multi-region primary key.")
|
|
509
|
+
|
|
501
510
|
if key_id in replicate_to_store.keys:
|
|
502
511
|
raise AlreadyExistsException(
|
|
503
512
|
f"Unable to replicate key {key_id} to region {replica_region}, as the key "
|
|
504
513
|
f"already exist there"
|
|
505
514
|
)
|
|
506
|
-
replica_key = copy.deepcopy(
|
|
515
|
+
replica_key = copy.deepcopy(primary_key)
|
|
507
516
|
replica_key.replicate_metadata(request, account_id, replica_region)
|
|
508
517
|
replicate_to_store.keys[key_id] = replica_key
|
|
509
518
|
|
|
510
|
-
self.update_primary_key_with_replica_keys(
|
|
519
|
+
self.update_primary_key_with_replica_keys(primary_key, replica_key, replica_region)
|
|
511
520
|
|
|
512
521
|
return ReplicateKeyResponse(ReplicaKeyMetadata=replica_key.metadata)
|
|
513
522
|
|
|
@@ -8,6 +8,7 @@ import re
|
|
|
8
8
|
import string
|
|
9
9
|
from typing import TYPE_CHECKING, Any
|
|
10
10
|
|
|
11
|
+
from localstack import config
|
|
11
12
|
from localstack.aws.api import CommonServiceException, RequestContext
|
|
12
13
|
from localstack.aws.api import lambda_ as api_spec
|
|
13
14
|
from localstack.aws.api.lambda_ import (
|
|
@@ -665,7 +666,9 @@ def validate_and_set_batch_size(service: str, batch_size: int | None = None) ->
|
|
|
665
666
|
def map_layer_out(layer_version: "LayerVersion") -> PublishLayerVersionResponse:
|
|
666
667
|
return PublishLayerVersionResponse(
|
|
667
668
|
Content=LayerVersionContentOutput(
|
|
668
|
-
Location=layer_version.code.generate_presigned_url(
|
|
669
|
+
Location=layer_version.code.generate_presigned_url(
|
|
670
|
+
endpoint_url=config.external_service_url()
|
|
671
|
+
),
|
|
669
672
|
CodeSha256=layer_version.code.code_sha256,
|
|
670
673
|
CodeSize=layer_version.code.code_size,
|
|
671
674
|
# SigningProfileVersionArn="", # same as in function configuration
|
|
@@ -715,12 +718,12 @@ def validate_layer_runtimes_and_architectures(
|
|
|
715
718
|
|
|
716
719
|
if compatible_runtimes and set(compatible_runtimes).difference(ALL_RUNTIMES):
|
|
717
720
|
constraint = f"Member must satisfy enum value set: {VALID_RUNTIMES}"
|
|
718
|
-
validation_msg = f"Value '[{', '.join(
|
|
721
|
+
validation_msg = f"Value '[{', '.join(list(compatible_runtimes))}]' at 'compatibleRuntimes' failed to satisfy constraint: {constraint}"
|
|
719
722
|
validations.append(validation_msg)
|
|
720
723
|
|
|
721
724
|
if compatible_architectures and set(compatible_architectures).difference(ARCHITECTURES):
|
|
722
725
|
constraint = "[Member must satisfy enum value set: [x86_64, arm64]]"
|
|
723
|
-
validation_msg = f"Value '[{', '.join(
|
|
726
|
+
validation_msg = f"Value '[{', '.join(list(compatible_architectures))}]' at 'compatibleArchitectures' failed to satisfy constraint: Member must satisfy constraint: {constraint}"
|
|
724
727
|
validations.append(validation_msg)
|
|
725
728
|
|
|
726
729
|
return validations
|
|
@@ -280,7 +280,7 @@ class DockerRuntimeExecutor(RuntimeExecutor):
|
|
|
280
280
|
container_name: str
|
|
281
281
|
|
|
282
282
|
def __init__(self, id: str, function_version: FunctionVersion) -> None:
|
|
283
|
-
super(
|
|
283
|
+
super().__init__(id=id, function_version=function_version)
|
|
284
284
|
self.ip = None
|
|
285
285
|
self.executor_endpoint = ExecutorEndpoint(self.id)
|
|
286
286
|
self.container_name = self._generate_container_name()
|
|
@@ -42,7 +42,7 @@ def get_sqs_client(function_version: FunctionVersion, client_config=None):
|
|
|
42
42
|
# TODO: remove once DLQ handling is refactored following the removal of the legacy lambda provider
|
|
43
43
|
class LegacyInvocationException(Exception):
|
|
44
44
|
def __init__(self, message, log_output=None, result=None):
|
|
45
|
-
super(
|
|
45
|
+
super().__init__(message)
|
|
46
46
|
self.log_output = log_output
|
|
47
47
|
self.result = result
|
|
48
48
|
|
|
@@ -19,13 +19,9 @@ from localstack.aws.api.sqs import (
|
|
|
19
19
|
String,
|
|
20
20
|
TagMap,
|
|
21
21
|
)
|
|
22
|
-
from localstack.services.sqs.models import SqsQueue, StandardQueue
|
|
23
|
-
from localstack.services.sqs.provider import
|
|
24
|
-
|
|
25
|
-
_create_message_attribute_hash,
|
|
26
|
-
to_sqs_api_message,
|
|
27
|
-
)
|
|
28
|
-
from localstack.services.sqs.utils import generate_message_id
|
|
22
|
+
from localstack.services.sqs.models import SqsQueue, StandardQueue, to_sqs_api_message
|
|
23
|
+
from localstack.services.sqs.provider import QueueUpdateWorker
|
|
24
|
+
from localstack.services.sqs.utils import create_message_attribute_hash, generate_message_id
|
|
29
25
|
from localstack.utils.objects import singleton_factory
|
|
30
26
|
from localstack.utils.strings import md5
|
|
31
27
|
from localstack.utils.time import now
|
|
@@ -189,7 +185,7 @@ class FakeSqsClient:
|
|
|
189
185
|
MD5OfBody=md5(MessageBody),
|
|
190
186
|
Body=MessageBody,
|
|
191
187
|
Attributes=self._create_message_attributes(MessageSystemAttributes),
|
|
192
|
-
MD5OfMessageAttributes=
|
|
188
|
+
MD5OfMessageAttributes=create_message_attribute_hash(MessageAttributes),
|
|
193
189
|
MessageAttributes=MessageAttributes,
|
|
194
190
|
)
|
|
195
191
|
queue_item = queue.put(
|
|
@@ -204,7 +200,7 @@ class FakeSqsClient:
|
|
|
204
200
|
"MD5OfMessageBody": message["MD5OfBody"],
|
|
205
201
|
"MD5OfMessageAttributes": message.get("MD5OfMessageAttributes"),
|
|
206
202
|
"SequenceNumber": queue_item.sequence_number,
|
|
207
|
-
"MD5OfMessageSystemAttributes":
|
|
203
|
+
"MD5OfMessageSystemAttributes": create_message_attribute_hash(MessageSystemAttributes),
|
|
208
204
|
}
|
|
209
205
|
|
|
210
206
|
|
|
@@ -14,6 +14,7 @@ from datetime import datetime
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from typing import IO, Literal, TypedDict
|
|
16
16
|
|
|
17
|
+
import boto3
|
|
17
18
|
from botocore.exceptions import ClientError
|
|
18
19
|
|
|
19
20
|
from localstack import config
|
|
@@ -40,7 +41,7 @@ from localstack.aws.api.lambda_ import (
|
|
|
40
41
|
TracingMode,
|
|
41
42
|
)
|
|
42
43
|
from localstack.aws.connect import connect_to
|
|
43
|
-
from localstack.constants import AWS_REGION_US_EAST_1
|
|
44
|
+
from localstack.constants import AWS_REGION_US_EAST_1, INTERNAL_AWS_SECRET_ACCESS_KEY
|
|
44
45
|
from localstack.services.lambda_.api_utils import qualified_lambda_arn, unqualified_lambda_arn
|
|
45
46
|
from localstack.utils.archives import unzip
|
|
46
47
|
from localstack.utils.strings import long_uid, short_uid
|
|
@@ -74,7 +75,7 @@ InitializationType = Literal["on-demand", "provisioned-concurrency"]
|
|
|
74
75
|
|
|
75
76
|
class ArchiveCode(metaclass=ABCMeta):
|
|
76
77
|
@abstractmethod
|
|
77
|
-
def generate_presigned_url(self, endpoint_url: str
|
|
78
|
+
def generate_presigned_url(self, endpoint_url: str):
|
|
78
79
|
"""
|
|
79
80
|
Generates a presigned url pointing to the code archive
|
|
80
81
|
"""
|
|
@@ -168,15 +169,17 @@ class S3Code(ArchiveCode):
|
|
|
168
169
|
)
|
|
169
170
|
target_file.flush()
|
|
170
171
|
|
|
171
|
-
def generate_presigned_url(self, endpoint_url: str
|
|
172
|
+
def generate_presigned_url(self, endpoint_url: str) -> str:
|
|
172
173
|
"""
|
|
173
174
|
Generates a presigned url pointing to the code archive
|
|
174
175
|
"""
|
|
175
|
-
s3_client =
|
|
176
|
+
s3_client = boto3.client(
|
|
177
|
+
"s3",
|
|
176
178
|
region_name=AWS_REGION_US_EAST_1,
|
|
177
179
|
aws_access_key_id=config.INTERNAL_RESOURCE_ACCOUNT,
|
|
180
|
+
aws_secret_access_key=INTERNAL_AWS_SECRET_ACCESS_KEY,
|
|
178
181
|
endpoint_url=endpoint_url,
|
|
179
|
-
)
|
|
182
|
+
)
|
|
180
183
|
params = {"Bucket": self.s3_bucket, "Key": self.s3_key}
|
|
181
184
|
if self.s3_object_version:
|
|
182
185
|
params["VersionId"] = self.s3_object_version
|
|
@@ -257,8 +260,8 @@ class HotReloadingCode(ArchiveCode):
|
|
|
257
260
|
code_sha256: str = "hot-reloading-hash-not-available"
|
|
258
261
|
code_size: int = 0
|
|
259
262
|
|
|
260
|
-
def generate_presigned_url(self, endpoint_url: str
|
|
261
|
-
return f"
|
|
263
|
+
def generate_presigned_url(self, endpoint_url: str) -> str:
|
|
264
|
+
return f"file://{self.host_path}"
|
|
262
265
|
|
|
263
266
|
def get_unzipped_code_location(self) -> Path:
|
|
264
267
|
path = os.path.expandvars(self.host_path)
|
|
@@ -478,7 +478,11 @@ class LambdaService:
|
|
|
478
478
|
] = new_version_state
|
|
479
479
|
|
|
480
480
|
except Exception:
|
|
481
|
-
LOG.
|
|
481
|
+
LOG.error(
|
|
482
|
+
"Failed to update function version for arn %s",
|
|
483
|
+
function_arn,
|
|
484
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
485
|
+
)
|
|
482
486
|
|
|
483
487
|
def update_alias(self, old_alias: VersionAlias, new_alias: VersionAlias, function: Function):
|
|
484
488
|
# if pointer changed, need to restart provisioned
|
|
@@ -12,7 +12,7 @@ from localstack.utils.platform import get_arch
|
|
|
12
12
|
"""Customized LocalStack version of the AWS Lambda Runtime Interface Emulator (RIE).
|
|
13
13
|
https://github.com/localstack/lambda-runtime-init/blob/localstack/README-LOCALSTACK.md
|
|
14
14
|
"""
|
|
15
|
-
LAMBDA_RUNTIME_DEFAULT_VERSION = "v0.1.
|
|
15
|
+
LAMBDA_RUNTIME_DEFAULT_VERSION = "v0.1.36-pre"
|
|
16
16
|
LAMBDA_RUNTIME_VERSION = config.LAMBDA_INIT_RELEASE_VERSION or LAMBDA_RUNTIME_DEFAULT_VERSION
|
|
17
17
|
LAMBDA_RUNTIME_INIT_URL = "https://github.com/localstack/lambda-runtime-init/releases/download/{version}/aws-lambda-rie-{arch}"
|
|
18
18
|
|
|
@@ -313,7 +313,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
|
|
|
313
313
|
LOG.warning(
|
|
314
314
|
"Failed to restore function version %s",
|
|
315
315
|
fn_version.id.qualified_arn(),
|
|
316
|
-
exc_info=
|
|
316
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
317
317
|
)
|
|
318
318
|
# restore provisioned concurrency per function considering both versions and aliases
|
|
319
319
|
for (
|
|
@@ -344,7 +344,7 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
|
|
|
344
344
|
"Failed to restore provisioned concurrency %s for function %s",
|
|
345
345
|
provisioned_config,
|
|
346
346
|
fn_arn,
|
|
347
|
-
exc_info=
|
|
347
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
348
348
|
)
|
|
349
349
|
|
|
350
350
|
for esm in state.event_source_mappings.values():
|
|
@@ -1512,7 +1512,8 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
|
|
|
1512
1512
|
code_location = None
|
|
1513
1513
|
if code := version.config.code:
|
|
1514
1514
|
code_location = FunctionCodeLocation(
|
|
1515
|
-
Location=code.generate_presigned_url(),
|
|
1515
|
+
Location=code.generate_presigned_url(endpoint_url=config.external_service_url()),
|
|
1516
|
+
RepositoryType="S3",
|
|
1516
1517
|
)
|
|
1517
1518
|
elif image := version.config.image:
|
|
1518
1519
|
code_location = FunctionCodeLocation(
|
|
@@ -74,7 +74,7 @@ class LambdaLayerVersionIdentifier(ResourceIdentifier):
|
|
|
74
74
|
resource = "layer-version"
|
|
75
75
|
|
|
76
76
|
def __init__(self, account_id: str, region: str, layer_name: str):
|
|
77
|
-
super(
|
|
77
|
+
super().__init__(account_id, region, layer_name)
|
|
78
78
|
|
|
79
79
|
def generate(
|
|
80
80
|
self, existing_ids: ExistingIds = None, tags: Tags = None, next_version: int = None
|
|
@@ -22,10 +22,13 @@ from localstack.aws.api.logs import (
|
|
|
22
22
|
InputLogEvents,
|
|
23
23
|
InvalidParameterException,
|
|
24
24
|
KmsKeyId,
|
|
25
|
+
ListLogGroupsRequest,
|
|
26
|
+
ListLogGroupsResponse,
|
|
25
27
|
ListTagsForResourceResponse,
|
|
26
28
|
ListTagsLogGroupResponse,
|
|
27
29
|
LogGroupClass,
|
|
28
30
|
LogGroupName,
|
|
31
|
+
LogGroupSummary,
|
|
29
32
|
LogsApi,
|
|
30
33
|
LogStreamName,
|
|
31
34
|
PutLogEventsResponse,
|
|
@@ -43,7 +46,7 @@ from localstack.services.plugins import ServiceLifecycleHook
|
|
|
43
46
|
from localstack.utils.aws import arns
|
|
44
47
|
from localstack.utils.aws.client_types import ServicePrincipal
|
|
45
48
|
from localstack.utils.bootstrap import is_api_enabled
|
|
46
|
-
from localstack.utils.
|
|
49
|
+
from localstack.utils.numbers import is_number
|
|
47
50
|
from localstack.utils.patch import patch
|
|
48
51
|
|
|
49
52
|
LOG = logging.getLogger(__name__)
|
|
@@ -60,8 +63,8 @@ class LogsProvider(LogsApi, ServiceLifecycleHook):
|
|
|
60
63
|
log_group_name: LogGroupName,
|
|
61
64
|
log_stream_name: LogStreamName,
|
|
62
65
|
log_events: InputLogEvents,
|
|
63
|
-
sequence_token: SequenceToken = None,
|
|
64
|
-
entity: Entity = None,
|
|
66
|
+
sequence_token: SequenceToken | None = None,
|
|
67
|
+
entity: Entity | None = None,
|
|
65
68
|
**kwargs,
|
|
66
69
|
) -> PutLogEventsResponse:
|
|
67
70
|
logs_backend = get_moto_logs_backend(context.account_id, context.region)
|
|
@@ -97,33 +100,32 @@ class LogsProvider(LogsApi, ServiceLifecycleHook):
|
|
|
97
100
|
) -> DescribeLogGroupsResponse:
|
|
98
101
|
region_backend = get_moto_logs_backend(context.account_id, context.region)
|
|
99
102
|
|
|
100
|
-
prefix: str = request.get("logGroupNamePrefix", "")
|
|
101
|
-
pattern: str = request.get("logGroupNamePattern", "")
|
|
103
|
+
prefix: str | None = request.get("logGroupNamePrefix", "")
|
|
104
|
+
pattern: str | None = request.get("logGroupNamePattern", "")
|
|
102
105
|
|
|
103
106
|
if pattern and prefix:
|
|
104
107
|
raise InvalidParameterException(
|
|
105
108
|
"LogGroup name prefix and LogGroup name pattern are mutually exclusive parameters."
|
|
106
109
|
)
|
|
107
110
|
|
|
108
|
-
|
|
111
|
+
moto_groups = copy.deepcopy(dict(region_backend.groups)).values()
|
|
109
112
|
|
|
110
113
|
groups = [
|
|
111
|
-
group.to_describe_dict()
|
|
112
|
-
for
|
|
114
|
+
{"logGroupClass": LogGroupClass.STANDARD} | group.to_describe_dict()
|
|
115
|
+
for group in sorted(moto_groups, key=lambda g: g.name)
|
|
113
116
|
if not (prefix or pattern)
|
|
114
|
-
or (prefix and name.startswith(prefix))
|
|
115
|
-
or (pattern and pattern in name)
|
|
117
|
+
or (prefix and group.name.startswith(prefix))
|
|
118
|
+
or (pattern and pattern in group.name)
|
|
116
119
|
]
|
|
117
120
|
|
|
118
|
-
groups = sorted(groups, key=lambda x: x["logGroupName"])
|
|
119
121
|
return DescribeLogGroupsResponse(logGroups=groups)
|
|
120
122
|
|
|
121
123
|
@handler("DescribeLogStreams", expand=False)
|
|
122
124
|
def describe_log_streams(
|
|
123
125
|
self, context: RequestContext, request: DescribeLogStreamsRequest
|
|
124
126
|
) -> DescribeLogStreamsResponse:
|
|
125
|
-
log_group_name: str = request.get("logGroupName")
|
|
126
|
-
log_group_identifier: str = request.get("logGroupIdentifier")
|
|
127
|
+
log_group_name: str | None = request.get("logGroupName")
|
|
128
|
+
log_group_identifier: str | None = request.get("logGroupIdentifier")
|
|
127
129
|
|
|
128
130
|
if log_group_identifier and log_group_name:
|
|
129
131
|
raise CommonServiceException(
|
|
@@ -138,13 +140,29 @@ class LogsProvider(LogsApi, ServiceLifecycleHook):
|
|
|
138
140
|
|
|
139
141
|
return moto.call_moto_with_request(context, request_copy)
|
|
140
142
|
|
|
143
|
+
@handler("ListLogGroups", expand=False)
|
|
144
|
+
def list_log_groups(
|
|
145
|
+
self, context: RequestContext, request: ListLogGroupsRequest
|
|
146
|
+
) -> ListLogGroupsResponse:
|
|
147
|
+
pattern: str | None = request.get("logGroupNamePattern")
|
|
148
|
+
region_backend: LogsBackend = get_moto_logs_backend(context.account_id, context.region)
|
|
149
|
+
moto_groups = copy.deepcopy(region_backend.groups).values()
|
|
150
|
+
groups = [
|
|
151
|
+
LogGroupSummary(
|
|
152
|
+
logGroupName=group.name, logGroupArn=group.arn, logGroupClass=LogGroupClass.STANDARD
|
|
153
|
+
)
|
|
154
|
+
for group in sorted(moto_groups, key=lambda g: g.name)
|
|
155
|
+
if not pattern or pattern in group.name
|
|
156
|
+
]
|
|
157
|
+
return ListLogGroupsResponse(logGroups=groups)
|
|
158
|
+
|
|
141
159
|
def create_log_group(
|
|
142
160
|
self,
|
|
143
161
|
context: RequestContext,
|
|
144
162
|
log_group_name: LogGroupName,
|
|
145
|
-
kms_key_id: KmsKeyId = None,
|
|
146
|
-
tags: Tags = None,
|
|
147
|
-
log_group_class: LogGroupClass = None,
|
|
163
|
+
kms_key_id: KmsKeyId | None = None,
|
|
164
|
+
tags: Tags | None = None,
|
|
165
|
+
log_group_class: LogGroupClass | None = None,
|
|
148
166
|
**kwargs,
|
|
149
167
|
) -> None:
|
|
150
168
|
call_moto(context)
|
|
@@ -442,10 +460,9 @@ def moto_to_describe_dict(target, self):
|
|
|
442
460
|
# reported race condition in https://github.com/localstack/localstack/issues/8011
|
|
443
461
|
# making copy of "streams" dict here to avoid issues while summing up storedBytes
|
|
444
462
|
copy_streams = copy.deepcopy(self.streams)
|
|
445
|
-
# parity tests shows that the arn ends with ":*"
|
|
446
|
-
arn = self.arn if self.arn.endswith(":*") else f"{self.arn}:*"
|
|
447
463
|
log_group = {
|
|
448
|
-
"arn": arn,
|
|
464
|
+
"arn": f"{self.arn}:*",
|
|
465
|
+
"logGroupArn": self.arn,
|
|
449
466
|
"creationTime": self.creation_time,
|
|
450
467
|
"logGroupName": self.name,
|
|
451
468
|
"metricFilterCount": 0,
|
localstack/services/moto.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
This module provides tools to call
|
|
2
|
+
This module provides tools to call Moto service implementations.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import copy
|
|
@@ -65,6 +65,7 @@ def call_moto_with_request(
|
|
|
65
65
|
action=context.operation.name,
|
|
66
66
|
parameters=service_request,
|
|
67
67
|
region=context.region,
|
|
68
|
+
protocol=context.protocol,
|
|
68
69
|
)
|
|
69
70
|
# we keep the headers from the original request, but override them with the ones created from the `service_request`
|
|
70
71
|
headers = copy.deepcopy(context.request.headers)
|
|
@@ -18,7 +18,12 @@ from localstack.http.client import SimpleRequestsClient
|
|
|
18
18
|
from localstack.http.proxy import ProxyHandler
|
|
19
19
|
from localstack.services.edge import ROUTER
|
|
20
20
|
from localstack.services.opensearch import versions
|
|
21
|
-
from localstack.services.opensearch.packages import
|
|
21
|
+
from localstack.services.opensearch.packages import (
|
|
22
|
+
ELASTICSEARCH_DEFAULT_VERSION,
|
|
23
|
+
OPENSEARCH_DEFAULT_VERSION,
|
|
24
|
+
elasticsearch_package,
|
|
25
|
+
opensearch_package,
|
|
26
|
+
)
|
|
22
27
|
from localstack.utils.aws.arns import parse_arn
|
|
23
28
|
from localstack.utils.common import (
|
|
24
29
|
ShellCommandThread,
|
|
@@ -37,6 +42,9 @@ LOG = logging.getLogger(__name__)
|
|
|
37
42
|
INTERNAL_USER_AUTH = ("localstack-internal", "localstack-internal")
|
|
38
43
|
DEFAULT_BACKEND_HOST = "127.0.0.1"
|
|
39
44
|
|
|
45
|
+
# user that starts the opensearch process if the current user is root
|
|
46
|
+
OS_USER_OPENSEARCH = "localstack"
|
|
47
|
+
|
|
40
48
|
CommandSettings = dict[str, str]
|
|
41
49
|
|
|
42
50
|
|
|
@@ -314,7 +322,7 @@ class OpensearchCluster(Server):
|
|
|
314
322
|
|
|
315
323
|
@property
|
|
316
324
|
def default_version(self) -> str:
|
|
317
|
-
return
|
|
325
|
+
return OPENSEARCH_DEFAULT_VERSION
|
|
318
326
|
|
|
319
327
|
@property
|
|
320
328
|
def version(self) -> str:
|
|
@@ -336,7 +344,7 @@ class OpensearchCluster(Server):
|
|
|
336
344
|
|
|
337
345
|
@property
|
|
338
346
|
def os_user(self):
|
|
339
|
-
return
|
|
347
|
+
return OS_USER_OPENSEARCH
|
|
340
348
|
|
|
341
349
|
def health(self) -> str | None:
|
|
342
350
|
return get_cluster_health_status(self.url, auth=self.auth)
|
|
@@ -580,7 +588,7 @@ class EdgeProxiedOpensearchCluster(Server):
|
|
|
580
588
|
|
|
581
589
|
@property
|
|
582
590
|
def default_version(self):
|
|
583
|
-
return
|
|
591
|
+
return OPENSEARCH_DEFAULT_VERSION
|
|
584
592
|
|
|
585
593
|
@property
|
|
586
594
|
def url(self) -> str:
|
|
@@ -658,7 +666,7 @@ class ElasticsearchCluster(OpensearchCluster):
|
|
|
658
666
|
|
|
659
667
|
@property
|
|
660
668
|
def default_version(self) -> str:
|
|
661
|
-
return
|
|
669
|
+
return ELASTICSEARCH_DEFAULT_VERSION
|
|
662
670
|
|
|
663
671
|
@property
|
|
664
672
|
def bin_name(self) -> str:
|
|
@@ -666,7 +674,7 @@ class ElasticsearchCluster(OpensearchCluster):
|
|
|
666
674
|
|
|
667
675
|
@property
|
|
668
676
|
def os_user(self):
|
|
669
|
-
return
|
|
677
|
+
return OS_USER_OPENSEARCH
|
|
670
678
|
|
|
671
679
|
def _ensure_installed(self):
|
|
672
680
|
elasticsearch_package.install(self.version)
|
|
@@ -710,7 +718,7 @@ class ElasticsearchCluster(OpensearchCluster):
|
|
|
710
718
|
class EdgeProxiedElasticsearchCluster(EdgeProxiedOpensearchCluster):
|
|
711
719
|
@property
|
|
712
720
|
def default_version(self):
|
|
713
|
-
return
|
|
721
|
+
return ELASTICSEARCH_DEFAULT_VERSION
|
|
714
722
|
|
|
715
723
|
def _backend_cluster(self) -> OpensearchCluster:
|
|
716
724
|
return ElasticsearchCluster(
|
|
@@ -9,13 +9,6 @@ import threading
|
|
|
9
9
|
import semver
|
|
10
10
|
|
|
11
11
|
from localstack import config
|
|
12
|
-
from localstack.constants import (
|
|
13
|
-
ELASTICSEARCH_DEFAULT_VERSION,
|
|
14
|
-
ELASTICSEARCH_DELETE_MODULES,
|
|
15
|
-
ELASTICSEARCH_PLUGIN_LIST,
|
|
16
|
-
OPENSEARCH_DEFAULT_VERSION,
|
|
17
|
-
OPENSEARCH_PLUGIN_LIST,
|
|
18
|
-
)
|
|
19
12
|
from localstack.packages import InstallTarget, Package, PackageInstaller
|
|
20
13
|
from localstack.packages.java import java_package
|
|
21
14
|
from localstack.services.opensearch import versions
|
|
@@ -32,6 +25,32 @@ from localstack.utils.sync import SynchronizedDefaultDict, retry
|
|
|
32
25
|
|
|
33
26
|
LOG = logging.getLogger(__name__)
|
|
34
27
|
|
|
28
|
+
# the version of opensearch which is used by default
|
|
29
|
+
OPENSEARCH_DEFAULT_VERSION = "OpenSearch_3.1"
|
|
30
|
+
|
|
31
|
+
# See https://docs.aws.amazon.com/opensearch-service/latest/developerguide/supported-plugins.html
|
|
32
|
+
OPENSEARCH_PLUGIN_LIST = [
|
|
33
|
+
"ingest-attachment",
|
|
34
|
+
"analysis-kuromoji",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
# the version of elasticsearch that is pre-seeded into the base image (sync with Dockerfile.base)
|
|
38
|
+
ELASTICSEARCH_DEFAULT_VERSION = "Elasticsearch_7.10"
|
|
39
|
+
|
|
40
|
+
# See https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/aes-supported-plugins.html
|
|
41
|
+
ELASTICSEARCH_PLUGIN_LIST = [
|
|
42
|
+
"analysis-icu",
|
|
43
|
+
"ingest-attachment",
|
|
44
|
+
"analysis-kuromoji",
|
|
45
|
+
"mapper-murmur3",
|
|
46
|
+
"mapper-size",
|
|
47
|
+
"analysis-phonetic",
|
|
48
|
+
"analysis-smartcn",
|
|
49
|
+
"analysis-stempel",
|
|
50
|
+
"analysis-ukrainian",
|
|
51
|
+
]
|
|
52
|
+
# Default ES modules to exclude (save apprx 66MB in the final image)
|
|
53
|
+
ELASTICSEARCH_DELETE_MODULES = ["ingest-geoip"]
|
|
35
54
|
|
|
36
55
|
_OPENSEARCH_INSTALL_LOCKS = SynchronizedDefaultDict(threading.RLock)
|
|
37
56
|
|