-
Notifications
You must be signed in to change notification settings - Fork 185
Implement port forwarding. #210
Implement port forwarding. #210
Conversation
Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please follow instructions at https://git.k8s.io/community/CLA.md#the-contributor-license-agreement to sign the CLA. It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify. Thanks.
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
Welcome @iciclespider! |
Codecov Report
@@ Coverage Diff @@
## master #210 +/- ##
=======================================
Coverage 92.37% 92.37%
=======================================
Files 13 13
Lines 1613 1613
=======================================
Hits 1490 1490
Misses 123 123 Continue to review full report at Codecov.
|
/check-cla |
/check-cla |
/assign @mbohlool |
/assign |
thanks for the PR. it looks good overall. could you please split this PR into two PRs, the first PR is for refactoring the existing code to make it reusable for port forwarding, the second PR is for port forwarding. this makes it easier to review, and also easier to rollback or fix the portforward PR in case it has any issue. |
1dd2756
to
74d0e29
Compare
@yliaog I rebased this PR against the merged master with the refactored code. I also updated the PR in kubernetes-client/python#1237 with a |
stream/ws_client.py
Outdated
ports = [] | ||
for key, value in query_params: | ||
if key == 'ports': | ||
for port in value.split(','): | ||
try: | ||
port = int(port) | ||
if not (0 < port < 65536): | ||
raise ValueError | ||
ports.append(port) | ||
except ValueError: | ||
raise ApiValueError("Invalid port number `" + str(port) + "`") | ||
if not ports: | ||
raise ApiValueError("Missing required parameter `ports`") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this logic can be abstracted out to another function with a simpler implementation like below.
def _valid_ports(ports):
_ports = []
for p in ports:
try:
p = int(p)
except ValueError:
raise ApiValueError("Invalid port number `{}`. Port number must be an integer".format(p))
if (p < 0) or (p > 65536):
raise ApiValueError("Invalid port number `{}`. Port number must be between 0 and 65536".format(p))
_ports.append(p)
return _ports
ports = query_params.get("ports", "")
if not ports:
raise ApiValueError("Missing required parameter `ports`")
ports = ports.split(",")
ports = _valid_ports(ports)
# There is no check required here, since any unwanted port value would be checked in _valid_ports itself.
# We can be assured that we get a non-empty port list here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line will not work:
ports = query_params.get("ports", "")
query_params is not a dictionary, it a list of two element tuples.
/assign |
raise ValueError("Invalid port number") | ||
return self.ports[port_number].socket | ||
|
||
def error(self, port_number): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please change it to error_channel or something more descriptive. 'error' looks like error/exception, which is confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just pushed the change to error_channel
.
However, a little clarification. This is not really a "channel" that can send and receive data. From the client perspective, it is read only and cannot be written to. And the only time it is written to by kubernetes is when there is an error/exception case on the server and that port is unusable. It is written as the parting message.
func (h *websocketStreamHandler) portForward(p *websocketStreamPair) {
defer p.dataStream.Close()
defer p.errorStream.Close()
klog.V(5).Infof("(conn=%p) invoking forwarder.PortForward for port %d", h.conn, p.port)
err := h.forwarder.PortForward(h.pod, h.uid, p.port, p.dataStream)
klog.V(5).Infof("(conn=%p) done invoking forwarder.PortForward for port %d", h.conn, p.port)
if err != nil {
msg := fmt.Errorf("error forwarding port %d to pod %s, uid %v: %v", p.port, h.pod, h.uid, err)
runtime.HandleError(msg)
fmt.Fprint(p.errorStream, msg.Error())
}
}
stream/ws_client.py
Outdated
def _proxy(self): | ||
channel_ports = [] | ||
channel_initialized = [] | ||
python_ports = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about call it local_port?
* Rename `_Port.error` to be `_Port.error_channel`. * Correct comment about where setsockopt is being called. * Add comments clarifying why the double call to the same methods to setup channel information. * Allow for ports specified with both local and remote port numbers.
…t and a remote port.
@yliaog I submitted another commit for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yliaog I pushed a commit that addresses the comments.
d22b840
to
27d3369
Compare
27d3369
to
5d39d0d
Compare
thanks for the PR, really appreciate it. /lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: iciclespider, yliaog The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Looking for feed back if this looks on track as a way of implementing port forwarding.
See the example and unittests added in this PR kubernetes-client/python#1237 for usage.
kubernetes-client/python#166