@@ -64,6 +64,35 @@ class SQSQueueProvider(ResourceProvider[SQSQueueProperties]):
6464 TYPE = "AWS::SQS::Queue" # Autogenerated. Don't change
6565 SCHEMA = util .get_schema_path (Path (__file__ )) # Autogenerated. Don't change
6666
67+ # Values used when a property is removed from a template and needs to be set to its default.
68+ # If AWS changes their defaults in the future, our parity tests should break.
69+ DEFAULT_ATTRIBUTE_VALUES = {
70+ "ReceiveMessageWaitTimeSeconds" : "0" ,
71+ "DelaySeconds" : "0" ,
72+ "KmsMasterKeyId" : "" ,
73+ "RedrivePolicy" : "" ,
74+ "MessageRetentionPeriod" : "345600" ,
75+ "MaximumMessageSize" : "262144" , # Note: CloudFormation sets this to 256KB on update, but 1MB on create
76+ "VisibilityTimeout" : "30" ,
77+ "KmsDataKeyReusePeriodSeconds" : "300" ,
78+ }
79+
80+ # Private method for creating a unique queue name, if none is specified.
81+ def _autogenerated_queue_name (self , request : ResourceRequest [SQSQueueProperties ]) -> str :
82+ queue_name = util .generate_default_name (request .stack_name , request .logical_resource_id )
83+ isFifoQueue = request .desired_state .get ("FifoQueue" )
84+
85+ # Note that it's an SQS FIFO queue only if the FifoQueue property is set to boolean True, or the string "true"
86+ # (case insensitive). If it's None (property was omitted) or False, or any type of string (e.g. a typo
87+ # such as "Fasle"), then it's not a FIFO queue. This extra check is needed because the CloudFormation engine
88+ # doesn't fully validate the FifoQueue property before passing it to the resource provider.
89+ if (
90+ isFifoQueue == True # noqa: E712
91+ or (isinstance (isFifoQueue , str ) and isFifoQueue .lower () == "true" )
92+ ):
93+ queue_name = f"{ queue_name [:- 5 ]} .fifo"
94+ return queue_name
95+
6796 def create (
6897
1AF0
self ,
6998 request : ResourceRequest [SQSQueueProperties ],
@@ -74,8 +103,6 @@ def create(
74103 Primary identifier fields:
75104 - /properties/QueueUrl
76105
77-
78-
79106 Create-only properties:
80107 - /properties/FifoQueue
81108 - /properties/QueueName
@@ -92,26 +119,13 @@ def create(
92119 - sqs:TagQueue
93120
94121 """
95- # TODO: validations
122+ # TODO: validations - what validations are needed?
96123 model = request .desired_state
97124 sqs = request .aws_client_factory .sqs
98125
99- if model .get ("FifoQueue" , False ):
100- model ["FifoQueue" ] = model ["FifoQueue" ]
101-
102- queue_name = model .get ("QueueName" )
103- if not queue_name :
104- # TODO: verify patterns here
105- if model .get ("FifoQueue" ):
106- queue_name = util .generate_default_name (
107- request .stack_name , request .logical_resource_id
108- )[:- 5 ]
109- queue_name = f"{ queue_name } .fifo"
110- else :
111- queue_name = util .generate_default_name (
112- request .stack_name , request .logical_resource_id
113- )
114- model ["QueueName" ] = queue_name
126+ # if no QueueName is specified, automatically generate one
127+ if not model .get ("QueueName" ):
128+ model ["QueueName" ] = self ._autogenerated_queue_name (request )
115129
116130 attributes = self ._compile_sqs_queue_attributes (model )
117131 result = request .aws_client_factory .sqs .create_queue (
@@ -184,38 +198,30 @@ def update(
184198 """
185199 sqs = request .aws_client_factory .sqs
186200 model = request .desired_state
201+ prev_model = request .previous_state
187202
188203 assert request .previous_state is not None
189204
190- should_replace = (
191- request .desired_state .get ("QueueName" , request .previous_state ["QueueName" ])
192 - != request .previous_state ["QueueName" ]
193- ) or (
194- request .desired_state .get ("FifoQueue" , request .previous_state .get ("FifoQueue" ))
195- != request .previous_state .get ("FifoQueue" )
205+ queue_url = prev_model ["QueueUrl" ]
206+ self ._populate_missing_attributes_with_defaults (model )
207+ sqs .set_queue_attributes (
208+ QueueUrl = queue_url , Attributes = self ._compile_sqs_queue_attributes (model )
196209 )
197210
198- if not should_replace :
199- return ProgressEvent (OperationStatus .SUCCESS , resource_model = request .previous_state )
200-
201- # TODO: copied from the create handler, extract?
202- if model .get ("FifoQueue" ):
203- queue_name = util .generate_default_name (
204- request .stack_name , request .logical_resource_id
205- )[:- 5 ]
206- queue_name = f"{ queue_name } .fifo"
207- else :
208- queue_name = util .generate_default_name (request .stack_name , request .logical_resource_id )
209-
210- # replacement (TODO: find out if we should handle this in the provider or outside of it)
211- # delete old queue
212- sqs .delete_queue (QueueUrl = request .previous_state ["QueueUrl" ])
213- # create new queue (TODO: re-use create logic to make this more robust, e.g. for
214- # auto-generated queue names)
215- model ["QueueUrl" ] = sqs .create_queue (QueueName = queue_name )["QueueUrl" ]
216- model ["Arn" ] = sqs .get_queue_attributes (
217- QueueUrl = model ["QueueUrl" ], AttributeNames = ["QueueArn" ]
218- )["Attributes" ]["QueueArn" ]
211+ (tags_to_remove , tags_to_add_or_update ) = util .resource_tags_to_remove_or_update (
212+ prev_model .get ("Tags" , []), model .get ("Tags" , [])
213+ )
214+ sqs .untag_queue (QueueUrl = queue_url , TagKeys = tags_to_remove )
215+ sqs .tag_queue (QueueUrl = queue_url , Tags = tags_to_add_or_update )
216+
217+ model ["QueueUrl" ] = queue_url
218+ model ["Arn" ] = request .previous_state ["Arn" ]
219+
220+ # For QueueName and FifoQueue, always use the value from the previous model. These fields
221+ # are create-only, so they cannot be changed via an update (even though they might be omitted)
222+ model ["QueueName" ] = prev_model .get ("QueueName" )
223+ model ["FifoQueue" ] = prev_model .get ("FifoQueue" , False )
224+
<
F0D0
/code>219225 return ProgressEvent (OperationStatus .SUCCESS , resource_model = model )
220226
221227 def _compile_sqs_queue_attributes (self , properties : SQSQueueProperties ) -> dict [str , str ]:
@@ -250,6 +256,15 @@ def _compile_sqs_queue_attributes(self, properties: SQSQueueProperties) -> dict[
250256
251257 return result
252258
259+ def _populate_missing_attributes_with_defaults (self , properties : SQSQueueProperties ) -> None :
260+ """
261+ For any attribute that is missing from the desired state, populate it with the default value.
262+ This is the only way to remove an attribute from an existing SQS queue's configuration.
263+ :param properties: the properties passed from cloudformation
264+ """
265+ for k , v in self .DEFAULT_ATTRIBUTE_VALUES .items ():
266+ properties .setdefault (k , v )
267+
253268 def list (
254269 self ,
255270 request : ResourceRequest [SQSQueueProperties ],
0 commit comments