Properly template tags in parent blocks (#81624)

When templating tags (which happens outside of standard `post_validate`) we
need to template each object in the inheritance chain and set the templated
values on those objects individually. That way when `task.tags` is called the
`extend` functionality properly picks up the templated values of all
parents into one flatten list.

Fixes #81053
pull/79687/merge
Martin Krizek 1 year ago committed by GitHub
parent 304e63d76e
commit 9b3ed5ec68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
bugfixes:
- Properly template tags in parent blocks (https://github.com/ansible/ansible/issues/81053)

@ -23,6 +23,17 @@ from ansible.errors import AnsibleError
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from ansible.playbook.attribute import FieldAttribute from ansible.playbook.attribute import FieldAttribute
from ansible.template import Templar from ansible.template import Templar
from ansible.utils.sentinel import Sentinel
def _flatten_tags(tags: list) -> list:
rv = set()
for tag in tags:
if isinstance(tag, list):
rv.update(tag)
else:
rv.add(tag)
return list(rv)
class Taggable: class Taggable:
@ -34,11 +45,7 @@ class Taggable:
if isinstance(ds, list): if isinstance(ds, list):
return ds return ds
elif isinstance(ds, string_types): elif isinstance(ds, string_types):
value = ds.split(',') return [x.strip() for x in ds.split(',')]
if isinstance(value, list):
return [x.strip() for x in value]
else:
return [ds]
else: else:
raise AnsibleError('tags must be specified as a list', obj=ds) raise AnsibleError('tags must be specified as a list', obj=ds)
@ -47,16 +54,12 @@ class Taggable:
if self.tags: if self.tags:
templar = Templar(loader=self._loader, variables=all_vars) templar = Templar(loader=self._loader, variables=all_vars)
tags = templar.template(self.tags) obj = self
while obj is not None:
_temp_tags = set() if (_tags := getattr(obj, "_tags", Sentinel)) is not Sentinel:
for tag in tags: obj._tags = _flatten_tags(templar.template(_tags))
if isinstance(tag, list): obj = obj._parent
_temp_tags.update(tag) tags = set(self.tags)
else:
_temp_tags.add(tag)
tags = _temp_tags
self.tags = list(tags)
else: else:
# this makes isdisjoint work for untagged # this makes isdisjoint work for untagged
tags = self.untagged tags = self.untagged

@ -73,3 +73,12 @@ ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=list --tags t
ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=untagged --tags untagged "$@" ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=untagged --tags untagged "$@"
ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=untagged_list --tags untagged,tag3 "$@" ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=untagged_list --tags untagged,tag3 "$@"
ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=tagged --tags tagged "$@" ansible-playbook -i ../../inventory ansible_run_tags.yml -e expect=tagged --tags tagged "$@"
ansible-playbook test_template_parent_tags.yml "$@" 2>&1 | tee out.txt
[ "$(grep out.txt -ce 'Tagged_task')" = "1" ]; rm out.txt
ansible-playbook test_template_parent_tags.yml --tags tag1 "$@" 2>&1 | tee out.txt
[ "$(grep out.txt -ce 'Tagged_task')" = "1" ]; rm out.txt
ansible-playbook test_template_parent_tags.yml --skip-tags tag1 "$@" 2>&1 | tee out.txt
[ "$(grep out.txt -ce 'Tagged_task')" = "0" ]; rm out.txt

@ -0,0 +1,10 @@
- hosts: localhost
gather_facts: false
vars:
tags_in_var:
- tag1
tasks:
- block:
- name: Tagged_task
debug:
tags: "{{ tags_in_var }}"

@ -29,6 +29,7 @@ class TaggableTestObj(Taggable):
def __init__(self): def __init__(self):
self._loader = DictDataLoader({}) self._loader = DictDataLoader({})
self.tags = [] self.tags = []
self._parent = None
class TestTaggable(unittest.TestCase): class TestTaggable(unittest.TestCase):

Loading…
Cancel
Save