Merge pull request #2765 from rtfd/fix-symlink

Fix symlinking race condition
tox-dependencies
Eric Holscher 2017-03-31 13:34:54 -07:00 committed by GitHub
commit bb36f34d4c
2 changed files with 25 additions and 8 deletions

View File

@ -62,6 +62,7 @@ from django.conf import settings
from readthedocs.builds.models import Version from readthedocs.builds.models import Version
from readthedocs.core.utils.extend import SettingsOverrideObject from readthedocs.core.utils.extend import SettingsOverrideObject
from readthedocs.core.utils import safe_makedirs
from readthedocs.projects import constants from readthedocs.projects import constants
from readthedocs.projects.models import Domain from readthedocs.projects.models import Domain
from readthedocs.projects.utils import run from readthedocs.projects.utils import run
@ -100,19 +101,19 @@ class Symlink(object):
if os.path.islink(self.project_root) and not self.project.single_version: if os.path.islink(self.project_root) and not self.project.single_version:
self._log("Removing single version symlink") self._log("Removing single version symlink")
os.unlink(self.project_root) os.unlink(self.project_root)
os.makedirs(self.project_root) safe_makedirs(self.project_root)
elif (self.project.single_version and elif (self.project.single_version and
not os.path.islink(self.project_root) and not os.path.islink(self.project_root) and
os.path.exists(self.project_root)): os.path.exists(self.project_root)):
shutil.rmtree(self.project_root) shutil.rmtree(self.project_root)
elif not os.path.lexists(self.project_root): elif not os.path.lexists(self.project_root):
os.makedirs(self.project_root) safe_makedirs(self.project_root)
# CNAME root directories # CNAME root directories
if not os.path.lexists(self.CNAME_ROOT): if not os.path.lexists(self.CNAME_ROOT):
os.makedirs(self.CNAME_ROOT) safe_makedirs(self.CNAME_ROOT)
if not os.path.lexists(self.PROJECT_CNAME_ROOT): if not os.path.lexists(self.PROJECT_CNAME_ROOT):
os.makedirs(self.PROJECT_CNAME_ROOT) safe_makedirs(self.PROJECT_CNAME_ROOT)
def run(self): def run(self):
""" """
@ -177,7 +178,7 @@ class Symlink(object):
if rels.count(): if rels.count():
# Don't creat the `projects/` directory unless subprojects exist. # Don't creat the `projects/` directory unless subprojects exist.
if not os.path.exists(self.subproject_root): if not os.path.exists(self.subproject_root):
os.makedirs(self.subproject_root) safe_makedirs(self.subproject_root)
for rel in rels: for rel in rels:
# A mapping of slugs for the subproject URL to the actual built # A mapping of slugs for the subproject URL to the actual built
# documentation # documentation
@ -194,7 +195,7 @@ class Symlink(object):
) )
symlink_dir = os.sep.join(symlink.split(os.path.sep)[:-1]) symlink_dir = os.sep.join(symlink.split(os.path.sep)[:-1])
if not os.path.lexists(symlink_dir): if not os.path.lexists(symlink_dir):
os.makedirs(symlink_dir) safe_makedirs(symlink_dir)
run('ln -nsf %s %s' % (docs_dir, symlink)) run('ln -nsf %s %s' % (docs_dir, symlink))
# Remove old symlinks # Remove old symlinks
@ -219,7 +220,7 @@ class Symlink(object):
if os.path.islink(language_dir): if os.path.islink(language_dir):
os.unlink(language_dir) os.unlink(language_dir)
if not os.path.lexists(language_dir): if not os.path.lexists(language_dir):
os.makedirs(language_dir) safe_makedirs(language_dir)
for (language, slug) in translations.items(): for (language, slug) in translations.items():
self._log(u"Symlinking translation: {0}->{1}".format(language, slug)) self._log(u"Symlinking translation: {0}->{1}".format(language, slug))
@ -271,7 +272,7 @@ class Symlink(object):
version_queryset = self.get_version_queryset() version_queryset = self.get_version_queryset()
if version_queryset.count(): if version_queryset.count():
if not os.path.exists(version_dir): if not os.path.exists(version_dir):
os.makedirs(version_dir) safe_makedirs(version_dir)
for version in version_queryset: for version in version_queryset:
self._log(u"Symlinking Version: %s" % version) self._log(u"Symlinking Version: %s" % version)
symlink = os.path.join(version_dir, version.slug) symlink = os.path.join(version_dir, version.slug)

View File

@ -1,3 +1,4 @@
import errno
import getpass import getpass
import logging import logging
import os import os
@ -153,3 +154,18 @@ def slugify(value, *args, **kwargs):
slugify = allow_lazy(slugify, six.text_type, SafeText) slugify = allow_lazy(slugify, six.text_type, SafeText)
def safe_makedirs(directory_name):
"""
Makedirs has an issue where it has a race condition around
checking for a directory and then creating it.
This catches the exception in the case where the dir already exists.
"""
try:
os.makedirs(directory_name)
except OSError as e:
if e.errno == errno.EEXIST:
pass
raise