98 lines
3.3 KiB
Python
98 lines
3.3 KiB
Python
import re
|
|
|
|
import boto3
|
|
import botocore
|
|
|
|
|
|
def stack_exists(args, *, stack_name):
|
|
client = boto3.client("cloudformation", region_name=args.aws_region)
|
|
waiter = client.get_waiter("stack_exists")
|
|
try:
|
|
waiter.wait(StackName=stack_name, WaiterConfig={"MaxAttempts": 1})
|
|
return True
|
|
except botocore.exceptions.WaiterError as e:
|
|
return False
|
|
|
|
|
|
def create_or_update_stack(
|
|
args, *, client, stack_name, template_url=None, template_body=None, params=None
|
|
):
|
|
kwargs = {
|
|
"StackName": stack_name,
|
|
"Capabilities": [
|
|
"CAPABILITY_IAM",
|
|
"CAPABILITY_NAMED_IAM",
|
|
"CAPABILITY_AUTO_EXPAND",
|
|
],
|
|
}
|
|
if template_url:
|
|
kwargs["TemplateURL"] = template_url
|
|
if template_body:
|
|
kwargs["TemplateBody"] = template_body
|
|
if params:
|
|
kwargs["Parameters"] = params
|
|
|
|
if stack_exists(args, stack_name=stack_name):
|
|
print(f"Stack {stack_name} already exists. Updating...")
|
|
try:
|
|
response = client.update_stack(**kwargs)
|
|
return {"StackName": stack_name, "Action": "update"}
|
|
except botocore.exceptions.ClientError as e:
|
|
if e.response["Error"]["Code"] == "ValidationError" and re.search(
|
|
"No updates are to be performed", e.response["Error"]["Message"]
|
|
):
|
|
print(f"No update was made to {stack_name}")
|
|
return {"StackName": stack_name, "Action": "noop"}
|
|
else:
|
|
raise e
|
|
else:
|
|
kwargs.update({"OnFailure": "ROLLBACK", "EnableTerminationProtection": False})
|
|
response = client.create_stack(**kwargs)
|
|
return {"StackName": stack_name, "Action": "create"}
|
|
|
|
|
|
def replace_stack(
|
|
args, *, client, stack_name, template_url=None, template_body=None, params=None
|
|
):
|
|
"""Delete an existing stack and create a new stack with identical name"""
|
|
|
|
if not stack_exists(args, stack_name=stack_name):
|
|
raise ValueError(f"Stack {stack_name} does not exist")
|
|
r = client.delete_stack(StackName=stack_name)
|
|
delete_waiter = client.get_waiter("stack_delete_complete")
|
|
delete_waiter.wait(StackName=stack_name)
|
|
|
|
kwargs = {
|
|
"StackName": stack_name,
|
|
"Capabilities": [
|
|
"CAPABILITY_IAM",
|
|
"CAPABILITY_NAMED_IAM",
|
|
"CAPABILITY_AUTO_EXPAND",
|
|
],
|
|
"OnFailure": "ROLLBACK",
|
|
"EnableTerminationProtection": False,
|
|
}
|
|
if template_url:
|
|
kwargs["TemplateURL"] = template_url
|
|
if template_body:
|
|
kwargs["TemplateBody"] = template_body
|
|
if params:
|
|
kwargs["Parameters"] = params
|
|
response = client.create_stack(**kwargs)
|
|
return {"StackName": stack_name, "Action": "create"}
|
|
|
|
|
|
def wait(promise, *, client):
|
|
stack_name = promise["StackName"]
|
|
print(f"Waiting for {stack_name}...")
|
|
if promise["Action"] == "create":
|
|
waiter = client.get_waiter("stack_create_complete")
|
|
waiter.wait(StackName=stack_name)
|
|
print(f"Finished creating stack {stack_name}")
|
|
elif promise["Action"] == "update":
|
|
waiter = client.get_waiter("stack_update_complete")
|
|
waiter.wait(StackName=stack_name)
|
|
print(f"Finished updating stack {stack_name}")
|
|
elif promise["Action"] != "noop":
|
|
raise ValueError(f"Invalid promise {promise}")
|