1 Commits

Author SHA1 Message Date
Paul Payne
c2efd6359a Adds jellyfin app (untested). 2025-08-16 08:05:35 -07:00
13 changed files with 206 additions and 36 deletions

View File

@@ -2,7 +2,7 @@
Welcome! So excited you're here!
_This project is massively in progress. It's not ready to be used yet (even though I am using it as I develop it). This is published publicly for transparency. If you want to help out, please [get in touch](https://forum.civilsociety.dev/c/wild-cloud/5)._
_This project is massively in progress. It's not ready to be used yet (even though I am using it as I develop it). This is published publicly for transparency. If you want to help out, please get in touch._
## Why Build Your Own Cloud?

View File

@@ -0,0 +1,73 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: jellyfin
namespace: jellyfin
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
component: web
template:
metadata:
labels:
component: web
spec:
securityContext:
runAsNonRoot: true
runAsUser: 999
runAsGroup: 999
seccompProfile:
type: RuntimeDefault
containers:
- name: jellyfin
image: "{{ .apps.jellyfin.image }}"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8096
protocol: TCP
env:
- name: TZ
value: "{{ .apps.jellyfin.timezone }}"
- name: JELLYFIN_PublishedServerUrl
value: "{{ .apps.jellyfin.publishedServerUrl }}"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
volumeMounts:
- name: config
mountPath: /config
- name: cache
mountPath: /cache
- name: media
mountPath: /media
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 10
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
volumes:
- name: config
persistentVolumeClaim:
claimName: jellyfin-config
- name: cache
persistentVolumeClaim:
claimName: jellyfin-cache
- name: media
persistentVolumeClaim:
claimName: jellyfin-media

View File

@@ -0,0 +1,24 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jellyfin-public
namespace: jellyfin
annotations:
external-dns.alpha.kubernetes.io/target: "{{ .cloud.domain }}"
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
spec:
rules:
- host: "{{ .apps.jellyfin.domain }}"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jellyfin
port:
number: {{ .apps.jellyfin.port }}
tls:
- secretName: "{{ .apps.jellyfin.tlsSecretName }}"
hosts:
- "{{ .apps.jellyfin.domain }}"

View File

@@ -0,0 +1,15 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: jellyfin
labels:
- includeSelectors: true
pairs:
app: jellyfin
managedBy: kustomize
partOf: wild-cloud
resources:
- namespace.yaml
- deployment.yaml
- service.yaml
- ingress.yaml
- pvc.yaml

View File

@@ -0,0 +1,16 @@
name: jellyfin
description: Jellyfin is a free and open-source media server and suite of multimedia applications designed to organize, manage, and share digital media files
version: 10.10.3
icon: https://jellyfin.org/images/banner-light.svg
requires: []
defaultConfig:
image: jellyfin/jellyfin:10.10.3
domain: jellyfin.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls
port: 8096
configStorage: 1Gi
cacheStorage: 10Gi
mediaStorage: 100Gi
timezone: UTC
publishedServerUrl: "https://jellyfin.{{ .cloud.domain }}"
requiredSecrets: []

View File

@@ -0,0 +1,5 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: jellyfin

36
apps/jellyfin/pvc.yaml Normal file
View File

@@ -0,0 +1,36 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jellyfin-config
namespace: jellyfin
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: "{{ .apps.jellyfin.configStorage }}"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jellyfin-cache
namespace: jellyfin
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: "{{ .apps.jellyfin.cacheStorage }}"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jellyfin-media
namespace: jellyfin
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs
resources:
requests:
storage: "{{ .apps.jellyfin.mediaStorage }}"

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: jellyfin
namespace: jellyfin
spec:
ports:
- name: http
port: {{ .apps.jellyfin.port }}
targetPort: http
protocol: TCP
selector:
component: web

View File

@@ -28,8 +28,8 @@ Tests project detection and script execution:
### `test_config_functions.bats`
Tests configuration and secret access:
- `wild-config` command
- `wild-secret` command
- `get_current_config()` function
- `get_current_secret()` function
- Configuration access from subdirectories
- Fixture data usage

View File

