From 08a08b6af8572f71870344b24c9de46e05315cf3 Mon Sep 17 00:00:00 2001 From: Waldemar Hummer Date: Wed, 29 Jul 2020 12:44:02 +0200 Subject: [PATCH] Fix SQS tag list from CloudFormation resource creation (#3197) * fix sqs tag list from cloudformation resource creation the method `create_from_cloudformation_json` of the Sqs resource does not handle the difference of format of the Tags field in the resource template and the format expected in Sqs resource class. In cfn resource template Tags is specified as a list of dicts. But the Sqs resource expects that the tags field be a single dict. This behaviour causes a crash when a queue is created with tags from `create_from_cloudformation_json` and later the list_queue_tags is called because it tries to call `items` from `queue.tags` but tags is actually a list of dicts. * fix comment * fix linter * minor Co-authored-by: Hudo Assenco --- moto/core/utils.py | 11 +++++++++++ moto/sqs/models.py | 11 +++++++++-- tests/test_sqs/test_sqs.py | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/moto/core/utils.py b/moto/core/utils.py index c9bf9347..235b895e 100644 --- a/moto/core/utils.py +++ b/moto/core/utils.py @@ -356,3 +356,14 @@ def tags_from_query_string( else: response_values[tag_key] = None return response_values + + +def tags_from_cloudformation_tags_list(tags_list): + """Return tags in dict form from cloudformation resource tags form (list of dicts)""" + tags = {} + for entry in tags_list: + key = entry["Key"] + value = entry["Value"] + tags[key] = value + + return tags diff --git a/moto/sqs/models.py b/moto/sqs/models.py index 08541645..4befbb50 100644 --- a/moto/sqs/models.py +++ b/moto/sqs/models.py @@ -18,6 +18,7 @@ from moto.core.utils import ( get_random_message_id, unix_time, unix_time_millis, + tags_from_cloudformation_tags_list, ) from .utils import generate_receipt_handle from .exceptions import ( @@ -357,11 +358,17 @@ class Queue(BaseModel): def create_from_cloudformation_json( cls, resource_name, cloudformation_json, region_name ): - properties = cloudformation_json["Properties"] + properties = deepcopy(cloudformation_json["Properties"]) + # remove Tags from properties and convert tags list to dict + tags = properties.pop("Tags", []) + tags_dict = tags_from_cloudformation_tags_list(tags) sqs_backend = sqs_backends[region_name] return sqs_backend.create_queue( - name=properties["QueueName"], region=region_name, **properties + name=properties["QueueName"], + tags=tags_dict, + region=region_name, + **properties ) @classmethod diff --git a/tests/test_sqs/test_sqs.py b/tests/test_sqs/test_sqs.py index 9e389615..61edcaa9 100644 --- a/tests/test_sqs/test_sqs.py +++ b/tests/test_sqs/test_sqs.py @@ -17,12 +17,34 @@ from boto.exception import SQSError from boto.sqs.message import Message, RawMessage from botocore.exceptions import ClientError from freezegun import freeze_time -from moto import mock_sqs, mock_sqs_deprecated, settings +from moto import mock_sqs, mock_sqs_deprecated, mock_cloudformation, settings from nose import SkipTest from nose.tools import assert_raises from tests.helpers import requires_boto_gte from moto.core import ACCOUNT_ID +sqs_template_with_tags = """ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "SQSQueue": { + "Type": "AWS::SQS::Queue", + "Properties": { + "Tags" : [ + { + "Key" : "keyname1", + "Value" : "value1" + }, + { + "Key" : "keyname2", + "Value" : "value2" + } + ] + } + } + } +}""" + @mock_sqs def test_create_fifo_queue_fail(): @@ -1933,3 +1955,17 @@ def test_send_messages_to_fifo_without_message_group_id(): ex.response["Error"]["Message"].should.equal( "The request must contain the parameter MessageGroupId." ) + + +@mock_sqs +@mock_cloudformation +def test_create_from_cloudformation_json_with_tags(): + cf = boto3.client("cloudformation", region_name="us-east-1") + client = boto3.client("sqs", region_name="us-east-1") + + cf.create_stack(StackName="test-sqs", TemplateBody=sqs_template_with_tags) + + queue_url = client.list_queues()["QueueUrls"][0] + + queue_tags = client.list_queue_tags(QueueUrl=queue_url)["Tags"] + queue_tags.should.equal({"keyname1": "value1", "keyname2": "value2"})