From c83c7d17acef3cb9cc85ed9b94347a9f0cfbb03b Mon Sep 17 00:00:00 2001
From: Sheogorath <sheogorath@shivering-isles.com>
Date: Sun, 18 Feb 2024 02:38:29 +0100
Subject: [PATCH] feat(jellyfin): Add pdb-manager for jellyfin

This patch introduces a new container to the jellyfin instance that
talks to the kubernetes API and manages a PDB for jellyfin. It blocks
evictions while at least one device is playing a video.

This should prevent awkward situations during updates.
---
 apps/base/jellyfin/namespace.yaml             |  3 +-
 apps/base/jellyfin/serviceaccount.yaml        |  1 -
 .../jellyfin/jellyfin-pdb-mgr/deployment.yaml | 39 +++++++++++++
 .../jellyfin-pdb-mgr/kustomization.yaml       | 15 +++++
 .../jellyfin-pdb-mgr/poddisruptionbudget.yaml | 10 ++++
 .../k8s01/jellyfin/jellyfin-pdb-mgr/role.yaml | 25 ++++++++
 apps/k8s01/jellyfin/jellyfin-token.yaml       | 58 +++++++++++++++++++
 apps/k8s01/jellyfin/kustomization.yaml        |  6 +-
 .../networkpolicies/allow-to-kube-system.yaml | 16 +++++
 9 files changed, 170 insertions(+), 3 deletions(-)
 create mode 100644 apps/k8s01/jellyfin/jellyfin-pdb-mgr/deployment.yaml
 create mode 100644 apps/k8s01/jellyfin/jellyfin-pdb-mgr/kustomization.yaml
 create mode 100644 apps/k8s01/jellyfin/jellyfin-pdb-mgr/poddisruptionbudget.yaml
 create mode 100644 apps/k8s01/jellyfin/jellyfin-pdb-mgr/role.yaml
 create mode 100644 apps/k8s01/jellyfin/jellyfin-token.yaml
 create mode 100644 shared/networkpolicies/allow-to-kube-system.yaml

diff --git a/apps/base/jellyfin/namespace.yaml b/apps/base/jellyfin/namespace.yaml
index 3afecf797..df6dd5379 100644
--- a/apps/base/jellyfin/namespace.yaml
+++ b/apps/base/jellyfin/namespace.yaml
@@ -1,4 +1,5 @@
 apiVersion: v1
 kind: Namespace
 metadata:
-  name: jellyfin
\ No newline at end of file
+  name: jellyfin
+  labels: {}
diff --git a/apps/base/jellyfin/serviceaccount.yaml b/apps/base/jellyfin/serviceaccount.yaml
index 4271ce201..fb3202c9c 100644
--- a/apps/base/jellyfin/serviceaccount.yaml
+++ b/apps/base/jellyfin/serviceaccount.yaml
@@ -3,5 +3,4 @@ apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: jellyfin
-  namespace: jellyfin
 automountServiceAccountToken: false
