summaryrefslogtreecommitdiff
path: root/lib/ansible/template
diff options
context:
space:
mode:
authorSloane Hertel <19572925+s-hertel@users.noreply.github.com>2022-03-07 15:39:56 -0500
committerGitHub <noreply@github.com>2022-03-07 15:39:56 -0500
commit8063643b4cec51a72377da5f3fa354d3ff9e737a (patch)
tree11141d73e98087d16bfa9465a5dc609bd78feb8a /lib/ansible/template
parent50d28de9ba0d7271b966b3888916195cb9d28965 (diff)
downloadansible-8063643b4cec51a72377da5f3fa354d3ff9e737a.tar.gz
Fix collection redirects for filter and test plugins (#77210)
* Fix collection redirects for jinja2 filters/tests * Handle recursive redirects Co-authored-by: Matt Martz <matt@sivel.net>
Diffstat (limited to 'lib/ansible/template')
-rw-r--r--lib/ansible/template/__init__.py89
1 files changed, 53 insertions, 36 deletions
diff --git a/lib/ansible/template/__init__.py b/lib/ansible/template/__init__.py
index 894418ef52..3df7c5025e 100644
--- a/lib/ansible/template/__init__.py
+++ b/lib/ansible/template/__init__.py
@@ -455,6 +455,7 @@ class JinjaPluginIntercept(MutableMapping):
# FUTURE: we can cache FQ filter/test calls for the entire duration of a run, since a given collection's impl's
# aren't supposed to change during a run
def __getitem__(self, key):
+ original_key = key
self._load_ansible_plugins()
try:
@@ -469,56 +470,61 @@ class JinjaPluginIntercept(MutableMapping):
if func:
return func
- # didn't find it in the pre-built Jinja env, assume it's a former builtin and follow the normal routing path
- leaf_key = key
- key = 'ansible.builtin.' + key
- else:
- leaf_key = key.split('.')[-1]
+ key, leaf_key = get_fqcr_and_name(key)
+ seen = set()
+
+ while True:
+ if key in seen:
+ raise TemplateSyntaxError(
+ 'recursive collection redirect found for %r' % original_key,
+ 0
+ )
+ seen.add(key)
- acr = AnsibleCollectionRef.try_parse_fqcr(key, self._dirname)
+ acr = AnsibleCollectionRef.try_parse_fqcr(key, self._dirname)
- if not acr:
- raise KeyError('invalid plugin name: {0}'.format(key))
+ if not acr:
+ raise KeyError('invalid plugin name: {0}'.format(key))
- ts = _get_collection_metadata(acr.collection)
+ ts = _get_collection_metadata(acr.collection)
- # TODO: implement support for collection-backed redirect (currently only builtin)
- # TODO: implement cycle detection (unified across collection redir as well)
+ # TODO: implement cycle detection (unified across collection redir as well)
- routing_entry = ts.get('plugin_routing', {}).get(self._dirname, {}).get(leaf_key, {})
+ routing_entry = ts.get('plugin_routing', {}).get(self._dirname, {}).get(leaf_key, {})
- deprecation_entry = routing_entry.get('deprecation')
- if deprecation_entry:
- warning_text = deprecation_entry.get('warning_text')
- removal_date = deprecation_entry.get('removal_date')
- removal_version = deprecation_entry.get('removal_version')
+ deprecation_entry = routing_entry.get('deprecation')
+ if deprecation_entry:
+ warning_text = deprecation_entry.get('warning_text')
+ removal_date = deprecation_entry.get('removal_date')
+ removal_version = deprecation_entry.get('removal_version')
- if not warning_text:
- warning_text = '{0} "{1}" is deprecated'.format(self._dirname, key)
+ if not warning_text:
+ warning_text = '{0} "{1}" is deprecated'.format(self._dirname, key)
- display.deprecated(warning_text, version=removal_version, date=removal_date, collection_name=acr.collection)
+ display.deprecated(warning_text, version=removal_version, date=removal_date, collection_name=acr.collection)
- tombstone_entry = routing_entry.get('tombstone')
+ tombstone_entry = routing_entry.get('tombstone')
- if tombstone_entry:
- warning_text = tombstone_entry.get('warning_text')
- removal_date = tombstone_entry.get('removal_date')
- removal_version = tombstone_entry.get('removal_version')
+ if tombstone_entry:
+ warning_text = tombstone_entry.get('warning_text')
+ removal_date = tombstone_entry.get('removal_date')
+ removal_version = tombstone_entry.get('removal_version')
- if not warning_text:
- warning_text = '{0} "{1}" has been removed'.format(self._dirname, key)
+ if not warning_text:
+ warning_text = '{0} "{1}" has been removed'.format(self._dirname, key)
- exc_msg = display.get_deprecation_message(warning_text, version=removal_version, date=removal_date,
- collection_name=acr.collection, removed=True)
+ exc_msg = display.get_deprecation_message(warning_text, version=removal_version, date=removal_date,
+ collection_name=acr.collection, removed=True)
- raise AnsiblePluginRemovedError(exc_msg)
+ raise AnsiblePluginRemovedError(exc_msg)
- redirect_fqcr = routing_entry.get('redirect', None)
- if redirect_fqcr:
- acr = AnsibleCollectionRef.from_fqcr(ref=redirect_fqcr, ref_type=self._dirname)
- display.vvv('redirecting {0} {1} to {2}.{3}'.format(self._dirname, key, acr.collection, acr.resource))
- key = redirect_fqcr
- # TODO: handle recursive forwarding (not necessary for builtin, but definitely for further collection redirs)
+ redirect = routing_entry.get('redirect', None)
+ if redirect:
+ next_key, leaf_key = get_fqcr_and_name(redirect, collection=acr.collection)
+ display.vvv('redirecting (type: {0}) {1}.{2} to {3}'.format(self._dirname, acr.collection, acr.resource, next_key))
+ key = next_key
+ else:
+ break
func = self._collection_jinja_func_cache.get(key)
@@ -593,6 +599,17 @@ class JinjaPluginIntercept(MutableMapping):
return len(self._delegatee)
+def get_fqcr_and_name(resource, collection='ansible.builtin'):
+ if '.' not in resource:
+ name = resource
+ fqcr = collection + '.' + resource
+ else:
+ name = resource.split('.')[-1]
+ fqcr = resource
+
+ return fqcr, name
+
+
@_unroll_iterator
def _ansible_finalize(thing):
"""A custom finalize function for jinja2, which prevents None from being