@@ -36,18 +36,14 @@ teardown() {
@test "init_wild_env sets WC_HOME correctly" {
mkdir -p "$TEST_PROJECT_DIR/deep/nested"
cd "$TEST_PROJECT_DIR/deep/nested"
unset WC_HOME
export WC_ROOT="$PROJECT_ROOT"
export PATH="$PROJECT_ROOT/bin:$PATH"
unset WC_HOME WC_ROOT
init_wild_env
assert_equal "$WC_HOME" "$TEST_PROJECT_DIR"
}
@test "init_wild_env sets WC_ROOT correctly" {
cd "$TEST_PROJECT_DIR"
unset WC_HOME
export WC_ROOT="$PROJECT_ROOT"
export PATH="$PROJECT_ROOT/bin:$PATH"
unset WC_HOME WC_ROOT
init_wild_env
# WC_ROOT is set (value depends on test execution context)
assert [ -n "$WC_ROOT" ]
@@ -62,7 +58,7 @@ teardown() {
@test "print functions work correctly" {
cd "$TEST_PROJECT_DIR"
run bash -c '
source "$PROJECT_ROOT/scripts/common.sh"
source "$PROJECT_ROOT/bin/wild-common.sh"
print_header "Test Header"
print_info "Test info message"
print_warning "Test warning message"

View File

@@ -15,47 +15,45 @@ teardown() {
teardown_test_project "config-test"
}
@test "wild-config with existing config" {
CLUSTER_NAME=$(wild-config "cluster.name")
@test "get_current_config with existing config" {
CLUSTER_NAME=$(get_current_config "cluster.name")
assert_equal "$CLUSTER_NAME" "test-cluster"
}
@test "wild-config with nested path" {
VIP=$(wild-config "cluster.nodes.control.vip")
@test "get_current_config with nested path" {
VIP=$(get_current_config "cluster.nodes.control.vip")
assert_equal "$VIP" "192.168.100.200"
}
@test "wild-config with non-existent key" {
NONEXISTENT=$(wild-config "nonexistent.key")
@test "get_current_config with non-existent key" {
NONEXISTENT=$(get_current_config "nonexistent.key")
assert_equal "$NONEXISTENT" ""
}
@test "active nodes configuration access - interface" {
CONTROL_NODE_INTERFACE=$(wild-config "cluster.nodes.active.\"192.168.100.201\".interface")
CONTROL_NODE_INTERFACE=$(get_current_config "cluster.nodes.active.\"192.168.100.201\".interface")
assert_equal "$CONTROL_NODE_INTERFACE" "eth0"
}
@test "active nodes configuration access - maintenance IP" {
MAINTENANCE_IP=$(wild-config "cluster.nodes.active.\"192.168.100.201\".maintenanceIp")
MAINTENANCE_IP=$(get_current_config "cluster.nodes.active.\"192.168.100.201\".maintenanceIp")
assert_equal "$MAINTENANCE_IP" "192.168.100.131"
}
@test "wild-secret function" {
@test "get_current_secret function" {
# Create temporary secrets file for testing
cp "$TEST_DIR/fixtures/sample-secrets.yaml" "$TEST_PROJECT_DIR/secrets.yaml"
SECRET_VAL=$(wild-secret "operator.cloudflareApiToken")
SECRET_VAL=$(get_current_secret "operator.cloudflareApiToken")
assert_equal "$SECRET_VAL" "test_api_token_123456789"
}
@test "config access from subdirectory" {
mkdir -p "$TEST_PROJECT_DIR/config-subdir"
cd "$TEST_PROJECT_DIR/config-subdir"
unset WC_HOME
export WC_ROOT="$PROJECT_ROOT"
export PATH="$PROJECT_ROOT/bin:$PATH"
unset WC_HOME WC_ROOT
init_wild_env
SUBDIR_CLUSTER=$(wild-config "cluster.name")
SUBDIR_CLUSTER=$(get_current_config "cluster.name")
assert_equal "$SUBDIR_CLUSTER" "test-cluster"
}

View File

@@ -29,7 +29,7 @@ setup_test_project() {
fi
# Source wild-common.sh
source "$PROJECT_ROOT/scripts/common.sh"
source "$PROJECT_ROOT/bin/wild-common.sh"
}
# Clean up test environment

View File

@@ -59,9 +59,7 @@ teardown() {
cd "$TEST_PROJECT_DIR/config-test"
# Set up environment like the scripts do
unset WC_HOME
export WC_ROOT="$PROJECT_ROOT"
export PATH="$PROJECT_ROOT/bin:$PATH"
unset WC_HOME WC_ROOT
init_wild_env
CLUSTER_NAME=$("$PROJECT_ROOT/bin/wild-config" cluster.name 2>/dev/null)
@@ -70,10 +68,8 @@ teardown() {
@test "environment variables from project root" {
cd "$TEST_PROJECT_DIR"
unset WC_HOME
export WC_ROOT="$PROJECT_ROOT"
export PATH="$PROJECT_ROOT/bin:$PATH"
source "$PROJECT_ROOT/scripts/common.sh"
unset WC_HOME WC_ROOT
source "$PROJECT_ROOT/bin/wild-common.sh"
init_wild_env
assert_equal "$WC_HOME" "$TEST_PROJECT_DIR"
@@ -83,10 +79,8 @@ teardown() {
@test "environment variables from nested directory" {
mkdir -p "$TEST_PROJECT_DIR/deep/very"
cd "$TEST_PROJECT_DIR/deep/very"
unset WC_HOME
export WC_ROOT="$PROJECT_ROOT"
export PATH="$PROJECT_ROOT/bin:$PATH"
source "$PROJECT_ROOT/scripts/common.sh"
unset WC_HOME WC_ROOT
source "$PROJECT_ROOT/bin/wild-common.sh"
init_wild_env
assert_equal "$WC_HOME" "$TEST_PROJECT_DIR"