Uh oh!
There was an error while loading. Please reload this page.
implement pod exec websockets v5 - #2486
Conversation
k8s-ci-robot
commented
Dec 14, 2025
Welcome @aojea! |
k8s-triage-robot
commented
Mar 14, 2026
The Kubernetes project currently lacks enough contributors to adequately respond to all PRs. This bot triages PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /lifecycle stale |
k8s-triage-robot
commented
Apr 13, 2026
The Kubernetes project currently lacks enough active contributors to adequately respond to all PRs. This bot triages PRs according to the following rules:
You can:
Please send feedback to sig-contributor-experience at kubernetes/community. /lifecycle rotten |
seans3
commented
Apr 23, 2026
/remove-lifecycle rotten |
seans3
commented
Apr 23, 2026
/assign |
seans3
commented
May 6, 2026
/lifecycle frozen |
k8s-ci-robot
commented
May 6, 2026
@seans3: The DetailsIn response to this:
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-sigs/prow repository. |
seans3
left a comment
There was a problem hiding this comment.
This looks good and substantially complete.
One high-level observation: I believe falling back to v4.channel.k8s.io is the right approach for backwards compatibility, but we should probably note that for clusters < v1.30, the client will silently revert to the broken behavior and hang on EOF.
I've left a few minor inline comments mostly about tests and constants.
| del self._channels[channel] | ||
| return ret | ||
| if channel in self._closed_channels: |
There was a problem hiding this comment.
Do we need this same short-circuiting code in peek_channel and read_channel ?
| """Close a channel (v5 protocol only).""" | ||
| if self.subprotocol != V5_CHANNEL_PROTOCOL: | ||
| return | ||
| data = bytes([255, channel]) |
There was a problem hiding this comment.
Should we define a constant (e.g. CLOSE_CHANNEL = 255 or V5_HALF_CLOSE = 255) for this magic number, and reference the constant?
| mock_ws.send.assert_not_called() | ||
| def test_update_receives_close_v5(self): |
There was a problem hiding this comment.
Should we add an additional unit test to verify how readline_channel handles a closed channel with leftover data?
While the current unit tests cover the parsing of the close signal itself, the new logic you added inside readline_channel—which flushes the remaining buffer even if it lacks a newline—is currently untested. Having a test that asserts readline_channel successfully flushes leftover data (e.g. "hello") and then returns an empty string on the subsequent call would ensure this specific edge-case logic is protected from future regressions.
There was a problem hiding this comment.
added tests also for read_channel and peek_channel to validate channels are drained and follow the expected semantics
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
seans3
commented
May 6, 2026
/assign @yliaog |
| # In Py3, iterating bytes gives int, but indexing bytes gives int. | ||
| # websocket-client frame.data might be bytes. | ||
| if channel == 255 and self.subprotocol == V5_CHANNEL_PROTOCOL: # v5 CLOSE |
There was a problem hiding this comment.
better to replace 255 with a constant
implementation is able to signal a channel is closed to the other side. Clients are also able to drain the closed channels.
aojea
commented
May 12, 2026
seans3
commented
May 12, 2026
Looks great--thanks. /lgtm |
k8s-ci-robot
commented
May 12, 2026
@seans3: changing LGTM is restricted to collaborators DetailsIn response to this:
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-sigs/prow repository. |
| if resp.subprotocol != "v5.channel.k8s.io": | ||
| resp.close() | ||
| api.delete_namespaced_pod(name=name, body={}, namespace='default') | ||
| self.skipTest("Skipping test: v5.channel.k8s.io subprotocol not negotiated") |
There was a problem hiding this comment.
the test is skipped in the e2e test flow: https://github.com/kubernetes-client/python/actions/runs/25740181177/job/75636044165
how to make it run?
There was a problem hiding this comment.
kubernetes/e2e_test/test_client.py::TestClient::test_pod_exec_close_channel SKIPPED
There was a problem hiding this comment.
we need to get a more recent version of kind, let me update that
There was a problem hiding this comment.
it was not the version, the e2e tests were not initializing the subprotocol so it never matched ... now the client if is not preconfigured, uses the subprotocol obtained during the negotiation
aojea
commented
May 12, 2026
@yliaog is it normal for the e2e tests to take so long? they take more than 3 hours, that sounds like a lot |
aojea
commented
May 12, 2026
yliaog
commented
May 13, 2026
the e2e takes a long time in the informer tests, i have not had a chance to look into why it takes so long. |
aojea
commented
May 13, 2026
Fixed im the latest commit, there was deadlockimg when.closing, added a comment |
yliaog
commented
May 13, 2026
k8s-ci-robot
commented
May 13, 2026
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: aojea, yliaog 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 |
Uh oh!
There was an error while loading. Please reload this page.
##### [\`36.0.2\`](https://github.com/kubernetes-client/python/releases/tag/v36.0.2) Getting started: ```bash pip install --pre --upgrade kubernetes ``` Or from source, download attached zip file, then ```bash unzip client-python-v36.0.2.zip cd client-python-v36.0.2 python setup-release.py install ``` Then follow examples in <https://github.com/kubernetes-client/python/tree/release-36.0/examples> Changelog: <https://github.com/kubernetes-client/python/blob/release-36.0/CHANGELOG.md> --- ##### [\`36.0.1\`](https://github.com/kubernetes-client/python/blob/HEAD/CHANGELOG.md#v3601) Kubernetes API Version: v1.36.1 ##### Bug or Regression - Fix `load_incluster_config()` and `load_kube_config()` (sync and async, with a static token) so requests carry an `Authorization` header on `kubernetes-client/python` v36+. Without this fix, in-cluster pods upgrading to v36 silently send unauthenticated requests and the apiserver rejects them as `system:anonymous`. ([#2585](kubernetes-client/python#2585), [@jmacek](https://github.com/Jmacek)) ##### Deprecation - Support new exec v5 websocket subprotocol ([#2486](kubernetes-client/python#2486), [@aojea](https://github.com/aojea))
##### [\`36.0.2\`](https://github.com/kubernetes-client/python/releases/tag/v36.0.2) Getting started: ```bash pip install --pre --upgrade kubernetes ``` Or from source, download attached zip file, then ```bash unzip client-python-v36.0.2.zip cd client-python-v36.0.2 python setup-release.py install ``` Then follow examples in <https://github.com/kubernetes-client/python/tree/release-36.0/examples> Changelog: <https://github.com/kubernetes-client/python/blob/release-36.0/CHANGELOG.md> --- ##### [\`36.0.1\`](https://github.com/kubernetes-client/python/blob/HEAD/CHANGELOG.md#v3601) Kubernetes API Version: v1.36.1 ##### Bug or Regression - Fix `load_incluster_config()` and `load_kube_config()` (sync and async, with a static token) so requests carry an `Authorization` header on `kubernetes-client/python` v36+. Without this fix, in-cluster pods upgrading to v36 silently send unauthenticated requests and the apiserver rejects them as `system:anonymous`. ([#2585](kubernetes-client/python#2585), [@jmacek](https://github.com/Jmacek)) ##### Deprecation - Support new exec v5 websocket subprotocol ([#2486](kubernetes-client/python#2486), [@aojea](https://github.com/aojea))
/kind feature
/kind api-change
/kind deprecation
cc: @yliaog@siyuanfoundation