mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 01:57:35 -03:30
Merge branch 'devel' of https://github.com/sundeep-co-in/ansible-tower into STAGE
This commit is contained in:
commit
e908bc34e6
1
Makefile
1
Makefile
@ -229,6 +229,7 @@ clean-venv:
|
||||
|
||||
# Remove temporary build files, compiled Python files.
|
||||
clean: clean-rpm clean-deb clean-ui clean-tar clean-packer clean-bundle
|
||||
rm -rf awx/public
|
||||
rm -rf awx/lib/site-packages
|
||||
rm -rf dist/*
|
||||
rm -rf tmp
|
||||
|
||||
@ -2244,22 +2244,18 @@ class WorkflowJobListSerializer(WorkflowJobSerializer, UnifiedJobListSerializer)
|
||||
pass
|
||||
|
||||
class WorkflowNodeBaseSerializer(BaseSerializer):
|
||||
job_type = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
|
||||
job_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
|
||||
limit = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
|
||||
skip_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
|
||||
job_type = serializers.SerializerMethodField()
|
||||
job_tags = serializers.SerializerMethodField()
|
||||
limit = serializers.SerializerMethodField()
|
||||
skip_tags = serializers.SerializerMethodField()
|
||||
success_nodes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
failure_nodes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
always_nodes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
fail_on_job_failure = serializers.BooleanField(
|
||||
help_text=('If set to true, and if the job runs and fails, '
|
||||
'the workflow is marked as failed.'),
|
||||
default=True)
|
||||
|
||||
class Meta:
|
||||
fields = ('*', '-name', '-description', 'id', 'url', 'related',
|
||||
'unified_job_template', 'success_nodes', 'failure_nodes', 'always_nodes',
|
||||
'inventory', 'credential', 'job_type', 'job_tags', 'skip_tags', 'limit', 'skip_tags', 'fail_on_job_failure')
|
||||
'inventory', 'credential', 'job_type', 'job_tags', 'skip_tags', 'limit', 'skip_tags')
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(WorkflowNodeBaseSerializer, self).get_related(obj)
|
||||
@ -2267,12 +2263,17 @@ class WorkflowNodeBaseSerializer(BaseSerializer):
|
||||
res['unified_job_template'] = obj.unified_job_template.get_absolute_url()
|
||||
return res
|
||||
|
||||
def validate(self, attrs):
|
||||
# char_prompts go through different validation, so remove them here
|
||||
for fd in ['job_type', 'job_tags', 'skip_tags', 'limit']:
|
||||
if fd in attrs:
|
||||
attrs.pop(fd)
|
||||
return super(WorkflowNodeBaseSerializer, self).validate(attrs)
|
||||
def get_job_type(self, obj):
|
||||
return obj.char_prompts.get('job_type', None)
|
||||
|
||||
def get_job_tags(self, obj):
|
||||
return obj.char_prompts.get('job_tags', None)
|
||||
|
||||
def get_skip_tags(self, obj):
|
||||
return obj.char_prompts.get('skip_tags', None)
|
||||
|
||||
def get_limit(self, obj):
|
||||
return obj.char_prompts.get('limit', None)
|
||||
|
||||
|
||||
class WorkflowJobTemplateNodeSerializer(WorkflowNodeBaseSerializer):
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
# Workflow Job Template Workflow Node List
|
||||
|
||||
Workflow nodes reference templates to execute and define the ordering
|
||||
in which to execute them. After a job in this workflow finishes,
|
||||
the subsequent actions are to:
|
||||
|
||||
- run nodes contained in "failure_nodes" or "always_nodes" if job failed
|
||||
- run nodes contained in "success_nodes" or "always_nodes" if job succeeded
|
||||
|
||||
The workflow is marked as failed if any jobs run as part of that workflow fail
|
||||
and have the field `fail_on_job_failure` set to true. If not, the workflow
|
||||
job is marked as successful.
|
||||
|
||||
{% include "api/sub_list_create_api_view.md" %}
|
||||
@ -2406,7 +2406,6 @@ class JobTemplateLabelList(DeleteLastUnattachLabelMixin, SubListCreateAttachDeta
|
||||
serializer_class = LabelSerializer
|
||||
parent_model = JobTemplate
|
||||
relationship = 'labels'
|
||||
new_in_300 = True
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
# If a label already exists in the database, attach it instead of erroring out
|
||||
@ -2700,7 +2699,6 @@ class WorkflowJobTemplateList(ListCreateAPIView):
|
||||
model = WorkflowJobTemplate
|
||||
serializer_class = WorkflowJobTemplateListSerializer
|
||||
always_allow_superuser = False
|
||||
new_in_310 = True
|
||||
|
||||
# TODO: RBAC
|
||||
'''
|
||||
@ -2718,12 +2716,10 @@ class WorkflowJobTemplateDetail(RetrieveUpdateDestroyAPIView):
|
||||
model = WorkflowJobTemplate
|
||||
serializer_class = WorkflowJobTemplateSerializer
|
||||
always_allow_superuser = False
|
||||
new_in_310 = True
|
||||
|
||||
|
||||
class WorkflowJobTemplateLabelList(JobTemplateLabelList):
|
||||
parent_model = WorkflowJobTemplate
|
||||
new_in_310 = True
|
||||
|
||||
|
||||
# TODO:
|
||||
@ -2731,7 +2727,6 @@ class WorkflowJobTemplateLaunch(GenericAPIView):
|
||||
|
||||
model = WorkflowJobTemplate
|
||||
serializer_class = EmptySerializer
|
||||
new_in_310 = True
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
data = {}
|
||||
@ -2757,12 +2752,6 @@ class WorkflowJobTemplateWorkflowNodesList(SubListCreateAPIView):
|
||||
parent_model = WorkflowJobTemplate
|
||||
relationship = 'workflow_job_template_nodes'
|
||||
parent_key = 'workflow_job_template'
|
||||
new_in_310 = True
|
||||
|
||||
def update_raw_data(self, data):
|
||||
for fd in ['job_type', 'job_tags', 'skip_tags', 'limit', 'skip_tags']:
|
||||
data[fd] = None
|
||||
return super(WorkflowJobTemplateWorkflowNodesList, self).update_raw_data(data)
|
||||
|
||||
# TODO:
|
||||
class WorkflowJobTemplateJobsList(SubListAPIView):
|
||||
@ -2778,14 +2767,12 @@ class WorkflowJobList(ListCreateAPIView):
|
||||
|
||||
model = WorkflowJob
|
||||
serializer_class = WorkflowJobListSerializer
|
||||
new_in_310 = True
|
||||
|
||||
# TODO:
|
||||
class WorkflowJobDetail(RetrieveDestroyAPIView):
|
||||
|
||||
model = WorkflowJob
|
||||
serializer_class = WorkflowJobSerializer
|
||||
new_in_310 = True
|
||||
|
||||
class WorkflowJobWorkflowNodesList(SubListAPIView):
|
||||
|
||||
@ -2795,7 +2782,6 @@ class WorkflowJobWorkflowNodesList(SubListAPIView):
|
||||
parent_model = WorkflowJob
|
||||
relationship = 'workflow_job_nodes'
|
||||
parent_key = 'workflow_job'
|
||||
new_in_310 = True
|
||||
|
||||
class SystemJobTemplateList(ListAPIView):
|
||||
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0044_v310_project_playbook_files'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='workflowjobnode',
|
||||
name='fail_on_job_failure',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workflowjobtemplatenode',
|
||||
name='fail_on_job_failure',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
||||
@ -60,10 +60,6 @@ class WorkflowNodeBase(CreatedModifiedModel):
|
||||
default=None,
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
fail_on_job_failure = models.BooleanField(
|
||||
blank=True,
|
||||
default=True,
|
||||
)
|
||||
# Prompting-related fields
|
||||
inventory = models.ForeignKey(
|
||||
'Inventory',
|
||||
@ -97,22 +93,6 @@ class WorkflowNodeBase(CreatedModifiedModel):
|
||||
data[fd] = self.char_prompts[fd]
|
||||
return data
|
||||
|
||||
@property
|
||||
def job_type(self):
|
||||
return self.char_prompts.get('job_type', None)
|
||||
|
||||
@property
|
||||
def job_tags(self):
|
||||
return self.char_prompts.get('job_tags', None)
|
||||
|
||||
@property
|
||||
def skip_tags(self):
|
||||
return self.char_prompts.get('skip_tags', None)
|
||||
|
||||
@property
|
||||
def limit(self):
|
||||
return self.char_prompts.get('limit', None)
|
||||
|
||||
def get_prompts_warnings(self):
|
||||
ujt_obj = self.unified_job_template
|
||||
if ujt_obj is None:
|
||||
@ -157,7 +137,7 @@ class WorkflowNodeBase(CreatedModifiedModel):
|
||||
Return field names that should be copied from template node to job node.
|
||||
'''
|
||||
return ['workflow_job', 'unified_job_template',
|
||||
'inventory', 'credential', 'char_prompts', 'fail_on_job_failure']
|
||||
'inventory', 'credential', 'char_prompts']
|
||||
|
||||
class WorkflowJobTemplateNode(WorkflowNodeBase):
|
||||
# TODO: Ensure the API forces workflow_job_template being set
|
||||
@ -403,9 +383,6 @@ class WorkflowJob(UnifiedJob, WorkflowJobOptions, JobNotificationMixin, Workflow
|
||||
from awx.main.tasks import RunWorkflowJob
|
||||
return RunWorkflowJob
|
||||
|
||||
def _has_failed(self):
|
||||
return self.workflow_job_nodes.filter(job__status='failed', fail_on_job_failure=True).exists()
|
||||
|
||||
def socketio_emit_data(self):
|
||||
return {}
|
||||
|
||||
|
||||
@ -73,12 +73,10 @@ def process_finished_workflow_jobs(workflow_jobs):
|
||||
dag = WorkflowDAG(workflow_job)
|
||||
if dag.is_workflow_done():
|
||||
with transaction.atomic():
|
||||
if workflow_job._has_failed():
|
||||
workflow_job.status = 'failed'
|
||||
else:
|
||||
workflow_job.status = 'successful'
|
||||
# TODO: detect if wfj failed
|
||||
workflow_job.status = 'completed'
|
||||
workflow_job.save()
|
||||
workflow_job.websocket_emit_status(workflow_job.status)
|
||||
workflow_job.websocket_emit_status('completed')
|
||||
|
||||
def rebuild_graph():
|
||||
"""Regenerate the task graph by refreshing known tasks from Tower, purging
|
||||
|
||||
@ -90,42 +90,3 @@ class TestWorkflowJobTemplate:
|
||||
assert len(parent_qs) == 1
|
||||
assert parent_qs[0] == wfjt.workflow_job_template_nodes.all()[1]
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestWorkflowJobFailure:
|
||||
@pytest.fixture
|
||||
def wfj(self):
|
||||
return WorkflowJob.objects.create(name='test-wf-job')
|
||||
|
||||
def test_workflow_has_failed(self, wfj):
|
||||
"""
|
||||
Test that a single failed node with fail_on_job_failure = true
|
||||
leads to the entire WF being marked as failed
|
||||
"""
|
||||
job = Job.objects.create(name='test-job', status='failed')
|
||||
# Node has a failed job connected
|
||||
WorkflowJobNode.objects.create(workflow_job=wfj, job=job)
|
||||
assert wfj._has_failed()
|
||||
|
||||
def test_workflow_not_failed_unran_job(self, wfj):
|
||||
"""
|
||||
Test that an un-ran node will not mark workflow job as failed
|
||||
"""
|
||||
WorkflowJobNode.objects.create(workflow_job=wfj)
|
||||
assert not wfj._has_failed()
|
||||
|
||||
def test_workflow_not_failed_successful_job(self, wfj):
|
||||
"""
|
||||
Test that a sucessful node will not mark workflow job as failed
|
||||
"""
|
||||
job = Job.objects.create(name='test-job', status='successful')
|
||||
WorkflowJobNode.objects.create(workflow_job=wfj, job=job)
|
||||
assert not wfj._has_failed()
|
||||
|
||||
def test_workflow_not_failed_failed_job_but_okay(self, wfj):
|
||||
"""
|
||||
Test that a failed node will not mark workflow job as failed
|
||||
if the fail_on_job_failure is set to false
|
||||
"""
|
||||
job = Job.objects.create(name='test-job', status='failed')
|
||||
WorkflowJobNode.objects.create(workflow_job=wfj, job=job, fail_on_job_failure=False)
|
||||
assert not wfj._has_failed()
|
||||
|
||||
@ -139,7 +139,6 @@ class TestWorkflowJobCreate:
|
||||
char_prompts=wfjt_node_no_prompts.char_prompts,
|
||||
inventory=None, credential=None,
|
||||
unified_job_template=wfjt_node_no_prompts.unified_job_template,
|
||||
fail_on_job_failure=True,
|
||||
workflow_job=workflow_job_unit)
|
||||
|
||||
def test_create_with_prompts(self, wfjt_node_with_prompts, workflow_job_unit, mocker):
|
||||
@ -151,7 +150,6 @@ class TestWorkflowJobCreate:
|
||||
inventory=wfjt_node_with_prompts.inventory,
|
||||
credential=wfjt_node_with_prompts.credential,
|
||||
unified_job_template=wfjt_node_with_prompts.unified_job_template,
|
||||
fail_on_job_failure=True,
|
||||
workflow_job=workflow_job_unit)
|
||||
|
||||
@mock.patch('awx.main.models.workflow.WorkflowNodeBase.get_parent_nodes', lambda self: [])
|
||||
@ -217,7 +215,7 @@ class TestWorkflowWarnings:
|
||||
|
||||
def test_warn_scan_errors_node_prompts(self, job_node_with_prompts):
|
||||
job_node_with_prompts.unified_job_template.job_type = 'scan'
|
||||
job_node_with_prompts.char_prompts['job_type'] = 'run'
|
||||
job_node_with_prompts.job_type = 'run'
|
||||
job_node_with_prompts.inventory = Inventory(name='different-inventory', pk=23)
|
||||
assert 'ignored' in job_node_with_prompts.get_prompts_warnings()
|
||||
assert 'job_type' in job_node_with_prompts.get_prompts_warnings()['ignored']
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user