Implementation for /exec using websocket#120
Conversation
mbohlool
left a comment
There was a problem hiding this comment.
Thank you for taking care of this. It is great to see progress on exec support. We just need to make sure it supports all authentication methods and also let a way to interactively communicate. More info at each individual comments.
| command=exec_command, | ||
| stderr=False, stdin=False, | ||
| stdout=True, tty=False) | ||
| print('EXEC response : %s' % resp) |
There was a problem hiding this comment.
Can we do better instead of printing out the output? If we know what output we expect, we can check that or even part of the output.
There was a problem hiding this comment.
ack. let's make sure we get 3 lines back
| Makes the HTTP request using RESTClient. | ||
| """ | ||
| if url.endswith('/exec') and method == "GET": | ||
| return ws_client.GET(self.config, |
There was a problem hiding this comment.
I maybe not familiar enough with exec command, but does it let interactive connection with the host? I mean can a script send something, then read the result, then decide what next command to send, etc.? In that case, should we return some kind of client allows that instead of command result?
There was a problem hiding this comment.
Nope. the "/exec" is one shot only.
|
|
||
|
|
||
| class WSClient: | ||
| def __init__(self, configuration, url): |
There was a problem hiding this comment.
what does configuration used for?
There was a problem hiding this comment.
will add code for getting the ssl configuration options shortly
| on_message=self.on_message, | ||
| on_error=self.on_error, | ||
| on_close=self.on_close, | ||
| header=[]) |
There was a problem hiding this comment.
should we send authorization token?
| self.ws.on_open = self.on_open | ||
| sslopt = None | ||
| if url.startswith('wss://'): | ||
| sslopt = {"cert_reqs": ssl.CERT_NONE} |
There was a problem hiding this comment.
Should we use ssl cert (and client cert and key) from configuration?
| from six.moves.urllib.parse import quote_plus | ||
|
|
||
|
|
||
| class WSClient: |
There was a problem hiding this comment.
I would suggest testing this (even manually) on GCE, GCP and minikube to make sure we cover a good range of authentication methods.
There was a problem hiding this comment.
@mbohlool i do not have any kind of access to GCE or GCP. so i'll rely on your help with testing these
| else: | ||
| time.sleep(1) | ||
|
|
||
| # exec_command = 'uptime' |
There was a problem hiding this comment.
remove the commented out codes.
|
@dims Sorry, I missed [WIP] in the title. Please ignore my comments until you are done with this. |
50919ab to
aec77d5
Compare
7e9adec to
df1f320
Compare
|
Tested by adding a TLS proxy in front of the K8S API |
|
I think I had a comment about interactiveness of this solution but I cannot find it. The solution does not let the script to interactive with the server. Like open connection, run command, get output/error, run another command, etc... Is it possible to support that? |
| auth_settings=auth_settings, | ||
| callback=params.get('callback'), | ||
| _return_http_data_only=params.get('_return_http_data_only'), | ||
| _preload_content=params.get('_preload_content', True), |
There was a problem hiding this comment.
What is the purpose of this change? I mean if we need the flag to be false, we should be able to do it in the client, where we override /exec path, right? This is the only place that can be overwritten by the code generation.
There was a problem hiding this comment.
the _preload_content tries to coerce the response into a json. would be great to find another spot to put this.
There was a problem hiding this comment.
api_client.py, if you move your changes out of request method into __call_api itself, you have access to preload_content flag. You can even branch before calling request to make sure the preload_content flag does not have any effect. e.g. if exec, do stuff and return, else do normal stuff of calling request and preload_content stuff. We should not change generated files as much as possible.
There was a problem hiding this comment.
i figured out a way to avoid this change, please see latest update
|
@dims This is awesome work! |
|
@chekolyn kudos to you for finding a way :) |
|
@mbohlool : the API definition does not allow for interactivity |
you can return an object instead of an string, right? the object can have methods to work interactively with the open connection. We do that if _preload_content flag is false (returning a connection object). Having said that, instead of checking for "/exec", do you think it maybe better to pass a parameter like preload_content? something like _upgrade_connection, or _use_websocket. Then user can decide if they want to use websocket for an api call, and also we don't need to change client if anything other than /exec uses websocket (in future). |
c787a71 to
36e95fe
Compare
|
@mbohlool i am happy with what we have here in this PR now. i am probably missing something about interactivity idea that you have brought up a few times. I hope we can merge this and then iterate on it. I have added a FIXME for the "/exec" check in one spot, so we can deal with that later. Hope this is good enough. |
| pass | ||
|
|
||
|
|
||
| WSResponse = collections.namedtuple('WSResponse', ['data']) |
There was a problem hiding this comment.
I like the idea of WSResponse. make it easier to add interactivity later (which I am fine with).
There was a problem hiding this comment.
Does WSResponse needs an 'status'? and 'reason' in case of an error?
| stderr=False, stdin=False, | ||
| stdout=True, tty=False) | ||
| print('EXEC response : %s' % resp) | ||
| self.assertEqual(3, len(resp.splitlines())) |
There was a problem hiding this comment.
resp is a WSResponse, right? should we check it's data field?
There was a problem hiding this comment.
@mbohlool the WSResponse is already unwrapped and the resp is just the string. If it were WSResponse the assert would fail :)
There was a problem hiding this comment.
That was my problem too, why the assert didn't fail. I am just trying to run it locally (just curious to see it), then I will merge. Thanks.
There was a problem hiding this comment.
I think I got the place that WSResponse is unwrapped, now I have more questions about that, e.g. why we don't have 'status' in WSResponse? rest.py checks for an status and raise an exception if status is not in OK range.
|
@dims OK. I am fine with this change (except one comment above) and we can merge and iterate on it later. It is in a pretty good shape. |
inspired by the POC from @chekolyn * Adds a new requirement on websocket-client * Add a new class WSClient that uses WebSocketApp from the websocket-client. * Make sure we pass Authorization header * Make sure we honor the SSL settings in configuration * Some of the code will get overwritten when we generate fresh classes from swagger definition. To remind us added a e2e test so we don't lose the changes * Added a new configuration option to enable/disable failures when hostnames in certificates don't match Fixes kubernetes-client#58
36e95fe to
066bba1
Compare
Codecov Report@@ Coverage Diff @@
## master #120 +/- ##
=======================================
Coverage 94.46% 94.46%
=======================================
Files 9 9
Lines 668 668
=======================================
Hits 631 631
Misses 37 37Continue to review full report at Codecov.
|
|
@mbohlool : there is no rest.py in the call stack. api_client.py calls ws_client.py directly so we don't need the "status" field. |
|
@dims Where we unwrap WSResponse then? |
This commit reapplies PR kubernetes-client#120 that was removed by kubernetes-client#353. Below is the original commit message. inspired by the POC from @chekolyn * Adds a new requirement on websocket-client * Add a new class WSClient that uses WebSocketApp from the websocket-client. * Make sure we pass Authorization header * Make sure we honor the SSL settings in configuration * Some of the code will get overwritten when we generate fresh classes from swagger definition. To remind us added a e2e test so we don't lose the changes * Added a new configuration option to enable/disable failures when hostnames in certificates don't match Fixes kubernetes-client#58 Fixes kubernetes-client#409
This commit reapplies PR kubernetes-client#120 that was removed by kubernetes-client#353. Below is the original commit message. inspired by the POC from @chekolyn * Adds a new requirement on websocket-client * Add a new class WSClient that uses WebSocketApp from the websocket-client. * Make sure we pass Authorization header * Make sure we honor the SSL settings in configuration * Some of the code will get overwritten when we generate fresh classes from swagger definition. To remind us added a e2e test so we don't lose the changes * Added a new configuration option to enable/disable failures when hostnames in certificates don't match Fixes kubernetes-client#58 Fixes kubernetes-client#409
inspired by the POC from @chekolyn
Reference #58 (by mbohlool: Removed Fix. This PR is definitely a huge step toward the right direction, but I don't call 58 done yet.)