mirror of
https://github.com/ansible/awx.git
synced 2026-06-10 01:16:18 -02:30
Expose JOB_VARIABLE_PREFIXES as a configurable setting (#16452)
* INCLUDE_AWX_VAR_PREFIX to USE_TOWER_VAR_PREFIX boolean toggle replace the include legacy prefix boolean with a tower-or-awx toggle USE_TOWER_VAR_PREFIX=True (default) emits only tower_ prefixed variables, false emits only awx_ (deprecated) * Clean up dead constant and cache get_job_variable_prefixes() calls * Revise tests to reflect new behavior * Fix fragile fallback test to actually exercise getattr default * Fix mock target for settings fallback test
This commit is contained in:
@@ -17,7 +17,7 @@ from awx.api import serializers
|
||||
from awx.api.generics import APIView, GenericAPIView
|
||||
from awx.api.permissions import WebhookKeyPermission
|
||||
from awx.main.models import Job, JobTemplate, WorkflowJob, WorkflowJobTemplate
|
||||
from awx.main.constants import JOB_VARIABLE_PREFIXES
|
||||
from awx.main.utils.common import get_job_variable_prefixes
|
||||
|
||||
logger = logging.getLogger('awx.api.views.webhooks')
|
||||
|
||||
@@ -166,7 +166,7 @@ class WebhookReceiverBase(APIView):
|
||||
'extra_vars': {},
|
||||
}
|
||||
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
kwargs['extra_vars']['{}_webhook_event_type'.format(name)] = event_type
|
||||
kwargs['extra_vars']['{}_webhook_event_guid'.format(name)] = event_guid
|
||||
kwargs['extra_vars']['{}_webhook_event_ref'.format(name)] = event_ref
|
||||
|
||||
@@ -325,6 +325,22 @@ register(
|
||||
category_slug='jobs',
|
||||
)
|
||||
|
||||
register(
|
||||
'INCLUDE_DEPRECATED_AWX_VAR_PREFIX',
|
||||
field_class=fields.BooleanField,
|
||||
default=True,
|
||||
label=_('Include Deprecated AWX Variable Prefix'),
|
||||
help_text=_(
|
||||
'When enabled (default), auto-generated job variables are emitted '
|
||||
'with both the tower_ prefix and the deprecated awx_ prefix for '
|
||||
'backward compatibility. Disable to emit only tower_ prefixed '
|
||||
'variables and eliminate duplicates. The awx_ prefix is deprecated '
|
||||
'and this setting will default to False in a future release.'
|
||||
),
|
||||
category=_('Jobs'),
|
||||
category_slug='jobs',
|
||||
)
|
||||
|
||||
register(
|
||||
'AWX_ISOLATION_BASE_PATH',
|
||||
field_class=fields.CharField,
|
||||
|
||||
@@ -100,10 +100,6 @@ MAX_ISOLATED_PATH_COLON_DELIMITER = 2
|
||||
|
||||
SURVEY_TYPE_MAPPING = {'text': str, 'textarea': str, 'password': str, 'multiplechoice': str, 'multiselect': str, 'integer': int, 'float': (float, int)}
|
||||
|
||||
JOB_VARIABLE_PREFIXES = [
|
||||
'awx',
|
||||
'tower',
|
||||
]
|
||||
|
||||
# Note, the \u001b[... are ansi color codes. We don't currenly import any of the python modules which define the codes.
|
||||
# Importing a library just for this message seemed like overkill
|
||||
|
||||
@@ -52,7 +52,7 @@ from awx.main.models.mixins import (
|
||||
WebhookTemplateMixin,
|
||||
OpaQueryPathMixin,
|
||||
)
|
||||
from awx.main.constants import JOB_VARIABLE_PREFIXES
|
||||
from awx.main.utils.common import get_job_variable_prefixes
|
||||
|
||||
logger = logging.getLogger('awx.main.models.jobs')
|
||||
|
||||
@@ -817,19 +817,20 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
|
||||
|
||||
def awx_meta_vars(self):
|
||||
r = super(Job, self).awx_meta_vars()
|
||||
prefixes = get_job_variable_prefixes()
|
||||
if self.project:
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in prefixes:
|
||||
r['{}_project_revision'.format(name)] = self.project.scm_revision
|
||||
r['{}_project_scm_branch'.format(name)] = self.project.scm_branch
|
||||
if self.scm_branch:
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in prefixes:
|
||||
r['{}_job_scm_branch'.format(name)] = self.scm_branch
|
||||
if self.job_template:
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in prefixes:
|
||||
r['{}_job_template_id'.format(name)] = self.job_template.pk
|
||||
r['{}_job_template_name'.format(name)] = self.job_template.name
|
||||
if self.execution_node:
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in prefixes:
|
||||
r['{}_execution_node'.format(name)] = self.execution_node
|
||||
return r
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@ from awx.main.utils.common import (
|
||||
)
|
||||
from awx.main.utils.encryption import encrypt_dict, decrypt_field
|
||||
from awx.main.utils import polymorphic
|
||||
from awx.main.constants import ACTIVE_STATES, CAN_CANCEL, JOB_VARIABLE_PREFIXES
|
||||
from awx.main.constants import ACTIVE_STATES, CAN_CANCEL
|
||||
from awx.main.utils.common import get_job_variable_prefixes
|
||||
from awx.main.redact import UriCleaner, REPLACE_STR
|
||||
from awx.main.consumers import emit_channel_notification
|
||||
from awx.main.fields import AskForField, OrderedManyToManyField
|
||||
@@ -1568,7 +1569,8 @@ class UnifiedJob(
|
||||
by AWX, for purposes of client playbook hooks
|
||||
"""
|
||||
r = {}
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
prefixes = get_job_variable_prefixes()
|
||||
for name in prefixes:
|
||||
r['{}_job_id'.format(name)] = self.pk
|
||||
r['{}_job_launch_type'.format(name)] = self.launch_type
|
||||
|
||||
@@ -1577,7 +1579,7 @@ class UnifiedJob(
|
||||
wj = self.get_workflow_job()
|
||||
if wj:
|
||||
schedule = getattr_dne(wj, 'schedule')
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in prefixes:
|
||||
r['{}_workflow_job_id'.format(name)] = wj.pk
|
||||
r['{}_workflow_job_name'.format(name)] = wj.name
|
||||
r['{}_workflow_job_launch_type'.format(name)] = wj.launch_type
|
||||
@@ -1588,12 +1590,12 @@ class UnifiedJob(
|
||||
if not created_by:
|
||||
schedule = getattr_dne(self, 'schedule')
|
||||
if schedule:
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in prefixes:
|
||||
r['{}_schedule_id'.format(name)] = schedule.pk
|
||||
r['{}_schedule_name'.format(name)] = schedule.name
|
||||
|
||||
if created_by:
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in prefixes:
|
||||
r['{}_user_id'.format(name)] = created_by.pk
|
||||
r['{}_user_name'.format(name)] = created_by.username
|
||||
r['{}_user_email'.format(name)] = created_by.email
|
||||
@@ -1602,7 +1604,7 @@ class UnifiedJob(
|
||||
|
||||
inventory = getattr_dne(self, 'inventory')
|
||||
if inventory:
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in prefixes:
|
||||
r['{}_inventory_id'.format(name)] = inventory.pk
|
||||
r['{}_inventory_name'.format(name)] = inventory.name
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from crum import impersonate
|
||||
# AWX
|
||||
from awx.main.models import UnifiedJobTemplate, Job, JobTemplate, WorkflowJobTemplate, Project, WorkflowJob, Schedule, Credential
|
||||
from awx.api.versioning import reverse
|
||||
from awx.main.constants import JOB_VARIABLE_PREFIXES
|
||||
from awx.main.utils.common import get_job_variable_prefixes
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -160,7 +160,13 @@ class TestMetaVars:
|
||||
job = Job.objects.create(name='job', created_by=admin_user)
|
||||
job.save()
|
||||
|
||||
user_vars = ['_'.join(x) for x in itertools.product(['tower', 'awx'], ['user_name', 'user_id', 'user_email', 'user_first_name', 'user_last_name'])]
|
||||
user_vars = [
|
||||
'_'.join(x)
|
||||
for x in itertools.product(
|
||||
get_job_variable_prefixes(),
|
||||
['user_name', 'user_id', 'user_email', 'user_first_name', 'user_last_name'],
|
||||
)
|
||||
]
|
||||
|
||||
for key in user_vars:
|
||||
assert key in job.awx_meta_vars()
|
||||
@@ -179,7 +185,7 @@ class TestMetaVars:
|
||||
|
||||
workflow_job.workflow_nodes.create(job=job)
|
||||
data = job.awx_meta_vars()
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
assert data['{}_user_id'.format(name)] == admin_user.id
|
||||
assert data['{}_user_name'.format(name)] == admin_user.username
|
||||
assert data['{}_workflow_job_id'.format(name)] == workflow_job.pk
|
||||
@@ -189,7 +195,7 @@ class TestMetaVars:
|
||||
schedule = Schedule.objects.create(name='job-schedule', rrule='DTSTART:20171129T155939z\nFREQ=MONTHLY', unified_job_template=job_template)
|
||||
job = Job.objects.create(name='fake-job', launch_type='workflow', schedule=schedule, job_template=job_template)
|
||||
data = job.awx_meta_vars()
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
assert data['{}_schedule_id'.format(name)] == schedule.pk
|
||||
assert '{}_user_name'.format(name) not in data
|
||||
|
||||
@@ -201,7 +207,7 @@ class TestMetaVars:
|
||||
job = Job.objects.create(launch_type='workflow')
|
||||
workflow_job.workflow_nodes.create(job=job)
|
||||
result_hash = {}
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
result_hash['{}_job_id'.format(name)] = job.id
|
||||
result_hash['{}_job_launch_type'.format(name)] = 'workflow'
|
||||
result_hash['{}_workflow_job_name'.format(name)] = 'workflow-job'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from unittest import mock
|
||||
|
||||
from awx.main.models import UnifiedJob, UnifiedJobTemplate, WorkflowJob, WorkflowJobNode, WorkflowApprovalTemplate, Job, User, Project, JobTemplate, Inventory
|
||||
from awx.main.constants import JOB_VARIABLE_PREFIXES
|
||||
from awx.main.utils.common import get_job_variable_prefixes
|
||||
|
||||
|
||||
def test_incorrectly_formatted_variables():
|
||||
@@ -50,7 +50,7 @@ class TestMetaVars:
|
||||
maker = User(username='joe', pk=47, id=47)
|
||||
inv = Inventory(name='example-inv', id=45)
|
||||
result_hash = {}
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
result_hash['{}_job_id'.format(name)] = 42
|
||||
result_hash['{}_job_launch_type'.format(name)] = 'manual'
|
||||
result_hash['{}_user_name'.format(name)] = 'joe'
|
||||
@@ -75,8 +75,48 @@ class TestMetaVars:
|
||||
project=Project(name='jobs-sync', scm_revision='12345444'),
|
||||
job_template=JobTemplate(name='jobs-jt', id=92, pk=92),
|
||||
).awx_meta_vars()
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
assert data['{}_project_revision'.format(name)] == '12345444'
|
||||
assert '{}_job_template_id'.format(name) in data
|
||||
assert data['{}_job_template_id'.format(name)] == 92
|
||||
assert data['{}_job_template_name'.format(name)] == 'jobs-jt'
|
||||
|
||||
|
||||
class TestGetJobVariablePrefixes:
|
||||
"""Tests for the get_job_variable_prefixes() helper function."""
|
||||
|
||||
def test_default_returns_both(self):
|
||||
from django.conf import settings
|
||||
|
||||
with mock.patch.object(settings, 'INCLUDE_DEPRECATED_AWX_VAR_PREFIX', True, create=True):
|
||||
assert get_job_variable_prefixes() == ['awx', 'tower']
|
||||
|
||||
def test_disabled_returns_tower_only(self):
|
||||
from django.conf import settings
|
||||
|
||||
with mock.patch.object(settings, 'INCLUDE_DEPRECATED_AWX_VAR_PREFIX', False, create=True):
|
||||
assert get_job_variable_prefixes() == ['tower']
|
||||
|
||||
def test_fallback_when_setting_not_available(self):
|
||||
"""When setting is not available, falls back to both prefixes for backward compatibility."""
|
||||
fake_settings = mock.MagicMock(spec=[])
|
||||
with mock.patch('django.conf.settings', fake_settings):
|
||||
assert get_job_variable_prefixes() == ['awx', 'tower']
|
||||
|
||||
def test_job_metavars_both_prefixes(self):
|
||||
"""With INCLUDE_DEPRECATED_AWX_VAR_PREFIX=True, both awx_ and tower_ variables."""
|
||||
from django.conf import settings
|
||||
|
||||
with mock.patch.object(settings, 'INCLUDE_DEPRECATED_AWX_VAR_PREFIX', True, create=True):
|
||||
data = Job(name='fake-job', pk=1, id=1, launch_type='manual').awx_meta_vars()
|
||||
assert 'awx_job_id' in data
|
||||
assert 'tower_job_id' in data
|
||||
|
||||
def test_job_metavars_tower_only(self):
|
||||
"""With INCLUDE_DEPRECATED_AWX_VAR_PREFIX=False, only tower_ prefixed variables."""
|
||||
from django.conf import settings
|
||||
|
||||
with mock.patch.object(settings, 'INCLUDE_DEPRECATED_AWX_VAR_PREFIX', False, create=True):
|
||||
data = Job(name='fake-job', pk=1, id=1, launch_type='manual').awx_meta_vars()
|
||||
assert 'tower_job_id' in data
|
||||
assert 'awx_job_id' not in data
|
||||
|
||||
@@ -37,7 +37,7 @@ from awx.main.utils import encrypt_field, encrypt_value
|
||||
from awx.main.utils.safe_yaml import SafeLoader
|
||||
|
||||
from awx.main.utils.licensing import Licenser
|
||||
from awx.main.constants import JOB_VARIABLE_PREFIXES
|
||||
from awx.main.utils.common import get_job_variable_prefixes
|
||||
|
||||
from receptorctl.socket_interface import ReceptorControl
|
||||
|
||||
@@ -372,12 +372,12 @@ class TestExtraVarSanitation(TestJobExecution):
|
||||
extra_vars = yaml.load(fd, Loader=SafeLoader)
|
||||
|
||||
# ensure that strings are marked as unsafe
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
for variable_name in ['_job_template_name', '_user_name', '_job_launch_type', '_project_revision', '_inventory_name']:
|
||||
assert hasattr(extra_vars['{}{}'.format(name, variable_name)], '__UNSAFE__')
|
||||
|
||||
# ensure that non-strings are marked as safe
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
for variable_name in ['_job_template_id', '_job_id', '_user_id', '_inventory_id']:
|
||||
assert not hasattr(extra_vars['{}{}'.format(name, variable_name)], '__UNSAFE__')
|
||||
|
||||
@@ -524,7 +524,7 @@ class TestGenericRun:
|
||||
call_args, _ = task._write_extra_vars_file.call_args_list[0]
|
||||
|
||||
private_data_dir, extra_vars, safe_dict = call_args
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
assert extra_vars['{}_user_id'.format(name)] == 123
|
||||
assert extra_vars['{}_user_name'.format(name)] == "angry-spud"
|
||||
|
||||
@@ -615,7 +615,7 @@ class TestAdhocRun(TestJobExecution):
|
||||
call_args, _ = task._write_extra_vars_file.call_args_list[0]
|
||||
|
||||
private_data_dir, extra_vars = call_args
|
||||
for name in JOB_VARIABLE_PREFIXES:
|
||||
for name in get_job_variable_prefixes():
|
||||
assert extra_vars['{}_user_id'.format(name)] == 123
|
||||
assert extra_vars['{}_user_name'.format(name)] == "angry-spud"
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ __all__ = [
|
||||
'get_event_partition_epoch',
|
||||
'cleanup_new_process',
|
||||
'unified_job_class_to_event_table_name',
|
||||
'get_job_variable_prefixes',
|
||||
]
|
||||
|
||||
|
||||
@@ -773,6 +774,21 @@ def get_cpu_effective_capacity(cpu_count, is_control_node=False):
|
||||
return max(1, int(cpu_count * forkcpu))
|
||||
|
||||
|
||||
def get_job_variable_prefixes():
|
||||
"""Return the list of active job variable prefixes based on INCLUDE_DEPRECATED_AWX_VAR_PREFIX setting.
|
||||
|
||||
When True (default), returns both 'awx' and 'tower' prefixes for backward compatibility.
|
||||
When False, returns only 'tower'. The 'awx' prefix is deprecated and this setting
|
||||
will default to False in a future release.
|
||||
"""
|
||||
from django.conf import settings
|
||||
|
||||
include_awx = getattr(settings, 'INCLUDE_DEPRECATED_AWX_VAR_PREFIX', True)
|
||||
if include_awx:
|
||||
return ['awx', 'tower']
|
||||
return ['tower']
|
||||
|
||||
|
||||
def convert_mem_str_to_bytes(mem_str):
|
||||
"""Convert string with suffix indicating units to memory in bytes (base 2)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user