Switch to Kubernetes Watch API
All checks were successful
continuous-integration/drone Build is passing
All checks were successful
continuous-integration/drone Build is passing
This commit is contained in:
parent
02606b9e3d
commit
2b2f838c20
@ -32,13 +32,16 @@ spec:
|
||||
containers:
|
||||
- name: camdetect
|
||||
image: harbor.k-space.ee/k-space/camera-motion-detect:latest
|
||||
ports:
|
||||
- containerPort: 5000
|
||||
name: "http"
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "250m"
|
||||
cpu: "200m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
cpu: "1"
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
|
@ -1,100 +1,135 @@
|
||||
import asyncio
|
||||
import os
|
||||
import yaml
|
||||
from base64 import b64decode
|
||||
from kubernetes_asyncio.client.api_client import ApiClient
|
||||
from kubernetes_asyncio import client, config
|
||||
from kubernetes_asyncio import client, config, watch
|
||||
from os import path
|
||||
from time import time
|
||||
from urllib.parse import urlparse
|
||||
|
||||
NAMESPACE = os.environ["MY_POD_NAMESPACE"]
|
||||
|
||||
LABEL_MANAGED_BY = "camera-operator"
|
||||
with open("camera-service.yml") as stream:
|
||||
SERVICE_BODY = stream.read()
|
||||
with open("camera-deployment.yml") as stream:
|
||||
DEPLOYMENT_BODY = stream.read()
|
||||
|
||||
async def apply_changes(item, v1, now, apps_api):
|
||||
target = item["spec"]["target"]
|
||||
replicas = item["spec"].get("replicas")
|
||||
|
||||
# Pull in secrets for the target URL
|
||||
secret_ref = item["spec"].get("secretRef")
|
||||
if secret_ref:
|
||||
secret = await v1.read_namespaced_secret(secret_ref, NAMESPACE)
|
||||
username = b64decode(secret.data.get("username", b"")).decode("ascii")
|
||||
password = b64decode(secret.data.get("password", b"")).decode("ascii")
|
||||
o = urlparse(target)
|
||||
netloc = o.netloc
|
||||
if "@" in netloc:
|
||||
_, netloc = o.netloc.split("@", 1)
|
||||
target = o._replace(netloc="%s:%s@%s" % (username, password, netloc)).geturl()
|
||||
|
||||
name = "camera-%s" % item["metadata"]["name"]
|
||||
print("Applying changes for", name, "CRD")
|
||||
|
||||
# Generate Deployment
|
||||
body = yaml.safe_load(DEPLOYMENT_BODY.replace("foobar", name))
|
||||
body["metadata"]["labels"] ["app.kubernetes.io/managed-by"] = LABEL_MANAGED_BY
|
||||
body["metadata"]["labels"] ["modified"] = now
|
||||
body["spec"]["template"]["spec"]["containers"][0]["args"] = [target]
|
||||
|
||||
if replicas:
|
||||
body["spec"]["replicas"] = replicas
|
||||
try:
|
||||
await apps_api.replace_namespaced_deployment(
|
||||
name = name, body = body, namespace=NAMESPACE)
|
||||
print(" * Updated deployment %s/%s" % (NAMESPACE, name))
|
||||
except client.exceptions.ApiException as e:
|
||||
await apps_api.create_namespaced_deployment(
|
||||
body = body, namespace=NAMESPACE)
|
||||
print(" * Created deployment %s/%s" % (NAMESPACE, name))
|
||||
|
||||
# Generate Service
|
||||
body = yaml.safe_load(SERVICE_BODY.replace("foobar", name))
|
||||
body["metadata"]["labels"] ["app.kubernetes.io/managed-by"] = LABEL_MANAGED_BY
|
||||
body["metadata"]["labels"] ["modified"] = now
|
||||
try:
|
||||
await v1.replace_namespaced_service(
|
||||
name = name, body = body, namespace=NAMESPACE)
|
||||
print(" * Updated service %s/%s" % (NAMESPACE, name))
|
||||
except client.exceptions.ApiException as e:
|
||||
await v1.create_namespaced_service(
|
||||
body = body, namespace=NAMESPACE)
|
||||
print(" * Created service %s/%s" % (NAMESPACE, name))
|
||||
|
||||
|
||||
async def main():
|
||||
|
||||
await config.load_kube_config()
|
||||
if os.getenv("KUBECONFIG"):
|
||||
await config.load_kube_config()
|
||||
else:
|
||||
await config.load_incluster_config()
|
||||
async with ApiClient() as api:
|
||||
|
||||
v1 = client.CoreV1Api(api)
|
||||
apps_api = client.AppsV1Api()
|
||||
|
||||
print("Listing namespaces")
|
||||
ret = await v1.list_namespace()
|
||||
api_instance = client.CustomObjectsApi(api)
|
||||
now = str(time())
|
||||
|
||||
for i in ret.items:
|
||||
try:
|
||||
resp = await api_instance.list_namespaced_custom_object(
|
||||
"k-space.ee", "v1alpha1", i.metadata.name, "cams")
|
||||
except client.exceptions.ApiException:
|
||||
w = watch.Watch()
|
||||
args = "k-space.ee", "v1alpha1", NAMESPACE, "cams"
|
||||
|
||||
resp = await api_instance.list_namespaced_custom_object(*args)
|
||||
for i in resp["items"]:
|
||||
await apply_changes(i, v1, now, apps_api)
|
||||
|
||||
print("Cleaning up dangling deployments and services")
|
||||
resp = await v1.list_namespaced_service(NAMESPACE)
|
||||
for i in resp.items:
|
||||
if not i.metadata.labels:
|
||||
continue
|
||||
for item in resp["items"]:
|
||||
target = item["spec"]["target"]
|
||||
replicas = item["spec"].get("replicas")
|
||||
if i.metadata.labels.get("app.kubernetes.io/managed-by") != LABEL_MANAGED_BY:
|
||||
continue
|
||||
if i.metadata.labels.get("modified") == now:
|
||||
continue
|
||||
print(" * Removing service: %s/%s" % (NAMESPACE, i.metadata.name))
|
||||
await v1.delete_namespaced_service(i.metadata.name, NAMESPACE)
|
||||
|
||||
# Pull in secrets for the target URL
|
||||
secret_ref = item["spec"].get("secretRef")
|
||||
if secret_ref:
|
||||
secret = await v1.read_namespaced_secret(secret_ref, i.metadata.name)
|
||||
username = b64decode(secret.data.get("username", b"")).decode("ascii")
|
||||
password = b64decode(secret.data.get("password", b"")).decode("ascii")
|
||||
o = urlparse(target)
|
||||
netloc = o.netloc
|
||||
if "@" in netloc:
|
||||
_, netloc = o.netloc.split("@", 1)
|
||||
target = o._replace(netloc="%s:%s@%s" % (username, password, netloc)).geturl()
|
||||
resp = await apps_api.list_namespaced_deployment(NAMESPACE)
|
||||
for i in resp.items:
|
||||
if not i.metadata.labels:
|
||||
continue
|
||||
if i.metadata.labels.get("app.kubernetes.io/managed-by") != LABEL_MANAGED_BY:
|
||||
continue
|
||||
if i.metadata.labels.get("modified") == now:
|
||||
continue
|
||||
print(" * Removing deployment: %s/%s" % (NAMESPACE, i.metadata.name))
|
||||
await apps_api.delete_namespaced_deployment(i.metadata.name, NAMESPACE)
|
||||
|
||||
name = "camera-%s" % item["metadata"]["name"]
|
||||
print("Applying", name)
|
||||
print("Subscribing to updates")
|
||||
|
||||
# Generate Deployment
|
||||
body = yaml.safe_load(DEPLOYMENT_BODY.replace("foobar", name))
|
||||
body["metadata"]["labels"] ["app.kubernetes.io/managed-by"] = LABEL_MANAGED_BY
|
||||
body["metadata"]["labels"] ["modified"] = now
|
||||
body["spec"]["template"]["spec"]["containers"][0]["args"] = [target]
|
||||
|
||||
if replicas:
|
||||
body["spec"]["replicas"] = replicas
|
||||
async for event in w.stream(api_instance.list_namespaced_custom_object, *args):
|
||||
if event["type"] == "ADDED":
|
||||
await apply_changes(event["object"], v1, now, apps_api)
|
||||
elif event["type"] == "DELETED":
|
||||
name = "camera-%s" % event["object"]["metadata"]["name"]
|
||||
try:
|
||||
await apps_api.replace_namespaced_deployment(
|
||||
name = name, body = body, namespace=i.metadata.name)
|
||||
print("Updated deployment %s/%s" % (i.metadata.name, name))
|
||||
await v1.delete_namespaced_service(name, NAMESPACE)
|
||||
except client.exceptions.ApiException as e:
|
||||
await apps_api.create_namespaced_deployment(
|
||||
body = body, namespace=i.metadata.name)
|
||||
print("Created deployment %s/%s" % (i.metadata.name, name))
|
||||
|
||||
# Generate Service
|
||||
body = yaml.safe_load(SERVICE_BODY.replace("foobar", name))
|
||||
body["metadata"]["labels"] ["app.kubernetes.io/managed-by"] = LABEL_MANAGED_BY
|
||||
body["metadata"]["labels"] ["modified"] = now
|
||||
pass
|
||||
else:
|
||||
print("Removed service: %s/%s" % (NAMESPACE, name))
|
||||
try:
|
||||
await v1.replace_namespaced_service(
|
||||
name = name, body = body, namespace=i.metadata.name)
|
||||
print("Updated service %s/%s" % (i.metadata.name, name))
|
||||
await apps_api.delete_namespaced_deployment(name, NAMESPACE)
|
||||
except client.exceptions.ApiException as e:
|
||||
await v1.create_namespaced_service(
|
||||
body = body, namespace=i.metadata.name)
|
||||
print("Created service %s/%s" % (i.metadata.name, name))
|
||||
pass
|
||||
else:
|
||||
print("Removed deployment: %s/%s" % (NAMESPACE, name))
|
||||
|
||||
deployments = await apps_api.list_deployment_for_all_namespaces()
|
||||
for dep in deployments.items:
|
||||
if not dep.metadata.labels:
|
||||
continue
|
||||
if dep.metadata.labels.get("app.kubernetes.io/managed-by") != LABEL_MANAGED_BY:
|
||||
continue
|
||||
if dep.metadata.labels.get("modified") == now:
|
||||
continue
|
||||
print("Removing deployment: %s/%s" % (dep.metadata.namespace, dep.metadata.name))
|
||||
await apps_api.delete_namespaced_deployment(name=dep.metadata.name, namespace=dep.metadata.namespace)
|
||||
print("Done")
|
||||
|
||||
if __name__ == '__main__':
|
||||
loop = asyncio.get_event_loop()
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(main())
|
||||
loop.close()
|
||||
|
||||
|
Reference in New Issue
Block a user