diff --git a/examples/create_deployment.py b/examples/create_deployment.py index 0ce1e2fa1d..87f50eeaee 100644 --- a/examples/create_deployment.py +++ b/examples/create_deployment.py @@ -27,8 +27,8 @@ def main(): with open(path.join(path.dirname(__file__), "nginx-deployment.yaml")) as f: dep = yaml.safe_load(f) - k8s_beta = client.ExtensionsV1beta1Api() - resp = k8s_beta.create_namespaced_deployment( + k8s_apps_v1 = client.AppsV1Api() + resp = k8s_apps_v1.create_namespaced_deployment( body=dep, namespace="default") print("Deployment created. status='%s'" % str(resp.status)) diff --git a/examples/create_deployment_from_yaml.py b/examples/create_deployment_from_yaml.py index dcb7ac14d7..d66a2b6b8c 100644 --- a/examples/create_deployment_from_yaml.py +++ b/examples/create_deployment_from_yaml.py @@ -24,7 +24,7 @@ def main(): config.load_kube_config() k8s_client = client.ApiClient() utils.create_from_yaml(k8s_client, "nginx-deployment.yaml") - k8s_api = client.ExtensionsV1beta1Api(k8s_client) + k8s_api = client.AppsV1Api(k8s_client) deps = k8s_api.read_namespaced_deployment("nginx-deployment", "default") print("Deployment {0} created".format(deps.metadata.name)) diff --git a/examples/deployment_examples.py b/examples/deployment_examples.py index 29f55d34d3..5ec656fc7d 100644 --- a/examples/deployment_examples.py +++ b/examples/deployment_examples.py @@ -25,19 +25,19 @@ def create_deployment_object(): # Configureate Pod template container container = client.V1Container( name="nginx", - image="nginx:1.7.9", + image="nginx:1.15.4", ports=[client.V1ContainerPort(container_port=80)]) # Create and configurate a spec section template = client.V1PodTemplateSpec( metadata=client.V1ObjectMeta(labels={"app": "nginx"}), spec=client.V1PodSpec(containers=[container])) # Create the specification of deployment - spec = client.ExtensionsV1beta1DeploymentSpec( + spec = client.AppsV1beta1DeploymentSpec( replicas=3, template=template) # Instantiate the deployment object - deployment = client.ExtensionsV1beta1Deployment( - api_version="extensions/v1beta1", + deployment = client.AppsV1beta1Deployment( + api_version="apps/v1beta1", kind="Deployment", metadata=client.V1ObjectMeta(name=DEPLOYMENT_NAME), spec=spec) @@ -55,7 +55,7 @@ def create_deployment(api_instance, deployment): def update_deployment(api_instance, deployment): # Update container image - deployment.spec.template.spec.containers[0].image = "nginx:1.9.1" + deployment.spec.template.spec.containers[0].image = "nginx:1.16.0" # Update the deployment api_response = api_instance.patch_namespaced_deployment( name=DEPLOYMENT_NAME, @@ -80,16 +80,16 @@ def main(): # utility. If no argument provided, the config will be loaded from # default location. config.load_kube_config() - extensions_v1beta1 = client.ExtensionsV1beta1Api() + apps_v1beta1 = client.AppsV1beta1Api() # Create a deployment object with client-python API. The deployment we # created is same as the `nginx-deployment.yaml` in the /examples folder. deployment = create_deployment_object() - create_deployment(extensions_v1beta1, deployment) + create_deployment(apps_v1beta1, deployment) - update_deployment(extensions_v1beta1, deployment) + update_deployment(apps_v1beta1, deployment) - delete_deployment(extensions_v1beta1) + delete_deployment(apps_v1beta1) if __name__ == '__main__': diff --git a/examples/nginx-deployment.yaml b/examples/nginx-deployment.yaml index d05940d29b..5dd80da371 100644 --- a/examples/nginx-deployment.yaml +++ b/examples/nginx-deployment.yaml @@ -1,9 +1,14 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment + labels: + app: nginx spec: replicas: 3 + selector: + matchLabels: + app: nginx template: metadata: labels: @@ -11,7 +16,6 @@ spec: spec: containers: - name: nginx - image: nginx:1.7.9 + image: nginx:1.15.4 ports: - containerPort: 80 - diff --git a/examples/notebooks/create_deployment.ipynb b/examples/notebooks/create_deployment.ipynb index 0fa6c05105..b4e1229dd7 100644 --- a/examples/notebooks/create_deployment.ipynb +++ b/examples/notebooks/create_deployment.ipynb @@ -47,7 +47,7 @@ "outputs": [], "source": [ "config.load_kube_config()\n", - "extension = client.ExtensionsV1beta1Api()" + "apps_api = client.AppsV1beta1Api()" ] }, { @@ -70,7 +70,7 @@ }, "outputs": [], "source": [ - "deployment = client.ExtensionsV1beta1Deployment()" + "deployment = client.AppsV1beta1Deployment()" ] }, { @@ -93,7 +93,7 @@ }, "outputs": [], "source": [ - "deployment.api_version = \"extensions/v1beta1\"\n", + "deployment.api_version = \"apps/v1beta1\"\n", "deployment.kind = \"Deployment\"\n", "deployment.metadata = client.V1ObjectMeta(name=\"nginx-deployment\")" ] @@ -118,7 +118,7 @@ }, "outputs": [], "source": [ - "spec = client.ExtensionsV1beta1DeploymentSpec()\n", + "spec = client.AppsV1beta1DeploymentSpec()\n", "spec.replicas = 3" ] }, @@ -207,7 +207,7 @@ }, "outputs": [], "source": [ - "extension.create_namespaced_deployment(namespace=\"default\", body=deployment)" + "apps_api.create_namespaced_deployment(namespace=\"default\", body=deployment)" ] }, { @@ -253,7 +253,7 @@ }, "outputs": [], "source": [ - "extension.replace_namespaced_deployment(name=\"nginx-deployment\", namespace=\"default\", body=deployment)" + "apps_api.replace_namespaced_deployment(name=\"nginx-deployment\", namespace=\"default\", body=deployment)" ] }, { @@ -277,10 +277,10 @@ }, "outputs": [], "source": [ - "rollback = client.ExtensionsV1beta1DeploymentRollback()\n", - "rollback.api_version = \"extensions/v1beta1\"\n", + "rollback = client.AppsV1beta1DeploymentRollback()\n", + "rollback.api_version = \"apps/v1beta1\"\n", "rollback.kind = \"DeploymentRollback\"\n", - "rollback.rollback_to = client.ExtensionsV1beta1RollbackConfig()\n", + "rollback.rollback_to = client.AppsV1beta1RollbackConfig()\n", "rollback.rollback_to.revision = 0\n", "rollback.name = \"nginx-deployment\"" ] diff --git a/examples/notebooks/intro_notebook.ipynb b/examples/notebooks/intro_notebook.ipynb index 53bf63185b..b4e3b8e8e9 100644 --- a/examples/notebooks/intro_notebook.ipynb +++ b/examples/notebooks/intro_notebook.ipynb @@ -89,9 +89,9 @@ }, "outputs": [], "source": [ - "api_instance = client.ExtensionsV1beta1Api()\n", - "dep = client.ExtensionsV1beta1Deployment()\n", - "spec = client.ExtensionsV1beta1DeploymentSpec()" + "api_instance = client.AppsV1beta1Api()\n", + "dep = client.AppsV1beta1Deployment()\n", + "spec = client.AppsV1beta1DeploymentSpec()" ] }, { diff --git a/kubernetes/client/api_client.py b/kubernetes/client/api_client.py index 40a451cff5..9e60ded5f5 100644 --- a/kubernetes/client/api_client.py +++ b/kubernetes/client/api_client.py @@ -14,6 +14,7 @@ import os import re import json +import atexit import mimetypes import tempfile from multiprocessing.pool import ThreadPool @@ -74,12 +75,20 @@ def __init__(self, configuration=None, header_name=None, header_value=None, self.cookie = cookie # Set default User-Agent. self.user_agent = 'Swagger-Codegen/10.0.1/python' - - def __del__(self): + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def close(self): if self._pool: self._pool.close() self._pool.join() self._pool = None + if hasattr(atexit, 'unregister'): + atexit.unregister(self.close) @property def pool(self): @@ -87,6 +96,7 @@ def pool(self): avoids instantiating unused threadpool for blocking clients. """ if self._pool is None: + atexit.register(self.close) self._pool = ThreadPool(self.pool_threads) return self._pool diff --git a/kubernetes/e2e_test/test_extensions.py b/kubernetes/e2e_test/test_apps.py similarity index 84% rename from kubernetes/e2e_test/test_extensions.py rename to kubernetes/e2e_test/test_apps.py index b659910317..1908374a3e 100644 --- a/kubernetes/e2e_test/test_extensions.py +++ b/kubernetes/e2e_test/test_apps.py @@ -17,12 +17,12 @@ import yaml from kubernetes.client import api_client -from kubernetes.client.apis import extensions_v1beta1_api +from kubernetes.client.apis import apps_v1_api from kubernetes.client.models import v1_delete_options from kubernetes.e2e_test import base -class TestClientExtensions(unittest.TestCase): +class TestClientApps(unittest.TestCase): @classmethod def setUpClass(cls): @@ -30,14 +30,17 @@ def setUpClass(cls): def test_create_deployment(self): client = api_client.ApiClient(configuration=self.config) - api = extensions_v1beta1_api.ExtensionsV1beta1Api(client) + api = apps_v1_api.AppsV1Api(client) name = 'nginx-deployment-' + str(uuid.uuid4()) - deployment = '''apiVersion: extensions/v1beta1 + deployment = '''apiVersion: apps/v1 kind: Deployment metadata: name: %s spec: replicas: 3 + selector: + matchLabels: + app: nginx template: metadata: labels: @@ -45,7 +48,7 @@ def test_create_deployment(self): spec: containers: - name: nginx - image: nginx:1.7.9 + image: nginx:1.15.4 ports: - containerPort: 80 ''' @@ -60,16 +63,19 @@ def test_create_deployment(self): def test_create_daemonset(self): client = api_client.ApiClient(configuration=self.config) - api = extensions_v1beta1_api.ExtensionsV1beta1Api(client) + api = apps_v1_api.AppsV1Api(client) name = 'nginx-app-' + str(uuid.uuid4()) daemonset = { - 'apiVersion': 'extensions/v1beta1', + 'apiVersion': 'apps/v1', 'kind': 'DaemonSet', 'metadata': { 'labels': {'app': 'nginx'}, 'name': '%s' % name, }, 'spec': { + 'selector': { + 'matchLabels': {'app': 'nginx'}, + }, 'template': { 'metadata': { 'labels': {'app': 'nginx'}, @@ -77,7 +83,7 @@ def test_create_daemonset(self): 'spec': { 'containers': [ {'name': 'nginx-app', - 'image': 'nginx:1.10'}, + 'image': 'nginx:1.15.4'}, ], }, }, @@ -91,4 +97,4 @@ def test_create_daemonset(self): self.assertIsNotNone(resp) options = v1_delete_options.V1DeleteOptions() - resp = api.delete_namespaced_daemon_set(name, 'default', body=options) \ No newline at end of file + resp = api.delete_namespaced_daemon_set(name, 'default', body=options) diff --git a/kubernetes/e2e_test/test_utils.py b/kubernetes/e2e_test/test_utils.py index b5684a7986..0242a9c4dc 100644 --- a/kubernetes/e2e_test/test_utils.py +++ b/kubernetes/e2e_test/test_utils.py @@ -39,12 +39,12 @@ def tearDownClass(cls): def test_create_apps_deployment_from_yaml(self): """ - Should be able to create an apps/v1beta1 deployment. + Should be able to create an apps/v1 deployment. """ k8s_client = client.api_client.ApiClient(configuration=self.config) utils.create_from_yaml( k8s_client, self.path_prefix + "apps-deployment.yaml") - app_api = client.AppsV1beta1Api(k8s_client) + app_api = client.AppsV1Api(k8s_client) dep = app_api.read_namespaced_deployment(name="nginx-app", namespace="default") self.assertIsNotNone(dep) @@ -52,21 +52,6 @@ def test_create_apps_deployment_from_yaml(self): name="nginx-app", namespace="default", body={}) - def test_create_extensions_deployment_from_yaml(self): - """ - Should be able to create an extensions/v1beta1 deployment. - """ - k8s_client = client.api_client.ApiClient(configuration=self.config) - utils.create_from_yaml( - k8s_client, self.path_prefix + "extensions-deployment.yaml") - ext_api = client.ExtensionsV1beta1Api(k8s_client) - dep = ext_api.read_namespaced_deployment(name="nginx-deployment", - namespace="default") - self.assertIsNotNone(dep) - ext_api.delete_namespaced_deployment( - name="nginx-deployment", namespace="default", - body={}) - def test_create_pod_from_yaml(self): """ Should be able to create a pod. @@ -134,7 +119,7 @@ def test_create_deployment_non_default_namespace_from_yaml(self): utils.create_from_yaml( k8s_client, self.path_prefix + "dep-deployment.yaml") core_api = client.CoreV1Api(k8s_client) - ext_api = client.ExtensionsV1beta1Api(k8s_client) + ext_api = client.AppsV1Api(k8s_client) nmsp = core_api.read_namespace(name="dep") self.assertIsNotNone(nmsp) dep = ext_api.read_namespaced_deployment(name="nginx-deployment", @@ -186,7 +171,7 @@ def test_create_general_list_from_yaml(self): utils.create_from_yaml( k8s_client, self.path_prefix + "list.yaml") core_api = client.CoreV1Api(k8s_client) - ext_api = client.ExtensionsV1beta1Api(k8s_client) + ext_api = client.AppsV1Api(k8s_client) svc = core_api.read_namespaced_service(name="list-service-test", namespace="default") self.assertIsNotNone(svc) @@ -266,7 +251,7 @@ def test_create_from_list_in_multi_resource_yaml(self): utils.create_from_yaml( k8s_client, self.path_prefix + "multi-resource-with-list.yaml") core_api = client.CoreV1Api(k8s_client) - app_api = client.AppsV1beta1Api(k8s_client) + app_api = client.AppsV1Api(k8s_client) pod_0 = core_api.read_namespaced_pod( name="mock-pod-0", namespace="default") self.assertIsNotNone(pod_0) @@ -317,7 +302,7 @@ def test_create_from_multi_resource_yaml_with_conflict(self): def test_create_from_multi_resource_yaml_with_multi_conflicts(self): """ - Should create an extensions/v1beta1 deployment + Should create an apps/v1 deployment and fail to create the same deployment twice. Should raise an exception that contains two error messages. """ @@ -327,14 +312,14 @@ def test_create_from_multi_resource_yaml_with_multi_conflicts(self): k8s_client, self.path_prefix + "triple-nginx.yaml") exp_error = ('Error from server (Conflict): {"kind":"Status",' '"apiVersion":"v1","metadata":{},"status":"Failure",' - '"message":"deployments.extensions \\"triple-nginx\\" ' + '"message":"deployments.apps \\"triple-nginx\\" ' 'already exists","reason":"AlreadyExists",' - '"details":{"name":"triple-nginx","group":"extensions",' + '"details":{"name":"triple-nginx","group":"apps",' '"kind":"deployments"},"code":409}\n' ) exp_error += exp_error self.assertEqual(exp_error, str(cm.exception)) - ext_api = client.ExtensionsV1beta1Api(k8s_client) + ext_api = client.AppsV1Api(k8s_client) dep = ext_api.read_namespaced_deployment(name="triple-nginx", namespace="default") self.assertIsNotNone(dep) @@ -342,14 +327,16 @@ def test_create_from_multi_resource_yaml_with_multi_conflicts(self): name="triple-nginx", namespace="default", body={}) - def test_create_namespaces_apps_deployment_from_yaml(self): + def test_create_namespaced_apps_deployment_from_yaml(self): """ - Should be able to create an apps/v1beta1 deployment. + Should be able to create an apps/v1beta1 deployment + in a test namespace. """ k8s_client = client.api_client.ApiClient(configuration=self.config) utils.create_from_yaml( - k8s_client, self.path_prefix + "apps-deployment.yaml", namespace=self.test_namespace) - app_api = client.AppsV1beta1Api(k8s_client) + k8s_client, self.path_prefix + "apps-deployment.yaml", + namespace=self.test_namespace) + app_api = client.AppsV1Api(k8s_client) dep = app_api.read_namespaced_deployment(name="nginx-app", namespace=self.test_namespace) self.assertIsNotNone(dep) @@ -357,16 +344,17 @@ def test_create_namespaces_apps_deployment_from_yaml(self): name="nginx-app", namespace=self.test_namespace, body={}) - def test_create_from_list_in_multi_resource_yaml(self): + def test_create_from_list_in_multi_resource_yaml_namespaced(self): """ Should be able to create the items in the PodList and a deployment - specified in the multi-resource file + specified in the multi-resource file in a test namespace """ k8s_client = client.api_client.ApiClient(configuration=self.config) utils.create_from_yaml( - k8s_client, self.path_prefix + "multi-resource-with-list.yaml", namespace=self.test_namespace) + k8s_client, self.path_prefix + "multi-resource-with-list.yaml", + namespace=self.test_namespace) core_api = client.CoreV1Api(k8s_client) - app_api = client.AppsV1beta1Api(k8s_client) + app_api = client.AppsV1Api(k8s_client) pod_0 = core_api.read_namespaced_pod( name="mock-pod-0", namespace=self.test_namespace) self.assertIsNotNone(pod_0) diff --git a/kubernetes/e2e_test/test_yaml/apps-deployment.yaml b/kubernetes/e2e_test/test_yaml/apps-deployment.yaml index a2ffa6b996..eb33bbac55 100644 --- a/kubernetes/e2e_test/test_yaml/apps-deployment.yaml +++ b/kubernetes/e2e_test/test_yaml/apps-deployment.yaml @@ -1,4 +1,4 @@ -apiVersion: apps/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: nginx-app diff --git a/kubernetes/e2e_test/test_yaml/dep-deployment.yaml b/kubernetes/e2e_test/test_yaml/dep-deployment.yaml index be9b281f20..57170d92a4 100644 --- a/kubernetes/e2e_test/test_yaml/dep-deployment.yaml +++ b/kubernetes/e2e_test/test_yaml/dep-deployment.yaml @@ -1,10 +1,13 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: dep spec: replicas: 3 + selector: + matchLabels: + app: nginx template: metadata: labels: @@ -12,7 +15,6 @@ spec: spec: containers: - name: nginx - image: nginx:1.7.9 + image: nginx:1.15.4 ports: - containerPort: 80 - diff --git a/kubernetes/e2e_test/test_yaml/extensions-deployment.yaml b/kubernetes/e2e_test/test_yaml/extensions-deployment.yaml deleted file mode 100644 index d05940d29b..0000000000 --- a/kubernetes/e2e_test/test_yaml/extensions-deployment.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: nginx-deployment -spec: - replicas: 3 - template: - metadata: - labels: - app: nginx - spec: - containers: - - name: nginx - image: nginx:1.7.9 - ports: - - containerPort: 80 - diff --git a/kubernetes/e2e_test/test_yaml/list.yaml b/kubernetes/e2e_test/test_yaml/list.yaml index 3416ec429d..15aab9ad5c 100644 --- a/kubernetes/e2e_test/test_yaml/list.yaml +++ b/kubernetes/e2e_test/test_yaml/list.yaml @@ -11,7 +11,7 @@ items: port: 80 selector: app: list-deployment-test -- apiVersion: extensions/v1beta1 +- apiVersion: apps/v1 kind: Deployment metadata: name: list-deployment-test @@ -19,6 +19,9 @@ items: app: list-deployment-test spec: replicas: 1 + selector: + matchLabels: + app: list-deployment-test template: metadata: labels: @@ -26,4 +29,4 @@ items: spec: containers: - name: nginx - image: nginx \ No newline at end of file + image: nginx:1.15.4 \ No newline at end of file diff --git a/kubernetes/e2e_test/test_yaml/multi-resource-with-list.yaml b/kubernetes/e2e_test/test_yaml/multi-resource-with-list.yaml index b3228b8cde..996e4b6961 100644 --- a/kubernetes/e2e_test/test_yaml/multi-resource-with-list.yaml +++ b/kubernetes/e2e_test/test_yaml/multi-resource-with-list.yaml @@ -24,7 +24,7 @@ items: image: busybox command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600'] --- -apiVersion: apps/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: mock diff --git a/kubernetes/e2e_test/test_yaml/triple-nginx.yaml b/kubernetes/e2e_test/test_yaml/triple-nginx.yaml index 9a4fda66cf..a071c88b50 100644 --- a/kubernetes/e2e_test/test_yaml/triple-nginx.yaml +++ b/kubernetes/e2e_test/test_yaml/triple-nginx.yaml @@ -1,9 +1,12 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: triple-nginx spec: replicas: 3 + selector: + matchLabels: + app: nginx template: metadata: labels: @@ -11,16 +14,19 @@ spec: spec: containers: - name: nginx - image: nginx:1.7.9 + image: nginx:1.15.4 ports: - containerPort: 80 --- -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: triple-nginx spec: replicas: 3 + selector: + matchLabels: + app: nginx template: metadata: labels: @@ -28,16 +34,19 @@ spec: spec: containers: - name: nginx - image: nginx:1.7.9 + image: nginx:1.15.4 ports: - containerPort: 80 --- -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: Deployment metadata: name: triple-nginx spec: replicas: 3 + selector: + matchLabels: + app: nginx template: metadata: labels: @@ -45,6 +54,6 @@ spec: spec: containers: - name: nginx - image: nginx:1.7.9 + image: nginx:1.15.4 ports: - containerPort: 80 \ No newline at end of file diff --git a/kubernetes/test/test_api_client.py b/kubernetes/test/test_api_client.py new file mode 100644 index 0000000000..f0a9416cf7 --- /dev/null +++ b/kubernetes/test/test_api_client.py @@ -0,0 +1,25 @@ +# coding: utf-8 + + +import atexit +import weakref +import unittest + +import kubernetes + + +class TestApiClient(unittest.TestCase): + + def test_context_manager_closes_threadpool(self): + with kubernetes.client.ApiClient() as client: + self.assertIsNotNone(client.pool) + pool_ref = weakref.ref(client._pool) + self.assertIsNotNone(pool_ref()) + self.assertIsNone(pool_ref()) + + def test_atexit_closes_threadpool(self): + client = kubernetes.client.ApiClient() + self.assertIsNotNone(client.pool) + self.assertIsNotNone(client._pool) + atexit._run_exitfuncs() + self.assertIsNone(client._pool) diff --git a/requirements.txt b/requirements.txt index 74e38b7cd4..05a4dd3807 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ certifi>=14.05.14 # MPL six>=1.9.0 # MIT python-dateutil>=2.5.3 # BSD setuptools>=21.0.0 # PSF/ZPL -pyyaml>=3.12 # MIT +pyyaml~=3.12 # MIT google-auth>=1.0.1 # Apache-2.0 ipaddress>=1.0.17;python_version=="2.7" # PSF websocket-client>=0.32.0,!=0.40.0,!=0.41.*,!=0.42.* # LGPLv2+