Skip to content

Commit b242b41

Browse files
humitosagjohnson
authored andcommitted
Define useful celery beat task for development (#3762)
* Task to remove orphan symlinks * Use a better db query to accomplish this task * Setup task to be ran by celery beat * Register celery beat task useful for development
1 parent 7ac7dfc commit b242b41

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

readthedocs/projects/tasks.py

+18
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,24 @@ def symlink_domain(project_pk, domain_pk, delete=False):
810810
sym.symlink_cnames(domain)
811811

812812

813+
@app.task(queue='web')
814+
def remove_orphan_symlinks():
815+
"""
816+
Remove orphan symlinks.
817+
818+
List CNAME_ROOT for Public and Private symlinks, check that all the listed
819+
cname exist in the database and if doesn't exist, they are un-linked.
820+
"""
821+
for symlink in [PublicSymlink, PrivateSymlink]:
822+
for domain_path in [symlink.PROJECT_CNAME_ROOT, symlink.CNAME_ROOT]:
823+
valid_cnames = set(Domain.objects.all().values_list('domain', flat=True))
824+
orphan_cnames = set(os.listdir(domain_path)) - valid_cnames
825+
for cname in orphan_cnames:
826+
orphan_domain_path = os.path.join(domain_path, cname)
827+
log.info('Unlinking orphan CNAME: %s', orphan_domain_path)
828+
os.unlink(orphan_domain_path)
829+
830+
813831
@app.task(queue='web')
814832
def symlink_subproject(project_pk):
815833
project = Project.objects.get(pk=project_pk)

readthedocs/rtd_tests/tests/test_project_symlinks.py

+74-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from readthedocs.builds.models import Version
1616
from readthedocs.projects.models import Project, Domain
17-
from readthedocs.projects.tasks import symlink_project
17+
from readthedocs.projects.tasks import symlink_project, remove_orphan_symlinks
1818
from readthedocs.core.symlink import PublicSymlink, PrivateSymlink
1919

2020

@@ -166,6 +166,79 @@ def test_symlink_cname(self):
166166
filesystem['private_web_root'] = public_root
167167
self.assertFilesystem(filesystem)
168168

169+
def test_symlink_remove_orphan_symlinks(self):
170+
self.domain = get(Domain, project=self.project, domain='woot.com',
171+
url='http://woot.com', cname=True)
172+
self.symlink.symlink_cnames()
173+
174+
# Editing the Domain and calling save will symlink the new domain and
175+
# leave the old one as orphan.
176+
self.domain.domain = 'foobar.com'
177+
self.domain.save()
178+
179+
filesystem = {
180+
'private_cname_project': {
181+
'foobar.com': {'type': 'link', 'target': 'user_builds/kong'},
182+
'woot.com': {'type': 'link', 'target': 'user_builds/kong'},
183+
},
184+
'private_cname_root': {
185+
'foobar.com': {'type': 'link', 'target': 'private_web_root/kong'},
186+
'woot.com': {'type': 'link', 'target': 'private_web_root/kong'},
187+
},
188+
'private_web_root': {'kong': {'en': {}}},
189+
'public_cname_project': {
190+
'foobar.com': {'type': 'link', 'target': 'user_builds/kong'},
191+
'woot.com': {'type': 'link', 'target': 'user_builds/kong'},
192+
},
193+
'public_cname_root': {
194+
'foobar.com': {'type': 'link', 'target': 'public_web_root/kong'},
195+
'woot.com': {'type': 'link', 'target': 'public_web_root/kong'},
196+
},
197+
'public_web_root': {
198+
'kong': {'en': {'latest': {
199+
'type': 'link',
200+
'target': 'user_builds/kong/rtd-builds/latest',
201+
}}}
202+
}
203+
}
204+
if self.privacy == 'private':
205+
public_root = filesystem['public_web_root'].copy()
206+
private_root = filesystem['private_web_root'].copy()
207+
filesystem['public_web_root'] = private_root
208+
filesystem['private_web_root'] = public_root
209+
self.assertFilesystem(filesystem)
210+
211+
remove_orphan_symlinks()
212+
filesystem = {
213+
'private_cname_project': {
214+
'foobar.com': {'type': 'link', 'target': 'user_builds/kong'},
215+
},
216+
'private_cname_root': {
217+
'foobar.com': {'type': 'link', 'target': 'private_web_root/kong'},
218+
},
219+
'private_web_root': {'kong': {'en': {}}},
220+
'public_cname_project': {
221+
'foobar.com': {'type': 'link', 'target': 'user_builds/kong'},
222+
},
223+
'public_cname_root': {
224+
'foobar.com': {'type': 'link', 'target': 'public_web_root/kong'},
225+
},
226+
'public_web_root': {
227+
'kong': {'en': {'latest': {
228+
'type': 'link',
229+
'target': 'user_builds/kong/rtd-builds/latest',
230+
}}},
231+
},
232+
}
233+
if self.privacy == 'private':
234+
public_root = filesystem['public_web_root'].copy()
235+
private_root = filesystem['private_web_root'].copy()
236+
filesystem['public_web_root'] = private_root
237+
filesystem['private_web_root'] = public_root
238+
239+
self.assertFilesystem(filesystem)
240+
241+
169242
def test_symlink_cname_dont_link_missing_domains(self):
170243
"""Domains should be relinked after deletion"""
171244
self.domain = get(Domain, project=self.project, domain='woot.com',

readthedocs/settings/base.py

+16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
from readthedocs.core.settings import Settings
1010

11+
from celery.schedules import crontab
12+
13+
1114
try:
1215
import readthedocsext # noqa
1316
ext = True
@@ -241,6 +244,19 @@ def USE_PROMOS(self): # noqa
241244
CELERY_CREATE_MISSING_QUEUES = True
242245

243246
CELERY_DEFAULT_QUEUE = 'celery'
247+
CELERYBEAT_SCHEDULE = {
248+
# Ran every hour on minute 30
249+
'hourly-remove-orphan-symlinks': {
250+
'task': 'readthedocs.projects.tasks.remove_orphan_symlinks',
251+
'schedule': crontab(minute=30),
252+
'options': {'queue': 'web'},
253+
},
254+
'quarter-finish-inactive-builds': {
255+
'task': 'readthedocs.projects.tasks.finish_inactive_builds',
256+
'schedule': crontab(minute='*/15'),
257+
'options': {'queue': 'web'},
258+
},
259+
}
244260

245261
# Docker
246262
DOCKER_ENABLE = False

0 commit comments

Comments
 (0)