From e053a58223ea9866ee8d7f96eab2af35a0b34d44 Mon Sep 17 00:00:00 2001 From: Jeff Bradberry Date: Wed, 1 Apr 2020 15:34:42 -0400 Subject: [PATCH] Handle some more bad cases when doing OPTIONS calls - deprecated endpoints - read-only endpoints - insufficient privileges The latter case currently just drops it on the floor, but ought to do something better. --- awxkit/awxkit/api/pages/api.py | 40 +++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/awxkit/awxkit/api/pages/api.py b/awxkit/awxkit/api/pages/api.py index 905aa3d152..a3c2144b5b 100644 --- a/awxkit/awxkit/api/pages/api.py +++ b/awxkit/awxkit/api/pages/api.py @@ -54,8 +54,26 @@ class ApiV2(base.Base): # Common import/export methods - def _get_options(self, endpoint): - return endpoint.options().json['actions'].get('POST', {}) + def _get_options(self, _page): + if getattr(self, '_options', None) is None: + self._options = {} + + if isinstance(_page, page.TentativePage): + url = str(_page) + else: + url = _page.url + + if url in self._options: + return self._options[url] + + options = _page.options() + warning = options.r.headers.get('Warning', '') + if '299' in warning and 'deprecated' in warning: + return self._options.setdefault(url, None) + if 'POST' not in options.r.headers.get('Allow', ''): + return self._options.setdefault(url, None) + + return self._options.setdefault(url, options.json['actions'].get('POST', {})) # Export methods @@ -63,6 +81,8 @@ class ApiV2(base.Base): # Drop any (credential_type) assets that are being managed by the Tower instance. if asset.json.get('managed_by_tower'): return None + if options is None: # Deprecated endpoint or insufficient permissions + return None try: # Note: doing asset[key] automatically parses json blob strings, which can be a problem. @@ -88,14 +108,16 @@ class ApiV2(base.Base): if rel.__class__.__name__ in EXPORTABLE_RELATIONS: by_natural_key = True + related_options = self._get_options(related_endpoint) + if related_options is None: + continue elif rel.__class__.__name__ in EXPORTABLE_DEPENDENT_OBJECTS: - by_natural_key = False + by_natural_key, related_options = False, None else: continue data = related_endpoint.get(all_pages=True) if 'results' in data: - related_options = self._get_options(related_endpoint) related[key] = [ x.get_natural_key() if by_natural_key else self._serialize_asset(x, related_options) for x in data.results @@ -113,6 +135,10 @@ class ApiV2(base.Base): def _get_assets(self, resource, value): endpoint = getattr(self, resource) + options = self._get_options(endpoint) + if options is None: + return None + if value: from awxkit.cli.options import pk_or_name @@ -121,7 +147,6 @@ class ApiV2(base.Base): else: results = endpoint.get(all_pages=True).results - options = self._get_options(endpoint) assets = (self._serialize_asset(asset, options) for asset in results) return [asset for asset in assets if asset is not None] @@ -160,9 +185,8 @@ class ApiV2(base.Base): def _register_existing_assets(self, resource): endpoint = getattr(self, resource) options = self._get_options(endpoint) - if getattr(self, '_options', None) is None: - self._options = {} - self._options[resource] = options + if options is None: + return results = endpoint.get(all_pages=True).results for pg in results: