1616 CreateChangeSetOutput ,
1717 CreateStackInput ,
1818 CreateStackOutput ,
19+ DeleteChangeSetOutput ,
1920 DeletionMode ,
2021 DescribeChangeSetOutput ,
2122 DescribeStackEventsOutput ,
@@ -82,9 +83,14 @@ def is_changeset_arn(change_set_name_or_id: str) -> bool:
8283 return ARN_CHANGESET_REGEX .match (change_set_name_or_id ) is not None
8384
8485
85- class StackNotFoundError (ValidationError ):
86+ class StackWithNameNotFoundError (ValidationError ):
8687 def __init__ (self , stack_name : str ):
87- super ().__init__ (f"Stack with id { stack_name } does not exist" )
88+ super ().__init__ (f"Stack [{ stack_name } ] does not exist" )
89+
90+
91+ class StackWithIdNotFoundError (ValidationError ):
92+ def __init__ (self , stack_id : str ):
93+ super ().__init__ ("Stack with id <stack-name> does not exist" )
8894
8995
9096def find_stack_v2 (state : CloudFormationStore , stack_name : str | None ) -> Stack | None :
@@ -115,7 +121,7 @@ def find_change_set_v2(
115121 if stack_name is not None :
116122 stack = find_stack_v2 (state , stack_name )
117123 if not stack :
118- raise StackNotFoundError (stack_name )
124+ raise StackWithNameNotFoundError (stack_name )
119125
120126 for change_set_id in stack .change_set_ids :
121127 change_set_candidate = state .change_sets [change_set_id ]
@@ -448,6 +454,33 @@ def describe_change_set(
448454 )
449455 return result
450456
457+ @handler ("DeleteChangeSet" )
458+ def delete_change_set (
459+ self ,
460+ context : RequestContext ,
461+ change_set_name : ChangeSetNameOrId ,
462+ stack_name : StackNameOrId = None ,
463+ ** kwargs ,
464+ ) -> DeleteChangeSetOutput :
465+ state = get_cloudformation_store (context .account_id , context .region )
466+
467+ if is_changeset_arn (change_set_name ):
468+ change_set = state .change_sets .get (change_set_name )
469+ elif not is_changeset_arn (change_set_name ) and stack_name :
470+ change_set = find_change_set_v2 (state , change_set_name , stack_name )
471+ else :
472+ raise ValidationError (
473+ "StackName must be specified if ChangeSetName is not specified as an ARN."
474+ )
475+
476+ if not change_set :
477+ return DeleteChangeSetOutput ()
478+
479+ change_set .stack .change_set_ids .remove (change_set .change_set_id )
480+ state .change_sets .pop (change_set .change_set_id )
481+
482+ return DeleteChangeSetOutput ()
483+
451484 @handler ("CreateStack" , expand = False )
452485 def create_stack (self , context : RequestContext , request : CreateStackInput ) -> CreateStackOutput :
453486 try :
@@ -548,7 +581,7 @@ def describe_stacks(
548581 state = get_cloudformation_store (context .account_id , context .region )
549582 stack = find_stack_v2 (state , stack_name )
550583 if not stack :
551- raise StackNotFoundError (stack_name )
584+ raise StackWithIdNotFoundError (stack_name )
552585 return DescribeStacksOutput (Stacks = [stack .describe_details ()])
553586
554587 @handler ("DescribeStackResources" )
@@ -565,7 +598,7 @@ def describe_stack_resources(
565598 state = get_cloudformation_store (context .account_id , context .region )
566599 stack = find_stack_v2 (state , stack_name )
567600 if not stack :
568- raise StackNotFoundError (stack_name )
601+ raise StackWithIdNotFoundError (stack_name )
569602 # TODO: filter stack by PhysicalResourceId!
570603 statuses = []
571604 for resource_id , resource_status in stack .resource_states .items ():
@@ -586,7 +619,7 @@ def describe_stack_events(
586619 state = get_cloudformation_store (context .account_id , context .region )
587620 stack = find_stack_v2 (state , stack_name )
588621 if not stack :
589- raise StackNotFoundError (stack_name )
622+ raise StackWithIdNotFoundError (stack_name )
590623 return DescribeStackEventsOutput (StackEvents = stack .events )
591624
592625 @handler ("GetTemplateSummary" , expand = False )
@@ -601,7 +634,7 @@ def get_template_summary(
601634 if stack_name :
602635 stack = find_stack_v2 (state , stack_name )
603636 if not stack :
604- raise StackNotFoundError (stack_name )
637+ raise StackWithIdNotFoundError (stack_name )
605638 template = stack .template
606639 else :
607640 template_body = request .get ("TemplateBody" )
0 commit comments