Repository: localstack/localstack

Issue:
CloudFormation PhysicalResourceId for AWS::SQS::Queue should be the queue URL
According to the [AWS CFN docs for SQS](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html#aws-properties-sqs-queues-return-values-ref), the physical resource ID should be its queue URL.

>Ref
>
>When you pass the logical ID of this resource to the intrinsic Ref function, Ref returns the queue URL. For example:
>
>{ "Ref": "https://sqs.us-east-2.amazonaws.com/123456789012/aa4-MyQueue-Z5NOSZO2PZE9" }

However, SQS queues created in Localstack via CloudFormation have physical resource IDs that are equal to the queue name:

```
$ aws cloudformation list-stack-resources --stack-name my-stack --endpoint http://localhost:4581 | grep -A3 -B3 Queue
        {
            "LogicalResourceId": "EventQueue",
            "PhysicalResourceId": "my-stack-events",
            "ResourceType": "AWS::SQS::Queue",
            "LastUpdatedTimestamp": "2011-06-21T20:15:58Z",
            "ResourceStatus": "CREATE_COMPLETE"
        },
```

I believe that this [was fixed at one point](https://github.com/localstack/localstack/pull/577) in Localstack, but seems to have since been reverted. I've [submitted an upstream issue](https://github.com/spulec/moto/issues/2407) to Moto as well.


Discussion:
**Edit**
Patch turned out to be useless -- closed

~Have submitted a patch to Moto that addresses the issue (at least in the limited tests I've done with Serverless Framework).~

~https://github.com/spulec/moto/pull/2411~

Resolving patch:
diff --git a/localstack/services/cloudformation/cloudformation_starter.py b/localstack/services/cloudformation/cloudformation_starter.py
--- a/localstack/services/cloudformation/cloudformation_starter.py
+++ b/localstack/services/cloudformation/cloudformation_starter.py
@@ -352,6 +352,19 @@ def SQS_Queue_get_cfn_attribute(self, attribute_name):
     SQS_Queue_get_cfn_attribute_orig = sqs_models.Queue.get_cfn_attribute
     sqs_models.Queue.get_cfn_attribute = SQS_Queue_get_cfn_attribute
 
+    # Patch SQS physical_resource_id(..) method in moto
+
+    @property
+    def SQS_Queue_physical_resource_id(self):
+        result = SQS_Queue_physical_resource_id_orig.fget(self)
+        if '://' not in result:
+            # convert ID to queue URL
+            return aws_stack.get_sqs_queue_url(result)
+        return result
+
+    SQS_Queue_physical_resource_id_orig = sqs_models.Queue.physical_resource_id
+    sqs_models.Queue.physical_resource_id = SQS_Queue_physical_resource_id
+
     # Patch Lambda get_cfn_attribute(..) method in moto
 
     def Lambda_Function_get_cfn_attribute(self, attribute_name):
diff --git a/localstack/services/infra.py b/localstack/services/infra.py
--- a/localstack/services/infra.py
+++ b/localstack/services/infra.py
@@ -19,7 +19,8 @@
 from localstack.utils.common import (TMP_THREADS, run, get_free_tcp_port,
     FuncThread, ShellCommandThread, get_service_protocol, in_docker, is_port_open)
 from localstack.utils.server import multiserver
-from localstack.utils.bootstrap import setup_logging, is_debug, canonicalize_api_names, load_plugins
+from localstack.utils.bootstrap import (
+    setup_logging, is_debug, canonicalize_api_names, load_plugins, in_ci)
 from localstack.utils.analytics import event_publisher
 from localstack.services import generic_proxy, install
 from localstack.services.es import es_api
@@ -403,7 +404,8 @@ def start_infra(asynchronous=False, apis=None):
         # load plugins
         load_plugins()
 
-        event_publisher.fire_event(event_publisher.EVENT_START_INFRA, {'d': in_docker() and 1 or 0})
+        event_publisher.fire_event(event_publisher.EVENT_START_INFRA,
+            {'d': in_docker() and 1 or 0, 'c': in_ci() and 1 or 0})
 
         # set up logging
         setup_logging()


Test patch:
diff --git a/tests/integration/templates/template2.yaml b/tests/integration/templates/template2.yaml
--- a/tests/integration/templates/template2.yaml
+++ b/tests/integration/templates/template2.yaml
@@ -18,3 +18,7 @@ Resources:
         - Protocol: sqs
           Endpoint:
             "Fn::GetAtt": ["SQSQueue2", "Arn"]
+Outputs:
+  SQSQueue1URL:
+    Value:
+      Ref: SQSQueue1
diff --git a/tests/integration/test_cloudformation.py b/tests/integration/test_cloudformation.py
--- a/tests/integration/test_cloudformation.py
+++ b/tests/integration/test_cloudformation.py
@@ -25,8 +25,9 @@ def bucket_exists(name):
 def queue_exists(name):
     sqs_client = aws_stack.connect_to_service('sqs')
     queues = sqs_client.list_queues()
+    url = name if '://' in name else aws_stack.get_sqs_queue_url(name)
     for queue_url in queues['QueueUrls']:
-        if queue_url.endswith('/%s' % name):
+        if queue_url == url:
             return True
 
 
@@ -116,7 +117,7 @@ def check_stack():
 
         # assert that topic tags have been created
         tags = s3.get_bucket_tagging(Bucket='cf-test-bucket-1')['TagSet']
-        self.assertEqual(tags, [{'Key': 'foobar', 'Value': 'cf-test-queue-1'}])
+        self.assertEqual(tags, [{'Key': 'foobar', 'Value': aws_stack.get_sqs_queue_url('cf-test-queue-1')}])
 
     def test_list_stack_events(self):
         cloudformation = aws_stack.connect_to_service('cloudformation')
@@ -152,15 +153,14 @@ def check_stack():
 
         retry(check_stack, retries=3, sleep=2)
 
-        list_stack_summaries = list_stack_resources(TEST_STACK_NAME_2)
+        stack_summaries = list_stack_resources(TEST_STACK_NAME_2)
         queue_urls = get_queue_urls()
         topic_arns = get_topic_arns()
 
-        stack_queues = [r for r in list_stack_summaries if r['ResourceType'] == 'AWS::SQS::Queue']
+        stack_queues = [r for r in stack_summaries if r['ResourceType'] == 'AWS::SQS::Queue']
         for resource in stack_queues:
-            url = aws_stack.get_sqs_queue_url(resource['PhysicalResourceId'])
-            self.assertIn(url, queue_urls)
+            self.assertIn(resource['PhysicalResourceId'], queue_urls)
 
-        stack_topics = [r for r in list_stack_summaries if r['ResourceType'] == 'AWS::SNS::Topic']
+        stack_topics = [r for r in stack_summaries if r['ResourceType'] == 'AWS::SNS::Topic']
         for resource in stack_topics:
             self.assertIn(resource['PhysicalResourceId'], topic_arns)