Skip to content

Commit

Permalink
Do not fail when unlinking an non-existing path (#4760)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
humitos authored and ericholscher committed Oct 16, 2018
1 parent bd4c82b commit 846c7e2
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 10 deletions.
16 changes: 8 additions & 8 deletions readthedocs/core/symlink.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@

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
Expand Down Expand Up @@ -95,7 +95,7 @@ def sanity_check(self):
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
Expand Down Expand Up @@ -164,7 +164,7 @@ def remove_symlink_cname(self, domain):
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):
"""
Expand Down Expand Up @@ -210,7 +210,7 @@ def symlink_subprojects(self):
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):
"""
Expand All @@ -227,7 +227,7 @@ def symlink_translations(self):
# 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)

Expand All @@ -246,7 +246,7 @@ def symlink_translations(self):
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)

Expand All @@ -262,7 +262,7 @@ def symlink_single_version(self):
# 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)

Expand Down Expand Up @@ -300,7 +300,7 @@ def symlink_versions(self):
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."""
Expand Down
17 changes: 17 additions & 0 deletions readthedocs/core/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
4 changes: 2 additions & 2 deletions readthedocs/projects/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
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 (
Expand Down Expand Up @@ -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')
Expand Down

0 comments on commit 846c7e2

Please sign in to comment.