Repository: getmoto/moto
Pull request #7149 (2023-12-21 02:11:14)

Update LaunchTemplate by physical ID rather than by name
## Purpose

Looking up a launch template resource by its name attribute can be tricky when that name is randomly generated by cloudformation rather than one of the declared resource attributes. Instead, do the lookup using the physical resource id.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/getmoto/moto/pull/7149?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`be0dffc`)](https://app.codecov.io/gh/getmoto/moto/commit/be0dffcd3829e2eaae377da9da495b04e6e2cc93?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) 95.88% compared to head [(`009829a`)](https://app.codecov.io/gh/getmoto/moto/pull/7149?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) 95.88%.
> Report is 1 commits behind head on master.


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #7149   +/-   ##
=======================================
  Coverage   95.88%   95.88%           
=======================================
  Files         834      834           
  Lines       82256    82259    +3     
=======================================
+ Hits        78869    78874    +5     
+ Misses       3387     3385    -2     
```

| [Flag](https://app.codecov.io/gh/getmoto/moto/pull/7149/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | Coverage Δ | |
|---|---|---|
| [servertests](https://app.codecov.io/gh/getmoto/moto/pull/7149/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `35.81% <0.00%> (+<0.01%)` | :arrow_up: |
| [unittests](https://app.codecov.io/gh/getmoto/moto/pull/7149/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `95.82% <100.00%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/getmoto/moto/pull/7149?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).


Patch:
diff --git a/moto/dynamodb/models/table.py b/moto/dynamodb/models/table.py
index b183cbdc7..441f66f3d 100644
--- a/moto/dynamodb/models/table.py
+++ b/moto/dynamodb/models/table.py
@@ -848,9 +848,9 @@ class Table(CloudFormationModel):
             passes_all_conditions = True
             for attribute_name in filters:
                 attribute = item.attrs.get(attribute_name)
+                (comparison_operator, comparison_objs) = filters[attribute_name]
 
                 if attribute:
-                    (comparison_operator, comparison_objs) = filters[attribute_name]
                     # Attribute found
                     if not attribute.compare(comparison_operator, comparison_objs):
                         passes_all_conditions = False
diff --git a/moto/ec2/models/launch_templates.py b/moto/ec2/models/launch_templates.py
index 16db8d79d..47243440a 100644
--- a/moto/ec2/models/launch_templates.py
+++ b/moto/ec2/models/launch_templates.py
@@ -167,11 +167,10 @@ class LaunchTemplate(TaggedEC2Resource, CloudFormationModel):
 
         properties = cloudformation_json["Properties"]
 
-        name = properties.get("LaunchTemplateName")
         data = properties.get("LaunchTemplateData")
         description = properties.get("VersionDescription")
 
-        launch_template = backend.get_launch_template_by_name(name)
+        launch_template = backend.get_launch_template(original_resource.id)
 
         launch_template.create_version(data, description)
 


Test patch:
diff --git a/tests/test_dynamodb/test_dynamodb.py b/tests/test_dynamodb/test_dynamodb.py
index 65bad216f..1ec5f8a1a 100644
--- a/tests/test_dynamodb/test_dynamodb.py
+++ b/tests/test_dynamodb/test_dynamodb.py
@@ -1616,6 +1616,50 @@ def test_bad_scan_filter():
     assert exc.value.response["Error"]["Code"] == "ValidationException"
 
 
+@mock_dynamodb
+def test_scan_with_scanfilter():
+    table_name = "my-table"
+    item = {"partitionKey": "pk-2", "my-attr": 42}
+    client = boto3.client("dynamodb", region_name="us-east-1")
+    res = boto3.resource("dynamodb", region_name="us-east-1")
+    res.create_table(
+        TableName=table_name,
+        KeySchema=[{"AttributeName": "partitionKey", "KeyType": "HASH"}],
+        AttributeDefinitions=[{"AttributeName": "partitionKey", "AttributeType": "S"}],
+        BillingMode="PAY_PER_REQUEST",
+    )
+    table = res.Table(table_name)
+    table.put_item(Item={"partitionKey": "pk-1"})
+    table.put_item(Item=item)
+
+    # ScanFilter: EQ
+    # The DynamoDB table-resource sends the AttributeValueList in the wrong format
+    # So this operation never finds any data, in Moto or AWS
+    table.scan(
+        ScanFilter={
+            "my-attr": {"AttributeValueList": [{"N": "42"}], "ComparisonOperator": "EQ"}
+        }
+    )
+
+    # ScanFilter: EQ
+    # If we use the boto3-client, we do receive the correct data
+    items = client.scan(
+        TableName=table_name,
+        ScanFilter={
+            "partitionKey": {
+                "AttributeValueList": [{"S": "pk-1"}],
+                "ComparisonOperator": "EQ",
+            }
+        },
+    )["Items"]
+    assert items == [{"partitionKey": {"S": "pk-1"}}]
+
+    # ScanFilter: NONE
+    # Note that we can use the table-resource here, because we're not using the AttributeValueList
+    items = table.scan(ScanFilter={"my-attr": {"ComparisonOperator": "NULL"}})["Items"]
+    assert items == [{"partitionKey": "pk-1"}]
+
+
 @mock_dynamodb
 def test_duplicate_create():
     client = boto3.client("dynamodb", region_name="us-east-1")
