Skip to content

Commit d5c1b71

Browse files
committed
remove duplicate code from resource
1 parent 005f624 commit d5c1b71

File tree

1 file changed

+1
-314
lines changed

1 file changed

+1
-314
lines changed

openshift/dynamic/resource.py

Lines changed: 1 addition & 314 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,4 @@
1-
import copy
2-
import yaml
3-
from functools import partial
4-
5-
from pprint import pformat
6-
7-
8-
class Resource(object):
9-
""" Represents an API resource type, containing the information required to build urls for requests """
10-
11-
def __init__(self, prefix=None, group=None, api_version=None, kind=None,
12-
namespaced=False, verbs=None, name=None, preferred=False, client=None,
13-
singularName=None, shortNames=None, categories=None, subresources=None, **kwargs):
14-
15-
if None in (api_version, kind, prefix):
16-
raise ValueError("At least prefix, kind, and api_version must be provided")
17-
18-
self.prefix = prefix
19-
self.group = group
20-
self.api_version = api_version
21-
self.kind = kind
22-
self.namespaced = namespaced
23-
self.verbs = verbs
24-
self.name = name
25-
self.preferred = preferred
26-
self.client = client
27-
self.singular_name = singularName or (name[:-1] if name else "")
28-
self.short_names = shortNames
29-
self.categories = categories
30-
self.subresources = {
31-
k: Subresource(self, **v) for k, v in (subresources or {}).items()
32-
}
33-
34-
self.extra_args = kwargs
35-
36-
def to_dict(self):
37-
return {
38-
'_type': 'Resource',
39-
'prefix': self.prefix,
40-
'group': self.group,
41-
'api_version': self.api_version,
42-
'kind': self.kind,
43-
'namespaced': self.namespaced,
44-
'verbs': self.verbs,
45-
'name': self.name,
46-
'preferred': self.preferred,
47-
'singular_name': self.singular_name,
48-
'short_names': self.short_names,
49-
'categories': self.categories,
50-
'subresources': {k: sr.to_dict() for k, sr in self.subresources.items()},
51-
'extra_args': self.extra_args,
52-
}
53-
54-
@property
55-
def group_version(self):
56-
if self.group:
57-
return '{}/{}'.format(self.group, self.api_version)
58-
return self.api_version
59-
60-
def __repr__(self):
61-
return '<{}({}/{})>'.format(self.__class__.__name__, self.group_version, self.name)
62-
63-
@property
64-
def urls(self):
65-
full_prefix = '{}/{}'.format(self.prefix, self.group_version)
66-
resource_name = self.name.lower()
67-
return {
68-
'base': '/{}/{}'.format(full_prefix, resource_name),
69-
'namespaced_base': '/{}/namespaces/{{namespace}}/{}'.format(full_prefix, resource_name),
70-
'full': '/{}/{}/{{name}}'.format(full_prefix, resource_name),
71-
'namespaced_full': '/{}/namespaces/{{namespace}}/{}/{{name}}'.format(full_prefix, resource_name)
72-
}
73-
74-
def path(self, name=None, namespace=None):
75-
url_type = []
76-
path_params = {}
77-
if self.namespaced and namespace:
78-
url_type.append('namespaced')
79-
path_params['namespace'] = namespace
80-
if name:
81-
url_type.append('full')
82-
path_params['name'] = name
83-
else:
84-
url_type.append('base')
85-
return self.urls['_'.join(url_type)].format(**path_params)
86-
87-
def __getattr__(self, name):
88-
if name in self.subresources:
89-
return self.subresources[name]
90-
return partial(getattr(self.client, name), self)
1+
from kubernetes.dynamic.resource import Resource, Subresource, ResourceField # noqa
912

923

934
class ResourceList(Resource):
@@ -110,92 +21,6 @@ def base_resource(self):
11021
return self.__base_resource
11122
return None
11223

