AAP-41742: Fix workflow node update failing when JT has unprompted labels

PATCH extra_data on a workflow node fails with
{"labels":["Field is not configured to prompt on launch."]}
when the node has labels associated but the JT has
ask_labels_on_launch=False.

The serializer was passing all persisted M2M state from prompts_dict()
to _accept_or_ignore_job_kwargs() on every PATCH, re-validating
unchanged fields. Fix scopes validation to only the fields in the
request; full re-validation still occurs when unified_job_template
is being changed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dirk Julich
2026-04-23 18:17:01 +02:00
parent 99ac0d39dc
commit 947d1c55eb
2 changed files with 44 additions and 2 deletions

View File

@@ -4143,8 +4143,24 @@ class LaunchConfigurationBaseSerializer(BaseSerializer):
# Build unsaved version of this config, use it to detect prompts errors
mock_obj = self._build_mock_obj(attrs)
if set(list(ujt.get_ask_mapping().keys()) + ['extra_data']) & set(attrs.keys()):
accepted, rejected, errors = ujt._accept_or_ignore_job_kwargs(_exclude_errors=self.exclude_errors, **mock_obj.prompts_dict())
ask_mapping_keys = set(ujt.get_ask_mapping().keys())
requested_prompt_fields = set(attrs.keys()) & ask_mapping_keys
if 'extra_data' in attrs:
requested_prompt_fields.add('extra_vars')
# prompts_dict() pulls persisted M2M state (labels, credentials,
# instance_groups) via the instance pk. Only re-validate the full prompt
# state when the caller is switching the underlying template; otherwise
# restrict validation to the fields the request explicitly provided.
if 'unified_job_template' in attrs:
prompts_to_validate = mock_obj.prompts_dict()
elif requested_prompt_fields:
prompts_to_validate = {k: v for k, v in mock_obj.prompts_dict().items() if k in requested_prompt_fields}
else:
prompts_to_validate = None
if prompts_to_validate is not None:
accepted, rejected, errors = ujt._accept_or_ignore_job_kwargs(_exclude_errors=self.exclude_errors, **prompts_to_validate)
else:
# Only perform validation of prompts if prompts fields are provided
errors = {}

View File

@@ -13,6 +13,7 @@ from awx.main.models.workflow import (
WorkflowJobTemplateNode,
)
from awx.main.models.credential import Credential
from awx.main.models.label import Label
from awx.main.scheduler import TaskManager, WorkflowManager, DependencyManager
# Django
@@ -51,6 +52,31 @@ def test_node_accepts_prompted_fields(inventory, project, workflow_job_template,
post(url, {'unified_job_template': job_template.pk, 'limit': 'webservers'}, user=admin_user, expect=201)
@pytest.mark.django_db
def test_node_extra_data_patch_with_unprompted_labels(inventory, project, organization, workflow_job_template, patch, admin_user):
"""AAP-41742: PATCH extra_data on a workflow node should succeed even when
the node has labels associated but the JT has ask_labels_on_launch=False."""
jt = JobTemplate.objects.create(
inventory=inventory,
project=project,
playbook='helloworld.yml',
ask_variables_on_launch=True,
ask_labels_on_launch=False,
)
label = Label.objects.create(name='repro-label', organization=organization)
node = WorkflowJobTemplateNode.objects.create(
workflow_job_template=workflow_job_template,
unified_job_template=jt,
extra_data={'foo': 'bar'},
)
node.labels.add(label)
url = reverse('api:workflow_job_template_node_detail', kwargs={'pk': node.pk})
r = patch(url, {'extra_data': {'foo': 'edited'}}, user=admin_user, expect=200)
assert r.data['extra_data'] == {'foo': 'edited'}
@pytest.mark.django_db
@pytest.mark.parametrize(
"field_name, field_value",