Skip to content

Commit c500a63

Browse files
authored
Merge pull request #2573 from yliaog/automated-release-of-36.0.0b1-upstream-release-36.0-1778702049
Automated release of 36.0.0b1 upstream release 36.0 1778702049
2 parents 33ef6f1 + 4af39fd commit c500a63

27 files changed

Lines changed: 381 additions & 36 deletions

.github/workflows/e2e-master.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
cluster_name: kubernetes-python-e2e-master-${{ matrix.python-version }}
2525
# The kind version to be used to spin the cluster up
2626
# this needs to be updated whenever a new Kind version is released
27-
version: v0.17.0
27+
version: v0.31.0
2828
# Update the config here whenever a new client snapshot is performed
2929
# This would eventually point to cluster with the latest Kubernetes version
3030
# as we sync with Kubernetes upstream

.github/workflows/e2e-release-35.0.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
cluster_name: kubernetes-python-e2e-release-35.0-${{ matrix.python-version }}
2525
# The kind version to be used to spin the cluster up
2626
# this needs to be updated whenever a new Kind version is released
27-
version: v0.17.0
27+
version: v0.31.0
2828
# Update the config here whenever a new client snapshot is performed
2929
# This would eventually point to cluster with the latest Kubernetes version
3030
# as we sync with Kubernetes upstream

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# v36.0.0b1
2+
3+
Kubernetes API Version: v1.36.1
4+
5+
### Deprecation
6+
- Support new exec v5 websocket subprotocol (#2486, @aojea)
7+
18
# v36.0.0a3
29

310
Kubernetes API Version: v1.36.0

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ supported versions of Kubernetes clusters.
134134
- [client 33.y.z](https://pypi.org/project/kubernetes/33.1.0/): Kubernetes 1.32 or below (+-), Kubernetes 1.33 (✓), Kubernetes 1.34 or above (+-)
135135
- [client 34.y.z](https://pypi.org/project/kubernetes/34.1.0/): Kubernetes 1.33 or below (+-), Kubernetes 1.34 (✓), Kubernetes 1.35 or above (+-)
136136
- [client 35.y.z](https://pypi.org/project/kubernetes/35.0.0/): Kubernetes 1.34 or below (+-), Kubernetes 1.35 (✓), Kubernetes 1.36 or above (+-)
137-
- [client 36.y.z](https://pypi.org/project/kubernetes/36.0.0a3/): Kubernetes 1.35 or below (+-), Kubernetes 1.36 (✓), Kubernetes 1.37 or above (+-)
137+
- [client 36.y.z](https://pypi.org/project/kubernetes/36.0.0b1/): Kubernetes 1.35 or below (+-), Kubernetes 1.36 (✓), Kubernetes 1.37 or above (+-)
138138

139139

140140
> See [here](#homogenizing-the-kubernetes-python-client-versions) for an explanation of why there is no v13-v16 release.

kubernetes/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ No description provided (generated by Openapi Generator https://github.com/opena
44
This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
55

66
- API version: release-1.36
7-
- Package version: 36.0.0a3
7+
- Package version: 36.0.0b1
88
- Build package: org.openapitools.codegen.languages.PythonLegacyClientCodegen
99

1010
## Requirements.

kubernetes/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
__project__ = 'kubernetes'
1616
# The version is auto-updated. Please do not edit.
17-
__version__ = "36.0.0a3"
17+
__version__ = "36.0.0b1"
1818

1919
from . import client
2020
from . import config

kubernetes/base/stream/ws_client.py

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
STDERR_CHANNEL = 2
4040
ERROR_CHANNEL = 3
4141
RESIZE_CHANNEL = 4
42+
CLOSE_CHANNEL = 255
43+
44+
V4_CHANNEL_PROTOCOL = "v4.channel.k8s.io"
45+
V5_CHANNEL_PROTOCOL = "v5.channel.k8s.io"
4246

4347
class _IgnoredIO:
4448
def write(self, _x):
@@ -59,26 +63,40 @@ def __init__(self, configuration, url, headers, capture_all, binary=False):
5963
"""
6064
self._connected = False
6165
self._channels = {}
66+
self._closed_channels = set()
67+
self.subprotocol = None
6268
self.binary = binary
6369
self.newline = '\n' if not self.binary else b'\n'
6470
if capture_all:
6571
self._all = StringIO() if not self.binary else BytesIO()
6672
else:
6773
self._all = _IgnoredIO()
6874
self.sock = create_websocket(configuration, url, headers)
75+
self.subprotocol = getattr(self.sock, 'subprotocol', None)
76+
if not self.subprotocol and self.sock:
77+
headers_dict = self.sock.getheaders()
78+
if headers_dict:
79+
for k, v in headers_dict.items():
80+
if k.lower() == 'sec-websocket-protocol':
81+
self.subprotocol = v
82+
break
6983
self._connected = True
7084
self._returncode = None
7185

7286
def peek_channel(self, channel, timeout=0):
7387
"""Peek a channel and return part of the input,
7488
empty string otherwise."""
89+
if channel in self._closed_channels and channel not in self._channels:
90+
return b"" if self.binary else ""
7591
self.update(timeout=timeout)
7692
if channel in self._channels:
7793
return self._channels[channel]
78-
return ""
94+
return b"" if self.binary else ""
7995

8096
def read_channel(self, channel, timeout=0):
8197
"""Read data from a channel."""
98+
if channel in self._closed_channels and channel not in self._channels:
99+
return b"" if self.binary else ""
82100
if channel not in self._channels:
83101
ret = self.peek_channel(channel, timeout)
84102
else:
@@ -93,6 +111,7 @@ def readline_channel(self, channel, timeout=None):
93111
timeout = float("inf")
94112
start = time.time()
95113
while self.is_open() and time.time() - start < timeout:
114+
# Always try to drain the channel first
96115
if channel in self._channels:
97116
data = self._channels[channel]
98117
if self.newline in data:
@@ -104,6 +123,14 @@ def readline_channel(self, channel, timeout=None):
104123
else:
105124
del self._channels[channel]
106125
return ret
126+
127+
if channel in self._closed_channels:
128+
if channel in self._channels:
129+
ret = self._channels[channel]
130+
del self._channels[channel]
131+
return ret
132+
return b"" if self.binary else ""
133+
107134
self.update(timeout=(timeout - time.time() + start))
108135

109136
def write_channel(self, channel, data):
@@ -119,6 +146,14 @@ def write_channel(self, channel, data):
119146
payload = channel_prefix + data
120147
self.sock.send(payload, opcode=opcode)
121148

149+
def close_channel(self, channel):
150+
"""Close a channel (v5 protocol only)."""
151+
if self.subprotocol != V5_CHANNEL_PROTOCOL:
152+
return
153+
data = bytes([CLOSE_CHANNEL, channel])
154+
self.sock.send(data, opcode=ABNF.OPCODE_BINARY)
155+
self._closed_channels.add(channel)
156+
122157
def peek_stdout(self, timeout=0):
123158
"""Same as peek_channel with channel=1."""
124159
return self.peek_channel(STDOUT_CHANNEL, timeout=timeout)
@@ -200,13 +235,24 @@ def update(self, timeout=0):
200235
return
201236
elif op_code == ABNF.OPCODE_BINARY or op_code == ABNF.OPCODE_TEXT:
202237
data = frame.data
203-
if six.PY3 and not self.binary:
204-
data = data.decode("utf-8", "replace")
205-
if len(data) > 1:
238+
if len(data) > 0:
239+
# Parse channel from raw bytes to support v5 CLOSE signal AND avoid charset issues
206240
channel = data[0]
207-
if six.PY3 and not self.binary:
208-
channel = ord(channel)
241+
# In Py3, iterating bytes gives int, but indexing bytes gives int.
242+
# websocket-client frame.data might be bytes.
243+
244+
if channel == CLOSE_CHANNEL and self.subprotocol == V5_CHANNEL_PROTOCOL: # v5 CLOSE
245+
if len(data) > 1:
246+
# data[1] is already int in Py3 bytes
247+
close_chan = data[1]
248+
self._closed_channels.add(close_chan)
249+
return
250+
209251
data = data[1:]
252+
# Decode data if expected text
253+
if not self.binary:
254+
data = data.decode("utf-8", "replace")
255+
210256
if data:
211257
if channel in [STDOUT_CHANNEL, STDERR_CHANNEL]:
212258
# keeping all messages in the order they received
@@ -476,7 +522,7 @@ def create_websocket(configuration, url, headers=None):
476522
header.append("sec-websocket-protocol: %s" %
477523
headers['sec-websocket-protocol'])
478524
else:
479-
header.append("sec-websocket-protocol: v4.channel.k8s.io")
525+
header.append("sec-websocket-protocol: %s,%s" % (V5_CHANNEL_PROTOCOL, V4_CHANNEL_PROTOCOL))
480526

481527
if url.startswith('wss://') and configuration.verify_ssl:
482528
ssl_opts = {

0 commit comments

Comments
 (0)