Merge pull request #2172 from yliaog/automated-release-of-29.0.0b1-upstream-release-29.0-1703785760

Automated release of 29.0.0b1 upstream release 29.0 1703785760
This commit is contained in:
Kubernetes Prow Robot 2024-01-02 19:18:31 +01:00 committed by GitHub
commit e5abf1428a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 168 additions and 27 deletions

View File

@ -30,7 +30,7 @@ jobs:
# as we sync with Kubernetes upstream
config: .github/workflows/kind-configs/cluster-1.25.yaml
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -30,7 +30,7 @@ jobs:
# as we sync with Kubernetes upstream
config: .github/workflows/kind-configs/cluster-1.15.yaml
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -30,7 +30,7 @@ jobs:
# as we sync with Kubernetes upstream
config: .github/workflows/kind-configs/cluster-1.16.yaml
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -30,7 +30,7 @@ jobs:
# as we sync with Kubernetes upstream
config: .github/workflows/kind-configs/cluster-1.17.yaml
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -30,7 +30,7 @@ jobs:
# as we sync with Kubernetes upstream
config: .github/workflows/kind-configs/cluster-1.18.yaml
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -30,7 +30,7 @@ jobs:
# as we sync with Kubernetes upstream
config: .github/workflows/kind-configs/cluster-1.26.yaml
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -17,7 +17,7 @@ jobs:
with:
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -1,3 +1,11 @@
# v29.0.0b1
Kubernetes API Version: v1.29.0
### Bug or Regression
- Fix UTF-8 failures in Watch (#2100, @davidopic)
- Fix upper version boundary of urllib3, since other dependencies don't support urllib3 in version 2 (#2105, @jsaalfeld)
# v29.0.0a1
Kubernetes API Version: v1.29.0

View File

@ -4,7 +4,7 @@ No description provided (generated by Openapi Generator https://github.com/opena
This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: release-1.29
- Package version: 29.0.0a1
- Package version: 29.0.0b1
- Build package: org.openapitools.codegen.languages.PythonClientCodegen
## Requirements.

View File

@ -14,7 +14,7 @@
__project__ = 'kubernetes'
# The version is auto-updated. Please do not edit.
__version__ = "29.0.0a1"
__version__ = "29.0.0b1"
from . import client
from . import config

View File

@ -52,20 +52,33 @@ def _find_return_type(func):
def iter_resp_lines(resp):
prev = ""
for seg in resp.stream(amt=None, decode_content=False):
if isinstance(seg, bytes):
seg = seg.decode('utf8')
seg = prev + seg
lines = seg.split("\n")
if not seg.endswith("\n"):
prev = lines[-1]
lines = lines[:-1]
buffer = bytearray()
for segment in resp.stream(amt=None, decode_content=False):
# Append the segment (chunk) to the buffer
#
# Performance note: depending on contents of buffer and the type+value of segment,
# encoding segment into the buffer could be a wasteful step. The approach used here
# simplifies the logic farther down, but in the future it may be reasonable to
# sacrifice readability for performance.
if isinstance(segment, bytes):
buffer.extend(segment)
elif isinstance(segment, str):
buffer.extend(segment.encode("utf-8"))
else:
prev = ""
for line in lines:
raise TypeError(
f"Received invalid segment type, {type(segment)}, from stream. Accepts only 'str' or 'bytes'.")
# Split by newline (safe for utf-8 because multi-byte sequences cannot contain the newline byte)
next_newline = buffer.find(b'\n')
while next_newline != -1:
# Convert bytes to a valid utf-8 string, replacing any invalid utf-8 with the '<27>' character
line = buffer[:next_newline].decode(
"utf-8", errors="replace")
buffer = buffer[next_newline+1:]
if line:
yield line
next_newline = buffer.find(b'\n')
class Watch(object):

View File

@ -61,6 +61,9 @@ class WatchTests(unittest.TestCase):
if count == 4:
w.stop()
# make sure that all three records were consumed by the stream
self.assertEqual(4, count)
fake_api.get_namespaces.assert_called_once_with(
_preload_content=False, watch=True)
fake_resp.stream.assert_called_once_with(
@ -68,6 +71,123 @@ class WatchTests(unittest.TestCase):
fake_resp.close.assert_called_once()
fake_resp.release_conn.assert_called_once()
def test_watch_with_interspersed_newlines(self):
fake_resp = Mock()
fake_resp.close = Mock()
fake_resp.release_conn = Mock()
fake_resp.stream = Mock(
return_value=[
'\n',
'{"type": "ADDED", "object": {"metadata":',
'{"name": "test1","resourceVersion": "1"}}}\n{"type": "ADDED", ',
'"object": {"metadata": {"name": "test2", "resourceVersion": "2"}}}\n',
'\n',
'',
'{"type": "ADDED", "object": {"metadata": {"name": "test3", "resourceVersion": "3"}}}\n',
'\n\n\n',
'\n',
])
fake_api = Mock()
fake_api.get_namespaces = Mock(return_value=fake_resp)
fake_api.get_namespaces.__doc__ = ':return: V1NamespaceList'
w = Watch()
count = 0
# Consume all test events from the mock service, stopping when no more data is available.
# Note that "timeout_seconds" below is not a timeout; rather, it disables retries and is
# the only way to do so. Without that, the stream will re-read the test data forever.
for e in w.stream(fake_api.get_namespaces, timeout_seconds=1):
count += 1
self.assertEqual("test%d" % count, e['object'].metadata.name)
self.assertEqual(3, count)
def test_watch_with_multibyte_utf8(self):
fake_resp = Mock()
fake_resp.close = Mock()
fake_resp.release_conn = Mock()
fake_resp.stream = Mock(
return_value=[
# two-byte utf-8 character
'{"type":"MODIFIED","object":{"data":{"utf-8":"© 1"},"metadata":{"name":"test1","resourceVersion":"1"}}}\n',
# same copyright character expressed as bytes
b'{"type":"MODIFIED","object":{"data":{"utf-8":"\xC2\xA9 2"},"metadata":{"name":"test2","resourceVersion":"2"}}}\n'
# same copyright character with bytes split across two stream chunks
b'{"type":"MODIFIED","object":{"data":{"utf-8":"\xC2',
b'\xA9 3"},"metadata":{"n',
# more chunks of the same event, sent as a mix of bytes and strings
'ame":"test3","resourceVersion":"3"',
'}}}',
b'\n'
])
fake_api = Mock()
fake_api.get_configmaps = Mock(return_value=fake_resp)
fake_api.get_configmaps.__doc__ = ':return: V1ConfigMapList'
w = Watch()
count = 0
# Consume all test events from the mock service, stopping when no more data is available.
# Note that "timeout_seconds" below is not a timeout; rather, it disables retries and is
# the only way to do so. Without that, the stream will re-read the test data forever.
for event in w.stream(fake_api.get_configmaps, timeout_seconds=1):
count += 1
self.assertEqual("MODIFIED", event['type'])
self.assertEqual("test%d" % count, event['object'].metadata.name)
self.assertEqual("© %d" % count, event['object'].data["utf-8"])
self.assertEqual(
"%d" % count, event['object'].metadata.resource_version)
self.assertEqual("%d" % count, w.resource_version)
self.assertEqual(3, count)
def test_watch_with_invalid_utf8(self):
fake_resp = Mock()
fake_resp.close = Mock()
fake_resp.release_conn = Mock()
fake_resp.stream = Mock(
# test 1 uses 1 invalid utf-8 byte
# test 2 uses a sequence of 2 invalid utf-8 bytes
# test 3 uses a sequence of 3 invalid utf-8 bytes
return_value=[
# utf-8 sequence for 😄 is \xF0\x9F\x98\x84
# all other sequences below are invalid
# ref: https://www.w3.org/2001/06/utf-8-wrong/UTF-8-test.html
b'{"type":"MODIFIED","object":{"data":{"utf-8":"\xF0\x9F\x98\x84 1","invalid":"\x80 1"},"metadata":{"name":"test1"}}}\n',
b'{"type":"MODIFIED","object":{"data":{"utf-8":"\xF0\x9F\x98\x84 2","invalid":"\xC0\xAF 2"},"metadata":{"name":"test2"}}}\n',
# mix bytes/strings and split byte sequences across chunks
b'{"type":"MODIFIED","object":{"data":{"utf-8":"\xF0\x9F\x98',
b'\x84 ',
b'',
b'3","invalid":"\xE0\x80',
b'\xAF ',
'3"},"metadata":{"n',
'ame":"test3"',
'}}}',
b'\n'
])
fake_api = Mock()
fake_api.get_configmaps = Mock(return_value=fake_resp)
fake_api.get_configmaps.__doc__ = ':return: V1ConfigMapList'
w = Watch()
count = 0
# Consume all test events from the mock service, stopping when no more data is available.
# Note that "timeout_seconds" below is not a timeout; rather, it disables retries and is
# the only way to do so. Without that, the stream will re-read the test data forever.
for event in w.stream(fake_api.get_configmaps, timeout_seconds=1):
count += 1
self.assertEqual("MODIFIED", event['type'])
self.assertEqual("test%d" % count, event['object'].metadata.name)
self.assertEqual("😄 %d" % count, event['object'].data["utf-8"])
# expect N replacement characters in test N
self.assertEqual("<EFBFBD> %d".replace('<EFBFBD>', '<EFBFBD>'*count) %
count, event['object'].data["invalid"])
self.assertEqual(3, count)
def test_watch_for_follow(self):
fake_resp = Mock()
fake_resp.close = Mock()

View File

@ -14,7 +14,7 @@
from __future__ import absolute_import
__version__ = "29.0.0a1"
__version__ = "29.0.0b1"
# import apis into sdk package
from kubernetes.client.api.well_known_api import WellKnownApi

View File

@ -78,7 +78,7 @@ class ApiClient(object):
self.default_headers[header_name] = header_value
self.cookie = cookie
# Set default User-Agent.
self.user_agent = 'OpenAPI-Generator/29.0.0a1/python'
self.user_agent = 'OpenAPI-Generator/29.0.0b1/python'
self.client_side_validation = configuration.client_side_validation
def __enter__(self):

View File

@ -354,7 +354,7 @@ class Configuration(object):
"OS: {env}\n"\
"Python Version: {pyversion}\n"\
"Version of the API: release-1.29\n"\
"SDK Package Version: 29.0.0a1".\
"SDK Package Version: 29.0.0b1".\
format(env=sys.platform, pyversion=sys.version)
def get_host_settings(self):

View File

@ -18,13 +18,13 @@ import sys
KUBERNETES_BRANCH = "release-1.29"
# client version for packaging and releasing.
CLIENT_VERSION = "29.0.0a1"
CLIENT_VERSION = "29.0.0b1"
# Name of the release package
PACKAGE_NAME = "kubernetes"
# Stage of development, mainly used in setup.py's classifiers.
DEVELOPMENT_STATUS = "3 - Alpha"
DEVELOPMENT_STATUS = "4 - Beta"
# If called directly, return the constant value given

View File

@ -16,9 +16,9 @@ from setuptools import setup
# Do not edit these constants. They will be updated automatically
# by scripts/update-client.sh.
CLIENT_VERSION = "29.0.0a1"
CLIENT_VERSION = "29.0.0b1"
PACKAGE_NAME = "kubernetes"
DEVELOPMENT_STATUS = "3 - Alpha"
DEVELOPMENT_STATUS = "4 - Beta"
# To install the library, run the following
#