summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Jones <matburt@redhat.com>2016-07-15 17:46:42 +0200
committerMatthew Jones <matburt@redhat.com>2016-07-15 17:47:02 +0200
commiteb97b8c91646a55e360eb5f2f8ea800f2f5a1696 (patch)
tree5e2fecb49d1d7d318ee2c4aa643d3c5ca1b258a6
parentSetting the local var CredentialList to the deep clone seems to be problemati... (diff)
downloadawx-eb97b8c91646a55e360eb5f2f8ea800f2f5a1696.tar.xz
awx-eb97b8c91646a55e360eb5f2f8ea800f2f5a1696.zip
Switch disallowed object delete to 409
In the case of running job conflicts
-rw-r--r--awx/api/views.py8
-rw-r--r--awx/main/access.py50
-rw-r--r--awx/main/tests/functional/api/test_job_template.py12
3 files changed, 45 insertions, 25 deletions
diff --git a/awx/api/views.py b/awx/api/views.py
index 64ea980f66..908cb1c95a 100644
--- a/awx/api/views.py
+++ b/awx/api/views.py
@@ -2184,14 +2184,6 @@ class JobTemplateDetail(RetrieveUpdateDestroyAPIView):
serializer_class = JobTemplateSerializer
always_allow_superuser = False
- def destroy(self, request, *args, **kwargs):
- obj = self.get_object()
- can_delete = request.user.can_access(JobTemplate, 'delete', obj)
- if not can_delete:
- raise PermissionDenied("Cannot delete job template.")
- return super(JobTemplateDetail, self).destroy(request, *args, **kwargs)
-
-
class JobTemplateLaunch(RetrieveAPIView, GenericAPIView):
model = JobTemplate
diff --git a/awx/main/access.py b/awx/main/access.py
index af05c58be6..f96f9baf26 100644
--- a/awx/main/access.py
+++ b/awx/main/access.py
@@ -12,7 +12,7 @@ from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
# Django REST Framework
-from rest_framework.exceptions import ParseError, PermissionDenied, ValidationError
+from rest_framework.exceptions import ParseError, PermissionDenied, APIException
# AWX
from awx.main.utils import * # noqa
@@ -25,7 +25,7 @@ from awx.main.conf import tower_settings
__all__ = ['get_user_queryset', 'check_user_access',
'user_accessible_objects',
- 'user_admin_role',]
+ 'user_admin_role', 'StateConflict',]
PERMISSION_TYPES = [
PERM_INVENTORY_ADMIN,
@@ -57,6 +57,9 @@ access_registry = {
# ...
}
+class StateConflict(APIException):
+ status_code = 409
+ default_detail = 'Object state is not correct'
def register_access(model_class, access_class):
access_classes = access_registry.setdefault(model_class, [])
@@ -315,11 +318,15 @@ class OrganizationAccess(BaseAccess):
if not is_change_possible:
return False
active_jobs = []
- active_jobs.extend(Job.objects.filter(project__in=obj.projects.all(), status__in=ACTIVE_STATES))
- active_jobs.extend(ProjectUpdate.objects.filter(project__in=obj.projects.all(), status__in=ACTIVE_STATES))
- active_jobs.extend(InventoryUpdate.objects.filter(inventory_source__inventory__organization=obj, status__in=ACTIVE_STATES))
+ active_jobs.extend([dict(type="job", id=o.id)
+ for o in Job.objects.filter(project__in=obj.projects.all(), status__in=ACTIVE_STATES)])
+ active_jobs.extend([dict(type="project_update", id=o.id)
+ for o in ProjectUpdate.objects.filter(project__in=obj.projects.all(), status__in=ACTIVE_STATES)])
+ active_jobs.extend([dict(type="inventory_update", id=o.id)
+ for o in InventoryUpdate.objects.filter(inventory_source__inventory__organization=obj, status__in=ACTIVE_STATES)])
if len(active_jobs) > 0:
- raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs)))
+ raise StateConflict({"conflict": "Resource is being used by running jobs",
+ "active_jobs": active_jobs})
return True
class InventoryAccess(BaseAccess):
@@ -387,10 +394,13 @@ class InventoryAccess(BaseAccess):
if not is_can_admin:
return False
active_jobs = []
- active_jobs.extend(Job.objects.filter(inventory=obj, status__in=ACTIVE_STATES))
- active_jobs.extend(InventoryUpdate.objects.filter(inventory_source__inventory=obj, status__in=ACTIVE_STATES))
+ active_jobs.extend([dict(type="job", id=o.id)
+ for o in Job.objects.filter(inventory=obj, status__in=ACTIVE_STATES)])
+ active_jobs.extend([dict(type="inventory_update", id=o.id)
+ for o in InventoryUpdate.objects.filter(inventory_source__inventory=obj, status__in=ACTIVE_STATES)])
if len(active_jobs) > 0:
- raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs)))
+ raise StateConflict({"conflict": "Resource is being used by running jobs",
+ "active_jobs": active_jobs})
return True
def can_run_ad_hoc_commands(self, obj):
@@ -508,9 +518,11 @@ class GroupAccess(BaseAccess):
if not is_delete_allowed:
return False
active_jobs = []
- active_jobs.extend(InventoryUpdate.objects.filter(inventory_source__in=obj.inventory_sources.all(), status__in=ACTIVE_STATES))
+ active_jobs.extend([dict(type="inventory_update", id=o.id)
+ for o in InventoryUpdate.objects.filter(inventory_source__in=obj.inventory_sources.all(), status__in=ACTIVE_STATES)])
if len(active_jobs) > 0:
- raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs)))
+ raise StateConflict({"conflict": "Resource is being used by running jobs",
+ "active_jobs": active_jobs})
return True
class InventorySourceAccess(BaseAccess):
@@ -765,10 +777,13 @@ class ProjectAccess(BaseAccess):
if not is_change_allowed:
return False
active_jobs = []
- active_jobs.extend(Job.objects.filter(project=obj, status__in=ACTIVE_STATES))
- active_jobs.extend(ProjectUpdate.objects.filter(project=obj, status__in=ACTIVE_STATES))
+ active_jobs.extend([dict(type="job", id=o.id)
+ for o in Job.objects.filter(project=obj, status__in=ACTIVE_STATES)])
+ active_jobs.extend([dict(type="project_update", id=o.id)
+ for o in ProjectUpdate.objects.filter(project=obj, status__in=ACTIVE_STATES)])
if len(active_jobs) > 0:
- raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs)))
+ raise StateConflict({"conflict": "Resource is being used by running jobs",
+ "active_jobs": active_jobs})
return True
@check_superuser
@@ -989,14 +1004,15 @@ class JobTemplateAccess(BaseAccess):
return True
- @check_superuser
def can_delete(self, obj):
is_delete_allowed = self.user in obj.admin_role
if not is_delete_allowed:
return False
- active_jobs = obj.jobs.filter(status__in=ACTIVE_STATES)
+ active_jobs = [dict(type="job", id=o.id)
+ for o in obj.jobs.filter(status__in=ACTIVE_STATES)]
if len(active_jobs) > 0:
- raise ValidationError("Delete not allowed while there are jobs running. Number of jobs {}".format(len(active_jobs)))
+ raise StateConflict({"conflict": "Resource is being used by running jobs",
+ "active_jobs": active_jobs})
return True
class JobAccess(BaseAccess):
diff --git a/awx/main/tests/functional/api/test_job_template.py b/awx/main/tests/functional/api/test_job_template.py
index 14c92c4a54..e9aa4135c4 100644
--- a/awx/main/tests/functional/api/test_job_template.py
+++ b/awx/main/tests/functional/api/test_job_template.py
@@ -335,3 +335,15 @@ def test_jt_without_project(inventory):
data["job_type"] = "scan"
serializer = JobTemplateSerializer(data=data)
assert serializer.is_valid()
+
+@pytest.mark.django_db
+def test_disallow_template_delete_on_running_job(job_template_factory, delete, admin_user):
+ objects = job_template_factory('jt',
+ credential='c',
+ job_type="run",
+ project='p',
+ inventory='i',
+ organization='o')
+ j = objects.job_template.create_unified_job()
+ delete_response = delete(reverse('api:job_template_detail', args=[objects.job_template.pk]), user=admin_user)
+ assert delete_response.status_code == 409