#!/usr/bin/env bash set -euo pipefail readonly expected_context="kubernetes-admin@kubernetes" readonly node="arschrock" readonly media_path="/komposthaufen/multimedia" readonly dynmap_tiles_path="/home/feedc0de/mcserver/plugins/dynmap/web/tiles" readonly dynmap_ready_label="minecraft.brunner.ninja/dynmap-ready" readonly library_services=(lidarr.service readarr.service radarr.service sonarr.service bazarr.service) readonly whisparr_service="whisparr.service" readonly whisparr_library_path="/mnt/msdownld.tmp/_hardware/whisparr" current_context="$(kubectl config current-context)" if [[ "${current_context}" != "${expected_context}" ]]; then echo "Refusing to modify ${node}: context is ${current_context}, expected ${expected_context}." >&2 exit 1 fi if ! mountpoint -q /komposthaufen; then echo "/komposthaufen is not a mountpoint; storage remains offline." >&2 exit 1 fi if [[ "$(findmnt -T "${media_path}" -n -o FSTYPE)" != bcachefs ]]; then echo "${media_path} is not backed by bcachefs; storage remains offline." >&2 exit 1 fi if [[ ! -d "${media_path}/Videos" ]]; then echo "Required directory ${media_path}/Videos is missing; storage remains offline." >&2 exit 1 fi if ! mountpoint -q "${dynmap_tiles_path}" || [[ "$(findmnt -T "${dynmap_tiles_path}" -n -o FSTYPE)" != bcachefs ]]; then echo "${dynmap_tiles_path} is not mounted from bcachefs; Paper remains stopped." >&2 exit 1 fi for attempt in $(seq 1 60); do if kubectl get node "${node}" >/dev/null 2>&1; then # Stale readiness after a reboot may have mounted the empty system-disk # directory. Drop both gates and recreate every hostPath consumer first. kubectl label node "${node}" media.brunner.ninja/multimedia-ready- 2>/dev/null || true if ! kubectl label node "${node}" "${dynmap_ready_label}-" 2>/dev/null; then labels="$(kubectl get node "${node}" --output=jsonpath='{.metadata.labels}')" if [[ "${labels}" == *"${dynmap_ready_label}"* ]]; then echo "Could not clear the stale Dynmap-ready label on ${node}." >&2 exit 1 fi fi for namespace in plex jellyfin copyparty; do mapfile -t pods < <( kubectl --namespace "${namespace}" get pods \ --selector="app.kubernetes.io/component=server" \ --output=name 2>/dev/null || true ) if ((${#pods[@]})); then kubectl --namespace "${namespace}" delete "${pods[@]}" --wait=false for pod in "${pods[@]}"; do kubectl --namespace "${namespace}" wait --for=delete "${pod}" --timeout=5m done fi done mapfile -t paper_pods < <( kubectl --namespace mcserver get pods \ --selector="app.kubernetes.io/name=minecraft-paper,app.kubernetes.io/instance=paper" \ --output=name 2>/dev/null || true ) if ((${#paper_pods[@]})); then kubectl --namespace mcserver delete "${paper_pods[@]}" --wait=false for pod in "${paper_pods[@]}"; do kubectl --namespace mcserver wait --for=delete "${pod}" --timeout=15m done fi kubectl label node "${node}" media.brunner.ninja/multimedia-ready=true --overwrite kubectl label node "${node}" "${dynmap_ready_label}=true" --overwrite sudo -n systemctl start "${library_services[@]}" if mountpoint -q /mnt \ && [[ "$(findmnt -T /mnt -n -o FSTYPE)" == bcachefs ]] \ && [[ -d "${whisparr_library_path}" ]]; then sudo -n systemctl start "${whisparr_service}" else echo "Whisparr remains stopped; its /mnt Bcachefs library is not ready." fi echo "${node} is media-ready; Plex, Jellyfin, Copyparty and Paper may now be scheduled." exit 0 fi sleep 2 done echo "Kubernetes API did not become available; ${node} remains media-offline." >&2 exit 1