diff --git a/tests/test_ec2/test_launch_templates_cloudformation.py b/tests/test_ec2/test_launch_templates_cloudformation.py
index ede997e20..b80361671 100644
--- a/tests/test_ec2/test_launch_templates_cloudformation.py
+++ b/tests/test_ec2/test_launch_templates_cloudformation.py
@@ -297,3 +297,84 @@ def test_two_launch_templates():
         launch_templates["LaunchTemplates"][0]["LaunchTemplateName"]
         != launch_templates["LaunchTemplates"][1]["LaunchTemplateName"]
     )
+
+
+@mock_autoscaling
+@mock_cloudformation
+@mock_ec2
+def test_launch_template_unnamed_update():
+    cf_client = boto3.client("cloudformation", region_name="us-west-1")
+    ec2_client = boto3.client("ec2", region_name="us-west-1")
+
+    stack_name = str(uuid4())
+
+    template_json = json.dumps(
+        {
+            "AWSTemplateFormatVersion": "2010-09-09",
+            "Description": "AWS CloudFormation Template to create a LaunchTemplate",
+            "Resources": {
+                "LaunchTemplate": {
+                    "Type": "AWS::EC2::LaunchTemplate",
+                    "Properties": {
+                        "LaunchTemplateData": {
+                            "ImageId": EXAMPLE_AMI_ID,
+                            "InstanceType": "t3.small",
+                            "UserData": "",
+                        },
+                    },
+                },
+            },
+        }
+    )
+
+    cf_client.create_stack(
+        StackName=stack_name,
+        TemplateBody=template_json,
+        Capabilities=["CAPABILITY_NAMED_IAM"],
+        OnFailure="DELETE",
+    )
+
+    launch_template_resource = cf_client.describe_stack_resources(
+        StackName=stack_name, LogicalResourceId="LaunchTemplate"
+    )["StackResources"][0]
+    launch_template_id = launch_template_resource["PhysicalResourceId"]
+    launch_template = ec2_client.describe_launch_templates(
+        LaunchTemplateIds=[launch_template_id]
+    )["LaunchTemplates"][0]
+    assert launch_template["LatestVersionNumber"] == 1
+
+    template_json = json.dumps(
+        {
+            "AWSTemplateFormatVersion": "2010-09-09",
+            "Description": "AWS CloudFormation Template to create a LaunchTemplate",
+            "Resources": {
+                "LaunchTemplate": {
+                    "Type": "AWS::EC2::LaunchTemplate",
+                    "Properties": {
+                        "LaunchTemplateData": {
+                            "ImageId": EXAMPLE_AMI_ID,
+                            "InstanceType": "t3.medium",
+                            "UserData": "",
+                        },
+                    },
+                },
+            },
+        }
+    )
+
+    cf_client.update_stack(
+        StackName=stack_name,
+        TemplateBody=template_json,
+        Capabilities=["CAPABILITY_NAMED_IAM"],
+    )
+
+    updated_launch_template_resource = cf_client.describe_stack_resources(
+        StackName=stack_name, LogicalResourceId="LaunchTemplate"
+    )["StackResources"][0]
+    updated_launch_template_id = updated_launch_template_resource["PhysicalResourceId"]
+    updated_launch_template = ec2_client.describe_launch_templates(
+        LaunchTemplateIds=[updated_launch_template_id]
+    )["LaunchTemplates"][0]
+
+    assert launch_template_id == updated_launch_template_id
+    assert updated_launch_template["LatestVersionNumber"] == 2