Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
resource.py406 linesDownload Raw Back to dynamic
1# Copyright 2019 The Kubernetes Authors.2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7#     http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14 15import copy16import yaml17from functools import partial18 19from pprint import pformat20 21 22class Resource(object):23    """ Represents an API resource type, containing the information required to build urls for requests """24 25    def __init__(self, prefix=None, group=None, api_version=None, kind=None,26                 namespaced=False, verbs=None, name=None, preferred=False, client=None,27                 singularName=None, shortNames=None, categories=None, subresources=None, **kwargs):28 29        if None in (api_version, kind, prefix):30            raise ValueError("At least prefix, kind, and api_version must be provided")31 32        self.prefix = prefix33        self.group = group34        self.api_version = api_version35        self.kind = kind36        self.namespaced = namespaced37        self.verbs = verbs38        self.name = name39        self.preferred = preferred40        self.client = client41        self.singular_name = singularName or (name[:-1] if name else "")42        self.short_names = shortNames43        self.categories = categories44        self.subresources = {45            k: Subresource(self, **v) for k, v in (subresources or {}).items()46        }47 48        self.extra_args = kwargs49 50    def to_dict(self):51        d = {52            '_type': 'Resource',53            'prefix': self.prefix,54            'group': self.group,55            'api_version': self.api_version,56            'kind': self.kind,57            'namespaced': self.namespaced,58            'verbs': self.verbs,59            'name': self.name,60            'preferred': self.preferred,61            'singularName': self.singular_name,62            'shortNames': self.short_names,63            'categories': self.categories,64            'subresources': {k: sr.to_dict() for k, sr in self.subresources.items()},65        }66        d.update(self.extra_args)67        return d68 69    @property70    def group_version(self):71        if self.group:72            return '{}/{}'.format(self.group, self.api_version)73        return self.api_version74 75    def __repr__(self):76        return '<{}({}/{})>'.format(self.__class__.__name__, self.group_version, self.name)77 78    @property79    def urls(self):80        full_prefix = '{}/{}'.format(self.prefix, self.group_version)81        resource_name = self.name.lower()82        return {83            'base': '/{}/{}'.format(full_prefix, resource_name),84            'namespaced_base': '/{}/namespaces/{{namespace}}/{}'.format(full_prefix, resource_name),85            'full': '/{}/{}/{{name}}'.format(full_prefix, resource_name),86            'namespaced_full': '/{}/namespaces/{{namespace}}/{}/{{name}}'.format(full_prefix, resource_name)87        }88 89    def path(self, name=None, namespace=None):90        url_type = []91        path_params = {}92        if self.namespaced and namespace:93            url_type.append('namespaced')94            path_params['namespace'] = namespace95        if name:96            url_type.append('full')97            path_params['name'] = name98        else:99            url_type.append('base')100        return self.urls['_'.join(url_type)].format(**path_params)101 102    def __getattr__(self, name):103        if name in self.subresources:104            return self.subresources[name]105        return partial(getattr(self.client, name), self)106 107 108class ResourceList(Resource):109    """ Represents a list of API objects """110 111    def __init__(self, client, group='', api_version='v1', base_kind='', kind=None, base_resource_lookup=None):112        self.client = client113        self.group = group114        self.api_version = api_version115        self.kind = kind or '{}List'.format(base_kind)116        self.base_kind = base_kind117        self.base_resource_lookup = base_resource_lookup118        self.__base_resource = None119 120    def base_resource(self):121        if self.__base_resource:122            return self.__base_resource123        elif self.base_resource_lookup:124            self.__base_resource = self.client.resources.get(**self.base_resource_lookup)125            return self.__base_resource126        elif self.base_kind:127            self.__base_resource = self.client.resources.get(group=self.group, api_version=self.api_version, kind=self.base_kind)128            return self.__base_resource129        return None130 131    def _items_to_resources(self, body):132        """ Takes a List body and return a dictionary with the following structure:133            {134                'api_version': str,135                'kind': str,136                'items': [{137                    'resource': Resource,138                    'name': str,139                    'namespace': str,140                }]141            }142        """143        if body is None:144            raise ValueError("You must provide a body when calling methods on a ResourceList")145 146        api_version = body['apiVersion']147        kind = body['kind']148        items = body.get('items')149        if not items:150            raise ValueError('The `items` field in the body must be populated when calling methods on a ResourceList')151 152        if self.kind != kind:153            raise ValueError('Methods on a {} must be called with a body containing the same kind. Received {} instead'.format(self.kind, kind))154 155        return {156            'api_version': api_version,157            'kind': kind,158            'items': [self._item_to_resource(item) for item in items]159        }160 161    def _item_to_resource(self, item):162        metadata = item.get('metadata', {})163        resource = self.base_resource()164        if not resource:165            api_version = item.get('apiVersion', self.api_version)166            kind = item.get('kind', self.base_kind)167            resource = self.client.resources.get(api_version=api_version, kind=kind)168        return {169            'resource': resource,170            'definition': item,171            'name': metadata.get('name'),172            'namespace': metadata.get('namespace')173        }174 175    def get(self, body, name=None, namespace=None, **kwargs):176        if name:177            raise ValueError('Operations on ResourceList objects do not support the `name` argument')178        resource_list = self._items_to_resources(body)179        response = copy.deepcopy(body)180 181        response['items'] = [182            item['resource'].get(name=item['name'], namespace=item['namespace'] or namespace, **kwargs).to_dict()183            for item in resource_list['items']184        ]185        return ResourceInstance(self, response)186 187    def delete(self, body, name=None, namespace=None, **kwargs):188        if name:189            raise ValueError('Operations on ResourceList objects do not support the `name` argument')190        resource_list = self._items_to_resources(body)191        response = copy.deepcopy(body)192 193        response['items'] = [194            item['resource'].delete(name=item['name'], namespace=item['namespace'] or namespace, **kwargs).to_dict()195            for item in resource_list['items']196        ]197        return ResourceInstance(self, response)198 199    def verb_mapper(self, verb, body, **kwargs):200        resource_list = self._items_to_resources(body)201        response = copy.deepcopy(body)202        response['items'] = [203            getattr(item['resource'], verb)(body=item['definition'], **kwargs).to_dict()204            for item in resource_list['items']205        ]206        return ResourceInstance(self, response)207 208    def create(self, *args, **kwargs):209        return self.verb_mapper('create', *args, **kwargs)210 211    def replace(self, *args, **kwargs):212        return self.verb_mapper('replace', *args, **kwargs)213 214    def patch(self, *args, **kwargs):215        return self.verb_mapper('patch', *args, **kwargs)216 217    def to_dict(self):218        return {219            '_type': 'ResourceList',220            'group': self.group,221            'api_version': self.api_version,222            'kind': self.kind,223            'base_kind': self.base_kind224        }225 226    def __getattr__(self, name):227        if self.base_resource():228            return getattr(self.base_resource(), name)229        return None230 231 232class Subresource(Resource):233    """ Represents a subresource of an API resource. This generally includes operations234        like scale, as well as status objects for an instantiated resource235    """236 237    def __init__(self, parent, **kwargs):238        self.parent = parent239        self.prefix = parent.prefix240        self.group = parent.group241        self.api_version = parent.api_version242        self.kind = kwargs.pop('kind')243        self.name = kwargs.pop('name')244        self.subresource = kwargs.pop('subresource', None) or self.name.split('/')[1]245        self.namespaced = kwargs.pop('namespaced', False)246        self.verbs = kwargs.pop('verbs', None)247        self.extra_args = kwargs248 249    #TODO(fabianvf): Determine proper way to handle differences between resources + subresources250    def create(self, body=None, name=None, namespace=None, **kwargs):251        name = name or body.get('metadata', {}).get('name')252        body = self.parent.client.serialize_body(body)253        if self.parent.namespaced:254            namespace = self.parent.client.ensure_namespace(self.parent, namespace, body)255        path = self.path(name=name, namespace=namespace)256        return self.parent.client.request('post', path, body=body, **kwargs)257 258    @property259    def urls(self):260        full_prefix = '{}/{}'.format(self.prefix, self.group_version)261        return {262            'full': '/{}/{}/{{name}}/{}'.format(full_prefix, self.parent.name, self.subresource),263            'namespaced_full': '/{}/namespaces/{{namespace}}/{}/{{name}}/{}'.format(full_prefix, self.parent.name, self.subresource)264        }265 266    def __getattr__(self, name):267        return partial(getattr(self.parent.client, name), self)268 269    def to_dict(self):270        d = {271            'kind': self.kind,272            'name': self.name,273            'subresource': self.subresource,274            'namespaced': self.namespaced,275            'verbs': self.verbs276        }277        d.update(self.extra_args)278        return d279 280 281class ResourceInstance(object):282    """ A parsed instance of an API resource. It exists solely to283        ease interaction with API objects by allowing attributes to284        be accessed with '.' notation.285    """286 287    def __init__(self, client, instance):288        self.client = client289        # If we have a list of resources, then set the apiVersion and kind of290        # each resource in 'items'291        kind = instance['kind']292        if kind.endswith('List') and 'items' in instance:293            kind = instance['kind'][:-4]294            if not instance['items']:295                instance['items'] = []296            for item in instance['items']:297                if 'apiVersion' not in item:298                    item['apiVersion'] = instance['apiVersion']299                if 'kind' not in item:300                    item['kind'] = kind301 302        self.attributes = self.__deserialize(instance)303        self.__initialised = True304 305    def __deserialize(self, field):306        if isinstance(field, dict):307            return ResourceField(params={308                k: self.__deserialize(v) for k, v in field.items()309            })310        elif isinstance(field, (list, tuple)):311            return [self.__deserialize(item) for item in field]312        else:313            return field314 315    def __serialize(self, field):316        if isinstance(field, ResourceField):317            return {318                k: self.__serialize(v) for k, v in field.__dict__.items()319            }320        elif isinstance(field, (list, tuple)):321            return [self.__serialize(item) for item in field]322        elif isinstance(field, ResourceInstance):323            return field.to_dict()324        else:325            return field326 327    def to_dict(self):328        return self.__serialize(self.attributes)329 330    def to_str(self):331        return repr(self)332 333    def __repr__(self):334        return "ResourceInstance[{}]:\n  {}".format(335            self.attributes.kind,336            '  '.join(yaml.safe_dump(self.to_dict()).splitlines(True))337        )338 339    def __getattr__(self, name):340        if not '_ResourceInstance__initialised' in self.__dict__:341            return super(ResourceInstance, self).__getattr__(name)342        return getattr(self.attributes, name)343 344    def __setattr__(self, name, value):345        if not '_ResourceInstance__initialised' in self.__dict__:346            return super(ResourceInstance, self).__setattr__(name, value)347        elif name in self.__dict__:348            return super(ResourceInstance, self).__setattr__(name, value)349        else:350            self.attributes[name] = value351 352    def __getitem__(self, name):353        return self.attributes[name]354 355    def __setitem__(self, name, value):356        self.attributes[name] = value357 358    def __dir__(self):359        return dir(type(self)) + list(self.attributes.__dict__.keys())360 361 362class ResourceField(object):363    """ A parsed instance of an API resource attribute. It exists364        solely to ease interaction with API objects by allowing365        attributes to be accessed with '.' notation366    """367 368    def __init__(self, params):369        self.__dict__.update(**params)370 371    def __repr__(self):372        return pformat(self.__dict__)373 374    def __eq__(self, other):375        return self.__dict__ == other.__dict__376 377    def __getitem__(self, name):378        return self.__dict__.get(name)379 380    # Here resource.items will return items if available or resource.__dict__.items function if not381    # resource.get will call resource.__dict__.get after attempting resource.__dict__.get('get')382    def __getattr__(self, name):383        return self.__dict__.get(name, getattr(self.__dict__, name, None))384 385    def __setattr__(self, name, value):386        self.__dict__[name] = value387 388    def __dir__(self):389        return dir(type(self)) + list(self.__dict__.keys())390 391    def __iter__(self):392        for k, v in self.__dict__.items():393            yield (k, v)394 395    def to_dict(self):396        return self.__serialize(self)397 398    def __serialize(self, field):399        if isinstance(field, ResourceField):400            return {401                k: self.__serialize(v) for k, v in field.__dict__.items()402            }403        if isinstance(field, (list, tuple)):404            return [self.__serialize(item) for item in field]405        return field406