-
Notifications
You must be signed in to change notification settings - Fork 40
/
Copy pathtasks.py
176 lines (150 loc) · 6.68 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
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
from invoke import task
DOCKER_COMPOSE = 'common/dockerfiles/docker-compose.yml'
DOCKER_COMPOSE_SEARCH = 'common/dockerfiles/docker-compose-search.yml'
DOCKER_COMPOSE_WEBPACK = 'common/dockerfiles/docker-compose-webpack.yml'
DOCKER_COMPOSE_ASSETS = 'dockerfiles/docker-compose-assets.yml'
DOCKER_COMPOSE_OVERRIDE = 'docker-compose.override.yml'
DOCKER_COMPOSE_COMMAND = f'docker-compose -f {DOCKER_COMPOSE} -f {DOCKER_COMPOSE_OVERRIDE} -f {DOCKER_COMPOSE_SEARCH} -f {DOCKER_COMPOSE_WEBPACK}'
@task(help={
'cache': 'Build Docker image using cache (default: False)',
})
def build(c, cache=False):
"""Build docker image for servers."""
cache_opt = '' if cache else '--no-cache'
c.run(f'{DOCKER_COMPOSE_COMMAND} build {cache_opt}', pty=True)
@task(help={
'command': 'Command to pass directly to "docker-compose"',
})
def compose(c, command):
"""Pass the command to docker-compose directly."""
c.run(f'{DOCKER_COMPOSE_COMMAND} {command}', pty=True)
@task(help={
'volumes': 'Delete all the data storaged in volumes as well (default: False)',
})
def down(c, volumes=False):
"""Stop and remove all the docker containers."""
if volumes:
c.run(f'{DOCKER_COMPOSE_COMMAND} down -v', pty=True)
else:
c.run(f'{DOCKER_COMPOSE_COMMAND} down', pty=True)
@task(help={
'search': 'Start search container (default: True)',
'init': 'Perform initialization steps (default: False)',
'reload': 'Enable automatic process reloading (default: True)',
'webpack': 'Start webpack development server (default: False)',
'ext-theme': 'Enable new theme from ext-theme (default: False)',
'scale-build': 'Add additional build instances (default: 1)',
})
def up(c, search=True, init=False, reload=True, webpack=False, ext_theme=False, scale_build=1):
"""Start all the docker containers for a Read the Docs instance"""
cmd = []
cmd.append('INIT=t' if init else 'INIT=')
cmd.append('DOCKER_NO_RELOAD=t' if not reload else 'DOCKER_NO_RELOAD=')
cmd.append('docker-compose')
cmd.append(f'-f {DOCKER_COMPOSE}')
cmd.append(f'-f {DOCKER_COMPOSE_OVERRIDE}')
if search:
cmd.append(f'-f {DOCKER_COMPOSE_SEARCH}')
if webpack:
# This option implies the theme is enabled automatically
ext_theme = True
cmd.append(f'-f {DOCKER_COMPOSE_WEBPACK}')
cmd.insert(0, 'RTD_EXT_THEME_DEV_SERVER_ENABLED=t')
if ext_theme:
cmd.insert(0, 'RTD_EXT_THEME_ENABLED=t')
cmd.append('up')
cmd.append(f'--scale build={scale_build}')
c.run(' '.join(cmd), pty=True)
@task(help={
'running': 'Open the shell in a running container',
'container': 'Container to open the shell (default: web)'
})
def shell(c, running=True, container='web'):
"""Run a shell inside a container."""
if running:
c.run(f'{DOCKER_COMPOSE_COMMAND} exec {container} /bin/bash', pty=True)
else:
c.run(f'{DOCKER_COMPOSE_COMMAND} run --rm {container} /bin/bash', pty=True)
@task(help={
'command': 'Command to pass directly to "django-admin" inside the container',
'running': 'Execute "django-admin" in a running container',
'backupdb': 'Backup postgres database before running Django "manage" command',
})
def manage(c, command, running=True, backupdb=False):
"""Run manage.py with a specific command."""
subcmd = 'run --rm'
if running:
subcmd = 'exec'
if backupdb:
c.run(f'{DOCKER_COMPOSE_COMMAND} {subcmd} database pg_dumpall -c -U docs_user > dump_`date +%d-%m-%Y"_"%H_%M_%S`__`git rev-parse HEAD`.sql', pty=True)
c.run(f'{DOCKER_COMPOSE_COMMAND} {subcmd} web python3 manage.py {command}', pty=True)
@task(help={
'container': 'Container to attach',
})
def attach(c, container):
"""Attach a tty to a running container (useful for pdb)."""
prefix = c['container_prefix'] # readthedocsorg or readthedocs-corporate
c.run(f'docker attach --sig-proxy=false --detach-keys="ctrl-p,ctrl-p" {prefix}_{container}_1', pty=True)
@task(help={
'containers': 'Container(s) to restart (it may restart "nginx" container if required)',
})
def restart(c, containers):
"""Restart one or more containers."""
c.run(f'{DOCKER_COMPOSE_COMMAND} restart {containers}', pty=True)
# When restarting a container that nginx is connected to, we need to restart
# nginx as well because it has the IP cached
need_nginx_restart = [
'web',
'proxito',
'storage',
]
for extra in need_nginx_restart:
if extra in containers:
c.run(f'{DOCKER_COMPOSE_COMMAND} restart nginx', pty=True)
break
@task(help={
'only_latest': 'Only pull the latest tag. Use if you don\'t need all images (default: False)',
})
def pull(c, only_latest=False):
"""Pull all docker images required for build servers."""
images = [
('7.0', 'latest')
]
if not only_latest:
images.extend([
('6.0', 'stable'),
('8.0', 'testing'),
])
for image, tag in images:
c.run(f'docker pull readthedocs/build:{image}', pty=True)
c.run(f'docker tag readthedocs/build:{image} readthedocs/build:{tag}', pty=True)
@task(help={
'arguments': 'Arguments to pass directly to "tox" command',
'running': 'Run all tests in a running container',
})
def test(c, arguments='', running=True):
"""Run all test suite using ``tox``."""
if running:
c.run(f'{DOCKER_COMPOSE_COMMAND} exec -e GITHUB_TOKEN=$GITHUB_TOKEN web tox {arguments}', pty=True)
else:
c.run(f'{DOCKER_COMPOSE_COMMAND} run -e GITHUB_TOKEN=$GITHUB_TOKEN --rm --no-deps web tox {arguments}', pty=True)
@task
def buildassets(c):
"""Build all assets for the application and push them to backend storage"""
c.run(f'docker-compose -f {DOCKER_COMPOSE_ASSETS} run --rm assets bash -c "npm ci && node_modules/bower/bin/bower --allow-root update && npm run build"', pty=True)
c.run(f'{DOCKER_COMPOSE_COMMAND} run --rm web python3 manage.py collectstatic --noinput', pty=True)
@task(help={
'file': 'SQL File that should be use to go back old state',
'running': 'Execute in a running container',
})
def restoredb(c, file, running=True):
"""Restore to old db state when running --backupdb migrate"""
subcmd = 'run --rm'
if running:
subcmd = 'exec'
c.run(f"{DOCKER_COMPOSE_COMMAND} 'start database'", pty=True)
c.run(f"docker cp {file} community_database_1:/tmp/dump.sql")
c.run(f'{DOCKER_COMPOSE_COMMAND} {subcmd} database /bin/bash', pty=True)
c.run(f'{DOCKER_COMPOSE_COMMAND} {subcmd} dropdb -U docs_user docs_db', pty=True)
c.run(f'{DOCKER_COMPOSE_COMMAND} {subcmd} createdb -U docs_user docs_db', pty=True)
c.run(f'{DOCKER_COMPOSE_COMMAND} {subcmd} psql -U docs_user docs_db < /tmp/dump.sql', pty=True)