From 9054917008f83e883a3b5ffc7881163044397030 Mon Sep 17 00:00:00 2001 From: Pavel Raiskup Date: May 28 2019 12:19:49 +0000 Subject: [frontend] delete builds in temporary projects Also, don't delete it yet if there's some pending action. Make sure this all is properly unit-tested. Thanks for the report to @dturecek. --- diff --git a/frontend/coprs_frontend/commands/clean_expired_projects.py b/frontend/coprs_frontend/commands/clean_expired_projects.py index c7316f8..cf4ce3c 100644 --- a/frontend/coprs_frontend/commands/clean_expired_projects.py +++ b/frontend/coprs_frontend/commands/clean_expired_projects.py @@ -1,6 +1,6 @@ from flask_script import Command from coprs import db_session_scope -from coprs.logic.coprs_logic import CoprsLogic +from coprs.logic.complex_logic import ComplexLogic class CleanExpiredProjectsCommand(Command): @@ -12,4 +12,4 @@ class CleanExpiredProjectsCommand(Command): # pylint: disable=method-hidden def run(self): with db_session_scope(): - CoprsLogic.delete_expired_projects() + ComplexLogic.delete_expired_projects() diff --git a/frontend/coprs_frontend/coprs/logic/complex_logic.py b/frontend/coprs_frontend/coprs/logic/complex_logic.py index 68ba0ca..9401261 100644 --- a/frontend/coprs_frontend/coprs/logic/complex_logic.py +++ b/frontend/coprs_frontend/coprs/logic/complex_logic.py @@ -1,5 +1,6 @@ # coding: utf-8 +import datetime import time import flask import sqlalchemy @@ -9,7 +10,7 @@ from .builds_logic import BuildsLogic from copr_common.enums import StatusEnum from coprs import models from coprs import exceptions -from coprs.exceptions import ObjectNotFound +from coprs.exceptions import ObjectNotFound, ActionInProgressException from coprs.logic.packages_logic import PackagesLogic from coprs.logic.actions_logic import ActionsLogic @@ -24,23 +25,48 @@ class ComplexLogic(object): """ @classmethod - def delete_copr(cls, copr): + def delete_copr(cls, copr, admin_action=False): """ Delete copr and all its builds. :param copr: + :param admin_action: set to True to bypass permission check :raises ActionInProgressException: :raises InsufficientRightsException: """ + + if admin_action: + user = copr.user + else: + user = flask.g.user + builds_query = BuildsLogic.get_multiple_by_copr(copr=copr) if copr.persistent: raise exceptions.InsufficientRightsException("This project is protected against deletion.") for build in builds_query: - BuildsLogic.delete_build(flask.g.user, build, send_delete_action=False) + BuildsLogic.delete_build(user, build, send_delete_action=False) + + CoprsLogic.delete_unsafe(user, copr) + + + @classmethod + def delete_expired_projects(cls): + query = ( + models.Copr.query + .filter(models.Copr.delete_after.isnot(None)) + .filter(models.Copr.delete_after < datetime.datetime.now()) + .filter(models.Copr.deleted.isnot(True)) + ) + for copr in query.all(): + print("deleting project '{}'".format(copr.full_name)) + try: + cls.delete_copr(copr, admin_action=True) + except ActionInProgressException as e: + print(e) + print("project {} postponed".format(copr.full_name)) - CoprsLogic.delete_unsafe(flask.g.user, copr) @classmethod def fork_copr(cls, copr, user, dstname, dstgroup=None): diff --git a/frontend/coprs_frontend/coprs/logic/coprs_logic.py b/frontend/coprs_frontend/coprs/logic/coprs_logic.py index 00c7224..3ada091 100644 --- a/frontend/coprs_frontend/coprs/logic/coprs_logic.py +++ b/frontend/coprs_frontend/coprs/logic/coprs_logic.py @@ -370,20 +370,6 @@ class CoprsLogic(object): raise exceptions.InsufficientRightsException( "Only owners may delete their projects.") - @classmethod - def delete_expired_projects(cls): - query = ( - models.Copr.query - .filter(models.Copr.delete_after.isnot(None)) - .filter(models.Copr.delete_after < datetime.datetime.now()) - .filter(models.Copr.deleted.isnot(True)) - ) - for copr in query.all(): - print("deleting project '{}'".format(copr.full_name)) - CoprsLogic.delete_unsafe(copr.user, copr) - - - class CoprPermissionsLogic(object): @classmethod def get(cls, copr, searched_user): diff --git a/frontend/coprs_frontend/tests/test_logic/test_complex_logic.py b/frontend/coprs_frontend/tests/test_logic/test_complex_logic.py index fcb3c72..cd4e72a 100644 --- a/frontend/coprs_frontend/tests/test_logic/test_complex_logic.py +++ b/frontend/coprs_frontend/tests/test_logic/test_complex_logic.py @@ -1,6 +1,8 @@ +import datetime import json from unittest import mock +from coprs import models, helpers from copr_common.enums import ActionTypeEnum from coprs.logic.actions_logic import ActionsLogic from coprs.logic.complex_logic import ComplexLogic, ProjectForking @@ -23,6 +25,39 @@ class TestComplexLogic(CoprsTestCase): assert data["builds_map"] == {'fedora-18-x86_64': ['bar', '00000005-hello-world'], 'srpm-builds': ['bar', '00000005']} + def test_delete_expired_coprs(self, f_users, f_mock_chroots, f_coprs, f_builds, f_db): + query = self.db.session.query(models.Copr) + + # nothing is deleted at the beginning + assert len([c for c in query.all() if c.deleted]) == 0 + + # one is to be deleted in the future + self.c1.delete_after_days = 2 + # one is already to be deleted + self.c2.delete_after = datetime.datetime.now() - datetime.timedelta(days=1) + + # and one is not to be temporary at all (c3) + + ComplexLogic.delete_expired_projects() + self.db.session.commit() + + query = self.db.session.query(models.Copr) + assert len(query.all()) == 3 # we only set deleted=true + + # some builds are not finished, nothing deleted yet + assert len([c for c in query.all() if c.deleted]) == 0 + + b = self.db.session.query(models.Build).get(3) + b.canceled = True + + ComplexLogic.delete_expired_projects() + self.db.session.commit() + # some builds are not finished, nothing deleted yet + assert len([c for c in query.all() if c.deleted]) == 1 + + # test that build is deleted as well + assert not self.db.session.query(models.Build).get(3) + class TestProjectForking(CoprsTestCase): diff --git a/frontend/coprs_frontend/tests/test_logic/test_coprs_logic.py b/frontend/coprs_frontend/tests/test_logic/test_coprs_logic.py index 5681fa6..4ef31c4 100644 --- a/frontend/coprs_frontend/tests/test_logic/test_coprs_logic.py +++ b/frontend/coprs_frontend/tests/test_logic/test_coprs_logic.py @@ -79,24 +79,3 @@ class TestCoprsLogic(CoprsTestCase): data = json.loads(actions[0].data) assert data["ownername"] == self.u1.name assert data["projectname"] == name - - - def test_delete_expired_coprs(self, f_users, f_mock_chroots, f_coprs, f_db): - query = self.db.session.query(models.Copr) - - # nothing is deleted at the beginning - assert len([c for c in query.all() if c.deleted]) == 0 - - # one is to be deleted in the future - self.c1.delete_after_days = 2 - # one is already to be deleted - self.c2.delete_after = datetime.datetime.now() - datetime.timedelta(days=1) - - # and one is not to be temporary at all (c3) - - CoprsLogic.delete_expired_projects() - self.db.session.commit() - - query = self.db.session.query(models.Copr) - assert len(query.all()) == 3 # we only set deleted=true - assert len([c for c in query.all() if c.deleted]) == 1