diff --git a/src/ralph/lib/transitions/async.py b/src/ralph/lib/transitions/async.py index b95808feb9..cdf7b02bd5 100644 --- a/src/ralph/lib/transitions/async.py +++ b/src/ralph/lib/transitions/async.py @@ -58,7 +58,7 @@ def run_async_transition(job_id): def _perform_async_transition(transition_job): transition = transition_job.transition obj = transition_job.obj - _check_instances_for_transition([obj], transition) + _check_instances_for_transition([obj], transition, check_async_job=False) _check_action_with_instances([obj], transition) # check if this job isn't already finished diff --git a/src/ralph/lib/transitions/models.py b/src/ralph/lib/transitions/models.py index 5867caf7fb..f65bdc5e85 100644 --- a/src/ralph/lib/transitions/models.py +++ b/src/ralph/lib/transitions/models.py @@ -31,7 +31,7 @@ get_field_by_relation_path ) from ralph.attachments.models import Attachment -from ralph.lib.external_services.models import Job +from ralph.lib.external_services.models import Job, JobStatus from ralph.lib.mixins.models import TimeStampMixin from ralph.lib.transitions.conf import ( DEFAULT_ASYNC_TRANSITION_SERVICE_NAME, @@ -147,7 +147,9 @@ def _check_and_get_transition(obj, transition, field): return transition -def _check_instances_for_transition(instances, transition): +def _check_instances_for_transition( + instances, transition, check_async_job=True +): """Check in respect of the instances source status. Args: @@ -169,6 +171,22 @@ def _check_instances_for_transition(instances, transition): for instance, error_details in error.items(): errors[instance].append(error_details) + if transition.is_async and check_async_job: + for instance in instances: + if TransitionJob.objects.filter( + object_id=instance.id, + content_type=ContentType.objects.get_for_model(instance), + status__in=(JobStatus.STARTED, JobStatus.QUEUED) + ).exists(): + logger.warning( + 'Another async transition is already running for {}'.format( + instance, + ) + ) + errors[instance].append( + 'Another async transition for this object is already stared' + ) + if errors: raise TransitionNotAllowedError( 'Transition {} is not allowed for objects'.format(transition.name), diff --git a/src/ralph/lib/transitions/tests/test_actions.py b/src/ralph/lib/transitions/tests/test_actions.py index 275eb49f7d..c9539b1e1f 100644 --- a/src/ralph/lib/transitions/tests/test_actions.py +++ b/src/ralph/lib/transitions/tests/test_actions.py @@ -14,7 +14,11 @@ OfficeInfrastructureFactory, WarehouseFactory ) -from ralph.lib.transitions.models import TransitionsHistory +from ralph.lib.transitions.models import ( + JobStatus, + TransitionJob, + TransitionsHistory +) from ralph.lib.transitions.tests import TransitionTestCase from ralph.licences.tests.factories import LicenceFactory @@ -198,6 +202,34 @@ def test_async_gui(self): history = self.get_transition_history(bo.pk) self.assertIn(self.user.username, history.kwargs['user']) + def test_async_gui_running_new_when_another_in_progress_should_return_error(self): # noqa + # mock another async job running + TransitionJob.objects.create( + obj=self.bo, + transition=self.transition_2, + status=JobStatus.STARTED, + service_name='ASYNC', + ) + request = self.client.post( + reverse( + 'admin:back_office_backofficeasset_transition', + args=(self.bo.id, self.transition_2.id) + ), + self.prepare_gui_data(), + follow=True + ) + self.assertTrue( + request.redirect_chain[0][0], + reverse( + 'admin:back_office_backofficeasset_change', + args=(self.bo.id,) + ) + ) + self.assertIn( + 'Another async transition for this object is already stared', + str(list(request.context['messages'])[1]) + ) + def test_api_options(self): request = self.api_client.options( reverse(