113-
def _items_to_resources(self, body):
114-
""" Takes a List body and return a dictionary with the following structure:
115-
{
116-
'api_version': str,
117-
'kind': str,
118-
'items': [{
119-
'resource': Resource,
120-
'name': str,
121-
'namespace': str,
122-
}]
123-
}
124-
"""
125-
if body is None:
126-
raise ValueError("You must provide a body when calling methods on a ResourceList")
127-
128-
api_version = body['apiVersion']
129-
kind = body['kind']
130-
items = body.get('items')
131-
if not items:
132-
raise ValueError('The `items` field in the body must be populated when calling methods on a ResourceList')
133-
134-
if self.kind != kind:
135-
raise ValueError('Methods on a {} must be called with a body containing the same kind. Received {} instead'.format(self.kind, kind))
136-
137-
return {
138-
'api_version': api_version,
139-
'kind': kind,
140-
'items': [self._item_to_resource(item) for item in items]
141-
}
142-
143-
def _item_to_resource(self, item):
144-
metadata = item.get('metadata', {})
145-
resource = self.base_resource()
146-
if not resource:
147-
api_version = item.get('apiVersion', self.api_version)
148-
kind = item.get('kind', self.base_kind)
149-
resource = self.client.resources.get(api_version=api_version, kind=kind)
150-
return {
151-
'resource': resource,
152-
'definition': item,
153-
'name': metadata.get('name'),
154-
'namespace': metadata.get('namespace')
155-
}
156-
157-
def get(self, body=None, name=None, namespace=None, **kwargs):
158-
if name:
159-
raise ValueError('Operations on ResourceList objects do not support the `name` argument')
160-
resource_list = self._items_to_resources(body)
161-
response = copy.deepcopy(body)
162-
163-
response['items'] = [
164-
item['resource'].get(name=item['name'], namespace=item['namespace'] or namespace, **kwargs).to_dict()
165-
for item in resource_list['items']
166-
]
167-
return ResourceInstance(self, response)
168-
169-
def delete(self, body=None, name=None, namespace=None, **kwargs):
170-
if name:
171-
raise ValueError('Operations on ResourceList objects do not support the `name` argument')
172-
resource_list = self._items_to_resources(body)
173-
response = copy.deepcopy(body)
174-
175-
response['items'] = [
176-
item['resource'].delete(name=item['name'], namespace=item['namespace'] or namespace, **kwargs).to_dict()
177-
for item in resource_list['items']
178-
]
179-
return ResourceInstance(self, response)
180-
181-
def verb_mapper(self, verb, body=None, **kwargs):
182-
resource_list = self._items_to_resources(body)
183-
response = copy.deepcopy(body)
184-
response['items'] = [
185-
getattr(item['resource'], verb)(body=item['definition'], **kwargs).to_dict()
186-
for item in resource_list['items']
187-
]
188-
return ResourceInstance(self, response)
189-
190-
def create(self, *args, **kwargs):
191-
return self.verb_mapper('create', *args, **kwargs)
192-
193-
def replace(self, *args, **kwargs):
194-
return self.verb_mapper('replace', *args, **kwargs)
195-
196-
def patch(self, *args, **kwargs):
197-
return self.verb_mapper('patch', *args, **kwargs)
198-
19924
def apply(self, *args, **kwargs):
20025
return self.verb_mapper('apply', *args, **kwargs)
20126

@@ -215,54 +40,6 @@ def __getattr__(self, name):
21540
return None
21641

21742

