Repository: celery/kombu

Issue:
Kombu cannot create SQS queues with server-side encryption turned on
We need to configure queues to use server-side encryption when the queues are automatically created by Kombu. This can be done by setting the `KmsMasterKeyId ` and `KmsDataKeyReusePeriodSeconds` keys of the Attributes `boto3 sqs.create_queue ` parameter.

Having a way to set arbitrary Attributes values as new features are added to SQS would be nice.



Discussion:

Resolving patch:
diff --git a/kombu/transport/SQS.py b/kombu/transport/SQS.py
--- a/kombu/transport/SQS.py
+++ b/kombu/transport/SQS.py
@@ -9,16 +9,14 @@
 
 SQS Features supported by this transport:
   Long Polling:
-    https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/
-      sqs-long-polling.html
+    https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-long-polling.html
 
     Long polling is enabled by setting the `wait_time_seconds` transport
     option to a number > 1.  Amazon supports up to 20 seconds.  This is
     enabled with 10 seconds by default.
 
   Batch API Actions:
-   https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/
-     sqs-batch-api.html
+   https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-batch-api.html
 
     The default behavior of the SQS Channel.drain_events() method is to
     request up to the 'prefetch_count' messages on every request to SQS.
@@ -33,7 +31,7 @@
     up to 'prefetch_count' messages from queueA and work on them all before
     moving on to queueB.  If queueB is empty, it will wait up until
     'polling_interval' expires before moving back and checking on queueA.
-"""
+"""  # noqa: E501
 
 from __future__ import absolute_import, unicode_literals
 
@@ -183,11 +181,23 @@ def _new_queue(self, queue, **kwargs):
             if queue.endswith('.fifo'):
                 attributes['FifoQueue'] = 'true'
 
-            resp = self._queue_cache[queue] = self.sqs.create_queue(
-                QueueName=queue, Attributes=attributes)
+            resp = self._create_queue(queue, attributes)
             self._queue_cache[queue] = resp['QueueUrl']
             return resp['QueueUrl']
 
+    def _create_queue(self, queue_name, attributes):
+        """Create an SQS queue with a given name and nominal attributes."""
+        # Allow specifying additional boto create_queue Attributes
+        # via transport options
+        attributes.update(
+            self.transport_options.get('sqs-creation-attributes') or {},
+        )
+
+        return self.sqs.create_queue(
+            QueueName=queue_name,
+            Attributes=attributes,
+        )
+
     def _delete(self, queue, *args, **kwargs):
         """Delete queue by name."""
         super(Channel, self)._delete(queue)
@@ -504,7 +514,34 @@ def wait_time_seconds(self):
 
 
 class Transport(virtual.Transport):
-    """SQS Transport."""
+    """SQS Transport.
+
+    Additional queue attributes can be supplied to SQS during queue
+    creation by passing an ``sqs-creation-attributes`` key in
+    transport_options. ``sqs-creation-attributes`` must be a dict whose
+    key-value pairs correspond with Attributes in the
+    `CreateQueue SQS API`_.
+
+    For example, to have SQS queues created with server-side encryption
+    enabled using the default Amazon Managed Customer Master Key, you
+    can set ``KmsMasterKeyId`` Attribute. When the queue is initially
+    created by Kombu, encryption will be enabled.
+
+    .. code-block:: python
+
+        from kombu.transport.SQS import Transport
+
+        transport = Transport(
+            ...,
+            transport_options={
+                'sqs-creation-attributes': {
+                    'KmsMasterKeyId': 'alias/aws/sqs',
+                },
+            }
+        )
+
+    .. _CreateQueue SQS API: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_CreateQueue.html#API_CreateQueue_RequestParameters
+    """  # noqa: E501
 
     Channel = Channel
 


Test patch:
diff --git a/t/unit/transport/test_SQS.py b/t/unit/transport/test_SQS.py
--- a/t/unit/transport/test_SQS.py
+++ b/t/unit/transport/test_SQS.py
@@ -35,8 +35,10 @@ def __init__(self):
 class QueueMock(object):
     """ Hold information about a queue. """
 
-    def __init__(self, url):
+    def __init__(self, url, creation_attributes=None):
         self.url = url
+        # arguments of boto3.sqs.create_queue
+        self.creation_attributes = creation_attributes
         self.attributes = {'ApproximateNumberOfMessages': '0'}
 
         self.messages = []
@@ -70,7 +72,10 @@ def _get_q(self, url):
         raise Exception("Queue url {} not found".format(url))
 
     def create_queue(self, QueueName=None, Attributes=None):
-        q = self._queues[QueueName] = QueueMock('sqs://' + QueueName)
+        q = self._queues[QueueName] = QueueMock(
+            'sqs://' + QueueName,
+            Attributes,
+        )
         return {'QueueUrl': q.url}
 
     def list_queues(self, QueueNamePrefix=None):
@@ -226,6 +231,22 @@ def test_new_queue(self):
         # For cleanup purposes, delete the queue and the queue file
         self.channel._delete(queue_name)
 
+    def test_new_queue_custom_creation_attributes(self):
+        self.connection.transport_options['sqs-creation-attributes'] = {
+            'KmsMasterKeyId': 'alias/aws/sqs',
+        }
+        queue_name = 'new_custom_attribute_queue'
+        self.channel._new_queue(queue_name)
+
+        assert queue_name in self.sqs_conn_mock._queues.keys()
+        queue = self.sqs_conn_mock._queues[queue_name]
+
+        assert 'KmsMasterKeyId' in queue.creation_attributes
+        assert queue.creation_attributes['KmsMasterKeyId'] == 'alias/aws/sqs'
+
+        # For cleanup purposes, delete the queue and the queue file
+        self.channel._delete(queue_name)
+
     def test_dont_create_duplicate_new_queue(self):
         # All queue names start with "q", except "unittest_queue".
         # which is definitely out of cache when get_all_queues returns the