diff --git a/app/radio/Dockerfile b/app/radio/Dockerfile new file mode 100644 index 00000000..be256b66 --- /dev/null +++ b/app/radio/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine:3.9 + +RUN set -e -x ;\ + apk add --no-cache icecast + +USER icecast + +ENTRYPOINT ["/usr/bin/icecast", "-c", "/usr/share/icecast/icecast.xml"] diff --git a/app/radio/kube/radio.libsonnet b/app/radio/kube/radio.libsonnet new file mode 100644 index 00000000..08a7ffbb --- /dev/null +++ b/app/radio/kube/radio.libsonnet @@ -0,0 +1,154 @@ +local kube = import "../../../kube/kube.libsonnet"; + +{ + local radio = self, + local cfg = radio.cfg, + + cfg:: { + namespace: error "namespace must be set", + appName: "radio", + prefix: "", # if set, should be 'foo-' + port: 2137, + + icecast: { + location: error "location must be set", + admin: error "admin must be set", + limits: { + clients: 100, + sources: 2, + threadpool: 5, + queueSize: 524288, + clientTimeout: 30, + headerTimeout: 15, + sourceTimeout: 10, + burstOnConnect: true, + burstSize: 65535, + }, + authentication: { + sourcePassword: error "source password must be set", + relayPassword: error "relay password must be set", + adminPassword: error "admin password must be set", + }, + hostname: "localhost", + listenPort: 8080, + mounts: [], + }, + + tag: "latest", + image: "registry.k0.hswaw.net/app/radio:" + cfg.tag, + resources: { + requests: { + cpu: "100m", + memory: "100Mi", + }, + limits: { + cpu: "300m", + memory: "200Mi", + }, + }, + }, + + mount:: { + username: error "mount username must be defined", + password: error "mount password must be defined", + genre: "Classical", + bitrate: 128, + hidden: false, + }, + + makeName(suffix):: cfg.prefix + suffix, + + metadata:: { + namespace: cfg.namespace, + labels: { + "app.kubernetes.io/name": cfg.appName, + "app.kubernetes.io/managed-by": "kubecfg", + "app.kubernetes.io/component": "radio", + }, + }, + + configMap: kube.ConfigMap(radio.makeName("icecast")) { + metadata+: radio.metadata, + data: { + "icecast.xml": std.manifestXmlJsonml(["icecast", + ["location", cfg.icecast.location], + ["admin", cfg.icecast.admin], + ["limits", + ["clients", std.toString(cfg.icecast.limits.clients)], + ["sources", std.toString(cfg.icecast.limits.sources)], + ["threadpool", std.toString(cfg.icecast.limits.threadpool)], + ["queue-size", std.toString(cfg.icecast.limits.queueSize)], + ["client-timeout", std.toString(cfg.icecast.limits.clientTimeout)], + ["header-timeout", std.toString(cfg.icecast.limits.headerTimeout)], + ["source-timeout", std.toString(cfg.icecast.limits.sourceTimeout)], + ["burst-on-connect", if cfg.icecast.limits.burstOnConnect then "1" else "0"], + ["burst-size", std.toString(cfg.icecast.limits.burstSize)], + ], + ["authentication", + ["source-password", cfg.icecast.authentication.sourcePassword], + ["relay-password", cfg.icecast.authentication.relayPassword], + ["admin-password", cfg.icecast.authentication.adminPassword], + ], + ["hostname", cfg.icecast.hostname], + ["listen-socket", + ["port", std.toString(cfg.icecast.listenPort)], + ], + ["logging", + ["accesslog", "-"], + ["errorlog", "-"], + ["loglevel", "2"], + ], + ["security", + ["chroot", "0"], + ], + ] + [ + ["mount", {type: "normal"}, + ["mount-name", m], + ["username", cfg.icecast.mounts[m].username], + ["password", cfg.icecast.mounts[m].password], + ["public", if cfg.icecast.mounts[m].public then "1" else "0"], + ["genre", cfg.icecast.mounts[m].genre], + ["bitrate", std.toString(cfg.icecast.mounts[m].bitrate)], + ["hidden", if cfg.icecast.mounts[m].hidden then "1" else "0"], + ] + for m in std.objectFields(cfg.icecast.mounts) + ]), + }, + }, + + deployment: kube.Deployment(radio.makeName("icecast")) { + metadata+: radio.metadata, + spec+: { + replicas: 1, + template+: { + spec+: { + volumes_: { + config: kube.ConfigMapVolume(radio.configMap), + }, + containers_: { + radio: kube.Container(radio.makeName("radio")) { + image: cfg.image, + ports_: { + client: { containerPort: cfg.icecast.listenPort }, + }, + volumeMounts_: { + config: { mountPath: "/usr/share/icecast/icecast.xml", subPath: "icecast.xml" }, + }, + resources: cfg.resources, + }, + }, + }, + }, + }, + }, + svc: kube.Service(radio.makeName("icecast")) { + metadata+: radio.metadata, + target_pod:: radio.deployment.spec.template, + spec+: { + ports: [ + { name: "client", port: cfg.port, targetPort: cfg.icecast.listenPort, protocol: "TCP" }, + ], + type: "LoadBalancer", + }, + }, +}