Initial commit, adding mumble kubernetes files and CI
Validate and deploy Mumble / Validate manifests (push) Failing after 27s
Validate and deploy Mumble / Deploy to Kubernetes (push) Has been skipped

This commit is contained in:
2026-08-08 21:35:33 +02:00
parent 5262508344
commit e05116dd1e
8 changed files with 771 additions and 2 deletions
+104
View File
@@ -0,0 +1,104 @@
name: Validate and deploy Mumble
on:
push:
pull_request:
env:
KUBECTL_VERSION: v1.36.3
KUBERNETES_API: https://host.containers.internal:6443
KUBERNETES_TLS_SERVER_NAME: 192.168.0.2
NAMESPACE: default
jobs:
validate:
name: Validate manifests
runs-on: ubuntu-latest
steps:
- name: Check out source
uses: actions/checkout@v4
- name: Install kubectl
run: |
curl --fail --silent --show-error --location \
--output "${RUNNER_TEMP}/kubectl" \
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
curl --fail --silent --show-error --location \
--output "${RUNNER_TEMP}/kubectl.sha256" \
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl.sha256"
printf '%s %s\n' "$(cat "${RUNNER_TEMP}/kubectl.sha256")" "${RUNNER_TEMP}/kubectl" | sha256sum --check
chmod 0700 "${RUNNER_TEMP}/kubectl"
- name: Validate scripts and Kubernetes resources
run: |
sh -n install.sh
bash -n create-ci-kubeconfig.sh
"${RUNNER_TEMP}/kubectl" create \
--dry-run=client \
--validate=false \
--filename mumble.yaml >/dev/null
deploy:
name: Deploy to Kubernetes
if: gitea.ref == 'refs/heads/main'
needs: validate
runs-on: ubuntu-latest
steps:
- name: Check out source
uses: actions/checkout@v4
- name: Install kubectl
run: |
curl --fail --silent --show-error --location \
--output "${RUNNER_TEMP}/kubectl" \
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
curl --fail --silent --show-error --location \
--output "${RUNNER_TEMP}/kubectl.sha256" \
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl.sha256"
printf '%s %s\n' "$(cat "${RUNNER_TEMP}/kubectl.sha256")" "${RUNNER_TEMP}/kubectl" | sha256sum --check
chmod 0700 "${RUNNER_TEMP}/kubectl"
- name: Configure Kubernetes access
env:
KUBE_CONFIG_BASE64: ${{ secrets.KUBE_CONFIG_BASE64 }}
run: |
test -n "${KUBE_CONFIG_BASE64}"
printf '%s' "${KUBE_CONFIG_BASE64}" | base64 --decode > "${RUNNER_TEMP}/kubeconfig"
chmod 0600 "${RUNNER_TEMP}/kubeconfig"
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
"${RUNNER_TEMP}/kubectl" config set-cluster cluster \
--server="${KUBERNETES_API}" \
--tls-server-name="${KUBERNETES_TLS_SERVER_NAME}"
- name: Apply and verify
run: |
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
"${RUNNER_TEMP}/kubectl" apply \
--dry-run=server \
--validate=false \
--filename mumble.yaml
"${RUNNER_TEMP}/kubectl" apply \
--validate=false \
--filename mumble.yaml
for attempt in {1..60}; do
IFS='|' read -r generation observed desired updated available unavailable <<< "$(
"${RUNNER_TEMP}/kubectl" --namespace "${NAMESPACE}" \
get deployment/mumble \
--output=jsonpath='{.metadata.generation}|{.status.observedGeneration}|{.spec.replicas}|{.status.updatedReplicas}|{.status.availableReplicas}|{.status.unavailableReplicas}'
)"
echo "Rollout ${attempt}/60: generation ${observed}/${generation}, replicas ${updated}/${desired} updated, ${available}/${desired} available"
if [[ "${observed}" == "${generation}" \
&& "${updated}" == "${desired}" \
&& "${available}" == "${desired}" \
&& ( -z "${unavailable}" || "${unavailable}" == "0" ) ]]; then
echo "Mumble deployment successfully rolled out"
exit 0
fi
sleep 5
done
echo "Mumble rollout did not complete within five minutes" >&2
exit 1
+7
View File
@@ -0,0 +1,7 @@
# Legacy host configuration contains obsolete plaintext credentials.
mumble-server.ini
# Never commit generated cluster credentials.
kubeconfig
*.kubeconfig
.env
+178 -2
View File
@@ -1,3 +1,179 @@
# mumble-kubernetes
# Mumble on Kubernetes
Configuration files to run my mumble server in kubernetes with cert-manager and L2 LoadBalancer (uses my shared postgresql)
Production deployment for the brunner.ninja Mumble server. It runs the
official Mumble server image with trusted TLS, a MetalLB address, and the
shared PostgreSQL instance in the `default` namespace.
## Architecture
- `mumblevoip/mumble-server:v1.5.915-1` runs as an unprivileged user.
- MetalLB advertises `192.168.0.14`; TCP and UDP use port `64738` on the same
LoadBalancer Service.
- `brunner-ninja-tls`, maintained by cert-manager, supplies the certificate for
both `brunner.ninja` and `*.brunner.ninja`.
- A small sidecar watches the mounted certificate and asks Mumble to reload TLS
with `SIGUSR1` after renewal.
- Mumble stores channels, ACLs, registered users, bans, and server state in the
`mumble` database on `postgresql.default.svc.cluster.local`.
- `/data` and the generated INI are ephemeral. No Mumble PVC is needed.
- The single replica uses a `Recreate` strategy because two processes must not
serve the same Mumble virtual server database concurrently.
The runtime INI is rendered into a mode-`0600` in-memory volume. This avoids an
upstream container-entrypoint behavior that can print `registerPassword` when
configuration is supplied through environment variables.
## Repository contents
- `mumble.yaml`: LoadBalancer Service and Mumble Deployment.
- `install.sh`: administrator installation, credential generation, idempotent
PostgreSQL bootstrap, and rollout verification.
- `test.sh`: local syntax and client-side Kubernetes validation.
- `ci-deployer.yaml`: restricted Gitea Actions ServiceAccount, Role, binding,
and revocable token request.
- `create-ci-kubeconfig.sh`: one-time CI identity bootstrap.
- `.gitea/workflows/deploy.yml`: validation on every push and pull request;
deployment after successful validation of `main`.
The old host-level `mumble-server.ini` is intentionally ignored because it
contains obsolete plaintext credentials. Do not commit kubeconfigs, database
passwords, TLS keys, or generated Kubernetes Secret values.
## Prerequisites
- Kubernetes and kubectl.
- MetalLB pool `default` containing the reserved address `192.168.0.14`.
- `default/postgresql` Service and Secret. The Secret must contain
`postgres-password`.
- `default/brunner-ninja-tls`, maintained by the existing cert-manager
`letsencrypt-dns` setup.
- Router forwarding of TCP and UDP `64738` to `192.168.0.14`. On the MikroTik,
the two rules use `dst-address-list=WANs`, which also permits LAN hairpin
connections through `brunner.ninja`.
## Install or restore manually
Run the first installation with an administrator kubeconfig:
```sh
./install.sh
```
On the first run, the script creates `default/mumble-secrets` with independent
256-bit random database, SuperUser, and public-registry passwords. It then
creates the PostgreSQL role and database if absent. Later runs preserve the
Secret and database, reset the role to the stored password, reapply ownership
and grants, and safely recreate or update the workload.
Rotate only the public Mumble registry password with:
```sh
ROTATE_REGISTRATION_PASSWORD=true ./install.sh
```
Retrieve the Mumble administrator password without placing it in Git:
```sh
kubectl get secret mumble-secrets -n default \
-o jsonpath='{.data.superuser-password}' | base64 -d; echo
```
Connect to `brunner.ninja:64738` or `mumble.brunner.ninja:64738`.
### Restoring a PostgreSQL backup
Stop Mumble before replacing its database:
```sh
kubectl delete deployment mumble -n default
```
Restore the `mumble` database and ensure its objects are owned by role
`mumble`, then run `./install.sh`. The bootstrap is idempotent: it does not drop
an existing database or tables. Mumble may migrate an older compatible schema
when it starts. Its channels and registrations remain in PostgreSQL.
## Validate locally
```sh
./test.sh
```
The test checks both shell scripts and parses the Kubernetes resources without
contacting the cluster.
## Automatic deployment with Gitea Actions
The workflow validates every push and pull request. A successful push to
`main` applies `mumble.yaml` and polls the Deployment for up to five minutes.
It does not build a container because this repository deliberately uses the
official, version-pinned Mumble image.
CI does not invoke `install.sh` and cannot create application credentials or
bootstrap PostgreSQL. Its Role can call `get`, `patch`, and `update` only for
the existing `default/mumble` Service and Deployment. It cannot create or
delete workloads and has no direct Kubernetes Secrets API permission.
### One-time CI setup
1. Install Mumble manually with `./install.sh` so the Service, Deployment,
application Secret, role, and database exist.
2. With an administrator kubeconfig active, run:
```sh
./create-ci-kubeconfig.sh
```
3. Copy the single base64 output line into the Gitea repository Actions secret
named `KUBE_CONFIG_BASE64`.
4. Protect `main` and require the validation job before merging.
5. Push a harmless README change and confirm the deploy job reports a healthy
rollout.
The runner networking in the workflow matches the existing brunner.ninja
runner: API URL `https://host.containers.internal:6443`, with TLS server name
`192.168.0.2`.
To revoke CI access, delete the token and remove the Gitea secret:
```sh
kubectl delete secret mumble-deployer-token -n default
```
Running `./create-ci-kubeconfig.sh` again recreates the token and prints a new
kubeconfig.
### Security boundary
The Role prevents ordinary API reads of Secrets and limits updates to two named
objects. Kubernetes RBAC cannot restrict individual fields inside a Deployment
patch, however. A malicious change merged into `main` could modify the Mumble
pod specification and potentially expose Secrets already referenced by that
workload. Branch protection and review of manifest and workflow changes remain
part of the security boundary. Admission policy would be required to constrain
specific pod fields further.
## Gitea CI or Argo CD?
Gitea CI is the recommended deployment mechanism for this repository today.
It is already proven on this cluster, the deployment is only two resources,
and it does not automatically undo deliberate maintenance such as deleting the
Deployment while restoring PostgreSQL.
| Capability | Gitea Actions used here | Argo CD |
| --- | --- | --- |
| Trigger | Push to `main` | Continuous Git reconciliation/webhook |
| Drift detection | No | Built-in diff and health view |
| Self-healing | No; rerun CI or `install.sh` | Optional automatic reconciliation |
| Deleted Deployment | Stays deleted for maintenance | Recreated when self-heal is enabled |
| Cluster credential | Restricted token per repository | Repository credential plus central Argo controller |
| Rollout view | Action log | Application resource tree, health, sync history, and UI |
| Pruning | Not performed | Optional, with confirmation controls available |
| Secret handling | Existing Secrets remain out of Git | Still needs an external Secret strategy |
Argo CD becomes attractive when several more workloads should share one
consistent deployment model, when live drift must be visible or automatically
repaired, or when multiple clusters/environments are introduced. It can remove
per-repository Kubernetes deploy tokens because CI only needs to update Git.
Adoption should start with an `AppProject`, explicit namespace/resource limits,
and a maintenance procedure that disables sync before database restores.
+58
View File
@@ -0,0 +1,58 @@
# Bootstrap this file once with an administrator identity. CI can update only
# the two existing Mumble resources and cannot call the Kubernetes Secrets API.
apiVersion: v1
kind: ServiceAccount
metadata:
name: mumble-deployer
namespace: default
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: mumble-deployer
namespace: default
rules:
- apiGroups:
- ""
resources:
- services
resourceNames:
- mumble
verbs:
- get
- patch
- update
- apiGroups:
- apps
resources:
- deployments
resourceNames:
- mumble
verbs:
- get
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mumble-deployer
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: mumble-deployer
subjects:
- kind: ServiceAccount
name: mumble-deployer
namespace: default
---
apiVersion: v1
kind: Secret
metadata:
name: mumble-deployer-token
namespace: default
annotations:
kubernetes.io/service-account.name: mumble-deployer
type: kubernetes.io/service-account-token
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
project_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
namespace=${NAMESPACE:-default}
service_account=${SERVICE_ACCOUNT:-mumble-deployer}
secret=${TOKEN_SECRET:-mumble-deployer-token}
kubectl apply --filename "${project_dir}/ci-deployer.yaml" >&2
for attempt in {1..30}; do
if kubectl --namespace "${namespace}" get secret "${secret}" \
--output=jsonpath='{.data.token}' 2>/dev/null | grep -q .; then
break
fi
if [[ "${attempt}" == 30 ]]; then
echo "Timed out waiting for the service-account token" >&2
exit 1
fi
sleep 1
done
workdir=$(mktemp --directory)
trap 'rm -rf "${workdir}"' EXIT
server=$(kubectl config view --minify --output=jsonpath='{.clusters[0].cluster.server}')
kubectl --namespace "${namespace}" get secret "${secret}" \
--output=jsonpath='{.data.ca\.crt}' | base64 --decode > "${workdir}/ca.crt"
token=$(kubectl --namespace "${namespace}" get secret "${secret}" \
--output=jsonpath='{.data.token}' | base64 --decode)
export KUBECONFIG="${workdir}/config"
kubectl config set-cluster cluster \
--server="${server}" \
--certificate-authority="${workdir}/ca.crt" \
--embed-certs=true >/dev/null
kubectl config set-credentials "${service_account}" --token="${token}" >/dev/null
kubectl config set-context mumble \
--cluster=cluster \
--user="${service_account}" \
--namespace="${namespace}" >/dev/null
kubectl config use-context mumble >/dev/null
echo "Store the following single line as the Gitea Actions secret KUBE_CONFIG_BASE64." >&2
echo "Treat it as a password; do not commit it." >&2
base64 --wrap=0 "${KUBECONFIG}"
printf '\n'
Executable
+62
View File
@@ -0,0 +1,62 @@
#!/bin/sh
set -eu
NAMESPACE=default
SECRET_NAME=mumble-secrets
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
for command in kubectl openssl; do
if ! command -v "$command" >/dev/null 2>&1; then
echo "Required command not found: $command" >&2
exit 1
fi
done
for dependency in secret/postgresql secret/brunner-ninja-tls; do
if ! kubectl get "$dependency" --namespace "$NAMESPACE" >/dev/null 2>&1; then
echo "Required Kubernetes object not found: $NAMESPACE/$dependency" >&2
exit 1
fi
done
if ! kubectl get secret "$SECRET_NAME" --namespace "$NAMESPACE" >/dev/null 2>&1; then
database_password=$(openssl rand -hex 32)
superuser_password=$(openssl rand -hex 32)
registration_password=$(openssl rand -hex 32)
kubectl create secret generic "$SECRET_NAME" \
--namespace "$NAMESPACE" \
--from-literal="database-password=$database_password" \
--from-literal="superuser-password=$superuser_password" \
--from-literal="registration-password=$registration_password"
unset database_password superuser_password registration_password
echo "Created $NAMESPACE/$SECRET_NAME with random database, SuperUser, and public-registry passwords."
else
echo "Keeping existing $NAMESPACE/$SECRET_NAME."
registration_password_present=$(kubectl get secret "$SECRET_NAME" \
--namespace "$NAMESPACE" \
-o go-template='{{if index .data "registration-password"}}yes{{end}}')
if [ -z "$registration_password_present" ] || [ "${ROTATE_REGISTRATION_PASSWORD:-false}" = true ]; then
registration_password=$(openssl rand -hex 32)
kubectl patch secret "$SECRET_NAME" \
--namespace "$NAMESPACE" \
--type=merge \
--patch "{\"stringData\":{\"registration-password\":\"$registration_password\"}}" >/dev/null
unset registration_password
echo "Set a new random public-registry password in $NAMESPACE/$SECRET_NAME."
fi
unset registration_password_present
fi
kubectl apply -f "$SCRIPT_DIR/mumble.yaml"
kubectl rollout status deployment/mumble --namespace "$NAMESPACE" --timeout=5m
echo
echo "Mumble is available at mumble.brunner.ninja:64738 (MetalLB 192.168.0.14)."
echo "Retrieve the SuperUser password with:"
echo "kubectl get secret $SECRET_NAME -n $NAMESPACE -o jsonpath='{.data.superuser-password}' | base64 -d; echo"
+295
View File
@@ -0,0 +1,295 @@
apiVersion: v1
kind: Service
metadata:
name: mumble
namespace: default
labels:
app.kubernetes.io/name: mumble
annotations:
metallb.io/address-pool: default
spec:
type: LoadBalancer
loadBalancerIP: 192.168.0.14
externalTrafficPolicy: Local
selector:
app.kubernetes.io/name: mumble
ports:
- name: mumble-tcp
port: 64738
targetPort: mumble-tcp
protocol: TCP
- name: mumble-udp
port: 64738
targetPort: mumble-udp
protocol: UDP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mumble
namespace: default
labels:
app.kubernetes.io/name: mumble
spec:
replicas: 1
# Two Mumble processes must not serve the same virtual server/database.
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: mumble
template:
metadata:
labels:
app.kubernetes.io/name: mumble
spec:
# Allows the certificate watcher to send SIGUSR1 to mumble-server.
shareProcessNamespace: true
automountServiceAccountToken: false
terminationGracePeriodSeconds: 30
securityContext:
fsGroup: 10000
fsGroupChangePolicy: OnRootMismatch
initContainers:
- name: render-config
image: busybox:1.37.0
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -ec
- |
umask 077
cat > /config/mumble-server.ini <<EOF
database=mumble
dbDriver=QPSQL
dbUsername=mumble
dbPassword=$MUMBLE_DB_PASSWORD
dbHost=postgresql.default.svc.cluster.local
dbPort=5432
ice="tcp -h 127.0.0.1 -p 6502"
logfile=
welcometext="<br />Servas auf <b>brunner.ninja</b>.<br />Dieser Server ist aus Funkfeuer und manchmal auch aus dem Internet erreichbar.<br />Bau kan Scha&szlig;!<br />"
port=64738
bandwidth=558000
users=100
messageburst=5
messagelimit=1
allowping=true
registerName=brunner.ninja
registerPassword=$MUMBLE_REGISTRATION_PASSWORD
registerUrl=https://brunner.ninja/
registerHostname=mumble.brunner.ninja
registerLocation=AT
sslCert=/certs/tls.crt
sslKey=/certs/tls.key
[Ice]
Ice.Warn.UnknownProperties=1
Ice.MessageSizeMax=65536
EOF
env:
- name: MUMBLE_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mumble-secrets
key: database-password
- name: MUMBLE_REGISTRATION_PASSWORD
valueFrom:
secretKeyRef:
name: mumble-secrets
key: registration-password
volumeMounts:
- name: config
mountPath: /config
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
resources:
requests:
cpu: 1m
memory: 2Mi
limits:
cpu: 20m
memory: 16Mi
- name: bootstrap-postgresql
image: bitnamilegacy/postgresql:17.6.0-debian-12-r4
imagePullPolicy: IfNotPresent
command:
- /bin/bash
- -ec
- |
psql \
--host=postgresql.default.svc.cluster.local \
--port=5432 \
--username=postgres \
--dbname=postgres \
--set=ON_ERROR_STOP=1 \
--set=mumble_password="$MUMBLE_DB_PASSWORD" <<'SQL'
SELECT format('CREATE ROLE %I LOGIN PASSWORD %L', 'mumble', :'mumble_password')
WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'mumble')
\gexec
SELECT format('ALTER ROLE %I LOGIN PASSWORD %L', 'mumble', :'mumble_password')
\gexec
SELECT format('CREATE DATABASE %I OWNER %I', 'mumble', 'mumble')
WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_database WHERE datname = 'mumble')
\gexec
ALTER DATABASE mumble OWNER TO mumble;
REVOKE ALL ON DATABASE mumble FROM PUBLIC;
GRANT ALL PRIVILEGES ON DATABASE mumble TO mumble;
SQL
env:
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: postgresql
key: postgres-password
- name: MUMBLE_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mumble-secrets
key: database-password
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
seccompProfile:
type: RuntimeDefault
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
cpu: 200m
memory: 128Mi
containers:
- name: mumble
image: mumblevoip/mumble-server:v1.5.915-1
imagePullPolicy: IfNotPresent
ports:
- name: mumble-tcp
containerPort: 64738
protocol: TCP
- name: mumble-udp
containerPort: 64738
protocol: UDP
env:
- name: MUMBLE_CHOWN_DATA
value: "false"
- name: MUMBLE_CUSTOM_CONFIG_FILE
value: /config/mumble-server.ini
- name: MUMBLE_SUPERUSER_PASSWORD
valueFrom:
secretKeyRef:
name: mumble-secrets
key: superuser-password
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /config
readOnly: true
- name: certificate
mountPath: /certs
readOnly: true
readinessProbe:
exec:
command:
- /bin/bash
- -ec
- >-
awk '$2 ~ /:FCE2$/ && $4 == "0A" { found=1 }
END { exit(found ? 0 : 1) }' /proc/net/tcp /proc/net/tcp6
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 6
livenessProbe:
exec:
command:
- /bin/bash
- -ec
- >-
awk '$2 ~ /:FCE2$/ && $4 == "0A" { found=1 }
END { exit(found ? 0 : 1) }' /proc/net/tcp /proc/net/tcp6
initialDelaySeconds: 30
periodSeconds: 20
timeoutSeconds: 3
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
cpu: 500m
memory: 512Mi
- name: certificate-reloader
image: busybox:1.37.0
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -ec
- |
previous="$(sha256sum /certs/tls.crt /certs/tls.key)"
while sleep 60; do
current="$(sha256sum /certs/tls.crt /certs/tls.key)"
if [ "$current" != "$previous" ]; then
set -- $(pidof mumble-server)
if [ "$#" -gt 0 ]; then
kill -USR1 "$@"
echo "Reloaded Mumble TLS certificate"
fi
previous="$current"
fi
done
volumeMounts:
- name: certificate
mountPath: /certs
readOnly: true
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
resources:
requests:
cpu: 1m
memory: 2Mi
limits:
cpu: 20m
memory: 16Mi
volumes:
# PostgreSQL stores Mumble's durable state; /data only holds generated config.
- name: data
emptyDir: {}
- name: config
emptyDir: {}
- name: certificate
secret:
secretName: brunner-ninja-tls
defaultMode: 0440
Executable
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
project_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
command -v kubectl >/dev/null 2>&1 || {
echo "Required command not found: kubectl" >&2
exit 1
}
sh -n "${project_dir}/install.sh"
bash -n "${project_dir}/create-ci-kubeconfig.sh"
kubectl create \
--dry-run=client \
--validate=false \
--filename "${project_dir}/mumble.yaml" >/dev/null
echo "Mumble scripts and Kubernetes resources are valid."