From 369186060490717a60713e76e4a3523630746b57 Mon Sep 17 00:00:00 2001 From: Ondra Machacek Date: Fri, 8 Mar 2019 08:44:02 +0100 Subject: [PATCH] Make list from kind resource item Previously when creating a resource list, kind was a dictionary meaning that there could be only one resource for specific kind. But for example Template kind can have to different resources: $ oc api-resources | grep Template$ processedtemplates template.openshift.io true Template templates template.openshift.io true Template So The client previsously found just one resource for kind Template. In order to work with both resources for that kind this patch introduces the change that kind will be list by default. --- openshift/dynamic/client.py | 33 +++++++++++++++++----------- test/unit/test_discoverer.py | 42 +++++++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/openshift/dynamic/client.py b/openshift/dynamic/client.py index 154f291f..12b6e6ac 100644 --- a/openshift/dynamic/client.py +++ b/openshift/dynamic/client.py @@ -3,6 +3,7 @@ import json import hashlib import tempfile +from collections import defaultdict from functools import partial from abc import abstractmethod, abstractproperty @@ -642,7 +643,7 @@ def default_groups(self, request_resources=False): if request_resources else ResourceGroup(True)) }} groups[DISCOVERY_PREFIX] = {'': { - 'v1': ResourceGroup(True, resources = {"List": ResourceList(self.client)}) + 'v1': ResourceGroup(True, resources = {"List": [ResourceList(self.client)]}) }} return groups @@ -691,7 +692,7 @@ def _load_server_info(self): def get_resources_for_api_version(self, prefix, group, version, preferred): """ returns a dictionary of resources associated with provided (prefix, group, version)""" - resources = {} + resources = defaultdict(list) subresources = {} path = '/'.join(filter(None, [prefix, group, version])) @@ -710,7 +711,7 @@ def get_resources_for_api_version(self, prefix, group, version, preferred): for key in ('prefix', 'group', 'api_version', 'client', 'preferred'): resource.pop(key, None) - resources[resource['kind']] = Resource( + resourceobj = Resource( prefix=prefix, group=group, api_version=version, @@ -719,8 +720,10 @@ def get_resources_for_api_version(self, prefix, group, version, preferred): subresources=subresources.get(resource['name']), **resource ) + resources[resource['kind']].append(resourceobj) + resource_list = ResourceList(self.client, group=group, api_version=version, base_kind=resource['kind']) - resources[resource_list.kind] = resource_list + resources[resource_list.kind].append(resource_list) return resources def get(self, **kwargs): @@ -779,6 +782,7 @@ def search(self, **kwargs): def __search(self, parts, resources, reqParams): part = parts[0] if part != '*': + resourcePart = resources.get(part) if not resourcePart: return [] @@ -799,11 +803,14 @@ def __search(self, parts, resources, reqParams): return self.__search(parts[1:], resourcePart, reqParams + [part] ) else: if parts[1] != '*' and isinstance(parts[1], dict): - for term, value in parts[1].items(): - if getattr(resourcePart, term) == value: - return [resourcePart] + for _resource in resourcePart: + for term, value in parts[1].items(): + if getattr(_resource, term) == value: + return [_resource] + + return [] else: - return [resourcePart] + return resourcePart else: matches = [] for key in resources.keys(): @@ -889,11 +896,13 @@ def __search(self, parts, resources): return self.__search(parts[1:], resourcePart) else: if parts[1] != '*' and isinstance(parts[1], dict): - for term, value in parts[1].items(): - if getattr(resourcePart, term) == value: - return [resourcePart] + for _resource in resourcePart: + for term, value in parts[1].items(): + if getattr(_resource, term) == value: + return [_resource] + return [] else: - return [resourcePart] + return resourcePart elif part == '*': matches = [] for key in resources.keys(): diff --git a/test/unit/test_discoverer.py b/test/unit/test_discoverer.py index d3f046bb..c597bcee 100644 --- a/test/unit/test_discoverer.py +++ b/test/unit/test_discoverer.py @@ -26,6 +26,33 @@ def mock_namespace(): verbs=['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'] ) +@pytest.fixture(scope='module') +def mock_templates(): + return Resource( + api_version='v1', + kind='Template', + name='templates', + namespaced=True, + preferred=True, + prefix='api', + shorter_names=[], + shortNames=[], + verbs=['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'] + ) + +@pytest.fixture(scope='module') +def mock_processedtemplates(): + return Resource( + api_version='v1', + kind='Template', + name='processedtemplates', + namespaced=True, + preferred=True, + prefix='api', + shorter_names=[], + shortNames=[], + verbs=['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'] + ) @pytest.fixture(scope='module') def mock_namespace_list(mock_namespace): @@ -35,7 +62,7 @@ def mock_namespace_list(mock_namespace): @pytest.fixture(scope='function', autouse=True) -def setup_client_monkeypatch(monkeypatch, mock_namespace, mock_namespace_list): +def setup_client_monkeypatch(monkeypatch, mock_namespace, mock_namespace_list, mock_templates, mock_processedtemplates): def mock_load_server_info(self): self.__version = {'kubernetes': 'mock-k8s-version'} @@ -45,8 +72,9 @@ def mock_parse_api_groups(self, request_resources=False): 'api': { '': { 'v1': { - 'Namespace': mock_namespace, - 'NamespaceList': mock_namespace_list + 'Namespace': [mock_namespace], + 'NamespaceList': [mock_namespace_list], + 'Template': [mock_templates, mock_processedtemplates], } } } @@ -92,3 +120,11 @@ def test_get_namespace_list_kind(client, mock_namespace_list): resource = client.resources.get(api_version='v1', kind='NamespaceList') assert resource == mock_namespace_list + + +def test_search_multiple_resources_for_template(client, mock_templates, mock_processedtemplates): + resources = client.resources.search(api_version='v1', kind='Template') + + assert len(resources) == 2 + assert mock_templates in resources + assert mock_processedtemplates in resources