From 28f3f846448bedc21098df2faa54986f3f7c3179 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Thu, 20 Apr 2023 21:12:43 +0000 Subject: [PATCH] SSM: Allow filtering by existence of tag-name (#6238) --- moto/ssm/models.py | 7 +++++-- moto/ssm/responses.py | 6 +++--- tests/test_ssm/test_ssm_boto3.py | 15 +++++++++------ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/moto/ssm/models.py b/moto/ssm/models.py index f6763bbf2..3ae5247b6 100644 --- a/moto/ssm/models.py +++ b/moto/ssm/models.py @@ -1434,7 +1434,7 @@ class SimpleSystemManagerBackend(BaseBackend): f"The following filter key is not valid: {key}. Valid filter keys include: [Type, KeyId]." ) - if not values: + if key in ["Name", "Type", "Path", "Tier", "Keyid"] and not values: raise InvalidFilterValue( "The following filter values are missing : null for filter key Name." ) @@ -1636,7 +1636,7 @@ class SimpleSystemManagerBackend(BaseBackend): elif key.startswith("tag:"): what = [tag["Value"] for tag in parameter.tags if tag["Key"] == key[4:]] - if what is None: + if what is None or what == []: return False # 'what' can be a list (of multiple tag-values, for instance) is_list = isinstance(what, list) @@ -1650,6 +1650,9 @@ class SimpleSystemManagerBackend(BaseBackend): elif option == "Contains" and not any(value in what for value in values): return False elif option == "Equals": + if is_list and len(values) == 0: + # User hasn't provided possible tag-values - they just want to know whether the tag exists + return True if is_list and not any(val in what for val in values): return False elif not is_list and not any(what == val for val in values): diff --git a/moto/ssm/responses.py b/moto/ssm/responses.py index 7ef8c9906..1469c4a51 100644 --- a/moto/ssm/responses.py +++ b/moto/ssm/responses.py @@ -2,15 +2,15 @@ import json from moto.core.responses import BaseResponse from .exceptions import ValidationException -from .models import ssm_backends +from .models import ssm_backends, SimpleSystemManagerBackend class SimpleSystemManagerResponse(BaseResponse): - def __init__(self): + def __init__(self) -> None: super().__init__(service_name="ssm") @property - def ssm_backend(self): + def ssm_backend(self) -> SimpleSystemManagerBackend: return ssm_backends[self.current_account][self.region] @property diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py index fda67a08a..ad2074440 100644 --- a/tests/test_ssm/test_ssm_boto3.py +++ b/tests/test_ssm/test_ssm_boto3.py @@ -1039,14 +1039,17 @@ def test_describe_parameters_tags(): Tags=[{"Key": "spam", "Value": "eggs"}], ) - response = client.describe_parameters( + parameters = client.describe_parameters( ParameterFilters=[{"Key": "tag:spam", "Values": ["eggs"]}] - ) + )["Parameters"] + assert len(parameters) == 1 + assert parameters[0]["Name"] == "/spam/eggs" - parameters = response["Parameters"] - parameters.should.have.length_of(1) - - parameters[0]["Name"].should.equal("/spam/eggs") + # Verify we can filter by the existence of a tag + filters = [{"Key": "tag:spam"}] + response = client.describe_parameters(ParameterFilters=filters) + assert len(response["Parameters"]) == 1 + assert {p["Name"] for p in response["Parameters"]} == set(["/spam/eggs"]) @mock_ssm