blogs/ecs-canary-deployments-pipeline/setup/scripts/delete_blog_contents.py (149 lines of code) (raw):
""" Cleanup script for the ECS-BlogPost. """
#!/usr/bin/env python
import os
import time
import boto3
from botocore.exceptions import ClientError, WaiterError
#Find the EnvironmentName parameter
AWS_REGION = os.environ.get('AWS_REGION', 'us-west-2')
ENVIRONMENT_NAME = str(os.environ.get('EnvironmentName', 'ecs-blogpost'))
#Client connections
APPMESH_CLIENT = boto3.client('appmesh', region_name=AWS_REGION)
CFN_CLIENT = boto3.client('cloudformation', region_name=AWS_REGION)
ECR_CLIENT = boto3.client('ecr', region_name=AWS_REGION)
SSM_CLIENT = boto3.client('ssm', region_name=AWS_REGION)
S3_RESOURCE = boto3.resource('s3')
S3_CLIENT = boto3.client('s3')
def _list_stacks():
""" List CFN Stacks by the desired prefix """
stacks_tobe_deleted = []
response = CFN_CLIENT.list_stacks(
StackStatusFilter=['CREATE_COMPLETE', 'DELETE_FAILED']
)
for stack in response['StackSummaries']:
if stack['StackName'].startswith(ENVIRONMENT_NAME):
stacks_tobe_deleted.append(stack['StackName'])
print("The list of stacks being deleted are: {}".format(stacks_tobe_deleted))
return stacks_tobe_deleted
def _delete_cfn_stack(stack):
""" Deletes CFN stack """
retries = 3
while True:
try:
CFN_CLIENT.delete_stack(
StackName=stack
)
waiter = CFN_CLIENT.get_waiter('stack_delete_complete')
waiter.wait(StackName=stack)
print("Deleted the CloudFormation stack: {} successfully.".format(stack))
return True
except WaiterError as _ex:
retries-=1
if retries<1:
print("CloudFormation Stack: {} deletion failed during the cleanup workflow.".format(stack))
return False
print("Sleeping for 60seconds during the cleanup workflow before second attempt of cleanup.")
time.sleep(60)
def _delete_route(apps):
""" Delete Routes in AppMesh Virtual Router """
for app in apps:
try:
APPMESH_CLIENT.delete_route(
meshName=ENVIRONMENT_NAME,
routeName=app+'-route',
virtualRouterName=app+'-vr'
)
except ClientError as ex:
if ex.response['Error']['Code'] == 'NotFoundException':
print("Could not find the route: {} on the VR: {}, no action needed".format(app+'-route', app+'-vr'))
continue
else:
raise ex
def _delete_ecr_images(apps):
""" Delete the ECR Images built by individual apps. """
for app in apps:
try:
ECR_CLIENT.delete_repository(
repositoryName=app,
force=True
)
print("Deleted the ECR Repository: {} successfully.".format(app))
except ClientError as ex:
if ex.response['Error']['Code'] == 'RepositoryNotFoundException':
print("Repository: {} is not found, no action needed".format(app))
continue
else:
raise ex
return True
def _delete_ssm_params(apps):
""" Delete the app specific SSM Param store. """
for app in apps:
try:
SSM_CLIENT.delete_parameter(
Name=ENVIRONMENT_NAME+'-canary-'+app+'-version'
)
except ClientError as ex:
if ex.response['Error']['Code'] == 'ParameterNotFound':
print("SSM Parameter for the app: {} is not found, no action needed".format(app))
continue
else:
raise ex
print("Deleted the App Specific Parameter store successfully.")
def main():
""" Main function. """
apps = [
'yelb-ui',
'yelb-appserver',
'yelb-redisserver',
'yelb-db'
]
#1. List the CFN Stacks and filter by the prefix 'ecs-blogpost'(which was the default EnvironmentName)
stacks_tobe_deleted = _list_stacks()
#2.1 Update the VR to set the routes to empty
_delete_route(apps)
#2.2 Delete the SSM Parameters created by Canary Stack.
_delete_ssm_params(apps)
#2.3 Iterate through the stacks_tobe_deleted, filter the canary stacks and delete them.
for app in apps:
for stack in stacks_tobe_deleted:
pattern = '{}-{}-'.format(ENVIRONMENT_NAME, app)
if stack.startswith(pattern):
_delete_cfn_stack(stack)
stacks_tobe_deleted.remove(stack)
else:
continue
#3. Iterate through the stacks_tobe_deleted, filter the app specific pipeline stack.
if _delete_ecr_images(apps):
for app in apps:
for stack in stacks_tobe_deleted:
pattern = '{}-pipeline-{}'.format(ENVIRONMENT_NAME, app)
if stack.startswith(pattern):
_delete_cfn_stack(stack)
stacks_tobe_deleted.remove(stack)
#4. Find the S3 bucket which hosted Pipeline artifacts.
for stack in stacks_tobe_deleted:
pattern = '{}-deployment-stepfunctions'.format(ENVIRONMENT_NAME)
if stack.startswith(pattern):
response = CFN_CLIENT.describe_stacks(
StackName=stack
)['Stacks'][0]['Outputs']
for output in response:
pattern = '{}-deployment-'.format(ENVIRONMENT_NAME)
if (output['OutputValue']).startswith(pattern):
if S3_RESOURCE.Bucket(output['OutputValue']) in S3_RESOURCE.buckets.all():
bucket = S3_RESOURCE.Bucket(output['OutputValue'])
bucket.objects.all().delete()
else:
break
_delete_cfn_stack(stack)
stacks_tobe_deleted.remove(stack)
#4.1 Find the S3 bucket which we created externally to support artifacts attachment into Lambda functions.
pattern = 'ecs-canary-blogpost-cloudformation-files-'
buckets_list = S3_CLIENT.list_buckets()
for bucket in buckets_list['Buckets']:
if bucket["Name"].startswith(pattern):
print(f'{bucket["Name"]} is about to be deleted')
bucket = S3_RESOURCE.Bucket(bucket["Name"])
bucket.objects.all().delete()
bucket.delete()
#5. Delete the Monitoring Stack.
for stack in stacks_tobe_deleted:
pattern = '{}-monitoring-resources'.format(ENVIRONMENT_NAME)
if stack.startswith(pattern):
_delete_cfn_stack(stack)
stacks_tobe_deleted.remove(stack)
#6. Delete the clusterresources Stack.
for stack in stacks_tobe_deleted:
pattern = '{}-clusterresources'.format(ENVIRONMENT_NAME)
if stack.startswith(pattern):
_delete_cfn_stack(stack)
stacks_tobe_deleted.remove(stack)
#7. Delete the VPC Stack.
for stack in stacks_tobe_deleted:
pattern = '{}-vpc'.format(ENVIRONMENT_NAME)
if stack.startswith(pattern):
_delete_cfn_stack(stack)
stacks_tobe_deleted.remove(stack)
print("Cleanup Done Successfully...Have a good day!")
if __name__=='__main__':
main()