Do not fail when unlinking an non-existing path (#4760)

* Do not fail when unlinking an non-existing path

If we try to unlink a path that doesn't exist, we need to do nothing
instead of failing hard and logging an exception on Sentry.

* Rename unlink to safe_unlink
humitos/integrations/handle-error-codes
Manuel Kaufmann 2018-10-16 10:21:11 +02:00 committed by Eric Holscher
parent bd4c82b43b
commit 846c7e2ef9
3 changed files with 27 additions and 10 deletions

View File

@ -63,7 +63,7 @@ from django.conf import settings
from readthedocs.builds.models import Version
from readthedocs.core.utils.extend import SettingsOverrideObject
from readthedocs.core.utils import safe_makedirs
from readthedocs.core.utils import safe_makedirs, safe_unlink
from readthedocs.projects import constants
from readthedocs.projects.models import Domain
from readthedocs.projects.utils import run
@ -95,7 +95,7 @@ class Symlink(object):
log.info(constants.LOG_TEMPLATE.format(
project=self.project.slug, version='',
msg="Removing single version symlink"))
os.unlink(self.project_root)
safe_unlink(self.project_root)
safe_makedirs(self.project_root)
elif (self.project.single_version and
not os.path.islink(self.project_root) and
@ -164,7 +164,7 @@ class Symlink(object):
log.info(constants.LOG_TEMPLATE.format(project=self.project.slug,
version='', msg=log_msg))
symlink = os.path.join(self.CNAME_ROOT, domain.domain)
os.unlink(symlink)
safe_unlink(symlink)
def symlink_subprojects(self):
"""
@ -210,7 +210,7 @@ class Symlink(object):
if os.path.exists(self.subproject_root):
for subproj in os.listdir(self.subproject_root):
if subproj not in subprojects:
os.unlink(os.path.join(self.subproject_root, subproj))
safe_unlink(os.path.join(self.subproject_root, subproj))
def symlink_translations(self):
"""
@ -227,7 +227,7 @@ class Symlink(object):
# Make sure the language directory is a directory
language_dir = os.path.join(self.project_root, self.project.language)
if os.path.islink(language_dir):
os.unlink(language_dir)
safe_unlink(language_dir)
if not os.path.lexists(language_dir):
safe_makedirs(language_dir)
@ -246,7 +246,7 @@ class Symlink(object):
lang not in ['projects', self.project.language]):
to_delete = os.path.join(self.project_root, lang)
if os.path.islink(to_delete):
os.unlink(to_delete)
safe_unlink(to_delete)
else:
shutil.rmtree(to_delete)
@ -262,7 +262,7 @@ class Symlink(object):
# Clean up symlinks
symlink = self.project_root
if os.path.islink(symlink):
os.unlink(symlink)
safe_unlink(symlink)
if os.path.exists(symlink):
shutil.rmtree(symlink)
@ -300,7 +300,7 @@ class Symlink(object):
if os.path.exists(version_dir):
for old_ver in os.listdir(version_dir):
if old_ver not in versions:
os.unlink(os.path.join(version_dir, old_ver))
safe_unlink(os.path.join(version_dir, old_ver))
def get_default_version(self):
"""Look up project default version, return None if not found."""

View File

@ -225,3 +225,20 @@ def safe_makedirs(directory_name):
if e.errno == errno.EEXIST:
pass
raise
def safe_unlink(path):
"""
Unlink ``path`` symlink using ``os.unlink``.
This helper handles the exception ``FileNotFoundError`` to avoid logging in
cases where the symlink does not exist already and there is nothing to
unlink.
:param path: symlink path to unlink
:type path: str
"""
try:
os.unlink(path)
except FileNotFoundError:
log.warning('Unlink failed. Path %s does not exists', path)

View File

@ -36,7 +36,7 @@ from readthedocs.builds.syncers import Syncer
from readthedocs.config import ConfigError
from readthedocs.core.resolver import resolve_path
from readthedocs.core.symlink import PublicSymlink, PrivateSymlink
from readthedocs.core.utils import send_email, broadcast
from readthedocs.core.utils import send_email, broadcast, safe_unlink
from readthedocs.doc_builder.config import load_yaml_config
from readthedocs.doc_builder.constants import DOCKER_LIMITS
from readthedocs.doc_builder.environments import (
@ -973,7 +973,7 @@ def remove_orphan_symlinks():
for cname in orphan_cnames:
orphan_domain_path = os.path.join(domain_path, cname)
log.info('Unlinking orphan CNAME: %s', orphan_domain_path)
os.unlink(orphan_domain_path)
safe_unlink(orphan_domain_path)
@app.task(queue='web')