all repos — janny @ ec88575db2d8915a34cc523c2e2f01af6575ee4f

clean up Kubernetes resources after a set TTL

Implement event creation on resource cleanup
Anirudh Oppiliappan x@icyphox.sh
Sat, 13 Mar 2021 10:20:42 +0530
commit

ec88575db2d8915a34cc523c2e2f01af6575ee4f

parent

4f685e1161bc6eb5e47c4922d15d620d28bccd0f

4 files changed, 71 insertions(+), 34 deletions(-)

jump to
M deploy/clusterrole.yamldeploy/clusterrole.yaml

@@ -6,33 +6,15 @@ rules:

- apiGroups: - "" resources: - - pods - - services - - configmaps + - events verbs: - - get - - list - - watch - - delete + - create - apiGroups: - - apps + - "*" resources: - - deployments - - daemonsets - - statefulsets - - replicasets - - controllerrevisions + - "*" verbs: - get - - list - watch - - delete -- apiGroups: - - batch - resources: - - jobs - verbs: - - get - list - - watch - delete
M janny/cleanup.pyjanny/cleanup.py

@@ -1,5 +1,7 @@

import time import json +import datetime +from types import SimpleNamespace from janny.utils import parse_delta, RUNNING from janny.auth import SESSION as s

@@ -7,27 +9,73 @@ from janny.config import API_HOST, logger

def clean_up( - url: str, kube_resource: str, resource_name: str, kill_time: str, namespace: str + url: str, resource_obj: SimpleNamespace, object_name: str, kill_time: str, namespace: str ): """ - Parse the kill_time and create call send_delete_event(). + Parse the kill_time and create call send_delete_request(); create an event after deletion. """ delta = parse_delta(kill_time) secs = delta.total_seconds() time.sleep(secs) - logger.info(f"Slept for {kill_time}. Cleaning resource {resource_name} now.") - send_delete_event(url, kube_resource, resource_name, namespace) + logger.info(f"Slept for {kill_time}, cleaning resource {object_name} now") + send_delete_request(url, resource_obj.name, object_name, namespace) + message = f"Successfully deleted {resource_obj.name}/{object_name}" + now = datetime.datetime.utcnow().isoformat() + "Z" + event = { + "metadata": { + "namespace": namespace, + "generateName": "janny-", + }, + "type": "Normal", + "count": 1, + "action": f"Deleted resource {object_name}", + "eventTime": now, + "firstTimestamp": now, + "reason": "ResourceDeleted", + "message": message, + "involvedObject": { + "apiVersion": "v1", + "name": object_name, + "namespace": namespace, + "kind": resource_obj.kind, + }, + "reportingComponent": "janny", + "reportingInstance": "janny", + "source": { + "component": "janny", + }, + } -def send_delete_event(url: str, kube_resource: str, resource_name: str, namespace: str): + create_event(event, namespace) + + +def send_delete_request( + url: str, kube_resource: str, resource_name: str, namespace: str +): """ Sends a DELETE request to the resource. """ api_url = f"{API_HOST}{url}/namespaces/{namespace}/{kube_resource}/{resource_name}" response = s.delete(api_url, params={"propagationPolicy": "Background"}) - logger.info(f"Sent delete event to {kube_resource}/{resource_name}") + logger.info(f"Sent delete request to {kube_resource}/{resource_name}") response_json = json.loads(response.content) if "Success" not in response_json.values(): - logger.error(f"Deletion did not succeed. Recieved: {response_json}") + logger.error(f"Deletion failed! Recieved: {response_json}") RUNNING.remove(resource_name) + + +def create_event(event: dict, namespace: str): + """ + Create an Event for resource deletion. + FIXME: Maybe don't hardcode the group URL? + """ + + api_url = f"{API_HOST}/api/v1/namespaces/{namespace}/events" + response = s.post(api_url, json=event) + response_json = json.loads(response.content) + if "Failure" not in response_json.values(): + logger.info(f"Created event") + else: + logger.error(f"Failed to create event! Recieved: {response_json}")
M janny/main.pyjanny/main.py

@@ -6,7 +6,6 @@ from janny.cleanup import clean_up

from janny.config import logger - def get_resource_urls() -> list: """ Returns a list of tuples of all namespaced resources.

@@ -50,17 +49,24 @@ resource_list = get(f"{url}/namespaces/{namespace}/{resource.name}")

try: for r in resource_list.items: try: + annotations = vars(r.metadata.annotations) if ( - "janny.ttl" in vars(r.metadata.annotations) + "janny.ttl" in annotations and r.metadata.name not in RUNNING ): logger.info( - f"New resource to clean up: {resource.name}/{r.metadata.name}: {vars(r.metadata.annotations)}" + f"New resource to clean up: {resource.name}/{r.metadata.name}: ttl: {annotations['janny.ttl']}" ) kill_time = vars(r.metadata.annotations)["janny.ttl"] t = threading.Thread( target=clean_up, - args=[url, resource.name, r.metadata.name, kill_time, namespace], + args=[ + url, + resource, + r.metadata.name, + kill_time, + namespace, + ], ) logger.info(f"Starting cleaner thread for {r.metadata.name}") RUNNING.append(r.metadata.name)
M janny/utils.pyjanny/utils.py

@@ -36,5 +36,6 @@ parts = {k: int(v) for k, v in match.groupdict().items() if v}

return timedelta(**parts) return timedelta(0) -# List of running threads + +# List of running threads. Storing it here to prevent circular imports. RUNNING = list()