From 79089250c2fe015e7869345f52d13dc7dd4110af Mon Sep 17 00:00:00 2001 From: Joel Scheuner Date: Wed, 4 Oct 2023 15:12:41 +0200 Subject: [PATCH] Fix layer version handling for external Lambda layers --- localstack/services/lambda_/provider.py | 29 +++++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/localstack/services/lambda_/provider.py b/localstack/services/lambda_/provider.py index c10832ddaa6dc..2a737dbc4ce24 100644 --- a/localstack/services/lambda_/provider.py +++ b/localstack/services/lambda_/provider.py @@ -588,10 +588,13 @@ def _validate_layers(self, new_layers: list[str], region: str, account_id: int): visited_layers = dict() for layer_version_arn in new_layers: - layer_region, layer_account_id, layer_name, layer_version = api_utils.parse_layer_arn( - layer_version_arn - ) - if layer_version is None: + ( + layer_region, + layer_account_id, + layer_name, + layer_version_str, + ) = api_utils.parse_layer_arn(layer_version_arn) + if layer_version_str is None: raise ValidationException( f"1 validation error detected: Value '[{layer_version_arn}]'" + r" at 'layers' failed to satisfy constraint: Member must satisfy constraint: [Member must have length less than or equal to 140, Member must have length greater than or equal to 1, Member must satisfy regular expression pattern: (arn:[a-zA-Z0-9-]+:lambda:[a-zA-Z0-9-]+:\d{12}:layer:[a-zA-Z0-9-_]+:[0-9]+)|(arn:[a-zA-Z0-9-]+:lambda:::awslayer:[a-zA-Z0-9-_]+), Member must not be null]", @@ -599,6 +602,9 @@ def _validate_layers(self, new_layers: list[str], region: str, account_id: int): state = lambda_stores[layer_account_id][layer_region] layer = state.layers.get(layer_name) + layer_version = None + if layer is not None: + layer_version = layer.layer_versions.get(layer_version_str) if layer_account_id == get_aws_account_id(): if region and layer_region != region: raise InvalidParameterValueException( @@ -606,7 +612,7 @@ def _validate_layers(self, new_layers: list[str], region: str, account_id: int): f"Layers are expected to be in region {region}.", Type="User", ) - if layer is None or layer.layer_versions.get(layer_version) is None: + if layer is None or layer.layer_versions.get(layer_version_str) is None: raise InvalidParameterValueException( f"Layer version {layer_version_arn} does not exist.", Type="User" ) @@ -618,7 +624,7 @@ def _validate_layers(self, new_layers: list[str], region: str, account_id: int): raise AccessDeniedException( f"User: arn:aws:iam::{account_id}:{user} is not authorized to perform: lambda:GetLayerVersion on resource: {layer_version_arn} because no resource-based policy allows the lambda:GetLayerVersion action" ) - if layer is None: + if layer is None or layer_version is None: # Limitation: cannot fetch external layers when using the same account id as the target layer # because we do not want to trigger the layer fetcher for every non-existing layer. if self.layer_fetcher is None: @@ -633,7 +639,16 @@ def _validate_layers(self, new_layers: list[str], region: str, account_id: int): raise AccessDeniedException( f"User: arn:aws:iam::{account_id}:{user} is not authorized to perform: lambda:GetLayerVersion on resource: {layer_version_arn} because no resource-based policy allows the lambda:GetLayerVersion action" ) - state.layers[layer_name] = layer + + # Distinguish between new layer and new layer version + if layer_version is None: + # Create whole layer from scratch + state.layers[layer_name] = layer + else: + # Create layer version if another version of the same layer already exists + state.layers[layer_name].layer_versions[ + layer_version_str + ] = layer.layer_versions.get(layer_version_str) # only the first two matches in the array are considered for the error message layer_arn = ":".join(layer_version_arn.split(":")[:-1])