From fc93c01795afb085399e6ef66427b44436139875 Mon Sep 17 00:00:00 2001 From: Morten Olsen Date: Mon, 5 Jan 2026 13:50:43 +0100 Subject: [PATCH] add tubearchivist --- apps/charts/jellyfin-kids/values.yaml | 3 + apps/charts/tubearchivist/Chart.yaml | 7 ++ .../tubearchivist/templates/deployment.yaml | 1 + .../templates/elasticsearch-deployment.yaml | 87 +++++++++++++ .../templates/elasticsearch-service.yaml | 15 +++ apps/charts/tubearchivist/templates/pvc.yaml | 27 ++++ .../templates/redis-deployment.yaml | 63 ++++++++++ .../templates/redis-service.yaml | 15 +++ .../templates/secret-external-secrets.yaml | 1 + .../templates/secret-password-generators.yaml | 1 + .../tubearchivist/templates/service.yaml | 1 + .../templates/virtual-service.yaml | 1 + apps/charts/tubearchivist/values.yaml | 116 ++++++++++++++++++ apps/charts/volumes/values.yaml | 4 +- apps/common/README.md | 33 ++++- apps/common/templates/_helpers.tpl | 4 + 16 files changed, 375 insertions(+), 4 deletions(-) create mode 100644 apps/charts/tubearchivist/Chart.yaml create mode 100644 apps/charts/tubearchivist/templates/deployment.yaml create mode 100644 apps/charts/tubearchivist/templates/elasticsearch-deployment.yaml create mode 100644 apps/charts/tubearchivist/templates/elasticsearch-service.yaml create mode 100644 apps/charts/tubearchivist/templates/pvc.yaml create mode 100644 apps/charts/tubearchivist/templates/redis-deployment.yaml create mode 100644 apps/charts/tubearchivist/templates/redis-service.yaml create mode 100644 apps/charts/tubearchivist/templates/secret-external-secrets.yaml create mode 100644 apps/charts/tubearchivist/templates/secret-password-generators.yaml create mode 100644 apps/charts/tubearchivist/templates/service.yaml create mode 100644 apps/charts/tubearchivist/templates/virtual-service.yaml create mode 100644 apps/charts/tubearchivist/values.yaml diff --git a/apps/charts/jellyfin-kids/values.yaml b/apps/charts/jellyfin-kids/values.yaml index 9b880f9..d7eb653 100644 --- a/apps/charts/jellyfin-kids/values.yaml +++ b/apps/charts/jellyfin-kids/values.yaml @@ -37,6 +37,9 @@ volumes: - name: kidsmusic mountPath: /media/kids-music persistentVolumeClaim: kidsmusic # External PVC (not prefixed) + - name: media + mountPath: /media/youtube + persistentVolumeClaim: kidsyoutube # External PVC (not prefixed with release name) # Persistent volume claims persistentVolumeClaims: diff --git a/apps/charts/tubearchivist/Chart.yaml b/apps/charts/tubearchivist/Chart.yaml new file mode 100644 index 0000000..5bc60f5 --- /dev/null +++ b/apps/charts/tubearchivist/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +version: 1.0.0 +name: tubearchivist +dependencies: + - name: common + version: 1.0.0 + repository: file://../../common diff --git a/apps/charts/tubearchivist/templates/deployment.yaml b/apps/charts/tubearchivist/templates/deployment.yaml new file mode 100644 index 0000000..4508e33 --- /dev/null +++ b/apps/charts/tubearchivist/templates/deployment.yaml @@ -0,0 +1 @@ +{{ include "common.deployment" . }} diff --git a/apps/charts/tubearchivist/templates/elasticsearch-deployment.yaml b/apps/charts/tubearchivist/templates/elasticsearch-deployment.yaml new file mode 100644 index 0000000..0c9a365 --- /dev/null +++ b/apps/charts/tubearchivist/templates/elasticsearch-deployment.yaml @@ -0,0 +1,87 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Release.Name }}-es" + labels: + {{- include "common.labels" . | nindent 4 }} + app: "{{ .Release.Name }}-es" +spec: + strategy: + type: Recreate + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: "{{ .Release.Name }}-es" + template: + metadata: + labels: + app: "{{ .Release.Name }}-es" + spec: + initContainers: + - name: fix-permissions + image: busybox:latest + command: ['sh', '-c'] + args: + - | + mkdir -p /usr/share/elasticsearch/data + chown -R 1000:1000 /usr/share/elasticsearch/data + chmod -R 755 /usr/share/elasticsearch/data + securityContext: + runAsUser: 0 + volumeMounts: + - name: data + mountPath: /usr/share/elasticsearch/data + containers: + - name: "{{ .Release.Name }}-es" + image: "{{ .Values.elasticsearch.image.repository }}:{{ .Values.elasticsearch.image.tag }}" + imagePullPolicy: "{{ .Values.elasticsearch.image.pullPolicy }}" + ports: + - name: http + containerPort: 9200 + protocol: TCP + env: + - name: ELASTIC_PASSWORD + valueFrom: + secretKeyRef: + name: "{{ .Release.Name }}-secrets" + key: elastic-password + - name: ES_JAVA_OPTS + value: "-Xms1g -Xmx1g" + - name: xpack.security.enabled + value: "true" + - name: discovery.type + value: "single-node" + - name: path.repo + value: "/usr/share/elasticsearch/data/snapshot" + volumeMounts: + - name: data + mountPath: /usr/share/elasticsearch/data + resources: + limits: + memory: "2Gi" + requests: + memory: "1Gi" + livenessProbe: + tcpSocket: + port: http + initialDelaySeconds: 60 + periodSeconds: 30 + readinessProbe: + exec: + command: + - sh + - -c + - | + curl -s -u elastic:${ELASTIC_PASSWORD} http://localhost:9200/_cluster/health | grep -q '"status":"green"\|"status":"yellow"' + initialDelaySeconds: 30 + periodSeconds: 10 + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + securityContext: + fsGroup: 1000 + volumes: + - name: data + persistentVolumeClaim: + claimName: "{{ .Release.Name }}-es-data" diff --git a/apps/charts/tubearchivist/templates/elasticsearch-service.yaml b/apps/charts/tubearchivist/templates/elasticsearch-service.yaml new file mode 100644 index 0000000..d35245b --- /dev/null +++ b/apps/charts/tubearchivist/templates/elasticsearch-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: "{{ .Release.Name }}-es" + labels: + {{- include "common.labels" . | nindent 4 }} + app: "{{ .Release.Name }}-es" +spec: + ports: + - port: 9200 + targetPort: http + protocol: TCP + name: http + selector: + app: "{{ .Release.Name }}-es" diff --git a/apps/charts/tubearchivist/templates/pvc.yaml b/apps/charts/tubearchivist/templates/pvc.yaml new file mode 100644 index 0000000..095c477 --- /dev/null +++ b/apps/charts/tubearchivist/templates/pvc.yaml @@ -0,0 +1,27 @@ +{{ include "common.pvc" . }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: "{{ .Release.Name }}-redis-data" + labels: + {{- include "common.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: "{{ .Release.Name }}-es-data" + labels: + {{- include "common.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi diff --git a/apps/charts/tubearchivist/templates/redis-deployment.yaml b/apps/charts/tubearchivist/templates/redis-deployment.yaml new file mode 100644 index 0000000..fe1e990 --- /dev/null +++ b/apps/charts/tubearchivist/templates/redis-deployment.yaml @@ -0,0 +1,63 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Release.Name }}-redis" + labels: + {{- include "common.labels" . | nindent 4 }} + app: "{{ .Release.Name }}-redis" +spec: + strategy: + type: Recreate + replicas: 1 + revisionHistoryLimit: 2 + selector: + matchLabels: + app: "{{ .Release.Name }}-redis" + template: + metadata: + labels: + app: "{{ .Release.Name }}-redis" + spec: + initContainers: + - name: fix-permissions + image: busybox:latest + command: ['sh', '-c'] + args: + - | + mkdir -p /data + chown -R 999:999 /data || chown -R 0:0 /data + chmod -R 755 /data + securityContext: + runAsUser: 0 + volumeMounts: + - name: data + mountPath: /data + containers: + - name: "{{ .Release.Name }}-redis" + image: "{{ .Values.redis.image.repository }}:{{ .Values.redis.image.tag }}" + imagePullPolicy: "{{ .Values.redis.image.pullPolicy }}" + ports: + - name: tcp + containerPort: 6379 + protocol: TCP + volumeMounts: + - name: data + mountPath: /data + command: + - redis-server + - --appendonly + - "no" + - --save + - "" + - --stop-writes-on-bgsave-error + - "no" + livenessProbe: + tcpSocket: + port: tcp + readinessProbe: + tcpSocket: + port: tcp + volumes: + - name: data + persistentVolumeClaim: + claimName: "{{ .Release.Name }}-redis-data" diff --git a/apps/charts/tubearchivist/templates/redis-service.yaml b/apps/charts/tubearchivist/templates/redis-service.yaml new file mode 100644 index 0000000..bc6b11d --- /dev/null +++ b/apps/charts/tubearchivist/templates/redis-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: "{{ .Release.Name }}-redis" + labels: + {{- include "common.labels" . | nindent 4 }} + app: "{{ .Release.Name }}-redis" +spec: + ports: + - port: 6379 + targetPort: tcp + protocol: TCP + name: tcp + selector: + app: "{{ .Release.Name }}-redis" diff --git a/apps/charts/tubearchivist/templates/secret-external-secrets.yaml b/apps/charts/tubearchivist/templates/secret-external-secrets.yaml new file mode 100644 index 0000000..de340c4 --- /dev/null +++ b/apps/charts/tubearchivist/templates/secret-external-secrets.yaml @@ -0,0 +1 @@ +{{ include "common.externalSecrets.externalSecrets" . }} diff --git a/apps/charts/tubearchivist/templates/secret-password-generators.yaml b/apps/charts/tubearchivist/templates/secret-password-generators.yaml new file mode 100644 index 0000000..2183e0a --- /dev/null +++ b/apps/charts/tubearchivist/templates/secret-password-generators.yaml @@ -0,0 +1 @@ +{{ include "common.externalSecrets.passwordGenerators" . }} diff --git a/apps/charts/tubearchivist/templates/service.yaml b/apps/charts/tubearchivist/templates/service.yaml new file mode 100644 index 0000000..f024c64 --- /dev/null +++ b/apps/charts/tubearchivist/templates/service.yaml @@ -0,0 +1 @@ +{{ include "common.service" . }} diff --git a/apps/charts/tubearchivist/templates/virtual-service.yaml b/apps/charts/tubearchivist/templates/virtual-service.yaml new file mode 100644 index 0000000..766f6b9 --- /dev/null +++ b/apps/charts/tubearchivist/templates/virtual-service.yaml @@ -0,0 +1 @@ +{{ include "common.virtualService" . }} diff --git a/apps/charts/tubearchivist/values.yaml b/apps/charts/tubearchivist/values.yaml new file mode 100644 index 0000000..4316e5a --- /dev/null +++ b/apps/charts/tubearchivist/values.yaml @@ -0,0 +1,116 @@ +image: + repository: bbilly1/tubearchivist + tag: latest + pullPolicy: IfNotPresent + +subdomain: tubearchivist + +# Use the image's default CMD as specified in Dockerfile +# Dependencies are already installed in /root/.local from the multi-stage build +# Note: tini location may vary, using bash directly as fallback +command: + - /bin/bash + - /app/run.sh + +# Deployment configuration +deployment: + strategy: Recreate + replicas: 1 + revisionHistoryLimit: 2 + podAnnotations: + sidecar.istio.io/rewriteAppHTTPProbers: "false" + +# Container configuration +container: + ports: + - name: http + port: 8000 + protocol: TCP + healthProbe: + type: tcpSocket + port: http + initialDelaySeconds: 30 + periodSeconds: 120 + timeoutSeconds: 10 + failureThreshold: 3 + # securityContext removed - image expects to run as root to access /root/.local packages + +# Service configuration +service: + port: 80 + targetPort: http + type: ClusterIP + +# Volume configuration +volumes: + - name: media + mountPath: /youtube + persistentVolumeClaim: kidsyoutube # External PVC (not prefixed with release name) + - name: cache + mountPath: /cache + persistentVolumeClaim: data # Use "data" so common library prefixes it correctly + +# Persistent volume claims +persistentVolumeClaims: + - name: data + size: 10Gi + +# VirtualService configuration +virtualService: + enabled: true + gateways: + public: true + private: true + +# External Secrets configuration for initial credentials +externalSecrets: + - name: "{release}-secrets" + passwords: + - name: ta-password + length: 32 + allowRepeat: true + encoding: hex + secretKeys: + - ta-password + - name: elastic-password + length: 32 + allowRepeat: true + encoding: hex + secretKeys: + - elastic-password + +# Environment variables +env: + ES_URL: + value: "http://{release}-es.{namespace}.svc.cluster.local:9200" + REDIS_CON: + value: "redis://{release}-redis.{namespace}.svc.cluster.local:6379" + HOST_UID: "1000" + HOST_GID: "1000" + TA_HOST: + value: "https://{subdomain}.{domain}" + TA_USERNAME: "tubearchivist" + TA_PASSWORD: + valueFrom: + secretKeyRef: + name: "{release}-secrets" + key: ta-password + ELASTIC_PASSWORD: + valueFrom: + secretKeyRef: + name: "{release}-secrets" + key: elastic-password + +# Redis configuration +redis: + image: + repository: redis + tag: latest + pullPolicy: IfNotPresent + +# Elasticsearch configuration +elasticsearch: + image: + repository: bbilly1/tubearchivist-es + tag: latest + pullPolicy: IfNotPresent diff --git a/apps/charts/volumes/values.yaml b/apps/charts/volumes/values.yaml index e490392..286b0cf 100644 --- a/apps/charts/volumes/values.yaml +++ b/apps/charts/volumes/values.yaml @@ -20,5 +20,5 @@ shares: path: /mnt/HDD/Misc kidsmusic: path: /mnt/HDD/Misc/Kids_Music - images: - path: /mnt/HDD/images + kidsyoutube: + path: /mnt/HDD/Kids/YouTube diff --git a/apps/common/README.md b/apps/common/README.md index f1bbdc8..ad5df41 100644 --- a/apps/common/README.md +++ b/apps/common/README.md @@ -185,12 +185,16 @@ externalSecrets: passwords: - name: apiKey length: 32 - encoding: hex # or base64, alphanumeric + encoding: hex # Supported: base64, base64url, base32, hex, raw allowRepeat: true + secretKeys: # Required: sets the key name in the secret + - apiKey - name: encryptionKey length: 64 encoding: base64 allowRepeat: false + secretKeys: # Required: sets the key name in the secret + - encryptionKey # Environment variables env: @@ -271,6 +275,8 @@ Add secret generation templates: {{ include "common.externalSecrets.externalSecrets" . }} ``` +**Note:** Remember to include `secretKeys` in each password configuration in `values.yaml` to set the key names in the generated secret. See the [External Secrets](#external-secrets) section for details. + ## Complete Examples ### Example 1: Simple Stateless Application @@ -429,10 +435,14 @@ externalSecrets: length: 64 encoding: base64 allowRepeat: true + secretKeys: # Required: sets the key name in the secret + - encryptionKey - name: apiToken length: 32 encoding: hex allowRepeat: false + secretKeys: # Required: sets the key name in the secret + - apiToken env: ENCRYPTION_KEY: @@ -489,7 +499,26 @@ When `database.enabled: true`, the PostgresDatabase creates a secret named `{rel ### External Secrets -External secrets are created with the name specified in `externalSecrets[].name` (use `{release}` placeholder). Each password field becomes a key in the secret. +External secrets are created with the name specified in `externalSecrets[].name` (use `{release}` placeholder). + +**Important:** The `secretKeys` field is **required** for each password generator to set the key name in the secret. Without `secretKeys`, the Password generator defaults to using `password` as the key name. The `name` field in the password config is used for the generator name, not the secret key name. + +**Supported encodings:** `base64`, `base64url`, `base32`, `hex`, `raw` (note: `alphanumeric` is not supported) + +**Example:** +```yaml +externalSecrets: + - name: "{release}-secrets" + passwords: + - name: my-password-generator # Generator name (used in resource name) + length: 32 + encoding: hex + allowRepeat: true + secretKeys: # Required: sets the key name in the secret + - mySecretKey # This becomes the key name in the secret +``` + +The secret will contain a key named `mySecretKey` (not `my-password-generator`). ## Placeholders diff --git a/apps/common/templates/_helpers.tpl b/apps/common/templates/_helpers.tpl index cab7717..ee9ac24 100644 --- a/apps/common/templates/_helpers.tpl +++ b/apps/common/templates/_helpers.tpl @@ -330,6 +330,10 @@ spec: {{- include "common.selectorLabels" . | nindent 6 }} template: metadata: + {{- if .Values.deployment.podAnnotations }} + annotations: + {{- toYaml .Values.deployment.podAnnotations | nindent 8 }} + {{- end }} labels: {{- include "common.selectorLabels" . | nindent 8 }} spec: