Skip to content

Clean up logic around how we delete files for deleted projects and inactive versions #1686

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 23, 2015
17 changes: 16 additions & 1 deletion readthedocs/builds/forms.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import logging

from django import forms

from readthedocs.builds.models import VersionAlias, Version
from readthedocs.projects.models import Project
from readthedocs.core.utils import trigger_build
from readthedocs.projects.models import Project
from readthedocs.projects.tasks import clear_artifacts


log = logging.getLogger(__name__)


class AliasForm(forms.ModelForm):
Expand Down Expand Up @@ -33,3 +39,12 @@ def save(self, *args, **kwargs):
obj = super(VersionForm, self).save(*args, **kwargs)
if obj.active and not obj.built and not obj.uploaded:
trigger_build(project=obj.project, version=obj)

def clean(self):
cleaned_data = super(VersionForm, self).clean()
if self.instance.pk is not None: # new instance only
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is wrong or misleading, this will actually only act on "old" instances in the sense of ones that already existed before the form was saved.

if self.instance.active is True and cleaned_data['active'] is False:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Django will actually change the self.instance attributes before it calls the save method somewhere in the full clean cycle. That has bitten me a few times in the past.

Therefore I think the best place to grab old, unsaved values is in the __init__ method and save those original values into a form attribute. Then using this to test if the value has changed.

In this particular case you could use the self.changed_data attribute: https://docs.djangoproject.com/en/1.8/ref/forms/api/#django.forms.Form.changed_data

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fancy, didn't know about that addition.

log.info('Removing files for version %s' % self.instance.slug)
clear_artifacts.delay(version_pk=self.instance.pk)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is clearing artifacts happening in the clean method? Validating a form should not have sideeffects.

self.instance.built = False
return cleaned_data
7 changes: 7 additions & 0 deletions readthedocs/builds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
REPO_TYPE_HG, GITHUB_URL,
GITHUB_REGEXS, BITBUCKET_URL,
BITBUCKET_REGEXS)
from readthedocs.projects.tasks import clear_artifacts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this introduced a cyclic dependency: https://travis-ci.org/rtfd/readthedocs.org/jobs/81886399#L213
You might want to move the import directly into the delete method.


from .constants import (BUILD_STATE, BUILD_TYPES, VERSION_TYPES,
LATEST, NON_REPOSITORY_VERSIONS, STABLE,
Expand Down Expand Up @@ -158,6 +159,12 @@ def save(self, *args, **kwargs):
self.project.sync_supported_versions()
return obj

def delete(self, *args, **kwargs):
log.info('Removing files for version %s' % self.slug)
clear_artifacts.delay(version_pk=self.pk)
super(Version, self).delete(*args, **kwargs)


@property
def identifier_friendly(self):
'''Return display friendly identifier'''
Expand Down
13 changes: 10 additions & 3 deletions readthedocs/projects/views/private.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from django.contrib.auth.models import User
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.conf import settings
from django.http import HttpResponseRedirect, HttpResponseNotAllowed, Http404
from django.db.models import Q
from django.shortcuts import get_object_or_404, render_to_response, render
Expand Down Expand Up @@ -35,7 +36,7 @@
from readthedocs.projects.models import Project, EmailHook, WebHook, Domain
from readthedocs.projects.views.base import ProjectAdminMixin
from readthedocs.projects import constants, tasks
from readthedocs.projects.tasks import remove_path_from_web
from readthedocs.projects.tasks import remove_dir


from readthedocs.projects.signals import project_import
Expand Down Expand Up @@ -220,8 +221,14 @@ def project_delete(request, project_slug):
slug=project_slug)

if request.method == 'POST':
# Remove the repository checkout
remove_path_from_web.delay(path=project.doc_path)
# Support hacky "broadcast" with MULTIPLE_APP_SERVERS setting,
# otherwise put in normal celery queue
for server in getattr(settings, "MULTIPLE_APP_SERVERS", ['celery']):
log.info('Removing files on %s' % server)
remove_dir.apply_async(
args=[project.doc_path],
queue=server,
)

# Delete the project and everything related to it
project.delete()
Expand Down