Fix TagFilter implementation in tag:GetResources (#3366)

The `tag_filter` method has been re-arranged to mimic the actual AWS behavior:
Return `True` if *any* tag matches a filter and *all* filters are matched.

Python's closures are late-binding, so we have to modify the lambdas accordingly!

Closes #2814
This commit is contained in:
Brian Pandola 2020-10-09 05:55:48 -07:00 committed by GitHub
parent d00cefa25c
commit c1b2c78db2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 9 deletions

View File

@ -113,32 +113,35 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
# https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
# TODO move these to their respective backends
filters = [lambda t, v: True]
filters = []
for tag_filter_dict in tag_filters:
values = tag_filter_dict.get("Values", [])
if len(values) == 0:
# Check key matches
filters.append(lambda t, v: t == tag_filter_dict["Key"])
filters.append(lambda t, v, key=tag_filter_dict["Key"]: t == key)
elif len(values) == 1:
# Check its exactly the same as key, value
filters.append(
lambda t, v: t == tag_filter_dict["Key"] and v == values[0]
lambda t, v, key=tag_filter_dict["Key"], value=values[0]: t == key
and v == value
)
else:
# Check key matches and value is one of the provided values
filters.append(lambda t, v: t == tag_filter_dict["Key"] and v in values)
filters.append(
lambda t, v, key=tag_filter_dict["Key"], vl=values: t == key
and v in vl
)
def tag_filter(tag_list):
result = []
if tag_filters:
for tag in tag_list:
for f in filters:
temp_result = []
for f in filters:
for tag in tag_list:
f_result = f(tag["Key"], tag["Value"])
temp_result.append(f_result)
result.append(all(temp_result))
return any(result)
result.append(any(temp_result))
return all(result)
else:
return True

View File

@ -293,3 +293,62 @@ def test_get_resources_s3():
response_keys.remove(resource["Tags"][0]["Key"])
response_keys.should.have.length_of(0)
@mock_ec2
@mock_resourcegroupstaggingapi
def test_multiple_tag_filters():
client = boto3.client("ec2", region_name="eu-central-1")
resp = client.run_instances(
ImageId="ami-123",
MinCount=1,
MaxCount=1,
InstanceType="t2.micro",
TagSpecifications=[
{
"ResourceType": "instance",
"Tags": [
{"Key": "MY_TAG1", "Value": "MY_UNIQUE_VALUE"},
{"Key": "MY_TAG2", "Value": "MY_SHARED_VALUE"},
],
},
{
"ResourceType": "instance",
"Tags": [{"Key": "MY_TAG3", "Value": "MY_VALUE3"}],
},
],
)
instance_1_id = resp["Instances"][0]["InstanceId"]
resp = client.run_instances(
ImageId="ami-456",
MinCount=1,
MaxCount=1,
InstanceType="t2.micro",
TagSpecifications=[
{
"ResourceType": "instance",
"Tags": [
{"Key": "MY_TAG1", "Value": "MY_ALT_UNIQUE_VALUE"},
{"Key": "MY_TAG2", "Value": "MY_SHARED_VALUE"},
],
},
{
"ResourceType": "instance",
"Tags": [{"Key": "MY_ALT_TAG3", "Value": "MY_VALUE3"}],
},
],
)
instance_2_id = resp["Instances"][0]["InstanceId"]
rtapi = boto3.client("resourcegroupstaggingapi", region_name="eu-central-1")
results = rtapi.get_resources(
TagFilters=[
{"Key": "MY_TAG1", "Values": ["MY_UNIQUE_VALUE"]},
{"Key": "MY_TAG2", "Values": ["MY_SHARED_VALUE"]},
]
).get("ResourceTagMappingList", [])
results.should.have.length_of(1)
instance_1_id.should.be.within(results[0]["ResourceARN"])
instance_2_id.shouldnt.be.within(results[0]["ResourceARN"])