Repository: getmoto/moto

Issue:
Empty tags are not considered on filtering
I've been creating AMIs with tags such as RELEASE with an empty value (I just don't add anything on the console or an empty string "" in boto3/moto)

After that, I filter using those tags. The problem I've found is that when using MOTO to mock boto3 calls the describe_iamges() behaves differently compared to boto3.

When I try to filter with a key-value where the value is empty ("") the filter is not taken into account. So instead of filtering only those images that have a RELEASE tag with an empty value, all images are returned whether they have this filter or not.

I've verified that this is not the behavior on boto3 with a real AWS account. And is not. When using boto3 only those AMIs with the value "" are returned. This only happens when using empty values in the filtering, when using any other values the correct AMIs are returned.

Some code to try to reproduce
````

@pytest.fixture(scope='function')
def aws_credentials():
    os.environ['AWS_ACCESS_KEY_ID'] = 'testing'
    os.environ['AWS_SECRET_ACCESS_KEY'] = 'testing'
    os.environ['AWS_SECURITY_TOKEN'] = 'testing'
    os.environ['AWS_SESSION_TOKEN'] = 'testing'
    os.environ['AWS_DEFAULT_REGION'] = 'eu-west-1'


@pytest.fixture(scope='function')
def ec2(aws_credentials):
    with mock_ec2():
        yield boto3.client('ec2', region_name=os.getenv('AWS_DEFAULT_REGION'))

def fake_images_release_list(ec2):
    fake_images = []

    instances = ec2.run_instances(ImageId='foo', MinCount=1, MaxCount=1)["Instances"]

    # create images from instance
    for i in range(0, 9):
        with freeze_time(f"2021-01-01 08:00:0{i}"):
            image = ec2.create_image(InstanceId=instances[0]["InstanceId"],
                                     Name=f"MyFooAMI{i}",
                                     Description="Test")

            response = ec2.create_tags(Resources=[image["ImageId"]],
                                       Tags=[
                                           {
                                               'Key': 'Base_AMI_Name',
                                               'Value': 'Deep Learning Base AMI (Amazon Linux 2) Version 31.0',
                                           },
                                           {
                                               'Key': 'OS_Version',
                                               'Value': 'AWS Linux 2',
                                           }
                                       ])
            fake_images.append(image)

    # Add release tags to some of the images in the middle
    for image in fake_images[3:6]:
            response = ec2.create_tags(Resources=[image["ImageId"]],
                                       Tags=[{'Key': 'RELEASE', 'Value': ''}])

    return fake_images


def test_get_latest_image_tag_as_release3(ec2):
    fake_images = fake_images_release_list(ec2)

    filter = [{"Name": 'tag:Base_AMI_Name', "Values": ['Deep Learning Base AMI (Amazon Linux 2) Version 31.0']},
              {"Name": 'tag:OS_Version', "Values": ['AWS Linux 2']},
              {"Name": 'tag:RELEASE', "Values": ['']}]

    assert len(ec2.describe_images(Owners=['self'], Filters=filter)["Images"]) == 3
````

Instead of the 3 images mark as release in the middle, the 9 are amis are returned


Discussion:

Resolving patch:
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -249,7 +249,7 @@ def get_filter_value(self, filter_name, method_name=None):
                 if tag["key"] == tagname:
                     return tag["value"]
 
-            return ""
+            return None
         elif filter_name == "tag-key":
             return [tag["key"] for tag in tags]
         elif filter_name == "tag-value":
diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py
--- a/moto/ec2/utils.py
+++ b/moto/ec2/utils.py
@@ -458,7 +458,7 @@ def filter_internet_gateways(igws, filter_dict):
 def is_filter_matching(obj, filter, filter_value):
     value = obj.get_filter_value(filter)
 
-    if not filter_value:
+    if filter_value is None:
         return False
 
     if isinstance(value, six.string_types):


Test patch:
diff --git a/tests/test_ec2/test_amis.py b/tests/test_ec2/test_amis.py
--- a/tests/test_ec2/test_amis.py
+++ b/tests/test_ec2/test_amis.py
@@ -921,3 +921,44 @@ def test_create_image_with_tag_specification():
     ex.value.response["Error"]["Message"].should.equal(
         "'invalid-resource-type' is not a valid taggable resource type for this operation."
     )
+
+
+@mock_ec2
+def test_ami_filter_by_empty_tag():
+    ec2 = boto3.resource("ec2", region_name="us-west-1")
+    client = boto3.client("ec2", region_name="us-west-1")
+
+    fake_images = []
+    instance = ec2.create_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)[0]
+    for i in range(10):
+        image = client.create_image(
+            InstanceId=instance.instance_id,
+            Name="MyAMI{}".format(i),
+            Description="Test",
+        )
+
+        ec2.create_tags(
+            Resources=[image["ImageId"]],
+            Tags=[
+                {
+                    "Key": "Base_AMI_Name",
+                    "Value": "Deep Learning Base AMI (Amazon Linux 2) Version 31.0",
+                },
+                {"Key": "OS_Version", "Value": "AWS Linux 2"},
+            ],
+        )
+        fake_images.append(image)
+    # Add release tags to some of the images in the middle
+    for image in fake_images[3:6]:
+        ec2.create_tags(
+            Resources=[image["ImageId"]], Tags=[{"Key": "RELEASE", "Value": ""}]
+        )
+    images_filter = [
+        {
+            "Name": "tag:Base_AMI_Name",
+            "Values": ["Deep Learning Base AMI (Amazon Linux 2) Version 31.0"],
+        },
+        {"Name": "tag:OS_Version", "Values": ["AWS Linux 2"]},
+        {"Name": "tag:RELEASE", "Values": [""]},
+    ]
+    assert len(client.describe_images(Filters=images_filter)["Images"]) == 3