Repository: getmoto/moto

Issue:
ec2_client.describe_instances wildcard filters match any tag
When ec2_client.describe_instances filters by wildcard, it matches any tag. It should only match the specified tag.

An example failing test:

```
@mock_ec2
def test_filter_wildcard_in_specified_tag_only():
    ec2_client = boto3.client("ec2", region_name="us-west-1")

    tags_name = [{'Key': 'Name', 'Value': 'alice in wonderland'}]
    ec2_client.run_instances(MaxCount=1, MinCount=1, TagSpecifications=[{"ResourceType": "instance", "Tags": tags_name}])

    tags_owner = [{'Key': 'Owner', 'Value': 'alice in wonderland'}]
    ec2_client.run_instances(MaxCount=1, MinCount=1, TagSpecifications=[{"ResourceType": "instance", "Tags": tags_owner}])

    # should only match the Name tag
    response = ec2_client.describe_instances(Filters=[{"Name": "tag:Name", "Values": ["*alice*"]}])
    instances = [i for r in response["Reservations"] for i in r["Instances"]]
    instances.should.have.length_of(1)
```

2 instances will be returned instead of 1.

moto 2.2.1


Discussion:
Thanks for raising this @tekumara - marking it as a bug

Resolving patch:
diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py
--- a/moto/ec2/utils.py
+++ b/moto/ec2/utils.py
@@ -362,8 +362,10 @@ def get_obj_tag_names(obj):
     return tags
 
 
-def get_obj_tag_values(obj):
-    tags = set((tag["value"] for tag in obj.get_tags()))
+def get_obj_tag_values(obj, key=None):
+    tags = set(
+        (tag["value"] for tag in obj.get_tags() if tag["key"] == key or key is None)
+    )
     return tags
 
 
@@ -381,7 +383,8 @@ def tag_filter_matches(obj, filter_name, filter_values):
     elif filter_name == "tag-value":
         tag_values = get_obj_tag_values(obj)
     elif filter_name.startswith("tag:"):
-        tag_values = get_obj_tag_values(obj)
+        key = filter_name[4:]
+        tag_values = get_obj_tag_values(obj, key=key)
     else:
         tag_values = [get_obj_tag(obj, filter_name) or ""]
 


Test patch:
diff --git a/tests/test_ec2/test_instances.py b/tests/test_ec2/test_instances.py
--- a/tests/test_ec2/test_instances.py
+++ b/tests/test_ec2/test_instances.py
@@ -1724,3 +1724,32 @@ def test_warn_on_invalid_ami():
         match=r"Could not find AMI with image-id:invalid-ami.+",
     ):
         ec2.create_instances(ImageId="invalid-ami", MinCount=1, MaxCount=1)
+
+
+@mock_ec2
+def test_filter_wildcard_in_specified_tag_only():
+    ec2_client = boto3.client("ec2", region_name="us-west-1")
+
+    tags_name = [{"Key": "Name", "Value": "alice in wonderland"}]
+    ec2_client.run_instances(
+        ImageId=EXAMPLE_AMI_ID,
+        MaxCount=1,
+        MinCount=1,
+        TagSpecifications=[{"ResourceType": "instance", "Tags": tags_name}],
+    )
+
+    tags_owner = [{"Key": "Owner", "Value": "alice in wonderland"}]
+    ec2_client.run_instances(
+        ImageId=EXAMPLE_AMI_ID,
+        MaxCount=1,
+        MinCount=1,
+        TagSpecifications=[{"ResourceType": "instance", "Tags": tags_owner}],
+    )
+
+    # should only match the Name tag
+    response = ec2_client.describe_instances(
+        Filters=[{"Name": "tag:Name", "Values": ["*alice*"]}]
+    )
+    instances = [i for r in response["Reservations"] for i in r["Instances"]]
+    instances.should.have.length_of(1)
+    instances[0]["Tags"][0].should.have.key("Key").should.equal("Name")