-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtasks.py
139 lines (104 loc) · 4.06 KB
/
tasks.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import json
from pathlib import Path
from getpass import getpass
from invoke import task
import boto3
from botocore.exceptions import ClientError
def get_pass(secret, stage):
return getpass(f"Enter a password to use for [{stage}] {secret}: ")
def get_sendgrid_key(secret, stage):
return getpass(f"Enter the {stage} Sendgrid API key: ")
def get_stripe_keys(secret, stage):
pk = getpass(f"Enter the {stage} stripe publishable key: ")
sk = getpass(f"Enter the {stage} stripe secret key: ")
return json.dumps({"sk": sk, "pk": pk})
SECRETS = {
"dev": {
"Chicon8/tokens/session/dev": get_pass,
"Chicon8/tokens/jwt/dev": get_pass,
"Chicon8/sendgrid_api_key/dev": get_sendgrid_key,
"Chicon8/db/registration_dev/registration_dev_admin/dev": get_pass,
"Chicon8/sidekiq_password/dev": get_pass,
"Chicon8/stripe_api_key/dev": get_stripe_keys,
},
"staging": {
"Chicon8/tokens/session/staging": get_pass,
"Chicon8/tokens/jwt/staging": get_pass,
"Chicon8/sendgrid_api_key/staging": get_sendgrid_key,
"Chicon8/db/registration_dev/registration_dev_admin/staging": get_pass,
"Chicon8/sidekiq_password/staging": get_pass,
"Chicon8/stripe_api_key/staging": get_stripe_keys,
},
"prod": {
"Chicon8/db/registration/registration_admin/prod": get_pass,
"Chicon8/tokens/jwt/prod": get_pass,
"Chicon8/sendgrid_api_key/prod": get_pass,
"Chicon8/tokens/session/prod": get_pass,
"Chicon8/sidekiq_password/prod": get_pass,
"Chicon8/stripe_api_key/prod": get_stripe_keys,
},
}
UNSAFE_SECRETS = []
@task
def verify_secrets(c, stage):
"""Verify the presence of secrets for all required stage secrets
--stage <dev|staging|prod>"""
client = boto3.client("secretsmanager")
missing_secrets = []
for secret_name in SECRETS[stage]:
secret = client.describe_secret(SecretId=secret_name)
try:
client.get_secret_value(SecretId=secret_name, VersionStage="AWSCURRENT")
except ClientError as error:
if error.response["Error"]["Code"] == "ResourceNotFoundException":
missing_secrets.append(secret_name)
for secret in missing_secrets:
print(f"Missing {secret}")
@task
def rotate_secret(c, stage, secret_name):
"""Rotate a credential"""
assert secret_name not in UNSAFE_SECRETS, "Can't rotate an unsafe secret"
assert secret_name in SECRETS[stage], "Can't rotate an unconfigured secret"
secret_method = dict(SECRETS[stage])[secret_name]
client = boto3.client("secretsmanager")
secret_string = secret_method(secret_name, stage)
client.put_secret_value(SecretId=secret_name, SecretString=secret_string)
@task
def plan_site(c):
"""Generate a plan for the main site"""
c.run("terraform plan -out main-site.plan")
@task
def apply_site(c):
"""Execute an update to the main infrastructure"""
c.run("terraform apply main-site.plan")
@task(plan_site, apply_site)
def update_site(c):
"""Perform a full update of the site"""
...
@task
def plan_dns(c):
"""Plan DNS changes"""
with c.cd("dns"):
c.run("terraform plan -out dns.plan")
@task
def apply_dns(c):
"""Apply DNS changes"""
with c.cd("dsn"):
c.run("terraform apply dns.plan")
def stage_inventory(stages: List[str]) -> List[str]:
ansible = Path("ansible")
if stages == "all":
return list((ansible / "inventory").glob("*"))
else:
return [ansible / "inventory" / s for s in stages.split(",")]
def ansible(playbook: str, inventory_files: List[str], check=False) -> None:
ansible = Path("ansible")
playbook_file = ansible / playbook
inventory = " ".join(f"-i {ifile}" for ifile in inventory_files)
check_flag = "--check" if check else ""
command = f"ansible-playbook --diff {check_flag} {inventory} {playbook}"
c.run(command, env={"ANSIBLE_FORCE_COLOR": "1"})
@task
def bootstrap(c, stages):
"""Bootstrap the stages of the service"""
ansible("playbook-hosting-bootstrap.yml", stage_inventory(stages))