diff options
Diffstat (limited to 'lib/ansible')
-rw-r--r-- | lib/ansible/template/__init__.py | 89 |
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 |