-
Notifications
You must be signed in to change notification settings - Fork 1
/
deploy.py
114 lines (95 loc) · 4.05 KB
/
deploy.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import os
import argparse
import boto3
from botocore.exceptions import ClientError, WaiterError
from pkg_resources import require
def stack_exists(stack_name):
try:
stack = cf.describe_stacks(StackName=stack_name).get('Stacks')[0]
except ClientError:
print(f'Stack {stack_name} does not exist. Creating!')
return False
if stack.get('StackStatus') in ['CREATE_FAILED', 'UPDATE_FAILED', 'ROLLBACK_COMPLETE', 'UPDATE_ROLLBACK_FAILED']:
print(f'{stack_name} is in status {stack.get("StackStatus")}. Cleaning up now!')
cleanup_bad_stack(stack)
return False
return True
def cleanup_bad_stack(stack):
print(f'Stack {stack.get("StackName")} cleaning up.')
try:
cf.delete_stack(StackName=stack.get('StackName'))
wait_for_stack(stack.get('StackName'), 'stack_delete_complete')
except ClientError as e:
print(f'Stack {stack.get("StackName")} failed to delete with error: {str(e)}. Check the console for more details.')
raise e
def wait_for_stack(stack_name, status):
print(f'Waiting for {stack_name} to reach status {status}')
try:
waiter = cf.get_waiter(status)
waiter.wait(StackName=stack_name)
print(f'{stack_name} reached {status}!')
except WaiterError as e:
print(f'Stack {stack_name} failed to reach {status}: {str(e)}. Check the console for more details.')
#Create clients needed for operation
s3 = boto3.client('s3')
cf = boto3.client('cloudformation')
arg_parser = argparse.ArgumentParser(description='Create, update, or destroy a nested stack.')
arg_parser.add_argument('-n', '--stack_name', help='Name of the stack to create or update', required=True)
arg_parser.add_argument('-d', '--destroy', help='Destroy stack.', action='store_true')
args = arg_parser.parse_args()
template_urls = {}
bucket_name = 'stelligent-demo-nested-stack-bucket'
stack_name = args.stack_name
if args.destroy:
print(f'Destroying {stack_name}')
cf.delete_stack(StackName=stack_name)
wait_for_stack(stack_name, 'stack_delete_complete')
print(f'Stack {stack_name} deleted successfully')
else:
print("Uploading templates")
for x in os.listdir():
if x.endswith('.yml'):
s3.upload_file(x, bucket_name, x)
if 'master' in x:
template_urls['master'] = f'https://s3-{os.environ["AWS_REGION"]}.amazonaws.com/{bucket_name}/{x}'
if 'EC2' in x:
template_urls['instance'] = f'https://s3-{os.environ["AWS_REGION"]}.amazonaws.com/{bucket_name}/{x}'
if 'vpc' in x:
template_urls['vpc'] = f'https://s3-{os.environ["AWS_REGION"]}.amazonaws.com/{bucket_name}/{x}'
# Deploy!
if not stack_exists(stack_name):
cf.create_stack(
StackName=stack_name,
TemplateURL=template_urls.get('master'),
Parameters=[
{
'ParameterKey': 'VPCTemplateURL',
'ParameterValue': template_urls.get('vpc')
},
{
'ParameterKey': 'EC2TemplateURL',
'ParameterValue': template_urls.get('instance')
}
],
Capabilities=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND']
)
wait_for_stack(stack_name, 'stack_create_complete')
else:
print(f'Updating {stack_name}...')
cf.update_stack(
StackName=stack_name,
TemplateURL=template_urls.get('master'),
Parameters=[
{
'ParameterKey': 'VPCTemplateURL',
'ParameterValue': template_urls.get('vpc')
},
{
'ParameterKey': 'EC2TemplateURL',
'ParameterValue': template_urls.get('instance')
}
],
Capabilities=['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND']
)
wait_for_stack(stack_name, 'stack_update_complete')
print(f'Stack {stack_name} update completed!')