localstack-core 4.13.2.dev93__py3-none-any.whl → 4.13.2.dev95__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/services/kms/models.py +16 -41
- localstack/services/kms/provider.py +36 -21
- localstack/services/kms/utils.py +64 -2
- localstack/services/lambda_/invocation/models.py +2 -2
- localstack/services/lambda_/packages.py +1 -1
- localstack/services/lambda_/provider.py +12 -15
- localstack/services/opensearch/models.py +2 -5
- localstack/services/opensearch/provider.py +7 -9
- localstack/services/route53/models.py +3 -1
- localstack/services/route53/provider.py +5 -0
- localstack/services/s3/models.py +2 -4
- localstack/services/s3/provider.py +67 -61
- localstack/services/s3control/provider.py +10 -24
- localstack/services/sns/models.py +2 -2
- localstack/services/sns/provider.py +8 -9
- localstack/services/sqs/models.py +3 -0
- localstack/services/sqs/provider.py +22 -25
- localstack/utils/tagging.py +99 -1
- localstack/version.py +2 -2
- {localstack_core-4.13.2.dev93.dist-info → localstack_core-4.13.2.dev95.dist-info}/METADATA +1 -1
- {localstack_core-4.13.2.dev93.dist-info → localstack_core-4.13.2.dev95.dist-info}/RECORD +26 -26
- {localstack_core-4.13.2.dev93.data → localstack_core-4.13.2.dev95.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.13.2.dev93.dist-info → localstack_core-4.13.2.dev95.dist-info}/WHEEL +0 -0
- {localstack_core-4.13.2.dev93.dist-info → localstack_core-4.13.2.dev95.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.13.2.dev93.dist-info → localstack_core-4.13.2.dev95.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.13.2.dev93.dist-info → localstack_core-4.13.2.dev95.dist-info}/top_level.txt +0 -0
|
@@ -45,16 +45,15 @@ from localstack.aws.api.kms import (
|
|
|
45
45
|
OriginType,
|
|
46
46
|
ReplicateKeyRequest,
|
|
47
47
|
SigningAlgorithmSpec,
|
|
48
|
-
TagList,
|
|
49
48
|
UnsupportedOperationException,
|
|
50
49
|
)
|
|
51
|
-
from localstack.constants import TAG_KEY_CUSTOM_ID
|
|
52
50
|
from localstack.services.kms.exceptions import ValidationException
|
|
53
|
-
from localstack.services.kms.utils import is_valid_key_arn
|
|
51
|
+
from localstack.services.kms.utils import is_valid_key_arn
|
|
54
52
|
from localstack.services.stores import AccountRegionBundle, BaseStore, LocalAttribute
|
|
55
53
|
from localstack.utils.aws.arns import get_partition, kms_alias_arn, kms_key_arn
|
|
56
54
|
from localstack.utils.crypto import decrypt, encrypt
|
|
57
55
|
from localstack.utils.strings import long_uid, to_bytes, to_str
|
|
56
|
+
from localstack.utils.tagging import Tags
|
|
58
57
|
|
|
59
58
|
LOG = logging.getLogger(__name__)
|
|
60
59
|
|
|
@@ -114,9 +113,6 @@ RESERVED_ALIASES = [
|
|
|
114
113
|
# list of key names that should be skipped when serializing the encryption context
|
|
115
114
|
IGNORED_CONTEXT_KEYS = ["aws-crypto-public-key"]
|
|
116
115
|
|
|
117
|
-
# special tag name to allow specifying a custom key material for created keys
|
|
118
|
-
TAG_KEY_CUSTOM_KEY_MATERIAL = "_custom_key_material_"
|
|
119
|
-
|
|
120
116
|
|
|
121
117
|
def _serialize_ciphertext_blob(ciphertext: Ciphertext) -> bytes:
|
|
122
118
|
header = struct.pack(
|
|
@@ -289,7 +285,6 @@ class KmsCryptoKey:
|
|
|
289
285
|
class KmsKey:
|
|
290
286
|
metadata: KeyMetadata
|
|
291
287
|
crypto_key: KmsCryptoKey
|
|
292
|
-
tags: dict[str, str]
|
|
293
288
|
policy: str
|
|
294
289
|
is_key_rotation_enabled: bool
|
|
295
290
|
rotation_period_in_days: int
|
|
@@ -302,18 +297,13 @@ class KmsKey:
|
|
|
302
297
|
create_key_request: CreateKeyRequest = None,
|
|
303
298
|
account_id: str = None,
|
|
304
299
|
region: str = None,
|
|
300
|
+
custom_key_material: bytes | None = None,
|
|
301
|
+
custom_key_id: str | None = None,
|
|
305
302
|
):
|
|
306
303
|
create_key_request = create_key_request or CreateKeyRequest()
|
|
307
304
|
self.previous_keys = []
|
|
308
305
|
|
|
309
|
-
#
|
|
310
|
-
# least in the sense of DescribeKey not returning them with the rest of the metadata. Instead, tags are more
|
|
311
|
-
# like aliases:
|
|
312
|
-
# https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html
|
|
313
|
-
# "DescribeKey does not return the following information: ... Tags on the KMS key."
|
|
314
|
-
self.tags = {}
|
|
315
|
-
self.add_tags(create_key_request.get("Tags"))
|
|
316
|
-
# Same goes for the policy. It is in the request, but not in the metadata.
|
|
306
|
+
# Policy is in the request but not in the metadata.
|
|
317
307
|
self.policy = create_key_request.get("Policy") or self._get_default_key_policy(
|
|
318
308
|
account_id, region
|
|
319
309
|
)
|
|
@@ -322,13 +312,7 @@ class KmsKey:
|
|
|
322
312
|
# disable it."
|
|
323
313
|
self.is_key_rotation_enabled = False
|
|
324
314
|
|
|
325
|
-
self._populate_metadata(create_key_request, account_id, region)
|
|
326
|
-
custom_key_material = None
|
|
327
|
-
if TAG_KEY_CUSTOM_KEY_MATERIAL in self.tags:
|
|
328
|
-
# check if the _custom_key_material_ tag is specified, to use a custom key material for this key
|
|
329
|
-
custom_key_material = base64.b64decode(self.tags[TAG_KEY_CUSTOM_KEY_MATERIAL])
|
|
330
|
-
# remove the _custom_key_material_ tag from the tags to not readily expose the custom key material
|
|
331
|
-
del self.tags[TAG_KEY_CUSTOM_KEY_MATERIAL]
|
|
315
|
+
self._populate_metadata(create_key_request, account_id, region, custom_key_id=custom_key_id)
|
|
332
316
|
self.crypto_key = KmsCryptoKey(self.metadata.get("KeySpec"), custom_key_material)
|
|
333
317
|
self._internal_key_id = uuid.uuid4()
|
|
334
318
|
|
|
@@ -560,7 +544,11 @@ class KmsKey:
|
|
|
560
544
|
#
|
|
561
545
|
# Data keys are symmetric, data key pairs are asymmetric.
|
|
562
546
|
def _populate_metadata(
|
|
563
|
-
self,
|
|
547
|
+
self,
|
|
548
|
+
create_key_request: CreateKeyRequest,
|
|
549
|
+
account_id: str,
|
|
550
|
+
region: str,
|
|
551
|
+
custom_key_id: str | None = None,
|
|
564
552
|
) -> None:
|
|
565
553
|
self.metadata = KeyMetadata()
|
|
566
554
|
# Metadata fields coming from a creation request
|
|
@@ -597,9 +585,8 @@ class KmsKey:
|
|
|
597
585
|
else KeyState.PendingImport
|
|
598
586
|
)
|
|
599
587
|
|
|
600
|
-
if
|
|
601
|
-
|
|
602
|
-
self.metadata["KeyId"] = self.tags[TAG_KEY_CUSTOM_ID].strip()
|
|
588
|
+
if custom_key_id:
|
|
589
|
+
self.metadata["KeyId"] = custom_key_id
|
|
603
590
|
elif self.metadata.get("MultiRegion"):
|
|
604
591
|
# https://docs.aws.amazon.com/kms/latest/developerguide/multi-region-keys-overview.html
|
|
605
592
|
# "Notice that multi-Region keys have a distinctive key ID that begins with mrk-. You can use the mrk- prefix to
|
|
@@ -625,21 +612,6 @@ class KmsKey:
|
|
|
625
612
|
ReplicaKeys=[],
|
|
626
613
|
)
|
|
627
614
|
|
|
628
|
-
def add_tags(self, tags: TagList) -> None:
|
|
629
|
-
# Just in case we get None from somewhere.
|
|
630
|
-
if not tags:
|
|
631
|
-
return
|
|
632
|
-
|
|
633
|
-
validate_tag_list(tags)
|
|
634
|
-
|
|
635
|
-
# Do not care if we overwrite an existing tag:
|
|
636
|
-
# https://docs.aws.amazon.com/kms/latest/APIReference/API_TagResource.html
|
|
637
|
-
# "To edit a tag, specify an existing tag key and a new tag value."
|
|
638
|
-
for i, tag in enumerate(tags, start=1):
|
|
639
|
-
if tag.get("TagKey") != TAG_KEY_CUSTOM_KEY_MATERIAL:
|
|
640
|
-
validate_tag(i, tag)
|
|
641
|
-
self.tags[tag.get("TagKey")] = tag.get("TagValue")
|
|
642
|
-
|
|
643
615
|
def schedule_key_deletion(self, pending_window_in_days: int) -> None:
|
|
644
616
|
self.metadata["Enabled"] = False
|
|
645
617
|
# TODO For MultiRegion keys, the status of replicas get set to "PendingDeletion", while the primary key
|
|
@@ -870,5 +842,8 @@ class KmsStore(BaseStore):
|
|
|
870
842
|
# maps import tokens to import data
|
|
871
843
|
imports: dict[str, KeyImportState] = LocalAttribute(default=dict)
|
|
872
844
|
|
|
845
|
+
# maps key arn to tags
|
|
846
|
+
tags: Tags = LocalAttribute(default=Tags)
|
|
847
|
+
|
|
873
848
|
|
|
874
849
|
kms_stores = AccountRegionBundle("kms", KmsStore)
|
|
@@ -137,9 +137,12 @@ from localstack.services.kms.models import (
|
|
|
137
137
|
)
|
|
138
138
|
from localstack.services.kms.utils import (
|
|
139
139
|
execute_dry_run_capable,
|
|
140
|
+
get_custom_key_id,
|
|
141
|
+
get_custom_key_material,
|
|
140
142
|
is_valid_key_arn,
|
|
141
143
|
parse_key_arn,
|
|
142
144
|
validate_alias_name,
|
|
145
|
+
validate_and_filter_tags,
|
|
143
146
|
)
|
|
144
147
|
from localstack.services.plugins import ServiceLifecycleHook
|
|
145
148
|
from localstack.state import StateVisitor
|
|
@@ -229,7 +232,13 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
229
232
|
account_id: str, region_name: str, request: CreateKeyRequest = None
|
|
230
233
|
) -> KmsKey:
|
|
231
234
|
store = kms_stores[account_id][region_name]
|
|
232
|
-
key = KmsKey(
|
|
235
|
+
key = KmsKey(
|
|
236
|
+
request,
|
|
237
|
+
account_id,
|
|
238
|
+
region_name,
|
|
239
|
+
get_custom_key_material(request),
|
|
240
|
+
get_custom_key_id(request),
|
|
241
|
+
)
|
|
233
242
|
key_id = key.metadata["KeyId"]
|
|
234
243
|
store.keys[key_id] = key
|
|
235
244
|
return key
|
|
@@ -299,11 +308,9 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
299
308
|
store = kms_stores[account_id][region_name]
|
|
300
309
|
if alias_name not in RESERVED_ALIASES or alias_name in store.aliases:
|
|
301
310
|
return
|
|
302
|
-
create_key_request = {}
|
|
303
311
|
key_id = KmsProvider._create_kms_key(
|
|
304
312
|
account_id,
|
|
305
313
|
region_name,
|
|
306
|
-
create_key_request,
|
|
307
314
|
).metadata.get("KeyId")
|
|
308
315
|
create_alias_request = CreateAliasRequest(AliasName=alias_name, TargetKeyId=key_id)
|
|
309
316
|
KmsProvider._create_kms_alias(account_id, region_name, create_alias_request)
|
|
@@ -393,19 +400,25 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
393
400
|
def _is_rsa_spec(key_spec: str) -> bool:
|
|
394
401
|
return key_spec in [KeySpec.RSA_2048, KeySpec.RSA_3072, KeySpec.RSA_4096]
|
|
395
402
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
return [
|
|
403
|
+
def _get_key_tags(self, account_id: str, region: str, resource_arn: str) -> TagList:
|
|
404
|
+
store = self._get_store(account_id, region)
|
|
405
|
+
return [
|
|
406
|
+
Tag(TagKey=key, TagValue=value)
|
|
407
|
+
for key, value in store.tags.get_tags(resource_arn).items()
|
|
408
|
+
]
|
|
399
409
|
|
|
400
|
-
def _set_key_tags(self,
|
|
401
|
-
|
|
410
|
+
def _set_key_tags(self, account_id: str, region: str, resource_arn: str, tags: TagList) -> None:
|
|
411
|
+
validated_tags = validate_and_filter_tags(tags)
|
|
412
|
+
store = self._get_store(account_id, region)
|
|
413
|
+
store.tags.update_tags(
|
|
414
|
+
resource_arn, {tag["TagKey"]: tag["TagValue"] for tag in validated_tags}
|
|
415
|
+
)
|
|
402
416
|
|
|
403
417
|
def _remove_key_tags(
|
|
404
|
-
self,
|
|
418
|
+
self, account_id: str, region: str, resource_arn: str, tag_keys: TagKeyList
|
|
405
419
|
) -> None:
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
key.tags.pop(tag_key, None)
|
|
420
|
+
store = self._get_store(account_id, region)
|
|
421
|
+
store.tags.delete_tags(resource_arn, tag_keys)
|
|
409
422
|
|
|
410
423
|
#
|
|
411
424
|
# Operation Handlers
|
|
@@ -417,8 +430,10 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
417
430
|
context: RequestContext,
|
|
418
431
|
request: CreateKeyRequest = None,
|
|
419
432
|
) -> CreateKeyResponse:
|
|
433
|
+
tags = validate_and_filter_tags(request.get("Tags", []))
|
|
420
434
|
key = self._create_kms_key(context.account_id, context.region, request)
|
|
421
|
-
|
|
435
|
+
if tags:
|
|
436
|
+
self._set_key_tags(context.account_id, context.region, key.metadata["Arn"], tags)
|
|
422
437
|
return CreateKeyResponse(KeyMetadata=key.metadata)
|
|
423
438
|
|
|
424
439
|
@handler("ScheduleKeyDeletion", expand=False)
|
|
@@ -1487,7 +1502,9 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
1487
1502
|
key = self._get_kms_key(
|
|
1488
1503
|
context.account_id, context.region, request.get("KeyId"), any_key_state_allowed=True
|
|
1489
1504
|
)
|
|
1490
|
-
keys_list = PaginatedList(
|
|
1505
|
+
keys_list = PaginatedList(
|
|
1506
|
+
self._get_key_tags(context.account_id, context.region, key.metadata["Arn"])
|
|
1507
|
+
)
|
|
1491
1508
|
page, next_token = keys_list.get_page(
|
|
1492
1509
|
lambda tag: tag.get("TagKey"),
|
|
1493
1510
|
next_token=request.get("Marker"),
|
|
@@ -1519,7 +1536,6 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
1519
1536
|
|
|
1520
1537
|
@handler("TagResource", expand=False)
|
|
1521
1538
|
def tag_resource(self, context: RequestContext, request: TagResourceRequest) -> None:
|
|
1522
|
-
tags = request["Tags"]
|
|
1523
1539
|
key = self._get_kms_key(
|
|
1524
1540
|
context.account_id,
|
|
1525
1541
|
context.region,
|
|
@@ -1527,14 +1543,11 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
1527
1543
|
enabled_key_allowed=True,
|
|
1528
1544
|
disabled_key_allowed=True,
|
|
1529
1545
|
)
|
|
1530
|
-
|
|
1531
|
-
|
|
1546
|
+
if tags := request["Tags"]:
|
|
1547
|
+
self._set_key_tags(context.account_id, context.region, key.metadata["Arn"], tags)
|
|
1532
1548
|
|
|
1533
1549
|
@handler("UntagResource", expand=False)
|
|
1534
1550
|
def untag_resource(self, context: RequestContext, request: UntagResourceRequest) -> None:
|
|
1535
|
-
if not (tag_keys := request.get("TagKeys", [])):
|
|
1536
|
-
return
|
|
1537
|
-
|
|
1538
1551
|
key = self._get_kms_key(
|
|
1539
1552
|
context.account_id,
|
|
1540
1553
|
context.region,
|
|
@@ -1542,7 +1555,9 @@ class KmsProvider(KmsApi, ServiceLifecycleHook):
|
|
|
1542
1555
|
enabled_key_allowed=True,
|
|
1543
1556
|
disabled_key_allowed=True,
|
|
1544
1557
|
)
|
|
1545
|
-
self._remove_key_tags(
|
|
1558
|
+
self._remove_key_tags(
|
|
1559
|
+
context.account_id, context.region, key.metadata["Arn"], request["TagKeys"]
|
|
1560
|
+
)
|
|
1546
1561
|
|
|
1547
1562
|
def derive_shared_secret(
|
|
1548
1563
|
self,
|
localstack/services/kms/utils.py
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
|
+
import base64
|
|
1
2
|
import re
|
|
2
3
|
from collections.abc import Callable
|
|
3
4
|
|
|
4
|
-
from localstack.aws.api.kms import
|
|
5
|
+
from localstack.aws.api.kms import (
|
|
6
|
+
CreateKeyRequest,
|
|
7
|
+
DryRunOperationException,
|
|
8
|
+
Tag,
|
|
9
|
+
TagException,
|
|
10
|
+
TagList,
|
|
11
|
+
)
|
|
12
|
+
from localstack.constants import TAG_KEY_CUSTOM_ID
|
|
5
13
|
from localstack.services.kms.exceptions import ValidationException
|
|
6
14
|
from localstack.utils.aws.arns import ARN_PARTITION_REGEX
|
|
7
15
|
|
|
8
16
|
KMS_KEY_ARN_PATTERN = re.compile(
|
|
9
17
|
rf"{ARN_PARTITION_REGEX}:kms:(?P<region_name>[^:]+):(?P<account_id>\d{{12}}):((?=key/)key/|(?=alias/))(?P<key_id>[^:]+)$"
|
|
10
18
|
)
|
|
19
|
+
# special tag name to allow specifying a custom key material for created keys
|
|
20
|
+
TAG_KEY_CUSTOM_KEY_MATERIAL = "_custom_key_material_"
|
|
11
21
|
|
|
12
22
|
|
|
13
23
|
def get_hash_algorithm(signing_algorithm: str) -> str:
|
|
@@ -60,13 +70,65 @@ def validate_tag(tag_position: int, tag: Tag) -> None:
|
|
|
60
70
|
raise TagException("Tags beginning with aws: are reserved")
|
|
61
71
|
|
|
62
72
|
|
|
63
|
-
def
|
|
73
|
+
def validate_and_filter_tags(tag_list: TagList) -> TagList:
|
|
74
|
+
"""
|
|
75
|
+
Validates tags and filters out LocalStack specific tags
|
|
76
|
+
|
|
77
|
+
:param tag_list: The list of tags provided to apply to the KMS key.
|
|
78
|
+
:returns: Filtered and validated list of tags to apply to the KMS key.
|
|
79
|
+
"""
|
|
64
80
|
unique_tag_keys = {tag["TagKey"] for tag in tag_list}
|
|
65
81
|
if len(unique_tag_keys) < len(tag_list):
|
|
66
82
|
raise TagException("Duplicate tag keys")
|
|
67
83
|
if len(tag_list) > 50:
|
|
68
84
|
raise TagException("Too many tags")
|
|
69
85
|
|
|
86
|
+
validated_tags = []
|
|
87
|
+
for i, tag in enumerate(tag_list, start=1):
|
|
88
|
+
if tag["TagKey"] != TAG_KEY_CUSTOM_KEY_MATERIAL:
|
|
89
|
+
validate_tag(i, tag)
|
|
90
|
+
validated_tags.append(tag)
|
|
91
|
+
|
|
92
|
+
return validated_tags
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_custom_key_material(request: CreateKeyRequest | None) -> bytes | None:
|
|
96
|
+
"""
|
|
97
|
+
Retrieves custom material which is sent in a CreateKeyRequest via the Tags.
|
|
98
|
+
|
|
99
|
+
:param request: The request for creating a KMS key.
|
|
100
|
+
:returns: Custom key material for the KMS key.
|
|
101
|
+
"""
|
|
102
|
+
if not request:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
custom_key_material = None
|
|
106
|
+
tags = request.get("Tags", [])
|
|
107
|
+
for tag in tags:
|
|
108
|
+
if tag["TagKey"] == TAG_KEY_CUSTOM_KEY_MATERIAL:
|
|
109
|
+
custom_key_material = base64.b64decode(tag["TagValue"])
|
|
110
|
+
|
|
111
|
+
return custom_key_material
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_custom_key_id(request: CreateKeyRequest | None) -> bytes | None:
|
|
115
|
+
"""
|
|
116
|
+
Retrieves a custom Key ID for the KMS key which is sent in a CreateKeyRequest via the Tags.
|
|
117
|
+
|
|
118
|
+
:param request: The request for creating a KMS key.
|
|
119
|
+
:returns: THe custom Key ID for the KMS key.
|
|
120
|
+
"""
|
|
121
|
+
if not request:
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
custom_key_id = None
|
|
125
|
+
tags = request.get("Tags", [])
|
|
126
|
+
for tag in tags:
|
|
127
|
+
if tag["TagKey"] == TAG_KEY_CUSTOM_ID:
|
|
128
|
+
custom_key_id = tag["TagValue"]
|
|
129
|
+
|
|
130
|
+
return custom_key_id
|
|
131
|
+
|
|
70
132
|
|
|
71
133
|
def execute_dry_run_capable[T](func: Callable[..., T], dry_run: bool, *args, **kwargs) -> T:
|
|
72
134
|
"""
|
|
@@ -6,7 +6,7 @@ from localstack.services.lambda_.invocation.lambda_models import (
|
|
|
6
6
|
Layer,
|
|
7
7
|
)
|
|
8
8
|
from localstack.services.stores import AccountRegionBundle, BaseStore, LocalAttribute
|
|
9
|
-
from localstack.utils.tagging import
|
|
9
|
+
from localstack.utils.tagging import Tags
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class LambdaStore(BaseStore):
|
|
@@ -26,7 +26,7 @@ class LambdaStore(BaseStore):
|
|
|
26
26
|
capacity_providers: dict[str, CapacityProvider] = LocalAttribute(default=dict)
|
|
27
27
|
|
|
28
28
|
# maps resource ARNs for EventSourceMappings and CodeSigningConfiguration to tags
|
|
29
|
-
|
|
29
|
+
tags: Tags = LocalAttribute(default=Tags)
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
lambda_stores = AccountRegionBundle("lambda", LambdaStore)
|
|
@@ -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.41-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
|
|
|
@@ -4415,28 +4415,25 @@ class LambdaProvider(LambdaApi, ServiceLifecycleHook):
|
|
|
4415
4415
|
# only Function, Event Source Mapping, and Code Signing Config (not currently supported by LocalStack) ARNs
|
|
4416
4416
|
# are available for tagging in AWS
|
|
4417
4417
|
|
|
4418
|
+
@staticmethod
|
|
4418
4419
|
def _update_resource_tags(
|
|
4419
|
-
|
|
4420
|
+
resource_arn: str, account_id: str, region: str, tags: dict[str, str]
|
|
4420
4421
|
) -> None:
|
|
4421
|
-
|
|
4422
|
-
tag_svc_adapted_tags = [{"Key": key, "Value": value} for key, value in tags.items()]
|
|
4423
|
-
tagger_service.tag_resource(resource_arn, tag_svc_adapted_tags)
|
|
4422
|
+
lambda_stores[account_id][region].tags.update_tags(resource_arn, tags)
|
|
4424
4423
|
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
tagger_service = lambda_stores[account_id][region].TAGS
|
|
4429
|
-
return tagger_service.tags.get(resource_arn, {})
|
|
4424
|
+
@staticmethod
|
|
4425
|
+
def _list_resource_tags(resource_arn: str, account_id: str, region: str) -> dict[str, str]:
|
|
4426
|
+
return lambda_stores[account_id][region].tags.get_tags(resource_arn)
|
|
4430
4427
|
|
|
4428
|
+
@staticmethod
|
|
4431
4429
|
def _remove_resource_tags(
|
|
4432
|
-
|
|
4430
|
+
resource_arn: str, account_id: str, region: str, keys: TagKeyList
|
|
4433
4431
|
) -> None:
|
|
4434
|
-
|
|
4435
|
-
tagger_service.untag_resource(resource_arn, keys)
|
|
4432
|
+
lambda_stores[account_id][region].tags.delete_tags(resource_arn, keys)
|
|
4436
4433
|
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4434
|
+
@staticmethod
|
|
4435
|
+
def _remove_all_resource_tags(resource_arn: str, account_id: str, region: str) -> None:
|
|
4436
|
+
lambda_stores[account_id][region].tags.delete_all_tags(resource_arn)
|
|
4440
4437
|
|
|
4441
4438
|
def _get_tags(self, resource: TaggableResource) -> dict[str, str]:
|
|
4442
4439
|
account_id, region = self._get_account_id_and_region_for_taggable_resource(resource)
|
|
@@ -2,18 +2,15 @@ from localstack.aws.api.opensearch import DomainStatus
|
|
|
2
2
|
from localstack.services.stores import (
|
|
3
3
|
AccountRegionBundle,
|
|
4
4
|
BaseStore,
|
|
5
|
-
CrossRegionAttribute,
|
|
6
5
|
LocalAttribute,
|
|
7
6
|
)
|
|
8
|
-
from localstack.utils.tagging import
|
|
7
|
+
from localstack.utils.tagging import Tags
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
class OpenSearchStore(BaseStore):
|
|
12
11
|
# storage for domain resources (access should be protected with the _domain_mutex)
|
|
13
12
|
opensearch_domains: dict[str, DomainStatus] = LocalAttribute(default=dict)
|
|
14
|
-
|
|
15
|
-
# static tagging service instance
|
|
16
|
-
TAGS = CrossRegionAttribute(default=TaggingService)
|
|
13
|
+
tags: Tags = LocalAttribute(default=Tags)
|
|
17
14
|
|
|
18
15
|
|
|
19
16
|
opensearch_stores = AccountRegionBundle("opensearch", OpenSearchStore)
|
|
@@ -518,22 +518,20 @@ class OpensearchProvider(OpensearchApi, ServiceLifecycleHook):
|
|
|
518
518
|
cluster_manager().remove(DomainKey(domain_name, region, account_id).arn)
|
|
519
519
|
|
|
520
520
|
def _add_tags(self, context: RequestContext, arn: ARN, tag_list: TagList) -> None:
|
|
521
|
-
self.get_store(context.account_id, context.region).
|
|
521
|
+
self.get_store(context.account_id, context.region).tags.update_tags(
|
|
522
|
+
arn, {tag["Key"]: tag["Value"] for tag in tag_list}
|
|
523
|
+
)
|
|
522
524
|
|
|
523
525
|
def _remove_tags(self, context: RequestContext, arn: ARN, tag_keys: StringList) -> None:
|
|
524
|
-
self.get_store(context.account_id, context.region).
|
|
526
|
+
self.get_store(context.account_id, context.region).tags.delete_tags(arn, tag_keys)
|
|
525
527
|
|
|
526
528
|
def _remove_all_tags(self, context: RequestContext, arn: ARN) -> None:
|
|
527
|
-
self.get_store(context.account_id, context.region).
|
|
529
|
+
self.get_store(context.account_id, context.region).tags.delete_all_tags(arn)
|
|
528
530
|
|
|
529
531
|
def _list_tags(self, context: RequestContext, arn: ARN) -> TagList:
|
|
530
|
-
# The tagging service returns a dictionary with the given root name
|
|
531
532
|
store = self.get_store(context.account_id, context.region)
|
|
532
|
-
tags = store.
|
|
533
|
-
|
|
534
|
-
tag_list: TagList = tags["root"]
|
|
535
|
-
|
|
536
|
-
return tag_list
|
|
533
|
+
tags = store.tags.get_tags(arn)
|
|
534
|
+
return [{"Key": key, "Value": value} for key, value in tags.items()]
|
|
537
535
|
|
|
538
536
|
@handler("CreateDomain", expand=False)
|
|
539
537
|
def create_domain(
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from localstack.aws.api.route53 import DelegationSet
|
|
2
2
|
from localstack.services.stores import AccountRegionBundle, BaseStore, LocalAttribute
|
|
3
|
+
from localstack.utils.tagging import Tags
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class Route53Store(BaseStore):
|
|
6
7
|
# maps delegation set ID to reusable delegation set details
|
|
7
8
|
reusable_delegation_sets: dict[str, DelegationSet] = LocalAttribute(default=dict)
|
|
9
|
+
tags: Tags = LocalAttribute(default=Tags)
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
route53_stores = AccountRegionBundle("route53", Route53Store)
|
|
12
|
+
route53_stores = AccountRegionBundle("route53", Route53Store, validate=False)
|
|
@@ -27,6 +27,7 @@ from localstack.aws.api.route53 import (
|
|
|
27
27
|
from localstack.aws.connect import connect_to
|
|
28
28
|
from localstack.services.moto import call_moto
|
|
29
29
|
from localstack.services.plugins import ServiceLifecycleHook
|
|
30
|
+
from localstack.services.route53.models import Route53Store, route53_stores
|
|
30
31
|
from localstack.state import StateVisitor
|
|
31
32
|
|
|
32
33
|
|
|
@@ -37,6 +38,10 @@ class Route53Provider(Route53Api, ServiceLifecycleHook):
|
|
|
37
38
|
visitor.visit(route53_backends)
|
|
38
39
|
visitor.visit(route53_stores)
|
|
39
40
|
|
|
41
|
+
@staticmethod
|
|
42
|
+
def get_route53_store(account_id: str, region: str) -> Route53Store:
|
|
43
|
+
return route53_stores[account_id][region]
|
|
44
|
+
|
|
40
45
|
# No tag deletion logic to handle in Community. Overwritten in Pro implementation.
|
|
41
46
|
def remove_resource_tags(
|
|
42
47
|
self, context: RequestContext, resource_type: str, resource_id: str
|
localstack/services/s3/models.py
CHANGED
|
@@ -91,7 +91,7 @@ from localstack.services.stores import (
|
|
|
91
91
|
LocalAttribute,
|
|
92
92
|
)
|
|
93
93
|
from localstack.utils.aws import arns
|
|
94
|
-
from localstack.utils.tagging import
|
|
94
|
+
from localstack.utils.tagging import Tags
|
|
95
95
|
|
|
96
96
|
LOG = logging.getLogger(__name__)
|
|
97
97
|
|
|
@@ -763,9 +763,7 @@ class S3Store(BaseStore):
|
|
|
763
763
|
buckets: dict[BucketName, S3Bucket] = CrossRegionAttribute(default=dict)
|
|
764
764
|
global_bucket_map: dict[BucketName, AccountId] = CrossAccountAttribute(default=dict)
|
|
765
765
|
aws_managed_kms_key_id: SSEKMSKeyId = LocalAttribute(default=str)
|
|
766
|
-
|
|
767
|
-
# static tagging service instance
|
|
768
|
-
TAGS: TaggingService = CrossAccountAttribute(default=TaggingService)
|
|
766
|
+
tags: Tags = LocalAttribute(default=Tags)
|
|
769
767
|
|
|
770
768
|
|
|
771
769
|
class BucketCorsIndex:
|