Skip to content

Implementation for /exec using websocket #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 7, 2017

Conversation

dims
Copy link
Collaborator

@dims dims commented Feb 1, 2017

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

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.)

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Feb 1, 2017
Copy link
Contributor

@mbohlool mbohlool left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack. let's make sure we get 3 lines back

@@ -343,6 +344,13 @@ def request(self, method, url, query_params=None, headers=None,
"""
Makes the HTTP request using RESTClient.
"""
if url.endswith('/exec') and method == "GET":
return ws_client.GET(self.config,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. the "/exec" is one shot only.



class WSClient:
def __init__(self, configuration, url):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does configuration used for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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=[])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we send authorization token?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep for sure :)

self.ws.on_open = self.on_open
sslopt = None
if url.startswith('wss://'):
sslopt = {"cert_reqs": ssl.CERT_NONE}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use ssl cert (and client cert and key) from configuration?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right. yes.

from six.moves.urllib.parse import quote_plus


class WSClient:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest testing this (even manually) on GCE, GCP and minikube to make sure we cover a good range of authentication methods.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the commented out codes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@mbohlool
Copy link
Contributor

mbohlool commented Feb 1, 2017

@dims Sorry, I missed [WIP] in the title. Please ignore my comments until you are done with this.

@dims dims force-pushed the support-exec-calls branch 2 times, most recently from 50919ab to aec77d5 Compare February 2, 2017 15:05
@dims dims changed the title [WIP] implementation for /exec using websocket Implementation for /exec using websocket Feb 2, 2017
@dims dims force-pushed the support-exec-calls branch 2 times, most recently from 7e9adec to df1f320 Compare February 2, 2017 22:32
@dims
Copy link
Collaborator Author

dims commented Feb 2, 2017

Tested by adding a TLS proxy in front of the K8S API

sudo make-ssl-cert /usr/share/ssl-cert/ssleay.cnf example.pem
hitch --frontend=[*]:8443 --backend=[localhost]:8080 example.pem

@mbohlool
Copy link
Contributor

mbohlool commented Feb 2, 2017

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?

@@ -1007,7 +1007,7 @@ def connect_get_namespaced_pod_exec_with_http_info(self, name, namespace, **kwar
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),
Copy link
Contributor

@mbohlool mbohlool Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the _preload_content tries to coerce the response into a json. would be great to find another spot to put this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i figured out a way to avoid this change, please see latest update

@chekolyn
Copy link

chekolyn commented Feb 2, 2017

@dims This is awesome work!

@dims
Copy link
Collaborator Author

dims commented Feb 3, 2017

@chekolyn kudos to you for finding a way :)

@dims
Copy link
Collaborator Author

dims commented Feb 3, 2017

@mbohlool : the API definition does not allow for interactivity

@mbohlool
Copy link
Contributor

mbohlool commented Feb 3, 2017

@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).

@dims dims force-pushed the support-exec-calls branch 2 times, most recently from c787a71 to 36e95fe Compare February 3, 2017 03:17
@dims
Copy link
Collaborator Author

dims commented Feb 3, 2017

@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'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of WSResponse. make it easier to add interactivity later (which I am fine with).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resp is a WSResponse, right? should we check it's data field?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mbohlool the WSResponse is already unwrapped and the resp is just the string. If it were WSResponse the assert would fail :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@mbohlool
Copy link
Contributor

mbohlool commented Feb 3, 2017

@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.

@mbohlool mbohlool self-assigned this Feb 3, 2017
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
@dims dims force-pushed the support-exec-calls branch from 36e95fe to 066bba1 Compare February 7, 2017 21:50
@codecov-io
Copy link

Codecov Report

Merging #120 into master will not impact coverage.

@@           Coverage Diff           @@
##           master     #120   +/-   ##
=======================================
  Coverage   94.46%   94.46%           
=======================================
  Files           9        9           
  Lines         668      668           
=======================================
  Hits          631      631           
  Misses         37       37

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 192b67c...066bba1. Read the comment docs.

@dims
Copy link
Collaborator Author

dims commented Feb 7, 2017

@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.

https://www.evernote.com/shard/s412/sh/3a91433b-7125-4393-8506-2794b4565d2e/7ac292e184053c6a/res/ef38c1d4-214a-454b-b869-b2816c18087e/skitch.png

@mbohlool
Copy link
Contributor

mbohlool commented Feb 7, 2017

@dims Where we unwrap WSResponse then?

@dims
Copy link
Collaborator Author

dims commented Feb 7, 2017

@mbohlool mbohlool merged commit cfd34ef into kubernetes-client:master Feb 7, 2017
@mbohlool mbohlool mentioned this pull request Feb 9, 2017
dulek pushed a commit to dulek/client-python that referenced this pull request Dec 5, 2017
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
dulek pushed a commit to dulek/client-python that referenced this pull request Dec 5, 2017
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants