Add support for start/end_string arguments

In case template contains some Jinja content that we don't want to
resolve, there're not much options due to the bug, that ignores
{% raw %} tags. The other way to work it around is to override tags that
Jinja will use to detect the content it needs to render. This way
content that should be renderred can be placed in the same template
with the one that needs to stay intact.

Change-Id: I4d28f5ab03858f5555f5e9e555da1dddc0f2b016
Related-Bug: #1649381
This commit is contained in:
Dmitriy Rabotyagov 2023-04-28 20:25:57 +02:00
parent 795122864b
commit fdb572d0ba
6 changed files with 83 additions and 13 deletions

View File

@ -737,6 +737,10 @@ class ActionModule(ActionBase):
remote_src = self._task.args.get('remote_src', False) remote_src = self._task.args.get('remote_src', False)
yml_multilines = self._task.args.get('yml_multilines', False) yml_multilines = self._task.args.get('yml_multilines', False)
block_end_string = self._task.args.get('block_end_string', '%}')
block_start_string = self._task.args.get('block_start_string', '{%')
variable_end_string = self._task.args.get('variable_end_string', '}}')
variable_start_string = self._task.args.get('variable_start_string', '{{')
return True, dict( return True, dict(
source=source, source=source,
@ -748,7 +752,11 @@ class ActionModule(ActionBase):
ignore_none_type=ignore_none_type, ignore_none_type=ignore_none_type,
default_section=default_section, default_section=default_section,
yml_multilines=yml_multilines, yml_multilines=yml_multilines,
remote_src=remote_src remote_src=remote_src,
block_end_string=block_end_string,
block_start_string=block_start_string,
variable_end_string=variable_end_string,
variable_start_string=variable_start_string
) )
def resultant_ini_as_dict(self, resultant_dict, return_dict=None): def resultant_ini_as_dict(self, resultant_dict, return_dict=None):
@ -766,14 +774,22 @@ class ActionModule(ActionBase):
return return_dict return return_dict
def _check_templar(self, data): def _check_templar(self, data, extra):
if boolean(self._task.args.get('render_template', True)): if boolean(self._task.args.get('render_template', True)):
return self._templar.template( templar = self._templar
data, with templar.set_temporary_context(
preserve_trailing_newlines=True, variable_start_string=extra['variable_start_string'],
escape_backslashes=False, variable_end_string=extra['variable_end_string'],
convert_data=False block_start_string=extra['block_start_string'],
) block_end_string=extra['block_end_string'],
searchpath=extra['searchpath']
):
return templar.template(
data,
preserve_trailing_newlines=True,
escape_backslashes=False,
convert_data=False
)
else: else:
return data return data
@ -862,14 +878,12 @@ class ActionModule(ActionBase):
template_data_slurpee['content'] template_data_slurpee['content']
).decode('utf-8') ).decode('utf-8')
self._templar.environment.loader.searchpath = _vars['searchpath']
if LooseVersion(__ansible_version__) < LooseVersion("2.9"): if LooseVersion(__ansible_version__) < LooseVersion("2.9"):
self._templar.set_available_variables(temp_vars) self._templar.set_available_variables(temp_vars)
else: else:
self._templar.available_variables = temp_vars self._templar.available_variables = temp_vars
resultant = self._check_templar(data=template_data) resultant = self._check_templar(data=template_data, extra=_vars)
if LooseVersion(__ansible_version__) < LooseVersion("2.9"): if LooseVersion(__ansible_version__) < LooseVersion("2.9"):
# Access to protected method is unavoidable in Ansible # Access to protected method is unavoidable in Ansible
@ -898,7 +912,7 @@ class ActionModule(ActionBase):
if 'content' in slurpee: if 'content' in slurpee:
dest_data = base64.b64decode( dest_data = base64.b64decode(
slurpee['content']).decode('utf-8') slurpee['content']).decode('utf-8')
resultant_dest = self._check_templar(data=dest_data) resultant_dest = self._check_templar(data=dest_data, extra=_vars)
type_merger = getattr(self, type_merger = getattr(self,
CONFIG_TYPES.get(_vars['config_type'])) CONFIG_TYPES.get(_vars['config_type']))
_, config_new = type_merger( _, config_new = type_merger(
@ -941,7 +955,7 @@ class ActionModule(ActionBase):
# Re-template the resultant object as it may have new data within it # Re-template the resultant object as it may have new data within it
# as provided by an override variable. # as provided by an override variable.
resultant = self._check_templar(data=resultant) resultant = self._check_templar(data=resultant, extra=_vars)
# run the copy module # run the copy module
new_module_args = self._task.args.copy() new_module_args = self._task.args.copy()
@ -976,6 +990,10 @@ class ActionModule(ActionBase):
new_module_args.pop('ignore_none_type', None) new_module_args.pop('ignore_none_type', None)
new_module_args.pop('default_section', None) new_module_args.pop('default_section', None)
new_module_args.pop('yml_multilines', None) new_module_args.pop('yml_multilines', None)
new_module_args.pop('block_end_string', None)
new_module_args.pop('block_start_string', None)
new_module_args.pop('variable_end_string', None)
new_module_args.pop('variable_start_string', None)
# While this is in the copy module we dont want to use it. # While this is in the copy module we dont want to use it.
new_module_args.pop('remote_src', None) new_module_args.pop('remote_src', None)

View File

@ -0,0 +1,10 @@
---
features:
- |
Added support for arguments that can be used to avoid templating Jinja
content:
* variable_start_string
* variable_end_string
* block_start_string
* block_end_string

View File

@ -0,0 +1,4 @@
section1:
alfa: '{{ alfa_hotel }}'
baz: baz
tango: tango

View File

@ -0,0 +1,5 @@
---
section1:
alfa: "{{ alfa_hotel }}"
tango: "tango"

View File

@ -173,3 +173,28 @@
assert: assert:
that: that:
- "(test_list_only_replace_file_expected.content | b64decode) == (test_list_only_replace_file.content | b64decode)" - "(test_list_only_replace_file_expected.content | b64decode) == (test_list_only_replace_file.content | b64decode)"
- name: Test template with jinja vars in it
config_template:
src: "{{ playbook_dir }}/templates/test_jinja_variables.yml"
dest: "/tmp/test_jinja_variables.yml"
config_overrides: "{{ test_jinja_variables }}"
config_type: yaml
variable_end_string: "%]"
variable_start_string: "[%"
- name: Read test_jinja_variables.yml
slurp:
src: /tmp/test_jinja_variables.yml
register: test_jinja_variables
- debug:
msg: "Jinja strings - {{ test_jinja_variables.content | b64decode }}"
- debug:
msg: "Jinja strings Expected - {{ test_jinja_variables_expected.content | b64decode }}"
- name: Compare files
assert:
that:
- "(test_jinja_variables_expected.content | b64decode) == (test_jinja_variables.content | b64decode)"

View File

@ -73,6 +73,11 @@
src: "{{ playbook_dir }}/files/test_list_only_replace.yml.expected" src: "{{ playbook_dir }}/files/test_list_only_replace.yml.expected"
register: test_list_only_replace_file_expected register: test_list_only_replace_file_expected
- name: Read expected test_jinja_variables.yml
slurp:
src: "{{ playbook_dir }}/files/test_jinja_variables.yml.expected"
register: test_jinja_variables_expected
- import_tasks: test-common-tasks.yml - import_tasks: test-common-tasks.yml
handlers: handlers:
@ -168,6 +173,9 @@
baz: "hotel" baz: "hotel"
section3: section3:
alfa: "bravo" alfa: "bravo"
test_jinja_variables:
section1:
baz: "baz"
test_enhanced_comments_ini_overrides: test_enhanced_comments_ini_overrides:
DEFAULT: DEFAULT:
default_availability_zone: zone1 default_availability_zone: zone1