{"id":17132175,"url":"https://github.com/bitsofinfo/k8swatcher","last_synced_at":"2025-07-26T06:14:42.633Z","repository":{"id":57676731,"uuid":"488636895","full_name":"bitsofinfo/k8swatcher","owner":"bitsofinfo","description":"Python module that simplifies k8s watching anything on your k8s cluster","archived":false,"fork":false,"pushed_at":"2022-06-10T20:27:43.000Z","size":54,"stargazers_count":5,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-16T10:21:41.893Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bitsofinfo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-05-04T15:09:49.000Z","updated_at":"2024-04-09T12:12:16.000Z","dependencies_parsed_at":"2022-09-14T11:42:04.728Z","dependency_job_id":null,"html_url":"https://github.com/bitsofinfo/k8swatcher","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/bitsofinfo/k8swatcher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fk8swatcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fk8swatcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fk8swatcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fk8swatcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bitsofinfo","download_url":"https://codeload.github.com/bitsofinfo/k8swatcher/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsofinfo%2Fk8swatcher/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266180266,"owners_count":23888725,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T19:26:16.303Z","updated_at":"2025-07-26T06:14:42.610Z","avatar_url":"https://github.com/bitsofinfo.png","language":"Python","readme":"# k8swatcher \u003c!-- omit in toc --\u003e\n\n![GitHub Actions status](https://github.com/bitsofinfo/k8swatcher/actions/workflows/pypi.yml/badge.svg) [![PyPI version](https://badge.fury.io/py/k8swatcher.svg)](https://badge.fury.io/py/k8swatcher) \n\nPython module that simplifies watching anything on a kubernetes cluster. You can utilize this module in your own python application to fulfill the typical *\"list then watch\"* functionality as described in the \"[Efficient detection of changes](https://kubernetes.io/docs/reference/using-api/api-concepts/#efficient-detection-of-changes)\" section of the kubernetes API documentation. (without having to write that code yourself!). This module utilizes the [Python Kubernetes Client](https://github.com/kubernetes-client) under the covers. \n\n- [Install](#install)\n- [Using in your python code](#using-in-your-python-code)\n  - [kubernetes configuration](#kubernetes-configuration)\n  - [Direct](#direct)\n  - [Queuing via K8sWatcherService](#queuing-via-k8swatcherservice)\n  - [Asyncio via K8sWatcherService and a K8sEventHandler](#asyncio-via-k8swatcherservice-and-a-k8seventhandler)\n- [run locally w/ the built in CLI](#run-locally-w-the-built-in-cli)\n- [example CLI output](#example-cli-output)\n- [local dev](#local-dev)\n- [related projects](#related-projects)\n- [todo](#todo)\n\n## Install\n\n```\npip install k8swatcher\n```\n## Using in your python code\n\nThere are a few different ways you can utilize this module in your code. Note that the underlying [Python Kubernetes Client](https://github.com/kubernetes-client) does not support asyncio, so in real-world applications, you generally will be dealing w/ python `Threads` to handle events that come from the underlying library. The `K8sWatcherService` provides some convienence methods for this.\n\n### kubernetes configuration\n\nCurrently the module determines the kubernetes connection configuration via the `kubernetes.config.load_kube_config()` method which relies on the excuting processes's `~/.kube/config` current context (by default), unless you specify a specific file/context to use via the `K8sWatcher` and `K8sWatcherService` constructors.\n### Direct\n\nThe direct mode gives you more control on how process each `K8sWatchEvent` that is `yielded` from the `K8sWatcher`. \n\nFor each type of kubernetes object you want to watch... wire up a separate `K8sWatcher` then call its `watcher()` method which returns a long-lived `Generator` that returns `K8sWatchEvent` objects as things happen.\n\nA typical usage pattern would be to wire up separate `Threads`, one per thing you wish to watch, bind to `Queues` etc and then go from there...\n\n```python\nimport json\nfrom k8swatcher import K8sWatchConfig, K8sWatcher, K8sWatchEvent\n\nwatch_config = K8sWatchConfig(**{ \\\n                    \"namespace\": \"my-namespace\", \\\n                    \"kind\": \"Pod\", \\\n                    \"sdk_client_class_name\": \"CoreV1Api\", \\\n                    \"sdk_list_function_name\": \"list_namespaced_pod\", \\\n                    \"field_selector\": None, \\\n                    \"label_selector\": \"mylabel=x,myotherlabel=y\",\n                    \"include_k8s_objects\": True\n                })\n\nk8s_watcher = K8sWatcher(watch_config).watcher()\n\nfor event in k8s_watcher:\n    print(json.dumps(event.dict(),default=str,indent=2))\n    \n```\n\n### Queuing via K8sWatcherService\n\nIf you are not interested in writing your own consumer `Thread` code and would like each `K8sWatchEvent` to be delivered via python `Queues` you can use the `K8sWatcherService.queuing_watch(K8sWatchConfig, unified_queue=True|False)` method. Each time you call `queuing_watch`, `K8sWatcherService` creates a new `Thread` bound to a unique `K8sWatcher` instance to automatically capture all events emitted from it. Each event will be placed on a `Queue` that is returned to you. If you pass `unified_queue=True`, the same `Queue` instance will be returned for every call to `queuing_watch()` so you only have to monitor a single `Queue` that will contains different `K8sWatchEvents` across all the different `K8sWatchConfigs` you define.\n\n```python\nimport json\nfrom k8swatcher import K8sWatchConfig, K8sWatchService, K8sWatchEvent\nfrom threading import Thread\n\nclass MyConsumerThread(Thread):\n\n    def __init__(self, event_queue_to_monitor, *args, **kwargs):\n        kwargs.setdefault('daemon', True)\n        super().__init__(*args, **kwargs)\n\n    def run(self):\n        while True:\n            watch_event:K8sWatchEvent = self.watch_event_queue.get()\n            print(json.dumps(watch_event.dict(),default=str,indent=2))\n\npod_watch_config = K8sWatchConfig(**{ \\\n                    \"id\": k8s_kind,\n                    \"namespace\": k8s_namespace, \\\n                    \"kind\": \"Pod\", \\\n                  ...})\n\ningress_watch_config = K8sWatchConfig(**{ \\\n                    \"id\": k8s_kind,\n                    \"namespace\": k8s_namespace, \\\n                    \"kind\": \"Ingress\", \\\n                  ...})\n\nwatch_service = K8sWatcherService()\n\n\"\"\"\nWith `unified_queue=False` (the default):\n\n... each distinct call to queuing_watch() returns\na dedicated Queue per K8sWatchConfig\n\"\"\"\npod_event_queue:Queue = watch_service.queuing_watch(pod_watch_config)\ningress_event_queue:Queue = watch_service.queuing_watch(ingress_watch_config)\n\npod_consumer = MyConsumerThread(pod_event_queue)\npod_consumer.start()\n\ningress_consumer = MyConsumerThread(ingress_event_queue)\ningress_consumer.start()\n\npod_consumer.join()\ningress_consuner.join()\nwatch_service.join()\n\n\n\"\"\"\nHowever with `unified_queue=True`:\n\n... each distinct call to queuing_watch() returns\na the same Queue that will get events for all K8sWatchConfigs \n\"\"\"\nglobal_event_queue:Queue = watch_service.queuing_watch(pod_watch_config,unified_queue=True)\nwatch_service.queuing_watch(ingress_watch_config,unified_queue=True)\n\nall_events_consumer = MyConsumerThread(global_event_queue)\nall_events_consumer.start()\nall_events_consumer.join()\n\nwatch_service.join()\n...\n\n```\n\n### Asyncio via K8sWatcherService and a K8sEventHandler\n\nIf you don't want to manage any `Threads` at all, you can utilize the `K8sEventHandler` method. In this usage pattern you simply provide a class instance that implements the `K8sEventHandler` method `async def handle_k8s_watch_event(self, k8s_watch_event:K8sWatchEvent)` and your handler class will be called every time a new `K8sWatchEvent` is created. Internally `K8sWatcherService` manages a consumer thread automatically for you that captures all events and calls your `async` handler and then `awaits` it's finish.\n\n```python\nimport json\nfrom k8swatcher import K8sWatchConfig, K8sWatchService, K8sWatchEvent\n\nclass MyCustomHandler(K8sEventHandler):\n    async def handle_k8s_watch_event(self, k8s_watch_event:K8sWatchEvent):\n        watch_event:K8sWatchEvent = self.watch_event_queue.get()\n        print(json.dumps(watch_event.dict(),default=str,indent=2))\n        await doMyCustomStuff(k8s_watch_event)\n\n\npod_watch_config = K8sWatchConfig(**{ \\\n                    \"id\": k8s_kind,\n                    \"namespace\": k8s_namespace, \\\n                    \"kind\": \"Pod\", \\\n                  ...})\n\ningress_watch_config = K8sWatchConfig(**{ \\\n                    \"id\": k8s_kind,\n                    \"namespace\": k8s_namespace, \\\n                    \"kind\": \"Ingress\", \\\n                  ...})\n\nwatch_service = K8sWatcherService()\n\nwatch_service.asyncio_watch([pod_watch_config,ingress_watch_config],MyCustomHandler())\n\nwatch_service.join()\n```\n\n## run locally w/ the built in CLI\n\nIn addition to being able to utilize this module inline in your code, this module also includes a simple CLI you can use for testing out the functionality. The CLI is not intended for any production use.\n\n```\n$ k8swatcher --help\nUsage: k8swatcher [OPTIONS]\n\nOptions:\n  --k8s-kind TEXT                 k8s object kind to watch (i.e. Ingress, Pod\n                                  etc)  [required]\n  --k8s-namespace TEXT            k8s namespace to scope to (only applicable\n                                  w/ a list_namespaced_* function name)\n  --k8s-sdk-class-name TEXT       Python kubernetes-client class name to\n                                  utilize  [required]\n  --k8s-sdk-list-function-name TEXT\n                                  Python kubernetes-client class list function\n                                  name to utilize  [required]\n  --field-selector TEXT           --field-selector field.path=1,field2.path=3\n  --label-selector TEXT           --label-selector label1=z,label2=v\n  --suppress-bookmarks / --no-suppress-bookmarks\n                                  Suppress BOOKMARK events from the watcher\n                                  [default: suppress-bookmarks]\n  --include-k8s-objects / --no-include-k8s-objects\n                                  Include the full k8s object (as a dict) in\n                                  each event  [default: no-\n                                  include-k8s-objects]\n  --exec-mode [asyncio_watch|queuing_watch]\n                                  The preferred execution mode  [default:\n                                  ExecMode.queuing_watch]\n  --install-completion [bash|zsh|fish|powershell|pwsh]\n                                  Install completion for the specified shell.\n  --show-completion [bash|zsh|fish|powershell|pwsh]\n                                  Show completion for the specified shell, to\n                                  copy it or customize the installation.\n  --help                          Show this message and exit.\n```\n\nThe arguments `--k8s-sdk-class-name` and `--k8s-sdk-list-function-name` are specifically referring to the [Python kubernetes-client](https://github.com/kubernetes-client/python/tree/master/kubernetes/docs) api's.\n\nWatch `Ingress` objects across all namespaces...\n```\nk8swatcher \\\n    --k8s-kind Ingress \\\n    --k8s-sdk-class-name NetworkingV1Api \\\n    --k8s-sdk-list-function-name list_ingress_for_all_namespaces \\\n    --label-selector some-label=myvalue,other-label=true \n```\n\nWatch `Pod` objects in a specific namespace...\n```\nk8swatcher \\\n    --k8s-kind Pod \\\n    --k8s-namespace my-apps \\\n    --k8s-sdk-class-name CoreV1Api \\\n    --k8s-sdk-list-function-name list_namespaced_pod \\\n    --include-k8s-objects\n```\n\n## example CLI output\n\nAgain the CLI is just for testing/demo purposes. The object emitted to STDOUT is the object you can utilize in your code when leveraging this module.\n\n```\n2022-05-05 06:56:19,587 - K8sWatcher - DEBUG - __iter__() processing K8sWatchConfig[kind=Pod]\n2022-05-05 06:56:19,587 - K8sWatcher - DEBUG - handle_k8s_object_list() processing K8sWatchConfig[kind=Pod]\n{\n  \"event_type\": \"LOADED\",\n  \"resource_version\": \"24891356\",\n  \"k8s_tracked_object\": {\n    \"uid\": \"dc1321ba-4b4c-47a9-9d85-7b8a8d0ef2af\",\n    \"kind\": \"Pod\",\n    \"api_version\": \"v1\",\n    \"name\": \"my-service-dev-0-0-3-886bf55d5-p7kpd\",\n    \"resource_version\": \"24692787\",\n    \"namespace\": \"my-apps\",\n    \"k8s_object\": {\n      \"metadata\": {\n        \"creationTimestamp\": \"2022-05-04T14:22:21+00:00\",\n        \"generateName\": \"csi-azurefile-node-\",\n        \"labels\": {\n          \"app\": \"csi-azurefile-node\",\n          \"controller-revision-hash\": \"56dd69698c\",\n          \"pod-template-generation\": \"8\"\n        },\n        \"managedFields\": [\n            ...\n        ]\n        ...\n      }\n    }\n  }\n}\n2022-05-05 06:56:21,319 - K8sWatcher - DEBUG - __iter__() processing K8sWatchConfig[kind=Pod]\n2022-05-05 06:56:21,319 - K8sWatcher - DEBUG - handle_k8s_object_watch() processing K8sWatchConfig[kind=Pod]\n{\n  \"event_type\": \"MODIFIED\",\n  \"resource_version\": \"24891472\",\n  \"k8s_tracked_object\": {\n    \"uid\": \"dc1321ba-4b4c-47a9-9d85-7b8a8d0ef2af\",\n    \"kind\": \"Pod\",\n    \"api_version\": \"v1\",\n    \"name\": \"my-service-dev-0-0-3-886bf55d5-p7kpd\",\n    \"resource_version\": \"24891472\",\n    \"namespace\": \"my-apps\",\n    \"k8s_object\": {\n      \"metadata\": {\n        \"creationTimestamp\": \"2022-05-04T14:22:21+00:00\",\n        \"generateName\": \"csi-azurefile-node-\",\n        \"labels\": {\n          \"app\": \"csi-azurefile-node\",\n          \"controller-revision-hash\": \"56dd69698c\",\n          \"pod-template-generation\": \"8\"\n        },\n        \"managedFields\": [\n            ...\n        ]\n        ...\n      }\n    }\n  }\n}\n{\n  \"event_type\": \"MODIFIED\",\n  \"resource_version\": \"24891481\",\n  \"k8s_tracked_object\": {\n    \"uid\": \"dc1321ba-4b4c-47a9-9d85-7b8a8d0ef2af\",\n    \"kind\": \"Pod\",\n    \"api_version\": \"v1\",\n    \"name\": \"my-service-dev-0-0-3-886bf55d5-p7kpd\",\n    \"resource_version\": \"24891481\",\n    \"namespace\": \"my-apps\",\n    \"k8s_object\": {\n      \"metadata\": {\n        \"creationTimestamp\": \"2022-05-04T14:22:21+00:00\",\n        \"generateName\": \"csi-azurefile-node-\",\n        \"labels\": {\n          \"app\": \"csi-azurefile-node\",\n          \"controller-revision-hash\": \"56dd69698c\",\n          \"pod-template-generation\": \"8\"\n        },\n        \"managedFields\": [\n            ...\n        ]\n        ...\n      }\n    }\n  }\n}\n{\n  \"event_type\": \"MODIFIED\",\n  \"resource_version\": \"24891521\",\n  \"k8s_tracked_object\": {\n    \"uid\": \"dc1321ba-4b4c-47a9-9d85-7b8a8d0ef2af\",\n    \"kind\": \"Pod\",\n    \"api_version\": \"v1\",\n    \"name\": \"my-service-dev-0-0-3-886bf55d5-p7kpd\",\n    \"resource_version\": \"24891521\",\n    \"namespace\": \"my-apps\",\n    \"k8s_object\": {\n      \"metadata\": {\n        \"creationTimestamp\": \"2022-05-04T14:22:21+00:00\",\n        \"generateName\": \"csi-azurefile-node-\",\n        \"labels\": {\n          \"app\": \"csi-azurefile-node\",\n          \"controller-revision-hash\": \"56dd69698c\",\n          \"pod-template-generation\": \"8\"\n        },\n        \"managedFields\": [\n            ...\n        ]\n        ...\n      }\n    }\n  }\n}\n{\n  \"event_type\": \"DELETED\",\n  \"resource_version\": \"24891522\",\n  \"k8s_tracked_object\": {\n    \"uid\": \"dc1321ba-4b4c-47a9-9d85-7b8a8d0ef2af\",\n    \"kind\": \"Pod\",\n    \"api_version\": \"v1\",\n    \"name\": \"my-service-dev-0-0-3-886bf55d5-p7kpd\",\n    \"resource_version\": \"24891522\",\n    \"namespace\": \"my-apps\",\n    \"k8s_object\": {\n      \"metadata\": {\n        \"creationTimestamp\": \"2022-05-04T14:22:21+00:00\",\n        \"generateName\": \"csi-azurefile-node-\",\n        \"labels\": {\n          \"app\": \"csi-azurefile-node\",\n          \"controller-revision-hash\": \"56dd69698c\",\n          \"pod-template-generation\": \"8\"\n        },\n        \"managedFields\": [\n            ...\n        ]\n        ...\n      }\n    }\n  }\n}\n```\n\n## local dev\n\n```\npython3 -m venv k8swatcher.ve\nsource k8swatcher.ve/bin/activate\npip install -r requirements-dev.txt\n```\n\n## related projects\n\nhttps://github.com/bitsofinfo/kubernetes-acme-dns-registrar\n\n## todo\n\na few tests, using `kind` etc","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitsofinfo%2Fk8swatcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitsofinfo%2Fk8swatcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitsofinfo%2Fk8swatcher/lists"}