218-
class Subresource(Resource):
219-
""" Represents a subresource of an API resource. This generally includes operations
220-
like scale, as well as status objects for an instantiated resource
221-
"""
222-
223-
def __init__(self, parent, **kwargs):
224-
self.parent = parent
225-
self.prefix = parent.prefix
226-
self.group = parent.group
227-
self.api_version = parent.api_version
228-
self.kind = kwargs.pop('kind')
229-
self.name = kwargs.pop('name')
230-
self.subresource = self.name.split('/')[1]
231-
self.namespaced = kwargs.pop('namespaced', False)
232-
self.verbs = kwargs.pop('verbs', None)
233-
self.extra_args = kwargs
234-
235-
#TODO(fabianvf): Determine proper way to handle differences between resources + subresources
236-
def create(self, body=None, name=None, namespace=None, **kwargs):
237-
name = name or body.get('metadata', {}).get('name')
238-
body = self.parent.client.serialize_body(body)
239-
if self.parent.namespaced:
240-
namespace = self.parent.client.ensure_namespace(self.parent, namespace, body)
241-
path = self.path(name=name, namespace=namespace)
242-
return self.parent.client.request('post', path, body=body, **kwargs)
243-
244-
@property
245-
def urls(self):
246-
full_prefix = '{}/{}'.format(self.prefix, self.group_version)
247-
return {
248-
'full': '/{}/{}/{{name}}/{}'.format(full_prefix, self.parent.name, self.subresource),
249-
'namespaced_full': '/{}/namespaces/{{namespace}}/{}/{{name}}/{}'.format(full_prefix, self.parent.name, self.subresource)
250-
}
251-
252-
def __getattr__(self, name):
253-
return partial(getattr(self.parent.client, name), self)
254-
255-
def to_dict(self):
256-
return {
257-
'kind': self.kind,
258-
'name': self.name,
259-
'subresource': self.subresource,
260-
'namespaced': self.namespaced,
261-
'verbs': self.verbs,
262-
'extra_args': self.extra_args,
263-
}
264-
265-
26643
class ResourceInstance(object):
26744
""" A parsed instance of an API resource. It exists solely to
26845
ease interaction with API objects by allowing attributes to
@@ -285,93 +62,3 @@ def __init__(self, client, instance):
28562

28663
self.attributes = self.__deserialize(instance)
28764
self.__initialised = True
288-
289-
def __deserialize(self, field):
290-
if isinstance(field, dict):
291-
return ResourceField(**{
292-
k: self.__deserialize(v) for k, v in field.items()
293-
})
294-
elif isinstance(field, (list, tuple)):
295-
return [self.__deserialize(item) for item in field]
296-
else:
297-
return field
298-
299-
def __serialize(self, field):
300-
if isinstance(field, ResourceField):
301-
return {
302-
k: self.__serialize(v) for k, v in field.__dict__.items()
303-
}
304-
elif isinstance(field, (list, tuple)):
305-
return [self.__serialize(item) for item in field]
306-
elif isinstance(field, ResourceInstance):
307-
return field.to_dict()
308-
else:
309-
return field
310-
311-
def to_dict(self):
312-
return self.__serialize(self.attributes)
313-
314-
def to_str(self):
315-
return repr(self)
316-
317-
def __repr__(self):
318-
return "ResourceInstance[{}]:\n {}".format(
319-
self.attributes.kind,
320-
' '.join(yaml.safe_dump(self.to_dict()).splitlines(True))
321-
)
322-
323-
def __getattr__(self, name):
324-
if not '_ResourceInstance__initialised' in self.__dict__:
325-
return super(ResourceInstance, self).__getattr__(name)
326-
return getattr(self.attributes, name)
327-
328-
def __setattr__(self, name, value):
329-
if not '_ResourceInstance__initialised' in self.__dict__:
330-
return super(ResourceInstance, self).__setattr__(name, value)
331-
elif name in self.__dict__:
332-
return super(ResourceInstance, self).__setattr__(name, value)
333-
else:
334-
self.attributes[name] = value
335-
336-
def __getitem__(self, name):
337-
return self.attributes[name]
338-
339-
def __setitem__(self, name, value):
340-
self.attributes[name] = value
341-
342-
def __dir__(self):
343-
return dir(type(self)) + list(self.attributes.__dict__.keys())
344-
345-
346-
class ResourceField(object):
347-
""" A parsed instance of an API resource attribute. It exists
348-
solely to ease interaction with API objects by allowing
349-
attributes to be accessed with '.' notation
350-
"""
351-
352-
def __init__(self, **kwargs):
353-
self.__dict__.update(kwargs)
354-
355-
def __repr__(self):
356-
return pformat(self.__dict__)
357-
358-
def __eq__(self, other):
359-
return self.__dict__ == other.__dict__
360-
361-
def __getitem__(self, name):
362-
return self.__dict__.get(name)
363-
364-
# Here resource.items will return items if available or resource.__dict__.items function if not
365-
# resource.get will call resource.__dict__.get after attempting resource.__dict__.get('get')
366-
def __getattr__(self, name):
367-
return self.__dict__.get(name, getattr(self.__dict__, name, None))
368-
369-
def __setattr__(self, name, value):
370-
self.__dict__[name] = value
371-
372-
def __dir__(self):
373-
return dir(type(self)) + list(self.__dict__.keys())
374-
375-
def __iter__(self):
376-
for k, v in self.__dict__.items():
377-
yield (k, v)

0 commit comments

Comments
 (0)