diff --git a/awx/api/serializers.py b/awx/api/serializers.py
index 01664f03fd..5d7b90ae51 100644
--- a/awx/api/serializers.py
+++ b/awx/api/serializers.py
@@ -3199,7 +3199,7 @@ class JobRelaunchSerializer(BaseSerializer):
return attrs
-class JobCreateScheduleSerializer(BaseSerializer):
+class JobCreateScheduleSerializer(LabelsListMixin, BaseSerializer):
can_schedule = serializers.SerializerMethodField()
prompts = serializers.SerializerMethodField()
@@ -3230,6 +3230,8 @@ class JobCreateScheduleSerializer(BaseSerializer):
if 'credentials' in ret:
all_creds = [self._summarize('credential', cred) for cred in ret['credentials']]
ret['credentials'] = all_creds
+ if 'labels' in ret:
+ ret['labels'] = self._summary_field_labels(obj)
return ret
except JobLaunchConfig.DoesNotExist:
return {'all': _('Unknown, job may have been ran before launch configurations were saved.')}
@@ -3402,6 +3404,9 @@ class WorkflowJobTemplateSerializer(JobTemplateMixin, LabelsListMixin, UnifiedJo
limit = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
scm_branch = 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_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
+
class Meta:
model = WorkflowJobTemplate
fields = (
@@ -3420,6 +3425,11 @@ class WorkflowJobTemplateSerializer(JobTemplateMixin, LabelsListMixin, UnifiedJo
'webhook_service',
'webhook_credential',
'-execution_environment',
+ 'ask_labels_on_launch',
+ 'ask_skip_tags_on_launch',
+ 'ask_tags_on_launch',
+ 'skip_tags',
+ 'job_tags',
)
def get_related(self, obj):
@@ -3458,12 +3468,13 @@ class WorkflowJobTemplateSerializer(JobTemplateMixin, LabelsListMixin, UnifiedJo
def validate_extra_vars(self, value):
return vars_validate_or_raise(value)
+ # posting
def validate(self, attrs):
attrs = super(WorkflowJobTemplateSerializer, self).validate(attrs)
# process char_prompts, these are not direct fields on the model
mock_obj = self.Meta.model()
- for field_name in ('scm_branch', 'limit'):
+ for field_name in ('scm_branch', 'limit', 'skip_tags', 'job_tags'):
if field_name in attrs:
setattr(mock_obj, field_name, attrs[field_name])
attrs.pop(field_name)
@@ -3489,6 +3500,9 @@ class WorkflowJobSerializer(LabelsListMixin, UnifiedJobSerializer):
limit = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
scm_branch = 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_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
+
class Meta:
model = WorkflowJob
fields = (
@@ -3508,6 +3522,8 @@ class WorkflowJobSerializer(LabelsListMixin, UnifiedJobSerializer):
'webhook_service',
'webhook_credential',
'webhook_guid',
+ 'skip_tags',
+ 'job_tags',
)
def get_related(self, obj):
@@ -4333,6 +4349,10 @@ class WorkflowJobLaunchSerializer(BaseSerializer):
scm_branch = serializers.CharField(required=False, write_only=True, allow_blank=True)
workflow_job_template_data = serializers.SerializerMethodField()
+ labels = serializers.PrimaryKeyRelatedField(many=True, queryset=Label.objects.all(), required=False, write_only=True)
+ skip_tags = serializers.CharField(required=False, write_only=True, allow_blank=True)
+ job_tags = serializers.CharField(required=False, write_only=True, allow_blank=True)
+
class Meta:
model = WorkflowJobTemplate
fields = (
@@ -4352,8 +4372,22 @@ class WorkflowJobLaunchSerializer(BaseSerializer):
'workflow_job_template_data',
'survey_enabled',
'ask_variables_on_launch',
+ 'ask_labels_on_launch',
+ 'labels',
+ 'ask_skip_tags_on_launch',
+ 'ask_tags_on_launch',
+ 'skip_tags',
+ 'job_tags',
+ )
+ read_only_fields = (
+ 'ask_inventory_on_launch',
+ 'ask_variables_on_launch',
+ 'ask_skip_tags_on_launch',
+ 'ask_labels_on_launch',
+ 'ask_limit_on_launch',
+ 'ask_scm_branch_on_launch',
+ 'ask_tags_on_launch',
)
- read_only_fields = ('ask_inventory_on_launch', 'ask_variables_on_launch')
def get_survey_enabled(self, obj):
if obj:
@@ -4361,10 +4395,15 @@ class WorkflowJobLaunchSerializer(BaseSerializer):
return False
def get_defaults(self, obj):
+
defaults_dict = {}
for field_name in WorkflowJobTemplate.get_ask_mapping().keys():
if field_name == 'inventory':
defaults_dict[field_name] = dict(name=getattrd(obj, '%s.name' % field_name, None), id=getattrd(obj, '%s.pk' % field_name, None))
+ elif field_name == 'labels':
+ for label in obj.labels.all():
+ label_dict = {"id": label.id, "name": label.name}
+ defaults_dict.setdefault(field_name, []).append(label_dict)
else:
defaults_dict[field_name] = getattr(obj, field_name)
return defaults_dict
@@ -4373,6 +4412,7 @@ class WorkflowJobLaunchSerializer(BaseSerializer):
return dict(name=obj.name, id=obj.id, description=obj.description)
def validate(self, attrs):
+
template = self.instance
accepted, rejected, errors = template._accept_or_ignore_job_kwargs(**attrs)
@@ -4390,6 +4430,7 @@ class WorkflowJobLaunchSerializer(BaseSerializer):
WFJT_inventory = template.inventory
WFJT_limit = template.limit
WFJT_scm_branch = template.scm_branch
+
super(WorkflowJobLaunchSerializer, self).validate(attrs)
template.extra_vars = WFJT_extra_vars
template.inventory = WFJT_inventory
diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py
index 00d59484d8..012e320bc8 100644
--- a/awx/api/views/__init__.py
+++ b/awx/api/views/__init__.py
@@ -3197,13 +3197,17 @@ class WorkflowJobTemplateLaunch(RetrieveAPIView):
data['extra_vars'] = extra_vars
modified_ask_mapping = models.WorkflowJobTemplate.get_ask_mapping()
modified_ask_mapping.pop('extra_vars')
- for field_name, ask_field_name in obj.get_ask_mapping().items():
+
+ for field, ask_field_name in modified_ask_mapping.items():
if not getattr(obj, ask_field_name):
- data.pop(field_name, None)
- elif field_name == 'inventory':
- data[field_name] = getattrd(obj, "%s.%s" % (field_name, 'id'), None)
+ data.pop(field, None)
+ elif isinstance(getattr(obj.__class__, field).field, ForeignKey):
+ data[field] = getattrd(obj, "%s.%s" % (field, 'id'), None)
+ elif isinstance(getattr(obj.__class__, field).field, ManyToManyField):
+ data[field] = [item.id for item in getattr(obj, field).all()]
else:
- data[field_name] = getattr(obj, field_name)
+ data[field] = getattr(obj, field)
+
return data
def post(self, request, *args, **kwargs):
diff --git a/awx/main/migrations/0167_jt_prompt_everything_on_launch.py b/awx/main/migrations/0167_jt_prompt_everything_on_launch.py
index e0257e7103..b03f42235e 100644
--- a/awx/main/migrations/0167_jt_prompt_everything_on_launch.py
+++ b/awx/main/migrations/0167_jt_prompt_everything_on_launch.py
@@ -107,4 +107,20 @@ class Migration(migrations.Migration):
blank=True, editable=False, related_name='joblaunchconfigs', through='main.JobLaunchConfigInstanceGroupMembership', to='main.InstanceGroup'
),
),
+ # added WFJT prompts
+ migrations.AddField(
+ model_name='workflowjobtemplate',
+ name='ask_labels_on_launch',
+ field=awx.main.fields.AskForField(blank=True, default=False),
+ ),
+ migrations.AddField(
+ model_name='workflowjobtemplate',
+ name='ask_skip_tags_on_launch',
+ field=awx.main.fields.AskForField(blank=True, default=False),
+ ),
+ migrations.AddField(
+ model_name='workflowjobtemplate',
+ name='ask_tags_on_launch',
+ field=awx.main.fields.AskForField(blank=True, default=False),
+ ),
]
diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py
index d71dbc078f..731a3eaf65 100644
--- a/awx/main/models/jobs.py
+++ b/awx/main/models/jobs.py
@@ -227,15 +227,6 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
blank=True,
default=False,
)
- ask_limit_on_launch = AskForField(
- blank=True,
- default=False,
- )
- ask_tags_on_launch = AskForField(blank=True, default=False, allows_field='job_tags')
- ask_skip_tags_on_launch = AskForField(
- blank=True,
- default=False,
- )
ask_job_type_on_launch = AskForField(
blank=True,
default=False,
@@ -244,20 +235,11 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
blank=True,
default=False,
)
- ask_inventory_on_launch = AskForField(
- blank=True,
- default=False,
- )
ask_credential_on_launch = AskForField(blank=True, default=False, allows_field='credentials')
- ask_scm_branch_on_launch = AskForField(blank=True, default=False, allows_field='scm_branch')
ask_execution_environment_on_launch = AskForField(
blank=True,
default=False,
)
- ask_labels_on_launch = AskForField(
- blank=True,
- default=False,
- )
ask_forks_on_launch = AskForField(
blank=True,
default=False,
diff --git a/awx/main/models/mixins.py b/awx/main/models/mixins.py
index 0e38d7288c..df10f0b29f 100644
--- a/awx/main/models/mixins.py
+++ b/awx/main/models/mixins.py
@@ -104,6 +104,33 @@ class SurveyJobTemplateMixin(models.Model):
default=False,
)
survey_spec = prevent_search(JSONBlob(default=dict, blank=True))
+
+ ask_inventory_on_launch = AskForField(
+ blank=True,
+ default=False,
+ )
+ ask_limit_on_launch = AskForField(
+ blank=True,
+ default=False,
+ )
+ ask_scm_branch_on_launch = AskForField(
+ blank=True,
+ default=False,
+ allows_field='scm_branch',
+ )
+ ask_labels_on_launch = AskForField(
+ blank=True,
+ default=False,
+ )
+ ask_tags_on_launch = AskForField(
+ blank=True,
+ default=False,
+ allows_field='job_tags',
+ )
+ ask_skip_tags_on_launch = AskForField(
+ blank=True,
+ default=False,
+ )
ask_variables_on_launch = AskForField(blank=True, default=False, allows_field='extra_vars')
def survey_password_variables(self):
diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py
index 21b4f4361b..b3d8bc4bee 100644
--- a/awx/main/models/unified_jobs.py
+++ b/awx/main/models/unified_jobs.py
@@ -422,6 +422,7 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, ExecutionEn
if unified_job.__class__ in activity_stream_registrar.models:
activity_stream_create(None, unified_job, True)
unified_job.log_lifecycle("created")
+
return unified_job
@classmethod
diff --git a/awx/main/models/workflow.py b/awx/main/models/workflow.py
index 4f52ade6b4..4417807cbd 100644
--- a/awx/main/models/workflow.py
+++ b/awx/main/models/workflow.py
@@ -29,7 +29,7 @@ from awx.main.models import prevent_search, accepts_json, UnifiedJobTemplate, Un
from awx.main.models.notifications import NotificationTemplate, JobNotificationMixin
from awx.main.models.base import CreatedModifiedModel, VarsDictProperty
from awx.main.models.rbac import ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_AUDITOR
-from awx.main.fields import ImplicitRoleField, AskForField, JSONBlob
+from awx.main.fields import ImplicitRoleField, JSONBlob
from awx.main.models.mixins import (
ResourceMixin,
SurveyJobTemplateMixin,
@@ -385,7 +385,7 @@ class WorkflowJobOptions(LaunchTimeConfigBase):
@classmethod
def _get_unified_job_field_names(cls):
r = set(f.name for f in WorkflowJobOptions._meta.fields) | set(
- ['name', 'description', 'organization', 'survey_passwords', 'labels', 'limit', 'scm_branch']
+ ['name', 'description', 'organization', 'survey_passwords', 'labels', 'limit', 'scm_branch', 'job_tags', 'skip_tags']
)
r.remove('char_prompts') # needed due to copying launch config to launch config
return r
@@ -425,26 +425,28 @@ class WorkflowJobOptions(LaunchTimeConfigBase):
class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTemplateMixin, ResourceMixin, RelatedJobsMixin, WebhookTemplateMixin):
SOFT_UNIQUE_TOGETHER = [('polymorphic_ctype', 'name', 'organization')]
- FIELDS_TO_PRESERVE_AT_COPY = ['labels', 'organization', 'instance_groups', 'workflow_job_template_nodes', 'credentials', 'survey_spec']
+ FIELDS_TO_PRESERVE_AT_COPY = [
+ 'labels',
+ 'organization',
+ 'instance_groups',
+ 'workflow_job_template_nodes',
+ 'credentials',
+ 'survey_spec',
+ 'skip_tags',
+ 'job_tags',
+ ]
class Meta:
app_label = 'main'
- ask_inventory_on_launch = AskForField(
+ notification_templates_approvals = models.ManyToManyField(
+ "NotificationTemplate",
blank=True,
- default=False,
+ related_name='%(class)s_notification_templates_for_approvals',
)
- ask_limit_on_launch = AskForField(
- blank=True,
- default=False,
+ admin_role = ImplicitRoleField(
+ parent_role=['singleton:' + ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, 'organization.workflow_admin_role'],
)
- ask_scm_branch_on_launch = AskForField(
- blank=True,
- default=False,
- )
- notification_templates_approvals = models.ManyToManyField("NotificationTemplate", blank=True, related_name='%(class)s_notification_templates_for_approvals')
-
- admin_role = ImplicitRoleField(parent_role=['singleton:' + ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, 'organization.workflow_admin_role'])
execute_role = ImplicitRoleField(
parent_role=[
'admin_role',
diff --git a/awx/main/tests/factories/fixtures.py b/awx/main/tests/factories/fixtures.py
index 200fa0f195..27556d6efe 100644
--- a/awx/main/tests/factories/fixtures.py
+++ b/awx/main/tests/factories/fixtures.py
@@ -210,7 +210,7 @@ def mk_workflow_job_template(name, extra_vars='', spec=None, organization=None,
if extra_vars:
extra_vars = json.dumps(extra_vars)
- wfjt = WorkflowJobTemplate(name=name, extra_vars=extra_vars, organization=organization, webhook_service=webhook_service)
+ wfjt = WorkflowJobTemplate.objects.create(name=name, extra_vars=extra_vars, organization=organization, webhook_service=webhook_service)
if spec:
wfjt.survey_spec = spec
diff --git a/awx/main/tests/functional/conftest.py b/awx/main/tests/functional/conftest.py
index 2e3563a2b6..4f8b6bc83c 100644
--- a/awx/main/tests/functional/conftest.py
+++ b/awx/main/tests/functional/conftest.py
@@ -706,7 +706,7 @@ def jt_linked(organization, project, inventory, machine_credential, credential,
@pytest.fixture
def workflow_job_template(organization):
- wjt = WorkflowJobTemplate(name='test-workflow_job_template', organization=organization)
+ wjt = WorkflowJobTemplate.objects.create(name='test-workflow_job_template', organization=organization)
wjt.save()
return wjt
diff --git a/awx/main/tests/functional/models/test_workflow.py b/awx/main/tests/functional/models/test_workflow.py
index d8fa495c6c..b6df98fe59 100644
--- a/awx/main/tests/functional/models/test_workflow.py
+++ b/awx/main/tests/functional/models/test_workflow.py
@@ -287,12 +287,25 @@ class TestWorkflowJobTemplatePrompts:
@pytest.fixture
def wfjt_prompts(self):
return WorkflowJobTemplate.objects.create(
- ask_inventory_on_launch=True, ask_variables_on_launch=True, ask_limit_on_launch=True, ask_scm_branch_on_launch=True
+ ask_variables_on_launch=True,
+ ask_inventory_on_launch=True,
+ ask_tags_on_launch=True,
+ ask_labels_on_launch=True,
+ ask_limit_on_launch=True,
+ ask_scm_branch_on_launch=True,
+ ask_skip_tags_on_launch=True,
)
@pytest.fixture
def prompts_data(self, inventory):
- return dict(inventory=inventory, extra_vars={'foo': 'bar'}, limit='webservers', scm_branch='release-3.3')
+ return dict(
+ inventory=inventory,
+ extra_vars={'foo': 'bar'},
+ limit='webservers',
+ scm_branch='release-3.3',
+ job_tags='foo',
+ skip_tags='bar',
+ )
def test_apply_workflow_job_prompts(self, workflow_job_template, wfjt_prompts, prompts_data, inventory):
# null or empty fields used
@@ -300,6 +313,9 @@ class TestWorkflowJobTemplatePrompts:
assert workflow_job.limit is None
assert workflow_job.inventory is None
assert workflow_job.scm_branch is None
+ assert workflow_job.job_tags is None
+ assert workflow_job.skip_tags is None
+ assert len(workflow_job.labels.all()) is 0
# fields from prompts used
workflow_job = workflow_job_template.create_unified_job(**prompts_data)
@@ -307,15 +323,21 @@ class TestWorkflowJobTemplatePrompts:
assert workflow_job.limit == 'webservers'
assert workflow_job.inventory == inventory
assert workflow_job.scm_branch == 'release-3.3'
+ assert workflow_job.job_tags == 'foo'
+ assert workflow_job.skip_tags == 'bar'
# non-null fields from WFJT used
workflow_job_template.inventory = inventory
workflow_job_template.limit = 'fooo'
workflow_job_template.scm_branch = 'bar'
+ workflow_job_template.job_tags = 'baz'
+ workflow_job_template.skip_tags = 'dinosaur'
workflow_job = workflow_job_template.create_unified_job()
assert workflow_job.limit == 'fooo'
assert workflow_job.inventory == inventory
assert workflow_job.scm_branch == 'bar'
+ assert workflow_job.job_tags == 'baz'
+ assert workflow_job.skip_tags == 'dinosaur'
@pytest.mark.django_db
def test_process_workflow_job_prompts(self, inventory, workflow_job_template, wfjt_prompts, prompts_data):
@@ -340,12 +362,19 @@ class TestWorkflowJobTemplatePrompts:
ask_limit_on_launch=True,
scm_branch='bar',
ask_scm_branch_on_launch=True,
+ job_tags='foo',
+ skip_tags='bar',
),
user=org_admin,
expect=201,
)
wfjt = WorkflowJobTemplate.objects.get(id=r.data['id'])
- assert wfjt.char_prompts == {'limit': 'foooo', 'scm_branch': 'bar'}
+ assert wfjt.char_prompts == {
+ 'limit': 'foooo',
+ 'scm_branch': 'bar',
+ 'job_tags': 'foo',
+ 'skip_tags': 'bar',
+ }
assert wfjt.ask_scm_branch_on_launch is True
assert wfjt.ask_limit_on_launch is True
@@ -355,6 +384,67 @@ class TestWorkflowJobTemplatePrompts:
assert r.data['limit'] == 'prompt_limit'
assert r.data['scm_branch'] == 'prompt_branch'
+ @pytest.mark.django_db
+ def test_set_all_ask_for_prompts_false_from_post(self, post, organization, inventory, org_admin):
+ '''
+ Tests default behaviour and values of ask_for_* fields on WFJT via POST
+ '''
+ r = post(
+ url=reverse('api:workflow_job_template_list'),
+ data=dict(
+ name='workflow that tests ask_for prompts',
+ organization=organization.id,
+ inventory=inventory.id,
+ job_tags='',
+ skip_tags='',
+ ),
+ user=org_admin,
+ expect=201,
+ )
+ wfjt = WorkflowJobTemplate.objects.get(id=r.data['id'])
+
+ assert wfjt.ask_inventory_on_launch is False
+ assert wfjt.ask_labels_on_launch is False
+ assert wfjt.ask_limit_on_launch is False
+ assert wfjt.ask_scm_branch_on_launch is False
+ assert wfjt.ask_skip_tags_on_launch is False
+ assert wfjt.ask_tags_on_launch is False
+ assert wfjt.ask_variables_on_launch is False
+
+ @pytest.mark.django_db
+ def test_set_all_ask_for_prompts_true_from_post(self, post, organization, inventory, org_admin):
+ '''
+ Tests behaviour and values of ask_for_* fields on WFJT via POST
+ '''
+ r = post(
+ url=reverse('api:workflow_job_template_list'),
+ data=dict(
+ name='workflow that tests ask_for prompts',
+ organization=organization.id,
+ inventory=inventory.id,
+ job_tags='',
+ skip_tags='',
+ ask_inventory_on_launch=True,
+ ask_labels_on_launch=True,
+ ask_limit_on_launch=True,
+ ask_scm_branch_on_launch=True,
+ ask_skip_tags_on_launch=True,
+ ask_tags_on_launch=True,
+ ask_variables_on_launch=True,
+ ),
+ user=org_admin,
+ expect=201,
+ )
+ wfjt = WorkflowJobTemplate.objects.get(id=r.data['id'])
+
+ assert wfjt.ask_inventory_on_launch is True
+ assert wfjt.ask_labels_on_launch is True
+ assert wfjt.ask_limit_on_launch is True
+ assert wfjt.ask_scm_branch_on_launch is True
+ assert wfjt.ask_skip_tags_on_launch is True
+ assert wfjt.ask_tags_on_launch is True
+ assert wfjt.ask_variables_on_launch is True
+
@pytest.mark.django_db
def test_workflow_ancestors(organization):
diff --git a/awx/main/tests/unit/api/serializers/test_workflow_serializers.py b/awx/main/tests/unit/api/serializers/test_workflow_serializers.py
index 526f06c4c9..0cbf6b7af0 100644
--- a/awx/main/tests/unit/api/serializers/test_workflow_serializers.py
+++ b/awx/main/tests/unit/api/serializers/test_workflow_serializers.py
@@ -11,6 +11,7 @@ from awx.api.serializers import (
from awx.main.models import Job, WorkflowJobTemplateNode, WorkflowJob, WorkflowJobNode, WorkflowJobTemplate, Project, Inventory, JobTemplate
+@pytest.mark.django_db
@mock.patch('awx.api.serializers.UnifiedJobTemplateSerializer.get_related', lambda x, y: {})
class TestWorkflowJobTemplateSerializerGetRelated:
@pytest.fixture
@@ -58,6 +59,7 @@ class TestWorkflowNodeBaseSerializerGetRelated:
assert 'unified_job_template' not in related
+@pytest.mark.django_db
@mock.patch('awx.api.serializers.BaseSerializer.get_related', lambda x, y: {})
class TestWorkflowJobTemplateNodeSerializerGetRelated:
@pytest.fixture
@@ -146,6 +148,7 @@ class TestWorkflowJobTemplateNodeSerializerCharPrompts:
assert WFJT_serializer.instance.limit == 'webservers'
+@pytest.mark.django_db
@mock.patch('awx.api.serializers.BaseSerializer.validate', lambda self, attrs: attrs)
class TestWorkflowJobTemplateNodeSerializerSurveyPasswords:
@pytest.fixture
@@ -162,7 +165,7 @@ class TestWorkflowJobTemplateNodeSerializerSurveyPasswords:
def test_set_survey_passwords_create(self, jt):
serializer = WorkflowJobTemplateNodeSerializer()
- wfjt = WorkflowJobTemplate(name='fake-wfjt')
+ wfjt = WorkflowJobTemplate.objects.create(name='fake-wfjt')
attrs = serializer.validate({'unified_job_template': jt, 'workflow_job_template': wfjt, 'extra_data': {'var1': 'secret_answer'}})
assert 'survey_passwords' in attrs
assert 'var1' in attrs['survey_passwords']
@@ -171,7 +174,7 @@ class TestWorkflowJobTemplateNodeSerializerSurveyPasswords:
def test_set_survey_passwords_modify(self, jt):
serializer = WorkflowJobTemplateNodeSerializer()
- wfjt = WorkflowJobTemplate(name='fake-wfjt')
+ wfjt = WorkflowJobTemplate.objects.create(name='fake-wfjt')
serializer.instance = WorkflowJobTemplateNode(workflow_job_template=wfjt, unified_job_template=jt)
attrs = serializer.validate({'unified_job_template': jt, 'workflow_job_template': wfjt, 'extra_data': {'var1': 'secret_answer'}})
assert 'survey_passwords' in attrs
@@ -181,7 +184,7 @@ class TestWorkflowJobTemplateNodeSerializerSurveyPasswords:
def test_use_db_answer(self, jt, mocker):
serializer = WorkflowJobTemplateNodeSerializer()
- wfjt = WorkflowJobTemplate(name='fake-wfjt')
+ wfjt = WorkflowJobTemplate.objects.create(name='fake-wfjt')
serializer.instance = WorkflowJobTemplateNode(workflow_job_template=wfjt, unified_job_template=jt, extra_data={'var1': '$encrypted$foooooo'})
with mocker.patch('awx.main.models.mixins.decrypt_value', return_value='foo'):
attrs = serializer.validate({'unified_job_template': jt, 'workflow_job_template': wfjt, 'extra_data': {'var1': '$encrypted$'}})
@@ -196,7 +199,7 @@ class TestWorkflowJobTemplateNodeSerializerSurveyPasswords:
with that particular var omitted so on launch time the default takes effect
"""
serializer = WorkflowJobTemplateNodeSerializer()
- wfjt = WorkflowJobTemplate(name='fake-wfjt')
+ wfjt = WorkflowJobTemplate.objects.create(name='fake-wfjt')
jt.survey_spec['spec'][0]['default'] = '$encrypted$bar'
attrs = serializer.validate({'unified_job_template': jt, 'workflow_job_template': wfjt, 'extra_data': {'var1': '$encrypted$'}})
assert 'survey_passwords' in attrs
diff --git a/awx/main/tests/unit/models/test_survey_models.py b/awx/main/tests/unit/models/test_survey_models.py
index 9ec5673cd8..57058930ea 100644
--- a/awx/main/tests/unit/models/test_survey_models.py
+++ b/awx/main/tests/unit/models/test_survey_models.py
@@ -259,13 +259,14 @@ def test_survey_encryption_defaults(survey_spec_factory, question_type, default,
@pytest.mark.survey
+@pytest.mark.django_db
class TestWorkflowSurveys:
def test_update_kwargs_survey_defaults(self, survey_spec_factory):
"Assure that the survey default over-rides a JT variable"
spec = survey_spec_factory('var1')
spec['spec'][0]['default'] = 3
spec['spec'][0]['required'] = False
- wfjt = WorkflowJobTemplate(name="test-wfjt", survey_spec=spec, survey_enabled=True, extra_vars="var1: 5")
+ wfjt = WorkflowJobTemplate.objects.create(name="test-wfjt", survey_spec=spec, survey_enabled=True, extra_vars="var1: 5")
updated_extra_vars = wfjt._update_unified_job_kwargs({}, {})
assert 'extra_vars' in updated_extra_vars
assert json.loads(updated_extra_vars['extra_vars'])['var1'] == 3
@@ -277,7 +278,7 @@ class TestWorkflowSurveys:
spec['spec'][0]['required'] = False
spec['spec'][1]['required'] = True
spec['spec'][2]['required'] = False
- wfjt = WorkflowJobTemplate(name="test-wfjt", survey_spec=spec, survey_enabled=True, extra_vars="question2: hiworld")
+ wfjt = WorkflowJobTemplate.objects.create(name="test-wfjt", survey_spec=spec, survey_enabled=True, extra_vars="question2: hiworld")
assert wfjt.variables_needed_to_start == ['question2']
assert not wfjt.can_start_without_user_input()
@@ -311,6 +312,6 @@ class TestExtraVarsNoPrompt:
self.process_vars_and_assert(jt, provided_vars, valid)
def test_wfjt_extra_vars_counting(self, provided_vars, valid):
- wfjt = WorkflowJobTemplate(name='foo', extra_vars={'tmpl_var': 'bar'})
+ wfjt = WorkflowJobTemplate.objects.create(name='foo', extra_vars={'tmpl_var': 'bar'})
prompted_fields, ignored_fields, errors = wfjt._accept_or_ignore_job_kwargs(extra_vars=provided_vars)
self.process_vars_and_assert(wfjt, provided_vars, valid)
diff --git a/awx/main/tests/unit/models/test_workflow_unit.py b/awx/main/tests/unit/models/test_workflow_unit.py
index f8bb1e9c84..65190f92a3 100644
--- a/awx/main/tests/unit/models/test_workflow_unit.py
+++ b/awx/main/tests/unit/models/test_workflow_unit.py
@@ -94,7 +94,7 @@ def workflow_job_unit():
@pytest.fixture
def workflow_job_template_unit():
- return WorkflowJobTemplate(name='workflow')
+ return WorkflowJobTemplate.objects.create(name='workflow')
@pytest.fixture
@@ -151,6 +151,7 @@ def test_node_getter_and_setters():
assert node.job_type == 'check'
+@pytest.mark.django_db
class TestWorkflowJobCreate:
def test_create_no_prompts(self, wfjt_node_no_prompts, workflow_job_unit, mocker):
mock_create = mocker.MagicMock()
@@ -183,6 +184,7 @@ class TestWorkflowJobCreate:
)
+@pytest.mark.django_db
@mock.patch('awx.main.models.workflow.WorkflowNodeBase.get_parent_nodes', lambda self: [])
class TestWorkflowJobNodeJobKWARGS:
"""
@@ -231,4 +233,12 @@ class TestWorkflowJobNodeJobKWARGS:
def test_get_ask_mapping_integrity():
- assert list(WorkflowJobTemplate.get_ask_mapping().keys()) == ['extra_vars', 'inventory', 'limit', 'scm_branch']
+ assert list(WorkflowJobTemplate.get_ask_mapping().keys()) == [
+ 'inventory',
+ 'limit',
+ 'scm_branch',
+ 'labels',
+ 'job_tags',
+ 'skip_tags',
+ 'extra_vars',
+ ]
diff --git a/awx/main/tests/unit/test_access.py b/awx/main/tests/unit/test_access.py
index 547af7b42c..0059cb4984 100644
--- a/awx/main/tests/unit/test_access.py
+++ b/awx/main/tests/unit/test_access.py
@@ -196,6 +196,7 @@ def test_jt_can_add_bad_data(user_unit):
assert not access.can_add({'asdf': 'asdf'})
+@pytest.mark.django_db
class TestWorkflowAccessMethods:
@pytest.fixture
def workflow(self, workflow_job_template_factory):
diff --git a/awx/ui/src/components/LaunchPrompt/LaunchPrompt.test.js b/awx/ui/src/components/LaunchPrompt/LaunchPrompt.test.js
index 27263d479c..d4e3cee1ef 100644
--- a/awx/ui/src/components/LaunchPrompt/LaunchPrompt.test.js
+++ b/awx/ui/src/components/LaunchPrompt/LaunchPrompt.test.js
@@ -16,8 +16,12 @@ import CredentialsStep from './steps/CredentialsStep';
import CredentialPasswordsStep from './steps/CredentialPasswordsStep';
import OtherPromptsStep from './steps/OtherPromptsStep';
import PreviewStep from './steps/PreviewStep';
+import executionEnvironmentHelpTextStrings from 'screens/ExecutionEnvironment/shared/ExecutionEnvironment.helptext';
+import { ExecutionEnvironment } from 'types';
+import ExecutionEnvironmentStep from './steps/ExecutionEnvironmentStep';
jest.mock('../../api/models/Inventories');
+jest.mock('../../api/models/ExecutionEnvironments');
jest.mock('../../api/models/CredentialTypes');
jest.mock('../../api/models/Credentials');
jest.mock('../../api/models/JobTemplates');
@@ -150,13 +154,14 @@ describe('LaunchPrompt', () => {
const wizard = await waitForElement(wrapper, 'Wizard');
const steps = wizard.prop('steps');
- expect(steps).toHaveLength(6);
+ expect(steps).toHaveLength(7);
expect(steps[0].name.props.children).toEqual('Inventory');
expect(steps[1].name.props.children).toEqual('Credentials');
expect(steps[2].name.props.children).toEqual('Credential passwords');
- expect(steps[3].name.props.children).toEqual('Other prompts');
- expect(steps[4].name.props.children).toEqual('Survey');
- expect(steps[5].name.props.children).toEqual('Preview');
+ expect(steps[3].name.props.children).toEqual('Execution Environment');
+ expect(steps[4].name.props.children).toEqual('Other prompts');
+ expect(steps[5].name.props.children).toEqual('Survey');
+ expect(steps[6].name.props.children).toEqual('Preview');
expect(wizard.find('WizardHeader').prop('title')).toBe('Launch | Foobar');
expect(wizard.find('WizardHeader').prop('description')).toBe(
'Foo Description'
diff --git a/awx/ui/src/screens/Template/JobTemplateAdd/JobTemplateAdd.test.js b/awx/ui/src/screens/Template/JobTemplateAdd/JobTemplateAdd.test.js
index 7c119a522d..9fffccbb33 100644
--- a/awx/ui/src/screens/Template/JobTemplateAdd/JobTemplateAdd.test.js
+++ b/awx/ui/src/screens/Template/JobTemplateAdd/JobTemplateAdd.test.js
@@ -22,12 +22,18 @@ const jobTemplateData = {
allow_simultaneous: false,
ask_credential_on_launch: false,
ask_diff_mode_on_launch: false,
+ ask_execution_environment_on_launch: false,
+ ask_forks_on_launch: false,
+ ask_instance_groups_on_launch: false,
ask_inventory_on_launch: false,
+ ask_job_slice_count_on_launch: false,
ask_job_type_on_launch: false,
+ ask_labels_on_launch: false,
ask_limit_on_launch: false,
ask_scm_branch_on_launch: false,
ask_skip_tags_on_launch: false,
ask_tags_on_launch: false,
+ ask_timeout_on_launch: false,
ask_variables_on_launch: false,
ask_verbosity_on_launch: false,
ask_execution_environment_on_launch: false,
diff --git a/awx/ui/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.js b/awx/ui/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.js
index 500143973f..2ada0105d5 100644
--- a/awx/ui/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.js
+++ b/awx/ui/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.js
@@ -35,13 +35,18 @@ const mockJobTemplate = {
allow_simultaneous: false,
ask_scm_branch_on_launch: false,
ask_diff_mode_on_launch: false,
+ ask_execution_environment_on_launch: false,
+ ask_forks_on_launch: false,
+ ask_instance_groups_on_launch: false,
ask_variables_on_launch: false,
ask_limit_on_launch: false,
ask_tags_on_launch: false,
ask_skip_tags_on_launch: false,
ask_job_type_on_launch: false,
+ ask_labels_on_launch: false,
ask_verbosity_on_launch: false,
ask_inventory_on_launch: false,
+ ask_job_slice_count_on_launch: false,
ask_credential_on_launch: false,
ask_execution_environment_on_launch: false,
ask_forks_on_launch: false,
diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.js b/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.js
index a6cb0e1969..8a0c55cd06 100644
--- a/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.js
+++ b/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.js
@@ -82,7 +82,7 @@ describe('
{t`Concurrent jobs: If enabled, simultaneous runs of this workflow job template will be allowed.`}
diff --git a/awx/ui/src/screens/Template/shared/WorkflowJobTemplateForm.js b/awx/ui/src/screens/Template/shared/WorkflowJobTemplateForm.js index 1b9f1f9511..30e9ac8668 100644 --- a/awx/ui/src/screens/Template/shared/WorkflowJobTemplateForm.js +++ b/awx/ui/src/screens/Template/shared/WorkflowJobTemplateForm.js @@ -27,6 +27,7 @@ import CheckboxField from 'components/FormField/CheckboxField'; import Popover from 'components/Popover'; import { WorkFlowJobTemplate } from 'types'; import LabelSelect from 'components/LabelSelect'; +import { TagMultiSelect } from 'components/MultiSelect'; import WebhookSubForm from './WebhookSubForm'; import getHelpText from './WorkflowJobTemplate.helptext'; @@ -59,6 +60,8 @@ function WorkflowJobTemplateForm({ const [, webhookKeyMeta, webhookKeyHelpers] = useField('webhook_key'); const [, webhookCredentialMeta, webhookCredentialHelpers] = useField('webhook_credential'); + const [skipTagsField, , skipTagsHelpers] = useField('skip_tags'); + const [jobTagsField, , jobTagsHelpers] = useField('job_tags'); useEffect(() => { if (enableWebhooks) { @@ -167,7 +170,6 @@ function WorkflowJobTemplateForm({ }} /> -