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 (
@@ -86,6 +86,76 @@ def testing_catalog(monkeypatch):
8686 return plugin
8787
8888
89+ @markers .aws .only_localstack
90+ def test_ignore_unsupported_resources_toggle (testing_catalog , aws_client , monkeypatch , cleanups ):
91+ unsupported_resource = "AWS::LatestService::NotSupported"
92+
93+ # template with one supported and one unsupported resource
94+ bucket_name = f"cfn-toggle-{ short_uid ()} "
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+ }
105+ )
106+
107+ # 1) ignore lists empty -> change set should fail
108+ monkeypatch .setattr (config , "CFN_IGNORE_UNSUPPORTED_RESOURCE_TYPES" , False )
109+ monkeypatch .setattr (config , "CFN_IGNORE_UNSUPPORTED_TYPE_CREATE" , [])
110+ stack_name_fail = f"stack-fail-{ short_uid ()} "
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" ]
128+ assert ChangeSetResourceSupportChecker .TITLE_MESSAGE in status_reason
129+ assert unsupported_resource in status_reason
130+ cleanups .append (lambda : aws_client .cloudformation .delete_change_set (ChangeSetName = cs_id_fail ))
131+ cleanups .append (lambda : aws_client .cloudformation .delete_stack (StackName = stack_id_fail ))
132+
133+ # 2) add unsupported resource to create ignore list -> deployment succeeds and bucket is present
134+ monkeypatch .setattr (config , "CFN_IGNORE_UNSUPPORTED_TYPE_CREATE" , [unsupported_resource ])
135+ stack_name_ok = f"stack-ok-{ short_uid ()} "
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+ )
148+ aws_client .cloudformation .execute_change_set (ChangeSetName = cs_id_ok )
149+ aws_client .cloudformation .get_waiter ("stack_create_complete" ).wait (
150+ StackName = stack_name_ok ,
151+ )
152+
153+ buckets = aws_client .s3 .list_buckets ()["Buckets" ]
154+ assert any (b ["Name" ] == bucket_name for b in buckets )
155+
156+ cleanups .append (lambda : aws_client .cloudformation .delete_stack (StackName = stack_id_ok ))
157+
158+
89159@markers .aws .only_localstack
90160@pytest .mark .parametrize (
91161 "unsupported_resource, expected_service" ,
@@ -95,13 +165,10 @@ def test_catalog_reports_unsupported_resources_in_stack_status(
95165 testing_catalog , aws_client , unsupported_resource , expected_service , monkeypatch , cleanups
96166):
97167 monkeypatch .setattr (config , "CFN_IGNORE_UNSUPPORTED_RESOURCE_TYPES" , False )
98- template_body = textwrap .dedent (
99- f"""
100- AWSTemplateFormatVersion: '2010-09-09'
101- Resources:
102- Unsupported:
103- Type: { unsupported_resource }
104- """
168+ template_body = json .dumps (
169+ {
170+ "Resources" : {"Unsupported" : {"Type" : unsupported_resource }},
171+ }
105172 )
106173
107174 stack_name = f"stack-{ short_uid ()} "
@@ -118,29 +185,22 @@ def test_catalog_reports_unsupported_resources_in_stack_status(
118185 change_set_id = response ["Id" ]
119186 stack_id = response ["StackId" ]
120187
121- def _describe_failed_change_set ():
122- result = aws_client .cloudformation .describe_change_set (ChangeSetName = change_set_id )
123- status = result ["Status" ]
124- if status == "FAILED" :
125- return result
126- if status == "CREATE_COMPLETE" :
127- pytest .fail ("expected change set creation to fail for unsupported resource" )
128- raise Exception ("gave up on waiting for change set creation to fail" )
129-
130- change_set = retry (_describe_failed_change_set , retries = 20 , sleep = 2 )
131-
132- 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" ]
133195 assert ChangeSetResourceSupportChecker .TITLE_MESSAGE in status_reason
134196 assert unsupported_resource in status_reason
135197
136- def _describe_failed_stack ():
137- stack = aws_client .cloudformation .describe_stacks (StackName = stack_id )["Stacks" ][0 ]
138- stack_status = stack ["StackStatus" ]
139- if stack_status in {"CREATE_FAILED" , "ROLLBACK_COMPLETE" }:
140- return stack
141- 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+ )
142202
143- stack_description = retry ( _describe_failed_stack , retries = 30 , sleep = 2 )
203+ stack_description = exc_info . value . last_response [ "Stacks" ][ 0 ]
144204 stack_status_reason = stack_description .get ("StackStatusReason" , "" )
145205 assert ChangeSetResourceSupportChecker .TITLE_MESSAGE in stack_status_reason
146206 assert unsupported_resource in stack_status_reason
0 commit comments