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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
|
from fabric import task
def as_www_data(command, virtual=True):
"""
Simply prefixes the `command` with
sudo su www-data -s /bin/fish -c '`command`'
"""
www_data_shell = [
"sudo",
"su",
# "--login", # NOTE in login shell this c.cd("/path") won't work
"www-data",
"--shell",
"/bin/fish",
"--command",
]
# NOTE: not a nice solution, but we need to source the virtual only after
# we sudo as www-data
if virtual:
command = ["source", "/var/www/sec-certs/virt11_flask3/bin/activate.fish", "&&"] + command
joined_command = " ".join(command)
www_data_shell.append(f'"{joined_command}"')
return " ".join(www_data_shell)
def check_deploy(c):
resp = c.local("curl https://sec-certs.org/admin/candeploy").stdout.strip()
print("Can deploy?", resp)
return resp == "OK"
@task
def ps(c):
"""Print running processes."""
c.run("ps axuf")
@task
def pull(c):
"""Pull latest git (origin/page)."""
with c.cd("/var/www/sec-certs"):
c.run(as_www_data(["git", "pull", "origin", "page"], virtual=False))
@task
def tail_dramatiq(c):
"""Tail the dramatiq log."""
c.run("tail -f /var/log/dramatiq/certs.log")
@task
def reload_dramatiq(c):
"""Reload the dramatiq worker."""
if c.run("test -f /run/uwsgi/app/certs/dramatiq.pid"):
resp = c.run("cat /run/uwsgi/app/certs/dramatiq.pid")
pid = resp.stdout.strip()
if c.run(f"test -d /proc/{pid}"):
print(f"Reloading dramatiq {pid}")
c.sudo(f"kill {pid}")
print("Reloaded dramatiq")
else:
print("Dramatiq pidfile found but not running.")
else:
print("Dramatiq pidfile not found")
@task
def tail_uwsgi(c):
"""Tail the uWSGI log."""
c.sudo("tail -f /var/log/uwsgi/app/certs.log")
@task
def reload_uwsgi(c):
"""Reload the uWSGI master."""
if c.run("test -f /run/uwsgi/app/certs/pid"):
resp = c.run("cat /run/uwsgi/app/certs/pid")
pid = resp.stdout.strip()
if c.run(f"test -d /proc/{pid}"):
print(f"Reloading uWSGI {pid}.")
c.sudo(f"kill -1 {pid}")
print("Reloaded uWSGI.")
else:
print("uWSGI pidfile found but not running.")
else:
print("uWSGI pidfile not found.")
@task
def start_uwsgi(c):
"""Start the uWSGI master."""
if c.run("test -f /run/uwsgi/app/certs/pid"):
resp = c.run("cat /run/uwsgi/app/certs/pid")
pid = resp.stdout.strip()
if c.run(f"test -d /proc/{pid}"):
print(f"uWSGI is already running (pid = {pid}).")
return
print("Old pidfile found, starting anyway.")
c.sudo("cd /opt/uwsgi && ./uwsgi --ini /etc/uwsgi/apps-enabled/certs.ini --daemonize /var/log/uwsgi/app/certs.log")
@task
def stop_uwsgi(c):
"""Stop the uWSGI master."""
if c.run("test -f /run/uwsgi/app/certs/pid"):
resp = c.run("cat /run/uwsgi/app/certs/pid")
pid = resp.stdout.strip()
if c.run(f"test -d /proc/{pid}"):
print(f"Stopping uWSGI {pid}.")
c.sudo(f"kill -9 {pid}")
print("Stopped uWSGI.")
else:
print("uWSGI pidfile found but not running.")
else:
print("uWSGI pidfile not found.")
@task
def tail_nginx(c):
"""Tail the nginx access log."""
c.run("tail -f /var/log/nginx/access.log")
@task
def reload_nginx(c):
"""Reload nginx."""
c.sudo("systemctl reload nginx")
@task
def start_nginx(c):
"""Start nginx."""
c.sudo("systemctl start nginx")
@task
def stop_nginx(c):
"""Stop nginx."""
c.sudo("systemctl stop nginx")
@task
def update(c):
"""Update the sec-certs clone."""
print("Updating...")
with c.cd("/var/www/sec-certs/sec-certs"):
c.run(as_www_data(["git", "pull", "--tags", "origin", "main"], virtual=False))
@task
def deploy(c, force=False):
"""Deploy the whole thing, does a reload-only deploy."""
if not check_deploy(c) and not force:
print("Aborting deploy, tasks are running.")
return
print("Pulling...")
pull(c)
print("Reloading dramatiq...")
reload_dramatiq(c)
print("Reloading uWSGI (reloads periodiq)...")
reload_uwsgi(c)
ps(c)
c.local("curl https://sec-certs.org")
c.local(
"curl https://sec-certs.org/cc/mergedsearch/?searchType=fulltext&q=something&cat=abcdefghijklmop&status=any&type=any"
)
c.local(
"curl https://sec-certs.org/fips/mergedsearch/?searchType=fulltext&q=something&cat=abcdef&status=Any&type=any"
)
c.local(
"curl https://sec-certs.org/pp/mergedsearch/?searchType=fulltext&q=something&cat=abcdefghijklmop&status=Any&type=any"
)
@task
def release(c):
"""Create a new release on Sentry."""
version = c.local("sentry-cli releases propose-version", hide="both").stdout.strip()
c.local(f"sentry-cli releases new {version}")
c.local(f"sentry-cli releases finalize {version}")
c.local(f"sentry-cli releases set-commits {version} --local")
@task
def enable_maintenance(c):
"""Enable maintenannce mode for the site."""
with c.cd("/var/www/sec-certs/"):
c.run(as_www_data(["cp", "mntnc.html", "maintenance.html"], virtual=False))
@task
def disable_maintenance(c):
"""Disable maintenannce mode for the site."""
with c.cd("/var/www/sec-certs/"):
c.run(as_www_data(["rm", "maintenance.html"], virtual=False))
@task
def run_cc(c):
"""Run the CC update task."""
print("Sending CC update task.")
with c.cd("/var/www/sec-certs/sec-certs"):
c.run(
as_www_data(
[
"env",
"INSTANCE_PATH=/data/flask/instance/",
"python",
"-c",
"'from sec_certs_page.cc.tasks import update_data; update_data.send()'",
],
virtual=True,
)
)
@task
def run_fips(c):
"""Run the FIPS update task."""
print("Sending FIPS update task.")
with c.cd("/var/www/sec-certs/sec-certs"):
c.run(
as_www_data(
[
"env",
"INSTANCE_PATH=/data/flask/instance/",
"python",
"-c",
"'from sec_certs_page.fips.tasks import update_data; update_data.send()'",
],
virtual=True,
)
)
@task
def shell(c):
"""Get Flask shell."""
with c.cd("/var/www/sec-certs/sec-certs"):
c.run(
as_www_data(
["env", "INSTANCE_PATH=/data/flask/instance/", "flask", "-A", "sec_certs_page", "shell"], virtual=True
)
)
@task
def dump(c):
"""Dump MongoDB data and download them."""
tmp_dir = c.run("mktemp -d").stdout.strip()
try:
with c.cd(tmp_dir):
print("Dumping...")
c.run("mongodump -d seccerts")
print("Archiving...")
c.run("tar --zstd -cf dump.tar.zst dump/")
print("Transmitting...")
c.get(f"{tmp_dir}/dump.tar.zst", "dump.tar.zst")
print("Done.")
finally:
c.run(f"rm -r '{tmp_dir}'")
@task
def restore(c):
"""Restore MongoDB data locally."""
print("Restoring...")
c.local("rm -rf dump/")
c.local("tar --zstd -xf dump.tar.zst")
c.local("mongorestore --nsInclude=seccerts.\\* --drop --maintainInsertionOrder")
|