From 125c5d03d5de0ecc027134ab21470762ba218fa5 Mon Sep 17 00:00:00 2001 From: Sergey Vasilyev Date: Tue, 18 Jun 2019 12:01:28 +0200 Subject: [PATCH] Add an example for built-in pods controller --- examples/09-builtins/README.md | 43 +++++++++++++++++++++++++++++++++ examples/09-builtins/example.py | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 examples/09-builtins/README.md create mode 100644 examples/09-builtins/example.py diff --git a/examples/09-builtins/README.md b/examples/09-builtins/README.md new file mode 100644 index 00000000..c10aaed1 --- /dev/null +++ b/examples/09-builtins/README.md @@ -0,0 +1,43 @@ +# Kopf example for built-in resources + +Kopf can also handle the built-in resources, such as Pods, Jobs, etc. + +In this example, we take control all over the pods (namespaced/cluster-wide), +and allow the pods to exist for no longer than 30 seconds -- +either after creation or after the operator restart. + +For no specific reason, just for fun. Maybe, as a way of Chaos Engineering +to force making the resilient applications (tolerant to pod killing). + +However, the system namespaces (kube-system, etc) are explicitly protected -- +to prevent killing the cluster itself. + +Start the operator: + +```bash +kopf run example.py --verbose +``` + +Start a sample pod: + +```bash +kubectl run -it --image=ubuntu expr1 -- bash -i +# wait for 30s +``` + +Since `kubectl run` creates a Deployment, not just a Pod, +a new pod will be created every 30 seconds. Observe with: + +```bash +kubectl get pods --watch +``` + +*Please note that Kopf puts a finalizer on the managed resources, +so the pod deletion will be blocked unless the operator is running +(to remove the finalizer). This will be made optional in #24.* + +Cleanup in the end: + +```bash +$ kubectl delete deployment expr1 +``` diff --git a/examples/09-builtins/example.py b/examples/09-builtins/example.py new file mode 100644 index 00000000..a5e70e81 --- /dev/null +++ b/examples/09-builtins/example.py @@ -0,0 +1,43 @@ +import asyncio + +import kopf +import pykube + +TIMEOUT = 30 +tasks = {} # dict{namespace: dict{name: asyncio.Task}} + + +@kopf.on.resume('', 'v1', 'pods') +@kopf.on.create('', 'v1', 'pods') +async def pod_in_sight(namespace, name, logger, **kwargs): + if namespace.startswith('kube-'): + return + else: + task = asyncio.create_task(pod_killer(namespace, name, logger)) + tasks.setdefault(namespace, {}) + tasks[namespace][name] = task + + +@kopf.on.delete('', 'v1', 'pods') +async def pod_deleted(namespace, name, **kwargs): + if namespace in tasks and name in tasks[namespace]: + task = tasks[namespace][name] + task.cancel() # it will also remove from `tasks` + + +async def pod_killer(namespace, name, logger): + try: + logger.info(f"=== Pod killing happens in {TIMEOUT}s.") + await asyncio.sleep(TIMEOUT) + logger.info(f"=== Pod killing happens NOW!") + + api = kopf.get_pykube_api() + pod = pykube.Pod.objects(api, namespace=namespace).get_by_name(name) + pod.delete() + + except asyncio.CancelledError: + logger.info(f"=== Pod killing is cancelled!") + + finally: + if namespace in tasks and name in tasks[namespace]: + del tasks[namespace][name]