diff options
author | Sloane Hertel <19572925+s-hertel@users.noreply.github.com> | 2022-03-07 15:39:56 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-07 15:39:56 -0500 |
commit | 8063643b4cec51a72377da5f3fa354d3ff9e737a (patch) | |
tree | 11141d73e98087d16bfa9465a5dc609bd78feb8a /lib/ansible/template | |
parent | 50d28de9ba0d7271b966b3888916195cb9d28965 (diff) | |
download | ansible-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__.py | 89 |
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 |