\ No newline at end of file
diff --git a/apps/k8s01/jellyfin/jellyfin-pdb-mgr/deployment.yaml b/apps/k8s01/jellyfin/jellyfin-pdb-mgr/deployment.yaml
new file mode 100644
index 000000000..70477daf0
--- /dev/null
+++ b/apps/k8s01/jellyfin/jellyfin-pdb-mgr/deployment.yaml
@@ -0,0 +1,39 @@
+- op: add
+  path: /spec/template/spec/containers/-
+  value:
+    command:
+      - /bin/bash
+      - -c
+    args:
+      - |
+        #!/bin/sh
+
+        while true; do
+            if curl --variable %JELLYFIN_KEY 'http://localhost:8096/Sessions\?ApiKey\={{JELLYFIN_KEY:url}}' | jq -e '.[].NowPlayingItem != null'; then
+                kubectl patch --patch '{"spec": {"maxUnavailable:": 0}}' jellyfin
+            else
+                kubectl patch --patch '{"spec": {"maxUnavailable:": 1}}' jellyfin
+            fi
+            sleep 15
+        done
+    env:
+      - name: JELLYFIN_KEY
+        valueFrom:
+          secretRef:
+            name: jellyfin-pdb-mgr
+            key: token
+    image: quay.io/shivering-isles/koolbox:0.5.1
+    imagePullPolicy: IfNotPresent
+    name: jellyfin-pdb-mgr
+    resources:
+      requests:
+        memory: 64Mi
+        cpu: 10m
+      limits:
+        memory: 64Mi
+        cpu: 100m
+    securityContext:
+      allowPrivilegeEscalation: false
+- op: add
+  path: /spec/template/spec/automountServiceAccountToken
+  value: true
\ No newline at end of file
diff --git a/apps/k8s01/jellyfin/jellyfin-pdb-mgr/kustomization.yaml b/apps/k8s01/jellyfin/jellyfin-pdb-mgr/kustomization.yaml
new file mode 100644
index 000000000..e10aa17be
--- /dev/null
+++ b/apps/k8s01/jellyfin/jellyfin-pdb-mgr/kustomization.yaml
@@ -0,0 +1,15 @@
+apiVersion: kustomize.config.k8s.io/v1alpha1
+kind: Component
+
+resources:
+  - poddisruptionbudget.yaml
+  - role.yaml
+  - ../../../../shared/networkpolicies/allow-to-kube-system.yaml
+
+patches:
+  - path: deployment.yaml
+    target:
+      kind: Deployment
+      group: apps
+      version: v1
+      name: jellyfin
\ No newline at end of file
diff --git a/apps/k8s01/jellyfin/jellyfin-pdb-mgr/poddisruptionbudget.yaml b/apps/k8s01/jellyfin/jellyfin-pdb-mgr/poddisruptionbudget.yaml
new file mode 100644
index 000000000..8a97e97a9
--- /dev/null
+++ b/apps/k8s01/jellyfin/jellyfin-pdb-mgr/poddisruptionbudget.yaml
@@ -0,0 +1,10 @@
+---
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+  name: jellyfin
+spec:
+  minAvailable: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/component: jellyfin
\ No newline at end of file
diff --git a/apps/k8s01/jellyfin/jellyfin-pdb-mgr/role.yaml b/apps/k8s01/jellyfin/jellyfin-pdb-mgr/role.yaml
new file mode 100644
index 000000000..301836322
--- /dev/null
+++ b/apps/k8s01/jellyfin/jellyfin-pdb-mgr/role.yaml
@@ -0,0 +1,25 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: jellyfin-pdb-mgr
+rules:
+- apiGroups:
+  - policy
+  resources:
+  - poddisruptionbudgets
+  verbs:
+  - patch
+  - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: jellyfin-pdb-mgr
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: jellyfin-pdb-mgr
+subjects:
+- kind: ServiceAccount
+  name: jellyfin
\ No newline at end of file
diff --git a/apps/k8s01/jellyfin/jellyfin-token.yaml b/apps/k8s01/jellyfin/jellyfin-token.yaml
new file mode 100644
index 000000000..567865333
--- /dev/null
+++ b/apps/k8s01/jellyfin/jellyfin-token.yaml
@@ -0,0 +1,58 @@
+apiVersion: v1
+kind: Secret
+metadata:
+    name: jellyfin-pdb-mgr
+stringData:
+    token: ENC[AES256_GCM,data:+a71royIE/6QLcM3KiHePXCR4BqSBhTWBSvHIVtcw9w=,iv:/m/PIqe+pCGNX6sGW90BW+2qRdZ9L5CmO2dMz6lxA38=,tag:0s6Ft3Ms20W7aKyC1KegWQ==,type:str]
+sops:
+    kms: []
+    gcp_kms: []
+    azure_kv: []
+    hc_vault: []
+    age: []
+    lastmodified: "2024-02-18T01:38:22Z"
+    mac: ENC[AES256_GCM,data:HOpJetCDdSJmLdQ8LSQ5GS2j8V8rnzUBlI4EaWMLRMbkSWwoS/lJQ50KS91a55xZXS5xBgeO5STBvqKINo5JvOurmFYzYX2yRuO3gZ92yBLw6WwNYMwmAMJiey9Qr8E+Q2pJUPxxFllRQdAyjPqkhvIxefZ6bxyKbvYFSr/ZcM0=,iv:++g3Vkh3PGpXsSNIHFqz2h6QkoBAht/4s4RnvAeCduA=,tag:3rdBG+2R0Hn5LRc9QmqiWw==,type:str]
+    pgp:
+        - created_at: "2024-02-18T01:37:39Z"
+          enc: |-
+            -----BEGIN PGP MESSAGE-----
+
+            wcFMA7kpg2bgzVHcAQ//euYaemqol8lRFqeL8eIwASthHstLhYIOYsmjPRD2EPdJ
+            HdPd1GkV0+fXIEm5uT+hKFq52Ip12+l2GE3a51GUlunWZwLEEjuYpDZF+XaRNITq
+            yfFsnvHxKddpZHa9hZL8syOjlLgJIxneLw076PFAnkxc+4su3Ak92DcgRd7hsv6b
+            J/RbzV/F+kZ7BWI5IEzBlKYKq5BAI1o7WJ8frYWpeegdCgeBqFbfxEFIhnLOVxCs
+            xIVCM5nToV3vJh5LZK+JW3LuRAVVo35oIzmVC2WsbmHYp6uUTUHUN4si+So6xOzO
+            geYuuDEgwbOajcdlB/byjDBcBxFFJ3IkTVN5YqWSrVePJjf9fZ0LXwW2uhOs162b
+            uyMzvLRQ3x27KbHr6oxcwi0dQ3t4igmip2hBwpWFGwl4ZdGGbf7wrZ1ROteCMHUU
+            h/P4aMdGoDIzv0NQ2CS9SFDSlokWweFWcymKYDOx5l2dmSwi95x+tdWCAd9AeCrN
+            7gClOSPv9SUcYzQwy+JjP4DVUmqFg7WQVILvuHIeV+sx7b9qjwKV5XLwFzzAdd3h
+            kkP7CK8umcku7eAeouPwkD686cOFBhl6orM2UUW9p8dR0IPsps45bz2M7yaaOvg6
+            lWxYCWU10QbcH8Z5+Sx7iTzo7BCQcWMkpcNe2azVD78riYvkdd1B7aMCI840nBLS
+            UQGTqjkV2mcy7N+o20a3aG+GMZ+gokfc5fCjjcxeBrQP9eeS/6aM9J6COlTM7Oq9
+            SRwTrPycAqzNwPMCvtd5WfkvJFKJDmhZa96XOTy71lS3yg==
+            =P6VL
+            -----END PGP MESSAGE-----
+          fp: 286791FB6648539775DB31B8FCB98C2A3EC6F601
+        - created_at: "2024-02-18T01:37:39Z"
+          enc: |
+            -----BEGIN PGP MESSAGE-----
+
+            hQIMA4oYbIHZIrAPAQ//QanePBkQOhPcQu0JuTEYt7Q/rAgMHqTcwxYeRNPp2r0g
+            CPsCBAjXb5+XDSiUxPHupGH0KJsbxQQUrMor/wHZN8f4UCye1uHWWf5RRXeqdkBB
+            PCkJhJOs5M+zq5LFNp4baG09pxIGAQytFSbFk5tcHM2UWu2ds/DqwZUgJoab2X/e
+            07gp1liIG6huJ+6kSKh+Lhsy8BgVH6iV/y8yEE7vSRHTGkfjIjArBbk1fl/IJdne
+            sS03vlI1h64rGHE3Xyc5GXz7FONeUoWq+JLaq5wCZ1MBkDQvWB76aEjbzBrzT0wl
+            KJyCiUIsOPZCJXYoxEoANFLrb+Gekeyw3B2uSao2oFdElNfO+7qZ0IjPpAImiHJs
+            SMS4qg2Dx2qnR00GJo518xjefTC3PfrFVcZpRv9K9Vbf7D8H8TnsWbl3niVlVq4e
+            zwgKC9XbPD5CbxroUXj0ayJuYcXQP0rFWwCBWqqQcmrh9v0Uc6NXGGyGB+VTgle7
+            aCwQWelo3mDlehsA7lt5ag9sHWpbKtfEVf6qoMmQHEousvq1OT1IXhZxKFmmafJQ
+            +03iBFonFQ0sqKbQUVVyfAjvBT8Fgqnh6b5AUqxyLBl20gHgBwbrUV4brcS7IJde
+            tRBXybUInCBMzwbsstVCOevhgh/BWnZNbCarPaJGVfHFbr6bAekBRSJXHmAxFBnU
+            aAEJAhDNb11du/FtFXCnXLOEWH+XuDnSLKHY9TXfauGoxsaOofZEvXKvdLE7UJqV
+            bADh+C9tgDOOHTS5n8LU/EXDF4jXaKUv7yLORdpfBSCxz2vM8vpIRg6F6wsAxBjU
+            OaBkuw7wMapR
+            =OT6k
+            -----END PGP MESSAGE-----
+          fp: B137EE1549DFAF960DD1E2B15147025FB9F09E07
+    encrypted_regex: ^(data|stringData|email|dnsZones?|dnsNames?|.*(H|h)osts?|tang|externalURL|.*-secret|.*Secrets?|.*-domain|password|subjects|node|apiURL|.*(S|s)erverNames?|.*SecretKey|externalName)$
+    version: 3.7.3
diff --git a/apps/k8s01/jellyfin/kustomization.yaml b/apps/k8s01/jellyfin/kustomization.yaml
index c98b7c9f3..df4093d69 100644
--- a/apps/k8s01/jellyfin/kustomization.yaml
+++ b/apps/k8s01/jellyfin/kustomization.yaml
@@ -13,6 +13,7 @@ resources:
   - certificate.yaml
   - ingress.yaml
   - slo.yaml
+  - jellyfin-token.yaml
   - ../../../shared/resourcequotas/default.yaml
 
 patches:
@@ -21,4 +22,7 @@ patches:
       kind: Deployment
       group: apps
       version: v1
-      name: jellyfin
\ No newline at end of file
+      name: jellyfin
+
+components:
+  - jellyfin-pdb-mgr
\ No newline at end of file
diff --git a/shared/networkpolicies/allow-to-kube-system.yaml b/shared/networkpolicies/allow-to-kube-system.yaml
new file mode 100644
index 000000000..bbce0e223
--- /dev/null
+++ b/shared/networkpolicies/allow-to-kube-system.yaml
@@ -0,0 +1,16 @@
+---
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  name: allow-to-kube-system
+spec:
+  egress:
+  - to:
+    - namespaceSelector:
+        matchLabels:
+          kubernetes.io/metadata.name: kube-system
+  - to:
+    - ipBlock:
+        cidr: 192.168.100.0/24 # Kubernetes hosts
+  podSelector:
+    matchLabels: {}
\ No newline at end of file
-- 
GitLab