From 76e3e607ac703884bae4bc6452203c78715c3931 Mon Sep 17 00:00:00 2001 From: Jiri Kyjovsky Date: Mon, 10 Jul 2023 13:22:43 +0200 Subject: [PATCH] frontend: don't send new create project task if project is deleting wait for the end of the deletion of project to avoid race condition when fork is called immidiatelly after delete --- .../coprs/logic/complex_logic.py | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/frontend/coprs_frontend/coprs/logic/complex_logic.py b/frontend/coprs_frontend/coprs/logic/complex_logic.py index 9a77d5da1..81bdf2203 100644 --- a/frontend/coprs_frontend/coprs/logic/complex_logic.py +++ b/frontend/coprs_frontend/coprs/logic/complex_logic.py @@ -1,5 +1,4 @@ # coding: utf-8 - import os import datetime import time @@ -7,7 +6,9 @@ import flask import sqlalchemy -from copr_common.enums import StatusEnum +from sqlalchemy import and_ + +from copr_common.enums import StatusEnum, ActionTypeEnum, BackendResultEnum, ActionObjectTypeEnum from coprs import app from coprs import db from coprs import helpers @@ -125,6 +126,42 @@ def delete_expired_projects(cls): print(e) print("project {} postponed".format(copr.full_name)) + @classmethod + def _wait_for_copr_to_be_deleted(cls, ownername, dirname): + actions_to_wait = ( + models.Action.query + .filter(models.Action.object_type == ActionObjectTypeEnum.copr) + .filter(models.Action.result == BackendResultEnum("waiting")) + .filter(models.Action.action_type == ActionTypeEnum("delete")) + .filter( + and_( + models.Action.data.contains(ownername), + models.Action.data.contains(dirname) + ) + ) + ).all() + + for _ in range(3600): # if project is in deleting for a day it's bad, right? + for action_to_refresh in actions_to_wait: + db.session.refresh(action_to_refresh) + + if all(action.result != BackendResultEnum("waiting") for action in actions_to_wait): + app.logger.info( + f"No blocking actions for project {ownername}/{dirname} so far. Forking..." + ) + return + + blocking_actions = list( + filter( + lambda action: action.result == BackendResultEnum("waiting"), + actions_to_wait + ) + ) + app.logger.info( + f"Blocking delete actions: {blocking_actions} to wait for project:" + f" {ownername}/{dirname}" + ) + time.sleep(60) @classmethod def fork_copr(cls, copr, user, dstname, dstgroup=None): @@ -133,6 +170,8 @@ def fork_copr(cls, copr, user, dstname, dstgroup=None): created = (not bool(forking.get(copr, dstname))) fcopr = forking.fork_copr(copr, dstname) + cls._wait_for_copr_to_be_deleted(fcopr.owner_name, fcopr.name) + if fcopr.full_name == copr.full_name: raise exceptions.DuplicateException("Source project should not be same as destination")