summaryrefslogtreecommitdiff
path: root/lib/ansible
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible')
-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 6dc7d8e394..5006c035d4 100644
--- a/lib/ansible/template/__init__.py
+++ b/lib/ansible/template/__init__.py
@@ -445,6 +445,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:
@@ -459,56 +460,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)
@@ -576,6 +582,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
+
+
class AnsibleEnvironment(Environment):
'''
Our custom environment, which simply allows us to override the class-level