Supress exception with concurrent deletion

Relates https://github.com/ansible/ansible-tower/issues/7768

This issue, as well as
https://github.com/ansible/ansible-tower/issues/7622, both rooted in a
concurrency issue of Django ORM:
https://github.com/ansible/ansible-tower/issues/762://code.djangoproject.com/ticket/28806

The solution related deals specifically with the related issue, but is
not a general solution. A general workaround can be found in
https://github.com/ansible/tower/pull/500.

Signed-off-by: Aaron Tan <jangsutsr@gmail.com>
This commit is contained in:
Aaron Tan
2017-11-16 16:43:21 -05:00
parent e20599d7bb
commit 0641c6b0a6
2 changed files with 57 additions and 17 deletions

View File

@@ -1106,8 +1106,13 @@ class ProjectUpdateSerializer(UnifiedJobSerializer, ProjectOptionsSerializer):
def get_related(self, obj): def get_related(self, obj):
res = super(ProjectUpdateSerializer, self).get_related(obj) res = super(ProjectUpdateSerializer, self).get_related(obj)
try:
res.update(dict(
project = self.reverse('api:project_detail', kwargs={'pk': obj.project.pk}),
))
except ObjectDoesNotExist:
pass
res.update(dict( res.update(dict(
project = self.reverse('api:project_detail', kwargs={'pk': obj.project.pk}),
cancel = self.reverse('api:project_update_cancel', kwargs={'pk': obj.pk}), cancel = self.reverse('api:project_update_cancel', kwargs={'pk': obj.pk}),
scm_inventory_updates = self.reverse('api:project_update_scm_inventory_updates', kwargs={'pk': obj.pk}), scm_inventory_updates = self.reverse('api:project_update_scm_inventory_updates', kwargs={'pk': obj.pk}),
notifications = self.reverse('api:project_update_notifications_list', kwargs={'pk': obj.pk}), notifications = self.reverse('api:project_update_notifications_list', kwargs={'pk': obj.pk}),
@@ -1728,8 +1733,15 @@ class InventoryUpdateSerializer(UnifiedJobSerializer, InventorySourceOptionsSeri
def get_related(self, obj): def get_related(self, obj):
res = super(InventoryUpdateSerializer, self).get_related(obj) res = super(InventoryUpdateSerializer, self).get_related(obj)
try:
res.update(dict(
inventory_source = self.reverse(
'api:inventory_source_detail', kwargs={'pk': obj.inventory_source.pk}
),
))
except ObjectDoesNotExist:
pass
res.update(dict( res.update(dict(
inventory_source = self.reverse('api:inventory_source_detail', kwargs={'pk': obj.inventory_source.pk}),
cancel = self.reverse('api:inventory_update_cancel', kwargs={'pk': obj.pk}), cancel = self.reverse('api:inventory_update_cancel', kwargs={'pk': obj.pk}),
notifications = self.reverse('api:inventory_update_notifications_list', kwargs={'pk': obj.pk}), notifications = self.reverse('api:inventory_update_notifications_list', kwargs={'pk': obj.pk}),
)) ))
@@ -2339,14 +2351,30 @@ class JobOptionsSerializer(LabelsListMixin, BaseSerializer):
def get_related(self, obj): def get_related(self, obj):
res = super(JobOptionsSerializer, self).get_related(obj) res = super(JobOptionsSerializer, self).get_related(obj)
res['labels'] = self.reverse('api:job_template_label_list', kwargs={'pk': obj.pk}) res['labels'] = self.reverse('api:job_template_label_list', kwargs={'pk': obj.pk})
if obj.inventory: try:
res['inventory'] = self.reverse('api:inventory_detail', kwargs={'pk': obj.inventory.pk}) if obj.inventory:
if obj.project: res['inventory'] = self.reverse('api:inventory_detail', kwargs={'pk': obj.inventory.pk})
res['project'] = self.reverse('api:project_detail', kwargs={'pk': obj.project.pk}) except ObjectDoesNotExist:
if obj.credential: setattr(obj, 'inventory', None)
res['credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.credential.pk}) try:
if obj.vault_credential: if obj.project:
res['vault_credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.vault_credential.pk}) res['project'] = self.reverse('api:project_detail', kwargs={'pk': obj.project.pk})
except ObjectDoesNotExist:
setattr(obj, 'project', None)
try:
if obj.credential:
res['credential'] = self.reverse(
'api:credential_detail', kwargs={'pk': obj.credential.pk}
)
except ObjectDoesNotExist:
setattr(obj, 'credential', None)
try:
if obj.vault_credential:
res['vault_credential'] = self.reverse(
'api:credential_detail', kwargs={'pk': obj.vault_credential.pk}
)
except ObjectDoesNotExist:
setattr(obj, 'vault_credential', None)
if self.version > 1: if self.version > 1:
if isinstance(obj, UnifiedJobTemplate): if isinstance(obj, UnifiedJobTemplate):
res['extra_credentials'] = self.reverse( res['extra_credentials'] = self.reverse(
@@ -2584,15 +2612,23 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer):
notifications = self.reverse('api:job_notifications_list', kwargs={'pk': obj.pk}), notifications = self.reverse('api:job_notifications_list', kwargs={'pk': obj.pk}),
labels = self.reverse('api:job_label_list', kwargs={'pk': obj.pk}), labels = self.reverse('api:job_label_list', kwargs={'pk': obj.pk}),
)) ))
if obj.job_template: try:
res['job_template'] = self.reverse('api:job_template_detail', if obj.job_template:
kwargs={'pk': obj.job_template.pk}) res['job_template'] = self.reverse('api:job_template_detail',
kwargs={'pk': obj.job_template.pk})
except ObjectDoesNotExist:
setattr(obj, 'job_template', None)
if (obj.can_start or True) and self.version == 1: # TODO: remove in 3.3 if (obj.can_start or True) and self.version == 1: # TODO: remove in 3.3
res['start'] = self.reverse('api:job_start', kwargs={'pk': obj.pk}) res['start'] = self.reverse('api:job_start', kwargs={'pk': obj.pk})
if obj.can_cancel or True: if obj.can_cancel or True:
res['cancel'] = self.reverse('api:job_cancel', kwargs={'pk': obj.pk}) res['cancel'] = self.reverse('api:job_cancel', kwargs={'pk': obj.pk})
if obj.project_update: try:
res['project_update'] = self.reverse('api:project_update_detail', kwargs={'pk': obj.project_update.pk}) if obj.project_update:
res['project_update'] = self.reverse(
'api:project_update_detail', kwargs={'pk': obj.project_update.pk}
)
except ObjectDoesNotExist:
pass
res['relaunch'] = self.reverse('api:job_relaunch', kwargs={'pk': obj.pk}) res['relaunch'] = self.reverse('api:job_relaunch', kwargs={'pk': obj.pk})
return res return res

View File

@@ -12,6 +12,7 @@ from django.db.models import Q, Prefetch
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist
# Django REST Framework # Django REST Framework
from rest_framework.exceptions import ParseError, PermissionDenied, ValidationError from rest_framework.exceptions import ParseError, PermissionDenied, ValidationError
@@ -1134,8 +1135,11 @@ class ProjectUpdateAccess(BaseAccess):
def can_start(self, obj, validate_license=True): def can_start(self, obj, validate_license=True):
# for relaunching # for relaunching
if obj and obj.project: try:
return self.user in obj.project.update_role if obj and obj.project:
return self.user in obj.project.update_role
except ObjectDoesNotExist:
pass
return False return False
@check_superuser @check_superuser