Skip to content

Commit d3c1e8a

Browse files
authored
Merge pull request #2946 from rtfd/broadcast-fixes
Move move_files to a broadcast model.
2 parents 053b54d + 318c39a commit d3c1e8a

File tree

11 files changed

+60
-91
lines changed

11 files changed

+60
-91
lines changed

readthedocs/builds/models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ
163163
def delete(self, *args, **kwargs): # pylint: disable=arguments-differ
164164
from readthedocs.projects import tasks
165165
log.info('Removing files for version %s', self.slug)
166-
tasks.clear_artifacts.delay(version_pk=self.pk)
166+
broadcast(type='app', task=tasks.clear_artifacts, args=[self.pk])
167167
broadcast(type='app', task=tasks.symlink_project, args=[self.project.pk])
168168
super(Version, self).delete(*args, **kwargs)
169169

readthedocs/core/management/commands/set_metadata.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from readthedocs.projects import tasks
99
from readthedocs.projects.models import Project
10+
from readthedocs.core.utils import broadcast
1011

1112
log = logging.getLogger(__name__)
1213

@@ -20,6 +21,6 @@ def handle(self, *args, **options):
2021
for p in queryset:
2122
log.info("Generating metadata for %s", p)
2223
try:
23-
tasks.update_static_metadata(p.pk)
24+
broadcast(type='app', task=tasks.update_static_metadata, args=[p.pk])
2425
except Exception:
2526
log.error('Build failed for %s', p, exc_info=True)

readthedocs/core/utils/__init__.py

+4-15
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,10 @@
2828
SYNC_USER = getattr(settings, 'SYNC_USER', getpass.getuser())
2929

3030

31-
def run_on_app_servers(command):
32-
"""A helper to copy a single file across app servers"""
33-
log.info("Running %s on app servers", command)
34-
ret_val = 0
35-
if getattr(settings, "MULTIPLE_APP_SERVERS", None):
36-
for server in settings.MULTIPLE_APP_SERVERS:
37-
ret = os.system("ssh %s@%s %s" % (SYNC_USER, server, command))
38-
if ret != 0:
39-
ret_val = ret
40-
return ret_val
41-
ret = os.system(command)
42-
return ret
43-
44-
45-
def broadcast(type, task, args): # pylint: disable=redefined-builtin
31+
def broadcast(type, task, args, kwargs=None): # pylint: disable=redefined-builtin
4632
assert type in ['web', 'app', 'build']
33+
if kwargs is None:
34+
kwargs = {}
4735
default_queue = getattr(settings, 'CELERY_DEFAULT_QUEUE', 'celery')
4836
if type in ['web', 'app']:
4937
servers = getattr(settings, "MULTIPLE_APP_SERVERS", [default_queue])
@@ -53,6 +41,7 @@ def broadcast(type, task, args): # pylint: disable=redefined-builtin
5341
task.apply_async(
5442
queue=server,
5543
args=args,
44+
kwargs=kwargs,
5645
)
5746

5847

readthedocs/projects/models.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from readthedocs.projects import constants
2626
from readthedocs.projects.exceptions import ProjectImportError
2727
from readthedocs.projects.templatetags.projects_tags import sort_version_aware
28-
from readthedocs.projects.utils import make_api_version, update_static_metadata
28+
from readthedocs.projects.utils import make_api_version
2929
from readthedocs.projects.version_handling import determine_stable_version
3030
from readthedocs.projects.version_handling import version_windows
3131
from readthedocs.core.resolver import resolve, resolve_domain
@@ -330,7 +330,8 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ
330330
except Exception:
331331
log.error('failed to symlink project', exc_info=True)
332332
try:
333-
update_static_metadata(project_pk=self.pk)
333+
if not first_save:
334+
broadcast(type='app', task=tasks.update_static_metadata, args=[self.pk])
334335
except Exception:
335336
log.error('failed to update static metadata', exc_info=True)
336337
try:

readthedocs/projects/tasks.py

+28-42
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
BUILD_STATE_FINISHED)
3333
from readthedocs.builds.models import Build, Version
3434
from readthedocs.builds.signals import build_complete
35-
from readthedocs.core.utils import send_email, run_on_app_servers, broadcast
35+
from readthedocs.core.utils import send_email, broadcast
3636
from readthedocs.core.symlink import PublicSymlink, PrivateSymlink
3737
from readthedocs.cdn.purge import purge
3838
from readthedocs.doc_builder.loader import get_builder_class
@@ -395,11 +395,10 @@ def build_docs_html(self):
395395

