SCM inventory cancel propagation

For manually initiated inventory updates, also cancel the source
project of "sync" type, like jobs do

For automatic inventory updates spawned from source project update,
of launch type "scm", handle contigency cases
This commit is contained in:
AlanCoding 2017-05-09 09:42:53 -04:00
parent 55c2f5a2d6
commit 04d6fb1f95
4 changed files with 48 additions and 1 deletions

View File

@ -1393,6 +1393,8 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin):
res = super(InventoryUpdate, self).cancel(job_explanation=job_explanation)
if res:
map(lambda x: x.cancel(job_explanation=self._build_job_explanation()), self.get_dependent_jobs())
if self.launch_type != 'scm' and self.source_project_update:
self.source_project_update.cancel(job_explanation=job_explanation)
return res

View File

@ -501,6 +501,13 @@ class ProjectUpdate(UnifiedJob, ProjectOptions, JobNotificationMixin):
update_fields.append('scm_delete_on_next_update')
parent_instance.save(update_fields=update_fields)
def cancel(self, job_explanation=None):
res = super(ProjectUpdate, self).cancel(job_explanation=job_explanation)
if res and self.launch_type != 'sync':
for inv_src in self.scm_inventory_updates.filter(status='running'):
inv_src.cancel(job_explanation='Source project update `{}` was canceled.'.format(self.name))
return res
'''
JobNotificationMixin
'''

View File

@ -1391,6 +1391,15 @@ class RunProjectUpdate(BaseTask):
except Exception as e:
# A failed file update does not block other actions
logger.error('Encountered error updating project dependent inventory: {}'.format(e))
# Stop all dependent inventory updates if project update was canceled
project_update.refresh_from_db()
if project_update.cancel_flag:
break
# Don't update inventory scm_revision if update was canceled
local_inv_update.refresh_from_db()
if local_inv_update.cancel_flag:
continue
inv_src.scm_last_revision = scm_revision
inv_src.save(update_fields=['scm_last_revision'])

View File

@ -3,7 +3,7 @@ import mock
import os
from awx.main.tasks import RunProjectUpdate, RunInventoryUpdate
from awx.main.models import ProjectUpdate, InventoryUpdate
from awx.main.models import ProjectUpdate, InventoryUpdate, InventorySource
@pytest.fixture
@ -43,3 +43,32 @@ class TestDependentInventoryUpdate:
inv_update = InventoryUpdate.objects.first()
iu_run_mock.assert_called_once_with(inv_update.id)
assert inv_update.source_project_update_id == proj_update.pk
def test_dependent_inventory_project_cancel(self, project, inventory):
'''
Test that dependent inventory updates exhibit good behavior on cancel
of the source project update
'''
task = RunProjectUpdate()
proj_update = ProjectUpdate.objects.create(project=project)
kwargs = dict(
source_project=project,
source='scm',
source_path='inventory_file',
update_on_project_update=True,
inventory=inventory
)
is1 = InventorySource.objects.create(name="test-scm-inv", **kwargs)
is2 = InventorySource.objects.create(name="test-scm-inv2", **kwargs)
def user_cancels_project(pk):
ProjectUpdate.objects.all().update(cancel_flag=True)
with mock.patch.object(RunInventoryUpdate, 'run') as iu_run_mock:
iu_run_mock.side_effect = user_cancels_project
task._update_dependent_inventories(proj_update, [is1, is2])
# Verify that it bails after 1st update, detecting a cancel
assert is2.inventory_updates.count() == 0
iu_run_mock.assert_called_once()