diff --git a/_adopt_replica_pvcs.sh b/_adopt_replica_pvcs.sh new file mode 100644 index 0000000..b3bcd4c --- /dev/null +++ b/_adopt_replica_pvcs.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -euo pipefail +NS=prole-db +CLUSTER_UID=$(kubectl get cluster prole-db -n "$NS" -o jsonpath='{.metadata.uid}' 2>/dev/null) +echo "Cluster UID: $CLUSTER_UID" + +echo "=== Pausing CNPG controller ===" +kubectl scale deployment cnpg-controller-manager -n cnpg-system --replicas=0 +sleep 5 + +echo "=== Cleaning up serial 8+ PVCs and jobs ===" +for serial in 8 9 10; do + kubectl delete pvc "prole-db-${serial}" "prole-db-${serial}-wal" -n "$NS" --ignore-not-found 2>/dev/null || true + kubectl delete job -n "$NS" -l "cnpg.io/instanceName=prole-db-${serial}" --ignore-not-found 2>/dev/null || true +done + +echo "=== Patching replica PVCs with full CNPG labels/annotations ===" +for serial in 3 7; do + # Data PVC + kubectl patch pvc "prole-db-${serial}" -n "$NS" --type=merge -p "{ + \"metadata\": { + \"labels\": { + \"app.kubernetes.io/component\": \"database\", + \"app.kubernetes.io/managed-by\": \"cloudnative-pg\", + \"app.kubernetes.io/name\": \"postgresql\", + \"cnpg.io/cluster\": \"prole-db\", + \"cnpg.io/instanceName\": \"prole-db-${serial}\", + \"cnpg.io/instanceRole\": \"replica\", + \"cnpg.io/pvcRole\": \"PG_DATA\", + \"role\": \"replica\" + }, + \"annotations\": { + \"cnpg.io/nodeSerial\": \"${serial}\", + \"cnpg.io/operatorVersion\": \"1.28.1\", + \"cnpg.io/pvcStatus\": \"ready\" + }, + \"ownerReferences\": [{ + \"apiVersion\": \"postgresql.cnpg.io/v1\", + \"controller\": true, + \"kind\": \"Cluster\", + \"name\": \"prole-db\", + \"uid\": \"${CLUSTER_UID}\" + }] + } + }" + echo " Patched prole-db-${serial}" + + # WAL PVC + kubectl patch pvc "prole-db-${serial}-wal" -n "$NS" --type=merge -p "{ + \"metadata\": { + \"labels\": { + \"app.kubernetes.io/component\": \"database\", + \"app.kubernetes.io/managed-by\": \"cloudnative-pg\", + \"app.kubernetes.io/name\": \"postgresql\", + \"cnpg.io/cluster\": \"prole-db\", + \"cnpg.io/instanceName\": \"prole-db-${serial}\", + \"cnpg.io/instanceRole\": \"replica\", + \"cnpg.io/pvcRole\": \"PG_WAL\", + \"role\": \"replica\" + }, + \"annotations\": { + \"cnpg.io/nodeSerial\": \"${serial}\", + \"cnpg.io/operatorVersion\": \"1.28.1\", + \"cnpg.io/pvcStatus\": \"ready\" + }, + \"ownerReferences\": [{ + \"apiVersion\": \"postgresql.cnpg.io/v1\", + \"controller\": true, + \"kind\": \"Cluster\", + \"name\": \"prole-db\", + \"uid\": \"${CLUSTER_UID}\" + }] + } + }" + echo " Patched prole-db-${serial}-wal" +done + +echo "=== Resuming CNPG controller ===" +kubectl scale deployment cnpg-controller-manager -n cnpg-system --replicas=1 +echo "Done — CNPG should now create instance pods for serials 3 (pi) and 7 (merlin)" +sleep 20 +kubectl get pods,jobs -n "$NS" -o wide 2>/dev/null | grep -v "Completed\|initdb" +kubectl get cluster prole-db -n "$NS" -o jsonpath='Phase: {.status.phase} Ready: {.status.readyInstances}/{.spec.instances}{"\n"}' 2>/dev/null diff --git a/_fix_replica_merlin.sh b/_fix_replica_merlin.sh new file mode 100644 index 0000000..6f5d98c --- /dev/null +++ b/_fix_replica_merlin.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +set -euo pipefail +NS=prole-db + +echo "=== Pausing CNPG controller ===" +kubectl scale deployment cnpg-controller-manager -n cnpg-system --replicas=0 +sleep 5 + +echo "=== Cleaning up all pending/failed replica PVCs and jobs ===" +# Keep prole-db-1 (primary) and prole-db-3 (pi replica). Delete everything else. +for serial in 2 4 5 6 7 8 9; do + kubectl delete pvc "prole-db-${serial}" "prole-db-${serial}-wal" -n "$NS" --ignore-not-found 2>/dev/null || true + kubectl delete job -n "$NS" -l "cnpg.io/instanceName=prole-db-${serial}" --ignore-not-found 2>/dev/null || true +done +echo "Cleaned up stale PVCs and jobs" + +echo "=== Clearing Released PV claimRefs ===" +for pv in synology-iscsi-d002-data synology-iscsi-d002-wal; do + kubectl patch pv "$pv" --type=json \ + -p '[{"op":"remove","path":"/spec/claimRef"}]' 2>/dev/null || \ + kubectl patch pv "$pv" --type=merge \ + -p '{"spec":{"claimRef":{"name":"","uid":"","namespace":""}}}' 2>/dev/null || true + echo " Patched $pv" +done + +echo "=== Waiting for d002 PVs to become Available ===" +for pv in synology-iscsi-d002-data synology-iscsi-d002-wal; do + for i in $(seq 1 30); do + phase=$(kubectl get pv "$pv" -o jsonpath='{.status.phase}' 2>/dev/null) + echo " $pv: $phase" + if [[ "$phase" == "Available" ]]; then break; fi + sleep 3 + done +done + +echo "=== Getting CNPG PVC labels from existing prole-db-3 PVC ===" +CNPG_LABELS=$(kubectl get pvc prole-db-3 -n "$NS" -o jsonpath='{.metadata.labels}' 2>/dev/null || echo "{}") +echo " Labels: $CNPG_LABELS" + +# Determine next serial CNPG will use (latestGeneratedNode + 1) +NEXT_SERIAL=$(kubectl get cluster prole-db -n "$NS" -o jsonpath='{.status.latestGeneratedNode}' 2>/dev/null || echo "4") +NEXT_SERIAL=$((NEXT_SERIAL + 1)) +echo "=== Pre-binding prole-db-${NEXT_SERIAL} PVCs to d002 (merlin) ===" + +kubectl create -f - </dev/null) + echo " $pvc: $phase" + if [[ "$phase" == "Bound" ]]; then break; fi + sleep 3 + done +done + +echo "=== Resuming CNPG controller ===" +kubectl scale deployment cnpg-controller-manager -n cnpg-system --replicas=1 +echo "Done — CNPG will create prole-db-${NEXT_SERIAL}-join targeting merlin.prole.org" +sleep 15 +kubectl get jobs,pods -n "$NS" -o wide | grep -v "Completed\|initdb" diff --git a/_import_pi.sh b/_import_pi.sh new file mode 100644 index 0000000..167067d --- /dev/null +++ b/_import_pi.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +echo "Importing knoe-db:18-140 to pi.prole.org..." +docker save myrddin.prole.org:5000/knoe-db:18-140 \ + | ssh -o StrictHostKeyChecking=no -o BatchMode=yes \ + -o ServerAliveInterval=30 -o ServerAliveCountMax=20 \ + -i ~/.ssh/id_ed25519_ansible ansible@pi.prole.org \ + "sudo k3s ctr images import -" 2>&1 | tail -5 +echo "Done importing to pi." +kubectl get jobs,pods -n prole-db -o wide 2>/dev/null | grep -v "Completed\|initdb" diff --git a/_monitor_run.sh b/_monitor_run.sh new file mode 100644 index 0000000..124bb0d --- /dev/null +++ b/_monitor_run.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +for i in $(seq 1 90); do + sleep 10 + if ! pgrep -f "prole.sh.*service/prole.cfg" >/dev/null 2>&1; then + break + fi +done +tail -60 /tmp/prole_run.log diff --git a/_patch_cluster.sh b/_patch_cluster.sh new file mode 100644 index 0000000..bd6c4c7 --- /dev/null +++ b/_patch_cluster.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -e +cp k8s/prole/prole-db.yaml deploy/opentofu/k3s/manifests/prole/prole-db.yaml +echo "Mirrored prole-db.yaml to opentofu" +kubectl patch cluster prole-db -n prole-db --type=merge -p \ + '{"spec":{"imageName":"myrddin.prole.org:5000/knoe-db:18-140","affinity":{"podAntiAffinityType":"preferred","nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"node.kubernetes.io/instance-type","operator":"In","values":["k3s"]}]}]}}}}}' +echo "Patched live cluster" +sleep 5 +kubectl get pods -n prole-db -o wide diff --git a/_prebind_pvcs.sh b/_prebind_pvcs.sh new file mode 100644 index 0000000..199bd69 --- /dev/null +++ b/_prebind_pvcs.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# Pre-bind CNPG PVCs to specific Synology iSCSI PVs so CNPG schedules replicas +# on the correct nodes (merlin=d002, pi=d003) rather than defaulting to myrddin. +set -euo pipefail +NS=prole-db + +# Delete any pending (unbound) PVCs for replicas 2 and 3 +for pvc in prole-db-2 prole-db-2-wal prole-db-3 prole-db-3-wal; do + phase=$(kubectl get pvc "$pvc" -n "$NS" -o jsonpath='{.status.phase}' 2>/dev/null || true) + if [[ "$phase" == "Pending" || -z "$phase" ]]; then + kubectl delete pvc "$pvc" -n "$NS" --ignore-not-found + echo "Deleted pending PVC: $pvc" + fi +done + +# Pre-bind replica-2 PVCs → synology-iscsi-d002 on merlin.prole.org +kubectl apply -f - <<'YAML' +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prole-db-2 + namespace: prole-db +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 29Gi + storageClassName: synology-iscsi + volumeName: synology-iscsi-d002-data +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prole-db-2-wal + namespace: prole-db +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 29Gi + storageClassName: synology-iscsi + volumeName: synology-iscsi-d002-wal +YAML + +# Pre-bind replica-3 PVCs → synology-iscsi-d003 on pi.prole.org +kubectl apply -f - <<'YAML' +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prole-db-3 + namespace: prole-db +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 29Gi + storageClassName: synology-iscsi + volumeName: synology-iscsi-d003-data +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prole-db-3-wal + namespace: prole-db +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 29Gi + storageClassName: synology-iscsi + volumeName: synology-iscsi-d003-wal +YAML + +echo "Waiting for PVCs to bind..." +sleep 5 +kubectl get pvc -n "$NS" + +# Delete the stuck pod so CNPG recreates it with correct affinity +kubectl delete pod -n "$NS" -l cnpg.io/instanceName=prole-db-2 --ignore-not-found +echo "Done — CNPG will recreate replica-2 pod targeting merlin.prole.org" diff --git a/_rebind_d002.sh b/_rebind_d002.sh new file mode 100644 index 0000000..b3e62f5 --- /dev/null +++ b/_rebind_d002.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +set -euo pipefail +NS=prole-db + +# Release d002 PVs by deleting old prole-db-2 PVCs +kubectl delete pvc prole-db-2 prole-db-2-wal -n "$NS" --ignore-not-found +echo "Deleted prole-db-2 PVCs" + +# Wait for PVs to become Available again +for pv in synology-iscsi-d002-data synology-iscsi-d002-wal; do + for i in $(seq 1 20); do + phase=$(kubectl get pv "$pv" -o jsonpath='{.status.phase}' 2>/dev/null || echo "") + if [[ "$phase" == "Available" ]]; then + echo " PV $pv is Available" + break + fi + sleep 2 + done +done + +# Pre-bind prole-db-4 PVCs to d002 PVs on merlin +kubectl apply -f - <<'YAML' +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prole-db-4 + namespace: prole-db +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 29Gi + storageClassName: synology-iscsi + volumeName: synology-iscsi-d002-data +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prole-db-4-wal + namespace: prole-db +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 29Gi + storageClassName: synology-iscsi + volumeName: synology-iscsi-d002-wal +YAML +echo "Created prole-db-4 PVCs pre-bound to d002 (merlin)" + +# Delete the stuck prole-db-4-join Job so CNPG recreates it reading the new PVCs +kubectl delete job -n "$NS" -l cnpg.io/instanceName=prole-db-4 --ignore-not-found +echo "Deleted prole-db-4-join Job — CNPG will recreate targeting merlin" + +sleep 10 +kubectl get pvc,pods -n "$NS" | grep -v "Completed\|initdb" diff --git a/_rebind_d002b.sh b/_rebind_d002b.sh new file mode 100644 index 0000000..c32b23a --- /dev/null +++ b/_rebind_d002b.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail +NS=prole-db + +# Delete the job first, then the PVCs CNPG created with wrong volumeName +kubectl delete job -n "$NS" -l cnpg.io/instanceName=prole-db-4 --ignore-not-found +kubectl delete pvc prole-db-4 prole-db-4-wal -n "$NS" --ignore-not-found +echo "Deleted prole-db-4 job and PVCs" + +# Wait for d002 PVs to become Available +for pv in synology-iscsi-d002-data synology-iscsi-d002-wal; do + for i in $(seq 1 30); do + phase=$(kubectl get pv "$pv" -o jsonpath='{.status.phase}' 2>/dev/null || echo "") + echo " $pv: $phase" + if [[ "$phase" == "Available" ]]; then break; fi + sleep 2 + done +done + +# Pre-bind with 1Gi (matching CNPG cluster spec size) +kubectl create -f - <<'YAML' +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prole-db-4 + namespace: prole-db +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 1Gi + storageClassName: synology-iscsi + volumeName: synology-iscsi-d002-data +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: prole-db-4-wal + namespace: prole-db +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 1Gi + storageClassName: synology-iscsi + volumeName: synology-iscsi-d002-wal +YAML +echo "Created prole-db-4 PVCs pre-bound to d002 (merlin)" +sleep 8 +kubectl get pvc -n "$NS" diff --git a/conf/database_versions.json b/conf/database_versions.json new file mode 100644 index 0000000..cd23553 --- /dev/null +++ b/conf/database_versions.json @@ -0,0 +1,13 @@ +{ + "postgresql": { + "stable": "16", + "current": "17", + "latest": "18" + }, + "percona": { + "stable": "16", + "current": "17", + "latest": "18", + "v18": "18" + } +} diff --git a/conf/dev/prole.cfg b/conf/dev/prole.cfg index 8b4463a..34044b7 100644 --- a/conf/dev/prole.cfg +++ b/conf/dev/prole.cfg @@ -1,5 +1,5 @@ ; Prole Master Configuration File -; Generated by install.py on 2026-03-22 05:28:56 +; Generated by install.py on 2026-03-22 21:34:24 ; This file is used as input for Ansible deployment and k8s cluster creation. [User] @@ -36,7 +36,7 @@ dependencies.opentofu.install = true dependencies.python.install = true dependencies.verify_all = false disk_selection.disk_type = local -disk_selection.local_path = /Users/chrisfu +disk_selection.local_path = /Users/chrisfu/dev/prole/prole-tools-app/dist disk_selection.removable_mount = env_setup.NAMESPACE = ${NAMESPACE} env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf @@ -49,9 +49,9 @@ init_cluster.at_rest_encryption_enabled = true init_cluster.cluster_env = dev init_cluster.deployment_target = prole-dev-cluster init_cluster.gitops_enabled = false -init_cluster.k3s_server_url = -init_cluster.k3s_token = -init_cluster.kerberos_enabled = false +init_cluster.k3s_server_url = https://myrddin.prole.org:6443 +init_cluster.k3s_token = ${PROLE_SECRET:v1:zaODrWdgRwk4Mn5f:5l3tyJapMmolvCe1YR8LpyfZZm_E4mApbEDdTwYdbIZ_hotg6TV6e19P60a8JAV_3CIKqgNBhGJ4wp_LY3o-5P-BJS8zs118bcPi3doRYItAQfRI9mPp0myMtKRb01xO7iWMY__t9M09m_tPsKd3LN_sobbpNNQRwxeY2w==} +init_cluster.kerberos_enabled = true init_cluster.mode = k3d init_cluster.start_cluster = true init_cluster.supabase_enabled = false @@ -60,35 +60,35 @@ init_cnpg_deploy.run_deploy = true init_db_build.run_build = true init_password.db_host_port = 5432 init_password.db_namespace = ${NAMESPACE} -init_password.db_password = ${PROLE_SECRET:v1:Ug4MMDdbizuLhRxp:7_zShAVRIyTwzI230RPi0DZkaYyec25z} -init_password.db_password_confirm = ${PROLE_SECRET:v1:Ug4MMDdbizuLhRxp:7_zShAVRIyTwzI230RPi0DZkaYyec25z} +init_password.db_password = ${PROLE_SECRET:v1:vb6MFP3JQ8ZA-kCH:P09DQs-Tf_unwlEZGjQiQug9imzKxk7z} +init_password.db_password_confirm = ${PROLE_SECRET:v1:vb6MFP3JQ8ZA-kCH:P09DQs-Tf_unwlEZGjQiQug9imzKxk7z} init_password.db_username = root init_password.generate_ssh_key = true init_scripts.run_scripts = true -kerberos_config.enabled = false -kerberos_config.kdc = -kerberos_config.password = ${PROLE_SECRET:v1:HY4WS78rVJaooM8W:JXfyvQkXR-Pg3Hg5FCItCMNfTIsXlW_mNDTEHElowhJ9zv2z} -kerberos_config.realm = +kerberos_config.enabled = true +kerberos_config.kdc = 10.0.0.3 +kerberos_config.password = ${PROLE_SECRET:v1:vYQE4jE-zNwvWosG:LB5ajSn0j84cLnbtg6x8o-jjIwdZh5ELNW1h1pp012xDcy7R} +kerberos_config.realm = PROLE.ORG kerberos_config.test_connection = false kerberos_config.user = administrator network_scan.run = true ollama_config.model = -ollama_config.server_host = +ollama_config.server_host = fairyland.prole.org ollama_config.server_port = 11434 [Global] ; Variables used by name in more than one place or assumed global scope CLUSTER_ENV = dev DB_HOST_PORT = 5432 -DB_PASSWORD = ${PROLE_SECRET:v1:UfmNthRE-DSKbvW2:F64wGEHyh2iCF0MbYgKOUU_y4PQnzMhk} +DB_PASSWORD = ${PROLE_SECRET:v1:RdWFdqimNbYIof7M:rIB4trg7FLDfUWzYoXLFsU-1BUywEDE4} DEPLOYMENT_MODE = k3d DEPLOYMENT_TARGET = prole-dev-cluster DOCKER_PRELOAD = false NAMESPACE = knoe-db OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2 PROLE_DB_USER = root -PROLE_K3S_SERVER = -PROLE_K3S_TOKEN = +PROLE_K3S_SERVER = https://myrddin.prole.org:6443 +PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:zaODrWdgRwk4Mn5f:5l3tyJapMmolvCe1YR8LpyfZZm_E4mApbEDdTwYdbIZ_hotg6TV6e19P60a8JAV_3CIKqgNBhGJ4wp_LY3o-5P-BJS8zs118bcPi3doRYItAQfRI9mPp0myMtKRb01xO7iWMY__t9M09m_tPsKd3LN_sobbpNNQRwxeY2w==} PROLE_OPENTOFU_URL = http://127.0.0.1:8080 SERVICE_NAMESPACE = knoe-system @@ -107,12 +107,18 @@ ANSIBLE_INVENTORY = /Users/chrisfu/dev/prole/infrastructure/inventory ANSIBLE_REALM = PROLE.ORG ANSIBLE_TOPOLOGY = {"domain":"prole.org","realm":"PROLE.ORG","internal_records":{"aventage.prole.org":"10.0.0.206","fairyland.prole.org":"10.0.0.208","loghost.prole.org":"10.0.0.3","merlin.prole.org":"10.0.0.6","morana.prole.org":"10.0.0.66","morgoth.prole.org":"10.0.0.204","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207","synology.prole.org":"10.0.0.203","zinfandel.prole.org":"10.0.0.205"},"ad_dc":{"host":"myrddin.prole.org","ip":"10.0.0.3"},"k3s":{"server_url":"https://myrddin.prole.org:6443","server_host":"myrddin.prole.org","token_present":true},"groups":{"iscsi":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_servers":["myrddin.prole.org"],"k3s_agents":["pi.prole.org","merlin.prole.org"],"k3s_hosts:children":["k3s_servers","k3s_agents"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org"],"ssl_hosts":["myrddin.prole.org"],"mariadb_primary":["merlin.prole.org"],"mariadb_replica":["raspberry.prole.org"],"mariadb:children":["mariadb_primary","mariadb_replica"],"merlin_bootstrap":["merlin"]},"hosts":{"merlin":"10.0.0.6","merlin.prole.org":"10.0.0.6","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207"},"unmapped_hosts":["k3s_agents","k3s_servers","mariadb_primary","mariadb_replica"]} KDC_ANSIBLE_DETECTED = 10.0.0.3 +KDC_AUTO_DETECTED = 10.0.0.3 +KERBEROS_AUTO_ENABLED = True [Port Forwards] ; No configuration values captured yet for this section. [System Environment] -; No configuration values captured yet for this section. +PROLE_CONF = /Users/chrisfu/dev/prole/conf +PROLE_DATA = /Users/chrisfu/dev/prole/data +PROLE_HOME = /Users/chrisfu/dev/prole +PROLE_LOGS = /Users/chrisfu/dev/prole/logs +PROLE_SERVICE = /Users/chrisfu/dev/prole/etc [Monitoring] ; No configuration values captured yet for this section. @@ -121,10 +127,16 @@ KDC_ANSIBLE_DETECTED = 10.0.0.3 ; No configuration values captured yet for this section. [Ollama] -; No configuration values captured yet for this section. +OLLAMA_HOST = http://fairyland.prole.org:11434 +OLLAMA_SERVERS = fairyland.prole.org:11434,k3d.localhost:11434,morgoth.prole.org:11434 +OLLAMA_SERVER_HOST = fairyland.prole.org +OLLAMA_SERVER_PORT = 11434 [Optional Features] +AT_REST_ENCRYPTION_ENABLED = True GITOPS_ENABLED = False +KERBEROS_ENABLED = True +SUPABASE_ENABLED = False [GitOps] ; No configuration values captured yet for this section. @@ -133,7 +145,9 @@ GITOPS_ENABLED = False ; No configuration values captured yet for this section. [Initialize Cluster] -; No configuration values captured yet for this section. +ENVIRONMENT = dev +K3S_SERVER_URL = https://myrddin.prole.org:6443 +K3S_TOKEN = ${PROLE_SECRET:v1:zaODrWdgRwk4Mn5f:5l3tyJapMmolvCe1YR8LpyfZZm_E4mApbEDdTwYdbIZ_hotg6TV6e19P60a8JAV_3CIKqgNBhGJ4wp_LY3o-5P-BJS8zs118bcPi3doRYItAQfRI9mPp0myMtKRb01xO7iWMY__t9M09m_tPsKd3LN_sobbpNNQRwxeY2w==} [Dev Cluster (k3d)] CLUSTER_ENV = dev @@ -144,20 +158,21 @@ MODE = k3d [Service Cluster (k3s)] CLUSTER_ENV = prole-service-cluster DISPLAY_NAME = prole-service-cluster -K3S_SERVER_URL = -K3S_TOKEN = +K3S_SERVER_URL = https://myrddin.prole.org:6443 +K3S_TOKEN = ${PROLE_SECRET:v1:zaODrWdgRwk4Mn5f:5l3tyJapMmolvCe1YR8LpyfZZm_E4mApbEDdTwYdbIZ_hotg6TV6e19P60a8JAV_3CIKqgNBhGJ4wp_LY3o-5P-BJS8zs118bcPi3doRYItAQfRI9mPp0myMtKRb01xO7iWMY__t9M09m_tPsKd3LN_sobbpNNQRwxeY2w==} MODE = k3s PIPELINE_URL = http://127.0.0.1:8080 [Prod Cluster (k8s)] -ARTIFACTS_DIR = /private/var/folders/rt/pywlnmxj3dn7t5552vwdcpp80000gn/T/pytest-of-chrisfu/pytest-54/test_encryption_toggle0/data/staging +ARTIFACTS_DIR = /Users/chrisfu/dev/prole/data/staging CLUSTER_ENV = prole-prod-cluster DISPLAY_NAME = prole-prod-cluster MODE = k8s PIPELINE_URL = http://127.0.0.1:8080 [Docker Build] -; No configuration values captured yet for this section. +LOCAL_REGISTRY = localhost:5000 +LOCAL_REGISTRY_INTERNAL = k3d-prole-registry.localhost:5000 [Initialization Scripts] ; No configuration values captured yet for this section. diff --git a/conf/service/prole.cfg b/conf/service/prole.cfg deleted file mode 100644 index a773f42..0000000 --- a/conf/service/prole.cfg +++ /dev/null @@ -1,180 +0,0 @@ -; Prole Master Configuration File -; Generated by install.py on 2026-03-21 13:37:28 -; This file is used as input for Ansible deployment and k8s cluster creation. - -[User] -; User-editable values; derived values below reference these by default. -; No user values captured yet for this section. - -[Inputs] -; Screen-scoped inputs used for unattended replays (-S) -build.deploy_env = Dev -build.run_build = false -database_options.distribution = percona -database_options.ext.dblink = true -database_options.ext.pg_buffercache = true -database_options.ext.pg_freespacemap = true -database_options.ext.pg_repack = true -database_options.ext.pg_stat_monitor = true -database_options.ext.pg_stat_statements = true -database_options.ext.pgaudit = true -database_options.ext.pgbadger = true -database_options.ext.pgcrypto = true -database_options.ext.pgrowlocks = true -database_options.ext.pgvector = true -database_options.ext.postgis = true -database_options.ext.postgres_fdw = true -database_options.version_type = v18 -dependencies.ansible.install = true -dependencies.auto_install_missing = true -dependencies.brew.install = true -dependencies.docker.install = true -dependencies.k3d.install = true -dependencies.kubectl.install = true -dependencies.kubectx.install = true -dependencies.opentofu.install = true -dependencies.python.install = true -dependencies.verify_all = false -disk_selection.disk_type = local -disk_selection.local_path = /Users/chrisfu/dev/prole/prole-tools-app/dist -disk_selection.removable_mount = -env_setup.NAMESPACE = ${NAMESPACE} -env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf -env_setup.PROLE_DATA = /Users/chrisfu/dev/prole/data -env_setup.PROLE_HOME = /Users/chrisfu/dev/prole -env_setup.PROLE_LOGS = /Users/chrisfu/dev/prole/logs -env_setup.PROLE_SERVICE = /Users/chrisfu/dev/prole/etc -init_cluster.argocd_enabled = false -init_cluster.at_rest_encryption_enabled = true -init_cluster.cluster_env = service -init_cluster.deployment_target = prole-service-cluster -init_cluster.gitops_enabled = false -init_cluster.k3s_server_url = https://myrddin.prole.org:6443 -init_cluster.k3s_token = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==} -init_cluster.kerberos_enabled = true -init_cluster.mode = k3s -init_cluster.start_cluster = true -init_cluster.supabase_enabled = false -init_cnpg_deploy.force_rollout = false -init_cnpg_deploy.run_deploy = true -init_db_build.run_build = true -init_password.db_host_port = 5432 -init_password.db_namespace = ${NAMESPACE} -init_password.db_password = -init_password.db_password_confirm = -init_password.db_username = root -init_password.generate_ssh_key = true -init_scripts.run_scripts = true -kerberos_config.enabled = true -kerberos_config.kdc = 10.0.0.3 -kerberos_config.password = ${PROLE_SECRET:v1:71ROfYyYysQDJDpX:0K4cB3p9nnMb1yi2OImqUm-pa9Zg21JYTmYHzYB_7czTKMQJ} -kerberos_config.realm = PROLE.ORG -kerberos_config.test_connection = false -kerberos_config.user = administrator -network_scan.run = true -ollama_config.model = -ollama_config.server_host = fairyland.prole.org -ollama_config.server_port = 11434 - -[Global] -; Variables used by name in more than one place or assumed global scope -CLUSTER_ENV = service -DB_HOST_PORT = 5432 -DB_PASSWORD = -DEPLOYMENT_MODE = k3s -DEPLOYMENT_TARGET = prole-service-cluster -DOCKER_PRELOAD = false -NAMESPACE = prole-db -OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2 -PROLE_DB_USER = root -PROLE_HOME = $HOME/dev/prole -PROLE_K3S_SERVER = https://myrddin.prole.org:6443 -PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==} -PROLE_OPENTOFU_URL = http://127.0.0.1:8080 -SERVICE_NAMESPACE = knoe-system - -[Welcome] -; No configuration values captured yet for this section. - -[Dependencies] -; No configuration values captured yet for this section. - -[Network] -AD_DC_HOST = myrddin.prole.org -AD_DC_IP = 10.0.0.3 -ANSIBLE_DOMAIN = prole.org -ANSIBLE_INFRASTRUCTURE = /Users/chrisfu/dev/prole/infrastructure -ANSIBLE_INVENTORY = /Users/chrisfu/dev/prole/infrastructure/inventory -ANSIBLE_REALM = PROLE.ORG -ANSIBLE_TOPOLOGY = {"domain":"prole.org","realm":"PROLE.ORG","internal_records":{"aventage.prole.org":"10.0.0.206","fairyland.prole.org":"10.0.0.208","loghost.prole.org":"10.0.0.3","merlin.prole.org":"10.0.0.6","morana.prole.org":"10.0.0.66","morgoth.prole.org":"10.0.0.204","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207","synology.prole.org":"10.0.0.203","zinfandel.prole.org":"10.0.0.205"},"ad_dc":{"host":"myrddin.prole.org","ip":"10.0.0.3"},"k3s":{"server_url":"https://myrddin.prole.org:6443","server_host":"myrddin.prole.org","token_present":true},"groups":{"iscsi":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_servers":["myrddin.prole.org"],"k3s_agents":["pi.prole.org","merlin.prole.org"],"k3s_hosts:children":["k3s_servers","k3s_agents"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org"],"ssl_hosts":["myrddin.prole.org"],"mariadb_primary":["merlin.prole.org"],"mariadb_replica":["raspberry.prole.org"],"mariadb:children":["mariadb_primary","mariadb_replica"],"merlin_bootstrap":["merlin"]},"hosts":{"merlin":"10.0.0.6","merlin.prole.org":"10.0.0.6","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207"},"unmapped_hosts":["k3s_agents","k3s_servers","mariadb_primary","mariadb_replica"]} -KDC_ANSIBLE_DETECTED = 10.0.0.3 -KDC_AUTO_DETECTED = 10.0.0.3 -KERBEROS_AUTO_ENABLED = True - -[Port Forwards] -; No configuration values captured yet for this section. - -[System Environment] -PROLE_CONF = /Users/chrisfu/dev/prole/conf -PROLE_DATA = /Users/chrisfu/dev/prole/data -PROLE_HOME = /Users/chrisfu/dev/prole -PROLE_LOGS = /Users/chrisfu/dev/prole/logs -PROLE_SERVICE = /Users/chrisfu/dev/prole/etc - -[Monitoring] -; No configuration values captured yet for this section. - -[Kerberos Authentication] -; No configuration values captured yet for this section. - -[Ollama] -OLLAMA_HOST = http://fairyland.prole.org:11434 -OLLAMA_SERVERS = fairyland.prole.org:11434,k3d.localhost:11434,morgoth.prole.org:11434 -OLLAMA_SERVER_HOST = fairyland.prole.org -OLLAMA_SERVER_PORT = 11434 - -[Optional Features] -GITOPS_ENABLED = False - -[GitOps] -; No configuration values captured yet for this section. - -[Database Creation] -; No configuration values captured yet for this section. - -[Initialize Cluster] -; No configuration values captured yet for this section. - -[Dev Cluster (k3d)] -CLUSTER_ENV = dev -DISPLAY_NAME = knoe-dev-cluster -KUBECTL_CONTEXT = -MODE = k3d - -[Service Cluster (k3s)] -CLUSTER_ENV = prole-service-cluster -DISPLAY_NAME = prole-service-cluster -K3S_SERVER_URL = https://myrddin.prole.org:6443 -K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==} -MODE = k3s -PIPELINE_URL = http://127.0.0.1:8080 - -[Prod Cluster (k8s)] -ARTIFACTS_DIR = /Users/chrisfu/dev/prole/data/staging -CLUSTER_ENV = prole-prod-cluster -DISPLAY_NAME = prole-prod-cluster -MODE = k8s -PIPELINE_URL = http://127.0.0.1:8080 - -[Docker Build] -; No configuration values captured yet for this section. - -[Initialization Scripts] -; No configuration values captured yet for this section. - -[Deployment] -MODE = k3s -TARGET = prole-service-cluster - -[Install] -; No configuration values captured yet for this section. diff --git a/conf/service/prole.cfg b/conf/service/prole.cfg new file mode 120000 index 0000000..ad5623b --- /dev/null +++ b/conf/service/prole.cfg @@ -0,0 +1 @@ +service/prole.cfg \ No newline at end of file diff --git a/conf/service/prole.cfg.legacy.1774223813 b/conf/service/prole.cfg.legacy.1774223813 new file mode 100644 index 0000000..5b812b5 --- /dev/null +++ b/conf/service/prole.cfg.legacy.1774223813 @@ -0,0 +1,195 @@ +; Prole Master Configuration File +; Generated by install.py on 2026-03-22 16:46:41 +; This file is used as input for Ansible deployment and k8s cluster creation. + +[User] +; User-editable values; derived values below reference these by default. +; No user values captured yet for this section. + +[Inputs] +; Screen-scoped inputs used for unattended replays (-S) +build.deploy_env = Dev +build.run_build = false +database_options.distribution = percona +database_options.ext.dblink = true +database_options.ext.pg_buffercache = true +database_options.ext.pg_freespacemap = true +database_options.ext.pg_repack = true +database_options.ext.pg_stat_monitor = true +database_options.ext.pg_stat_statements = true +database_options.ext.pgaudit = true +database_options.ext.pgbadger = true +database_options.ext.pgcrypto = true +database_options.ext.pgrowlocks = true +database_options.ext.pgvector = true +database_options.ext.postgis = true +database_options.ext.postgres_fdw = true +database_options.version_type = v18 +dependencies.ansible.install = true +dependencies.auto_install_missing = true +dependencies.brew.install = true +dependencies.docker.install = true +dependencies.k3d.install = true +dependencies.kubectl.install = true +dependencies.kubectx.install = true +dependencies.opentofu.install = true +dependencies.python.install = true +dependencies.verify_all = false +disk_selection.disk_type = local +disk_selection.local_path = /Users/chrisfu/dev/prole/prole-tools-app/dist +disk_selection.removable_mount = +env_setup.NAMESPACE = ${NAMESPACE} +env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf +env_setup.PROLE_DATA = /Users/chrisfu/dev/prole/data +env_setup.PROLE_HOME = /Users/chrisfu/dev/prole +env_setup.PROLE_LOGS = /Users/chrisfu/dev/prole/logs +env_setup.PROLE_SERVICE = /Users/chrisfu/dev/prole/etc +init_cluster.argocd_enabled = false +init_cluster.at_rest_encryption_enabled = true +init_cluster.cluster_env = service +init_cluster.deployment_target = prole-service-cluster +init_cluster.gitops_enabled = false +init_cluster.k3s_server_url = https://myrddin.prole.org:6443 +init_cluster.k3s_token = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==} +init_cluster.kerberos_enabled = true +init_cluster.mode = k3s +init_cluster.start_cluster = true +init_cluster.supabase_enabled = false +init_cnpg_deploy.force_rollout = false +init_cnpg_deploy.run_deploy = true +init_db_build.run_build = true +init_password.db_host_port = 5432 +init_password.db_namespace = ${NAMESPACE} +init_password.db_password = ${PROLE_SECRET:v1:LtY6hRqgZOeWP8OX:wWt4cT0CS2c4GZPB1RpSAo9cIFhLYvhw} +init_password.db_password_confirm = ${PROLE_SECRET:v1:LtY6hRqgZOeWP8OX:wWt4cT0CS2c4GZPB1RpSAo9cIFhLYvhw} +init_password.db_username = root +init_password.generate_ssh_key = true +init_scripts.run_scripts = true +kerberos_config.enabled = true +kerberos_config.init_authority = false +kerberos_config.kdc = 10.0.0.3 +kerberos_config.password = ${PROLE_SECRET:v1:71ROfYyYysQDJDpX:0K4cB3p9nnMb1yi2OImqUm-pa9Zg21JYTmYHzYB_7czTKMQJ} +kerberos_config.realm = PROLE.ORG +kerberos_config.test_connection = false +kerberos_config.user = administrator +network_scan.run = true +ollama_config.model = +ollama_config.server_host = fairyland.prole.org +ollama_config.server_port = 11434 + +[Global] +; Variables used by name in more than one place or assumed global scope +ARGOCD_NAMESPACE = argocd +CLUSTER_ENV = service +DB_HOST_PORT = 5432 +DB_PASSWORD = ${PROLE_SECRET:v1:NmgNZUhCsk-p0_DF:VB1tKREj45eEWcnN1GAlcGV6b_Lrl9sx} +DEPLOYMENT_MODE = k3s +DEPLOYMENT_TARGET = prole-service-cluster +DOCKER_IMPORT_DIR = +DOCKER_PRELOAD = false +NAMESPACE = prole-db +OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2 +PROLE_DB_USER = root +PROLE_HOME = /Users/chrisfu/dev/prole +PROLE_K3S_SERVER = https://myrddin.prole.org:6443 +PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==} +PROLE_OPENTOFU_URL = http://127.0.0.1:8080 +REGISTRY_NAMESPACE = knoe-system +SERVICE_NAMESPACE = knoe-system + +[Welcome] +; No configuration values captured yet for this section. + +[Dependencies] +STATUS = All installed + +[Network] +AD_DC_HOST = myrddin.prole.org +AD_DC_IP = 10.0.0.3 +ANSIBLE_DOMAIN = prole.org +ANSIBLE_INFRASTRUCTURE = /Users/chrisfu/dev/prole/infrastructure +ANSIBLE_INVENTORY = /Users/chrisfu/dev/prole/infrastructure/inventory +ANSIBLE_REALM = PROLE.ORG +ANSIBLE_TOPOLOGY = {"domain":"prole.org","realm":"PROLE.ORG","internal_records":{"aventage.prole.org":"10.0.0.206","fairyland.prole.org":"10.0.0.208","loghost.prole.org":"10.0.0.3","merlin.prole.org":"10.0.0.6","morana.prole.org":"10.0.0.66","morgoth.prole.org":"10.0.0.204","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207","synology.prole.org":"10.0.0.203","zinfandel.prole.org":"10.0.0.205"},"ad_dc":{"host":"myrddin.prole.org","ip":"10.0.0.3"},"k3s":{"server_url":"https://myrddin.prole.org:6443","server_host":"myrddin.prole.org","token_present":true},"groups":{"iscsi":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_servers":["myrddin.prole.org"],"k3s_agents":["pi.prole.org","merlin.prole.org"],"k3s_hosts:children":["k3s_servers","k3s_agents"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org"],"ssl_hosts":["myrddin.prole.org"],"mariadb_primary":["merlin.prole.org"],"mariadb_replica":["raspberry.prole.org"],"mariadb:children":["mariadb_primary","mariadb_replica"],"merlin_bootstrap":["merlin"]},"hosts":{"merlin":"10.0.0.6","merlin.prole.org":"10.0.0.6","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207"},"unmapped_hosts":["k3s_agents","k3s_servers","mariadb_primary","mariadb_replica"]} +KDC_ANSIBLE_DETECTED = 10.0.0.3 +KDC_AUTO_DETECTED = 10.0.0.3 +KERBEROS_AUTO_ENABLED = True + +[Port Forwards] +PORT_FORWARD_K3S_MAPPING_1 = id=argocd;namespace=argocd;target=svc/argocd-server;address=0.0.0.0;hostPort=8081;servicePort=80;protocol=TCP;description=ArgoCD +PORT_FORWARD_K3S_MAPPING_2 = id=garage;namespace=knoe-system;target=svc/garage;address=0.0.0.0;hostPort=3900;servicePort=3900;protocol=TCP;description=Garage S3 +PORT_FORWARD_K3S_MAPPING_3 = id=openbao;namespace=knoe-system;target=svc/openbao;address=0.0.0.0;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenBao +PORT_FORWARD_K3S_MAPPING_4 = id=opentofu;namespace=knoe-system;target=svc/opentofu;address=0.0.0.0;hostPort=8080;servicePort=8080;protocol=TCP;description=OpenTofu +PORT_FORWARD_K3S_MAPPING_5 = id=dashboard;namespace=kubernetes-dashboard;target=svc/kubernetes-dashboard-kong-proxy;address=127.0.0.1;hostPort=8443;servicePort=443;protocol=TCP;description=Kubernetes Dashboard +PORT_FORWARD_K3S_MAPPING_6 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL (primary) +PORT_FORWARD_K3S_MAPPING_7 = id=prometheus;namespace=monitoring;target=svc/kps-kube-prometheus-stack-prometheus;address=127.0.0.1;hostPort=9090;servicePort=9090;protocol=TCP;description=Prometheus UI +PORT_FORWARD_K3S_MAPPING_8 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=80;protocol=TCP;description=Grafana UI + +[System Environment] +NAMESPACE = prole-db +PROLE_CONF = /Users/chrisfu/dev/prole/conf +PROLE_DATA = /Users/chrisfu/dev/prole/data +PROLE_HOME = /Users/chrisfu/dev/prole +PROLE_LOGS = /Users/chrisfu/dev/prole/logs +PROLE_SERVICE = /Users/chrisfu/dev/prole/etc + +[Monitoring] +; No configuration values captured yet for this section. + +[Kerberos Authentication] +; No configuration values captured yet for this section. + +[Ollama] +OLLAMA_HOST = http://fairyland.prole.org:11434 +OLLAMA_SERVERS = fairyland.prole.org:11434,k3d.localhost:11434,morgoth.prole.org:11434 +OLLAMA_SERVER_HOST = fairyland.prole.org +OLLAMA_SERVER_PORT = 11434 + +[Optional Features] +AT_REST_ENCRYPTION_ENABLED = true +GITOPS_ENABLED = false +KERBEROS_ENABLED = true +SUPABASE_ENABLED = false + +[GitOps] + +[Database Creation] +DB_NAME = prole-db +DB_USER = root + +[Initialize Cluster] +; No configuration values captured yet for this section. + +[Dev Cluster (k3d)] +CLUSTER_ENV = k3d-knoe-dev-cluster +DISPLAY_NAME = knoe-dev-cluster +KUBECTL_CONTEXT = service +MODE = k3d + +[Service Cluster (k3s)] +CLUSTER_ENV = prole-service-cluster +DISPLAY_NAME = prole-service-cluster +K3S_SERVER_URL = https://myrddin.prole.org:6443 +K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==} +MODE = k3s +PIPELINE_URL = http://127.0.0.1:8080 + +[Prod Cluster (k8s)] +ARTIFACTS_DIR = +CLUSTER_ENV = prole-prod-cluster +DISPLAY_NAME = prole-prod-cluster +MODE = k8s +PIPELINE_URL = http://127.0.0.1:8080 + +[Docker Build] +; No configuration values captured yet for this section. + +[Initialization Scripts] +; No configuration values captured yet for this section. + +[Deployment] +MODE = k3s +TARGET = prole-service-cluster + +[Install] +STATUS = Failed diff --git a/conf/service/service/prole.cfg b/conf/service/service/prole.cfg new file mode 100644 index 0000000..1d0bae9 --- /dev/null +++ b/conf/service/service/prole.cfg @@ -0,0 +1,195 @@ +; Prole Master Configuration File +; Generated by install.py on 2026-03-22 20:46:43 +; This file is used as input for Ansible deployment and k8s cluster creation. + +[User] +; User-editable values; derived values below reference these by default. +; No user values captured yet for this section. + +[Inputs] +; Screen-scoped inputs used for unattended replays (-S) +build.deploy_env = Dev +build.run_build = false +database_options.distribution = percona +database_options.ext.dblink = true +database_options.ext.pg_buffercache = true +database_options.ext.pg_freespacemap = true +database_options.ext.pg_repack = true +database_options.ext.pg_stat_monitor = true +database_options.ext.pg_stat_statements = true +database_options.ext.pgaudit = true +database_options.ext.pgbadger = true +database_options.ext.pgcrypto = true +database_options.ext.pgrowlocks = true +database_options.ext.pgvector = true +database_options.ext.postgis = true +database_options.ext.postgres_fdw = true +database_options.version_type = v18 +dependencies.ansible.install = true +dependencies.auto_install_missing = true +dependencies.brew.install = true +dependencies.docker.install = true +dependencies.k3d.install = true +dependencies.kubectl.install = true +dependencies.kubectx.install = true +dependencies.opentofu.install = true +dependencies.python.install = true +dependencies.verify_all = false +disk_selection.disk_type = local +disk_selection.local_path = /Users/chrisfu/dev/prole/prole-tools-app/dist +disk_selection.removable_mount = +env_setup.NAMESPACE = ${NAMESPACE} +env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf +env_setup.PROLE_DATA = /Users/chrisfu/dev/prole/data +env_setup.PROLE_HOME = /Users/chrisfu/dev/prole +env_setup.PROLE_LOGS = /Users/chrisfu/dev/prole/logs +env_setup.PROLE_SERVICE = /Users/chrisfu/dev/prole/etc +init_cluster.argocd_enabled = false +init_cluster.at_rest_encryption_enabled = true +init_cluster.cluster_env = service +init_cluster.deployment_target = prole-service-cluster +init_cluster.gitops_enabled = false +init_cluster.k3s_server_url = https://myrddin.prole.org:6443 +init_cluster.k3s_token = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==} +init_cluster.kerberos_enabled = true +init_cluster.mode = k3s +init_cluster.start_cluster = true +init_cluster.supabase_enabled = false +init_cnpg_deploy.force_rollout = false +init_cnpg_deploy.run_deploy = true +init_db_build.run_build = true +init_password.db_host_port = 5432 +init_password.db_namespace = ${NAMESPACE} +init_password.db_password = ${PROLE_SECRET:v1:vz4oaR5QA3zIJWDs:blYR6vqpY0e_GqGWFufDwlY4yxXWXrGF} +init_password.db_password_confirm = ${PROLE_SECRET:v1:vz4oaR5QA3zIJWDs:blYR6vqpY0e_GqGWFufDwlY4yxXWXrGF} +init_password.db_username = root +init_password.generate_ssh_key = true +init_scripts.run_scripts = true +kerberos_config.enabled = true +kerberos_config.init_authority = false +kerberos_config.kdc = 10.0.0.3 +kerberos_config.password = ${PROLE_SECRET:v1:71ROfYyYysQDJDpX:0K4cB3p9nnMb1yi2OImqUm-pa9Zg21JYTmYHzYB_7czTKMQJ} +kerberos_config.realm = PROLE.ORG +kerberos_config.test_connection = false +kerberos_config.user = administrator +network_scan.run = true +ollama_config.model = +ollama_config.server_host = fairyland.prole.org +ollama_config.server_port = 11434 + +[Global] +; Variables used by name in more than one place or assumed global scope +ARGOCD_NAMESPACE = argocd +CLUSTER_ENV = service +DB_HOST_PORT = 5432 +DB_PASSWORD = ${PROLE_SECRET:v1:NmgNZUhCsk-p0_DF:VB1tKREj45eEWcnN1GAlcGV6b_Lrl9sx} +DEPLOYMENT_MODE = k3s +DEPLOYMENT_TARGET = prole-service-cluster +DOCKER_IMPORT_DIR = +DOCKER_PRELOAD = false +NAMESPACE = prole-db +OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2 +PROLE_DB_USER = root +PROLE_HOME = /Users/chrisfu/dev/prole +PROLE_K3S_SERVER = https://myrddin.prole.org:6443 +PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==} +PROLE_OPENTOFU_URL = http://myrddin.prole.org:8080 +REGISTRY_NAMESPACE = knoe-system +SERVICE_NAMESPACE = knoe-system + +[Welcome] +; No configuration values captured yet for this section. + +[Dependencies] +STATUS = All installed + +[Network] +AD_DC_HOST = myrddin.prole.org +AD_DC_IP = 10.0.0.3 +ANSIBLE_DOMAIN = prole.org +ANSIBLE_INFRASTRUCTURE = /Users/chrisfu/dev/prole/infrastructure +ANSIBLE_INVENTORY = /Users/chrisfu/dev/prole/infrastructure/inventory +ANSIBLE_REALM = PROLE.ORG +ANSIBLE_TOPOLOGY = {"domain":"prole.org","realm":"PROLE.ORG","internal_records":{"aventage.prole.org":"10.0.0.206","fairyland.prole.org":"10.0.0.208","loghost.prole.org":"10.0.0.3","merlin.prole.org":"10.0.0.6","morana.prole.org":"10.0.0.66","morgoth.prole.org":"10.0.0.204","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207","synology.prole.org":"10.0.0.203","zinfandel.prole.org":"10.0.0.205"},"ad_dc":{"host":"myrddin.prole.org","ip":"10.0.0.3"},"k3s":{"server_url":"https://myrddin.prole.org:6443","server_host":"myrddin.prole.org","token_present":true},"groups":{"iscsi":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_servers":["myrddin.prole.org"],"k3s_agents":["pi.prole.org","merlin.prole.org"],"k3s_hosts:children":["k3s_servers","k3s_agents"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org"],"ssl_hosts":["myrddin.prole.org"],"mariadb_primary":["merlin.prole.org"],"mariadb_replica":["raspberry.prole.org"],"mariadb:children":["mariadb_primary","mariadb_replica"],"merlin_bootstrap":["merlin"]},"hosts":{"merlin":"10.0.0.6","merlin.prole.org":"10.0.0.6","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207"},"unmapped_hosts":["k3s_agents","k3s_servers","mariadb_primary","mariadb_replica"]} +KDC_ANSIBLE_DETECTED = 10.0.0.3 +KDC_AUTO_DETECTED = 10.0.0.3 +KERBEROS_AUTO_ENABLED = True + +[Port Forwards] +PORT_FORWARD_K3S_MAPPING_1 = id=argocd;namespace=argocd;target=svc/argocd-server;address=0.0.0.0;hostPort=8081;servicePort=80;protocol=TCP;description=ArgoCD +PORT_FORWARD_K3S_MAPPING_2 = id=garage;namespace=knoe-system;target=svc/garage;address=0.0.0.0;hostPort=3900;servicePort=3900;protocol=TCP;description=Garage S3 +PORT_FORWARD_K3S_MAPPING_3 = id=openbao;namespace=knoe-system;target=svc/openbao;address=0.0.0.0;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenBao +PORT_FORWARD_K3S_MAPPING_4 = id=opentofu;namespace=knoe-system;target=svc/opentofu;address=0.0.0.0;hostPort=8080;servicePort=8080;protocol=TCP;description=OpenTofu +PORT_FORWARD_K3S_MAPPING_5 = id=dashboard;namespace=kubernetes-dashboard;target=svc/kubernetes-dashboard-kong-proxy;address=127.0.0.1;hostPort=8443;servicePort=443;protocol=TCP;description=Kubernetes Dashboard +PORT_FORWARD_K3S_MAPPING_6 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL (primary) +PORT_FORWARD_K3S_MAPPING_7 = id=prometheus;namespace=monitoring;target=svc/kps-kube-prometheus-stack-prometheus;address=127.0.0.1;hostPort=9090;servicePort=9090;protocol=TCP;description=Prometheus UI +PORT_FORWARD_K3S_MAPPING_8 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=80;protocol=TCP;description=Grafana UI + +[System Environment] +NAMESPACE = prole-db +PROLE_CONF = /Users/chrisfu/dev/prole/conf +PROLE_DATA = /Users/chrisfu/dev/prole/data +PROLE_HOME = /Users/chrisfu/dev/prole +PROLE_LOGS = /Users/chrisfu/dev/prole/logs +PROLE_SERVICE = /Users/chrisfu/dev/prole/etc + +[Monitoring] +; No configuration values captured yet for this section. + +[Kerberos Authentication] +; No configuration values captured yet for this section. + +[Ollama] +OLLAMA_HOST = http://fairyland.prole.org:11434 +OLLAMA_SERVERS = fairyland.prole.org:11434,k3d.localhost:11434,morgoth.prole.org:11434 +OLLAMA_SERVER_HOST = fairyland.prole.org +OLLAMA_SERVER_PORT = 11434 + +[Optional Features] +AT_REST_ENCRYPTION_ENABLED = true +GITOPS_ENABLED = false +KERBEROS_ENABLED = true +SUPABASE_ENABLED = false + +[GitOps] + +[Database Creation] +DB_NAME = prole-db +DB_USER = root + +[Initialize Cluster] +; No configuration values captured yet for this section. + +[Dev Cluster (k3d)] +CLUSTER_ENV = k3d-knoe-dev-cluster +DISPLAY_NAME = knoe-dev-cluster +KUBECTL_CONTEXT = service +MODE = k3d + +[Service Cluster (k3s)] +CLUSTER_ENV = prole-service-cluster +DISPLAY_NAME = prole-service-cluster +K3S_SERVER_URL = https://myrddin.prole.org:6443 +K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==} +MODE = k3s +PIPELINE_URL = http://myrddin.prole.org:8080 + +[Prod Cluster (k8s)] +ARTIFACTS_DIR = +CLUSTER_ENV = prole-prod-cluster +DISPLAY_NAME = prole-prod-cluster +MODE = k8s +PIPELINE_URL = http://myrddin.prole.org:8080 + +[Docker Build] +; No configuration values captured yet for this section. + +[Initialization Scripts] +; No configuration values captured yet for this section. + +[Deployment] +MODE = k3s +TARGET = prole-service-cluster + +[Install] +STATUS = Failed diff --git a/deploy/opentofu/k3s/manifests/prole/garage-statefulset.yaml b/deploy/opentofu/k3s/manifests/prole/garage-statefulset.yaml index 6afa443..7566deb 100644 --- a/deploy/opentofu/k3s/manifests/prole/garage-statefulset.yaml +++ b/deploy/opentofu/k3s/manifests/prole/garage-statefulset.yaml @@ -71,8 +71,8 @@ spec: resources: requests: storage: 29Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: garage - prole.storage/volume: d001 + synology.storage/role: garage + synology.storage/volume: d001 diff --git a/deploy/opentofu/k3s/manifests/prole/iscsi-pvs.yaml b/deploy/opentofu/k3s/manifests/prole/iscsi-pvs.yaml index aaa9bb9..40e00bf 100644 --- a/deploy/opentofu/k3s/manifests/prole/iscsi-pvs.yaml +++ b/deploy/opentofu/k3s/manifests/prole/iscsi-pvs.yaml @@ -1,17 +1,17 @@ apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d001-data + name: synology-iscsi-d001-data labels: - prole.storage/role: data - prole.storage/volume: d001 + synology.storage/role: data + synology.storage/volume: d001 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d001/data @@ -27,17 +27,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d001-wal + name: synology-iscsi-d001-wal labels: - prole.storage/role: wal - prole.storage/volume: d001 + synology.storage/role: wal + synology.storage/volume: d001 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d001/wal @@ -53,17 +53,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d001-garage + name: synology-iscsi-d001-garage labels: - prole.storage/role: garage - prole.storage/volume: d001 + synology.storage/role: garage + synology.storage/volume: d001 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d001/garage @@ -79,17 +79,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d002-data + name: synology-iscsi-d002-data labels: - prole.storage/role: data - prole.storage/volume: d002 + synology.storage/role: data + synology.storage/volume: d002 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d002/data @@ -105,17 +105,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d002-wal + name: synology-iscsi-d002-wal labels: - prole.storage/role: wal - prole.storage/volume: d002 + synology.storage/role: wal + synology.storage/volume: d002 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d002/wal @@ -131,17 +131,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d003-data + name: synology-iscsi-d003-data labels: - prole.storage/role: data - prole.storage/volume: d003 + synology.storage/role: data + synology.storage/volume: d003 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d003/data @@ -157,17 +157,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d003-wal + name: synology-iscsi-d003-wal labels: - prole.storage/role: wal - prole.storage/volume: d003 + synology.storage/role: wal + synology.storage/volume: d003 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d003/wal @@ -183,17 +183,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d003-grafana + name: synology-iscsi-d003-grafana labels: - prole.storage/role: grafana - prole.storage/volume: d003 + synology.storage/role: grafana + synology.storage/volume: d003 spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d003/grafana @@ -209,17 +209,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d002-prometheus + name: synology-iscsi-d002-prometheus labels: - prole.storage/role: prometheus - prole.storage/volume: d002 + synology.storage/role: prometheus + synology.storage/volume: d002 spec: capacity: storage: 20Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d002/prometheus @@ -235,17 +235,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d001-openbao + name: synology-iscsi-d001-openbao labels: - prole.storage/role: openbao - prole.storage/volume: d001 + synology.storage/role: openbao + synology.storage/volume: d001 spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d001/openbao diff --git a/deploy/opentofu/k3s/manifests/prole/openbao-statefulset.yaml b/deploy/opentofu/k3s/manifests/prole/openbao-statefulset.yaml index 01916e5..ea374f2 100644 --- a/deploy/opentofu/k3s/manifests/prole/openbao-statefulset.yaml +++ b/deploy/opentofu/k3s/manifests/prole/openbao-statefulset.yaml @@ -51,7 +51,7 @@ spec: resources: requests: storage: 1Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: openbao + synology.storage/role: openbao diff --git a/deploy/opentofu/k3s/manifests/prole/prole-db.yaml b/deploy/opentofu/k3s/manifests/prole/prole-db.yaml index e27b5bf..01e4490 100644 --- a/deploy/opentofu/k3s/manifests/prole/prole-db.yaml +++ b/deploy/opentofu/k3s/manifests/prole/prole-db.yaml @@ -4,27 +4,23 @@ metadata: name: prole-db spec: instances: 3 - imageName: k3d-prole-registry:5000/knoe-db:18-138 + imageName: k3d-prole-registry:5000/knoe-db:18-140 postgresUID: 100 postgresGID: 101 maxSyncReplicas: 1 affinity: # CloudNativePG uses its own affinity schema (not corev1.Affinity). See: `kubectl explain cluster.spec.affinity` enablePodAntiAffinity: true - podAntiAffinityType: required + podAntiAffinityType: preferred topologyKey: kubernetes.io/hostname - tolerations: - - key: "node-role.kubernetes.io/control-plane" - operator: "Exists" - effect: "NoSchedule" nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: prole.org/node-role + - key: node.kubernetes.io/instance-type operator: In values: - - db + - k3s postgresql: parameters: @@ -92,10 +88,10 @@ spec: resources: requests: storage: 1Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: data + synology.storage/role: data walStorage: size: 1Gi pvcTemplate: @@ -104,9 +100,9 @@ spec: resources: requests: storage: 1Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: wal + synology.storage/role: wal monitoring: enablePodMonitor: false diff --git a/deploy/opentofu/k3s/manifests/prole/storageclass-prole-iscsi.yaml b/deploy/opentofu/k3s/manifests/prole/storageclass-synology-iscsi.yaml similarity index 87% rename from deploy/opentofu/k3s/manifests/prole/storageclass-prole-iscsi.yaml rename to deploy/opentofu/k3s/manifests/prole/storageclass-synology-iscsi.yaml index f48db60..3b4fd75 100644 --- a/deploy/opentofu/k3s/manifests/prole/storageclass-prole-iscsi.yaml +++ b/deploy/opentofu/k3s/manifests/prole/storageclass-synology-iscsi.yaml @@ -1,7 +1,7 @@ apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: - name: prole-iscsi + name: synology-iscsi provisioner: kubernetes.io/no-provisioner reclaimPolicy: Retain volumeBindingMode: WaitForFirstConsumer diff --git a/etc/init_cloudnative_pg.sh b/etc/init_cloudnative_pg.sh index a9bd781..01c0361 100755 --- a/etc/init_cloudnative_pg.sh +++ b/etc/init_cloudnative_pg.sh @@ -101,9 +101,9 @@ CERT_MANAGER_MANIFEST_URL=${CERT_MANAGER_MANIFEST_URL:-} CERT_MANAGER_FALLBACK_VERSION=${CERT_MANAGER_FALLBACK_VERSION:-1.19.3} # Protected storage requirements (k3s only) -PROLE_CNPG_STORAGE_CLASS=${PROLE_CNPG_STORAGE_CLASS:-prole-iscsi} -PROLE_PROTECTED_DATA_PATH=${PROLE_PROTECTED_DATA_PATH:-/prole/d001/data} -PROLE_PROTECTED_WAL_PATH=${PROLE_PROTECTED_WAL_PATH:-/prole/d001/wal} +PROLE_CNPG_STORAGE_CLASS=${PROLE_CNPG_STORAGE_CLASS:-synology-iscsi} +PROLE_PROTECTED_DATA_PATH=${PROLE_PROTECTED_DATA_PATH:-/synology/d001/data} +PROLE_PROTECTED_WAL_PATH=${PROLE_PROTECTED_WAL_PATH:-/synology/d001/wal} if [[ "$ACTION" != "deploy" && "$ACTION" != "rollout" && "$ACTION" != "force-rollout" ]]; then if [[ -n "${2:-}" ]]; then @@ -265,9 +265,9 @@ validate_cnpg_manifest_storage() { wal_sc="$v" fi fi - if [[ -n "$block" && "$line" =~ prole\.storage/role: ]]; then + if [[ -n "$block" && "$line" =~ synology\.storage/role: ]]; then local r - r=$(printf '%s' "$line" | sed -E 's/^.*prole\.storage\/role:[[:space:]]*//; s/[[:space:]]+$//') + r=$(printf '%s' "$line" | sed -E 's/^.*synology\.storage\/role:[[:space:]]*//; s/[[:space:]]+$//') if [[ "$block" == "storage" && -z "$data_role" ]]; then data_role="$r" elif [[ "$block" == "wal" && -z "$wal_role" ]]; then @@ -283,7 +283,7 @@ validate_cnpg_manifest_storage() { fi if [[ "$data_role" != "data" || "$wal_role" != "wal" ]]; then - echo "ERROR: CNPG Cluster manifest must include explicit PV selectors for Prole storage (prole.storage/role=data and prole.storage/role=wal)." >&2 + echo "ERROR: CNPG Cluster manifest must include explicit PV selectors for Prole storage (synology.storage/role=data and synology.storage/role=wal)." >&2 echo "Got selector roles: data='${data_role:-}', wal='${wal_role:-}'" >&2 return 1 fi @@ -307,8 +307,8 @@ validate_cnpg_runtime_storage() { local data_sc wal_sc data_role wal_role data_sc=$(printf '%s' "$cluster_json" | jq -r '.spec.storage.pvcTemplate.storageClassName // empty') wal_sc=$(printf '%s' "$cluster_json" | jq -r '.spec.walStorage.pvcTemplate.storageClassName // empty') - data_role=$(printf '%s' "$cluster_json" | jq -r '.spec.storage.pvcTemplate.selector.matchLabels["prole.storage/role"] // empty') - wal_role=$(printf '%s' "$cluster_json" | jq -r '.spec.walStorage.pvcTemplate.selector.matchLabels["prole.storage/role"] // empty') + data_role=$(printf '%s' "$cluster_json" | jq -r '.spec.storage.pvcTemplate.selector.matchLabels["synology.storage/role"] // empty') + wal_role=$(printf '%s' "$cluster_json" | jq -r '.spec.walStorage.pvcTemplate.selector.matchLabels["synology.storage/role"] // empty') if [[ "$data_sc" != "$sc" || "$wal_sc" != "$sc" ]]; then echo "ERROR: Live CNPG Cluster storageClassName must be '${sc}' for both data and walStorage (got data='${data_sc:-}', wal='${wal_sc:-}')." >&2 @@ -366,9 +366,9 @@ validate_cnpg_runtime_storage() { return 1 fi case "$pv_path" in - /prole/*) ;; + /synology/*) ;; *) - echo "ERROR: Bound PV '${pv}' path '${pv_path}' is not under /prole/. Refusing deployment." >&2 + echo "ERROR: Bound PV '${pv}' path '${pv_path}' is not under /synology/. Refusing deployment." >&2 return 1 ;; esac @@ -448,16 +448,20 @@ wait_for_apiserver_ready() { } ensure_namespace() { - if [[ -z "${PROLE_NAMESPACE:-}" ]]; then - echo "ERROR: PROLE_NAMESPACE is empty. Set NAMESPACE in conf/prole.cfg." >&2 - exit 1 - fi + require_namespace_configured if ! kubectl get namespace "$PROLE_NAMESPACE" >/dev/null 2>&1; then echo "Creating namespace '$PROLE_NAMESPACE' ..." kubectl create namespace "$PROLE_NAMESPACE" >/dev/null 2>&1 || true fi } +require_namespace_configured() { + if [[ -z "${PROLE_NAMESPACE:-}" ]]; then + echo "ERROR: PROLE_NAMESPACE is empty. Set NAMESPACE in conf/prole.cfg." >&2 + exit 1 + fi +} + kubectl_apply_retry() { local namespace="${1:-}" local attempts=${2:-8} @@ -540,6 +544,17 @@ get_latest_image() { echo "knoe-db:${pg_version}-${release}" } +_desired_cnpg_image() { + local image + if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then + image=$(get_latest_image) + else + image="knoe-db:$VERSION" + fi + image=$(resolve_cnpg_image "$image") + printf '%s' "$image" +} + _cnpg_current_image_name() { if ! kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then echo "" @@ -1287,6 +1302,151 @@ apply_prole_manifest_file() { return 1 } +_prole_k3s_registry_host_for_cache() { + # Best-effort host:port for the k3s registry, used as an optional cache source in k3d mode. + # Intentionally avoids k3d registry names and localhost-ish endpoints. + local r="${PROLE_K3S_REGISTRY:-${K3S_REGISTRY_HOST:-${LOCAL_REGISTRY_INTERNAL:-${LOCAL_REGISTRY:-}}}}" + + r="${r#http://}" + r="${r#https://}" + + case "${r}" in + "") + r="myrddin.prole.org:5000" + ;; + localhost:5000|127.0.0.1:5000|*.localhost|*.localhost:5000) + r="myrddin.prole.org:5000" + ;; + k3d-*|*/k3d-*) + r="myrddin.prole.org:5000" + ;; + esac + + printf '%s' "$r" +} + +_docker_build_knoe_db_image() { + # Build the knoe-db image with BuildKit and optional cache source. + # Args: [cache_ref] + local tag="$1" + local context_dir="$2" + local cache_ref="${3:-}" + + local ssh_args=() + if [[ -n "${SSH_AUTH_SOCK:-}" ]]; then + ssh_args+=(--ssh default) + fi + + if docker buildx version >/dev/null 2>&1; then + # Prefer an isolated docker-container builder to avoid host snapshot/cache corruption. + local builder_name="${PROLE_BUILDX_BUILDER:-prole-buildkit}" + if ! docker buildx inspect "$builder_name" >/dev/null 2>&1; then + docker buildx create --name "$builder_name" --driver docker-container --use >/dev/null 2>&1 || true + else + docker buildx use "$builder_name" >/dev/null 2>&1 || true + fi + + local cache_args=() + if [[ -n "$cache_ref" ]]; then + cache_args+=(--cache-from "type=registry,ref=${cache_ref}") + fi + + if docker buildx build \ + --pull \ + --tag "$tag" \ + --load \ + "${ssh_args[@]}" \ + "${cache_args[@]}" \ + "$context_dir"; then + return 0 + fi + fi + + # Fallback to docker build (still BuildKit-enabled); cache-from here only works if the cache image is present locally. + local cache_from_args=() + if [[ -n "$cache_ref" ]]; then + docker pull "$cache_ref" >/dev/null 2>&1 || true + cache_from_args+=(--cache-from "$cache_ref") + fi + + DOCKER_BUILDKIT=1 docker build \ + --pull \ + "${ssh_args[@]}" \ + "${cache_from_args[@]}" \ + -t "$tag" \ + "$context_dir" +} + +# Import a Docker image directly to each k3s node via SSH + 'k3s ctr images import'. +# Used as a last-resort fallback when the in-cluster registry is unreachable and +# the kubectl port-forward push also fails (e.g. registry service not yet deployed). +# Respects optional PROLE_SSH_KEY (path to private key) and K3S_SSH_USER env vars. +_import_image_to_k3s_nodes() { + local image="$1" + local ssh_user="${K3S_SSH_USER:-${ANSIBLE_USER:-ansible}}" + local ssh_key_args=() + # Auto-detect the SSH key: explicit override → ~/.ssh/id_ed25519_ansible (ansible user key) + # → prole ssh-keys dir → ssh-agent default. + local _auto_key_ansible="${HOME}/.ssh/id_ed25519_ansible" + local _auto_key_prole="${PROLE_HOME:-$SCRIPT_DIR/..}/ssh-keys/id_prole_ed25519" + local _ssh_key="${PROLE_SSH_KEY:-${K3S_SSH_KEY:-}}" + if [[ -z "$_ssh_key" && -f "$_auto_key_ansible" ]]; then + _ssh_key="$_auto_key_ansible" + elif [[ -z "$_ssh_key" && -f "$_auto_key_prole" ]]; then + _ssh_key="$_auto_key_prole" + fi + if [[ -n "$_ssh_key" && -f "$_ssh_key" ]]; then + ssh_key_args=(-i "$_ssh_key") + fi + + local nodes=() + mapfile -t nodes < <(kubectl get nodes \ + -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true) + if [[ ${#nodes[@]} -eq 0 ]]; then + echo " WARN: No k3s nodes found for direct image import." >&2 + return 1 + fi + + local tar_file + tar_file=$(mktemp /tmp/prole-img-XXXXXX.tar) + echo " Saving image '$image' for k3s node import ..." + if ! docker save "$image" -o "$tar_file" 2>/dev/null; then + echo " WARN: docker save failed for '$image'." >&2 + rm -f "$tar_file" 2>/dev/null || true + return 1 + fi + + local imported=0 + for node in "${nodes[@]}"; do + # Skip nodes that already have the image in k3s containerd. + if ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=10 \ + "${ssh_key_args[@]}" "${ssh_user}@${node}" \ + "sudo k3s ctr images ls 2>/dev/null | grep -qF '${plain_image}'" 2>/dev/null; then + echo " ✓ Image already in k3s containerd on '$node'; skipping import." + imported=$((imported + 1)) + continue + fi + echo " Importing '$image' to k3s node '$node' ..." + if ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=30 \ + -o ServerAliveInterval=30 -o ServerAliveCountMax=10 \ + "${ssh_key_args[@]}" "${ssh_user}@${node}" \ + "sudo k3s ctr images import -" < "$tar_file" >/dev/null 2>&1; then + echo " ✓ Image imported on node '$node'." + imported=$((imported + 1)) + else + echo " WARN: Failed to import image on node '$node'; skipping." >&2 + fi + done + + rm -f "$tar_file" 2>/dev/null || true + if [[ $imported -eq 0 ]]; then + echo " WARN: Image import failed on all k3s nodes." >&2 + return 1 + fi + echo " ✓ Image '$image' imported on $imported/${#nodes[@]} k3s node(s)." + return 0 +} + _push_to_k3s_registry() { local image="$1" local push_host="${LOCAL_REGISTRY:-${LOCAL_REGISTRY_INTERNAL:-myrddin.prole.org:5000}}" @@ -1434,6 +1594,7 @@ _ensure_prole_db_image() { local prole_db_dir="${PROLE_DB_BUILD_CONTEXT_DIR:-${runtime_home}/build/${mode_key}/knoe-db}" local source_db_dir="${PROLE_HOME:-$SCRIPT_DIR/..}/prole-db" local plain_image="${image##*/}" # e.g. knoe-db:18-088 + local k3s_cache_ref="" if [[ ! -f "$prole_db_dir/Dockerfile" ]]; then if [[ -d "$source_db_dir" ]]; then @@ -1446,6 +1607,32 @@ _ensure_prole_db_image() { if [[ "${PROLE_MODE:-}" == "k3s" ]]; then echo "Pre-flight: ensuring image '$image' is available in k3s registry/import path ..." + # Step 1 (k3s): if the CNPG cluster already exists and is running with this exact image + # the image is already present in k3s containerd on the nodes — skip push entirely. + local _k3s_current_image _k3s_ready + _k3s_current_image=$(kubectl get cluster "${CNPG_CLUSTER_NAME:-prole-db}" \ + -n "${CNPG_NAMESPACE:-${NAMESPACE:-prole-db}}" \ + -o jsonpath='{.spec.imageName}' 2>/dev/null || true) + _k3s_ready=$(kubectl get cluster "${CNPG_CLUSTER_NAME:-prole-db}" \ + -n "${CNPG_NAMESPACE:-${NAMESPACE:-prole-db}}" \ + -o jsonpath='{.status.readyInstances}' 2>/dev/null || true) + if [[ "$_k3s_current_image" == "$image" && "${_k3s_ready:-0}" -gt 0 ]]; then + echo " ✓ CNPG cluster already running with image '$image'; no push needed." + return 0 + fi + # Step 1b: check if image already exists in the registry via a quick manifest HEAD request. + local _reg_host="${LOCAL_REGISTRY:-${LOCAL_REGISTRY_INTERNAL:-myrddin.prole.org:5000}}" + _reg_host="${_reg_host#http://}"; _reg_host="${_reg_host#https://}" + local _img_name="${plain_image%%:*}" _img_tag="${plain_image##*:}" + if command -v curl >/dev/null 2>&1; then + if curl -k -fsS -m 3 \ + "https://${_reg_host}/v2/${_img_name}/manifests/${_img_tag}" >/dev/null 2>&1 \ + || curl -fsS -m 3 \ + "http://${_reg_host}/v2/${_img_name}/manifests/${_img_tag}" >/dev/null 2>&1; then + echo " ✓ Image '$image' already present in registry at '${_reg_host}'; no push needed." + return 0 + fi + fi else # Auto-detect active k3d cluster name local cluster_name="${K3D_CLUSTER_NAME:-}" @@ -1454,6 +1641,13 @@ _ensure_prole_db_image() { fi cluster_name="${cluster_name:-knoe-dev-cluster}" + # In k3d mode, optionally consult the k3s registry as a cache/source of truth before building. + local k3s_host + k3s_host=$(_prole_k3s_registry_host_for_cache) + if [[ -n "${k3s_host:-}" ]]; then + k3s_cache_ref="${k3s_host}/${plain_image}" + fi + echo "Pre-flight: verifying image '$image' is available in k3d cluster '$cluster_name' ..." # Step 1: check if already present in k3d containerd with matching digest @@ -1526,6 +1720,18 @@ _ensure_prole_db_image() { return $? fi + # Step 3b (k3d only): try to reuse a previously-built image from the k3s registry. + # If present, retag it to the desired image name and push/import into the k3d cluster. + if [[ "${PROLE_MODE:-}" == "k3d" && -n "${k3s_cache_ref:-}" && "${k3s_cache_ref}" != "${image}" ]]; then + if docker image inspect "$k3s_cache_ref" >/dev/null 2>&1 || docker pull "$k3s_cache_ref" >/dev/null 2>&1; then + echo " ✓ Found '$plain_image' in k3s registry (${k3s_cache_ref}); re-tagging for k3d as '$image' ..." + docker tag "$k3s_cache_ref" "$image" 2>/dev/null || true + _push_to_k3d_registry "$image" "$cluster_name" + return $? + fi + echo " (cache) '$plain_image' not present in k3s registry (${k3s_cache_ref}); building locally ..." + fi + # Step 4: image not found anywhere — build from source then push + import if [[ ! -f "$prole_db_dir/Dockerfile" ]]; then echo "ERROR: Dockerfile not found in '$prole_db_dir'; cannot build knoe-db image." >&2 @@ -1537,7 +1743,7 @@ _ensure_prole_db_image() { echo " Image '$image' not found in k3d, Docker daemon, or docker-import dir." fi echo " Building knoe-db image from '$prole_db_dir' ..." - if ! docker build -t "$plain_image" "$prole_db_dir"; then + if ! _docker_build_knoe_db_image "$plain_image" "$prole_db_dir" "${k3s_cache_ref:-}"; then echo "ERROR: docker build failed for image '$plain_image'." >&2 return 1 fi @@ -2538,14 +2744,14 @@ restore_cnpg_cluster() { } recycle_released_prole_iscsi_pvs() { - # StorageClass `prole-iscsi` uses `Retain` PV reclaim policy. After a CNPG reset, PVs can remain + # StorageClass `synology-iscsi` uses `Retain` PV reclaim policy. After a CNPG reset, PVs can remain # in `Released` with a stale `claimRef`, which prevents new PVCs (same names) from binding. # In that case, CNPG init jobs remain `Pending` with "didn't find available persistent volumes". - echo "Recycling Released prole-iscsi PVs (clearing stale claimRefs) for namespace '$NAMESPACE' ..." + echo "Recycling Released synology-iscsi PVs (clearing stale claimRefs) for namespace '$NAMESPACE' ..." local pvs pvs=$(kubectl get pv -o json \ | jq -r --arg ns "$NAMESPACE" '.items[] - | select(.spec.storageClassName == "prole-iscsi") + | select(.spec.storageClassName == "synology-iscsi") | select(.status.phase == "Released") | select((.spec.claimRef.namespace // "") == $ns) | .metadata.name' 2>/dev/null || true) @@ -2827,8 +3033,22 @@ get_latest_cnpg_version() { initialize() { ensure_tools - ensure_namespace + require_namespace_configured wait_for_apiserver_ready 180 + + local desired_image current_image + desired_image=$(_desired_cnpg_image) + current_image=$(_cnpg_current_image_name) + + if [[ -n "$current_image" && "$current_image" == "$desired_image" ]]; then + echo "✓ Namespace '$NAMESPACE' already contains CNPG cluster '$CNPG_CLUSTER_NAME' using image '$desired_image'; no re-deploy needed." + echo " (Use 'update'/'reload' if you intended to re-apply manifests.)" + return 0 + fi + + _enforce_namespace_image_policy "$desired_image" || return 1 + + ensure_namespace attempt_backup_if_active ensure_cnpg_operator pin_cnpg_controller @@ -2906,6 +3126,21 @@ update_reload() { deploy_cluster() { ensure_tools + require_namespace_configured + + wait_for_apiserver_ready 180 + + local image current_image_before + image=$(_desired_cnpg_image) + current_image_before=$(_cnpg_current_image_name) + + if [[ -n "$current_image_before" && "$current_image_before" == "$image" ]]; then + echo "✓ Namespace '$NAMESPACE' already contains CNPG cluster '$CNPG_CLUSTER_NAME' using image '$image'; skipping deploy." + return 0 + fi + + _enforce_namespace_image_policy "$image" || return 1 + ensure_namespace ensure_cnpg_operator pin_cnpg_controller @@ -2913,13 +3148,6 @@ deploy_cluster() { ensure_prole_protected_storage - local image - if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then - image=$(get_latest_image) - else - image="knoe-db:$VERSION" - fi - image=$(resolve_cnpg_image "$image") sync_manifest_image "$image" generate_tls_if_missing @@ -2949,11 +3177,20 @@ deploy_cluster() { kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true fi fi + + if [[ "${PROLE_UPGRADE:-0}" == "1" && -n "$current_image_before" && "$current_image_before" != "$image" ]]; then + echo "Upgrade requested; forcing CNPG rollout to ensure pods pick up image '$image' ..." + if kubectl cnpg version >/dev/null 2>&1; then + kubectl cnpg restart "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" || true + else + force_rollout || true + fi + fi } preflight_image() { ensure_tools - ensure_namespace + require_namespace_configured _ensure_prole_db_image } diff --git a/etc/init_garage_store.sh b/etc/init_garage_store.sh index a605a59..364bd68 100755 --- a/etc/init_garage_store.sh +++ b/etc/init_garage_store.sh @@ -60,7 +60,7 @@ else fi GARAGE_FILES=( - "$GARAGE_MANIFEST_DIR/storageclass-prole-iscsi.yaml" + "$GARAGE_MANIFEST_DIR/storageclass-synology-iscsi.yaml" "$GARAGE_MANIFEST_DIR/iscsi-pvs.yaml" "$GARAGE_MANIFEST_DIR/garage-configmap.yaml" "$GARAGE_MANIFEST_DIR/garage-statefulset.yaml" @@ -133,7 +133,7 @@ k3d_cleanup_pending_pvc() { return 0 fi selector=$(kubectl get pvc data-garage-0 -n "$NAMESPACE" -o jsonpath='{.spec.selector}' 2>/dev/null || true) - if printf '%s' "$selector" | grep -q "prole.storage/"; then + if printf '%s' "$selector" | grep -q "synology.storage/"; then echo "Removing pending Garage PVC with selector for k3d local-path ..." kubectl delete statefulset "$GARAGE_NAME" -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true kubectl delete pvc data-garage-0 -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true @@ -160,6 +160,24 @@ apply_manifests() { continue fi if [[ $diff_rc -ne 1 ]]; then + # For immutable StatefulSet VolumeClaimTemplates, kubectl diff itself returns an + # error (diff_rc != 1 but contains the immutable spec error). Handle it here. + if [[ "$(basename "$f")" == "garage-statefulset.yaml" ]] \ + && echo "$diff_out" | grep -q "updates to statefulset spec"; then + echo "WARN: Garage StatefulSet VolumeClaimTemplates changed (diff-stage); deleting and recreating ..." + kubectl delete statefulset "$GARAGE_NAME" -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true + local _pvc_sc_d + _pvc_sc_d=$(kubectl get pvc "data-garage-0" -n "$NAMESPACE" \ + -o jsonpath='{.spec.storageClassName}' 2>/dev/null || true) + if [[ -n "$_pvc_sc_d" && "$_pvc_sc_d" != "synology-iscsi" ]]; then + echo " Removing stale PVC 'data-garage-0' (storageClass: $_pvc_sc_d) ..." + kubectl delete pvc "data-garage-0" -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true + fi + printf '%s' "$rendered" | kubectl apply --validate=false -n "$NAMESPACE" -f - + GARAGE_APPLY_CHANGED=1 + GARAGE_STATEFULSET_CHANGED=1 + continue + fi echo "$diff_out" >&2 exit 1 fi @@ -175,6 +193,23 @@ apply_manifests() { && echo "$output" | grep -q "updates to statefulset spec"; then echo "WARN: Garage StatefulSet immutable in k3d; skipping apply." continue + elif [[ "$(basename "$f")" == "garage-statefulset.yaml" ]] \ + && echo "$output" | grep -q "updates to statefulset spec"; then + # VolumeClaimTemplates are immutable; delete the StatefulSet (PVCs are orphaned/preserved) + # and recreate so the new storageClass name takes effect. + echo "WARN: Garage StatefulSet VolumeClaimTemplates changed; deleting and recreating ..." + kubectl delete statefulset "$GARAGE_NAME" -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true + local _pvc_sc + _pvc_sc=$(kubectl get pvc "data-garage-0" -n "$NAMESPACE" \ + -o jsonpath='{.spec.storageClassName}' 2>/dev/null || true) + if [[ -n "$_pvc_sc" && "$_pvc_sc" != "synology-iscsi" ]]; then + echo " Removing stale PVC 'data-garage-0' (storageClass: $_pvc_sc) ..." + kubectl delete pvc "data-garage-0" -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true + fi + printf '%s' "$rendered" | kubectl apply --validate=false -n "$NAMESPACE" -f - + GARAGE_APPLY_CHANGED=1 + GARAGE_STATEFULSET_CHANGED=1 + continue fi echo "$output" >&2 exit 1 @@ -283,7 +318,7 @@ dump_debug() { } recycle_released_prole_iscsi_pv_for_pvc() { - # StorageClass `prole-iscsi` uses `Retain` PV reclaim policy. On restart, PVs can remain + # StorageClass `synology-iscsi` uses `Retain` PV reclaim policy. On restart, PVs can remain # in `Released` with a stale `claimRef`, which prevents a same-named PVC from binding. local ns="$1" local pvc="$2" @@ -301,7 +336,7 @@ recycle_released_prole_iscsi_pv_for_pvc() { local pv sc rp phase claim_ns claim_name claim_uid while IFS=$'\t' read -r pv sc rp phase claim_ns claim_name claim_uid; do - [[ "$sc" == "prole-iscsi" ]] || continue + [[ "$sc" == "synology-iscsi" ]] || continue [[ "$rp" == "Retain" ]] || continue [[ "$phase" == "Released" ]] || continue [[ "$claim_ns" == "$ns" ]] || continue @@ -322,7 +357,7 @@ wait_ready() { local initial_timeout="${PVC_REPAIR_WAIT_TIMEOUT:-30s}" if ! kubectl rollout status statefulset/$GARAGE_NAME -n "$NAMESPACE" --timeout="$initial_timeout"; then - echo "WARN: Garage not ready after $initial_timeout; checking for Released prole-iscsi PVs with stale claimRefs ..." >&2 + echo "WARN: Garage not ready after $initial_timeout; checking for Released synology-iscsi PVs with stale claimRefs ..." >&2 recycle_released_prole_iscsi_pv_for_pvc "$NAMESPACE" "data-garage-0" || true fi diff --git a/etc/init_monitoring.sh b/etc/init_monitoring.sh index e5dc445..9013d17 100755 --- a/etc/init_monitoring.sh +++ b/etc/init_monitoring.sh @@ -313,8 +313,8 @@ kind: PersistentVolume metadata: name: merlin-local-iscsi-${volume_id}-prometheus labels: - prole.storage/role: prometheus - prole.storage/volume: ${volume_id} + synology.storage/role: prometheus + synology.storage/volume: ${volume_id} spec: capacity: storage: 30Gi @@ -339,8 +339,8 @@ kind: PersistentVolume metadata: name: merlin-local-iscsi-${volume_id}-alertmanager labels: - prole.storage/role: alertmanager - prole.storage/volume: ${volume_id} + synology.storage/role: alertmanager + synology.storage/volume: ${volume_id} spec: capacity: storage: 5Gi @@ -365,8 +365,8 @@ kind: PersistentVolume metadata: name: merlin-local-iscsi-${volume_id}-grafana labels: - prole.storage/role: grafana - prole.storage/volume: ${volume_id} + synology.storage/role: grafana + synology.storage/volume: ${volume_id} spec: capacity: storage: 10Gi diff --git a/etc/init_openbao.sh b/etc/init_openbao.sh index 7f7b990..bea3e6e 100755 --- a/etc/init_openbao.sh +++ b/etc/init_openbao.sh @@ -423,7 +423,7 @@ iscsi_pv_names_from_manifest() { awk ' $1=="kind:" && $2=="PersistentVolume" {in_pv=1; next} in_pv && $1=="name:" {print $2; in_pv=0} - ' "$manifest" | sed 's/["\r]//g' | grep -v '^$' || true + ' "$manifest" | tr -d $'"\r' | grep -v '^$' || true } desired_pv_path_from_manifest() { @@ -435,7 +435,7 @@ desired_pv_path_from_manifest() { $1=="kind:" && $2=="PersistentVolume" {is_pv=1} is_pv && $1=="name:" && $2==pv {hit=1} hit && $1=="path:" {print $2; exit} - ' "$manifest" | sed 's/["\r]//g' | head -1 || true + ' "$manifest" | tr -d $'"\r' | head -1 || true } desired_pv_node_from_manifest() { @@ -448,7 +448,7 @@ desired_pv_node_from_manifest() { is_pv && $1=="name:" && $2==pv {hit=1} hit && $1=="values:" {in_values=1; next} in_values && $1=="-" {print $2; exit} - ' "$manifest" | sed 's/["\r]//g' | head -1 || true + ' "$manifest" | tr -d $'"\r' | head -1 || true } ensure_iscsi_pvs() { @@ -503,9 +503,9 @@ apply_k8s() { fi if [[ "$use_statefulset" == "1" ]]; then # Ensure storage class and PVs exist for k3s - if [[ -f "$SCRIPT_DIR/../k8s/prole/storageclass-prole-iscsi.yaml" ]]; then - echo "Applying StorageClass 'prole-iscsi' ..." - kubectl apply -f "$SCRIPT_DIR/../k8s/prole/storageclass-prole-iscsi.yaml" + if [[ -f "$SCRIPT_DIR/../k8s/prole/storageclass-synology-iscsi.yaml" ]]; then + echo "Applying StorageClass 'synology-iscsi' ..." + kubectl apply -f "$SCRIPT_DIR/../k8s/prole/storageclass-synology-iscsi.yaml" fi if [[ -f "$SCRIPT_DIR/../k8s/prole/iscsi-pvs.yaml" ]]; then ensure_iscsi_pvs "$SCRIPT_DIR/../k8s/prole/iscsi-pvs.yaml" @@ -518,6 +518,20 @@ apply_k8s() { else if [[ "${PROLE_MODE:-}" == "k3d" && "$output" == *"updates to statefulset spec"* ]]; then echo "WARN: OpenBao StatefulSet immutable in k3d; skipping apply." + elif [[ "$output" == *"updates to statefulset spec"* ]]; then + # VolumeClaimTemplates are immutable; delete the StatefulSet (PVCs are orphaned/preserved) + # and recreate so the new storageClass name takes effect. + echo "WARN: OpenBao StatefulSet VolumeClaimTemplates changed; deleting and recreating ..." + kubectl delete statefulset "$OPENBAO_NAME" -n "$OPENBAO_RESOURCE_NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true + local _pvc_sc + _pvc_sc=$(kubectl get pvc "data-openbao-0" -n "$OPENBAO_RESOURCE_NAMESPACE" \ + -o jsonpath='{.spec.storageClassName}' 2>/dev/null || true) + if [[ -n "$_pvc_sc" && "$_pvc_sc" != "synology-iscsi" ]]; then + echo " Removing stale PVC 'data-openbao-0' (storageClass: $_pvc_sc) ..." + kubectl delete pvc "data-openbao-0" -n "$OPENBAO_RESOURCE_NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true + fi + prole_render_manifest "$SCRIPT_DIR/../k8s/prole/openbao-statefulset.yaml" \ + | kubectl apply --validate=false -n "$OPENBAO_RESOURCE_NAMESPACE" -f - else echo "$output" >&2 return 1 @@ -554,7 +568,7 @@ k3d_cleanup_pending_openbao_pvc() { return 0 fi selector=$(kubectl get pvc data-openbao-0 -n "$OPENBAO_RESOURCE_NAMESPACE" -o jsonpath='{.spec.selector}' 2>/dev/null || true) - if printf '%s' "$selector" | grep -q "prole.storage/"; then + if printf '%s' "$selector" | grep -q "synology.storage/"; then echo "Removing pending OpenBao PVC with selector for k3d local-path ..." kubectl delete statefulset "$OPENBAO_NAME" -n "$OPENBAO_RESOURCE_NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true kubectl delete pvc data-openbao-0 -n "$OPENBAO_RESOURCE_NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true @@ -562,7 +576,7 @@ k3d_cleanup_pending_openbao_pvc() { } recycle_released_prole_iscsi_pv_for_pvc() { - # StorageClass `prole-iscsi` uses `Retain` PV reclaim policy. On restart, PVs can remain + # StorageClass `synology-iscsi` uses `Retain` PV reclaim policy. On restart, PVs can remain # in `Released` with a stale `claimRef`, which prevents a same-named PVC from binding. local ns="$1" local pvc="$2" @@ -580,7 +594,7 @@ recycle_released_prole_iscsi_pv_for_pvc() { local pv sc rp phase claim_ns claim_name claim_uid while IFS=$'\t' read -r pv sc rp phase claim_ns claim_name claim_uid; do - [[ "$sc" == "prole-iscsi" ]] || continue + [[ "$sc" == "synology-iscsi" ]] || continue [[ "$rp" == "Retain" ]] || continue [[ "$phase" == "Released" ]] || continue [[ "$claim_ns" == "$ns" ]] || continue @@ -621,13 +635,13 @@ wait_for_openbao() { echo "Waiting for OpenBao to become ready in namespace '$wait_ns' ..." if kubectl get statefulset/$OPENBAO_NAME -n "$wait_ns" >/dev/null 2>&1; then if ! kubectl rollout status statefulset/$OPENBAO_NAME -n "$wait_ns" --timeout="$initial_timeout"; then - echo "WARN: OpenBao not ready after $initial_timeout; checking for Released prole-iscsi PVs with stale claimRefs ..." >&2 + echo "WARN: OpenBao not ready after $initial_timeout; checking for Released synology-iscsi PVs with stale claimRefs ..." >&2 recycle_released_prole_iscsi_pv_for_pvc "$wait_ns" "data-openbao-0" || true fi kubectl rollout status statefulset/$OPENBAO_NAME -n "$wait_ns" --timeout=${ROLLOUT_TIMEOUT:-300s} else if ! kubectl rollout status deploy/$OPENBAO_NAME -n "$wait_ns" --timeout="$initial_timeout"; then - echo "WARN: OpenBao not ready after $initial_timeout; checking for Released prole-iscsi PVs with stale claimRefs ..." >&2 + echo "WARN: OpenBao not ready after $initial_timeout; checking for Released synology-iscsi PVs with stale claimRefs ..." >&2 recycle_released_prole_iscsi_pv_for_pvc "$wait_ns" "data-openbao-0" || true fi kubectl rollout status deploy/$OPENBAO_NAME -n "$wait_ns" --timeout=${ROLLOUT_TIMEOUT:-300s} diff --git a/etc/init_service_layer.sh b/etc/init_service_layer.sh index 0392251..cb6f4fc 100755 --- a/etc/init_service_layer.sh +++ b/etc/init_service_layer.sh @@ -246,7 +246,7 @@ deploy_service_layer() { SERVICE_NAMESPACE="$ns" PROLE_KDC_NAMESPACE="$ns" \ "$SCRIPT_DIR/init_kdc.sh" "$kdc_action" || rc=$? else - log "[INFO] Kerberos enabled: skipping standalone KDC deploy (KDC runs as `kdc` sidecar in `prole-auth`)." + log "[INFO] Kerberos enabled: skipping standalone KDC deploy (KDC runs as 'kdc' sidecar in 'prole-auth')." fi fi diff --git a/etc/lib/shell/common_core_lib.sh b/etc/lib/shell/common_core_lib.sh index 3d2942a..0977603 100644 --- a/etc/lib/shell/common_core_lib.sh +++ b/etc/lib/shell/common_core_lib.sh @@ -42,8 +42,17 @@ common_core_preparse_config() { fi local cfg_dir cfg_dir=$(cd "$(dirname "$COMMON_CORE_CONFIG_PATH")" && pwd) - PROLE_CONF="$cfg_dir" - export PROLE_CONF + # Preserve an already-set PROLE_CONF when it is a more-specific env subdir of + # cfg_dir (e.g. conf/service/ vs the injected-default conf/). This allows the + # parent shell / Python milestone to pass the correct env-specific conf dir and + # have it survive even when init scripts inject a generic fallback -c path. + local _current_conf="${PROLE_CONF:-}" + if [[ -n "$_current_conf" && "$_current_conf" == "${cfg_dir}/"* && -f "$_current_conf/prole.cfg" ]]; then + : # keep inherited PROLE_CONF — it is already more specific than cfg_dir + else + PROLE_CONF="$cfg_dir" + export PROLE_CONF + fi fi } diff --git a/etc/prole_cfg.sh b/etc/prole_cfg.sh index a07463d..298d101 100644 --- a/etc/prole_cfg.sh +++ b/etc/prole_cfg.sh @@ -518,7 +518,7 @@ fi # Safety: prole.cfg must override any pre-set PROLE_MODE from the environment. # Otherwise a leaked PROLE_MODE=k3d can trigger k3d-only manifest stripping (e.g., removing -# `storageClassName: prole-iscsi`) while targeting a k3s cluster. +# `storageClassName: synology-iscsi`) while targeting a k3s cluster. _prole_cfg_mode_hint="" if [[ -n "${prole_mode:-}" ]]; then _prole_cfg_mode_hint=$(prole_normalize_mode "$prole_mode") diff --git a/etc/render_manifest.py b/etc/render_manifest.py index 36b5904..9986a17 100644 --- a/etc/render_manifest.py +++ b/etc/render_manifest.py @@ -65,7 +65,7 @@ def _strip_blocks(text: str) -> str: break block_lines.append(next_line) j += 1 - if any("prole.storage/" in bl for bl in block_lines): + if any("synology.storage/" in bl for bl in block_lines): i = j continue out.extend(block_lines) @@ -78,7 +78,7 @@ def _strip_blocks(text: str) -> str: i += 1 continue - if stripped.startswith("storageClassName:") and "prole-iscsi" in stripped: + if stripped.startswith("storageClassName:") and "synology-iscsi" in stripped: i += 1 continue diff --git a/k8s/prole/garage-statefulset.yaml b/k8s/prole/garage-statefulset.yaml index 6afa443..7566deb 100644 --- a/k8s/prole/garage-statefulset.yaml +++ b/k8s/prole/garage-statefulset.yaml @@ -71,8 +71,8 @@ spec: resources: requests: storage: 29Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: garage - prole.storage/volume: d001 + synology.storage/role: garage + synology.storage/volume: d001 diff --git a/k8s/prole/iscsi-pvs.yaml b/k8s/prole/iscsi-pvs.yaml index aaa9bb9..40e00bf 100644 --- a/k8s/prole/iscsi-pvs.yaml +++ b/k8s/prole/iscsi-pvs.yaml @@ -1,17 +1,17 @@ apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d001-data + name: synology-iscsi-d001-data labels: - prole.storage/role: data - prole.storage/volume: d001 + synology.storage/role: data + synology.storage/volume: d001 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d001/data @@ -27,17 +27,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d001-wal + name: synology-iscsi-d001-wal labels: - prole.storage/role: wal - prole.storage/volume: d001 + synology.storage/role: wal + synology.storage/volume: d001 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d001/wal @@ -53,17 +53,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d001-garage + name: synology-iscsi-d001-garage labels: - prole.storage/role: garage - prole.storage/volume: d001 + synology.storage/role: garage + synology.storage/volume: d001 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d001/garage @@ -79,17 +79,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d002-data + name: synology-iscsi-d002-data labels: - prole.storage/role: data - prole.storage/volume: d002 + synology.storage/role: data + synology.storage/volume: d002 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d002/data @@ -105,17 +105,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d002-wal + name: synology-iscsi-d002-wal labels: - prole.storage/role: wal - prole.storage/volume: d002 + synology.storage/role: wal + synology.storage/volume: d002 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d002/wal @@ -131,17 +131,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d003-data + name: synology-iscsi-d003-data labels: - prole.storage/role: data - prole.storage/volume: d003 + synology.storage/role: data + synology.storage/volume: d003 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d003/data @@ -157,17 +157,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d003-wal + name: synology-iscsi-d003-wal labels: - prole.storage/role: wal - prole.storage/volume: d003 + synology.storage/role: wal + synology.storage/volume: d003 spec: capacity: storage: 29Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d003/wal @@ -183,17 +183,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d003-grafana + name: synology-iscsi-d003-grafana labels: - prole.storage/role: grafana - prole.storage/volume: d003 + synology.storage/role: grafana + synology.storage/volume: d003 spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d003/grafana @@ -209,17 +209,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d002-prometheus + name: synology-iscsi-d002-prometheus labels: - prole.storage/role: prometheus - prole.storage/volume: d002 + synology.storage/role: prometheus + synology.storage/volume: d002 spec: capacity: storage: 20Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d002/prometheus @@ -235,17 +235,17 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-iscsi-d001-openbao + name: synology-iscsi-d001-openbao labels: - prole.storage/role: openbao - prole.storage/volume: d001 + synology.storage/role: openbao + synology.storage/volume: d001 spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce - storageClassName: prole-iscsi + storageClassName: synology-iscsi persistentVolumeReclaimPolicy: Retain local: path: /synology/d001/openbao diff --git a/k8s/prole/monitoring-d004-pvs.yaml b/k8s/prole/monitoring-d004-pvs.yaml index 9dba63d..fbb8b9f 100644 --- a/k8s/prole/monitoring-d004-pvs.yaml +++ b/k8s/prole/monitoring-d004-pvs.yaml @@ -3,8 +3,8 @@ kind: PersistentVolume metadata: name: prole-monitoring-d004-prometheus labels: - prole.storage/role: prometheus - prole.storage/volume: d004 + synology.storage/role: prometheus + synology.storage/volume: d004 spec: capacity: storage: 30Gi @@ -29,8 +29,8 @@ kind: PersistentVolume metadata: name: prole-monitoring-d004-alertmanager labels: - prole.storage/role: alertmanager - prole.storage/volume: d004 + synology.storage/role: alertmanager + synology.storage/volume: d004 spec: capacity: storage: 5Gi @@ -55,8 +55,8 @@ kind: PersistentVolume metadata: name: prole-monitoring-d004-grafana labels: - prole.storage/role: grafana - prole.storage/volume: d004 + synology.storage/role: grafana + synology.storage/volume: d004 spec: capacity: storage: 10Gi diff --git a/k8s/prole/openbao-statefulset.yaml b/k8s/prole/openbao-statefulset.yaml index 01916e5..ea374f2 100644 --- a/k8s/prole/openbao-statefulset.yaml +++ b/k8s/prole/openbao-statefulset.yaml @@ -51,7 +51,7 @@ spec: resources: requests: storage: 1Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: openbao + synology.storage/role: openbao diff --git a/k8s/prole/prole-db-recovery.yaml.tpl b/k8s/prole/prole-db-recovery.yaml.tpl index 7531df8..0181a51 100644 --- a/k8s/prole/prole-db-recovery.yaml.tpl +++ b/k8s/prole/prole-db-recovery.yaml.tpl @@ -5,7 +5,7 @@ metadata: name: prole-db spec: instances: 3 - imageName: k3d-prole-registry:5000/knoe-db:18-138 + imageName: example.invalid:5000/knoe-db:0.0.0 postgresUID: 100 postgresGID: 101 maxSyncReplicas: 1 @@ -61,10 +61,10 @@ spec: resources: requests: storage: 1Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: data + synology.storage/role: data walStorage: size: 1Gi pvcTemplate: @@ -73,10 +73,10 @@ spec: resources: requests: storage: 1Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: wal + synology.storage/role: wal monitoring: enablePodMonitor: false diff --git a/k8s/prole/prole-db.yaml b/k8s/prole/prole-db.yaml index 2e7a718..f63def6 100644 --- a/k8s/prole/prole-db.yaml +++ b/k8s/prole/prole-db.yaml @@ -4,23 +4,23 @@ metadata: name: prole-db spec: instances: 3 - imageName: myrddin.prole.org:5000/knoe-db:18-125 + imageName: myrddin.prole.org:5000/knoe-db:18-140 postgresUID: 100 postgresGID: 101 maxSyncReplicas: 1 affinity: # CloudNativePG uses its own affinity schema (not corev1.Affinity). See: `kubectl explain cluster.spec.affinity` enablePodAntiAffinity: true - podAntiAffinityType: required + podAntiAffinityType: preferred topologyKey: kubernetes.io/hostname nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: prole.org/node-role + - key: node.kubernetes.io/instance-type operator: In values: - - db + - k3s postgresql: parameters: @@ -88,10 +88,10 @@ spec: resources: requests: storage: 1Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: data + synology.storage/role: data walStorage: size: 1Gi pvcTemplate: @@ -100,9 +100,9 @@ spec: resources: requests: storage: 1Gi - storageClassName: prole-iscsi + storageClassName: synology-iscsi selector: matchLabels: - prole.storage/role: wal + synology.storage/role: wal monitoring: enablePodMonitor: false diff --git a/k8s/prole/storageclass-prole-iscsi.yaml b/k8s/prole/storageclass-synology-iscsi.yaml similarity index 87% rename from k8s/prole/storageclass-prole-iscsi.yaml rename to k8s/prole/storageclass-synology-iscsi.yaml index f48db60..3b4fd75 100644 --- a/k8s/prole/storageclass-prole-iscsi.yaml +++ b/k8s/prole/storageclass-synology-iscsi.yaml @@ -1,7 +1,7 @@ apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: - name: prole-iscsi + name: synology-iscsi provisioner: kubernetes.io/no-provisioner reclaimPolicy: Retain volumeBindingMode: WaitForFirstConsumer diff --git a/knoe/core/actions.py b/knoe/core/actions.py index b6d28c3..6206309 100644 --- a/knoe/core/actions.py +++ b/knoe/core/actions.py @@ -856,6 +856,64 @@ class ProleInstaller: return self._ensure_namespace_prefix(ns) return self._ensure_namespace_prefix(self._generate_namespace_name()) + def _initial_db_namespace(self) -> str: + """Initial namespace suggestion for CNPG/Postgres. + + Unlike `_initial_namespace()`, this default is version-aware to encourage + parallel installs (e.g. `knoe-db-18-142`). + """ + + # Explicit overrides must win. + base = ( + os.environ.get("PROLE_DB_NAMESPACE") + or os.environ.get("DB_NAMESPACE") + or os.environ.get("PROLE_NAMESPACE") + or os.environ.get("NAMESPACE") + or "" + ).strip() + + if not base: + try: + existing = self._read_existing_env() + base = ( + (existing.get("NAMESPACE") or "") + or (existing.get("PROLE_NAMESPACE") or "") + ).strip() + except Exception: + base = "" + + if not base: + base = (self._read_existing_cfg_namespace() or "").strip() or "knoe-db" + + base = self._sanitize_namespace(self._ensure_namespace_prefix(base)) + + version = "" + try: + controller = getattr(self, "controller", None) + if controller is not None: + version = (controller.get_prole_db_version() or "").strip() + except Exception: + version = "" + + if not version: + return base + + version_ns = self._sanitize_namespace(version.replace(".", "-")) + + if base.endswith(f"-{version_ns}"): + return base + + combined = f"{base}-{version_ns}" + if len(combined) > 63: + # Keep the version suffix intact and trim the base to fit. + max_base_len = 63 - len(version_ns) - 1 + if max_base_len < 1: + return self._sanitize_namespace(f"knoe-db-{version_ns}") + base_trim = base[:max_base_len].rstrip("-") + combined = f"{base_trim}-{version_ns}" + + return self._sanitize_namespace(combined) + def _is_valid_namespace(self, name: str) -> bool: if not name or len(name) > 63: return False @@ -5613,8 +5671,8 @@ class ProleConsoleInstaller(ProleInstaller): NetworkScanMilestone(), EnvSetupMilestone(), SecretManagementMilestone(), - DatabaseCreationMilestone(), DockerBuildMilestone(), + DatabaseCreationMilestone(), ClusterLifecycleMilestone(), InitializationScriptsMilestone(), KerberosMilestone(), @@ -5768,8 +5826,8 @@ def _prepare_k3s_pipeline( _log(f" k3s_token: {'detected' if k3s_token else '(not detected)'}\n\n") # Load config to resolve namespace and secrets for pipeline tfvars - _log("==> Loading installer config...\n") - installer = ProleConsoleInstaller(controller, str(cfg_path)) + _log("==> Loading knoe config...\n") + knoe = ProleConsoleInstaller(controller, str(cfg_path)) try: existing_inputs = knoe._load_inputs_from_cfg() except Exception: diff --git a/knoe/milestone.py b/knoe/milestone.py index 7c818c8..ae7e4b3 100644 --- a/knoe/milestone.py +++ b/knoe/milestone.py @@ -85,10 +85,26 @@ class Milestone(ABC): namespace = (state.inputs.get("init_password.db_namespace", "") or "").strip() env["NAMESPACE"] = namespace or "default" - # Paths - env["PROLE_CONF"] = state.inputs.get( - "env_setup.PROLE_CONF", str(project_root / "conf") + # Paths — prefer the env-specific conf subdir (e.g. conf/service/) so that + # init scripts source the correct prole.cfg and don't fall back to the + # conf/prole.cfg symlink which may point to a different cluster env. + # Priority: (1) inherited PROLE_CONF from parent shell when it points to a + # valid env-specific subdir, (2) cluster_env-derived subdir, (3) stored value. + _inherited_conf = os.environ.get("PROLE_CONF", "") + _cluster_env_for_conf = state.inputs.get("init_cluster.cluster_env", "") + _env_conf_dir = ( + Path(project_root) / "conf" / _cluster_env_for_conf + if _cluster_env_for_conf + else None ) + if _inherited_conf and (Path(_inherited_conf) / "prole.cfg").exists(): + env["PROLE_CONF"] = _inherited_conf + elif _env_conf_dir and _env_conf_dir.is_dir(): + env["PROLE_CONF"] = str(_env_conf_dir) + else: + env["PROLE_CONF"] = state.inputs.get( + "env_setup.PROLE_CONF", str(project_root / "conf") + ) env["PROLE_DATA"] = state.inputs.get( "env_setup.PROLE_DATA", str(project_root / "prole-db" / "data") ) diff --git a/knoe/ui/screens/__init__.py b/knoe/ui/screens/__init__.py index b584457..f9c86a8 100644 --- a/knoe/ui/screens/__init__.py +++ b/knoe/ui/screens/__init__.py @@ -348,10 +348,10 @@ class ProleInstaller( ("Dependencies", "deps_summary"), ("Network", "network_scan"), ("System Environment", "env_setup"), - ("Cluster Environment", "init_cluster"), - ("Cluster Nodes", "cluster_nodes"), ("Database Options", "database_options"), ("Docker Build", "init_db_build"), + ("Cluster Environment", "init_cluster"), + ("Cluster Nodes", "cluster_nodes"), ("Database Creation", "init_password"), ("Initialization Scripts", "init_scripts"), ("Kerberos Authentication", "kerberos_config"), @@ -655,7 +655,7 @@ class ProleInstaller( self.db_password = tk.StringVar() self.db_password_confirm = tk.StringVar() self.namespace_owner = self._get_local_owner() - self.db_namespace = tk.StringVar(value=self._initial_namespace()) + self.db_namespace = tk.StringVar(value=self._initial_db_namespace()) try: _ns_from_cfg = ( (self.prole_cfg_data.get("Global", {}) or {}) @@ -663,7 +663,21 @@ class ProleInstaller( .strip() ) if _ns_from_cfg: - self.db_namespace.set(self._ensure_namespace_prefix(_ns_from_cfg)) + base = self._ensure_namespace_prefix(_ns_from_cfg) + ver = "" + try: + ver = (self.controller.get_prole_db_version() or "").strip() + except Exception: + ver = "" + if ver: + ver_ns = self._sanitize_namespace(ver.replace(".", "-")) + base_ns = self._sanitize_namespace(base) + if base_ns.endswith(f"-{ver_ns}"): + self.db_namespace.set(base_ns) + else: + self.db_namespace.set(self._sanitize_namespace(f"{base_ns}-{ver_ns}")) + else: + self.db_namespace.set(self._sanitize_namespace(base)) except Exception: pass self.db_host_port = tk.StringVar(value="5432") diff --git a/knoe/ui/screens/database.py b/knoe/ui/screens/database.py index 4b4f6e9..d9f8900 100644 --- a/knoe/ui/screens/database.py +++ b/knoe/ui/screens/database.py @@ -1420,6 +1420,7 @@ class DatabaseScreenMixin: # Use $PROLE_HOME/build for Docker build context # This avoids issues with PyInstaller's temporary _MEIPASS directory prole_home = resolve_prole_home() + env_key = self._cluster_env_key() mode_key = _deployment_mode_from_env(env_key) or "default" build_dir = prole_home / "build" / mode_key / "knoe-db" build_dir.mkdir(parents=True, exist_ok=True) @@ -1442,7 +1443,6 @@ class DatabaseScreenMixin: pub_key = pub_key_path.read_text().strip() username = self.db_username.get() - env_key = self._cluster_env_key() cmd = ["docker", "build", "--progress=plain"] cmd.extend(get_docker_build_platform_args(env_key)) cmd += [ diff --git a/knoe/ui/screens/docker.py b/knoe/ui/screens/docker.py index 29d4b6a..4c416e3 100644 --- a/knoe/ui/screens/docker.py +++ b/knoe/ui/screens/docker.py @@ -615,22 +615,48 @@ class DockerScreenMixin: raise Exception(f"Unknown environment: {env}") def create_or_recreate_k3d_dev_cluster(self, cluster_name="knoe-dev-cluster"): - """Create or restart local k3d cluster and wire it to the chosen registry.""" + """Create or restart local k3d cluster and wire it to the local k3d registry.""" result = subprocess.run( ["k3d", "cluster", "list"], capture_output=True, text=True ) if cluster_name in (result.stdout or ""): subprocess.run(["k3d", "cluster", "delete", cluster_name], check=True) - # Determine registry integration args - reg_args = [] - if getattr(self, "registry_url", None): - # If using the local k3d registry, we want to create or use it - if self.registry_url.startswith("localhost:5000"): - # Creating with --registry-create ensures it's available and integrated - reg_args = ["--registry-create", f"prole-registry:0.0.0.0:5000"] - else: - reg_args = ["--registry-use", self.registry_url] + # Always ensure the local k3d registry exists before creating the cluster so + # that we can wire it via --registry-use. Using --registry-use (rather than + # --registry-create) is required when the registry container already exists, + # which is the common case after the first install run. + reg_name = "prole-registry" + registry_container = f"k3d-{reg_name}" + lst = subprocess.run( + ["k3d", "registry", "list"], capture_output=True, text=True + ) + if registry_container not in (lst.stdout or "") and reg_name not in ( + lst.stdout or "" + ): + subprocess.run( + ["k3d", "registry", "create", reg_name, "--port", "0.0.0.0:5000"], + check=True, + ) + # Ensure the registry container is actually running + running = subprocess.run( + [ + "docker", + "ps", + "--filter", + f"name={registry_container}", + "--format", + "{{.Names}}", + ], + capture_output=True, + text=True, + ) + if not (running.stdout or "").strip(): + subprocess.run(["docker", "start", registry_container], check=True) + + # Wire the cluster to the registry so containerd inside each node resolves + # k3d-prole-registry.localhost:5000 correctly. + reg_args = ["--registry-use", f"{registry_container}:5000"] prole_data = str(self._resolve_env_dir("PROLE_DATA", "data")) volume_args = _k3d_prole_data_volume_args(prole_data) diff --git a/mock_val/init_cloudnative_pg.sh b/mock_val/init_cloudnative_pg.sh index 35eaa6c..8f78b8d 100755 --- a/mock_val/init_cloudnative_pg.sh +++ b/mock_val/init_cloudnative_pg.sh @@ -61,9 +61,9 @@ CERT_MANAGER_MANIFEST_URL=${CERT_MANAGER_MANIFEST_URL:-} CERT_MANAGER_FALLBACK_VERSION=${CERT_MANAGER_FALLBACK_VERSION:-1.19.3} # Protected storage requirements (k3s only) -PROLE_CNPG_STORAGE_CLASS=${PROLE_CNPG_STORAGE_CLASS:-prole-iscsi} -PROLE_PROTECTED_DATA_PATH=${PROLE_PROTECTED_DATA_PATH:-/prole/d001/data} -PROLE_PROTECTED_WAL_PATH=${PROLE_PROTECTED_WAL_PATH:-/prole/d001/wal} +PROLE_CNPG_STORAGE_CLASS=${PROLE_CNPG_STORAGE_CLASS:-synology-iscsi} +PROLE_PROTECTED_DATA_PATH=${PROLE_PROTECTED_DATA_PATH:-/synology/d001/data} +PROLE_PROTECTED_WAL_PATH=${PROLE_PROTECTED_WAL_PATH:-/synology/d001/wal} if [[ "$ACTION" != "deploy" && "$ACTION" != "rollout" && "$ACTION" != "force-rollout" ]]; then if [[ -n "${2:-}" ]]; then @@ -225,9 +225,9 @@ validate_cnpg_manifest_storage() { wal_sc="$v" fi fi - if [[ -n "$block" && "$line" =~ prole\.storage/role: ]]; then + if [[ -n "$block" && "$line" =~ synology\.storage/role: ]]; then local r - r=$(printf '%s' "$line" | sed -E 's/^.*prole\.storage\/role:[[:space:]]*//; s/[[:space:]]+$//') + r=$(printf '%s' "$line" | sed -E 's/^.*synology\.storage\/role:[[:space:]]*//; s/[[:space:]]+$//') if [[ "$block" == "storage" && -z "$data_role" ]]; then data_role="$r" elif [[ "$block" == "wal" && -z "$wal_role" ]]; then @@ -243,7 +243,7 @@ validate_cnpg_manifest_storage() { fi if [[ "$data_role" != "data" || "$wal_role" != "wal" ]]; then - echo "ERROR: CNPG Cluster manifest must include explicit PV selectors for Prole storage (prole.storage/role=data and prole.storage/role=wal)." >&2 + echo "ERROR: CNPG Cluster manifest must include explicit PV selectors for Prole storage (synology.storage/role=data and synology.storage/role=wal)." >&2 echo "Got selector roles: data='${data_role:-}', wal='${wal_role:-}'" >&2 return 1 fi @@ -267,8 +267,8 @@ validate_cnpg_runtime_storage() { local data_sc wal_sc data_role wal_role data_sc=$(printf '%s' "$cluster_json" | jq -r '.spec.storage.pvcTemplate.storageClassName // empty') wal_sc=$(printf '%s' "$cluster_json" | jq -r '.spec.walStorage.pvcTemplate.storageClassName // empty') - data_role=$(printf '%s' "$cluster_json" | jq -r '.spec.storage.pvcTemplate.selector.matchLabels["prole.storage/role"] // empty') - wal_role=$(printf '%s' "$cluster_json" | jq -r '.spec.walStorage.pvcTemplate.selector.matchLabels["prole.storage/role"] // empty') + data_role=$(printf '%s' "$cluster_json" | jq -r '.spec.storage.pvcTemplate.selector.matchLabels["synology.storage/role"] // empty') + wal_role=$(printf '%s' "$cluster_json" | jq -r '.spec.walStorage.pvcTemplate.selector.matchLabels["synology.storage/role"] // empty') if [[ "$data_sc" != "$sc" || "$wal_sc" != "$sc" ]]; then echo "ERROR: Live CNPG Cluster storageClassName must be '${sc}' for both data and walStorage (got data='${data_sc:-}', wal='${wal_sc:-}')." >&2 @@ -326,9 +326,9 @@ validate_cnpg_runtime_storage() { return 1 fi case "$pv_path" in - /prole/*) ;; + /synology/*) ;; *) - echo "ERROR: Bound PV '${pv}' path '${pv_path}' is not under /prole/. Refusing deployment." >&2 + echo "ERROR: Bound PV '${pv}' path '${pv_path}' is not under /synology/. Refusing deployment." >&2 return 1 ;; esac @@ -1168,6 +1168,81 @@ apply_prole_manifest_file() { return 1 } +_prole_k3s_registry_host_for_cache() { + # Best-effort host:port for the k3s registry, used as an optional cache source in k3d mode. + # Intentionally avoids k3d registry names and localhost-ish endpoints. + local r="${PROLE_K3S_REGISTRY:-${K3S_REGISTRY_HOST:-${LOCAL_REGISTRY_INTERNAL:-${LOCAL_REGISTRY:-}}}}" + + r="${r#http://}" + r="${r#https://}" + + case "${r}" in + "") + r="myrddin.prole.org:5000" + ;; + localhost:5000|127.0.0.1:5000|*.localhost|*.localhost:5000) + r="myrddin.prole.org:5000" + ;; + k3d-*|*/k3d-*) + r="myrddin.prole.org:5000" + ;; + esac + + printf '%s' "$r" +} + +_docker_build_knoe_db_image() { + # Build the knoe-db image with BuildKit and optional cache source. + # Args: [cache_ref] + local tag="$1" + local context_dir="$2" + local cache_ref="${3:-}" + + local ssh_args=() + if [[ -n "${SSH_AUTH_SOCK:-}" ]]; then + ssh_args+=(--ssh default) + fi + + if docker buildx version >/dev/null 2>&1; then + # Prefer an isolated docker-container builder to avoid host snapshot/cache corruption. + local builder_name="${PROLE_BUILDX_BUILDER:-prole-buildkit}" + if ! docker buildx inspect "$builder_name" >/dev/null 2>&1; then + docker buildx create --name "$builder_name" --driver docker-container --use >/dev/null 2>&1 || true + else + docker buildx use "$builder_name" >/dev/null 2>&1 || true + fi + + local cache_args=() + if [[ -n "$cache_ref" ]]; then + cache_args+=(--cache-from "type=registry,ref=${cache_ref}") + fi + + if docker buildx build \ + --pull \ + --tag "$tag" \ + --load \ + "${ssh_args[@]}" \ + "${cache_args[@]}" \ + "$context_dir"; then + return 0 + fi + fi + + # Fallback to docker build (still BuildKit-enabled); cache-from here only works if the cache image is present locally. + local cache_from_args=() + if [[ -n "$cache_ref" ]]; then + docker pull "$cache_ref" >/dev/null 2>&1 || true + cache_from_args+=(--cache-from "$cache_ref") + fi + + DOCKER_BUILDKIT=1 docker build \ + --pull \ + "${ssh_args[@]}" \ + "${cache_from_args[@]}" \ + -t "$tag" \ + "$context_dir" +} + _push_to_k3s_registry() { local image="$1" local push_host="${LOCAL_REGISTRY:-${LOCAL_REGISTRY_INTERNAL:-myrddin.prole.org:5000}}" @@ -1310,6 +1385,7 @@ _ensure_prole_db_image() { image=$(resolve_cnpg_image "$image") local prole_db_dir="${PROLE_HOME:-$SCRIPT_DIR/..}/prole-db" local plain_image="${image##*/}" # e.g. knoe-db:18-088 + local k3s_cache_ref="" if [[ "${PROLE_MODE:-}" == "k3s" ]]; then echo "Pre-flight: ensuring image '$image' is available in k3s registry/import path ..." @@ -1321,6 +1397,13 @@ _ensure_prole_db_image() { fi cluster_name="${cluster_name:-knoe-dev-cluster}" + # In k3d mode, optionally consult the k3s registry as a cache/source of truth before building. + local k3s_host + k3s_host=$(_prole_k3s_registry_host_for_cache) + if [[ -n "${k3s_host:-}" ]]; then + k3s_cache_ref="${k3s_host}/${plain_image}" + fi + echo "Pre-flight: verifying image '$image' is available in k3d cluster '$cluster_name' ..." # Step 1: check if already present in k3d containerd with matching digest @@ -1393,6 +1476,17 @@ _ensure_prole_db_image() { return $? fi + # Step 3b (k3d only): try to reuse a previously-built image from the k3s registry. + if [[ "${PROLE_MODE:-}" == "k3d" && -n "${k3s_cache_ref:-}" && "${k3s_cache_ref}" != "${image}" ]]; then + if docker image inspect "$k3s_cache_ref" >/dev/null 2>&1 || docker pull "$k3s_cache_ref" >/dev/null 2>&1; then + echo " ✓ Found '$plain_image' in k3s registry (${k3s_cache_ref}); re-tagging for k3d as '$image' ..." + docker tag "$k3s_cache_ref" "$image" 2>/dev/null || true + _push_to_k3d_registry "$image" "$cluster_name" + return $? + fi + echo " (cache) '$plain_image' not present in k3s registry (${k3s_cache_ref}); building locally ..." + fi + # Step 4: image not found anywhere — build from source then push + import if [[ ! -f "$prole_db_dir/Dockerfile" ]]; then echo "ERROR: Dockerfile not found in '$prole_db_dir'; cannot build knoe-db image." >&2 @@ -1404,7 +1498,7 @@ _ensure_prole_db_image() { echo " Image '$image' not found in k3d, Docker daemon, or docker-import dir." fi echo " Building knoe-db image from '$prole_db_dir' ..." - if ! docker build -t "$plain_image" "$prole_db_dir"; then + if ! _docker_build_knoe_db_image "$plain_image" "$prole_db_dir" "${k3s_cache_ref:-}"; then echo "ERROR: docker build failed for image '$plain_image'." >&2 return 1 fi @@ -2148,14 +2242,14 @@ reset_and_reinit() { } recycle_released_prole_iscsi_pvs() { - # StorageClass `prole-iscsi` uses `Retain` PV reclaim policy. After a CNPG reset, PVs can remain + # StorageClass `synology-iscsi` uses `Retain` PV reclaim policy. After a CNPG reset, PVs can remain # in `Released` with a stale `claimRef`, which prevents new PVCs (same names) from binding. # In that case, CNPG init jobs remain `Pending` with "didn't find available persistent volumes". - echo "Recycling Released prole-iscsi PVs (clearing stale claimRefs) for namespace '$NAMESPACE' ..." + echo "Recycling Released synology-iscsi PVs (clearing stale claimRefs) for namespace '$NAMESPACE' ..." local pvs pvs=$(kubectl get pv -o json \ | jq -r --arg ns "$NAMESPACE" '.items[] - | select(.spec.storageClassName == "prole-iscsi") + | select(.spec.storageClassName == "synology-iscsi") | select(.status.phase == "Released") | select((.spec.claimRef.namespace // "") == $ns) | .metadata.name' 2>/dev/null || true) diff --git a/mock_val/init_garage_store.sh b/mock_val/init_garage_store.sh index a605a59..907ba8a 100755 --- a/mock_val/init_garage_store.sh +++ b/mock_val/init_garage_store.sh @@ -60,7 +60,7 @@ else fi GARAGE_FILES=( - "$GARAGE_MANIFEST_DIR/storageclass-prole-iscsi.yaml" + "$GARAGE_MANIFEST_DIR/storageclass-synology-iscsi.yaml" "$GARAGE_MANIFEST_DIR/iscsi-pvs.yaml" "$GARAGE_MANIFEST_DIR/garage-configmap.yaml" "$GARAGE_MANIFEST_DIR/garage-statefulset.yaml" @@ -133,7 +133,7 @@ k3d_cleanup_pending_pvc() { return 0 fi selector=$(kubectl get pvc data-garage-0 -n "$NAMESPACE" -o jsonpath='{.spec.selector}' 2>/dev/null || true) - if printf '%s' "$selector" | grep -q "prole.storage/"; then + if printf '%s' "$selector" | grep -q "synology.storage/"; then echo "Removing pending Garage PVC with selector for k3d local-path ..." kubectl delete statefulset "$GARAGE_NAME" -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true kubectl delete pvc data-garage-0 -n "$NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true @@ -283,7 +283,7 @@ dump_debug() { } recycle_released_prole_iscsi_pv_for_pvc() { - # StorageClass `prole-iscsi` uses `Retain` PV reclaim policy. On restart, PVs can remain + # StorageClass `synology-iscsi` uses `Retain` PV reclaim policy. On restart, PVs can remain # in `Released` with a stale `claimRef`, which prevents a same-named PVC from binding. local ns="$1" local pvc="$2" @@ -301,7 +301,7 @@ recycle_released_prole_iscsi_pv_for_pvc() { local pv sc rp phase claim_ns claim_name claim_uid while IFS=$'\t' read -r pv sc rp phase claim_ns claim_name claim_uid; do - [[ "$sc" == "prole-iscsi" ]] || continue + [[ "$sc" == "synology-iscsi" ]] || continue [[ "$rp" == "Retain" ]] || continue [[ "$phase" == "Released" ]] || continue [[ "$claim_ns" == "$ns" ]] || continue @@ -322,7 +322,7 @@ wait_ready() { local initial_timeout="${PVC_REPAIR_WAIT_TIMEOUT:-30s}" if ! kubectl rollout status statefulset/$GARAGE_NAME -n "$NAMESPACE" --timeout="$initial_timeout"; then - echo "WARN: Garage not ready after $initial_timeout; checking for Released prole-iscsi PVs with stale claimRefs ..." >&2 + echo "WARN: Garage not ready after $initial_timeout; checking for Released synology-iscsi PVs with stale claimRefs ..." >&2 recycle_released_prole_iscsi_pv_for_pvc "$NAMESPACE" "data-garage-0" || true fi diff --git a/mock_val/init_monitoring.sh b/mock_val/init_monitoring.sh index 1554227..de385f0 100755 --- a/mock_val/init_monitoring.sh +++ b/mock_val/init_monitoring.sh @@ -313,8 +313,8 @@ kind: PersistentVolume metadata: name: merlin-local-iscsi-${volume_id}-prometheus labels: - prole.storage/role: prometheus - prole.storage/volume: ${volume_id} + synology.storage/role: prometheus + synology.storage/volume: ${volume_id} spec: capacity: storage: 30Gi @@ -339,8 +339,8 @@ kind: PersistentVolume metadata: name: merlin-local-iscsi-${volume_id}-alertmanager labels: - prole.storage/role: alertmanager - prole.storage/volume: ${volume_id} + synology.storage/role: alertmanager + synology.storage/volume: ${volume_id} spec: capacity: storage: 5Gi @@ -365,8 +365,8 @@ kind: PersistentVolume metadata: name: merlin-local-iscsi-${volume_id}-grafana labels: - prole.storage/role: grafana - prole.storage/volume: ${volume_id} + synology.storage/role: grafana + synology.storage/volume: ${volume_id} spec: capacity: storage: 10Gi diff --git a/mock_val/init_openbao.sh b/mock_val/init_openbao.sh index 631644e..1b4557a 100755 --- a/mock_val/init_openbao.sh +++ b/mock_val/init_openbao.sh @@ -424,9 +424,9 @@ apply_k8s() { fi if [[ "$use_statefulset" == "1" ]]; then # Ensure storage class and PVs exist for k3s - if [[ -f "$SCRIPT_DIR/../k8s/prole/storageclass-prole-iscsi.yaml" ]]; then - echo "Applying StorageClass 'prole-iscsi' ..." - kubectl apply -f "$SCRIPT_DIR/../k8s/prole/storageclass-prole-iscsi.yaml" + if [[ -f "$SCRIPT_DIR/../k8s/prole/storageclass-synology-iscsi.yaml" ]]; then + echo "Applying StorageClass 'synology-iscsi' ..." + kubectl apply -f "$SCRIPT_DIR/../k8s/prole/storageclass-synology-iscsi.yaml" fi if [[ -f "$SCRIPT_DIR/../k8s/prole/iscsi-pvs.yaml" ]]; then echo "Applying iSCSI PersistentVolumes ..." @@ -476,7 +476,7 @@ k3d_cleanup_pending_openbao_pvc() { return 0 fi selector=$(kubectl get pvc data-openbao-0 -n "$OPENBAO_RESOURCE_NAMESPACE" -o jsonpath='{.spec.selector}' 2>/dev/null || true) - if printf '%s' "$selector" | grep -q "prole.storage/"; then + if printf '%s' "$selector" | grep -q "synology.storage/"; then echo "Removing pending OpenBao PVC with selector for k3d local-path ..." kubectl delete statefulset "$OPENBAO_NAME" -n "$OPENBAO_RESOURCE_NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true kubectl delete pvc data-openbao-0 -n "$OPENBAO_RESOURCE_NAMESPACE" --ignore-not-found >/dev/null 2>&1 || true @@ -484,7 +484,7 @@ k3d_cleanup_pending_openbao_pvc() { } recycle_released_prole_iscsi_pv_for_pvc() { - # StorageClass `prole-iscsi` uses `Retain` PV reclaim policy. On restart, PVs can remain + # StorageClass `synology-iscsi` uses `Retain` PV reclaim policy. On restart, PVs can remain # in `Released` with a stale `claimRef`, which prevents a same-named PVC from binding. local ns="$1" local pvc="$2" @@ -502,7 +502,7 @@ recycle_released_prole_iscsi_pv_for_pvc() { local pv sc rp phase claim_ns claim_name claim_uid while IFS=$'\t' read -r pv sc rp phase claim_ns claim_name claim_uid; do - [[ "$sc" == "prole-iscsi" ]] || continue + [[ "$sc" == "synology-iscsi" ]] || continue [[ "$rp" == "Retain" ]] || continue [[ "$phase" == "Released" ]] || continue [[ "$claim_ns" == "$ns" ]] || continue @@ -543,13 +543,13 @@ wait_for_openbao() { echo "Waiting for OpenBao to become ready in namespace '$wait_ns' ..." if kubectl get statefulset/$OPENBAO_NAME -n "$wait_ns" >/dev/null 2>&1; then if ! kubectl rollout status statefulset/$OPENBAO_NAME -n "$wait_ns" --timeout="$initial_timeout"; then - echo "WARN: OpenBao not ready after $initial_timeout; checking for Released prole-iscsi PVs with stale claimRefs ..." >&2 + echo "WARN: OpenBao not ready after $initial_timeout; checking for Released synology-iscsi PVs with stale claimRefs ..." >&2 recycle_released_prole_iscsi_pv_for_pvc "$wait_ns" "data-openbao-0" || true fi kubectl rollout status statefulset/$OPENBAO_NAME -n "$wait_ns" --timeout=${ROLLOUT_TIMEOUT:-300s} else if ! kubectl rollout status deploy/$OPENBAO_NAME -n "$wait_ns" --timeout="$initial_timeout"; then - echo "WARN: OpenBao not ready after $initial_timeout; checking for Released prole-iscsi PVs with stale claimRefs ..." >&2 + echo "WARN: OpenBao not ready after $initial_timeout; checking for Released synology-iscsi PVs with stale claimRefs ..." >&2 recycle_released_prole_iscsi_pv_for_pvc "$wait_ns" "data-openbao-0" || true fi kubectl rollout status deploy/$OPENBAO_NAME -n "$wait_ns" --timeout=${ROLLOUT_TIMEOUT:-300s} diff --git a/mock_val/prole_cfg.sh b/mock_val/prole_cfg.sh index b0505d3..034082b 100644 --- a/mock_val/prole_cfg.sh +++ b/mock_val/prole_cfg.sh @@ -488,7 +488,7 @@ fi # Safety: prole.cfg must override any pre-set PROLE_MODE from the environment. # Otherwise a leaked PROLE_MODE=k3d can trigger k3d-only manifest stripping (e.g., removing -# `storageClassName: prole-iscsi`) while targeting a k3s cluster. +# `storageClassName: synology-iscsi`) while targeting a k3s cluster. _prole_cfg_mode_hint="" if [[ -n "${prole_mode:-}" ]]; then _prole_cfg_mode_hint=$(prole_normalize_mode "$prole_mode") diff --git a/mock_val/render_manifest.py b/mock_val/render_manifest.py index 36b5904..9986a17 100644 --- a/mock_val/render_manifest.py +++ b/mock_val/render_manifest.py @@ -65,7 +65,7 @@ def _strip_blocks(text: str) -> str: break block_lines.append(next_line) j += 1 - if any("prole.storage/" in bl for bl in block_lines): + if any("synology.storage/" in bl for bl in block_lines): i = j continue out.extend(block_lines) @@ -78,7 +78,7 @@ def _strip_blocks(text: str) -> str: i += 1 continue - if stripped.startswith("storageClassName:") and "prole-iscsi" in stripped: + if stripped.startswith("storageClassName:") and "synology-iscsi" in stripped: i += 1 continue diff --git a/modes/k3d/conf/postgresql/.version b/modes/k3d/conf/postgresql/.version new file mode 100644 index 0000000..25bf17f --- /dev/null +++ b/modes/k3d/conf/postgresql/.version @@ -0,0 +1 @@ +18 \ No newline at end of file diff --git a/modes/k3d/prole-db/.version b/modes/k3d/prole-db/.version new file mode 100644 index 0000000..e440e5c --- /dev/null +++ b/modes/k3d/prole-db/.version @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/prole-db/.version b/prole-db/.version index eafdfb0..c2807f7 100644 --- a/prole-db/.version +++ b/prole-db/.version @@ -1 +1 @@ -138 \ No newline at end of file +140 \ No newline at end of file diff --git a/prole.sh b/prole.sh index 10dda7b..a4bc305 100755 --- a/prole.sh +++ b/prole.sh @@ -145,15 +145,19 @@ err_msg() { load_prole_cfg() { local cfg="${1:-$PROLE_CFG_DEFAULT}" + # Set PROLE_CONF before sourcing prole_cfg.sh so it is preserved (prole_cfg.sh + # honours a pre-set PROLE_CONF) and the correct config file is auto-detected. + if [[ -f "$cfg" ]]; then + PROLE_CONF="$(cd "$(dirname "$cfg")" && pwd)" + export PROLE_CONF + PROLE_CFG_FILE="$cfg" + export PROLE_CFG_FILE + fi if [[ -f "${ROOT_DIR}/etc/prole_cfg.sh" ]]; then # shellcheck disable=SC1090 source "${ROOT_DIR}/etc/prole_cfg.sh" fi - if [[ -f "$cfg" ]]; then - export PROLE_CONF - PROLE_CONF="$(cd "$(dirname "$cfg")" && pwd)" - fi - export PROLE_CFG_FILE="$cfg" + export PROLE_CFG_FILE="${cfg}" } resolve_prole_mode() { @@ -594,7 +598,8 @@ case "${CMD}" in CMD_ARGS=("${_INSTALL_PASSTHRU[@]}") # Ensure OpenBao URL is set appropriately for the active mode before running install.py. - _cfg_for_env="${PROLE_CFG_DEFAULT}" + # Honour top-level -c / --config if provided (CONFIG_PATH), falling back to default. + _cfg_for_env="${CONFIG_PATH:-${PROLE_CFG_DEFAULT}}" for ((i=0; i<${#CMD_ARGS[@]}; i++)); do if [[ "${CMD_ARGS[$i]}" == "-c" || "${CMD_ARGS[$i]}" == "--config" ]]; then if [[ $((i+1)) -lt ${#CMD_ARGS[@]} ]]; then diff --git a/scan/ansible_inventory_flat.txt b/scan/ansible_inventory_flat.txt new file mode 100644 index 0000000..27351dd --- /dev/null +++ b/scan/ansible_inventory_flat.txt @@ -0,0 +1,7 @@ +host,ip,roles +merlin,10.0.0.36,merlin_bootstrap +merlin.prole.org,,iscsi,k3s_agents,k3s_hosts,linux_hosts,mariadb,mariadb_primary +myrddin.prole.org,,ad_dc,iscsi,k3s_hosts,k3s_servers,linux_hosts,ssl_hosts +pi.prole.org,,iscsi,k3s_agents,k3s_hosts,linux_hosts,pihole +raspberry.prole.org,,iscsi,linux_hosts,mariadb,mariadb_replica,pihole +retropie.prole.org,,iscsi,linux_hosts diff --git a/scan/network_description.txt b/scan/network_description.txt new file mode 100644 index 0000000..9645de5 --- /dev/null +++ b/scan/network_description.txt @@ -0,0 +1,65 @@ +Network Discovery Summary: +Primary Router: 10.0.0.1 (eero_5d:50:f2) +DNS Servers: 10.0.0.4, 100.100.100.100, 10.0.0.5 +Detected Devices: +- 10.0.0.38 [7e:19:9a:fe:9f:ed] (7e:19:9a:fe:9f:ed): Ports [22, 445, 5900, 11434, 88], Services: ['SSH', 'VNC', 'SMB/CIFS (Possible Windows/AD)', 'Ollama', 'Active Directory Related'] +- 10.0.0.22 [2c:cf:67:45:8d:97] (RaspberryPi_45:8d:97): Ports [22, 80, 443], Services: ['SSH', 'Web Server'] +- 10.0.0.4 [b8:27:eb:b3:6f:2b] (RaspberryPiF_b3:6f:2b): Ports [22, 53, 443, 2049], Services: ['DNS', 'SSH', 'NFS', 'Web Server'] +- 10.0.0.1 [9c:57:bc:5d:50:f2] (eero_5d:50:f2): Ports [53], Services: ['DNS'] +- 10.0.0.3 [2c:cf:67:8c:2b:47] (RaspberryPi_8c:2b:47): Ports [22, 53, 80, 443, 2049, 445, 5900, 88, 389, 636], Services: ['DNS', 'SSH', 'NFS', 'VNC', 'Web Server', 'SMB/CIFS (Possible Windows/AD)', 'Active Directory Related'] +- 10.0.0.2 [dc:a4:ca:ea:1a:2f] (Apple_ea:1a:2f): Ports [], Services: [] +- 10.0.0.6 [2c:cf:67:45:8d:97] (RaspberryPi_45:8d:97): Ports [22, 80, 443], Services: ['SSH', 'Web Server'] +- 10.0.0.5 [b8:27:eb:88:3a:41] (RaspberryPiF_88:3a:41): Ports [22, 53, 443, 2049, 5900], Services: ['DNS', 'SSH', 'NFS', 'VNC', 'Web Server'] +- 10.0.0.26 [c8:db:26:08:52:f8] (Logitech_08:52:f8): Ports [], Services: [] +- 10.0.0.33 [ec:b5:fa:b0:76:e4] (PhilipsLight_b0:76:e4): Ports [80, 443], Services: ['Web Server'] +- 10.0.0.73 [4c:a9:19:b3:12:f8] (TuyaSmart_b3:12:f8): Ports [], Services: [] +- 10.0.0.37 [54:07:7d:22:c0:b2] (Netgear_22:c0:b2): Ports [80], Services: ['Web Server'] +- 10.0.0.45 [f8:b4:6a:30:a4:1f] (HewlettPacka_30:a4:1f): Ports [80, 443], Services: ['Web Server'] +- 10.0.0.46 [d4:f7:d5:40:ab:17] (SonyInteract_40:ab:17): Ports [], Services: [] +- 10.0.0.48 [0c:fe:45:53:f4:3c] (SonyInteract_53:f4:3c): Ports [], Services: [] +- 10.0.0.55 [48:a6:b8:a7:50:60] (Sonos_a7:50:60): Ports [], Services: [] +- 10.0.0.58 [7e:46:74:2a:b0:b2] (7e:46:74:2a:b0:b2): Ports [], Services: [] +- 10.0.0.41 [b8:27:eb:57:10:d9] (RaspberryPiF_57:10:d9): Ports [22], Services: ['SSH'] +- 10.0.0.203 [00:11:32:3b:2f:08] (Synology_3b:2f:08): Ports [22, 80, 443, 2049, 445], Services: ['SSH', 'NFS', 'Web Server', 'SMB/CIFS (Possible Windows/AD)'] +- 10.0.0.100 [24:fc:e5:51:cf:74] (SamsungElect_51:cf:74): Ports [], Services: [] +- 10.0.0.99 [20:28:bc:f1:dc:ba] (Visionscape_f1:dc:ba): Ports [], Services: [] +- 10.0.0.95 [b8:27:eb:88:3a:41] (RaspberryPiF_88:3a:41): Ports [22, 53, 443, 2049, 5900], Services: ['DNS', 'SSH', 'NFS', 'VNC', 'Web Server'] +- 10.0.0.204 [50:eb:f6:56:f3:26] (ASUSTekCOMPU_56:f3:26): Ports [22, 3389, 445, 5900, 11434], Services: ['SSH', 'VNC', 'SMB/CIFS (Possible Windows/AD)', 'RDP (Windows)', 'Ollama'] +- 10.0.0.1\ [9c:57:bc:5d:50:f2] (eero_5d:50:f2): Ports [], Services: [] +- 10.0.0.123 [3c:ef:8c:96:e3:3c] (ZhejiangDahu_96:e3:3c): Ports [80], Services: ['Web Server'] +- 10.0.0.124 [9c:8e:cd:02:73:c4] (AmcrestTechn_02:73:c4): Ports [80], Services: ['Web Server'] +- 10.0.0.111 [ca:0b:6c:5d:0a:e5] (ca:0b:6c:5d:0a:e5): Ports [], Services: [] +- 10.0.0.127 [58:55:ca:4a:60:6e] (Apple_4a:60:6e): Ports [22], Services: ['SSH'] +- 10.0.0.117 [40:f5:20:1e:5e:91] (Espressif_1e:5e:91): Ports [80], Services: ['Web Server'] +- 10.0.0.113 [60:81:10:92:02:4e] (Apple_92:02:4e): Ports [], Services: [] +- 10.0.0.107 [b8:06:0d:b7:7c:56] (TuyaSmart_b7:7c:56): Ports [], Services: [] +- 10.0.0.112 [f8:33:31:40:58:c0] (TexasInstrum_40:58:c0): Ports [80, 443], Services: ['Web Server'] +- 10.0.0.130 [f8:bb:bf:a3:84:eb] (eero_a3:84:eb): Ports [53], Services: ['DNS'] +- 10.0.0.125 [a0:60:32:04:73:ba] (AmcrestTechn_04:73:ba): Ports [80], Services: ['Web Server'] +- 10.0.0.205 [a8:20:66:28:12:e7] (Apple_28:12:e7): Ports [22, 445, 5900, 88], Services: ['SSH', 'VNC', 'SMB/CIFS (Possible Windows/AD)', 'Active Directory Related'] +- 10.0.0.143 [60:5f:8d:7a:aa:32] (eero_7a:aa:32): Ports [53], Services: ['DNS'] +- 10.0.0.145 [7c:a6:b0:04:db:1c] (7c:a6:b0:04:db:1c): Ports [], Services: [] +- 10.0.0.128 [20:c9:d0:94:30:dd] (Apple_94:30:dd): Ports [], Services: [] +- 10.0.0.155 [62:17:97:b3:06:a1] (62:17:97:b3:06:a1): Ports [], Services: [] +- 10.0.0.170 [b0:ee:7b:ca:73:99] (Roku_ca:73:99): Ports [], Services: [] +- 10.0.0.179 [48:a6:b8:a4:8e:cc] (Sonos_a4:8e:cc): Ports [], Services: [] +- 10.0.0.180 [4c:e1:73:42:1a:be] (HuizhouDehon_2:1a:be): Ports [445], Services: ['SMB/CIFS (Possible Windows/AD)'] +- 10.0.0.189 [00:17:88:a3:2f:cc] (PhilipsLight_a3:2f:cc): Ports [80, 443], Services: ['Web Server'] +- 10.0.0.188 [00:16:6c:c5:1f:54] (SamsungElect_c5:1f:54): Ports [80, 443], Services: ['Web Server'] +- 10.0.0.196 [60:5f:8d:88:08:f2] (eero_88:08:f2): Ports [53], Services: ['DNS'] +- 10.0.0.175 [28:80:88:e4:10:1a] (Netgear_e4:10:1a): Ports [80], Services: ['Web Server'] +- 10.0.0.199 [7c:a6:b0:01:a7:33] (7c:a6:b0:01:a7:33): Ports [], Services: [] +- 10.0.0.206 [00:a0:de:a2:0b:ef] (Yamaha_a2:0b:ef): Ports [80], Services: ['Web Server'] +- 10.0.0.208 [a0:ad:9f:30:84:5f] (ASUSTekCOMPU_30:84:5f): Ports [], Services: [] +Ollama Instances found at: 10.0.0.38, 10.0.0.204 + +Ansible Inventory Summary: +Inventory directory: /Users/chrisfu/dev/prole/infrastructure +Hosts detected: 6 +- merlin (10.0.0.36): roles [merlin_bootstrap] +- merlin.prole.org (unknown): roles [iscsi, k3s_agents, k3s_hosts, linux_hosts, mariadb, mariadb_primary] +- myrddin.prole.org (unknown): roles [ad_dc, iscsi, k3s_hosts, k3s_servers, linux_hosts, ssl_hosts] +- pi.prole.org (unknown): roles [iscsi, k3s_agents, k3s_hosts, linux_hosts, pihole] +- raspberry.prole.org (unknown): roles [iscsi, linux_hosts, mariadb, mariadb_replica, pihole] +- retropie.prole.org (unknown): roles [iscsi, linux_hosts] +Flat inventory file: /Users/chrisfu/dev/prole/scan/ansible_inventory_flat.txt \ No newline at end of file diff --git a/supabase/deploy.sh b/supabase/deploy.sh index 3ceb2f4..4ac14d0 100755 --- a/supabase/deploy.sh +++ b/supabase/deploy.sh @@ -1219,7 +1219,7 @@ ensure_k8s_supabase_static_pvs() { if [[ "${MODE:-}" != "k8s" ]]; then return 0 fi - if [[ -z "${storage_class:-}" || "$storage_class" != "prole-iscsi" ]]; then + if [[ -z "${storage_class:-}" || "$storage_class" != "synology-iscsi" ]]; then return 0 fi @@ -1227,7 +1227,7 @@ ensure_k8s_supabase_static_pvs() { local node base parent base_name node="${SUPABASE_PV_NODE:-myrddin.prole.org}" - base="${SUPABASE_PV_BASE_DIR:-/prole/d002/supabase}" + base="${SUPABASE_PV_BASE_DIR:-/synology/d002/supabase}" base="${base%/}" parent="$(dirname "$base")" base_name="$(basename "$base")" @@ -1298,12 +1298,12 @@ EOF # Static local PVs with Retain reclaimPolicy can get stuck in Released after a namespace reset. # Delete Released PV objects so they can be recreated and rebound to the new PVCs. local pvs=( - prole-supabase-db - prole-supabase-deno - prole-supabase-functions - prole-supabase-imgproxy - prole-supabase-snippets - prole-supabase-storage + synology-supabase-db + synology-supabase-deno + synology-supabase-functions + synology-supabase-imgproxy + synology-supabase-snippets + synology-supabase-storage ) local pv for pv in "${pvs[@]}"; do @@ -1320,9 +1320,9 @@ EOF apiVersion: v1 kind: PersistentVolume metadata: - name: prole-supabase-db + name: synology-supabase-db labels: - prole.storage/pvc: supabase-db + synology.storage/pvc: supabase-db spec: capacity: storage: 5Gi @@ -1345,9 +1345,9 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-supabase-deno + name: synology-supabase-deno labels: - prole.storage/pvc: supabase-deno + synology.storage/pvc: supabase-deno spec: capacity: storage: 1Gi @@ -1370,9 +1370,9 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-supabase-functions + name: synology-supabase-functions labels: - prole.storage/pvc: supabase-functions + synology.storage/pvc: supabase-functions spec: capacity: storage: 1Gi @@ -1395,9 +1395,9 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-supabase-imgproxy + name: synology-supabase-imgproxy labels: - prole.storage/pvc: supabase-imgproxy + synology.storage/pvc: supabase-imgproxy spec: capacity: storage: 1Gi @@ -1420,9 +1420,9 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-supabase-snippets + name: synology-supabase-snippets labels: - prole.storage/pvc: supabase-snippets + synology.storage/pvc: supabase-snippets spec: capacity: storage: 1Gi @@ -1445,9 +1445,9 @@ spec: apiVersion: v1 kind: PersistentVolume metadata: - name: prole-supabase-storage + name: synology-supabase-storage labels: - prole.storage/pvc: supabase-storage + synology.storage/pvc: supabase-storage spec: capacity: storage: 1Gi @@ -1513,7 +1513,7 @@ PY local storage_class storage_class="${SUPABASE_STORAGE_CLASS:-}" if [[ -z "$storage_class" && "$MODE" == "k8s" ]]; then - storage_class="prole-iscsi" + storage_class="synology-iscsi" fi ensure_k8s_supabase_static_pvs "$storage_class" diff --git a/supabase/helm/prole-supabase/templates/helpers/_pvc.tpl b/supabase/helm/prole-supabase/templates/helpers/_pvc.tpl index de5af61..df24a5d 100644 --- a/supabase/helm/prole-supabase/templates/helpers/_pvc.tpl +++ b/supabase/helm/prole-supabase/templates/helpers/_pvc.tpl @@ -34,7 +34,7 @@ metadata: name: {{ $pvcName }} labels: {{- include "supabase.labels" $root | nindent 4 }} - prole.storage/pvc: {{ $pvcName | quote }} + synology.storage/pvc: {{ $pvcName | quote }} {{- with $persistence.annotations }} annotations: {{- toYaml . | nindent 4 }} @@ -43,10 +43,10 @@ spec: {{- if $persistence.storageClassName }} storageClassName: {{ $persistence.storageClassName }} {{- end }} - {{- if eq ($persistence.storageClassName | default "") "prole-iscsi" }} + {{- if eq ($persistence.storageClassName | default "") "synology-iscsi" }} selector: matchLabels: - prole.storage/pvc: {{ $pvcName | quote }} + synology.storage/pvc: {{ $pvcName | quote }} {{- end }} accessModes: {{- range $persistence.accessModes }} diff --git a/tests/etc/test_cnpg_k3s_storage_guards.sh b/tests/etc/test_cnpg_k3s_storage_guards.sh index 1f46361..cb87c5c 100644 --- a/tests/etc/test_cnpg_k3s_storage_guards.sh +++ b/tests/etc/test_cnpg_k3s_storage_guards.sh @@ -4,7 +4,7 @@ set -euo pipefail # Regression tests for k3s CNPG storage guardrails in etc/init_cloudnative_pg.sh # - must fail if protected mounts are root-backed -# - must fail if manifest lacks explicit prole-iscsi + selector labels (prevents local-path fallback) +# - must fail if manifest lacks explicit synology-iscsi + selector labels (prevents local-path fallback) # - must succeed (with mocks) when mounts + manifest + runtime objects are correct SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) @@ -139,9 +139,13 @@ if [[ "$args" == *"get nodes"*"--no-headers"* ]]; then fi # Runtime validation calls +if [[ "$args" == *" get cluster prole-db -o jsonpath="* ]]; then + # Return empty imageName so _cnpg_current_image_name sees no prior image. + exit 0 +fi if [[ "$args" == *" get cluster prole-db -o json"* ]]; then cat < "$BIN_DIR/kubectl" +#!/usr/bin/env bash +set -euo pipefail + +echo "Mocked kubectl called with $@" >> "${MOCK_LOG:-/tmp/mock_calls.log}" + +args="$*" + +if [[ "$args" == *"get --raw=/readyz"* || "$args" == *"get --raw='/readyz'"* || "$args" == *"get --raw=\"/readyz\""* ]]; then + echo "ok" + exit 0 +fi + +if [[ "$args" == *"version --short"* ]]; then + echo "Client Version: v0.0.0" + echo "Server Version: v0.0.0" + exit 0 +fi + +# CNPG webhook wait: return an endpoint IP so the wait loop passes. +if [[ "$args" == *"get endpoints"*"cnpg-webhook-service"* ]]; then + echo "10.42.0.10" + exit 0 +fi + +if [[ "$args" == *"get endpoints barman-cloud"* ]]; then + echo "10.42.0.11" + exit 0 +fi + +# Barman plugin service registration: return a service name for label-based lookups. +if [[ "$args" == *"get svc"*"-l cnpg.io/pluginName=barman-cloud.cloudnative-pg.io"*"-o jsonpath="* ]]; then + echo "barman-cloud" + exit 0 +fi +if [[ "$args" == *"get svc barman-cloud"*"-o jsonpath="*".metadata.name"* ]]; then + echo "barman-cloud" + exit 0 +fi + +if [[ "$args" == *"get svc barman-cloud"*"-o jsonpath="*"pluginClientSecret"* ]]; then + echo "barman-cloud-client-tls" + exit 0 +fi +if [[ "$args" == *"get svc barman-cloud"*"-o jsonpath="*"pluginServerSecret"* ]]; then + echo "barman-cloud-server-tls" + exit 0 +fi +if [[ "$args" == *"get svc barman-cloud"*"-o jsonpath="*"pluginPort"* ]]; then + echo "443" + exit 0 +fi + +if [[ "$args" == *"get namespace"* ]]; then + exit 0 +fi + +if [[ "$args" == *"create namespace"* ]]; then + exit 0 +fi + +if [[ "$args" == *"get cluster"*"-o jsonpath="*".spec.imageName"* ]]; then + echo "${MOCK_CLUSTER_IMAGE:-}" + exit 0 +fi + +if [[ "$args" == *"get cluster"* ]]; then + exit 0 +fi + +if [[ "$args" == *"patch cluster"* ]]; then + exit 0 +fi + +if [[ "$args" == *"cnpg version"* ]]; then + exit 0 +fi + +if [[ "$args" == *"cnpg restart"* ]]; then + exit 0 +fi + +if [[ "$args" == *"apply"* ]]; then + exit 0 +fi + +if [[ "$args" == *"get secret"* ]]; then + exit 0 +fi + +exit 0 +K_EOF +chmod +x "$BIN_DIR/kubectl" + +mock_ok_tool() { + cat < "$BIN_DIR/$1" +#!/usr/bin/env bash +echo "Mocked $1 called with \$@" >> "${MOCK_LOG:-/tmp/mock_calls.log}" +exit 0 +M_EOF + chmod +x "$BIN_DIR/$1" +} + +# Prevent accidental real calls during tests. +mock_ok_tool docker +mock_ok_tool k3d +mock_ok_tool curl +mock_ok_tool jq +mock_ok_tool skopeo + +export PATH="$BIN_DIR:$PATH" +export MOCK_LOG + +CONF_DIR="$TMP_DIR/conf" +mkdir -p "$CONF_DIR" + +SERVICE_DIR="$TMP_DIR/service" +mkdir -p "$SERVICE_DIR/secrets" +printf '%s' "dummy-private-key" > "$SERVICE_DIR/secrets/admin.key" +printf '%s' "dummy-public-key" > "$SERVICE_DIR/secrets/admin.pub" + +write_cfg() { + local prole_home_val="$1" + cat < "$CONF_DIR/prole.cfg" +[User] +NAMESPACE = test-ns +SERVICE_NAMESPACE = test-system +PROLE_HOME = ${prole_home_val} +PROLE_SERVICE = ${SERVICE_DIR} + +[Global] +DEPLOYMENT_MODE = k3d + +[Docker Build] +LOCAL_REGISTRY = localhost:5000 +LOCAL_REGISTRY_INTERNAL = k3d-prole-registry:5000 +C_EOF +} + +reset_log() { + : > "$MOCK_LOG" +} + +assert_not_called() { + local needle="$1" + if grep -q "$needle" "$MOCK_LOG"; then + echo "FAILURE: expected not to see '$needle' in mock log" + sed -n '1,200p' "$MOCK_LOG" || true + exit 1 + fi +} + +assert_called() { + local needle="$1" + if ! grep -q "$needle" "$MOCK_LOG"; then + echo "FAILURE: expected to see '$needle' in mock log" + sed -n '1,200p' "$MOCK_LOG" || true + exit 1 + fi +} + +run_case() { + local name="$1"; shift + local expected_rc="$1"; shift + + reset_log + local out="$TMP_DIR/${name}.out" + local err="$TMP_DIR/${name}.err" + + set +e + "$@" >"$out" 2>"$err" + local rc=$? + set -e + + if [[ "$rc" != "$expected_rc" ]]; then + echo "FAILURE: $name expected RC=$expected_rc got RC=$rc" + echo "--- stdout ---" + sed -n '1,200p' "$out" || true + echo "--- stderr ---" + sed -n '1,200p' "$err" || true + echo "--- mock log ---" + sed -n '1,200p' "$MOCK_LOG" || true + exit 1 + fi +} + +############################################### +# Case 1: no-op on same image (initialize) +############################################### + +export PROLE_CONF="$CONF_DIR" +export PROLE_SERVICE="$SERVICE_DIR" + +write_cfg "$REPO_ROOT" + +export MOCK_CLUSTER_IMAGE="k3d-prole-registry:5000/knoe-db:18-140" + +run_case "noop_initialize" 0 \ + bash "$SCRIPT_UNDER_TEST" --mode k3d --namespace test-ns initialize prole-db 18-140 + +assert_not_called "create namespace" + +############################################### +# Case 2: mismatch without --upgrade fails early +############################################### + +export MOCK_CLUSTER_IMAGE="k3d-prole-registry:5000/knoe-db:18-138" + +run_case "mismatch_initialize" 1 \ + bash "$SCRIPT_UNDER_TEST" --mode k3d --namespace test-ns initialize prole-db 18-140 + +assert_not_called "create namespace" + +############################################### +# Case 3: mismatch with --upgrade triggers restart hook (deploy) +############################################### + +PROLE_HOME_UPGRADE="$TMP_DIR/prole_home" +mkdir -p "$PROLE_HOME_UPGRADE/deploy/opentofu/k3s/manifests/prole" +cat <<'Y_EOF' > "$PROLE_HOME_UPGRADE/deploy/opentofu/k3s/manifests/prole/prole-db.yaml" +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: prole-db +spec: + instances: 1 + imageName: knoe-db:dummy +Y_EOF +cat <<'Y_EOF' > "$PROLE_HOME_UPGRADE/deploy/opentofu/k3s/manifests/prole/prole-db-barman-objectstore.yaml" +apiVersion: postgresql.cnpg.io/v1 +kind: ObjectStore +metadata: + name: prole-db +spec: {} +Y_EOF + +write_cfg "$PROLE_HOME_UPGRADE" + +export MOCK_CLUSTER_IMAGE="k3d-prole-registry:5000/knoe-db:18-138" + +run_case "upgrade_deploy" 0 \ + bash "$SCRIPT_UNDER_TEST" --mode k3d --namespace test-ns --upgrade deploy 18-140 + +assert_called "cnpg restart" + +echo "SUCCESS" diff --git a/tests/etc/test_init_cloudnative_pg_instances.sh b/tests/etc/test_init_cloudnative_pg_instances.sh index 07100d2..8074b48 100644 --- a/tests/etc/test_init_cloudnative_pg_instances.sh +++ b/tests/etc/test_init_cloudnative_pg_instances.sh @@ -136,25 +136,25 @@ fi # Runtime storage validation (k3s) expects live Cluster + PVC/PV objects. if [[ "${args}" == *"-n test-ns"*"get cluster"*"prole-db"*"-o json"* ]]; then cat <<'JSON' -{"apiVersion":"postgresql.cnpg.io/v1","kind":"Cluster","metadata":{"name":"prole-db"},"spec":{"storage":{"pvcTemplate":{"storageClassName":"prole-iscsi","selector":{"matchLabels":{"prole.storage/role":"data"}}}},"walStorage":{"pvcTemplate":{"storageClassName":"prole-iscsi","selector":{"matchLabels":{"prole.storage/role":"wal"}}}}}} +{"apiVersion":"postgresql.cnpg.io/v1","kind":"Cluster","metadata":{"name":"prole-db"},"spec":{"storage":{"pvcTemplate":{"storageClassName":"synology-iscsi","selector":{"matchLabels":{"synology.storage/role":"data"}}}},"walStorage":{"pvcTemplate":{"storageClassName":"synology-iscsi","selector":{"matchLabels":{"synology.storage/role":"wal"}}}}}} JSON exit 0 fi if [[ "${args}" == *"-n test-ns"*"get pvc"*"cnpg.io/cluster=prole-db"*"-o json"* ]]; then cat <<'JSON' {"items":[ - {"metadata":{"name":"prole-db-1"},"spec":{"storageClassName":"prole-iscsi","volumeName":"pv-data"}}, - {"metadata":{"name":"prole-db-1-wal"},"spec":{"storageClassName":"prole-iscsi","volumeName":"pv-wal"}} + {"metadata":{"name":"prole-db-1"},"spec":{"storageClassName":"synology-iscsi","volumeName":"pv-data"}}, + {"metadata":{"name":"prole-db-1-wal"},"spec":{"storageClassName":"synology-iscsi","volumeName":"pv-wal"}} ]} JSON exit 0 fi if [[ "${args}" == "get pv pv-data -o json"* ]]; then - echo '{"spec":{"storageClassName":"prole-iscsi","local":{"path":"/prole/d001/data"}}}' + echo '{"spec":{"storageClassName":"synology-iscsi","local":{"path":"/synology/d001/data"}}}' exit 0 fi if [[ "${args}" == "get pv pv-wal -o json"* ]]; then - echo '{"spec":{"storageClassName":"prole-iscsi","local":{"path":"/prole/d001/wal"}}}' + echo '{"spec":{"storageClassName":"synology-iscsi","local":{"path":"/synology/d001/wal"}}}' exit 0 fi @@ -279,13 +279,18 @@ if grep -qE '^\s*- key:\s*kubernetes\\.io/hostname\s*$|myrddin\\.prole\\.org' "$ sed -n '1,160p' "$TMP_DIR/applied.yaml" || true exit 1 fi -if ! grep -qE '^\s*- key:\s*prole\.org/node-role\s*$' "$TMP_DIR/applied.yaml"; then - echo "FAILURE: expected node-role label affinity in applied manifest (default)" +if ! grep -qE '^\s*- key:\s*node\.kubernetes\.io/instance-type\s*$' "$TMP_DIR/applied.yaml"; then + echo "FAILURE: expected k3s instance-type node affinity in applied manifest (default)" sed -n '1,160p' "$TMP_DIR/applied.yaml" || true exit 1 fi -if ! grep -qE '^\s*podAntiAffinityType:\s*required\s*$' "$TMP_DIR/applied.yaml"; then - echo "FAILURE: expected podAntiAffinityType=required in applied manifest (default)" +if ! grep -qE '^\s*- k3s\s*$' "$TMP_DIR/applied.yaml"; then + echo "FAILURE: expected node affinity value k3s in applied manifest (default)" + sed -n '1,160p' "$TMP_DIR/applied.yaml" || true + exit 1 +fi +if ! grep -qE '^\s*podAntiAffinityType:\s*preferred\s*$' "$TMP_DIR/applied.yaml"; then + echo "FAILURE: expected podAntiAffinityType=preferred in applied manifest (default)" sed -n '1,160p' "$TMP_DIR/applied.yaml" || true exit 1 fi diff --git a/tests/etc/test_init_cloudnative_pg_k3d_k3s_registry_cache.sh b/tests/etc/test_init_cloudnative_pg_k3d_k3s_registry_cache.sh new file mode 100644 index 0000000..2a8895f --- /dev/null +++ b/tests/etc/test_init_cloudnative_pg_k3d_k3s_registry_cache.sh @@ -0,0 +1,155 @@ +#!/usr/bin/env bash +# Regression test: in k3d mode, init_cloudnative_pg.sh should consult the k3s registry +# as an optional cache/source before falling back to a local build. + +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd) +ETC_DIR="$REPO_ROOT/etc" +SCRIPT_UNDER_TEST="$ETC_DIR/init_cloudnative_pg.sh" + +TMP_DIR=$(mktemp -d) +trap 'rm -rf "$TMP_DIR"' EXIT + +BIN_DIR="$TMP_DIR/bin" +mkdir -p "$BIN_DIR" + +_log_file="$TMP_DIR/mock_calls.log" +export MOCK_LOG_FILE="$_log_file" + +# Minimal kubectl mock so preflight-image path doesn't hang. +cat <<'K_EOF' > "$BIN_DIR/kubectl" +#!/usr/bin/env bash +echo "Mocked kubectl called with $*" >> "${MOCK_LOG_FILE}" +args="$*" +if [[ "${args}" == *"get --raw=/readyz"* || "${args}" == *"get --raw='/readyz'"* || "${args}" == *"get --raw=\"/readyz\""* ]]; then + echo "ok" + exit 0 +fi +if [[ "${args}" == *"version --short"* ]]; then + echo "Client Version: v0.0.0" + echo "Server Version: v0.0.0" + exit 0 +fi +exit 0 +K_EOF +chmod +x "$BIN_DIR/kubectl" + +# docker mock: pretend local images are missing, but the k3s registry has the image. +cat <<'D_EOF' > "$BIN_DIR/docker" +#!/usr/bin/env bash +echo "Mocked docker called with $*" >> "${MOCK_LOG_FILE}" + +cmd1="${1:-}" +cmd2="${2:-}" +cmd3="${3:-}" + +if [[ "$cmd1" == "image" && "$cmd2" == "inspect" ]]; then + # Force local inspect misses so the script tries the k3s registry. + exit 1 +fi + +if [[ "$cmd1" == "pull" ]]; then + # Succeed only for the expected k3s registry ref. + if [[ "$cmd2" == myrddin.prole.org:5000/knoe-db:* ]]; then + exit 0 + fi + exit 1 +fi + +if [[ "$cmd1" == "build" || ( "$cmd1" == "buildx" && "$cmd2" == "build" ) ]]; then + echo "FAILURE: docker build should not be invoked when k3s registry contains the image" >&2 + exit 99 +fi + +exit 0 +D_EOF +chmod +x "$BIN_DIR/docker" + +# k3d mock: allow cluster list without failing; we assert we don't need image import when push succeeds. +cat <<'K3D_EOF' > "$BIN_DIR/k3d" +#!/usr/bin/env bash +echo "Mocked k3d called with $*" >> "${MOCK_LOG_FILE}" +if [[ "${1:-}" == "cluster" && "${2:-}" == "list" ]]; then + echo "knoe-dev-cluster" + exit 0 +fi +exit 0 +K3D_EOF +chmod +x "$BIN_DIR/k3d" + +# Other mocks used by the script in various branches. +for t in curl skopeo ansible-playbook tofu terraform ollama jq; do + cat < "$BIN_DIR/$t" +#!/usr/bin/env bash +echo "Mocked $t called with \$@" >> "${_log_file}" +exit 0 +M_EOF + chmod +x "$BIN_DIR/$t" +done + +# Mock prole.cfg for k3d mode. +mkdir -p "$TMP_DIR/conf" +cat < "$TMP_DIR/conf/prole.cfg" +[User] +NAMESPACE = test-ns +SERVICE_NAMESPACE = test-system +PROLE_HOME = $REPO_ROOT +PROLE_SERVICE = $TMP_DIR/service + +[Global] +DEPLOYMENT_MODE = k3d + +[Docker Build] +LOCAL_REGISTRY = localhost:5000 +LOCAL_REGISTRY_INTERNAL = k3d-prole-registry.localhost:5000 +C_EOF + +mkdir -p "$TMP_DIR/service/secrets" +printf '%s' "dummy-private-key" > "$TMP_DIR/service/secrets/admin.key" +printf '%s' "dummy-public-key" > "$TMP_DIR/service/secrets/admin.pub" + +export PATH="$BIN_DIR:$PATH" +export PROLE_HOME="$REPO_ROOT" +export PROLE_CONF="$TMP_DIR/conf" +export PROLE_PASSWD="test-password" +export PROLE_SERVICE="$TMP_DIR/service" + +# Run a path that triggers image preflight. +bash "$SCRIPT_UNDER_TEST" --mode k3d preflight-image > "$TMP_DIR/stdout" 2> "$TMP_DIR/stderr" +RC=$? + +# Assertions +if ! grep -q "Mocked docker called with pull myrddin.prole.org:5000/knoe-db:" "${_log_file}"; then + echo "FAILURE: expected a k3s-registry pull attempt in k3d mode" >&2 + echo "--- mock calls ---" >&2 + sed -n '1,200p' "${_log_file}" >&2 || true + exit 1 +fi + +if grep -q "Mocked docker called with build" "${_log_file}"; then + echo "FAILURE: did not expect docker build to run when cache image is present" >&2 + echo "--- mock calls ---" >&2 + sed -n '1,200p' "${_log_file}" >&2 || true + exit 1 +fi + +if grep -q "Mocked k3d called with image import" "${_log_file}"; then + echo "FAILURE: expected registry push path, not k3d image import fallback" >&2 + echo "--- mock calls ---" >&2 + sed -n '1,200p' "${_log_file}" >&2 || true + exit 1 +fi + +if [[ $RC -eq 0 || $RC -eq 1 || $RC -eq 2 ]]; then + echo "SUCCESS (RC=$RC)" + exit 0 +fi + +echo "FAILURE with RC $RC" >&2 +echo "--- stdout ---" >&2 +sed -n '1,200p' "$TMP_DIR/stdout" >&2 || true +echo "--- stderr ---" >&2 +sed -n '1,200p' "$TMP_DIR/stderr" >&2 || true +exit 1 diff --git a/tests/etc/test_init_garage_recycles_released_pv_claimref.sh b/tests/etc/test_init_garage_recycles_released_pv_claimref.sh index 6b37fc6..c73edee 100644 --- a/tests/etc/test_init_garage_recycles_released_pv_claimref.sh +++ b/tests/etc/test_init_garage_recycles_released_pv_claimref.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Regression test: Garage startup should auto-recycle a Released prole-iscsi PV with a stale +# Regression test: Garage startup should auto-recycle a Released synology-iscsi PV with a stale # claimRef when the PVC is Pending and rollout is stuck beyond the initial timeout. set -euo pipefail @@ -23,7 +23,7 @@ cp "$SOURCE_ETC_DIR/prole_cfg.sh" "$WORK_DIR/etc/prole_cfg.sh" chmod +x "$WORK_DIR/etc/init_garage_store.sh" # Dummy manifests required by init_garage_store.sh -for f in storageclass-prole-iscsi.yaml iscsi-pvs.yaml garage-configmap.yaml garage-statefulset.yaml garage-service.yaml; do +for f in storageclass-synology-iscsi.yaml iscsi-pvs.yaml garage-configmap.yaml garage-statefulset.yaml garage-service.yaml; do printf '%s\n' "apiVersion: v1" > "$WORK_DIR/k8s/prole/$f" done @@ -86,7 +86,7 @@ case "$sub" in # PV list for recycle helper if [[ "$*" == *"get pv"*"-o jsonpath="* ]]; then printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ - "prole-iscsi-d001-garage" "prole-iscsi" "Retain" "Released" "test-ns" "data-garage-0" "olduid" + "synology-iscsi-d001-garage" "synology-iscsi" "Retain" "Released" "test-ns" "data-garage-0" "olduid" exit 0 fi # Pod lookup for init_layout @@ -148,7 +148,7 @@ else exit 1 fi -if ! grep -q "patch pv prole-iscsi-d001-garage" "$KUBECTL_LOG"; then +if ! grep -q "patch pv synology-iscsi-d001-garage" "$KUBECTL_LOG"; then echo "FAILURE: expected PV claimRef recycle (kubectl patch pv) was not invoked" >&2 echo "--- kubectl log ---" >&2 sed -n '1,200p' "$KUBECTL_LOG" >&2 || true diff --git a/tests/etc/test_init_openbao_recycles_released_pv_claimref.sh b/tests/etc/test_init_openbao_recycles_released_pv_claimref.sh index 4ba335b..17ac989 100644 --- a/tests/etc/test_init_openbao_recycles_released_pv_claimref.sh +++ b/tests/etc/test_init_openbao_recycles_released_pv_claimref.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Regression test: OpenBao restart should auto-recycle a Released prole-iscsi PV with a stale +# Regression test: OpenBao restart should auto-recycle a Released synology-iscsi PV with a stale # claimRef when the PVC is Pending and rollout is stuck beyond the initial timeout. set -euo pipefail @@ -77,7 +77,7 @@ case "$sub" in # PV list for recycle helper if [[ "$*" == *"get pv"*"-o jsonpath="* ]]; then printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \ - "prole-iscsi-d001-openbao" "prole-iscsi" "Retain" "Released" "knoe-system" "data-openbao-0" "olduid" + "synology-iscsi-d001-openbao" "synology-iscsi" "Retain" "Released" "knoe-system" "data-openbao-0" "olduid" exit 0 fi exit 0 @@ -116,7 +116,7 @@ else exit 1 fi -if ! grep -q "patch pv prole-iscsi-d001-openbao" "$KUBECTL_LOG"; then +if ! grep -q "patch pv synology-iscsi-d001-openbao" "$KUBECTL_LOG"; then echo "FAILURE: expected PV claimRef recycle (kubectl patch pv) was not invoked" >&2 echo "--- kubectl log ---" >&2 sed -n '1,200p' "$KUBECTL_LOG" >&2 || true diff --git a/tests/test_reconcile_blocked_cluster_state.py b/tests/test_reconcile_blocked_cluster_state.py index b2e874d..70df316 100644 --- a/tests/test_reconcile_blocked_cluster_state.py +++ b/tests/test_reconcile_blocked_cluster_state.py @@ -53,7 +53,7 @@ def test_repair_flow_classifies_blockers_attempts_reclaim_and_avoids_blind_healt "uid": "new-garage-uid", }, "spec": { - "storageClassName": "prole-iscsi", + "storageClassName": "synology-iscsi", "accessModes": ["ReadWriteOnce"], "resources": {"requests": {"storage": "20Gi"}}, }, @@ -66,7 +66,7 @@ def test_repair_flow_classifies_blockers_attempts_reclaim_and_avoids_blind_healt "uid": "new-bao-uid", }, "spec": { - "storageClassName": "prole-iscsi", + "storageClassName": "synology-iscsi", "accessModes": ["ReadWriteOnce"], "resources": {"requests": {"storage": "20Gi"}}, }, @@ -78,9 +78,9 @@ def test_repair_flow_classifies_blockers_attempts_reclaim_and_avoids_blind_healt return { "items": [ { - "metadata": {"name": "prole-iscsi-d001-garage"}, + "metadata": {"name": "synology-iscsi-d001-garage"}, "spec": { - "storageClassName": "prole-iscsi", + "storageClassName": "synology-iscsi", "capacity": {"storage": "20Gi"}, "accessModes": ["ReadWriteOnce"], "claimRef": { @@ -92,9 +92,9 @@ def test_repair_flow_classifies_blockers_attempts_reclaim_and_avoids_blind_healt "status": {"phase": "Released"}, }, { - "metadata": {"name": "prole-iscsi-d001-openbao"}, + "metadata": {"name": "synology-iscsi-d001-openbao"}, "spec": { - "storageClassName": "prole-iscsi", + "storageClassName": "synology-iscsi", "capacity": {"storage": "20Gi"}, "accessModes": ["ReadWriteOnce"], "claimRef": { @@ -184,8 +184,8 @@ def test_repair_flow_classifies_blockers_attempts_reclaim_and_avoids_blind_healt # Reclaim was attempted for matching stale Released PVs. patched = [c for c in calls if c[:3] == ["kubectl", "patch", "pv"]] - assert any("prole-iscsi-d001-garage" in c for c in patched) - assert any("prole-iscsi-d001-openbao" in c for c in patched) + assert any("synology-iscsi-d001-garage" in c for c in patched) + assert any("synology-iscsi-d001-openbao" in c for c in patched) # Explicit scheduling/storage classification is surfaced. assert "Storage blockers" in msg diff --git a/tests/test_repair_stale_released_pv_claimref.py b/tests/test_repair_stale_released_pv_claimref.py index e85962e..ba2eb06 100644 --- a/tests/test_repair_stale_released_pv_claimref.py +++ b/tests/test_repair_stale_released_pv_claimref.py @@ -155,7 +155,7 @@ def test_reset_reclaim_stale_released_pvs_patches_concrete_garage_and_openbao( "uid": "new-garage-uid", }, "spec": { - "storageClassName": "prole-iscsi", + "storageClassName": "synology-iscsi", "accessModes": ["ReadWriteOnce"], "resources": {"requests": {"storage": "10Gi"}}, }, @@ -168,7 +168,7 @@ def test_reset_reclaim_stale_released_pvs_patches_concrete_garage_and_openbao( "uid": "new-bao-uid", }, "spec": { - "storageClassName": "prole-iscsi", + "storageClassName": "synology-iscsi", "accessModes": ["ReadWriteOnce"], "resources": {"requests": {"storage": "10Gi"}}, }, @@ -178,9 +178,9 @@ def test_reset_reclaim_stale_released_pvs_patches_concrete_garage_and_openbao( released_pvs = [ { - "metadata": {"name": "prole-iscsi-d001-garage"}, + "metadata": {"name": "synology-iscsi-d001-garage"}, "spec": { - "storageClassName": "prole-iscsi", + "storageClassName": "synology-iscsi", "capacity": {"storage": "20Gi"}, "accessModes": ["ReadWriteOnce"], "claimRef": { @@ -192,9 +192,9 @@ def test_reset_reclaim_stale_released_pvs_patches_concrete_garage_and_openbao( "status": {"phase": "Released"}, }, { - "metadata": {"name": "prole-iscsi-d001-openbao"}, + "metadata": {"name": "synology-iscsi-d001-openbao"}, "spec": { - "storageClassName": "prole-iscsi", + "storageClassName": "synology-iscsi", "capacity": {"storage": "20Gi"}, "accessModes": ["ReadWriteOnce"], "claimRef": { @@ -237,8 +237,8 @@ def test_reset_reclaim_stale_released_pvs_patches_concrete_garage_and_openbao( assert changed is True patched = [c for c in calls if c[:3] == ["kubectl", "patch", "pv"]] - assert any("prole-iscsi-d001-garage" in c for c in patched) - assert any("prole-iscsi-d001-openbao" in c for c in patched) + assert any("synology-iscsi-d001-garage" in c for c in patched) + assert any("synology-iscsi-d001-openbao" in c for c in patched) assert all('{"spec":{"claimRef":null}}' in c for c in patched) # Reset policy: never delete PVs.