Fix IntegrityError deleting job splitting JT

misc:
*show sharded jobs in recent_jobs
*test updates
This commit is contained in:
AlanCoding 2018-09-10 13:54:04 -04:00
parent f9bdb1da15
commit 7ff04dafd3
No known key found for this signature in database
GPG Key ID: FD2C3C012A72926B
8 changed files with 61 additions and 20 deletions

View File

@ -2976,12 +2976,9 @@ class JobTemplateMixin(object):
'''
def _recent_jobs(self, obj):
if hasattr(obj, 'workflow_jobs'):
job_mgr = obj.workflow_jobs
else:
job_mgr = obj.jobs
job_mgr = obj.unifiedjob_unified_jobs.non_polymorphic().only('id', 'status', 'finished')
return [{'id': x.id, 'status': x.status, 'finished': x.finished}
for x in job_mgr.all().order_by('-created')[:10]]
for x in job_mgr.order_by('-created')[:10]]
def get_summary_fields(self, obj):
d = super(JobTemplateMixin, self).get_summary_fields(obj)

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.11 on 2018-09-10 17:41
from __future__ import unicode_literals
import awx.main.utils.polymorphic
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0049_v340_add_job_template'),
]
operations = [
migrations.AlterField(
model_name='unifiedjob',
name='unified_job_template',
field=models.ForeignKey(default=None, editable=False, null=True, on_delete=awx.main.utils.polymorphic.SET_NULL, related_name='unifiedjob_unified_jobs', to='main.UnifiedJobTemplate'),
),
]

View File

@ -320,10 +320,13 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
def resources_needed_to_start(self):
return [fd for fd in ['project', 'inventory'] if not getattr(self, '{}_id'.format(fd))]
def create_unified_job(self, **kwargs):
def create_job(self, **kwargs):
'''
Create a new job based on this template.
'''
return self.create_unified_job(**kwargs)
def create_unified_job(self, **kwargs):
split_event = bool(
self.job_shard_count > 1 and
not kwargs.pop('_prevent_sharding', False)
@ -345,7 +348,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
create_kwargs = dict(workflow_job=job,
unified_job_template=self,
ancestor_artifacts=dict(job_shard=idx))
wfjn = WorkflowJobNode.objects.create(**create_kwargs)
WorkflowJobNode.objects.create(**create_kwargs)
return job
def get_absolute_url(self, request=None):
@ -480,7 +483,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
RelatedJobsMixin
'''
def _get_related_jobs(self):
return Job.objects.filter(job_template=self)
return UnifiedJob.objects.filter(unified_job_template=self)
class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskManagerJobMixin):

View File

@ -321,8 +321,6 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio
'''
Create a new unified job based on this unified job template.
'''
from awx.main.models import JobTemplate, WorkflowJob
new_job_passwords = kwargs.pop('survey_passwords', {})
eager_fields = kwargs.pop('_eager_fields', None)
@ -553,7 +551,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
default=None,
editable=False,
related_name='%(class)s_unified_jobs',
on_delete=models.SET_NULL,
on_delete=polymorphic.SET_NULL,
)
launch_type = models.CharField(
max_length=20,
@ -834,7 +832,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
'''
unified_job_class = self.__class__
unified_jt_class = self._get_unified_job_template_class()
parent_field_name = unified_job_class._get_parent_field_name()
parent_field_name = self._get_parent_field_name()
fields = unified_jt_class._get_unified_job_field_names() | set([parent_field_name])
create_data = {}
@ -881,6 +879,8 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
config = JobLaunchConfig(job=self)
if parent is None:
parent = getattr(self, self._get_parent_field_name())
if parent is None:
return
valid_fields = parent.get_ask_mapping().keys()
# Special cases allowed for workflows
if hasattr(self, 'extra_vars'):

View File

@ -122,6 +122,22 @@ def test_job_relaunch_on_failed_hosts(post, inventory, project, machine_credenti
assert r.data.get('limit') == hosts
@pytest.mark.django_db
def test_shard_jt_recent_jobs(shard_job_factory, admin_user, get):
workflow_job = shard_job_factory(3, spawn=True)
shard_jt = workflow_job.job_template
r = get(
url=shard_jt.get_absolute_url(),
user=admin_user,
expect=200
)
job_ids = [entry['id'] for entry in r.data['summary_fields']['recent_jobs']]
assert workflow_job.pk in job_ids
for node in workflow_job.workflow_nodes.all():
job = node.job
assert job.pk in job_ids
@pytest.mark.django_db
def test_block_unprocessed_events(delete, admin_user, mocker):
time_of_finish = parse("Thu Feb 28 09:10:20 2013 -0500")

View File

@ -82,6 +82,7 @@ def test_job_host_summary_representation(host):
host.delete()
assert 'N/A changed=1 dark=2 failures=3 ok=4 processed=5 skipped=6' == six.text_type(jhs)
@pytest.mark.django_db
class TestShardingModels:
@ -97,6 +98,6 @@ class TestShardingModels:
job = shard_job_factory(3, jt_kwargs={'ask_limit_on_launch': True}, prompts={'limit': 'foobar'}, spawn=True)
assert job.launch_config.prompts_dict() == {'limit': 'foobar'}
for node in job.workflow_nodes.all():
assert node.limit == None # data not saved in node prompts
assert node.limit is None # data not saved in node prompts
job = node.job
assert job.limit == 'foobar'

View File

@ -58,9 +58,7 @@ class TestCreateUnifiedJob:
job_with_links.save()
job_with_links.credentials.add(machine_credential)
job_with_links.credentials.add(net_credential)
with mocker.patch('awx.main.models.unified_jobs.UnifiedJobTemplate._get_unified_job_field_names',
return_value=['inventory', 'credential', 'limit']):
second_job = job_with_links.copy_unified_job()
second_job = job_with_links.copy_unified_job()
# Check that job data matches the original variables
assert second_job.credential == job_with_links.credential

View File

@ -71,14 +71,19 @@ class TestJobTemplateSerializerGetRelated():
class TestJobTemplateSerializerGetSummaryFields():
def test__recent_jobs(self, mocker, job_template, jobs):
job_template.jobs.all = mocker.MagicMock(**{'order_by.return_value': jobs})
job_template.jobs.all.return_value = job_template.jobs.all
job_template.unifiedjob_unified_jobs = mocker.MagicMock(**{
'non_polymorphic.return_value': mocker.MagicMock(**{
'only.return_value': mocker.MagicMock(**{
'order_by.return_value': jobs
})
})
})
serializer = JobTemplateSerializer()
recent_jobs = serializer._recent_jobs(job_template)
job_template.jobs.all.assert_called_once_with()
job_template.jobs.all.order_by.assert_called_once_with('-created')
job_template.unifiedjob_unified_jobs.non_polymorphic.assert_called_once_with()
job_template.unifiedjob_unified_jobs.non_polymorphic().only().order_by.assert_called_once_with('-created')
assert len(recent_jobs) == 10
for x in jobs[:10]:
assert recent_jobs == [{'id': x.id, 'status': x.status, 'finished': x.finished} for x in jobs[:10]]