-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add dynamic client support * Add dynamic client support * refactor(dynamic): use await magic method * Add docs readme info for DynamicClient Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * Fix example Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * Fix examples Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * Remove requirements entry and fix example Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * [docs] add readme info for DynamicClient Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * [fix] cleanup examples Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * [refactor] review feedback changes Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * [refactor] remove changes from client/rest.py Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * [test] Fix e2e test case Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * [lint] Fix flake8 errors Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> * [lint] Fix isort errors Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com> --------- Signed-off-by: Bob Haddleton <bob.haddleton@nokia.com>
- Loading branch information
Showing
19 changed files
with
3,148 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Copyright 2021 The Kubernetes Authors. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
""" | ||
This example demonstrates how to pass the custom header in the cluster. | ||
""" | ||
|
||
import asyncio | ||
|
||
from kubernetes_asyncio.client import api_client | ||
from kubernetes_asyncio.client.configuration import Configuration | ||
from kubernetes_asyncio.config import kube_config | ||
from kubernetes_asyncio.dynamic import DynamicClient | ||
|
||
|
||
async def main(): | ||
# Creating a dynamic client | ||
config = Configuration() | ||
await kube_config.load_kube_config(client_configuration=config) | ||
async with api_client.ApiClient(configuration=config) as apic: | ||
async with DynamicClient(apic) as client: | ||
# fetching the node api | ||
api = await client.resources.get(api_version="v1", kind="Node") | ||
|
||
# Creating a custom header | ||
params = {'header_params': {'Accept': 'application/json;as=PartialObjectMetadataList;v=v1;g=meta.k8s.io'}} | ||
|
||
resp = await api.get(**params) | ||
|
||
# Printing the kind and apiVersion after passing new header params. | ||
print("VERSION\t\t\t\tKIND") | ||
print(f"{resp.apiVersion}\t\t{resp.kind}") | ||
|
||
|
||
if __name__ == "__main__": | ||
loop = asyncio.new_event_loop() | ||
loop.run_until_complete(main()) | ||
loop.close() |
198 changes: 198 additions & 0 deletions
198
examples/dynamic-client/cluster_scoped_custom_resource.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
# Copyright 2021 The Kubernetes Authors. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
""" | ||
This example demonstrates the following: | ||
- Creation of a custom resource definition (CRD) using dynamic-client | ||
- Creation of cluster scoped custom resources (CR) using the above created CRD | ||
- List, patch (update), delete the custom resources | ||
- Delete the custom resource definition (CRD) | ||
""" | ||
|
||
import asyncio | ||
|
||
from kubernetes_asyncio.client import api_client | ||
from kubernetes_asyncio.client.configuration import Configuration | ||
from kubernetes_asyncio.config import kube_config | ||
from kubernetes_asyncio.dynamic import DynamicClient | ||
from kubernetes_asyncio.dynamic.exceptions import ResourceNotFoundError | ||
|
||
|
||
async def main(): | ||
# Creating a dynamic client | ||
config = Configuration() | ||
await kube_config.load_kube_config(client_configuration=config) | ||
async with api_client.ApiClient(configuration=config) as apic: | ||
client = await DynamicClient(apic) | ||
|
||
# fetching the custom resource definition (CRD) api | ||
crd_api = await client.resources.get( | ||
api_version="apiextensions.k8s.io/v1", kind="CustomResourceDefinition" | ||
) | ||
|
||
# Creating a Namespaced CRD named "ingressroutes.apps.example.com" | ||
name = "ingressroutes.apps.example.com" | ||
|
||
crd_manifest = { | ||
"apiVersion": "apiextensions.k8s.io/v1", | ||
"kind": "CustomResourceDefinition", | ||
"metadata": { | ||
"name": name, | ||
}, | ||
"spec": { | ||
"group": "apps.example.com", | ||
"versions": [ | ||
{ | ||
"name": "v1", | ||
"schema": { | ||
"openAPIV3Schema": { | ||
"properties": { | ||
"spec": { | ||
"properties": { | ||
"strategy": {"type": "string"}, | ||
"virtualhost": { | ||
"properties": { | ||
"fqdn": {"type": "string"}, | ||
"tls": { | ||
"properties": { | ||
"secretName": {"type": "string"} | ||
}, | ||
"type": "object", | ||
}, | ||
}, | ||
"type": "object", | ||
}, | ||
}, | ||
"type": "object", | ||
} | ||
}, | ||
"type": "object", | ||
} | ||
}, | ||
"served": True, | ||
"storage": True, | ||
} | ||
], | ||
"scope": "Cluster", | ||
"names": { | ||
"plural": "ingressroutes", | ||
"listKind": "IngressRouteList", | ||
"singular": "ingressroute", | ||
"kind": "IngressRoute", | ||
"shortNames": ["ir"], | ||
}, | ||
}, | ||
} | ||
|
||
crd_creation_response = await crd_api.create(crd_manifest) | ||
print( | ||
"\n[INFO] custom resource definition `ingressroutes.apps.example.com` created\n" | ||
) | ||
print("SCOPE\t\tNAME") | ||
print(f"{crd_creation_response.spec.scope}\t\t{crd_creation_response.metadata.name}") | ||
|
||
# Fetching the "ingressroutes" CRD api | ||
|
||
try: | ||
await client.resources.get( | ||
api_version="apps.example.com/v1", kind="IngressRoute" | ||
) | ||
except ResourceNotFoundError: | ||
# Need to wait a sec for the discovery layer to get updated | ||
await asyncio.sleep(2) | ||
|
||
ingressroute_api = await client.resources.get( | ||
api_version="apps.example.com/v1", kind="IngressRoute" | ||
) | ||
|
||
# Creating a custom resource (CR) `ingress-route-*`, using the above CRD `ingressroutes.apps.example.com` | ||
|
||
ingressroute_manifest_first = { | ||
"apiVersion": "apps.example.com/v1", | ||
"kind": "IngressRoute", | ||
"metadata": { | ||
"name": "ingress-route-first", | ||
}, | ||
"spec": { | ||
"virtualhost": { | ||
"fqdn": "www.google.com", | ||
"tls": {"secretName": "google-tls"}, | ||
}, | ||
"strategy": "RoundRobin", | ||
}, | ||
} | ||
|
||
ingressroute_manifest_second = { | ||
"apiVersion": "apps.example.com/v1", | ||
"kind": "IngressRoute", | ||
"metadata": { | ||
"name": "ingress-route-second", | ||
}, | ||
"spec": { | ||
"virtualhost": { | ||
"fqdn": "www.yahoo.com", | ||
"tls": {"secretName": "yahoo-tls"}, | ||
}, | ||
"strategy": "RoundRobin", | ||
}, | ||
} | ||
|
||
await ingressroute_api.create(body=ingressroute_manifest_first) | ||
await ingressroute_api.create(body=ingressroute_manifest_second) | ||
print("\n[INFO] custom resources `ingress-route-*` created\n") | ||
|
||
# Listing the `ingress-route-*` custom resources | ||
|
||
ingress_routes_list = await ingressroute_api.get() | ||
print("NAME\t\t\t\tFQDN\t\tTLS\t\t\t\tSTRATEGY") | ||
for item in ingress_routes_list.items: | ||
print(f"{item.metadata.name}\t{item.spec.virtualhost.fqdn}\t{item.spec.virtualhost.tls}\t" | ||
f"{item.spec.strategy}") | ||
|
||
# Patching the ingressroutes custom resources | ||
|
||
ingressroute_manifest_first["spec"]["strategy"] = "Random" | ||
ingressroute_manifest_second["spec"]["strategy"] = "WeightedLeastRequest" | ||
|
||
await ingressroute_api.patch(body=ingressroute_manifest_first, content_type="application/merge-patch+json") | ||
await ingressroute_api.patch(body=ingressroute_manifest_second, content_type="application/merge-patch+json") | ||
|
||
print( | ||
"\n[INFO] custom resources `ingress-route-*` patched to update the strategy\n" | ||
) | ||
patched_ingress_routes_list = await ingressroute_api.get() | ||
print("NAME\t\t\t\tFQDN\t\t\tTLS\t\t\tSTRATEGY") | ||
for item in patched_ingress_routes_list.items: | ||
print(f"{item.metadata.name}\t{item.spec.virtualhost.fqdn}\t{item.spec.virtualhost.tls}\t" | ||
f"{item.spec.strategy}") | ||
|
||
# Deleting the ingressroutes custom resources | ||
|
||
await ingressroute_api.delete(name="ingress-route-first") | ||
await ingressroute_api.delete(name="ingress-route-second") | ||
|
||
print("\n[INFO] custom resources `ingress-route-*` deleted") | ||
|
||
# Deleting the ingressroutes.apps.example.com custom resource definition | ||
|
||
await crd_api.delete(name=name) | ||
print( | ||
"\n[INFO] custom resource definition `ingressroutes.apps.example.com` deleted" | ||
) | ||
|
||
|
||
if __name__ == "__main__": | ||
loop = asyncio.new_event_loop() | ||
loop.run_until_complete(main()) | ||
loop.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# Copyright 2021 The Kubernetes Authors. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
""" | ||
This example demonstrates the following: | ||
- Creation of a k8s configmap using dynamic-client | ||
- List, patch(update), delete the configmap | ||
""" | ||
|
||
import asyncio | ||
|
||
from kubernetes_asyncio.client import api_client | ||
from kubernetes_asyncio.client.configuration import Configuration | ||
from kubernetes_asyncio.config import kube_config | ||
from kubernetes_asyncio.dynamic import DynamicClient | ||
|
||
|
||
async def main(): | ||
# Creating a dynamic client | ||
config = Configuration() | ||
await kube_config.load_kube_config(client_configuration=config) | ||
async with api_client.ApiClient(configuration=config) as apic: | ||
client = await DynamicClient(apic) | ||
|
||
# fetching the configmap api | ||
api = await client.resources.get(api_version="v1", kind="ConfigMap") | ||
|
||
configmap_name = "test-configmap" | ||
|
||
configmap_manifest = { | ||
"kind": "ConfigMap", | ||
"apiVersion": "v1", | ||
"metadata": { | ||
"name": configmap_name, | ||
"labels": { | ||
"foo": "bar", | ||
}, | ||
}, | ||
"data": { | ||
"config.json": '{"command":"/usr/bin/mysqld_safe"}', | ||
"frontend.cnf": "[mysqld]\nbind-address = 10.0.0.3\n", | ||
}, | ||
} | ||
|
||
# Creating configmap `test-configmap` in the `default` namespace | ||
|
||
await api.create(body=configmap_manifest, namespace="default") | ||
|
||
print("\n[INFO] configmap `test-configmap` created\n") | ||
|
||
# Listing the configmaps in the `default` namespace | ||
|
||
configmap_list = await api.get( | ||
name=configmap_name, namespace="default", label_selector="foo=bar" | ||
) | ||
|
||
print(f"NAME:\n{configmap_list.metadata.name}\n") | ||
print(f"DATA:\n{configmap_list.data}\n") | ||
|
||
# Updating the configmap's data, `config.json` | ||
|
||
configmap_manifest["data"]["config.json"] = "{}" | ||
|
||
configmap_patched = await api.patch( | ||
name=configmap_name, namespace="default", body=configmap_manifest | ||
) | ||
|
||
print("\n[INFO] configmap `test-configmap` patched\n") | ||
print(f"NAME:\n{configmap_patched.metadata.name}\n") | ||
print(f"DATA:\n{configmap_patched.data}\n") | ||
|
||
# Deleting configmap `test-configmap` from the `default` namespace | ||
|
||
await api.delete(name=configmap_name, body={}, namespace="default") | ||
print("\n[INFO] configmap `test-configmap` deleted\n") | ||
|
||
|
||
if __name__ == "__main__": | ||
loop = asyncio.new_event_loop() | ||
loop.run_until_complete(main()) | ||
loop.close() |
Oops, something went wrong.