# services/jellyfin/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jellyfin
  namespace: jellyfin
  labels:
    app: jellyfin
    atlas.bstein.dev/workload-profile: heavy
spec:
  replicas: 1
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
  selector:
    matchLabels:
      app: jellyfin
  template:
    metadata:
      labels:
        app: jellyfin
        atlas.bstein.dev/workload-profile: heavy
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "pegasus"
        vault.hashicorp.com/agent-inject-secret-ldap-config.xml: "kv/data/atlas/pegasus/jellyfin-ldap-config"
        vault.hashicorp.com/agent-inject-template-ldap-config.xml: |
          {{- with secret "kv/data/atlas/pegasus/jellyfin-ldap-config" -}}{{ index .Data.data "ldap-config.xml" }}{{- end -}}
    spec:
      serviceAccountName: pegasus-vault-sync
      # Clean up any lingering OIDC artifacts and strip the injected script tag
      initContainers:
        - name: strip-oidc
          image: docker.io/jellyfin/jellyfin:10.11.5
          securityContext:
            runAsUser: 0
            runAsGroup: 0
          command:
            - /bin/sh
            - -c
            - |
              set -euxo pipefail
              cp -a /jellyfin/jellyfin-web/. /web-root
              # remove injected OIDC script tags everywhere just in case
              for f in $(find /web-root -type f -name 'index.html'); do
                sed -i '/oidc\/inject/d' "$f"
                printf '%s\n' "$f"
              done
              # clean any lingering OIDC plugin artifacts on the config volume
              rm -rf "/config/plugins/OIDC Authentication_"* /config/plugins/configurations/JellyfinOIDCPlugin.v2.xml || true
          volumeMounts:
            - name: web-root
              mountPath: /web-root
            - name: config
              mountPath: /config
        # Force all users to authenticate via the LDAP plugin provider by updating the DB on start.
        # This keeps Flux enforcement for auth provider drift (e.g., after UI edits).
        - name: set-ldap-auth-provider
          image: docker.io/library/alpine:3.20
          securityContext:
            runAsUser: 0
            runAsGroup: 0
          command:
            - /bin/sh
            - -c
            - |
              set -euxo pipefail
              apk add --no-cache sqlite
              db="/config/data/jellyfin.db"
              if [ -f "$db" ]; then
                sqlite3 "$db" "UPDATE Users SET AuthenticationProviderId='Jellyfin.Plugin.LDAP_Auth.LdapAuthenticationProviderPlugin', Password=NULL, EnableLocalPassword=0 WHERE AuthenticationProviderId!='Jellyfin.Plugin.LDAP_Auth.LdapAuthenticationProviderPlugin';"
              else
                echo "db not found at $db, skipping"
              fi
          volumeMounts:
            - name: config
              mountPath: /config
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: longhorn-host
                    operator: In
                    values:
                      - "true"
                  - key: node-role.kubernetes.io/worker
                    operator: In
                    values:
                      - "true"
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              preference:
                matchExpressions:
                  - key: hardware
                    operator: In
                    values:
                      - rpi5
            - weight: 80
              preference:
                matchExpressions:
                  - key: kubernetes.io/hostname
                    operator: In
                    values:
                      - titan-24
      securityContext:
        runAsUser: 1000 
        fsGroup: 65532
        fsGroupChangePolicy: OnRootMismatch
        runAsGroup: 65532
      containers:
        - name: jellyfin
          image: docker.io/jellyfin/jellyfin:10.11.5
          imagePullPolicy: IfNotPresent
          command:
            - /entrypoint.sh
          args:
            - /jellyfin/jellyfin
          ports:
            - name: http
              containerPort: 8096
          env:
            - name: JELLYFIN_PublishedServerUrl
              value: "https://stream.bstein.dev"
            - name: PUID
              value: "1000"
            - name: PGID
              value: "65532"
            - name: UMASK
              value: "002"
            - name: VAULT_COPY_FILES
              value: /vault/secrets/ldap-config.xml:/config/plugins/configurations/LDAP-Auth.xml
          resources:
            requests:
              cpu: "500m"
              memory: 1Gi
            limits:
              cpu: "1500m"
              memory: 3Gi
          volumeMounts:
            - name: jellyfin-vault-entrypoint
              mountPath: /entrypoint.sh
              subPath: vault-entrypoint.sh
            - name: config
              mountPath: /config
            - name: cache
              mountPath: /cache
            - name: media
              mountPath: /media
            - name: web-root
              mountPath: /jellyfin/jellyfin-web
          lifecycle:
            postStart:
              exec:
                command:
                  - /bin/sh
                  - -c
                  - |
                    set -eux
                    for f in $(find /jellyfin/jellyfin-web -type f -name 'index.html'); do
                      sed -i '/oidc\/inject/d' "$f" || true
                    done
          securityContext:
            runAsUser: 0
            runAsGroup: 0
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: false
      volumes:
        - name: jellyfin-vault-entrypoint
          configMap:
            name: jellyfin-vault-entrypoint
            defaultMode: 493
        - name: web-root
          emptyDir: {}
        - name: config
          persistentVolumeClaim:
            claimName: jellyfin-config-astreae
        - name: cache
          emptyDir: {}
        - name: media
          persistentVolumeClaim:
            claimName: jellyfin-media-asteria-new
