games/valheim: init

This creates a valheim game server, using a public image but slightly
nerfing it to be able to run it unprivileged.

We also deploy our first server. The password is Well Known To Those
Versed In Hackerspace Lore.

Change-Id: Ic24262a3b02d3c17d2f00aa2967e240ea4eee7fb
diff --git a/games/valheim/prod.jsonnet b/games/valheim/prod.jsonnet
new file mode 100644
index 0000000..6c10b8b
--- /dev/null
+++ b/games/valheim/prod.jsonnet
@@ -0,0 +1,168 @@
+local kube = import "../../kube/kube.libsonnet";
+
+{
+    local top = self,
+    env(ns, name):: {
+        local env = self,
+        local cfg = env.cfg,
+        cfg:: {
+            name: name,
+            displayName: name,
+            image: "mbround18/valheim:latest",
+            password: error "password must be set",
+            storageClassName: "waw-hdd-redundant-3",
+            port: 2456,
+        },
+
+        local named = function(component) "%s-%s" % [name, component],
+
+        game: {
+            local game = self,
+            pvcs: {
+                backups: ns.Contain(kube.PersistentVolumeClaim(named("backups"))) {
+                    spec+: {
+                        storageClassName: cfg.storageClassName,
+                        accessModes: ["ReadWriteOnce"],
+                        resources: {
+                            requests: { storage: "10Gi" },
+                        },
+                    },
+                },
+                saves: ns.Contain(kube.PersistentVolumeClaim(named("saves"))) {
+                    spec+: {
+                        storageClassName: cfg.storageClassName,
+                        accessModes: ["ReadWriteOnce"],
+                        resources: {
+                            requests: { storage: "10Gi" },
+                        },
+                    },
+                },
+                server: ns.Contain(kube.PersistentVolumeClaim(named("server"))) {
+                    spec+: {
+                        storageClassName: cfg.storageClassName,
+                        accessModes: ["ReadWriteOnce"],
+                        resources: {
+                            requests: { storage: "10Gi" },
+                        },
+                    },
+                },
+            },
+            svc: ns.Contain(kube.Service(named("external"))) {
+                target_pod:: game.deployment.spec.template,
+                spec+: {
+                    ports: kube.mapToNamedList({
+                        zero: { port: cfg.port, targetPort: cfg.port, protocol: "UDP" },
+                        one: { port: cfg.port+1, targetPort: cfg.port+1, protocol: "UDP" },
+                        two: { port: cfg.port+2, targetPort: cfg.port+2, protocol: "UDP" },
+                    }),
+                    type: "LoadBalancer",
+                },
+            },
+
+            scripts: ns.Contain(kube.ConfigMap(named("scripts"))) {
+                data: {
+                    # Based on https://github.com/mbround18/valheim-docker ,
+                    # removed all reliance on running as root (thus removed
+                    # autoupdater/autobackups).
+                    "entrypoint.sh": |||
+                        #!/usr/bin/env bash
+                        log() {
+                            PREFIX="[entrypoint]"
+                            printf "%-16s: %s\n" "${PREFIX}" "$1"
+                        }
+                        line() {
+                            log "==========================================================================="
+                        }
+                        setup_filesystem() {
+                            log "Setting up file systems"
+                            mkdir -p /home/steam/valheim
+                            mkdir -p /home/steam/valheim/logs
+                            mkdir -p /home/steam/backups
+                            mkdir -p /home/steam/scripts
+                            mkdir -p /home/steam/valheim
+                            cp /home/steam/steamcmd/linux64/steamclient.so /home/steam/valheim
+                        }
+                        line
+                        log "Valheim Server - $(date)"
+                        log "Initializing your container..."
+                        line
+                        setup_filesystem
+                        log "Launching the rest of the fucking owl"
+                        cd /home/steam/valheim || exit 1
+                        exec "$@"
+                    |||
+                },
+            },
+            secret: ns.Contain(kube.Secret(named("game"))) {
+                data_: {
+                    # public game password
+                    public: cfg.password,
+                },
+            },
+            deployment: ns.Contain(kube.Deployment(named("game"))) {
+                spec+: {
+                    template+: {
+                        spec+: {
+                            containers_: {
+                                default: kube.Container("default") {
+                                    image: cfg.image,
+                                    command: [
+                                        "/bin/bash", "/scripts/entrypoint.sh", "/home/steam/scripts/start_valheim.sh",
+                                    ],
+                                    volumeMounts_: {
+                                        backups: { mountPath: "/home/steam/backups" },
+                                        saves: { mountPath: "/home/steam/.config/unity3d/IronGate/Valheim" },
+                                        server: { mountPath: "/home/steam/valheim" },
+                                        scripts: { mountPath: "/scripts" },
+                                    },
+                                    ports_: {
+                                        zero: { containerPort: cfg.port },
+                                        one: { containerPort: cfg.port + 1 },
+                                        two: { containerPort: cfg.port + 2 },
+                                    },
+                                    env_: {
+                                        PUBLIC: "1",
+                                        PASSWORD: kube.SecretKeyRef(game.secret, "public"),
+                                        NAME: cfg.displayName,
+                                    },
+                                    resources: {
+                                        requests: {
+                                            cpu: "500m",
+                                            memory: "2Gi",
+                                        },
+                                        limits: {
+                                            cpu: "1000m",
+                                            memory: "4Gi",
+                                        },
+                                    },
+                                },
+                            },
+                            securityContext: {
+                                runAsUser: 1000,
+                                runAsGroup: 1000,
+                                fsGroup: 1000,
+                            },
+                            volumes_: {
+                                backups: kube.PersistentVolumeClaimVolume(game.pvcs.backups),
+                                saves: kube.PersistentVolumeClaimVolume(game.pvcs.saves),
+                                server: kube.PersistentVolumeClaimVolume(game.pvcs.server),
+                                scripts: kube.ConfigMapVolume(game.scripts),
+                            },
+                        },
+                    },
+                },
+            },
+        },
+    },
+
+    ns: kube.Namespace("valheim") {
+    },
+
+    q3k: top.env(top.ns, "q3k") {
+        cfg+: {
+            ns: "valheim",
+            password: (std.split(importstr "secrets/plain/q3k-public", "\n"))[0],
+            displayName: "wypierdol z polski xD",
+        },
+    },
+}