Repository navigation
Dynamic Client - #56
Conversation
Codecov Report
@@ Coverage Diff @@
## master #56 +/- ##
=======================================
Coverage 91.85% 91.85%
=======================================
Files 11 11
Lines 994 994
=======================================
Hits 913 913
Misses 81 81Continue to review full report at Codecov.
|
roycaihw
left a comment
There was a problem hiding this comment.
Thanks for the contribution! I left some comments :)
| from kubernetes.client.api_client import ApiClient | ||
| from kubernetes.client.rest import ApiException | ||
|
|
||
| from openshift.dynamic.exceptions import ResourceNotFoundError, ResourceNotUniqueError, api_exception |
There was a problem hiding this comment.
The module path needs to be changed
| def group_version(self): | ||
| if self.group: | ||
| return '{}/{}'.format(self.group, self.api_version) | ||
| return self.api_version |
There was a problem hiding this comment.
nit: '/'.join(filter(None, [self.group, self.api_version])) like Line112?
| kind: The kind of the resource | ||
| arbitrary arguments (see below), in random order | ||
|
|
||
| The arbitrary arguments can be any valid attribute for an openshift.dynamic.Resource object |
| def path(self, name=None, namespace=None): | ||
| url_type = [] | ||
| path_params = {} | ||
| if self.namespaced and namespace: |
There was a problem hiding this comment.
should we catch the case where namespaced is set but namespace is None?
or does ensure_namespace rule out that case already?
There was a problem hiding this comment.
if namespaced is set and namespace is None, then we can still construct a valid path (api/v1/pods for example)
| 'base': '/{}/{}'.format(full_prefix, self.name), | ||
| 'namespaced_base': '/{}/namespaces/{{namespace}}/{}'.format(full_prefix, self.name), | ||
| 'full': '/{}/{}/{{name}}'.format(full_prefix, self.name), | ||
| 'namespaced_full': '/{}/namespaces/{{namespace}}/{}/{{name}}'.format(full_prefix, self.name) |
There was a problem hiding this comment.
can we determine if the resource is cluster-scoped or namespaced using self.namespaced here and only return two urls base and full?
There was a problem hiding this comment.
you can access namespaced resources without providing a namespace (ie, list all pods in every namespace, etc), so I don't think we should
| content_type = self.client.\ | ||
| select_header_content_type(['application/json-patch+json', 'application/merge-patch+json', 'application/strategic-merge-patch+json']) | ||
|
|
||
| return self.request('patch', path, body=body, content_type=content_type) |
| @meta_request | ||
| def delete(self, resource, name=None, namespace=None, label_selector=None, field_selector=None, **kwargs): | ||
| if not (name or label_selector or field_selector): | ||
| raise ValueError("At least one of name|label_selector|field_selector is required") |
There was a problem hiding this comment.
From the design doc, the following actions are marked as TODO: update, watch, deletecollection, proxy. I see update has been implemented (replace). I'm just curious if we don't want to have deletecollection for the first iteration.
There was a problem hiding this comment.
Yeah, the only reason I didn't yet implement deletecollection is that I'm not familiar with it, would love to get it in
There was a problem hiding this comment.
I think it's fine to add deletecollection as a followup
| full_prefix = '{}/{}'.format(self.prefix, self.group_version) | ||
| return { | ||
| 'full': '/{}/{}/{{name}}/{}'.format(full_prefix, self.parent.name, self.subresource), | ||
| 'namespaced_full': '/{}/namespaces/{{namespace}}/{}/{{name}}/{}'.format(full_prefix, self.parent.name, self.subresource) |
There was a problem hiding this comment.
ditto: if we could check self.namespaced and combine the two urls into one
| def _load_server_info(self): | ||
| self.__version = {'kubernetes': load_json(self.request('get', '/version'))} | ||
| try: | ||
| self.__version['openshift'] = load_json(self.request('get', '/version/openshift')) |
There was a problem hiding this comment.
I can go ahead and take it out, this belongs in openshift specific code not upstream
|
@roycaihw rebased to keep up to date with branch, any additional comments? |
|
ping @roycaihw |
|
We've also had some community interest in the other discussed dynamic implementation (the one that uses the OpenAPI spec for discovery), I haven't had time to look into it again yet but I'm hoping I'll get a chance for a second pass soon. |
roycaihw
left a comment
There was a problem hiding this comment.
It looks good to me in general. I left one nit and one question for clarification.
| return self.request('post', path, body=body, **kwargs) | ||
|
|
||
| @meta_request | ||
| def delete(self, resource, name=None, namespace=None, label_selector=None, field_selector=None, **kwargs): |
There was a problem hiding this comment.
k8s delete requests can have body of type DeleteOptions as alternative of some query parameters, although it's not a good practice to send delete request with body I suppose.
There was a problem hiding this comment.
Good call, this should definitely support delete options.
| self.client = client | ||
| self.configuration = client.configuration | ||
| self._load_server_info() | ||
| self.__resources = ResourceContainer(self.parse_api_groups()) |
There was a problem hiding this comment.
IIUC people need to create another ResourceContainer with client.parse_api_groups() when they want to re-discover new CRDs and aggregated APIs - self.__resources only records the resources that exist when the dynamic client gets created - is that correct?
There was a problem hiding this comment.
You're right, I'll break this stuff out into a discovery() method so that it can be called repeatedly
There was a problem hiding this comment.
I'll break this stuff out into a discovery() method so that it can be called repeatedly
This can be done in a separate PR.
|
I think this PR will be good to go after @fabianvf adds This dynamic client should also be e2e tested (something like test_client.py), probably in another PR. |
| from functools import partial | ||
| import yaml | ||
| from pprint import pformat | ||
| from kubernetes.client.rest import ApiException |
There was a problem hiding this comment.
better to have the import above be ordered?
There was a problem hiding this comment.
Yeah I can clean that up
| 503: ServiceUnavailableError, | ||
| 504: ServerTimeoutError, | ||
| }.get(e.status, DynamicApiError)(e, tb) | ||
|
|
There was a problem hiding this comment.
if just want to add traceback, why do you need to add all these specific HTTP errors (BadRequestError, UnauthorizedError, ...)? Is DynamicApiError alone not enough for the purpose?
There was a problem hiding this comment.
I prefer to expose errors that correspond to the HTTP errors we receive, it provides a cleaner way to handle errors when consuming the client (since you can just catch a NotFoundError rather than catching a DynamicApiError and checking the status code). Catching a generic DynamicApiError and checking the code still works if the user prefers to handle it that way.
| header_params['Accept'] = self.client.select_header_accept([ | ||
| 'application/json', | ||
| 'application/yaml', | ||
| 'application/vnd.kubernetes.protobuf' |
There was a problem hiding this comment.
i don't think protobuf is supported in python client, better to remove it here.
|
sorry, I've been on PTO + planning meetings the last few weeks, I'll try and carve up some time to clean all this up sometime this week or next |
|
We're actually doing a little bit of a rework of the dynamic client in openshift, driven by user feedback. I think I might hold off a bit on updating this PR, so that I can bring those improvements in |
|
@fabianvf I see that you're working on a dynamic client for OpenShift. Are there any plans to backport the dynamic client to this library? |
|
Yes, there's actually almost nothing specific to openshift in the client. There was a lot of churn so I put this PR on hold while we stabilized a bit, now it's mostly a matter of finding the time to bring in the relevant new stuff. |
|
Issues go stale after 90d of inactivity. If this issue is safe to close now please do so with Send feedback to sig-testing, kubernetes/test-infra and/or fejta. |
|
/remove-lifecycle stale
|
|
/lgtm Thanks a lot for working on this! Will merge after the test failure gets resolved kubernetes-client/python#919 |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: fabianvf, roycaihw The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
i have a meta request, could you please squash the commits to a few logical commits? |
61d676c to
53c4cb2
Compare
|
/lgtm |
Still TODO: