Initial commit

This commit is contained in:
Lauri Võsandi 2022-08-01 19:25:56 +03:00
commit ad7f7acdca
7 changed files with 342 additions and 0 deletions

2
.drone.yml Normal file
View File

@ -0,0 +1,2 @@
kind: template
load: docker.yaml

5
Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM harbor.k-space.ee/k-space/microservice-base
ADD camera-operator.py /camera-operator.py
ADD camera-deployment.yml camera-service.yml /config/
WORKDIR /config
ENTRYPOINT /camera-operator.py

94
camera-deployment.yml Normal file
View File

@ -0,0 +1,94 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: foobar
labels: {}
# Make sure keel.sh pulls updates for this deployment
annotations:
keel.sh/policy: force
keel.sh/trigger: poll
spec:
replicas: 1
# Make sure we do not congest the network during rollout
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
selector:
matchLabels:
app: foobar
template:
metadata:
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '5000'
labels:
app: foobar
component: camdetect
spec:
containers:
- name: camdetect
image: harbor.k-space.ee/k-space/camera-motion-detect:latest
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
command:
- /app/camdetect.py
env:
- name: SOURCE_NAME
value: foobar
- name: S3_ENDPOINT_URL
value: http://minio:9000
- name: MJPEGSTREAMER_CREDENTIALS
valueFrom:
secretKeyRef:
name: application-secrets
key: MJPEGSTREAMER_CREDENTIALS
- name: MONGO_URI
valueFrom:
secretKeyRef:
name: mongodb-application-application
key: connectionString.standard
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: minio-secret
key: accesskey
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: minio-secret
key: secretkey
# Make sure 2+ pods of same camera are scheduled on different hosts
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- foobar
topologyKey: kubernetes.io/hostname
# Make sure camera deployments are spread over workers
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: foobar
component: camdetect

87
camera-operator.py Normal file
View File

@ -0,0 +1,87 @@
import asyncio
import base64
import yaml
from kubernetes_asyncio.client.api_client import ApiClient
from kubernetes_asyncio import client, config
from os import path
from time import time
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 main():
await config.load_kube_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:
continue
for item in resp["items"]:
target = item["spec"]["target"]
secret_ref = item["spec"].get("secretRef")
replicas = item["spec"].get("replicas")
print("Applying", target)
name = "camera-%s" % item["metadata"]["name"]
# 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=i.metadata.name)
print("Updated deployment %s/%s" % (i.metadata.name, name))
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
try:
await v1.replace_namespaced_service(
name = name, body = body, namespace=i.metadata.name)
print("Updated service %s/%s" % (i.metadata.name, name))
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))
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.run_until_complete(main())
loop.close()

15
camera-service.yml Normal file
View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: foobar
labels:
component: camdetect
spec:
type: ClusterIP
selector:
app: foobar
component: camdetect
ports:
- protocol: TCP
port: 80
targetPort: 5000

67
cameras.yml Normal file
View File

@ -0,0 +1,67 @@
---
apiVersion: k-space.ee/v1alpha1
kind: Camera
metadata:
name: workshop
spec:
target: http://workshop.cam.k-space.ee:8080/?action=stream
secretRef: camera-secrets
---
apiVersion: k-space.ee/v1alpha1
kind: Camera
metadata:
name: server-room
spec:
target: http://server-room.cam.k-space.ee:8080/?action=stream
secretRef: camera-secrets
replicas: 2
---
apiVersion: k-space.ee/v1alpha1
kind: Camera
metadata:
name: printer
spec:
target: http://printer.cam.k-space.ee:8080/?action=stream
secretRef: camera-secrets
replicas: 2
---
apiVersion: k-space.ee/v1alpha1
kind: Camera
metadata:
name: chaos
spec:
target: http://chaos.cam.k-space.ee:8080/?action=stream
secretRef: camera-secrets
---
apiVersion: k-space.ee/v1alpha1
kind: Camera
metadata:
name: cyber
spec:
target: http://cyber.cam.k-space.ee:8080/?action=stream
secretRef: camera-secrets
---
apiVersion: k-space.ee/v1alpha1
kind: Camera
metadata:
name: kitchen
spec:
target: http://kitchen.cam.k-space.ee:8080/?action=stream
secretRef: camera-secrets
---
apiVersion: k-space.ee/v1alpha1
kind: Camera
metadata:
name: back-door
spec:
target: http://back-door.cam.k-space.ee:8080/?action=stream
secretRef: camera-secrets
---
apiVersion: k-space.ee/v1alpha1
kind: Camera
metadata:
name: ground-door
spec:
target: http://ground-door.cam.k-space.ee:8080/?action=stream
secretRef: camera-secrets

72
crd.yaml Normal file
View File

@ -0,0 +1,72 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: cams.k-space.ee
spec:
group: k-space.ee
names:
plural: cams
singular: cam
kind: Camera
shortNames:
- cam
scope: Namespaced
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
roi:
type: object
description: Region of interest for this camera
properties:
threshold:
type: integer
description: Percentage of pixels changed within ROI to
consider whole frame to have motion detected.
Defaults to 5.
enabled:
type: boolean
description: Whether motion detection is enabled for this
camera. Defaults to false.
left:
type: integer
description: Left boundary of ROI as
percentage of the width of a frame.
By default 0.
right:
type: integer
description: Right boundary of ROI as
percentage of the width of a frame.
By default 100.
top:
type: integer
description: Top boundary of ROI as
percentage of the height of a frame
By deafault 0.
bottom:
type: integer
description: Bottom boundary of ROI as
percentage of the height of a frame.
By default 100.
secretRef:
type: string
description: Secret that contains authentication credentials
target:
type: string
description: URL of the video feed stream
replicas:
type: integer
minimum: 1
maximum: 2
description: For highly available deployment set this to 2 or
higher. Make sure you also run Mongo and Minio in HA
configurations
required: ["target"]
required: ["spec"]