1- import textwrap
1+ import json
22
33import pytest
4+ from botocore .exceptions import WaiterError
45
56from localstack import config
67from localstack .services .cloudformation .engine .v2 import (
2223 CloudFormationResourcesSupportInLatest ,
2324)
2425from localstack .utils .strings import short_uid
25- from localstack .utils .sync import retry
2626
2727UNSUPPORTED_RESOURCE_CASES = [
2828 (
@@ -90,74 +90,41 @@ def testing_catalog(monkeypatch):
9090def test_ignore_unsupported_resources_toggle (testing_catalog , aws_client , monkeypatch , cleanups ):
9191 unsupported_resource = "AWS::LatestService::NotSupported"
9292
93- def _create_change_set (stack_name : str , template_body : str ):
94- change_set_name = f"cs-{ short_uid ()} "
95- response = aws_client .cloudformation .create_change_set (
96- StackName = stack_name ,
97- ChangeSetName = change_set_name ,
98- TemplateBody = template_body ,
99- ChangeSetType = "CREATE" ,
100- )
101- return response ["Id" ], response ["StackId" ]
102-
103- def _wait_for_failed_change_set (change_set_id : str ):
104- def _describe ():
105- result = aws_client .cloudformation .describe_change_set (ChangeSetName = change_set_id )
106- status = result ["Status" ]
107- if status == "FAILED" :
108- return result
109- if status == "CREATE_COMPLETE" :
110- pytest .fail ("expected change set creation to fail" )
111- raise Exception ("still waiting for change set to fail" )
112-
113- return retry (_describe , retries = 15 , sleep = 2 )
114-
115- def _wait_for_complete_change_set (change_set_id : str ):
116- def _describe ():
117- result = aws_client .cloudformation .describe_change_set (ChangeSetName = change_set_id )
118- status = result ["Status" ]
119- if status == "CREATE_COMPLETE" :
120- return result
121- if status == "FAILED" :
122- pytest .fail (f"change set unexpectedly failed: { result .get ('StatusReason' )} " )
123- raise Exception ("still waiting for change set" )
124-
125- return retry (_describe , retries = 15 , sleep = 2 )
126-
127- def _wait_for_stack_status (stack_name : str , expected : str ):
128- def _describe ():
129- stack = aws_client .cloudformation .describe_stacks (StackName = stack_name )["Stacks" ][0 ]
130- status = stack ["StackStatus" ]
131- if status == expected :
132- return stack
133- if status .endswith ("FAILED" ) or "ROLLBACK" in status :
134- pytest .fail (f"stack ended in failure: { status } ({ stack .get ('StackStatusReason' )} )" )
135- raise Exception ("still waiting for stack" )
136-
137- return retry (_describe , retries = 30 , sleep = 2 )
138-
13993 # template with one supported and one unsupported resource
14094 bucket_name = f"cfn-toggle-{ short_uid ()} "
141- template_body = textwrap .dedent (
142- f"""
143- AWSTemplateFormatVersion: '2010-09-09'
144- Resources:
145- SupportedBucket:
146- Type: AWS::S3::Bucket
147- Properties:
148- BucketName: { bucket_name }
149- Unsupported:
150- Type: { unsupported_resource }
151- """
95+ template_body = json .dumps (
96+ {
97+ "Resources" : {
98+ "SupportedBucket" : {
99+ "Type" : "AWS::S3::Bucket" ,
100+ "Properties" : {"BucketName" : bucket_name },
101+ },
102+ "Unsupported" : {"Type" : unsupported_resource },
103+ },
104+ }
152105 )
153106
154107 # 1) ignore lists empty -> change set should fail
155108 monkeypatch .setattr (config , "CFN_IGNORE_UNSUPPORTED_RESOURCE_TYPES" , False )
156109 monkeypatch .setattr (config , "CFN_IGNORE_UNSUPPORTED_TYPE_CREATE" , [])
157110 stack_name_fail = f"stack-fail-{ short_uid ()} "
158- cs_id_fail , stack_id_fail = _create_change_set (stack_name_fail , template_body )
159- failed_cs = _wait_for_failed_change_set (cs_id_fail )
160- status_reason = failed_cs .get ("StatusReason" , "" )
111+ change_set_name_fail = f"cs-{ short_uid ()} "
112+ response = aws_client .cloudformation .create_change_set (
113+ StackName = stack_name_fail ,
114+ ChangeSetName = change_set_name_fail ,
115+ TemplateBody = template_body ,
116+ ChangeSetType = "CREATE" ,
117+ )
118+ cs_id_fail , stack_id_fail = response ["Id" ], response ["StackId" ]
119+
120+ waiter = aws_client .cloudformation .get_waiter ("change_set_create_complete" )
121+ with pytest .raises (WaiterError ) as exc_info :
122+ waiter .wait (
123+ ChangeSetName = cs_id_fail ,
124+ )
125+
126+ assert exc_info .value .last_response ["Status" ] == "FAILED"
127+ status_reason = exc_info .value .last_response ["StatusReason" ]
161128 assert ChangeSetResourceSupportChecker .TITLE_MESSAGE in status_reason
162129 assert unsupported_resource in status_reason
163130 cleanups .append (lambda : aws_client .cloudformation .delete_change_set (ChangeSetName = cs_id_fail ))
@@ -166,10 +133,22 @@ def _describe():
166133 # 2) add unsupported resource to create ignore list -> deployment succeeds and bucket is present
167134 monkeypatch .setattr (config , "CFN_IGNORE_UNSUPPORTED_TYPE_CREATE" , [unsupported_resource ])
168135 stack_name_ok = f"stack-ok-{ short_uid ()} "
169- cs_id_ok , stack_id_ok = _create_change_set (stack_name_ok , template_body )
170- _wait_for_complete_change_set (cs_id_ok )
136+ change_set_name_ok = f"cs-{ short_uid ()} "
137+ response = aws_client .cloudformation .create_change_set (
138+ StackName = stack_name_ok ,
139+ ChangeSetName = change_set_name_ok ,
140+ TemplateBody = template_body ,
141+ ChangeSetType = "CREATE" ,
142+ )
143+ cs_id_ok , stack_id_ok = response ["Id" ], response ["StackId" ]
144+
145+ waiter .wait (
146+ ChangeSetName = cs_id_ok ,
147+ )
171148 aws_client .cloudformation .execute_change_set (ChangeSetName = cs_id_ok )
172- _wait_for_stack_status (stack_name_ok , "CREATE_COMPLETE" )
149+ aws_client .cloudformation .get_waiter ("stack_create_complete" ).wait (
150+ StackName = stack_name_ok ,
151+ )
173152
174153 buckets = aws_client .s3 .list_buckets ()["Buckets" ]
175154 assert any (b ["Name" ] == bucket_name for b in buckets )
@@ -186,13 +165,10 @@ def test_catalog_reports_unsupported_resources_in_stack_status(
186165 testing_catalog , aws_client , unsupported_resource , expected_service , monkeypatch , cleanups
187166):
188167 monkeypatch .setattr (config , "CFN_IGNORE_UNSUPPORTED_RESOURCE_TYPES" , False )
189- template_body = textwrap .dedent (
190- f"""
191- AWSTemplateFormatVersion: '2010-09-09'
192- Resources:
193- Unsupported:
194- Type: { unsupported_resource }
195- """
168+ template_body = json .dumps (
169+ {
170+ "Resources" : {"Unsupported" : {"Type" : unsupported_resource }},
171+ }
196172 )
197173
198174 stack_name = f"stack-{ short_uid ()} "
@@ -209,29 +185,22 @@ def test_catalog_reports_unsupported_resources_in_stack_status(
209185 change_set_id = response ["Id" ]
210186 stack_id = response ["StackId" ]
211187
212- def _describe_failed_change_set ():
213- result = aws_client .cloudformation .describe_change_set (ChangeSetName = change_set_id )
214- status = result ["Status" ]
215- if status == "FAILED" :
216- return result
217- if status == "CREATE_COMPLETE" :
218- pytest .fail ("expected change set creation to fail for unsupported resource" )
219- raise Exception ("gave up on waiting for change set creation to fail" )
220-
221- change_set = retry (_describe_failed_change_set , retries = 20 , sleep = 2 )
222-
223- status_reason = change_set .get ("StatusReason" , "" )
188+ waiter = aws_client .cloudformation .get_waiter ("change_set_create_complete" )
189+ with pytest .raises (WaiterError ) as exc_info :
190+ waiter .wait (
191+ ChangeSetName = change_set_id ,
192+ )
193+ assert exc_info .value .last_response ["Status" ] == "FAILED"
194+ status_reason = exc_info .value .last_response ["StatusReason" ]
224195 assert ChangeSetResourceSupportChecker .TITLE_MESSAGE in status_reason
225196 assert unsupported_resource in status_reason
226197
227- def _describe_failed_stack ():
228- stack = aws_client .cloudformation .describe_stacks (StackName = stack_id )["Stacks" ][0 ]
229- stack_status = stack ["StackStatus" ]
230- if stack_status in {"CREATE_FAILED" , "ROLLBACK_COMPLETE" }:
231- return stack
232- raise Exception ("gave on waiting for stack creation to fail for unsupported resource" )
198+ with pytest .raises (WaiterError ) as exc_info :
199+ aws_client .cloudformation .get_waiter ("stack_create_complete" ).wait (
200+ StackName = stack_id ,
201+ )
233202
234- stack_description = retry ( _describe_failed_stack , retries = 30 , sleep = 2 )
203+ stack_description = exc_info . value . last_response [ "Stacks" ][ 0 ]
235204 stack_status_reason = stack_description .get ("StackStatusReason" , "" )
236205 assert ChangeSetResourceSupportChecker .TITLE_MESSAGE in stack_status_reason
237206 assert unsupported_resource in stack_status_reason
0 commit comments