diff --git a/.github/workflows/deploy-wiki.yaml b/.github/workflows/deploy-wiki.yaml index b7d9464683..88b7150132 100644 --- a/.github/workflows/deploy-wiki.yaml +++ b/.github/workflows/deploy-wiki.yaml @@ -11,7 +11,7 @@ jobs: deploy-wiki: runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: true - name: Install rsync diff --git a/.github/workflows/e2e-master.yaml b/.github/workflows/e2e-master.yaml index caa24a62b6..0a5850d658 100644 --- a/.github/workflows/e2e-master.yaml +++ b/.github/workflows/e2e-master.yaml @@ -15,7 +15,7 @@ jobs: matrix: python-version: ["3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: true - name: Create Kind Cluster @@ -30,7 +30,7 @@ jobs: # as we sync with Kubernetes upstream config: .github/workflows/kind-configs/cluster-1.35.yaml - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/e2e-release-35.0.yaml b/.github/workflows/e2e-release-35.0.yaml index 44c0fc5862..94e068e9a4 100644 --- a/.github/workflows/e2e-release-35.0.yaml +++ b/.github/workflows/e2e-release-35.0.yaml @@ -15,7 +15,7 @@ jobs: matrix: python-version: [3.10, 3.11, 3.12] steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: true - name: Create Kind Cluster @@ -30,7 +30,7 @@ jobs: # as we sync with Kubernetes upstream config: .github/workflows/kind-configs/cluster-1.35.yaml - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index cbeeebbcee..e7e0040c0d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -13,11 +13,11 @@ jobs: use_coverage: 'coverage' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: submodules: true - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -45,7 +45,7 @@ jobs: - name: Upload coverage to Codecov if: "matrix.use_coverage" - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f with: fail_ci_if_error: false verbose: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d6713bcc6..51a1c42f7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# v36.0.3 + +Kubernetes API Version: v1.36.2 + +### Bug or Regression +- Fix Watch.stream selecting watch instead of follow when streaming pod logs. +- Start the leader election worker thread as a daemon so it does not block process shutdown. +- Fix readline_channel, readline_stdout and readline_stderr crashing with OverflowError when using the default timeout, and returning None when a timeout expires. +- Fix format_quantity returning imprecise, non-canonical values for the milli, micro and nano suffixes, and ignoring quantize=Decimal(0). + # v36.0.2 Kubernetes API Version: v1.36.1 diff --git a/kubernetes/README.md b/kubernetes/README.md index 3a9d898799..dc63efe610 100644 --- a/kubernetes/README.md +++ b/kubernetes/README.md @@ -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.36 -- Package version: 36.0.2 +- Package version: 36.0.3 - Build package: org.openapitools.codegen.languages.PythonLegacyClientCodegen ## Requirements. diff --git a/kubernetes/__init__.py b/kubernetes/__init__.py index 84f5bfaee6..bd39ab7fc2 100644 --- a/kubernetes/__init__.py +++ b/kubernetes/__init__.py @@ -14,7 +14,7 @@ __project__ = 'kubernetes' # The version is auto-updated. Please do not edit. -__version__ = "36.0.2" +__version__ = "36.0.3" from . import client from . import config diff --git a/kubernetes/aio/README.md b/kubernetes/aio/README.md index 4d739c65dc..db859d6ef1 100644 --- a/kubernetes/aio/README.md +++ b/kubernetes/aio/README.md @@ -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.36 -- Package version: 36.0.2 +- Package version: 36.0.3 - Build package: org.openapitools.codegen.languages.PythonLegacyClientCodegen ## Requirements. diff --git a/kubernetes/aio/__init__.py b/kubernetes/aio/__init__.py index 5d74f97f55..d49f28286f 100644 --- a/kubernetes/aio/__init__.py +++ b/kubernetes/aio/__init__.py @@ -14,7 +14,7 @@ __project__ = "kubernetes_aio" # The version is auto-updated. Please do not edit. -__version__ = "36.0.2" +__version__ = "36.0.3" import kubernetes.aio.client as client diff --git a/kubernetes/aio/client/__init__.py b/kubernetes/aio/client/__init__.py index 5323b4bf82..2709e43db6 100644 --- a/kubernetes/aio/client/__init__.py +++ b/kubernetes/aio/client/__init__.py @@ -14,7 +14,7 @@ from __future__ import absolute_import -__version__ = "36.0.2" +__version__ = "36.0.3" # import apis into sdk package from kubernetes.aio.client.api.well_known_api import WellKnownApi diff --git a/kubernetes/aio/client/api_client.py b/kubernetes/aio/client/api_client.py index 4f18901e10..22f6067df3 100644 --- a/kubernetes/aio/client/api_client.py +++ b/kubernetes/aio/client/api_client.py @@ -78,7 +78,7 @@ def __init__(self, configuration=None, header_name=None, header_value=None, self.default_headers[header_name] = header_value self.cookie = cookie # Set default User-Agent. - self.user_agent = 'OpenAPI-Generator/36.0.2/python' + self.user_agent = 'OpenAPI-Generator/36.0.3/python' self.client_side_validation = configuration.client_side_validation async def __aenter__(self): diff --git a/kubernetes/aio/client/api_client.py.orig b/kubernetes/aio/client/api_client.py.orig index 03d1ac9757..852e23c3e9 100644 --- a/kubernetes/aio/client/api_client.py.orig +++ b/kubernetes/aio/client/api_client.py.orig @@ -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/36.0.2/python' + self.user_agent = 'OpenAPI-Generator/36.0.3/python' self.client_side_validation = configuration.client_side_validation async def __aenter__(self): diff --git a/kubernetes/aio/client/configuration.py b/kubernetes/aio/client/configuration.py index 13dea3c9ab..ad01a1af5e 100644 --- a/kubernetes/aio/client/configuration.py +++ b/kubernetes/aio/client/configuration.py @@ -441,7 +441,7 @@ def to_debug_report(self): "OS: {env}\n"\ "Python Version: {pyversion}\n"\ "Version of the API: release-1.36\n"\ - "SDK Package Version: 36.0.2".\ + "SDK Package Version: 36.0.3".\ format(env=sys.platform, pyversion=sys.version) def get_host_settings(self): diff --git a/kubernetes/aio/client/configuration.py.orig b/kubernetes/aio/client/configuration.py.orig index 9779936845..430b817a14 100644 --- a/kubernetes/aio/client/configuration.py.orig +++ b/kubernetes/aio/client/configuration.py.orig @@ -432,7 +432,7 @@ conf = client.Configuration( "OS: {env}\n"\ "Python Version: {pyversion}\n"\ "Version of the API: release-1.36\n"\ - "SDK Package Version: 36.0.2".\ + "SDK Package Version: 36.0.3".\ format(env=sys.platform, pyversion=sys.version) def get_host_settings(self): diff --git a/kubernetes/aio/setup.py b/kubernetes/aio/setup.py index 4f252c56f2..68873ea2ac 100644 --- a/kubernetes/aio/setup.py +++ b/kubernetes/aio/setup.py @@ -13,7 +13,7 @@ from setuptools import setup, find_packages # noqa: H301 NAME = "client" -VERSION = "36.0.2" +VERSION = "36.0.3" # To install the library, run the following # # python setup.py install diff --git a/kubernetes/base/leaderelection/leaderelection.py b/kubernetes/base/leaderelection/leaderelection.py index 4fff8d0790..ea20f0a570 100644 --- a/kubernetes/base/leaderelection/leaderelection.py +++ b/kubernetes/base/leaderelection/leaderelection.py @@ -55,8 +55,7 @@ def run(self): logger.info("{} successfully acquired lease".format(self.election_config.lock.identity)) # Start leading and call OnStartedLeading() - threading.daemon = True - threading.Thread(target=self.election_config.onstarted_leading).start() + threading.Thread(target=self.election_config.onstarted_leading, daemon=True).start() self.renew_loop() diff --git a/kubernetes/base/leaderelection/leaderelection_test.py b/kubernetes/base/leaderelection/leaderelection_test.py index 9fb6d9bcf4..0cbcf00c88 100644 --- a/kubernetes/base/leaderelection/leaderelection_test.py +++ b/kubernetes/base/leaderelection/leaderelection_test.py @@ -22,6 +22,7 @@ import json import time import pytest +from unittest.mock import patch thread_lock = threading.RLock() @@ -194,6 +195,33 @@ def on_stopped_leading(): self.assert_history(leadership_history, ["get leadership", "start leading", "stop leading"]) + def test_onstarted_leading_runs_in_daemon_thread(self): + captured = {} + real_thread = threading.Thread + + def record_thread(*args, **kwargs): + thread = real_thread(*args, **kwargs) + captured["daemon"] = thread.daemon + return thread + + started = threading.Event() + + mock_lock = MockResourceLock("mock", "mock_namespace", "mock", thread_lock, + lambda: None, lambda: None, lambda: None, None) + mock_lock.renew_count_max = 1 + + config = electionconfig.Config(lock=mock_lock, lease_duration=2, + renew_deadline=1.5, retry_period=1, + onstarted_leading=started.set, + onstopped_leading=lambda: None) + + with patch.object(leaderelection.threading, "Thread", new=record_thread): + leaderelection.LeaderElection(config).run() + + self.assertTrue(started.wait(1), "onstarted_leading callback did not run") + self.assertIn("daemon", captured) + self.assertTrue(captured["daemon"]) + def assert_history(self, history, expected): self.assertIsNotNone(expected) self.assertIsNotNone(history) diff --git a/kubernetes/base/stream/ws_client.py b/kubernetes/base/stream/ws_client.py index 3031e01560..ffdd877ef4 100644 --- a/kubernetes/base/stream/ws_client.py +++ b/kubernetes/base/stream/ws_client.py @@ -128,6 +128,7 @@ def readline_channel(self, channel, timeout=None): return b"" if self.binary else "" self.update(timeout=(timeout - time.time() + start)) + return b"" if self.binary else "" def write_channel(self, channel, data): """Write data to a channel.""" @@ -216,11 +217,15 @@ def update(self, timeout=0): if hasattr(select, "poll"): poll = select.poll() poll.register(self.sock.sock, select.POLLIN) - if timeout is not None: + if timeout is not None and timeout != float("inf"): timeout *= 1_000 # poll method uses milliseconds as the time unit + else: + timeout = None r = poll.poll(timeout) poll.unregister(self.sock.sock) else: + if timeout == float("inf"): + timeout = None r, _, _ = select.select( (self.sock.sock, ), (), (), timeout) diff --git a/kubernetes/base/stream/ws_client_test.py b/kubernetes/base/stream/ws_client_test.py index 2785a831fb..80f9c692c5 100644 --- a/kubernetes/base/stream/ws_client_test.py +++ b/kubernetes/base/stream/ws_client_test.py @@ -342,6 +342,48 @@ def test_peek_channel_closed_with_leftover_data(self): self.assertEqual(data3, "") mock_update.assert_not_called() + def test_update_infinite_timeout_polls_without_overflow(self): + """Verify update maps an infinite (default) timeout to a blocking poll instead of overflowing""" + with patch.object(ws_client_module, 'create_websocket') as mock_create, \ + patch('select.poll') as mock_poll: + mock_poll.return_value.poll.return_value = [] + mock_ws = MagicMock() + mock_ws.subprotocol = V5_CHANNEL_PROTOCOL + mock_ws.connected = True + mock_ws.sock.fileno.return_value = 10 + mock_create.return_value = mock_ws + + client = WSClient(self.config_mock, "ws://test", headers=None, capture_all=True) + client.update(timeout=float("inf")) + + mock_poll.return_value.poll.assert_called_once_with(None) + + def test_readline_channel_returns_empty_string_on_expired_timeout(self): + """Verify readline_channel returns '' (not None) when a finite timeout expires""" + with patch.object(ws_client_module, 'create_websocket') as mock_create: + mock_ws = MagicMock() + mock_ws.subprotocol = V5_CHANNEL_PROTOCOL + mock_ws.connected = True + mock_create.return_value = mock_ws + + client = WSClient(self.config_mock, "ws://test", headers=None, capture_all=True, binary=False) + with patch.object(client, 'update'): + line = client.readline_channel(1, timeout=0.01) + self.assertEqual(line, "") + + def test_readline_channel_returns_empty_bytes_on_expired_timeout(self): + """Verify readline_channel returns b'' (not None) when a finite timeout expires in binary mode""" + with patch.object(ws_client_module, 'create_websocket') as mock_create: + mock_ws = MagicMock() + mock_ws.subprotocol = V5_CHANNEL_PROTOCOL + mock_ws.connected = True + mock_create.return_value = mock_ws + + client = WSClient(self.config_mock, "ws://test", headers=None, capture_all=True, binary=True) + with patch.object(client, 'update'): + line = client.readline_channel(1, timeout=0.01) + self.assertEqual(line, b"") + @pytest.fixture(scope="module") diff --git a/kubernetes/base/watch/watch.py b/kubernetes/base/watch/watch.py index 0e39e191a9..2eda0b34f4 100644 --- a/kubernetes/base/watch/watch.py +++ b/kubernetes/base/watch/watch.py @@ -19,7 +19,7 @@ from kubernetes import client PYDOC_RETURN_LABEL = ":rtype:" -PYDOC_FOLLOW_PARAM = ":param bool follow:" +PYDOC_FOLLOW_PARAM = ":param follow:" # Removing this suffix from return type name should give us event's object # type. e.g., if list_namespaces() returns "NamespaceList" type, diff --git a/kubernetes/base/watch/watch_test.py b/kubernetes/base/watch/watch_test.py index 07869095b6..00f855b0d0 100644 --- a/kubernetes/base/watch/watch_test.py +++ b/kubernetes/base/watch/watch_test.py @@ -204,7 +204,8 @@ def test_watch_for_follow(self): fake_api = Mock() fake_api.read_namespaced_pod_log = Mock(return_value=fake_resp) - fake_api.read_namespaced_pod_log.__doc__ = ':param bool follow:\n:rtype: str' + fake_api.read_namespaced_pod_log.__doc__ = ( + ':param follow:\n:type follow: bool\n:rtype: str') w = Watch() count = 1 diff --git a/kubernetes/client/__init__.py b/kubernetes/client/__init__.py index acb29a2340..8445b3b7c2 100644 --- a/kubernetes/client/__init__.py +++ b/kubernetes/client/__init__.py @@ -14,7 +14,7 @@ from __future__ import absolute_import -__version__ = "36.0.2" +__version__ = "36.0.3" # import apis into sdk package from kubernetes.client.api.well_known_api import WellKnownApi diff --git a/kubernetes/client/api_client.py b/kubernetes/client/api_client.py index 4662b9934c..1a62409c14 100644 --- a/kubernetes/client/api_client.py +++ b/kubernetes/client/api_client.py @@ -78,7 +78,7 @@ def __init__(self, configuration=None, header_name=None, header_value=None, self.default_headers[header_name] = header_value self.cookie = cookie # Set default User-Agent. - self.user_agent = 'OpenAPI-Generator/36.0.2/python' + self.user_agent = 'OpenAPI-Generator/36.0.3/python' self.client_side_validation = configuration.client_side_validation def __enter__(self): diff --git a/kubernetes/client/configuration.py b/kubernetes/client/configuration.py index 0797299eb1..8af8d65093 100644 --- a/kubernetes/client/configuration.py +++ b/kubernetes/client/configuration.py @@ -432,7 +432,7 @@ def to_debug_report(self): "OS: {env}\n"\ "Python Version: {pyversion}\n"\ "Version of the API: release-1.36\n"\ - "SDK Package Version: 36.0.2".\ + "SDK Package Version: 36.0.3".\ format(env=sys.platform, pyversion=sys.version) def get_host_settings(self): diff --git a/kubernetes/e2e_test/test_utils.py b/kubernetes/e2e_test/test_utils.py index 433089efa1..a1789ae7fe 100644 --- a/kubernetes/e2e_test/test_utils.py +++ b/kubernetes/e2e_test/test_utils.py @@ -757,9 +757,11 @@ def test_format_quantity(self): "0.3Gi") # == base 1000 == - self.assertEqual(quantity.format_quantity(Decimal(0.000_000_001), "n"), "1n") - self.assertEqual(quantity.format_quantity(Decimal(0.000_001), "u"), "1u") - self.assertEqual(quantity.format_quantity(Decimal(0.001), "m"), "1m") + # Exact Decimals: float-derived Decimals (e.g. Decimal(0.001)) carry binary + # rounding error that the precise scaling surfaces into the output. + self.assertEqual(quantity.format_quantity(Decimal("0.000000001"), "n"), "1n") + self.assertEqual(quantity.format_quantity(Decimal("0.000001"), "u"), "1u") + self.assertEqual(quantity.format_quantity(Decimal("0.001"), "m"), "1m") self.assertEqual(quantity.format_quantity(Decimal(1_000), "k"), "1k") self.assertEqual(quantity.format_quantity(Decimal(1_000_000), "M"), "1M") self.assertEqual(quantity.format_quantity(Decimal(1_000_000_000), "G"), "1G") @@ -781,3 +783,19 @@ def test_format_quantity(self): ), "333k", ) + + # == milli/micro/nano keep full precision in canonical decimal form == + # Regression: these divided by a float-built Decimal and rendered in + # scientific notation (e.g. "499.9999999999999895916591441m", "1E+9"). + self.assertEqual(quantity.format_quantity(Decimal("0.5"), "m"), "500m") + self.assertEqual(quantity.format_quantity(Decimal("0.5"), "u"), "500000u") + self.assertEqual(quantity.format_quantity(Decimal("0.5"), "n"), "500000000n") + self.assertEqual(quantity.format_quantity(Decimal("1"), "m"), "1000m") + self.assertEqual(quantity.format_quantity(Decimal("1"), "n"), "1000000000n") + + # == quantize=Decimal(0) is honored, not treated as falsy == + self.assertEqual(quantity.format_quantity(Decimal("0.5006"), "m"), "500.6m") + self.assertEqual( + quantity.format_quantity(Decimal("0.5006"), "m", quantize=Decimal(0)), + "501m", + ) diff --git a/kubernetes/utils/quantity.py b/kubernetes/utils/quantity.py index 484f347193..a45026dbe6 100644 --- a/kubernetes/utils/quantity.py +++ b/kubernetes/utils/quantity.py @@ -136,7 +136,7 @@ def format_quantity(quantity_value, suffix, quantize=None) -> str: if suffix[0] not in _EXPONENTS: raise ValueError(f"{quantity_value} has unknown suffix") - different_scale = quantity_value / Decimal(base ** _EXPONENTS[suffix[0]]) - if quantize: + different_scale = quantity_value / (Decimal(base) ** _EXPONENTS[suffix[0]]) + if quantize is not None: different_scale = different_scale.quantize(quantize) - return str(different_scale) + suffix + return format(different_scale, "f") + suffix diff --git a/scripts/apply-hotfixes.sh b/scripts/apply-hotfixes.sh index 56cffde1fc..fde634e63c 100755 --- a/scripts/apply-hotfixes.sh +++ b/scripts/apply-hotfixes.sh @@ -89,4 +89,15 @@ else exit 1 fi; +# Patching commit for bearer-token fallback in Configuration.auth_settings +git cherry-pick -n 5621a4c2cf5cc278e8f45ce759ce44c057f1dbe2 +if [ $? -eq 0 ] +then + echo Successfully patched changes for bearer-token fallback in Configuration.auth_settings +else + echo Failed to patch changes for bearer-token fallback in Configuration.auth_settings + git restore --staged . + exit 1 +fi; + git commit -m "Apply hotfixes" diff --git a/scripts/constants.py b/scripts/constants.py index 82e81a79e6..7cd5870a2b 100644 --- a/scripts/constants.py +++ b/scripts/constants.py @@ -18,7 +18,7 @@ KUBERNETES_BRANCH = "release-1.36" # client version for packaging and releasing. -CLIENT_VERSION = "36.0.2" +CLIENT_VERSION = "36.0.3" # Name of the release package PACKAGE_NAME = "kubernetes" diff --git a/scripts/release.sh b/scripts/release.sh index adb8e4310b..4a6be4a0e2 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -207,6 +207,10 @@ git diff-index --quiet --cached HEAD || git commit -am "update changelog" # Re-generate the client scripts/update-client.sh + +# Re-generate the asyncio client +scripts/update-client-asyncio.sh + # Apply hotfixes rm -r kubernetes/test/ git add . @@ -223,9 +227,6 @@ if [[ -n "$(git diff kubernetes/client/api/custom_objects_api.py)" ]]; then git commit -m "generated client change for custom_objects" fi -# Re-generate the asyncio client -scripts/update-client-asyncio.sh - # Check if there is any API change, then commit git add kubernetes/docs kubernetes/client/api/ kubernetes/client/models/ kubernetes/swagger.json.unprocessed scripts/swagger.json git add kubernetes/aio/docs kubernetes/aio/client/api/ kubernetes/aio/client/models/ kubernetes/aio/swagger.json.unprocessed diff --git a/setup-asyncio.py b/setup-asyncio.py index aef2b09be7..9c212e148d 100644 --- a/setup-asyncio.py +++ b/setup-asyncio.py @@ -16,7 +16,7 @@ # Do not edit these constants. They will be updated automatically # by scripts/update-client.sh. -CLIENT_VERSION = "36.0.2" +CLIENT_VERSION = "36.0.3" PACKAGE_NAME = "kubernetes.aio" DEVELOPMENT_STATUS = "5 - Production/Stable" diff --git a/setup-release.py b/setup-release.py index 8cf710992f..13077bd2b6 100644 --- a/setup-release.py +++ b/setup-release.py @@ -16,7 +16,7 @@ # Do not edit these constants. They will be updated automatically # by scripts/update-client.sh. -CLIENT_VERSION = "36.0.2" +CLIENT_VERSION = "36.0.3" PACKAGE_NAME = "kubernetes" DEVELOPMENT_STATUS = "5 - Production/Stable" diff --git a/setup.py b/setup.py index ddd7f41e07..fa3d3a271b 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ # Do not edit these constants. They will be updated automatically # by scripts/update-client.sh. -CLIENT_VERSION = "36.0.2" +CLIENT_VERSION = "36.0.3" PACKAGE_NAME = "kubernetes" DEVELOPMENT_STATUS = "5 - Production/Stable" diff --git a/test-requirements.txt b/test-requirements.txt index d6ebedc182..bda4bc35fd 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,4 +1,4 @@ -coverage>=4.0.3 +coverage>=7.14.1 nose>=1.3.7 pytest pytest-cov