Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
f12e425
Added ecr_bootstrap
CoshUS Feb 8, 2021
650fe2c
Added companion_stack_manager
CoshUS Feb 11, 2021
5397803
Added Companion Stack Manager
CoshUS Feb 16, 2021
6751928
Added update_companion_stack
CoshUS Feb 17, 2021
6c034ec
Updated companion_stack_builder File Name
CoshUS Feb 18, 2021
f6204af
Formatted with Black
CoshUS Feb 18, 2021
13b425f
Updated get_unreferenced_repos
CoshUS Mar 1, 2021
25d2dad
Updated guided_context to Use Companion Stack
CoshUS Mar 1, 2021
fbe5a66
Added Delete Auto Create ECR Repo Prompt
CoshUS Mar 1, 2021
0b9a881
Updated prompt_image_repository Flow
CoshUS Mar 1, 2021
676fa01
Added --resolve-image-repos
CoshUS Mar 4, 2021
2e16290
Addressed Some of Pylint Issues
CoshUS Mar 4, 2021
739b550
Updated Helper Text
CoshUS Apr 14, 2021
c48e07d
Merge branch 'develop' into feat/auto-ecr
CoshUS Apr 14, 2021
701932c
Updated Comments
CoshUS Apr 14, 2021
f1f9f45
Fixed Typing
CoshUS Apr 14, 2021
b82d544
Removed Unused Imports
CoshUS Apr 14, 2021
2d2cf5b
Updated Unit Tests
CoshUS Apr 15, 2021
9fb143e
Updated UX and Fixed Windows ANSI
CoshUS Apr 15, 2021
f015829
Updated Unit Tests
CoshUS Apr 16, 2021
cdef3a6
Fixed Import Order
CoshUS Apr 16, 2021
23576c3
Merge branch 'develop' into feat/auto-ecr
CoshUS Apr 16, 2021
6dd8c67
Added Ignore Import Check
CoshUS Apr 16, 2021
addd5a1
Added Integration Tests
CoshUS Apr 16, 2021
92002d4
Merge branch 'develop' into feat/auto-ecr
CoshUS Apr 16, 2021
edf778d
Updated help text.
CoshUS Apr 16, 2021
f8244a4
Added Comments for Name Generation
CoshUS Apr 16, 2021
0214950
Updated Image Option Validator
CoshUS Apr 19, 2021
7b5a46c
Updated CompanionStackBuilder to Use Dict instead of String
CoshUS Apr 19, 2021
c6f2698
Merge branch 'develop' into feat/auto-ecr
CoshUS Apr 19, 2021
5edd495
Fixed Argument Ordering
CoshUS Apr 23, 2021
7b3572e
Merge branch 'develop' into feat/auto-ecr
CoshUS Apr 23, 2021
9ffd4bd
Added Mapping Information to Help Text
CoshUS Apr 26, 2021
a83f7fc
Merge branch 'feat/auto-ecr' of https://github.com/CoshUS/aws-sam-cli…
CoshUS Apr 26, 2021
79ef739
Updated delete_unreferenced_repos Doc String
CoshUS Apr 26, 2021
90b5b4c
Updated sync_repos Doc String
CoshUS Apr 26, 2021
4157cb9
Added Justification for ECR Repo Physical ID
CoshUS Apr 26, 2021
5d84813
Refactored to be Less Coupled
CoshUS Apr 26, 2021
ce355cf
Refactored for prompt_specify_repos
CoshUS Apr 26, 2021
366b7f7
Fixed Unit Test
CoshUS Apr 26, 2021
c653dff
Moved WaiterConfig Out of Methods
CoshUS Apr 26, 2021
4be0e11
Updated Typing
CoshUS Apr 26, 2021
c18ad41
Updated Managed S3 Template to be Dict
CoshUS Apr 26, 2021
191309a
Fixed Typo
CoshUS Apr 26, 2021
bfdf4a7
Added Comments for _save_image_repositories
CoshUS Apr 26, 2021
e03c48a
Fixed Pylint Issue
CoshUS Apr 26, 2021
b016b14
Merge branch 'develop' into feat/auto-ecr
CoshUS Apr 26, 2021
85ced27
Added Missing Check for unreferenced_repo_uris
CoshUS Apr 27, 2021
6c3f54a
Merge branch 'feat/auto-ecr' of https://github.com/CoshUS/aws-sam-cli…
CoshUS Apr 27, 2021
1946945
Merge branch 'develop' into feat/auto-ecr
mgrandis May 3, 2021
7693d9a
Updated Variable Name
CoshUS May 4, 2021
6393418
Fixed Typo
CoshUS May 4, 2021
7411f9f
Updated Windows Check to Use platform.system()
CoshUS May 4, 2021
f3b5bcb
Updated update_companion_stack Logic
CoshUS May 4, 2021
3039f18
Merge branch 'feat/auto-ecr' of https://github.com/CoshUS/aws-sam-cli…
CoshUS May 4, 2021
3575581
Fixed Comment Typo
CoshUS May 4, 2021
17a7a41
Fixed Typos
CoshUS May 4, 2021
8a1c458
Fixed Test Name
CoshUS May 4, 2021
1263735
Added methods for cf and s3 files and init UI
hnnasit Jun 21, 2021
ba47369
Added unit tests for utils methods and s3_uploader
hnnasit Jun 23, 2021
d77f7c4
Removed s3_bucket and s3_prefix click options
hnnasit Jun 24, 2021
d506648
chore: Increase awareness of same file warning during package (#2946)
mndeveci Jun 24, 2021
698de67
fix: Allow the base64Encoded field in REST Api, skip validation of un…
moelasmar Jun 24, 2021
3f6f070
Fixed lint errors and added few unit tests
hnnasit Jun 25, 2021
af2f929
Make black happy
hnnasit Jun 25, 2021
481ef8a
Merge pull request #1 from hnnasit/delete-cf-s3-methods
hnnasit Jun 28, 2021
1d70155
Added methods for deleting template artifacts
hnnasit Jun 28, 2021
e3f7872
Wait method added for delete cf api
hnnasit Jun 28, 2021
3b2cdf1
fix: pass copy of environment variables for keeping cache valid (#2943)
mndeveci Jun 29, 2021
99f7db4
Added LOG statements
hnnasit Jun 29, 2021
e7304ec
Added and updated changes based on CR
hnnasit Jun 29, 2021
c2e43db
Fixed the unit tests in artifact_exporter.py
hnnasit Jun 29, 2021
dd35d53
Update HELP_TEXT in delete/command.py
hnnasit Jun 29, 2021
6fc1b99
fix: Skip build of Docker image if ImageUri is a valid ECR URL (#2934…
alexisfacques Jun 29, 2021
f43e763
Updated code based on Chris' comments
hnnasit Jun 30, 2021
5779cd3
Added condition for resources that have deletionpolicy specified
hnnasit Jun 30, 2021
bc9db14
Small changes and fixes based on the comments
hnnasit Jun 30, 2021
0d58f89
Add condition to managed bucket policy (#2999)
qingchm Jun 30, 2021
401a950
Removed region prompt
hnnasit Jun 30, 2021
50ac3cb
Update appveyor.yml to do docker login on both dockerhub and Public E…
qingchm Jul 1, 2021
f1e0569
chore: bump version to 1.25.0 (#3007)
qingchm Jul 1, 2021
2241563
temp: reduce python testing matrix (#3008)
sriram-mv Jul 1, 2021
fe83218
temp: disable testing against python 3.8, and enabled 3.7 (#3009)
moelasmar Jul 1, 2021
cc806a2
fix: enable all runtimes in python testing matrix (#3011)
qingchm Jul 1, 2021
0a38340
Added unit tests for ecr delete method and typing for methods
hnnasit Jul 3, 2021
8977c6a
Merge branch 'delete-template-artifacts' into delete-cf-s3-methods
hnnasit Jul 3, 2021
99c80e1
Merge pull request #2 from hnnasit/delete-cf-s3-methods
hnnasit Jul 3, 2021
aaa1b05
Reformatted delete_context and added option to skip user prompts
hnnasit Jul 5, 2021
e2e85a9
Removed return type from artifact_exporter for delete method
hnnasit Jul 5, 2021
c98e6ee
Added unit tests for artifact_exporter and delete_context
hnnasit Jul 5, 2021
17a427a
Added more unit tests for delete_context and artifact_exporter
hnnasit Jul 5, 2021
ac4e485
chore: update to aws-sam-translator 1.37.0 (#3019)
mgrandis Jul 6, 2021
a1e5c92
chore: bump version to 1.26.0 (#3020)
mgrandis Jul 6, 2021
e577d7f
Added more unit tests for delete_context and artifact_exporter
hnnasit Jul 6, 2021
70eac98
Merged feat/sam_delete changes into delete_template_artifacts repo
hnnasit Jul 6, 2021
58ead71
Added docs and comments for artifact_exporter and ecr_uploader
hnnasit Jul 6, 2021
45ee66f
Added log statements in delete_context and some updates in unit tests
hnnasit Jul 7, 2021
d151b01
Changed force to no-prompts and updated ecr delete method error hand…
hnnasit Jul 8, 2021
59c8576
chore: Improved --resolve-s3 option documentation and deployment with…
qingchm Jul 8, 2021
6f54240
Created a separate function for parsing ecr url in ecr_uploader
hnnasit Jul 9, 2021
30e3c02
Reformatted Template class init to pass template_str and init templat…
hnnasit Jul 9, 2021
b8a2591
Changed how s3 url is obtained for resource_zip edge-case: aws:glue:job
hnnasit Jul 9, 2021
7292353
Fixed edge case where resource artifact points to a path style url
hnnasit Jul 9, 2021
6ff2e22
run Make black
hnnasit Jul 9, 2021
6c9a060
Made the parse s3 url funcs protected and defined a parent method and…
hnnasit Jul 10, 2021
1675b7e
Changed parse_ecr_url function name to parse_image_url
hnnasit Jul 12, 2021
c4e9632
Merge branch 'develop' into feat/auto-ecr
CoshUS Jul 12, 2021
0e159c2
Merge remote-tracking branch 'wiltons-repo/feat/auto-ecr' into auto-e…
hnnasit Jul 13, 2021
5355028
Defined UI for auto ecr deleton and method calls from companion_stack…
hnnasit Jul 13, 2021
b160efc
Added code for deleting repos from companion stack
hnnasit Jul 13, 2021
4267e82
Handle json templates deployed to cf
hnnasit Jul 14, 2021
b172255
Changed the order of companion stack and ecr repos deletion
hnnasit Jul 14, 2021
7b5732d
Handle delete_failed status for ecr companion stack and changed delet…
hnnasit Jul 15, 2021
bbe7cec
Reformatted auto ecr deletion to handle deleting companion stack as i…
hnnasit Jul 15, 2021
8b0d979
Fixed and added more unit tests for delete_context
hnnasit Jul 15, 2021
189e383
When region is not provided, prompt user to enter profile and region
hnnasit Jul 16, 2021
a933202
Removed region prompt and reading it from current session or assign a…
hnnasit Jul 18, 2021
80f6561
Added ECR resource in packageable_resources and refactored ecr compan…
hnnasit Jul 19, 2021
8d19497
Added log statements and unit tests for ECRResource
hnnasit Jul 19, 2021
b7ffe45
Merged feat/sam_delete changes into auto-ecr-delete
hnnasit Jul 19, 2021
7bcd636
Better error handling for ecr delete_artifact
hnnasit Jul 19, 2021
9363b93
Revert "Merge remote-tracking branch 'wiltons-repo/feat/auto-ecr' int…
hnnasit Jul 20, 2021
203ea3a
Added unit test for delete ecr repository
hnnasit Jul 20, 2021
0b25fbb
Fixed small string nits and added docstring for ECRResource
hnnasit Jul 20, 2021
37a6510
Added some unit tests for s3_uploader, ecr_uploader and delete_context
hnnasit Jul 20, 2021
d3e0782
Updated to context refresh only when region and profile have non None…
hnnasit Jul 20, 2021
80d5428
Added unit test for ResourceImageDict class methods
hnnasit Jul 20, 2021
ec467db
Merge remote-tracking branch 'upstream/feat/sam_delete' into revert-w…
hnnasit Jul 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions samcli/commands/_utils/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
AWS_GLUE_JOB = "AWS::Glue::Job"
AWS_SERVERLESS_STATEMACHINE = "AWS::Serverless::StateMachine"
AWS_STEPFUNCTIONS_STATEMACHINE = "AWS::StepFunctions::StateMachine"
AWS_ECR_REPOSITORY = "AWS::ECR::Repository"

METADATA_WITH_LOCAL_PATHS = {AWS_SERVERLESSREPO_APPLICATION: ["LicenseUrl", "ReadmeUrl"]}

Expand Down Expand Up @@ -50,6 +51,7 @@
RESOURCES_WITH_IMAGE_COMPONENT = {
AWS_SERVERLESS_FUNCTION: ["ImageUri"],
AWS_LAMBDA_FUNCTION: ["Code"],
AWS_ECR_REPOSITORY: ["RepositoryName"],
}


Expand Down
174 changes: 147 additions & 27 deletions samcli/commands/delete/delete_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,26 @@
Delete a SAM stack
"""
import logging

import json
import boto3


import click
from click import confirm
from click import prompt

from samcli.lib.utils.hash import str_checksum
from samcli.cli.cli_config_file import TomlProvider
from samcli.lib.utils.botoconfig import get_boto_config_with_user_agent
from samcli.lib.delete.cf_utils import CfUtils

from samcli.lib.package.s3_uploader import S3Uploader
from samcli.lib.package.artifact_exporter import mktempfile, get_cf_template_name

from samcli.cli.context import Context

from samcli.commands.delete.exceptions import CfDeleteFailedStatusError

from samcli.lib.package.artifact_exporter import Template
from samcli.lib.package.ecr_uploader import ECRUploader
Expand All @@ -38,10 +46,12 @@ def __init__(self, stack_name: str, region: str, profile: str, config_file: str,
self.s3_prefix = None
self.cf_utils = None
self.s3_uploader = None
self.ecr_uploader = None
self.uploaders = None
self.cf_template_file_name = None
self.delete_artifacts_folder = None
self.delete_cf_template_file = None
self.companion_stack_name = None

def __enter__(self):
self.parse_config_file()
Expand Down Expand Up @@ -69,22 +79,30 @@ def parse_config_file(self):
if not self.stack_name:
self.stack_name = config_options.get("stack_name", None)
# If the stack_name is same as the one present in samconfig file,
# get the information about parameters if not specified by customer.
# get the information about parameters if not specified by user.
if self.stack_name and self.stack_name == config_options.get("stack_name", None):
LOG.debug("Local config present and using the defined options")
if not self.region:
self.region = config_options.get("region", None)
click.get_current_context().region = self.region
if not self.profile:
self.profile = config_options.get("profile", None)
click.get_current_context().profile = self.profile
self.s3_bucket = config_options.get("s3_bucket", None)
self.s3_prefix = config_options.get("s3_prefix", None)

def init_clients(self):
"""
Initialize all the clients being used by sam delete.
"""
if not self.region:
session = boto3.Session()
region = session.region_name
self.region = region if region else "us-east-1"

if self.profile:
Context.get_current_context().profile = self.profile
if self.region:
Context.get_current_context().region = self.region

boto_config = get_boto_config_with_user_agent()

# Define cf_client based on the region as different regions can have same stack-names
Expand All @@ -95,30 +113,29 @@ def init_clients(self):
s3_client = boto3.client("s3", region_name=self.region if self.region else None, config=boto_config)
ecr_client = boto3.client("ecr", region_name=self.region if self.region else None, config=boto_config)

self.region = s3_client._client_config.region_name if s3_client else self.region # pylint: disable=W0212
self.s3_uploader = S3Uploader(s3_client=s3_client, bucket_name=self.s3_bucket, prefix=self.s3_prefix)

ecr_uploader = ECRUploader(docker_client=None, ecr_client=ecr_client, ecr_repo=None, ecr_repo_multi=None)
self.ecr_uploader = ECRUploader(docker_client=None, ecr_client=ecr_client, ecr_repo=None, ecr_repo_multi=None)

self.uploaders = Uploaders(self.s3_uploader, ecr_uploader)
self.uploaders = Uploaders(self.s3_uploader, self.ecr_uploader)
self.cf_utils = CfUtils(cloudformation_client)

def guided_prompts(self):
def s3_prompts(self):
"""
Guided prompts asking customer to delete artifacts
Guided prompts asking user to delete s3 artifacts
"""
# Note: s3_bucket and s3_prefix information is only
# available if a local toml file is present or if
# this information is obtained from the template resources and so if this
# information is not found, warn the customer that S3 artifacts
# information is not found, warn the user that S3 artifacts
# will need to be manually deleted.

if not self.no_prompts and self.s3_bucket:
if self.s3_prefix:
self.delete_artifacts_folder = confirm(
click.style(
"\tAre you sure you want to delete the folder"
+ f" {self.s3_prefix} in S3 which contains the artifacts?",
f" {self.s3_prefix} in S3 which contains the artifacts?",
bold=True,
),
default=False,
Expand All @@ -137,6 +154,85 @@ def guided_prompts(self):
else:
self.delete_cf_template_file = True

def ecr_companion_stack_prompts(self):
"""
User prompt to delete the ECR companion stack.
"""
click.echo(f"\tFound ECR Companion Stack {self.companion_stack_name}")
if not self.no_prompts:
delete_ecr_companion_stack_prompt = confirm(
click.style(
"\tDo you you want to delete the ECR companion stack"
f" {self.companion_stack_name} in the region {self.region} ?",
bold=True,
),
default=False,
)
return delete_ecr_companion_stack_prompt
return True

def ecr_repos_prompts(self, template: Template):
"""
User prompts to delete the ECR repositories for the given template.

:param template: Template to get the ECR repositories.
"""
retain_repos = []
ecr_repos = template.get_ecr_repos()

if not self.no_prompts:
for logical_id in ecr_repos:
# Get all the repos from the companion stack
repo = ecr_repos[logical_id]
repo_name = repo["Repository"]

delete_repo = confirm(
click.style(
f"\tECR repository {repo_name}"
" may not be empty. Do you want to delete the repository and all the images in it ?",
bold=True,
),
default=False,
)
if not delete_repo:
retain_repos.append(logical_id)
return retain_repos

def delete_ecr_companion_stack(self):
"""
Delete the ECR companion stack and ECR repositories based
on user input.
"""
delete_ecr_companion_stack_prompt = self.ecr_companion_stack_prompts()
if delete_ecr_companion_stack_prompt or self.no_prompts:
cf_ecr_companion_stack = self.cf_utils.get_stack_template(self.companion_stack_name, TEMPLATE_STAGE)
ecr_stack_template_str = cf_ecr_companion_stack.get("TemplateBody", None)
ecr_stack_template_str = json.dumps(ecr_stack_template_str, indent=4, ensure_ascii=False)

ecr_companion_stack_template = Template(
template_path=None,
parent_dir=None,
uploaders=self.uploaders,
code_signer=None,
template_str=ecr_stack_template_str,
)

retain_repos = self.ecr_repos_prompts(ecr_companion_stack_template)

# Delete the repos created by ECR companion stack if not retained
ecr_companion_stack_template.delete(retain_resources=retain_repos)

click.echo(f"\t- Deleting ECR Companion Stack {self.companion_stack_name}")
try:
# If delete_stack fails and its status changes to DELETE_FAILED, retain
# the user input repositories and delete the stack.
self.cf_utils.delete_stack(stack_name=self.companion_stack_name)
self.cf_utils.wait_for_delete(stack_name=self.companion_stack_name)
LOG.debug("Deleted ECR Companion Stack: %s", self.companion_stack_name)
except CfDeleteFailedStatusError:
LOG.debug("delete_stack resulted failed and so re-try with retain_resources")
self.cf_utils.delete_stack(stack_name=self.companion_stack_name, retain_resources=retain_repos)

def delete(self):
"""
Delete method calls for Cloudformation stacks and S3 and ECR artifacts
Expand All @@ -145,12 +241,19 @@ def delete(self):
cf_template = self.cf_utils.get_stack_template(self.stack_name, TEMPLATE_STAGE)
template_str = cf_template.get("TemplateBody", None)

if isinstance(template_str, dict):
template_str = json.dumps(template_str, indent=4, ensure_ascii=False)

# Get the cloudformation template name using template_str
with mktempfile() as temp_file:
self.cf_template_file_name = get_cf_template_name(temp_file, template_str, "template")

template = Template(
template_path=None, parent_dir=None, uploaders=self.uploaders, code_signer=None, template_str=template_str
template_path=None,
parent_dir=None,
uploaders=self.uploaders,
code_signer=None,
template_str=template_str,
)

# If s3 info is not available, try to obtain it from CF
Expand All @@ -163,16 +266,21 @@ def delete(self):
self.s3_prefix = s3_info["s3_prefix"]
self.s3_uploader.prefix = self.s3_prefix

self.guided_prompts()
self.s3_prompts()

retain_resources = self.ecr_repos_prompts(template)

# Delete the primary stack
click.echo(f"\n\t- Deleting Cloudformation stack {self.stack_name}")
self.cf_utils.delete_stack(stack_name=self.stack_name)
self.cf_utils.wait_for_delete(self.stack_name)
LOG.debug("Deleted Cloudformation stack: %s", self.stack_name)
# ECR companion stack delete prompts, if it exists
parent_stack_hash = str_checksum(self.stack_name)
possible_companion_stack_name = f"{self.stack_name[:104]}-{parent_stack_hash[:8]}-CompanionStack"
ecr_companion_stack_exists = self.cf_utils.has_stack(stack_name=possible_companion_stack_name)
if ecr_companion_stack_exists:
LOG.debug("ECR Companion stack found for the input stack")
self.companion_stack_name = possible_companion_stack_name
self.delete_ecr_companion_stack()

# Delete the artifacts
template.delete()
# Delete the artifacts and retain resources user selected not to delete
template.delete(retain_resources=retain_resources)

# Delete the CF template file in S3
if self.delete_cf_template_file:
Expand All @@ -182,24 +290,33 @@ def delete(self):
elif self.delete_artifacts_folder:
self.s3_uploader.delete_prefix_artifacts()

# If s3_bucket information is not available
elif not self.s3_bucket:
# Delete the primary input stack
try:
click.echo(f"\t- Deleting Cloudformation stack {self.stack_name}")
self.cf_utils.delete_stack(stack_name=self.stack_name)
self.cf_utils.wait_for_delete(self.stack_name)
LOG.debug("Deleted Cloudformation stack: %s", self.stack_name)
except CfDeleteFailedStatusError:
LOG.debug("delete_stack resulted failed and so re-try with retain_resources")
self.cf_utils.delete_stack(stack_name=self.stack_name, retain_resources=retain_resources)

# If s3_bucket information is not available, warn the user
if not self.s3_bucket:
LOG.debug("Cannot delete s3 files as no s3_bucket found")
click.secho(
"\nWarning: s3_bucket and s3_prefix information cannot be obtained,"
" delete the files manually if required",
"\nWarning: s3_bucket and s3_prefix information could not be obtained from local config file"
" or cloudformation template, delete the s3 files manually if required",
fg="yellow",
)

def run(self):
"""
Delete the stack based on the argument provided by customers and samconfig.toml.
Delete the stack based on the argument provided by user and samconfig.toml.
"""
if not self.no_prompts:
delete_stack = confirm(
click.style(
f"\tAre you sure you want to delete the stack {self.stack_name}"
+ f" in the region {self.region} ?",
f"\tAre you sure you want to delete the stack {self.stack_name}" f" in the region {self.region} ?",
bold=True,
),
default=False,
Expand All @@ -214,4 +331,7 @@ def run(self):
click.echo("\nDeleted successfully")
else:
LOG.debug("Input stack does not exists on Cloudformation")
click.echo(f"Error: The input stack {self.stack_name} does not exist on Cloudformation")
click.echo(
f"Error: The input stack {self.stack_name} does"
f" not exist on Cloudformation in the region {self.region}"
)
10 changes: 10 additions & 0 deletions samcli/commands/delete/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ def __init__(self, stack_name, msg):
super().__init__(message=message_fmt.format(stack_name=self.stack_name, msg=msg))


class CfDeleteFailedStatusError(UserException):
def __init__(self, stack_name, msg):
self.stack_name = stack_name
self.msg = msg

message_fmt = "Stack could not be deleted as it encountered DELETE_FAILED status: {stack_name}, {msg}"

super().__init__(message=message_fmt.format(stack_name=self.stack_name, msg=msg))


class FetchTemplateFailedError(UserException):
def __init__(self, stack_name, msg):
self.stack_name = stack_name
Expand Down
20 changes: 10 additions & 10 deletions samcli/lib/delete/cf_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import logging


from typing import Dict
from typing import Dict, List, Optional
from botocore.exceptions import ClientError, BotoCoreError, WaiterError
from samcli.commands.delete.exceptions import DeleteFailedError, FetchTemplateFailedError
from samcli.commands.delete.exceptions import DeleteFailedError, FetchTemplateFailedError, CfDeleteFailedStatusError


LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -84,14 +84,17 @@ def get_stack_template(self, stack_name: str, stage: str) -> Dict:
LOG.error("Unable to get stack details.", exc_info=e)
raise e

def delete_stack(self, stack_name: str):
def delete_stack(self, stack_name: str, retain_resources: Optional[List] = None):
"""
Delete the Cloudformation stack with the given stack_name

:param stack_name: Name or ID of the stack
:param retain_resources: List of repositories to retain if the stack has DELETE_FAILED status.
"""
if not retain_resources:
retain_resources = []
try:
self._client.delete_stack(StackName=stack_name)
self._client.delete_stack(StackName=stack_name, RetainResources=retain_resources)

except (ClientError, BotoCoreError) as e:
# If there are credentials, environment errors,
Expand Down Expand Up @@ -120,10 +123,7 @@ def wait_for_delete(self, stack_name):
waiter.wait(StackName=stack_name, WaiterConfig=waiter_config)
except WaiterError as ex:

resp = ex.last_response
status = resp["Status"]
reason = resp["StatusReason"]
if "DELETE_FAILED" in str(ex):
raise CfDeleteFailedStatusError(stack_name=stack_name, msg="ex: {0}".format(ex)) from ex

raise DeleteFailedError(
stack_name=stack_name, msg="ex: {0} Status: {1}. Reason: {2}".format(ex, status, reason)
) from ex
raise DeleteFailedError(stack_name=stack_name, msg="ex: {0}".format(ex)) from ex
Loading