add tubearchivist

This commit is contained in:
Morten Olsen
2026-01-05 13:50:43 +01:00
parent aeb99ce6d5
commit fc93c01795
16 changed files with 375 additions and 4 deletions

View File

@@ -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:

View File

@@ -0,0 +1,7 @@
apiVersion: v2
version: 1.0.0
name: tubearchivist
dependencies:
- name: common
version: 1.0.0
repository: file://../../common

View File

@@ -0,0 +1 @@
{{ include "common.deployment" . }}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -0,0 +1 @@
{{ include "common.externalSecrets.externalSecrets" . }}

View File

@@ -0,0 +1 @@
{{ include "common.externalSecrets.passwordGenerators" . }}

View File

@@ -0,0 +1 @@
{{ include "common.service" . }}

View File

@@ -0,0 +1 @@
{{ include "common.virtualService" . }}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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: