mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 17:37:37 -02:30
Bug fixes from integration ran on launchtime branch
Make error message for muti-vault validation more consistent with historical message
This commit is contained in:
@@ -3106,15 +3106,15 @@ class WorkflowJobTemplateNodeSerializer(LaunchConfigurationBaseSerializer):
|
|||||||
})
|
})
|
||||||
if 'unified_job_template' in attrs:
|
if 'unified_job_template' in attrs:
|
||||||
ujt_obj = attrs['unified_job_template']
|
ujt_obj = attrs['unified_job_template']
|
||||||
elif self.instance:
|
ujt_obj = None
|
||||||
|
if self.instance:
|
||||||
ujt_obj = self.instance.unified_job_template
|
ujt_obj = self.instance.unified_job_template
|
||||||
else:
|
|
||||||
raise serializers.ValidationError({
|
|
||||||
"unified_job_template": _("Node needs to have a template attached.")})
|
|
||||||
if isinstance(ujt_obj, (WorkflowJobTemplate, SystemJobTemplate)):
|
if isinstance(ujt_obj, (WorkflowJobTemplate, SystemJobTemplate)):
|
||||||
raise serializers.ValidationError({
|
raise serializers.ValidationError({
|
||||||
"unified_job_template": _("Cannot nest a %s inside a WorkflowJobTemplate") % ujt_obj.__class__.__name__})
|
"unified_job_template": _("Cannot nest a %s inside a WorkflowJobTemplate") % ujt_obj.__class__.__name__})
|
||||||
attrs = super(WorkflowJobTemplateNodeSerializer, self).validate(attrs)
|
attrs = super(WorkflowJobTemplateNodeSerializer, self).validate(attrs)
|
||||||
|
if ujt_obj is None:
|
||||||
|
ujt_obj = attrs.get('unified_job_template')
|
||||||
accepted, rejected, errors = ujt_obj._accept_or_ignore_job_kwargs(**self._build_mock_obj(attrs).prompts_dict())
|
accepted, rejected, errors = ujt_obj._accept_or_ignore_job_kwargs(**self._build_mock_obj(attrs).prompts_dict())
|
||||||
# Do not raise survey validation errors
|
# Do not raise survey validation errors
|
||||||
errors.pop('variables_needed_to_start', None)
|
errors.pop('variables_needed_to_start', None)
|
||||||
@@ -3703,8 +3703,10 @@ class ScheduleSerializer(LaunchConfigurationBaseSerializer):
|
|||||||
elif self.instance:
|
elif self.instance:
|
||||||
ujt = self.instance.unified_job_template
|
ujt = self.instance.unified_job_template
|
||||||
accepted, rejected, errors = ujt.accept_or_ignore_variables(extra_data)
|
accepted, rejected, errors = ujt.accept_or_ignore_variables(extra_data)
|
||||||
|
if 'extra_vars' in errors:
|
||||||
|
errors['extra_data'] = errors.pop('extra_vars')
|
||||||
if errors:
|
if errors:
|
||||||
raise serializers.ValidationError({'extra_data': errors['extra_vars']})
|
raise serializers.ValidationError(errors)
|
||||||
return super(ScheduleSerializer, self).validate(attrs)
|
return super(ScheduleSerializer, self).validate(attrs)
|
||||||
|
|
||||||
# We reject rrules if:
|
# We reject rrules if:
|
||||||
|
|||||||
@@ -2820,7 +2820,7 @@ class JobTemplateLaunch(RetrieveAPIView):
|
|||||||
prompted_value = modern_data.pop(key)
|
prompted_value = modern_data.pop(key)
|
||||||
|
|
||||||
# add the deprecated credential specified in the request
|
# add the deprecated credential specified in the request
|
||||||
if not isinstance(prompted_value, Iterable):
|
if not isinstance(prompted_value, Iterable) or isinstance(prompted_value, basestring):
|
||||||
prompted_value = [prompted_value]
|
prompted_value = [prompted_value]
|
||||||
|
|
||||||
# If user gave extra_credentials, special case to use exactly
|
# If user gave extra_credentials, special case to use exactly
|
||||||
@@ -3063,7 +3063,7 @@ class JobTemplateCredentialsList(SubListCreateAttachDetachAPIView):
|
|||||||
|
|
||||||
def is_valid_relation(self, parent, sub, created=False):
|
def is_valid_relation(self, parent, sub, created=False):
|
||||||
if sub.unique_hash() in [cred.unique_hash() for cred in parent.credentials.all()]:
|
if sub.unique_hash() in [cred.unique_hash() for cred in parent.credentials.all()]:
|
||||||
return {"msg": _("Cannot assign multiple {credential_type} credentials.".format(
|
return {"error": _("Cannot assign multiple {credential_type} credentials.".format(
|
||||||
credential_type=sub.unique_hash(display=True)))}
|
credential_type=sub.unique_hash(display=True)))}
|
||||||
|
|
||||||
return super(JobTemplateCredentialsList, self).is_valid_relation(parent, sub, created)
|
return super(JobTemplateCredentialsList, self).is_valid_relation(parent, sub, created)
|
||||||
|
|||||||
@@ -381,7 +381,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
|||||||
that can be used to evaluate exclusivity
|
that can be used to evaluate exclusivity
|
||||||
'''
|
'''
|
||||||
if display:
|
if display:
|
||||||
type_alias = self.kind
|
type_alias = self.credential_type.name
|
||||||
else:
|
else:
|
||||||
type_alias = self.credential_type_id
|
type_alias = self.credential_type_id
|
||||||
if self.kind == 'vault' and self.inputs.get('vault_id', None):
|
if self.kind == 'vault' and self.inputs.get('vault_id', None):
|
||||||
|
|||||||
@@ -876,7 +876,7 @@ class LaunchTimeConfig(BaseModel):
|
|||||||
data[prompt_name] = self.display_extra_data()
|
data[prompt_name] = self.display_extra_data()
|
||||||
else:
|
else:
|
||||||
data[prompt_name] = self.extra_data
|
data[prompt_name] = self.extra_data
|
||||||
if self.survey_passwords:
|
if self.survey_passwords and not display:
|
||||||
data['survey_passwords'] = self.survey_passwords
|
data['survey_passwords'] = self.survey_passwords
|
||||||
else:
|
else:
|
||||||
prompt_val = getattr(self, prompt_name)
|
prompt_val = getattr(self, prompt_name)
|
||||||
@@ -889,11 +889,11 @@ class LaunchTimeConfig(BaseModel):
|
|||||||
Hides fields marked as passwords in survey.
|
Hides fields marked as passwords in survey.
|
||||||
'''
|
'''
|
||||||
if self.survey_passwords:
|
if self.survey_passwords:
|
||||||
extra_data = json.loads(self.extra_data)
|
extra_data = parse_yaml_or_json(self.extra_data)
|
||||||
for key, value in self.survey_passwords.items():
|
for key, value in self.survey_passwords.items():
|
||||||
if key in extra_data:
|
if key in extra_data:
|
||||||
extra_data[key] = value
|
extra_data[key] = value
|
||||||
return json.dumps(extra_data)
|
return extra_data
|
||||||
else:
|
else:
|
||||||
return self.extra_data
|
return self.extra_data
|
||||||
|
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ class SurveyJobTemplateMixin(models.Model):
|
|||||||
survey_errors += element_errors
|
survey_errors += element_errors
|
||||||
if key is not None and key in extra_vars:
|
if key is not None and key in extra_vars:
|
||||||
rejected[key] = extra_vars.pop(key)
|
rejected[key] = extra_vars.pop(key)
|
||||||
else:
|
elif key in extra_vars:
|
||||||
accepted[key] = extra_vars.pop(key)
|
accepted[key] = extra_vars.pop(key)
|
||||||
if survey_errors:
|
if survey_errors:
|
||||||
errors['variables_needed_to_start'] = survey_errors
|
errors['variables_needed_to_start'] = survey_errors
|
||||||
|
|||||||
@@ -99,10 +99,11 @@ class Schedule(CommonModel, LaunchTimeConfig):
|
|||||||
|
|
||||||
def get_job_kwargs(self):
|
def get_job_kwargs(self):
|
||||||
config_data = self.prompts_dict()
|
config_data = self.prompts_dict()
|
||||||
prompts, rejected, errors = self.unified_job_template._accept_or_ignore_job_kwargs(**config_data)
|
job_kwargs, rejected, errors = self.unified_job_template._accept_or_ignore_job_kwargs(**config_data)
|
||||||
if errors:
|
if errors:
|
||||||
logger.info('Errors creating scheduled job: {}'.format(errors))
|
logger.info('Errors creating scheduled job: {}'.format(errors))
|
||||||
return prompts
|
job_kwargs['_eager_fields'] = {'launch_type': 'scheduled', 'schedule': self}
|
||||||
|
return job_kwargs
|
||||||
|
|
||||||
def update_computed_fields(self):
|
def update_computed_fields(self):
|
||||||
future_rs = dateutil.rrule.rrulestr(self.rrule, forceset=True)
|
future_rs = dateutil.rrule.rrulestr(self.rrule, forceset=True)
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio
|
|||||||
'''
|
'''
|
||||||
Create a new unified job based on this unified job template.
|
Create a new unified job based on this unified job template.
|
||||||
'''
|
'''
|
||||||
original_passwords = kwargs.pop('survey_passwords', {})
|
new_job_passwords = kwargs.pop('survey_passwords', {})
|
||||||
eager_fields = kwargs.pop('_eager_fields', None)
|
eager_fields = kwargs.pop('_eager_fields', None)
|
||||||
unified_job_class = self._get_unified_job_class()
|
unified_job_class = self._get_unified_job_class()
|
||||||
fields = self._get_unified_job_field_names()
|
fields = self._get_unified_job_field_names()
|
||||||
@@ -363,12 +363,11 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio
|
|||||||
|
|
||||||
# For JobTemplate-based jobs with surveys, add passwords to list for perma-redaction
|
# For JobTemplate-based jobs with surveys, add passwords to list for perma-redaction
|
||||||
if hasattr(self, 'survey_spec') and getattr(self, 'survey_enabled', False):
|
if hasattr(self, 'survey_spec') and getattr(self, 'survey_enabled', False):
|
||||||
password_list = self.survey_password_variables()
|
for password in self.survey_password_variables():
|
||||||
hide_password_dict = getattr(unified_job, 'survey_passwords', {})
|
new_job_passwords[password] = REPLACE_STR
|
||||||
hide_password_dict.update(original_passwords)
|
if new_job_passwords:
|
||||||
for password in password_list:
|
unified_job.survey_passwords = new_job_passwords
|
||||||
hide_password_dict[password] = REPLACE_STR
|
kwargs['survey_passwords'] = new_job_passwords # saved in config object for relaunch
|
||||||
unified_job.survey_passwords = hide_password_dict
|
|
||||||
|
|
||||||
unified_job.save()
|
unified_job.save()
|
||||||
|
|
||||||
@@ -430,7 +429,10 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio
|
|||||||
'''
|
'''
|
||||||
Override in subclass if template accepts _any_ prompted params
|
Override in subclass if template accepts _any_ prompted params
|
||||||
'''
|
'''
|
||||||
return ({}, kwargs, {"all": ["Fields {} are not allowed on launch.".format(kwargs.keys())]})
|
errors = {}
|
||||||
|
if kwargs:
|
||||||
|
errors['all'] = [_("Fields {} are not allowed on launch.").format(kwargs.keys())]
|
||||||
|
return ({}, kwargs, errors)
|
||||||
|
|
||||||
def accept_or_ignore_variables(self, data, errors=None):
|
def accept_or_ignore_variables(self, data, errors=None):
|
||||||
'''
|
'''
|
||||||
@@ -859,8 +861,11 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
|
|||||||
return None
|
return None
|
||||||
JobLaunchConfig = self._meta.get_field('launch_config').related_model
|
JobLaunchConfig = self._meta.get_field('launch_config').related_model
|
||||||
config = JobLaunchConfig(job=self)
|
config = JobLaunchConfig(job=self)
|
||||||
|
valid_fields = self.unified_job_template.get_ask_mapping().keys()
|
||||||
|
if hasattr(self, 'extra_vars'):
|
||||||
|
valid_fields.extend(['survey_passwords', 'extra_vars'])
|
||||||
for field_name, value in kwargs.items():
|
for field_name, value in kwargs.items():
|
||||||
if (field_name not in self.unified_job_template.get_ask_mapping() and field_name != 'survey_passwords'):
|
if field_name not in valid_fields:
|
||||||
raise Exception('Unrecognized launch config field {}.'.format(field_name))
|
raise Exception('Unrecognized launch config field {}.'.format(field_name))
|
||||||
if field_name == 'credentials':
|
if field_name == 'credentials':
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ class WorkflowJobNode(WorkflowNodeBase):
|
|||||||
if extra_vars:
|
if extra_vars:
|
||||||
data['extra_vars'] = extra_vars
|
data['extra_vars'] = extra_vars
|
||||||
# ensure that unified jobs created by WorkflowJobs are marked
|
# ensure that unified jobs created by WorkflowJobs are marked
|
||||||
data['launch_type'] = 'workflow'
|
data['_eager_fields'] = {'launch_type': 'workflow'}
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@@ -366,7 +366,7 @@ class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTempl
|
|||||||
|
|
||||||
# WFJTs do not behave like JTs, it can not accept inventory, credential, etc.
|
# WFJTs do not behave like JTs, it can not accept inventory, credential, etc.
|
||||||
bad_kwargs = kwargs.copy()
|
bad_kwargs = kwargs.copy()
|
||||||
bad_kwargs.pop('extra_vars')
|
bad_kwargs.pop('extra_vars', None)
|
||||||
if bad_kwargs:
|
if bad_kwargs:
|
||||||
rejected_fields.update(bad_kwargs)
|
rejected_fields.update(bad_kwargs)
|
||||||
for field in bad_kwargs:
|
for field in bad_kwargs:
|
||||||
|
|||||||
@@ -307,14 +307,8 @@ def awx_periodic_scheduler(self):
|
|||||||
logger.warn("Cache timeout is in the future, bypassing schedule for template %s" % str(template.id))
|
logger.warn("Cache timeout is in the future, bypassing schedule for template %s" % str(template.id))
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
prompts = schedule.get_job_kwargs()
|
job_kwargs = schedule.get_job_kwargs()
|
||||||
new_unified_job = schedule.unified_job_template.create_unified_job(
|
new_unified_job = schedule.unified_job_template.create_unified_job(**job_kwargs)
|
||||||
_eager_fields=dict(
|
|
||||||
launch_type='scheduled',
|
|
||||||
schedule=schedule
|
|
||||||
),
|
|
||||||
**prompts
|
|
||||||
)
|
|
||||||
can_start = new_unified_job.signal_start()
|
can_start = new_unified_job.signal_start()
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('Error spawning scheduled job.')
|
logger.exception('Error spawning scheduled job.')
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ def test_prevent_multiple_machine_creds(get, post, job_template, admin, machine_
|
|||||||
assert get(url, admin).data['count'] == 1
|
assert get(url, admin).data['count'] == 1
|
||||||
|
|
||||||
resp = post(url, _new_cred('Second Cred'), admin, expect=400)
|
resp = post(url, _new_cred('Second Cred'), admin, expect=400)
|
||||||
assert 'Cannot assign multiple ssh credentials.' in resp.content
|
assert 'Cannot assign multiple Machine credentials.' in resp.content
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@@ -167,7 +167,7 @@ def test_extra_credentials_unique_by_kind(get, post, job_template, admin,
|
|||||||
assert get(url, admin).data['count'] == 1
|
assert get(url, admin).data['count'] == 1
|
||||||
|
|
||||||
resp = post(url, _new_cred('Second Cred'), admin, expect=400)
|
resp = post(url, _new_cred('Second Cred'), admin, expect=400)
|
||||||
assert 'Cannot assign multiple aws credentials.' in resp.content
|
assert 'Cannot assign multiple Amazon Web Services credentials.' in resp.content
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ class TestWorkflowJobNodeJobKWARGS:
|
|||||||
Tests for building the keyword arguments that go into creating and
|
Tests for building the keyword arguments that go into creating and
|
||||||
launching a new job that corresponds to a workflow node.
|
launching a new job that corresponds to a workflow node.
|
||||||
"""
|
"""
|
||||||
kwargs_base = {'launch_type': 'workflow'}
|
kwargs_base = {'_eager_fields': {'launch_type': 'workflow'}}
|
||||||
|
|
||||||
def test_null_kwargs(self, job_node_no_prompts):
|
def test_null_kwargs(self, job_node_no_prompts):
|
||||||
assert job_node_no_prompts.get_job_kwargs() == self.kwargs_base
|
assert job_node_no_prompts.get_job_kwargs() == self.kwargs_base
|
||||||
|
|||||||
Reference in New Issue
Block a user