396396
# Gracefully attempt to move files via task on web workers.
397397
try:
398-
move_files.delay(
399-
version_pk=self.version.pk,
400-
html=True,
401-
hostname=socket.gethostname(),
402-
)
398+
broadcast(type='app', task=move_files,
399+
args=[self.version.pk, socket.gethostname()],
400+
kwargs=dict(html=True)
401+
)
403402
except socket.error:
404403
# TODO do something here
405404
pass
@@ -555,25 +554,27 @@ def finish_build(version_pk, build_pk, hostname=None, html=False,
555554
version.save()
556555

557556
if not pdf:
558-
clear_pdf_artifacts(version)
557+
broadcast(type='app', task=clear_pdf_artifacts, args=[version.pk])
559558
if not epub:
560-
clear_epub_artifacts(version)
561-
562-
move_files(
563-
version_pk=version_pk,
564-
hostname=hostname,
565-
html=html,
566-
localmedia=localmedia,
567-
search=search,
568-
pdf=pdf,
569-
epub=epub,
570-
)
559+
broadcast(type='app', task=clear_epub_artifacts, args=[version.pk])
560+
561+
# Sync files to the web servers
562+
broadcast(type='app', task=move_files, args=[version_pk, hostname],
563+
kwargs=dict(
564+
html=html,
565+
localmedia=localmedia,
566+
search=search,
567+
pdf=pdf,
568+
epub=epub,
569+
))
571570

572571
# Symlink project on every web
573572
broadcast(type='app', task=symlink_project, args=[version.project.pk])
574573

574+
# Update metadata
575+
broadcast(type='app', task=update_static_metadata, args=[version.project.pk])
576+
575577
# Delayed tasks
576-
update_static_metadata.delay(version.project.pk)
577578
fileify.delay(version.pk, commit=build.commit)
578579
update_search.delay(version.pk, commit=build.commit)
579580

@@ -887,7 +888,6 @@ def update_static_metadata(project_pk, path=None):
887888
fh = open(path, 'w+')
888889
json.dump(metadata, fh)
889890
fh.close()
890-
Syncer.copy(path, path, host=socket.gethostname(), is_file=True)
891891
except (AttributeError, IOError) as e:
892892
log.debug(LOG_TEMPLATE.format(
893893
project=project.slug,
@@ -909,7 +909,7 @@ def remove_dir(path):
909909
shutil.rmtree(path, ignore_errors=True)
910910

911911

912-
@task(queue='web')
912+
@task()
913913
def clear_artifacts(version_pk):
914914
"""Remove artifacts from the web servers"""
915915
version = Version.objects.get(pk=version_pk)
@@ -920,33 +920,19 @@ def clear_artifacts(version_pk):
920920

921921

922922
def clear_pdf_artifacts(version):
923-
run_on_app_servers('rm -rf %s'
924-
% version.project.get_production_media_path(
925-
type_='pdf', version_slug=version.slug))
923+
remove_dir(version.project.get_production_media_path(
924+
type_='pdf', version_slug=version.slug))
926925

927926

928927
def clear_epub_artifacts(version):
929-
run_on_app_servers('rm -rf %s'
930-
% version.project.get_production_media_path(
931-
type_='epub', version_slug=version.slug))
928+
remove_dir(version.project.get_production_media_path(
929+
type_='epub', version_slug=version.slug))
932930

933931

934932
def clear_htmlzip_artifacts(version):
935-
run_on_app_servers('rm -rf %s'
936-
% version.project.get_production_media_path(
937-
type_='htmlzip', version_slug=version.slug))
933+
remove_dir(version.project.get_production_media_path(
934+
type_='htmlzip', version_slug=version.slug))
938935

939936

940937
def clear_html_artifacts(version):
941-
run_on_app_servers('rm -rf %s' % version.project.rtd_build_path(version=version.slug))
942-
943-
944-
@task(queue='web')
945-
def remove_path_from_web(path):
946-
"""Remove the given path from the web servers file system."""
947-
# Santity check for spaces in the path since spaces would result in
948-
# deleting unpredictable paths with "rm -rf".
949-
assert ' ' not in path, "No spaces allowed in path"
950-
951-
# TODO: We need some proper escaping here for the given path.
952-
run_on_app_servers('rm -rf {path}'.format(path=path))
938+
remove_dir(version.project.rtd_build_path(version=version.slug))

readthedocs/projects/utils.py

-6
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@ def version_from_slug(slug, version):
2929
return v
3030

3131

32-
def update_static_metadata(project_pk):
33-
"""This is here to avoid circular imports in models.py"""
34-
from readthedocs.projects import tasks
35-
tasks.update_static_metadata.delay(project_pk)
36-
37-
3832
def find_file(filename):
3933
"""Recursively find matching file from the current working path
4034

readthedocs/rtd_tests/tests/test_celery.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from __future__ import absolute_import
21
import os
32
import json
43
import shutil

readthedocs/rtd_tests/tests/test_core_tags.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class CoreTagsTests(TestCase):
1414
fixtures = ["eric", "test_data"]
1515

1616
def setUp(self):
17-
with mock.patch('readthedocs.projects.models.update_static_metadata'):
17+
with mock.patch('readthedocs.projects.models.broadcast'):
1818
self.client.login(username='eric', password='test')
1919
self.pip = Project.objects.get(slug='pip')
2020
self.pip_fr = Project.objects.create(name="PIP-FR", slug='pip-fr', language='fr', main_language_project=self.pip)

readthedocs/rtd_tests/tests/test_project_views.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from readthedocs.projects.models import Project, Domain
2222
from readthedocs.projects.views.private import ImportWizardView
2323
from readthedocs.projects.views.mixins import ProjectRelationMixin
24+
from readthedocs.projects import tasks
2425

2526

2627
@patch('readthedocs.projects.views.private.trigger_build', lambda x, basic: None)
@@ -371,13 +372,13 @@ def test_delete_project(self):
371372
response = self.client.get('/dashboard/pip/delete/')
372373
self.assertEqual(response.status_code, 200)
373374

374-
patcher = patch('readthedocs.projects.tasks.remove_dir')
375-
with patcher as remove_dir:
375+
with patch('readthedocs.projects.views.private.broadcast') as broadcast:
376376
response = self.client.post('/dashboard/pip/delete/')
377377
self.assertEqual(response.status_code, 302)
378378
self.assertFalse(Project.objects.filter(slug='pip').exists())
379-
remove_dir.apply_async.assert_called_with(
380-
queue='celery',
379+
broadcast.assert_called_with(
380+
type='app',
381+
task=tasks.remove_dir,
381382
args=[project.doc_path])
382383

383384

readthedocs/rtd_tests/tests/test_resolver.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@ class ResolverBase(TestCase):
1717

1818
def setUp(self):
1919
with mock.patch('readthedocs.projects.models.broadcast'):
20-
with mock.patch('readthedocs.projects.models.update_static_metadata'):
21-
self.owner = create_user(username='owner', password='test')
22-
self.tester = create_user(username='tester', password='test')
23-
self.pip = get(Project, slug='pip', users=[self.owner], main_language_project=None)
24-
self.subproject = get(Project, slug='sub', language='ja', users=[self.owner], main_language_project=None)
25-
self.translation = get(Project, slug='trans', language='ja', users=[self.owner], main_language_project=None)
26-
self.pip.add_subproject(self.subproject)
27-
self.pip.translations.add(self.translation)
20+
self.owner = create_user(username='owner', password='test')
21+
self.tester = create_user(username='tester', password='test')
22+
self.pip = get(Project, slug='pip', users=[self.owner], main_language_project=None)
23+
self.subproject = get(Project, slug='sub', language='ja', users=[self.owner], main_language_project=None)
24+
self.translation = get(Project, slug='trans', language='ja', users=[self.owner], main_language_project=None)
25+
self.pip.add_subproject(self.subproject)
26+
self.pip.translations.add(self.translation)
2827

2928

3029
class SmartResolverPathTests(ResolverBase):

readthedocs/rtd_tests/tests/test_subprojects.py

+9-10
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,15 @@ class ResolverBase(TestCase):
6868

6969
def setUp(self):
7070
with mock.patch('readthedocs.projects.models.broadcast'):
71-
with mock.patch('readthedocs.projects.models.update_static_metadata'):
72-
self.owner = create_user(username='owner', password='test')
73-
self.tester = create_user(username='tester', password='test')
74-
self.pip = get(Project, slug='pip', users=[self.owner], main_language_project=None)
75-
self.subproject = get(Project, slug='sub', language='ja', users=[
76-
self.owner], main_language_project=None)
77-
self.translation = get(Project, slug='trans', language='ja', users=[
78-
self.owner], main_language_project=None)
79-
self.pip.add_subproject(self.subproject)
80-
self.pip.translations.add(self.translation)
71+
self.owner = create_user(username='owner', password='test')
72+
self.tester = create_user(username='tester', password='test')
73+
self.pip = get(Project, slug='pip', users=[self.owner], main_language_project=None)
74+
self.subproject = get(Project, slug='sub', language='ja', users=[
75+
self.owner], main_language_project=None)
76+
self.translation = get(Project, slug='trans', language='ja', users=[
77+
self.owner], main_language_project=None)
78+
self.pip.add_subproject(self.subproject)
79+
self.pip.translations.add(self.translation)
8180

8281
@override_settings(PRODUCTION_DOMAIN='readthedocs.org')
8382
def test_resolver_subproject_alias(self):

0 commit comments

Comments
 (0)