Repository: localstack/localstack

Issue:
Unable to create fifo queue with new release 1.0.4.
In release 1.0.4, I can no longer create a fifo queue by setting the attribute FifoQueue=true.

I believe it is due to this code from this issue: https://github.com/localstack/localstack/pull/6117/files

FifoQueue was set as an internal attribute which cannot be changed: 

(snip from PR)
INTERNAL_QUEUE_ATTRIBUTES = [
    # these attributes cannot be changed by set_queue_attributes and should
    # therefore be ignored when comparing queue attributes for create_queue
    QueueAttributeName.ApproximateNumberOfMessages,
    QueueAttributeName.ApproximateNumberOfMessagesDelayed,
    QueueAttributeName.ApproximateNumberOfMessagesNotVisible,
    QueueAttributeName.ContentBasedDeduplication,
    QueueAttributeName.CreatedTimestamp,
    **QueueAttributeName.FifoQueue,**
    QueueAttributeName.LastModifiedTimestamp,
    QueueAttributeName.QueueArn,
]

Now I see this error when attempting to create a fifo queue with the java sdk. This worked last week fine, no changes other than pulling the latest localstack image.

nested exception is com.amazonaws.services.sqs.model.InvalidAttributeNameException: Unknown Attribute FifoQueue.


Discussion:
Welcome to LocalStack! Thanks for reporting your first issue and our team will be working towards fixing the issue for you or reach out for more background information. We recommend joining our [Slack Community](https://localstack.cloud/contact/) for real-time help and drop a message to LocalStack Pro Support if you are a Pro user! If you are willing to contribute towards fixing this issue, please have a look at our [contributing guidelines](https://github.com/localstack/.github/blob/main/CONTRIBUTING.md) and our [developer guide](https://docs.localstack.cloud/developer-guide/).

Resolving patch:
diff --git a/localstack/services/sqs/provider.py b/localstack/services/sqs/provider.py
--- a/localstack/services/sqs/provider.py
+++ b/localstack/services/sqs/provider.py
@@ -95,12 +95,12 @@
 INTERNAL_QUEUE_ATTRIBUTES = [
     # these attributes cannot be changed by set_queue_attributes and should
     # therefore be ignored when comparing queue attributes for create_queue
+    # 'FifoQueue' is handled on a per_queue basis
     QueueAttributeName.ApproximateNumberOfMessages,
     QueueAttributeName.ApproximateNumberOfMessagesDelayed,
     QueueAttributeName.ApproximateNumberOfMessagesNotVisible,
     QueueAttributeName.ContentBasedDeduplication,
     QueueAttributeName.CreatedTimestamp,
-    QueueAttributeName.FifoQueue,
     QueueAttributeName.LastModifiedTimestamp,
     QueueAttributeName.QueueArn,
 ]
@@ -564,7 +564,11 @@ def _assert_queue_name(self, name):
             )
 
     def validate_queue_attributes(self, attributes):
-        valid = [k[1] for k in inspect.getmembers(QueueAttributeName)]
+        valid = [
+            k[1]
+            for k in inspect.getmembers(QueueAttributeName)
+            if k not in INTERNAL_QUEUE_ATTRIBUTES
+        ]
         del valid[valid.index(QueueAttributeName.FifoQueue)]
 
         for k in attributes.keys():
@@ -705,8 +709,11 @@ def _assert_queue_name(self, name):
         super()._assert_queue_name(queue_name)
 
     def validate_queue_attributes(self, attributes):
-        valid = [k[1] for k in inspect.getmembers(QueueAttributeName)]
-
+        valid = [
+            k[1]
+            for k in inspect.getmembers(QueueAttributeName)
+            if k not in INTERNAL_QUEUE_ATTRIBUTES
+        ]
         for k in attributes.keys():
             if k not in valid:
                 raise InvalidAttributeName(f"Unknown Attribute {k}.")
@@ -936,7 +943,7 @@ def create_queue(
 
                 if attributes:
                     # if attributes are set, then we check whether the existing attributes match the passed ones
-                    self._validate_queue_attributes(attributes)
+                    queue.validate_queue_attributes(attributes)
                     for k, v in attributes.items():
                         if queue.attributes.get(k) != v:
                             LOG.debug(
@@ -1476,13 +1483,6 @@ def _create_message_attributes(
 
         return result
 
-    def _validate_queue_attributes(self, attributes: QueueAttributeMap):
-        valid = [k[1] for k in inspect.getmembers(QueueAttributeName)]
-
-        for k in attributes.keys():
-            if k not in valid or k in INTERNAL_QUEUE_ATTRIBUTES:
-                raise InvalidAttributeName(f"Unknown Attribute {k}.")
-
     def _validate_actions(self, actions: ActionNameList):
         service = load_service(service=self.service, version=self.version)
         # FIXME: this is a bit of a heuristic as it will also include actions like "ListQueues" which is not


Test patch:
diff --git a/tests/integration/test_sqs.py b/tests/integration/test_sqs.py
--- a/tests/integration/test_sqs.py
+++ b/tests/integration/test_sqs.py
@@ -447,6 +447,13 @@ def test_create_queue_with_default_attributes_is_idempotent(self, sqs_client, sq
         queue_url = sqs_create_queue(QueueName=queue_name, Attributes=attributes)
         assert sqs_create_queue(QueueName=queue_name) == queue_url
 
+    @pytest.mark.aws_validated
+    def test_create_fifo_queue_with_same_attributes_is_idempotent(self, sqs_create_queue):
+        queue_name = f"queue-{short_uid()}.fifo"
+        attributes = {"FifoQueue": "true"}
+        queue_url = sqs_create_queue(QueueName=queue_name, Attributes=attributes)
+        assert sqs_create_queue(QueueName=queue_name, Attributes=attributes) == queue_url
+
     @pytest.mark.aws_validated
     def test_create_queue_with_different_attributes_raises_exception(
         self, sqs_client, sqs_create_queue, snapshot