From 25a7e49da2be118a8ee8230929ff5565869da3ba Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Mon, 19 Oct 2026 22:12:54 +0200 Subject: [PATCH] Support named additional Mumble services --- README.md | 2 +- ...direct-service.yaml => additional-services.yaml} | 13 ++++++++----- chart/mumble/values.schema.json | 2 +- chart/mumble/values.yaml | 4 +--- test.sh | 4 ++-- 5 files changed, 13 insertions(+), 12 deletions(-) rename chart/mumble/templates/{direct-service.yaml => additional-services.yaml} (56%) diff --git a/README.md b/README.md index 5e27bbd..62781fd 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The PostgreSQL subchart is optional. With `postgresql.enabled=true`, set `postgr The homelab deployment is selected in [homelab-gitops](https://code.brunner.ninja/feedc0de/homelab-gitops/src/branch/main/apps/mumble). It uses the optional PostgreSQL subchart and keeps the existing `default/mumble` Service and Deployment names, the `mumble-secrets` and TLS Secrets, and TCP/UDP port 64738. The old shared PostgreSQL database is deliberately left intact as rollback data. `bootstrap-homelab-secrets.sh` is a guarded, one-time secret recovery/bootstrap helper; it does not deploy workloads. Back up the dedicated PostgreSQL PVC and both Mumble Secrets together. -`directAccess.enabled` optionally creates a second `mumble-direct` Service on the same TCP/UDP ports. Set `directAccess.externalIPs` to addresses already routed to a cluster node. This Service uses cluster-wide endpoint routing, so an address on one node can reach a ready Mumble pod on another node. It leaves the primary LoadBalancer Service and its client-IP policy unchanged. The homelab uses this for the Funkfeuer/OLSR address on `arschrock`. +`service.additionalServices` optionally creates named additional Services on the same TCP/UDP ports. Each entry creates `mumble-` and accepts Service `spec` fields such as `type` and `externalIPs`. For example, the homelab uses `service.additionalServices.funkfeuer.spec` with `type: ClusterIP` and `externalIPs: [10.12.4.170]`. This reaches a ready Mumble pod from an address routed to a cluster node and leaves the primary LoadBalancer Service and its client-IP policy unchanged. Chart package versions come from Git tags using `git describe --tags --long`. The tag `v1.5.939` denotes the tested upstream Mumble release; a later chart-only commit publishes a distinct `1.5.939-N-gHASH` chart version. Source CI validates the chart, publishes it to the indexed `brunner.ninja` repository, and promotes only this chart's selected version in GitOps. CI has no Kubernetes credential. diff --git a/chart/mumble/templates/direct-service.yaml b/chart/mumble/templates/additional-services.yaml similarity index 56% rename from chart/mumble/templates/direct-service.yaml rename to chart/mumble/templates/additional-services.yaml index 380f739..0c7ed2d 100644 --- a/chart/mumble/templates/direct-service.yaml +++ b/chart/mumble/templates/additional-services.yaml @@ -1,14 +1,16 @@ -{{- if and .Values.mumble.enabled .Values.directAccess.enabled }} +{{- if .Values.mumble.enabled }} +{{- range $name, $additional := .Values.service.additionalServices }} +--- apiVersion: v1 kind: Service metadata: - name: mumble-direct + name: {{ printf "mumble-%s" $name | trunc 63 | trimSuffix "-" }} labels: app.kubernetes.io/name: mumble spec: - type: ClusterIP - externalIPs: -{{ toYaml .Values.directAccess.externalIPs | indent 4 }} + {{- with $additional.spec }} + {{ toYaml . | indent 2 }} + {{- end }} selector: app.kubernetes.io/name: mumble ports: @@ -21,3 +23,4 @@ spec: targetPort: mumble-udp protocol: UDP {{- end }} +{{- end }} diff --git a/chart/mumble/values.schema.json b/chart/mumble/values.schema.json index d42c5ea..5ce384e 100644 --- a/chart/mumble/values.schema.json +++ b/chart/mumble/values.schema.json @@ -4,7 +4,7 @@ "properties": { "image": {"type": "object", "properties": {"repository": {"type": "string", "minLength": 1}, "tag": {"type": "string"}}}, "service": {"type": "object", "properties": {"type": {"enum": ["LoadBalancer", "ClusterIP"]}, "loadBalancerIP": {"type": "string"}, "externalTrafficPolicy": {"enum": ["Local", "Cluster"]}, "annotations": {"type": "object", "additionalProperties": {"type": "string"}}}}, - "directAccess": {"type": "object", "properties": {"enabled": {"type": "boolean"}, "externalIPs": {"type": "array", "items": {"type": "string", "format": "ipv4"}}}, "if": {"properties": {"enabled": {"const": true}}}, "then": {"required": ["externalIPs"], "properties": {"externalIPs": {"minItems": 1}}}}, + "service": {"type": "object", "properties": {"additionalServices": {"type": "object", "additionalProperties": {"type": "object", "required": ["spec"], "properties": {"spec": {"type": "object", "properties": {"type": {"enum": ["ClusterIP", "NodePort", "LoadBalancer"]}, "externalIPs": {"type": "array", "items": {"type": "string", "format": "ipv4"}}}}}}}}}, "database": {"type": "object", "properties": {"host": {"type": "string", "minLength": 1}}}, "secrets": {"type": "object", "properties": {"mumble": {"type": "string", "minLength": 1}, "postgresql": {"type": "string", "minLength": 1}, "tls": {"type": "string", "minLength": 1}}}, "config": {"type": "object", "properties": {"welcomeText": {"type": "string"}, "registrationName": {"type": "string"}, "registrationURL": {"type": "string"}, "registrationHostname": {"type": "string"}, "registrationLocation": {"type": "string"}}}, diff --git a/chart/mumble/values.yaml b/chart/mumble/values.yaml index 298e49a..1f475da 100644 --- a/chart/mumble/values.yaml +++ b/chart/mumble/values.yaml @@ -11,9 +11,7 @@ service: annotations: {} loadBalancerIP: "" externalTrafficPolicy: Local -directAccess: - enabled: false - externalIPs: [] + additionalServices: {} database: host: postgresql secrets: diff --git a/test.sh b/test.sh index 50aaa02..930de46 100755 --- a/test.sh +++ b/test.sh @@ -29,8 +29,8 @@ pod = deployment['spec']['template']['spec'] assert [container['name'] for container in pod['initContainers']] == ['render-config'] assert pod['containers'][0]['image'].endswith('v1.5.939') assert any(volume.get('secret', {}).get('secretName') == 'mumble-tls' for volume in pod['volumes']) -direct = render('--set', 'directAccess.enabled=true', '--set', 'directAccess.externalIPs[0]=10.12.4.170') -direct_service = next(doc for doc in direct if doc['kind'] == 'Service' and doc['metadata']['name'] == 'mumble-direct') +direct = render('--set', 'service.additionalServices.funkfeuer.spec.type=ClusterIP', '--set', 'service.additionalServices.funkfeuer.spec.externalIPs[0]=10.12.4.170') +direct_service = next(doc for doc in direct if doc['kind'] == 'Service' and doc['metadata']['name'] == 'mumble-funkfeuer') assert direct_service['spec']['externalIPs'] == ['10.12.4.170'] assert direct_service['spec']['type'] == 'ClusterIP' assert 'externalTrafficPolicy' not in direct_service['spec']