Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

inspect components, ansible_managed templatable #83053

Merged
merged 14 commits into from Apr 23, 2024
2 changes: 2 additions & 0 deletions changelogs/fragments/ansible_managed_restore.yml
@@ -0,0 +1,2 @@
bugfixes:
- ansible_managed will now be templatable if it's source data is confirmed to not be a template.
bcoca marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion lib/ansible/plugins/action/template.py
Expand Up @@ -130,7 +130,7 @@ def run(self, tmp=None, task_vars=None):
# NOTE in the case of ANSIBLE_DEBUG=1 task_vars is VarsWithSources(MutableMapping)
# so | operator cannot be used as it can be used only on dicts
# https://peps.python.org/pep-0584/#what-about-mapping-and-mutablemapping
temp_vars.update(generate_ansible_template_vars(self._task.args.get('src', None), source, dest))
temp_vars.update(generate_ansible_template_vars(self._task.args.get('src', None), self._templar.environment, source, dest))

# force templar to use AnsibleEnvironment to prevent issues with native types
# https://github.com/ansible/ansible/issues/46169
Expand Down
2 changes: 1 addition & 1 deletion lib/ansible/plugins/lookup/template.py
Expand Up @@ -146,7 +146,7 @@ def run(self, terms, variables, **kwargs):
# plus anything passed to the lookup with the template_vars=
# argument.
vars = deepcopy(variables)
vars.update(generate_ansible_template_vars(term, lookupfile))
vars.update(generate_ansible_template_vars(term, templar.environment, lookupfile))
vars.update(lookup_template_vars)

with templar.set_temporary_context(available_variables=vars, searchpath=searchpath):
Expand Down
33 changes: 28 additions & 5 deletions lib/ansible/template/__init__.py
Expand Up @@ -72,7 +72,7 @@
RANGE_TYPE = type(range(0))


def generate_ansible_template_vars(path, fullpath=None, dest_path=None):
def generate_ansible_template_vars(path, j2env, fullpath=None, dest_path=None):

if fullpath is None:
b_path = to_bytes(path)
Expand All @@ -98,13 +98,36 @@ def generate_ansible_template_vars(path, fullpath=None, dest_path=None):
else:
temp_vars['template_fullpath'] = fullpath

safe = True
morsel = ''

t_file = temp_vars['template_path'].replace('%', '%%')
t_host = temp_vars['template_host']
t_uid = temp_vars['template_uid']

if is_possibly_template(t_file, j2env):
safe = False
morsel += f' template file name({t_file})'

if is_possibly_template(t_host, j2env):
safe = False
morsel += f' host({t_host})'

if is_possibly_template(t_uid, j2env):
safe = False
morsel += f' uid({t_uid})'

managed_default = C.DEFAULT_MANAGED_STR
managed_str = managed_default.format(
host=temp_vars['template_host'],
uid=temp_vars['template_uid'],
file=temp_vars['template_path'].replace('%', '%%'),
host=t_host,
uid=t_uid,
file=t_file,
bcoca marked this conversation as resolved.
Show resolved Hide resolved
)
temp_vars['ansible_managed'] = to_unsafe_text(time.strftime(to_native(managed_str), time.localtime(os.path.getmtime(b_path))))
temp_vars['ansible_managed'] = time.strftime(to_native(managed_str), time.localtime(os.path.getmtime(b_path)))

if not safe:
temp_vars['ansible_managed'] = to_unsafe_text(temp_vars['ansible_managed'])
display.warning(f"Found templatable {morsel}, marking ansible_managed as unsafe, so it won't be templated.")

return temp_vars

Expand Down
52 changes: 43 additions & 9 deletions test/integration/targets/template/ansible_managed.yml
Expand Up @@ -2,13 +2,47 @@
- hosts: testhost
gather_facts: False
tasks:
- set_fact:
- name: set output_dir
set_fact:
output_dir: "{{ lookup('env', 'OUTPUT_DIR') }}"
- file:
path: '{{ output_dir }}/café.txt'
state: 'absent'
# Smoketest that ansible_managed with non-ascii chars works:
# https://github.com/ansible/ansible/issues/27262
- template:
src: 'templates/café.j2'
dest: '{{ output_dir }}/café.txt'

- name: Smoketest that ansible_managed with non-ascii chars works, https://github.com/ansible/ansible/issues/27262
block:
- name: ensure output file does not exist
file:
path: '{{ output_dir }}/café.txt'
state: 'absent'

- name: test templating with unicode in template name
template:
src: 'templates/café.j2'
dest: '{{ output_dir }}/café.txt'

always:
- name: clean up!
file:
path: '{{ output_dir }}/café.txt'
state: 'absent'

- name: check strftime resolution in ansible_managed
block:
- template:
src: "templates/%necho Onii-chan help Im stuck;exit 1%n.j2"
dest: "{{ output_dir }}/79129-strftime.sh"
mode: '0755'

- shell: "exec {{ output_dir | quote }}/79129-strftime.sh"

- name: Avoid templating 'injections' via file names
template:
src: !unsafe "templates/completely{{ 1 % 0 }} safe template.j2"
dest: "{{ output_dir }}/79129-jinja.sh"
mode: '0755'

- shell: "exec {{ output_dir | quote }}/79129-jinja.sh"
register: result

- assert:
that:
- "'Hello' in result.stdout"
- "'{' not in lookup('file', output_dir ~ '/79129-strftime.sh')"
29 changes: 0 additions & 29 deletions test/integration/targets/template/ansible_managed_79129.yml

This file was deleted.

@@ -0,0 +1,2 @@
[defaults]
ansible_managed=ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}({{{{q('pipe', 'uname -a')}}}})
6 changes: 3 additions & 3 deletions test/integration/targets/template/runme.sh
Expand Up @@ -7,11 +7,11 @@ ANSIBLE_ROLES_PATH=../ ansible-playbook template.yml -i ../../inventory -v "$@"
# Test for https://github.com/ansible/ansible/pull/35571
ansible testhost -i testhost, -m debug -a 'msg={{ hostvars["localhost"] }}' -e "vars1={{ undef() }}" -e "vars2={{ vars1 }}"

# Test for https://github.com/ansible/ansible/issues/27262
# ansible_managed tests
ANSIBLE_CONFIG=ansible_managed.cfg ansible-playbook ansible_managed.yml -i ../../inventory -v "$@"

# Test for https://github.com/ansible/ansible/pull/79129
ANSIBLE_CONFIG=ansible_managed.cfg ansible-playbook ansible_managed_79129.yml -i ../../inventory -v "$@"
# same as above but with ansible_managed j2 template
ANSIBLE_CONFIG=ansible_managed_templated.cfg ansible-playbook ansible_managed.yml -i ../../inventory -v "$@"

# Test for #42585
ANSIBLE_ROLES_PATH=../ ansible-playbook custom_template.yml -i ../../inventory -v "$@"
Expand Down