Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions doc/source/readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,40 @@ To list all pods:
More complicated examples, like asynchronous multiple watch or tail logs from pods,
you can find in `examples/` folder.

There is also support for DynamicClient which will query the cluster for all supported
resources. This allows for dynamic resource selection at runtime.
The above example using DynamicClient would look like this:
::

import asyncio
from kubernetes_asyncio import config
from kubernetes_asyncio.client.api_client import ApiClient
from kubernetes_asyncio.dynamic import DynamicClient


async def main():
# Configs can be set in Configuration class directly or using helper
# utility. If no argument provided, the config will be loaded from
# default location.
await config.load_kube_config()

# use the context manager to close http sessions automatically
async with ApiClient() as api:
client = await DynamicClient(api)
v1 = await client.resources.get(api_version="v1", kind="Pod")
print("Listing pods with their IPs:")
ret = await v1.get()

for i in ret.items:
print(i.status.pod_ip, i.metadata.namespace, i.metadata.name)


if __name__ == '__main__':
loop = asyncio.new_event_loop()
loop.run_until_complete(main())
loop.close()

Additional examples are in the `examples/dynamic-client` folder.

Versions
--------
Expand Down
49 changes: 49 additions & 0 deletions examples/dynamic-client/accept_header.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 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 examples/dynamic-client/cluster_scoped_custom_resource.py
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()
92 changes: 92 additions & 0 deletions examples/dynamic-client/configmap.py
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()
Loading