diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 65640995e0..2a2cd5a1fa 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index c5bd625714..3f4f4c5972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# v19.15.0 + +Kubernetes API Version: v1.19.15 + +### Feature +- The new parameter 'no_proxy' has been added to configuration for the REST and websocket client. ([kubernetes-client/python#1579](https://github.com/kubernetes-client/python/pull/1579), [@itaru2622](https://github.com/itaru2622))//github.com/itaru2622))//github.com/itaru2622))//github.com/itaru2622))//github.com/itaru2622))//github.com/itaru2622))//github.com/itaru2622))//github.com/itaru2622)) + # v19.15.0b1 Kubernetes API Version: v1.19.15 @@ -12,6 +19,7 @@ Kubernetes API Version: v1.19.15 - Type checking in `Client.serialize_body()` was made more restrictive and robust. ([kubernetes-client/python-base#241](https://github.com/kubernetes-client/python-base/pull/241), [@piglei](https://github.com/piglei)) ### Feature +- Support Proxy Authentication in websocket client(stream/ws_client) like REST client. ([kubernetes-client/python-base#256](https://github.com/kubernetes-client/python-base/pull/256), [@itaru2622](https://github.com/itaru2622)) - Support for the dryRun parameter has been added to the dynamic client. ([kubernetes-client/python-base#247](https://github.com/kubernetes-client/python-base/pull/247), [@gravesm](https://github.com/gravesm)) ### API Change diff --git a/kubernetes/README.md b/kubernetes/README.md index 700930725c..81665d6a75 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.19 -- Package version: 19.15.0b1 +- Package version: 19.15.0 - Build package: org.openapitools.codegen.languages.PythonClientCodegen ## Requirements. diff --git a/kubernetes/__init__.py b/kubernetes/__init__.py index f3dd46bd13..1957be4ce8 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__ = "19.15.0b1" +__version__ = "19.15.0" import kubernetes.client import kubernetes.config diff --git a/kubernetes/base b/kubernetes/base index b0afc93ffa..09dbbe521e 160000 --- a/kubernetes/base +++ b/kubernetes/base @@ -1 +1 @@ -Subproject commit b0afc93ffabb66d930abcdfb1255214d167bf8d5 +Subproject commit 09dbbe521e203634154764b903208bb28cd70f9d diff --git a/kubernetes/client/__init__.py b/kubernetes/client/__init__.py index 51689de28c..66bb740e1a 100644 --- a/kubernetes/client/__init__.py +++ b/kubernetes/client/__init__.py @@ -14,7 +14,7 @@ from __future__ import absolute_import -__version__ = "19.15.0b1" +__version__ = "19.15.0" # import apis into sdk package from kubernetes.client.api.admissionregistration_api import AdmissionregistrationApi diff --git a/kubernetes/client/api_client.py b/kubernetes/client/api_client.py index 9bb973bfd7..b9699c74fd 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/19.15.0b1/python' + self.user_agent = 'OpenAPI-Generator/19.15.0/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 b99f9f0d49..33aec424ee 100644 --- a/kubernetes/client/configuration.py +++ b/kubernetes/client/configuration.py @@ -156,6 +156,9 @@ def __init__(self, host="http://localhost", self.proxy = None """Proxy URL """ + self.no_proxy = None + """bypass proxy for host in the no_proxy list. + """ self.proxy_headers = None """Proxy headers """ @@ -347,7 +350,7 @@ def to_debug_report(self): "OS: {env}\n"\ "Python Version: {pyversion}\n"\ "Version of the API: release-1.19\n"\ - "SDK Package Version: 19.15.0b1".\ + "SDK Package Version: 19.15.0".\ format(env=sys.platform, pyversion=sys.version) def get_host_settings(self): diff --git a/kubernetes/client/rest.py b/kubernetes/client/rest.py index 2f3241611e..9e1b3859d5 100644 --- a/kubernetes/client/rest.py +++ b/kubernetes/client/rest.py @@ -25,6 +25,7 @@ import urllib3 from kubernetes.client.exceptions import ApiException, ApiValueError +from requests.utils import should_bypass_proxies logger = logging.getLogger(__name__) @@ -83,7 +84,7 @@ def __init__(self, configuration, pools_size=4, maxsize=None): maxsize = 4 # https pool manager - if configuration.proxy: + if configuration.proxy and not should_bypass_proxies(configuration.host, no_proxy=configuration.no_proxy or ''): self.pool_manager = urllib3.ProxyManager( num_pools=pools_size, maxsize=maxsize, diff --git a/kubernetes/e2e_test/test_batch.py b/kubernetes/e2e_test/test_batch.py index a8ea590dba..d68c80854c 100644 --- a/kubernetes/e2e_test/test_batch.py +++ b/kubernetes/e2e_test/test_batch.py @@ -26,7 +26,6 @@ class TestClientBatch(unittest.TestCase): def setUpClass(cls): cls.config = base.get_e2e_configuration() - def test_job_apis(self): client = api_client.ApiClient(configuration=self.config) api = batch_v1_api.BatchV1Api(client) @@ -56,4 +55,4 @@ def test_job_apis(self): self.assertEqual(name, resp.metadata.name) resp = api.delete_namespaced_job( - name=name, body={}, namespace='default') \ No newline at end of file + name=name, namespace='default', propagation_policy='Background') diff --git a/kubernetes/e2e_test/test_client.py b/kubernetes/e2e_test/test_client.py index 1034f0ef0d..eed3c909e8 100644 --- a/kubernetes/e2e_test/test_client.py +++ b/kubernetes/e2e_test/test_client.py @@ -35,6 +35,7 @@ else: import httplib + def short_uuid(): id = str(uuid.uuid4()) return id[-12:] @@ -60,6 +61,7 @@ def manifest_with_command(name, command): } } + class TestClient(unittest.TestCase): @classmethod @@ -71,7 +73,8 @@ def test_pod_apis(self): api = core_v1_api.CoreV1Api(client) name = 'busybox-test-' + short_uuid() - pod_manifest = manifest_with_command(name, "while true;do date;sleep 5; done") + pod_manifest = manifest_with_command( + name, "while true;do date;sleep 5; done") # wait for the default service account to be created timeout = time.time() + 30 @@ -84,9 +87,10 @@ def test_pod_apis(self): namespace='default') except ApiException as e: if (six.PY3 and e.status != HTTPStatus.NOT_FOUND) or ( - six.PY3 is False and e.status != httplib.NOT_FOUND): + six.PY3 is False and e.status != httplib.NOT_FOUND): print('error: %s' % e) - self.fail(msg="unexpected error getting default service account") + self.fail( + msg="unexpected error getting default service account") print('default service not found yet: %s' % e) time.sleep(1) continue @@ -111,25 +115,25 @@ def test_pod_apis(self): '-c', 'for i in $(seq 1 3); do date; done'] resp = stream(api.connect_get_namespaced_pod_exec, name, 'default', - command=exec_command, - stderr=False, stdin=False, - stdout=True, tty=False) + command=exec_command, + stderr=False, stdin=False, + stdout=True, tty=False) print('EXEC response : %s' % resp) self.assertEqual(3, len(resp.splitlines())) exec_command = 'uptime' resp = stream(api.connect_post_namespaced_pod_exec, name, 'default', - command=exec_command, - stderr=False, stdin=False, - stdout=True, tty=False) + command=exec_command, + stderr=False, stdin=False, + stdout=True, tty=False) print('EXEC response : %s' % resp) self.assertEqual(1, len(resp.splitlines())) resp = stream(api.connect_post_namespaced_pod_exec, name, 'default', - command='/bin/sh', - stderr=True, stdin=True, - stdout=True, tty=False, - _preload_content=False) + command='/bin/sh', + stderr=True, stdin=True, + stdout=True, tty=False, + _preload_content=False) resp.write_stdin("echo test string 1\n") line = resp.readline_stdout(timeout=5) self.assertFalse(resp.peek_stderr()) @@ -157,7 +161,8 @@ def test_exit_code(self): api = core_v1_api.CoreV1Api(client) name = 'busybox-test-' + short_uuid() - pod_manifest = manifest_with_command(name, "while true;do date;sleep 5; done") + pod_manifest = manifest_with_command( + name, "while true;do date;sleep 5; done") # wait for the default service account to be created timeout = time.time() + 30 @@ -171,9 +176,10 @@ def test_exit_code(self): namespace='default') except ApiException as e: if (six.PY3 and e.status != HTTPStatus.NOT_FOUND) or ( - six.PY3 is False and e.status != httplib.NOT_FOUND): + six.PY3 is False and e.status != httplib.NOT_FOUND): print('error: %s' % e) - self.fail(msg="unexpected error getting default service account") + self.fail( + msg="unexpected error getting default service account") print('default service not found yet: %s' % e) time.sleep(1) continue @@ -201,11 +207,16 @@ def test_exit_code(self): (["/bin/sh", "-c", "ls /"], 0) ) for command, value in commands_expected_values: - client = stream(api.connect_get_namespaced_pod_exec, name, 'default', - command=command, - stderr=True, stdin=False, - stdout=True, tty=False, - _preload_content=False) + client = stream( + api.connect_get_namespaced_pod_exec, + name, + 'default', + command=command, + stderr=True, + stdin=False, + stdout=True, + tty=False, + _preload_content=False) self.assertIsNone(client.returncode) client.run_forever(timeout=10) @@ -337,7 +348,8 @@ def test_portforward_raw(self): for sock in (sock1234, sock1235): self.assertTrue(pf.connected) - sent = b'Another test using fileno %s' % str(sock.fileno()).encode() + sent = b'Another test using fileno %s' % str( + sock.fileno()).encode() sock.sendall(sent) reply = b'' while True: @@ -361,7 +373,7 @@ def test_portforward_http(self): client = api_client.ApiClient(configuration=self.config) api = core_v1_api.CoreV1Api(client) - name = 'portforward-http-' + short_uuid() + name = 'portforward-http-' + short_uuid() pod_manifest = { 'apiVersion': 'v1', 'kind': 'Pod', @@ -404,7 +416,8 @@ def kubernetes_create_connection(address, *args, **kwargs): socket_create_connection = socket.create_connection try: socket.create_connection = kubernetes_create_connection - response = urllib_request.urlopen('http://%s.default.kubernetes/' % name) + response = urllib_request.urlopen( + 'http://%s.default.kubernetes/' % name) html = response.read().decode('utf-8') finally: socket.create_connection = socket_create_connection @@ -485,7 +498,7 @@ def test_replication_controller_apis(self): self.assertEqual(2, resp.spec.replicas) resp = api.delete_namespaced_replication_controller( - name=name, body={}, namespace='default') + name=name, namespace='default', propagation_policy='Background') def test_configmap_apis(self): client = api_client.ApiClient(configuration=self.config) @@ -521,7 +534,8 @@ def test_configmap_apis(self): resp = api.delete_namespaced_config_map( name=name, body={}, namespace='default') - resp = api.list_namespaced_config_map('default', pretty=True, label_selector="e2e-tests=true") + resp = api.list_namespaced_config_map( + 'default', pretty=True, label_selector="e2e-tests=true") self.assertEqual([], resp.items) def test_node_apis(self): diff --git a/kubernetes/e2e_test/test_utils.py b/kubernetes/e2e_test/test_utils.py index 05a056c5f0..73d1a0c62c 100644 --- a/kubernetes/e2e_test/test_utils.py +++ b/kubernetes/e2e_test/test_utils.py @@ -31,7 +31,9 @@ def setUpClass(cls): cls.test_namespace = "e2e-test-utils" k8s_client = client.api_client.ApiClient(configuration=cls.config) core_v1 = client.CoreV1Api(api_client=k8s_client) - body = client.V1Namespace(metadata=client.V1ObjectMeta(name=cls.test_namespace)) + body = client.V1Namespace( + metadata=client.V1ObjectMeta( + name=cls.test_namespace)) core_v1.create_namespace(body=body) @classmethod @@ -304,7 +306,7 @@ def test_create_from_multi_resource_yaml(self): name="mock", namespace="default") self.assertIsNotNone(ctr) core_api.delete_namespaced_replication_controller( - name="mock", namespace="default", body={}) + name="mock", namespace="default", propagation_policy="Background") core_api.delete_namespaced_service(name="mock", namespace="default", body={}) @@ -362,7 +364,7 @@ def test_create_from_multi_resource_yaml_with_conflict(self): name="mock-2", namespace="default") self.assertIsNotNone(ctr) core_api.delete_namespaced_replication_controller( - name="mock-2", namespace="default", body={}) + name="mock-2", namespace="default", propagation_policy="Background") core_api.delete_namespaced_service(name="mock-2", namespace="default", body={}) @@ -396,7 +398,7 @@ def test_create_from_multi_resource_yaml_with_multi_conflicts(self): def test_create_namespaced_apps_deployment_from_yaml(self): """ Should be able to create an apps/v1beta1 deployment - in a test namespace. + in a test namespace. """ k8s_client = client.api_client.ApiClient(configuration=self.config) utils.create_from_yaml( diff --git a/kubernetes/test/test_api_client.py b/kubernetes/test/test_api_client.py index f0a9416cf7..486b4ac5b8 100644 --- a/kubernetes/test/test_api_client.py +++ b/kubernetes/test/test_api_client.py @@ -6,7 +6,8 @@ import unittest import kubernetes - +from kubernetes.client.configuration import Configuration +import urllib3 class TestApiClient(unittest.TestCase): @@ -23,3 +24,28 @@ def test_atexit_closes_threadpool(self): self.assertIsNotNone(client._pool) atexit._run_exitfuncs() self.assertIsNone(client._pool) + + def test_rest_proxycare(self): + + pool = { 'proxy': urllib3.ProxyManager, 'direct': urllib3.PoolManager } + + for dst, proxy, no_proxy, expected_pool in [ + ( 'http://kube.local/', None, None, pool['direct']), + ( 'http://kube.local/', 'http://proxy.local:8080/', None, pool['proxy']), + ( 'http://127.0.0.1:8080/', 'http://proxy.local:8080/', 'localhost,127.0.0.0/8,.local', pool['direct']), + ( 'http://kube.local/', 'http://proxy.local:8080/', 'localhost,127.0.0.0/8,.local', pool['direct']), + ( 'http://kube.others.com:1234/','http://proxy.local:8080/', 'localhost,127.0.0.0/8,.local', pool['proxy']), + ( 'http://kube.others.com:1234/','http://proxy.local:8080/', '*', pool['direct']), + ]: + # setup input + config = Configuration() + setattr(config, 'host', dst) + if proxy is not None: + setattr(config, 'proxy', proxy) + if no_proxy is not None: + setattr(config, 'no_proxy', no_proxy) + # setup done + + # test + client = kubernetes.client.ApiClient(configuration=config) + self.assertEqual( expected_pool, type(client.rest_client.pool_manager) ) diff --git a/scripts/constants.py b/scripts/constants.py index 2e90b8d7a7..8560fdcec4 100644 --- a/scripts/constants.py +++ b/scripts/constants.py @@ -18,13 +18,13 @@ KUBERNETES_BRANCH = "release-1.19" # client version for packaging and releasing. -CLIENT_VERSION = "19.15.0b1" +CLIENT_VERSION = "19.15.0" # Name of the release package PACKAGE_NAME = "kubernetes" # Stage of development, mainly used in setup.py's classifiers. -DEVELOPMENT_STATUS = "4 - Beta" +DEVELOPMENT_STATUS = "5 - Production/Stable" # If called directly, return the constant value given diff --git a/scripts/release.sh b/scripts/release.sh index e034e64ec4..07c4018cb9 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -56,6 +56,9 @@ # - add a sentence about "changes since {last release}". In most cases our # releases should be sequential. This script (the workflow above) is based on # this assumption, and we should make the release note clear about that. +# - update readme; if it's a real release (instead of a snapshot in master +# branch), also create a PR to update changelog and readme in the master +# branch # # Usage: # $ KUBERNETES_BRANCH=release-1.19 CLIENT_VERSION=19.0.0-snapshot DEVELOPMENT_STATUS="3 - Alpha" scripts/release.sh @@ -64,6 +67,9 @@ set -o errexit set -o nounset set -o pipefail +# used by the client generator: https://github.com/kubernetes-client/gen/blob/729332ad08f0f4d98983b7beb027e2f657236ef9/openapi/openapi-generator/client-generator.sh#L52 +export USERNAME=kubernetes + repo_root="$(git rev-parse --show-toplevel)" declare -r repo_root cd "${repo_root}" diff --git a/scripts/update-submodule.sh b/scripts/update-submodule.sh index 2ef51eebbc..45431b37e0 100755 --- a/scripts/update-submodule.sh +++ b/scripts/update-submodule.sh @@ -40,7 +40,7 @@ source scripts/util/changelog.sh source scripts/util/common.sh util::common::check_sed -go get k8s.io/release/cmd/release-notes +go install k8s.io/release/cmd/release-notes@latest TARGET_RELEASE=${TARGET_RELEASE:-"v$(grep "^CLIENT_VERSION = \"" scripts/constants.py | sed "s/CLIENT_VERSION = \"//g" | sed "s/\"//g")"} diff --git a/setup.py b/setup.py index 45cf5498e9..ad126889be 100644 --- a/setup.py +++ b/setup.py @@ -16,9 +16,9 @@ # Do not edit these constants. They will be updated automatically # by scripts/update-client.sh. -CLIENT_VERSION = "19.15.0b1" +CLIENT_VERSION = "19.15.0" PACKAGE_NAME = "kubernetes" -DEVELOPMENT_STATUS = "4 - Beta" +DEVELOPMENT_STATUS = "5 - Production/Stable" # To install the library, run the following # @@ -78,5 +78,6 @@ "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", ], )