Refactor iSCSI Synology mounts to /synology/d00x

- Rename host mount namespace from /prole/d00x to /synology/d00x

- Make LUN ownership explicit per host in inventory (myrddin=d001; merlin=d002,d004; pi=d003)

- Add safe migration cleanup for legacy /prole/d00x mounts and stale /etc/fstab entries

- Update k3s/ArgoCD/OpenTofu manifests and PV node affinities to match new mount paths

- Improve iSCSI role check-mode behavior and add quiesce/detach flow in ansible.sh
This commit is contained in:
chrisfu 2026-03-21 10:43:06 -07:00
parent 0943186d08
commit 2244d6acd6
29 changed files with 1022 additions and 178 deletions

View File

@ -74,6 +74,13 @@ while [[ $# -gt 0 ]]; do
esac esac
done done
# Default to k3s hosts for the main site run (avoid touching non-k3s Linux/Pi hosts unless explicitly requested)
if [[ -z "${LIMIT}" ]]; then
if [[ "${PLAYBOOK}" =~ (^|/)infrastructure/playbooks/site\.yml$ ]]; then
LIMIT="k3s_hosts"
fi
fi
mkdir -p "${LOG_DIR}" mkdir -p "${LOG_DIR}"
ts="$(date +%Y%m%d-%H%M%S)" ts="$(date +%Y%m%d-%H%M%S)"

View File

@ -1,5 +1,5 @@
; Prole Master Configuration File ; Prole Master Configuration File
; Generated by install.py on 2026-03-20 16:11:36 ; Generated by install.py on 2026-03-20 16:59:08
; This file is used as input for Ansible deployment and k8s cluster creation. ; This file is used as input for Ansible deployment and k8s cluster creation.
[User] [User]
@ -36,7 +36,7 @@ dependencies.opentofu.install = true
dependencies.python.install = true dependencies.python.install = true
dependencies.verify_all = false dependencies.verify_all = false
disk_selection.disk_type = local 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 = disk_selection.removable_mount =
env_setup.NAMESPACE = ${NAMESPACE} env_setup.NAMESPACE = ${NAMESPACE}
env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf
@ -49,9 +49,9 @@ init_cluster.at_rest_encryption_enabled = true
init_cluster.cluster_env = prod init_cluster.cluster_env = prod
init_cluster.deployment_target = prole-prod-cluster init_cluster.deployment_target = prole-prod-cluster
init_cluster.gitops_enabled = false init_cluster.gitops_enabled = false
init_cluster.k3s_server_url = init_cluster.k3s_server_url = https://myrddin.prole.org:6443
init_cluster.k3s_token = init_cluster.k3s_token = ${PROLE_SECRET:v1:byngYfgT7BPkndc5:TukgM2QvPylhorfIm0t-tPysEPq-Lvc8lqbv594nzDsJxc6Kj6J9walkiVQS6o18ZJYNU7xpuYsRWV1yyZW8RBxvPi-glfs7C-sdj6Q3mwia2A9jLcxyJMck3_8z4L-mc7utmo8PIxw0MnjeF8P_Ixzoyi4cM3WoKtZiyA==}
init_cluster.kerberos_enabled = false init_cluster.kerberos_enabled = true
init_cluster.mode = k8s init_cluster.mode = k8s
init_cluster.start_cluster = true init_cluster.start_cluster = true
init_cluster.supabase_enabled = false init_cluster.supabase_enabled = false
@ -65,15 +65,15 @@ init_password.db_password_confirm =
init_password.db_username = root init_password.db_username = root
init_password.generate_ssh_key = true init_password.generate_ssh_key = true
init_scripts.run_scripts = true init_scripts.run_scripts = true
kerberos_config.enabled = false kerberos_config.enabled = true
kerberos_config.kdc = kerberos_config.kdc = 10.0.0.3
kerberos_config.password = ${PROLE_SECRET:v1:3vn_jWdWyDkkLs4m:k5wERNl46EUEa4QReopiBD6daCtPdE8Mkcp2zYyaMu7OMr2d} kerberos_config.password = ${PROLE_SECRET:v1:A6qvhIB8BlPEcQjO:0tFeTyOFqqkn13rROLkgcJ9NgYmVYRD1ZjJzCAeFwfrgSxQx}
kerberos_config.realm = kerberos_config.realm = PROLE.ORG
kerberos_config.test_connection = false kerberos_config.test_connection = false
kerberos_config.user = administrator kerberos_config.user = administrator
network_scan.run = true network_scan.run = true
ollama_config.model = ollama_config.model =
ollama_config.server_host = ollama_config.server_host = fairyland.prole.org
ollama_config.server_port = 11434 ollama_config.server_port = 11434
[Global] [Global]
@ -87,8 +87,9 @@ DOCKER_PRELOAD = false
NAMESPACE = prole-db NAMESPACE = prole-db
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2 OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2
PROLE_DB_USER = root PROLE_DB_USER = root
PROLE_K3S_SERVER = PROLE_HOME = $HOME/dev/prole
PROLE_K3S_TOKEN = PROLE_K3S_SERVER = https://myrddin.prole.org:6443
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:dQZZFvDfN_I8bFPY:yr9laHz55aM5lL_QTnBim1m0xBpX5SgfOPOVp-2HwLdii-2TNnT0gSAuWqSjgwI9JSoMT3QfD2lavVF2qMoWh9SFN8idZ3-VIYRsiZ7SM2BOiS_sbOG3_QDucvciuIxy5MXNyFEmxOGTtXwyzC1cnQjlsiEnKy0XbFF7dA==}
PROLE_OPENTOFU_URL = http://127.0.0.1:8080 PROLE_OPENTOFU_URL = http://127.0.0.1:8080
SERVICE_NAMESPACE = knoe-system SERVICE_NAMESPACE = knoe-system
@ -107,12 +108,18 @@ ANSIBLE_INVENTORY = /Users/chrisfu/dev/prole/infrastructure/inventory
ANSIBLE_REALM = PROLE.ORG 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"]} 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_ANSIBLE_DETECTED = 10.0.0.3
KDC_AUTO_DETECTED = 10.0.0.3
KERBEROS_AUTO_ENABLED = True
[Port Forwards] [Port Forwards]
; No configuration values captured yet for this section. ; No configuration values captured yet for this section.
[System Environment] [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] [Monitoring]
; No configuration values captured yet for this section. ; No configuration values captured yet for this section.
@ -121,7 +128,10 @@ KDC_ANSIBLE_DETECTED = 10.0.0.3
; No configuration values captured yet for this section. ; No configuration values captured yet for this section.
[Ollama] [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] [Optional Features]
; No configuration values captured yet for this section. ; No configuration values captured yet for this section.
@ -144,13 +154,13 @@ MODE = k3d
[Service Cluster (k3s)] [Service Cluster (k3s)]
CLUSTER_ENV = prole-service-cluster CLUSTER_ENV = prole-service-cluster
DISPLAY_NAME = prole-service-cluster DISPLAY_NAME = prole-service-cluster
K3S_SERVER_URL = K3S_SERVER_URL = https://myrddin.prole.org:6443
K3S_TOKEN = K3S_TOKEN = ${PROLE_SECRET:v1:_iFv8sZdDHQxTxd4:a3RRUkKtO9ctIm_393LrEg9Lq9tN1Sfl7B3TZLnSwEXEKgpNnp9VAeicvFy8kV4unSX99yXZPhTf3RbTQ7qaTt4Oj8vU9aFK905qa5BVPOzmAZo_10mTcfXYTOeNXALl4sprStWambtJ4CEYol3XsIXFywzGq1jkldPyLw==}
MODE = k3s MODE = k3s
PIPELINE_URL = http://127.0.0.1:8080 PIPELINE_URL = http://127.0.0.1:8080
[Prod Cluster (k8s)] [Prod Cluster (k8s)]
ARTIFACTS_DIR = /private/var/folders/rt/pywlnmxj3dn7t5552vwdcpp80000gn/T/pytest-of-chrisfu/pytest-26/test_encryption_toggle0/data/staging ARTIFACTS_DIR = /Users/chrisfu/dev/prole/data/staging
CLUSTER_ENV = prole-prod-cluster CLUSTER_ENV = prole-prod-cluster
DISPLAY_NAME = prole-prod-cluster DISPLAY_NAME = prole-prod-cluster
MODE = k8s MODE = k8s

View File

@ -1,5 +1,5 @@
; Prole Master Configuration File ; Prole Master Configuration File
; Generated by install.py on 2026-03-20 16:10:22 ; Generated by install.py on 2026-03-20 17:05:22
; This file is used as input for Ansible deployment and k8s cluster creation. ; This file is used as input for Ansible deployment and k8s cluster creation.
[User] [User]
@ -36,7 +36,7 @@ dependencies.opentofu.install = true
dependencies.python.install = true dependencies.python.install = true
dependencies.verify_all = false dependencies.verify_all = false
disk_selection.disk_type = local 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 = disk_selection.removable_mount =
env_setup.NAMESPACE = ${NAMESPACE} env_setup.NAMESPACE = ${NAMESPACE}
env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf
@ -49,9 +49,9 @@ init_cluster.at_rest_encryption_enabled = true
init_cluster.cluster_env = service init_cluster.cluster_env = service
init_cluster.deployment_target = prole-service-cluster init_cluster.deployment_target = prole-service-cluster
init_cluster.gitops_enabled = false init_cluster.gitops_enabled = false
init_cluster.k3s_server_url = init_cluster.k3s_server_url = https://myrddin.prole.org:6443
init_cluster.k3s_token = init_cluster.k3s_token = ${PROLE_SECRET:v1:s4MdUcnQRJBPdAss:irg5n342LjxLeLV8kGtoAAO_Od3QcmWtTXLQbPw2o2mUJrRnNEBOhN-z4Q6J3UZylEKp6ZhmD_TdRGqdpfpC79j1kFtaTPWCvl_cdHHiG9580AQ87V4XmtAwEh-_ICm4GS35szQHELlXkJH4j7CvfAngW8RW_1nvtLHQyQ==}
init_cluster.kerberos_enabled = false init_cluster.kerberos_enabled = true
init_cluster.mode = k3s init_cluster.mode = k3s
init_cluster.start_cluster = true init_cluster.start_cluster = true
init_cluster.supabase_enabled = false init_cluster.supabase_enabled = false
@ -60,37 +60,37 @@ init_cnpg_deploy.run_deploy = true
init_db_build.run_build = true init_db_build.run_build = true
init_password.db_host_port = 5432 init_password.db_host_port = 5432
init_password.db_namespace = ${NAMESPACE} init_password.db_namespace = ${NAMESPACE}
init_password.db_password = ${OPENBAO:kv/prole/knoe-db/db#password} init_password.db_password =
init_password.db_password_confirm = ${OPENBAO:kv/prole/knoe-db/db#password} init_password.db_password_confirm =
init_password.db_username = root init_password.db_username = root
init_password.generate_ssh_key = true init_password.generate_ssh_key = true
init_scripts.run_scripts = true init_scripts.run_scripts = true
kerberos_config.enabled = false kerberos_config.enabled = true
kerberos_config.kdc = kerberos_config.kdc = 10.0.0.3
kerberos_config.password = ${OPENBAO:kv/prole/knoe-db/kerberos#password} kerberos_config.password = ${PROLE_SECRET:v1:1zuEATgIOtkCPTIi:w2eKBUzce6mq6BJAlefU9cl6K05A9EDT81AQ1uSVzgI1NbUY}
kerberos_config.realm = kerberos_config.realm = PROLE.ORG
kerberos_config.test_connection = false kerberos_config.test_connection = false
kerberos_config.user = administrator kerberos_config.user = administrator
network_scan.run = true network_scan.run = true
ollama_config.model = ollama_config.model =
ollama_config.server_host = ollama_config.server_host = fairyland.prole.org
ollama_config.server_port = 11434 ollama_config.server_port = 11434
[Global] [Global]
; Variables used by name in more than one place or assumed global scope ; Variables used by name in more than one place or assumed global scope
ARGOCD_NAMESPACE = argocd
CLUSTER_ENV = service CLUSTER_ENV = service
DB_HOST_PORT = 5432 DB_HOST_PORT = 5432
DB_PASSWORD = ${OPENBAO:kv/prole/knoe-db/db#password} DB_PASSWORD =
DEPLOYMENT_MODE = k3s DEPLOYMENT_MODE = k3s
DEPLOYMENT_TARGET = prole-service-cluster DEPLOYMENT_TARGET = prole-service-cluster
DOCKER_IMPORT_DIR =
DOCKER_PRELOAD = false DOCKER_PRELOAD = false
NAMESPACE = knoe-db KUBECONTEXT = prole-k3s
NAMESPACE = prole-db
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2 OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2
PROLE_DB_USER = root PROLE_DB_USER = root
PROLE_K3S_SERVER = PROLE_HOME = $HOME/dev/prole
PROLE_K3S_TOKEN = PROLE_K3S_SERVER = https://myrddin.prole.org:6443
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:SNcp3N71DMccQw5G:UXmhfoiN8PKqw3jaarQxhhv5rtPjjUQTRSVo4pS6FkmHqLDt1xghL4RfgBdcaR5HA9lnlDc5jFpLruIrC1ivxk5HssTn1prde0lKvhioO7SkIOSl6HsA4XXosf6KveNASpxCdeZ6RkZlg2i7jpHxvxX3zbIrSKNISN5gEQ==}
PROLE_OPENTOFU_URL = http://127.0.0.1:8080 PROLE_OPENTOFU_URL = http://127.0.0.1:8080
SERVICE_NAMESPACE = knoe-system SERVICE_NAMESPACE = knoe-system
@ -109,12 +109,18 @@ ANSIBLE_INVENTORY = /Users/chrisfu/dev/prole/infrastructure/inventory
ANSIBLE_REALM = PROLE.ORG 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"]} 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_ANSIBLE_DETECTED = 10.0.0.3
KDC_AUTO_DETECTED = 10.0.0.3
KERBEROS_AUTO_ENABLED = True
[Port Forwards] [Port Forwards]
; No configuration values captured yet for this section. ; No configuration values captured yet for this section.
[System Environment] [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] [Monitoring]
; No configuration values captured yet for this section. ; No configuration values captured yet for this section.
@ -123,10 +129,16 @@ KDC_ANSIBLE_DETECTED = 10.0.0.3
; No configuration values captured yet for this section. ; No configuration values captured yet for this section.
[Ollama] [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] [Optional Features]
AT_REST_ENCRYPTION_ENABLED = True
GITOPS_ENABLED = False GITOPS_ENABLED = False
KERBEROS_ENABLED = True
SUPABASE_ENABLED = False
[GitOps] [GitOps]
; No configuration values captured yet for this section. ; No configuration values captured yet for this section.
@ -135,24 +147,26 @@ GITOPS_ENABLED = False
; No configuration values captured yet for this section. ; No configuration values captured yet for this section.
[Initialize Cluster] [Initialize Cluster]
; No configuration values captured yet for this section. ENVIRONMENT = service
K3S_SERVER_URL = https://myrddin.prole.org:6443
K3S_TOKEN = ${PROLE_SECRET:v1:570CIZjbSCH131v_:pDQwbH9nKYsCl5z0f-iAb-cilZlo1KLpI16c6Hgj0Ic81Jk1I7CSjEFo5UJ1ezV_pdat-7kxhaWE_TxCXG6ZnuOA8SBh3xCCi1LsgYFluPwm47b4IaS5ftvMOe5TVWbQfnzsXuU4SdO_3O9mgagKHdDIzu8kufYfpeDyqA==}
[Dev Cluster (k3d)] [Dev Cluster (k3d)]
CLUSTER_ENV = dev CLUSTER_ENV = dev
DISPLAY_NAME = knoe-dev-cluster DISPLAY_NAME = knoe-dev-cluster
KUBECTL_CONTEXT = KUBECTL_CONTEXT = prole-k3s
MODE = k3d MODE = k3d
[Service Cluster (k3s)] [Service Cluster (k3s)]
CLUSTER_ENV = prole-service-cluster CLUSTER_ENV = prole-service-cluster
DISPLAY_NAME = prole-service-cluster DISPLAY_NAME = prole-service-cluster
K3S_SERVER_URL = K3S_SERVER_URL = https://myrddin.prole.org:6443
K3S_TOKEN = K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==}
MODE = k3s MODE = k3s
PIPELINE_URL = http://127.0.0.1:8080 PIPELINE_URL = http://127.0.0.1:8080
[Prod Cluster (k8s)] [Prod Cluster (k8s)]
ARTIFACTS_DIR = /private/var/folders/rt/pywlnmxj3dn7t5552vwdcpp80000gn/T/pytest-of-chrisfu/pytest-25/test_encryption_toggle0/data/staging ARTIFACTS_DIR = /Users/chrisfu/dev/prole/data/staging
CLUSTER_ENV = prole-prod-cluster CLUSTER_ENV = prole-prod-cluster
DISPLAY_NAME = prole-prod-cluster DISPLAY_NAME = prole-prod-cluster
MODE = k8s MODE = k8s

View File

@ -14,7 +14,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d001/data path: /synology/d001/data
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -40,7 +40,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d001/wal path: /synology/d001/wal
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -66,7 +66,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d001/garage path: /synology/d001/garage
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -92,7 +92,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d002/data path: /synology/d002/data
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -100,7 +100,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - merlin.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -118,7 +118,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d002/wal path: /synology/d002/wal
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -126,7 +126,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - merlin.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -144,7 +144,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d003/data path: /synology/d003/data
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -152,7 +152,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - pi.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -170,7 +170,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d003/wal path: /synology/d003/wal
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -178,7 +178,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - pi.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -196,7 +196,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d003/grafana path: /synology/d003/grafana
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -204,7 +204,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - pi.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -222,7 +222,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d002/prometheus path: /synology/d002/prometheus
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -230,7 +230,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - merlin.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -248,7 +248,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d001/openbao path: /synology/d001/openbao
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:

View File

@ -4,7 +4,7 @@ metadata:
name: prole-db name: prole-db
spec: spec:
instances: 3 instances: 3
imageName: myrddin.prole.org:5000/prole-db:18-129 imageName: myrddin.prole.org:5000/prole-db:0.0.0
postgresUID: 100 postgresUID: 100
postgresGID: 101 postgresGID: 101
maxSyncReplicas: 1 maxSyncReplicas: 1

View File

@ -49,6 +49,25 @@ CNPG_MANIFEST_OVERRIDE=${CNPG_MANIFEST_OVERRIDE:-}
PROLE_HOME=${PROLE_HOME:-$(cd "$SCRIPT_DIR/.." && pwd)} PROLE_HOME=${PROLE_HOME:-$(cd "$SCRIPT_DIR/.." && pwd)}
BACKUP_DIR=${BACKUP_DIR:-$PROLE_HOME/prole/backup} BACKUP_DIR=${BACKUP_DIR:-$PROLE_HOME/prole/backup}
BACKUP_WAIT_TIMEOUT=${BACKUP_WAIT_TIMEOUT:-1800} BACKUP_WAIT_TIMEOUT=${BACKUP_WAIT_TIMEOUT:-1800}
# CNPG staged topology controls
# - CNPG_TOPOLOGY=auto (default): use stage-1 pinning if CNPG_STAGE1_NODE exists, otherwise label-based
# - CNPG_TOPOLOGY=stage1: pin CNPG pods to CNPG_STAGE1_NODE and relax anti-affinity to preferred
# - CNPG_TOPOLOGY=label: keep manifest label-based and keep anti-affinity as-is
CNPG_TOPOLOGY=${CNPG_TOPOLOGY:-auto}
CNPG_STAGE1_NODE=${CNPG_STAGE1_NODE:-myrddin.prole.org}
CNPG_DB_NODE_SELECTOR=${CNPG_DB_NODE_SELECTOR:-}
CNPG_ALLOW_COLOCATED_INSTANCES=${CNPG_ALLOW_COLOCATED_INSTANCES:-}
CNPG_POD_ANTI_AFFINITY_TYPE=${CNPG_POD_ANTI_AFFINITY_TYPE:-}
# Barman plugin identity (used for readiness checks)
BARMAN_PLUGIN_NAME=${BARMAN_PLUGIN_NAME:-barman-cloud.cloudnative-pg.io}
# Barman readiness timeouts (seconds). Keep them overridable for testability.
BARMAN_CRD_TIMEOUT=${BARMAN_CRD_TIMEOUT:-120}
BARMAN_TLS_TIMEOUT=${BARMAN_TLS_TIMEOUT:-180}
BARMAN_DEPLOY_TIMEOUT=${BARMAN_DEPLOY_TIMEOUT:-180}
BARMAN_SERVICE_TIMEOUT=${BARMAN_SERVICE_TIMEOUT:-180}
if [[ "${PROLE_MODE:-}" == "k3s" ]]; then if [[ "${PROLE_MODE:-}" == "k3s" ]]; then
CNPG_WAIT_TIMEOUT=${CNPG_WAIT_TIMEOUT:-900} CNPG_WAIT_TIMEOUT=${CNPG_WAIT_TIMEOUT:-900}
else else
@ -919,12 +938,8 @@ wait_for_barman_tls_secrets() {
start_time=$(date +%s) start_time=$(date +%s)
echo "Waiting for Barman Cloud TLS secrets (timeout: ${timeout}s)..." echo "Waiting for Barman Cloud TLS secrets (timeout: ${timeout}s)..."
while true; do while true; do
local client_crt client_key server_crt server_key if kubectl -n cnpg-system get secret barman-cloud-client-tls >/dev/null 2>&1 \
client_crt=$(kubectl -n cnpg-system get secret barman-cloud-client-tls -o jsonpath='{.data.tls\.crt}' 2>/dev/null || true) && kubectl -n cnpg-system get secret barman-cloud-server-tls >/dev/null 2>&1; then
client_key=$(kubectl -n cnpg-system get secret barman-cloud-client-tls -o jsonpath='{.data.tls\.key}' 2>/dev/null || true)
server_crt=$(kubectl -n cnpg-system get secret barman-cloud-server-tls -o jsonpath='{.data.tls\.crt}' 2>/dev/null || true)
server_key=$(kubectl -n cnpg-system get secret barman-cloud-server-tls -o jsonpath='{.data.tls\.key}' 2>/dev/null || true)
if [[ -n "$client_crt" && -n "$client_key" && -n "$server_crt" && -n "$server_key" ]]; then
echo "Barman Cloud TLS secrets are available." echo "Barman Cloud TLS secrets are available."
return 0 return 0
fi fi
@ -940,6 +955,46 @@ wait_for_barman_tls_secrets() {
done done
} }
wait_for_barman_plugin_service_registration() {
local timeout=${1:-180}
local start_time
start_time=$(date +%s)
echo "Waiting for Barman plugin service registration (timeout: ${timeout}s)..."
while true; do
local svc_name client_secret server_secret plugin_port endpoints
svc_name=$(kubectl -n cnpg-system get svc -l "cnpg.io/pluginName=$BARMAN_PLUGIN_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
if [[ -z "$svc_name" ]]; then
svc_name=$(kubectl -n cnpg-system get svc barman-cloud -o jsonpath='{.metadata.name}' 2>/dev/null || true)
fi
if [[ -n "$svc_name" ]]; then
client_secret=$(kubectl -n cnpg-system get svc "$svc_name" -o jsonpath='{.metadata.annotations.cnpg\.io/pluginClientSecret}' 2>/dev/null || true)
server_secret=$(kubectl -n cnpg-system get svc "$svc_name" -o jsonpath='{.metadata.annotations.cnpg\.io/pluginServerSecret}' 2>/dev/null || true)
plugin_port=$(kubectl -n cnpg-system get svc "$svc_name" -o jsonpath='{.metadata.annotations.cnpg\.io/pluginPort}' 2>/dev/null || true)
endpoints=$(kubectl -n cnpg-system get endpoints "$svc_name" -o jsonpath='{.subsets[*].addresses[*].ip}' 2>/dev/null || true)
if [[ -n "$client_secret" && -n "$server_secret" && -n "$plugin_port" && -n "$endpoints" ]]; then
if kubectl -n cnpg-system get secret "$client_secret" >/dev/null 2>&1 && kubectl -n cnpg-system get secret "$server_secret" >/dev/null 2>&1; then
echo "Barman plugin service registration is ready (service=${svc_name}, port=${plugin_port})."
return 0
fi
fi
fi
local elapsed=$(( $(date +%s) - start_time ))
if (( elapsed > timeout )); then
echo "Barman plugin service registration not ready after ${elapsed}s." >&2
kubectl -n cnpg-system get svc,ep 2>/dev/null >&2 || true
return 1
fi
if (( elapsed % 30 < 6 && elapsed > 5 )); then
echo " [${elapsed}s/${timeout}s] Still waiting for barman plugin service registration..."
fi
sleep 5
done
}
ensure_barman_plugin() { ensure_barman_plugin() {
ensure_cert_manager ensure_cert_manager
@ -955,25 +1010,28 @@ ensure_barman_plugin() {
kubectl -n cert-manager rollout status deploy/cert-manager-webhook --timeout=180s >/dev/null 2>&1 || true kubectl -n cert-manager rollout status deploy/cert-manager-webhook --timeout=180s >/dev/null 2>&1 || true
kubectl -n cert-manager rollout status deploy/cert-manager --timeout=180s >/dev/null 2>&1 || true kubectl -n cert-manager rollout status deploy/cert-manager --timeout=180s >/dev/null 2>&1 || true
kubectl -n cert-manager rollout status deploy/cert-manager-cainjector --timeout=180s >/dev/null 2>&1 || true kubectl -n cert-manager rollout status deploy/cert-manager-cainjector --timeout=180s >/dev/null 2>&1 || true
kubectl apply -f "$plugin_url" || true if ! kubectl apply -f "$plugin_url" >/dev/null 2>&1; then
echo "ERROR: Failed to apply Barman Cloud plugin manifest after cert-manager restart." >&2
return 1
fi
else
echo "ERROR: Failed to apply Barman Cloud plugin manifest." >&2
return 1
fi fi
else else
printf '%s\n' "$apply_out" printf '%s\n' "$apply_out"
fi fi
if ! wait_for_barman_crd 120; then wait_for_barman_crd "$BARMAN_CRD_TIMEOUT"
echo "WARN: Barman Cloud ObjectStore CRD not ready after install." >&2 wait_for_barman_tls_secrets "$BARMAN_TLS_TIMEOUT"
fi
if ! wait_for_barman_tls_secrets 180; then
echo "WARN: Barman Cloud TLS secrets not ready after install." >&2
fi
pin_barman_cloud pin_barman_cloud
if kubectl -n cnpg-system get deploy barman-cloud >/dev/null 2>&1; then if kubectl -n cnpg-system get deploy barman-cloud >/dev/null 2>&1; then
kubectl -n cnpg-system rollout status deploy/barman-cloud --timeout=180s || true kubectl -n cnpg-system rollout status deploy/barman-cloud --timeout="${BARMAN_DEPLOY_TIMEOUT}s"
fi fi
wait_for_barman_plugin_service_registration "$BARMAN_SERVICE_TIMEOUT"
} }
pin_cnpg_controller() { pin_cnpg_controller() {
@ -1415,8 +1473,8 @@ ensure_prole_stack_resources() {
done done
prole_render_manifest "$dir/ingress.yaml" | kubectl_apply_retry "" || true prole_render_manifest "$dir/ingress.yaml" | kubectl_apply_retry "" || true
fi fi
apply_barman_objectstore_if_present
apply_cnpg_cluster_manifest "$CNPG_MANIFEST_OVERRIDE" apply_cnpg_cluster_manifest "$CNPG_MANIFEST_OVERRIDE"
ensure_cnpg_cluster_scheduling || true
validate_cnpg_runtime_storage validate_cnpg_runtime_storage
reconcile_cnpg_instances reconcile_cnpg_instances
else else
@ -1456,8 +1514,8 @@ ensure_prole_stack_resources() {
done done
prole_render_manifest "$dir/ingress.yaml" | kubectl_apply_retry "" || true prole_render_manifest "$dir/ingress.yaml" | kubectl_apply_retry "" || true
fi fi
apply_barman_objectstore_if_present
apply_cnpg_cluster_manifest "$CNPG_MANIFEST" apply_cnpg_cluster_manifest "$CNPG_MANIFEST"
ensure_cnpg_cluster_scheduling || true
validate_cnpg_runtime_storage validate_cnpg_runtime_storage
reconcile_cnpg_instances reconcile_cnpg_instances
fi fi
@ -1492,15 +1550,125 @@ _coerce_uint() {
fi fi
} }
_cnpg_node_exists() {
local node_name="${1:-}"
[[ -n "$node_name" ]] || return 1
local out
out=$(kubectl get node "$node_name" -o jsonpath='{.metadata.name}' 2>/dev/null || true)
[[ "$out" == "$node_name" ]]
}
_cnpg_effective_topology() {
local t="${CNPG_TOPOLOGY:-auto}"
case "$t" in
stage1|stage-1|stage1-single-node|single-node)
echo "stage1"
return 0
;;
label|labels|label-db)
echo "label"
return 0
;;
auto|'')
if _cnpg_node_exists "${CNPG_STAGE1_NODE:-myrddin.prole.org}"; then
echo "stage1"
else
echo "label"
fi
return 0
;;
*)
echo "label"
return 0
;;
esac
}
_cnpg_effective_node_selector() {
if [[ -n "${CNPG_DB_NODE_SELECTOR:-}" ]]; then
echo "$CNPG_DB_NODE_SELECTOR"
return 0
fi
if [[ "$(_cnpg_effective_topology)" == "stage1" ]]; then
echo "kubernetes.io/hostname=${CNPG_STAGE1_NODE:-myrddin.prole.org}"
return 0
fi
echo "${CNPG_NODE_ROLE_LABEL_KEY:-prole.org/node-role}=${CNPG_NODE_ROLE_LABEL_VALUE:-db}"
}
_cnpg_allow_colocated_instances() {
local v="${CNPG_ALLOW_COLOCATED_INSTANCES:-}"
if [[ -n "$v" ]]; then
local v_lc
v_lc=$(printf '%s' "$v" | tr '[:upper:]' '[:lower:]')
case "$v_lc" in
1|true|yes|y) return 0 ;;
0|false|no|n) return 1 ;;
esac
fi
[[ "$(_cnpg_effective_topology)" == "stage1" ]]
}
patch_cnpg_manifest_for_topology() {
# Best-effort patch for stage-1 (single-node) scheduling: pin to CNPG_STAGE1_NODE and
# relax anti-affinity so all instances can schedule on one node.
local manifest_file="$1"
[[ -f "$manifest_file" ]] || return 0
if [[ "$(_cnpg_effective_topology)" != "stage1" ]]; then
return 0
fi
local host anti
host="${CNPG_STAGE1_NODE:-myrddin.prole.org}"
anti="${CNPG_POD_ANTI_AFFINITY_TYPE:-preferred}"
local tmp
tmp=$(mktemp -t prole-cnpg-topo.XXXXXX)
sed -E \
-e "s/^([[:space:]]*podAntiAffinityType:)[[:space:]]*required[[:space:]]*$/\\1 ${anti}/" \
-e 's/^([[:space:]]*-[[:space:]]*key:)[[:space:]]*prole\.org\/node-role[[:space:]]*$/\1 kubernetes.io\/hostname/' \
-e "s/^([[:space:]]*-[[:space:]]*)db[[:space:]]*$/\\1${host}/" \
"$manifest_file" >"$tmp"
mv "$tmp" "$manifest_file"
}
ensure_cnpg_cluster_scheduling() {
# Enforce the effective topology on the live Cluster object.
if ! kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then
return 0
fi
local selector key val topo anti
topo="$(_cnpg_effective_topology)"
selector="$(_cnpg_effective_node_selector)"
key="${selector%%=*}"
val="${selector#*=}"
anti="${CNPG_POD_ANTI_AFFINITY_TYPE:-}"
if [[ -z "$anti" ]]; then
if [[ "$topo" == "stage1" || "$(_cnpg_allow_colocated_instances && echo 1 || echo 0)" == "1" ]]; then
anti="preferred"
else
anti="required"
fi
fi
kubectl -n "$NAMESPACE" patch cluster "$CNPG_CLUSTER_NAME" --type merge -p "{\"spec\":{\"affinity\":{\"enablePodAntiAffinity\":true,\"podAntiAffinityType\":\"${anti}\",\"topologyKey\":\"kubernetes.io/hostname\",\"nodeAffinity\":{\"requiredDuringSchedulingIgnoredDuringExecution\":{\"nodeSelectorTerms\":[{\"matchExpressions\":[{\"key\":\"${key}\",\"operator\":\"In\",\"values\":[\"${val}\"]}]}]}}}}}}" >/dev/null 2>&1 || true
}
_ready_schedulable_nodes_count() { _ready_schedulable_nodes_count() {
kubectl get nodes --no-headers 2>/dev/null \ kubectl get nodes --no-headers 2>/dev/null \
| awk '$2 ~ /^Ready/ && $2 !~ /SchedulingDisabled/ {c++} END {print c+0}' | awk '$2 ~ /^Ready/ && $2 !~ /SchedulingDisabled/ {c++} END {print c+0}'
} }
_ready_schedulable_db_nodes_count() { _ready_schedulable_db_nodes_count() {
local key="${CNPG_NODE_ROLE_LABEL_KEY:-prole.org/node-role}" local selector
local val="${CNPG_NODE_ROLE_LABEL_VALUE:-db}" selector=$(_cnpg_effective_node_selector)
kubectl get nodes -l "${key}=${val}" --no-headers 2>/dev/null \ kubectl get nodes -l "$selector" --no-headers 2>/dev/null \
| awk '$2 ~ /^Ready/ && $2 !~ /SchedulingDisabled/ {c++} END {print c+0}' | awk '$2 ~ /^Ready/ && $2 !~ /SchedulingDisabled/ {c++} END {print c+0}'
} }
@ -1515,17 +1683,28 @@ reconcile_cnpg_instances() {
[[ -z "$current" ]] && return 0 [[ -z "$current" ]] && return 0
current=$(_coerce_uint "$current" 0) current=$(_coerce_uint "$current" 0)
local allow_colocated=0
if _cnpg_allow_colocated_instances; then
allow_colocated=1
fi
local min_required local min_required
min_required=$(_coerce_uint "${OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES:-2}" 2) min_required=$(_coerce_uint "${OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES:-2}" 2)
if (( min_required < 1 )); then if (( min_required < 1 )); then
min_required=1 min_required=1
fi fi
# Stage-1 single-node evaluation is an explicit mode; do not force scale-down
# solely due to node-count heuristics.
if (( allow_colocated == 1 )); then
min_required=1
fi
local ready_nodes eligible_db_nodes local ready_nodes eligible_nodes selector
selector=$(_cnpg_effective_node_selector)
ready_nodes=$(_coerce_uint "$(_ready_schedulable_nodes_count)" 0) ready_nodes=$(_coerce_uint "$(_ready_schedulable_nodes_count)" 0)
eligible_db_nodes=$(_coerce_uint "$(_ready_schedulable_db_nodes_count)" 0) eligible_nodes=$(_coerce_uint "$(_ready_schedulable_db_nodes_count)" 0)
if (( eligible_db_nodes < 1 )); then if (( eligible_nodes < 1 )); then
echo "ERROR: No Ready, schedulable CNPG DB nodes found (label ${CNPG_NODE_ROLE_LABEL_KEY:-prole.org/node-role}=${CNPG_NODE_ROLE_LABEL_VALUE:-db})." >&2 echo "ERROR: No Ready, schedulable CNPG eligible nodes found (selector ${selector})." >&2
echo "Refusing to force CNPG pods onto an arbitrary node during bootstrap." >&2 echo "Refusing to force CNPG pods onto an arbitrary node during bootstrap." >&2
return 1 return 1
fi fi
@ -1535,13 +1714,17 @@ reconcile_cnpg_instances() {
if (( desired < 1 )); then if (( desired < 1 )); then
desired=1 desired=1
fi fi
if (( desired > eligible_db_nodes )); then if (( allow_colocated == 0 )); then
desired=$eligible_db_nodes if (( desired > eligible_nodes )); then
desired=$eligible_nodes
fi
fi fi
# Degraded cluster: keep instances at 1 until enough nodes are Ready+schedulable. # Degraded cluster: keep instances at 1 until enough nodes are Ready+schedulable.
if (( ready_nodes < min_required )); then if (( allow_colocated == 0 )); then
desired=1 if (( ready_nodes < min_required )); then
desired=1
fi
fi fi
if (( desired < 1 )); then if (( desired < 1 )); then
@ -1569,7 +1752,7 @@ reconcile_cnpg_instances() {
return 0 return 0
fi fi
echo "Reconciling CNPG cluster '$CNPG_CLUSTER_NAME' instances=${desired} (was ${current}; ready_nodes=${ready_nodes}; eligible_db_nodes=${eligible_db_nodes}) ..." echo "Reconciling CNPG cluster '$CNPG_CLUSTER_NAME' instances=${desired} (was ${current}; ready_nodes=${ready_nodes}; eligible_nodes=${eligible_nodes}; allow_colocated=${allow_colocated}) ..."
# CloudNativePG validates `maxSyncReplicas < instances`. When we temporarily # CloudNativePG validates `maxSyncReplicas < instances`. When we temporarily
# scale down to a single instance during degraded startup, we must also # scale down to a single instance during degraded startup, we must also
@ -1619,6 +1802,9 @@ apply_cnpg_cluster_manifest() {
local tmp local tmp
tmp=$(mktemp -t prole-cnpg.XXXXXX) tmp=$(mktemp -t prole-cnpg.XXXXXX)
prole_render_manifest "$manifest" >"$tmp" prole_render_manifest "$manifest" >"$tmp"
patch_cnpg_manifest_for_topology "$tmp" || true
if ! validate_cnpg_manifest_storage "$tmp"; then if ! validate_cnpg_manifest_storage "$tmp"; then
rm -f "$tmp" rm -f "$tmp"
return 1 return 1
@ -2109,6 +2295,100 @@ reset_and_reinit() {
wait_for_cnpg_pods "${CNPG_WAIT_TIMEOUT}" wait_for_cnpg_pods "${CNPG_WAIT_TIMEOUT}"
} }
destroy_cnpg_cluster() {
# Destroy only CNPG + backup resources (not the entire prole stack).
# This is intended to support repeatable destroy/recreate/restore storage evaluation.
local delete_pvcs
delete_pvcs=$(_coerce_uint "${CNPG_DESTROY_DELETE_PVCS:-1}" 1)
# IMPORTANT: The recovery template uses `bootstrap.recovery.backup.name`, which requires the
# Backup CR to exist at restore time. By default we keep Backup/ObjectStore CRs.
local delete_backup_crs delete_objectstore_crs
delete_backup_crs=$(_coerce_uint "${CNPG_DESTROY_DELETE_BACKUP_CRS:-0}" 0)
delete_objectstore_crs=$(_coerce_uint "${CNPG_DESTROY_DELETE_OBJECTSTORE_CRS:-0}" 0)
echo "Destroying CNPG cluster '$CNPG_CLUSTER_NAME' in namespace '$NAMESPACE' ..."
kubectl -n "$NAMESPACE" delete scheduledbackup --all --ignore-not-found 2>/dev/null || true
if (( delete_backup_crs == 1 )); then
kubectl -n "$NAMESPACE" delete backup --all --ignore-not-found 2>/dev/null || true
fi
if (( delete_objectstore_crs == 1 )); then
kubectl -n "$NAMESPACE" delete objectstore --all --ignore-not-found 2>/dev/null || true
fi
kubectl -n "$NAMESPACE" delete cluster "$CNPG_CLUSTER_NAME" --ignore-not-found
kubectl -n "$NAMESPACE" wait --for=delete pod -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" --timeout=180s >/dev/null 2>&1 || true
if (( delete_pvcs == 1 )); then
echo "Deleting PVCs for CNPG cluster '$CNPG_CLUSTER_NAME' (label cnpg.io/cluster=$CNPG_CLUSTER_NAME) ..."
kubectl -n "$NAMESPACE" delete pvc -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" --ignore-not-found 2>/dev/null || true
else
echo "Leaving PVCs intact (CNPG_DESTROY_DELETE_PVCS=0)."
fi
recycle_released_prole_iscsi_pvs || true
}
restore_cnpg_cluster() {
ensure_tools
ensure_namespace
ensure_cnpg_operator
pin_cnpg_controller
ensure_barman_plugin
ensure_prole_protected_storage
if kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then
echo "ERROR: CNPG cluster '$CNPG_CLUSTER_NAME' already exists in namespace '$NAMESPACE'." >&2
echo "Run '$0 destroy' first (or delete the cluster) before restoring." >&2
return 1
fi
local backup_name
backup_name="${BACKUP_NAME:-}"
if [[ -z "$backup_name" ]]; then
backup_name=$(latest_completed_backup_name)
fi
if [[ -z "$backup_name" ]]; then
echo "ERROR: No completed backups found for restore (set BACKUP_NAME explicitly if needed)." >&2
return 1
fi
if [[ ! -f "$RECOVERY_TEMPLATE" ]]; then
echo "ERROR: Recovery template not found: $RECOVERY_TEMPLATE" >&2
return 1
fi
local image
if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then
image=$(get_latest_image)
else
image="prole-db:$VERSION"
fi
image=$(resolve_cnpg_image "$image")
local tmp_manifest
tmp_manifest=$(mktemp -t prole-cnpg-restore.XXXXXX)
sed \
-e "s/{{BACKUP_NAME}}/${backup_name}/g" \
-e "s|^ imageName:.*| imageName: ${image}|" \
"$RECOVERY_TEMPLATE" >"$tmp_manifest"
echo "Restoring CNPG cluster '$CNPG_CLUSTER_NAME' from backup '$backup_name' using image '$image' ..."
apply_cnpg_cluster_manifest "$tmp_manifest"
ensure_cnpg_cluster_scheduling || true
validate_cnpg_runtime_storage
reconcile_cnpg_instances
rm -f "$tmp_manifest"
if ! wait_for_cnpg_pods "${CNPG_WAIT_TIMEOUT}"; then
echo "ERROR: CNPG pods did not become ready after restore." >&2
return 1
fi
echo "Ensuring CNPG continuous backups after restore (ObjectStore + plugin config + ScheduledBackup + first successful backup) ..."
"$SCRIPT_DIR/init_cnpg_backup.sh" start
}
recycle_released_prole_iscsi_pvs() { recycle_released_prole_iscsi_pvs() {
# StorageClass `prole-iscsi` uses `Retain` PV reclaim policy. After a CNPG reset, PVs can remain # StorageClass `prole-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 `Released` with a stale `claimRef`, which prevents new PVCs (same names) from binding.
@ -2522,6 +2802,7 @@ deploy_cluster() {
return 1 return 1
fi fi
apply_cnpg_cluster_manifest "$manifest" apply_cnpg_cluster_manifest "$manifest"
ensure_cnpg_cluster_scheduling || true
validate_cnpg_runtime_storage validate_cnpg_runtime_storage
reconcile_cnpg_instances reconcile_cnpg_instances
@ -2548,10 +2829,8 @@ case "$ACTION" in
;; ;;
recreate) recreate)
ensure_tools ensure_tools
"$0" delete "$CNPG_CLUSTER_NAME" ensure_namespace
# Clean up PVCs left behind by the CNPG operator (not in static manifests) destroy_cnpg_cluster
echo "Cleaning up PVCs in namespace '$NAMESPACE' ..."
kubectl -n "$NAMESPACE" delete pvc --all --ignore-not-found 2>/dev/null || true
# Wait briefly for pods to fully terminate before re-creating # Wait briefly for pods to fully terminate before re-creating
echo "Waiting for pods to terminate in namespace '$NAMESPACE' ..." echo "Waiting for pods to terminate in namespace '$NAMESPACE' ..."
_wait_term=0 _wait_term=0
@ -2565,6 +2844,14 @@ case "$ACTION" in
done done
"$0" create "$CNPG_CLUSTER_NAME" "$0" create "$CNPG_CLUSTER_NAME"
;; ;;
destroy)
ensure_tools
ensure_namespace
destroy_cnpg_cluster
;;
restore)
restore_cnpg_cluster
;;
create) create)
ensure_tools ensure_tools
ensure_namespace ensure_namespace
@ -2593,13 +2880,13 @@ case "$ACTION" in
ensure_namespace ensure_namespace
ensure_cnpg_operator ensure_cnpg_operator
pin_cnpg_controller pin_cnpg_controller
ensure_barman_plugin
if [[ ! -f "$CNPG_MANIFEST" ]]; then if [[ ! -f "$CNPG_MANIFEST" ]]; then
echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2 echo "ERROR: CNPG manifest not found at $CNPG_MANIFEST" >&2
exit 1 exit 1
fi fi
echo "Starting CloudNative-PG cluster from $CNPG_MANIFEST in namespace $NAMESPACE..." echo "Starting CloudNative-PG cluster from $CNPG_MANIFEST in namespace $NAMESPACE..."
prole_render_manifest "$CNPG_MANIFEST" | kubectl apply -n "$NAMESPACE" -f - apply_cnpg_cluster_manifest "$CNPG_MANIFEST"
ensure_cnpg_cluster_scheduling || true
reconcile_cnpg_instances reconcile_cnpg_instances
;; ;;
stop) stop)

View File

@ -48,6 +48,8 @@ BARMAN_OBJECT_NAME=${BARMAN_OBJECT_NAME:-prole-db-barman-objectstore}
BACKUP_STATUS_TIMEOUT=${BACKUP_STATUS_TIMEOUT:-600} BACKUP_STATUS_TIMEOUT=${BACKUP_STATUS_TIMEOUT:-600}
BACKUP_STATUS_INTERVAL=${BACKUP_STATUS_INTERVAL:-10} BACKUP_STATUS_INTERVAL=${BACKUP_STATUS_INTERVAL:-10}
PLUGIN_READY_TIMEOUT=${PLUGIN_READY_TIMEOUT:-180} PLUGIN_READY_TIMEOUT=${PLUGIN_READY_TIMEOUT:-180}
OBJECTSTORE_READY_TIMEOUT=${OBJECTSTORE_READY_TIMEOUT:-180}
OBJECTSTORE_READY_INTERVAL=${OBJECTSTORE_READY_INTERVAL:-5}
SCHEDULED_BACKUP_NAME=${SCHEDULED_BACKUP_NAME:-prole-db-scheduled-backup} SCHEDULED_BACKUP_NAME=${SCHEDULED_BACKUP_NAME:-prole-db-scheduled-backup}
SCHEDULED_BACKUP_CRON=${SCHEDULED_BACKUP_CRON:-"0 3 * * *"} SCHEDULED_BACKUP_CRON=${SCHEDULED_BACKUP_CRON:-"0 3 * * *"}
@ -470,6 +472,94 @@ wait_for_plugin_ready() {
done done
} }
wait_for_barman_plugin_infra_ready() {
# Ensure the plugin controller and its registration artifacts (Service annotations + TLS secrets)
# are present *before* patching the CNPG Cluster to depend on the plugin.
local start_time now
local deployment_rows ns ready replicas
local plugin_deploy_ready plugin_ns svc_ns svc_name client_secret server_secret plugin_port
start_time=$(date +%s)
while true; do
plugin_deploy_ready=0
plugin_ns=""
deployment_rows=$(kubectl get deployment -A -l app.kubernetes.io/name=barman-cloud -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.status.readyReplicas}{"\t"}{.status.replicas}{"\n"}{end}' 2>/dev/null || true)
if [[ -z "$deployment_rows" ]]; then
deployment_rows=$(kubectl get deployment -A -o jsonpath='{range .items[?(@.metadata.name=="barman-cloud")]}{.metadata.namespace}{"\t"}{.status.readyReplicas}{"\t"}{.status.replicas}{"\n"}{end}' 2>/dev/null || true)
fi
while IFS=$'\t' read -r ns ready replicas; do
[[ -z "$ns" ]] && continue
ready=${ready:-0}
replicas=${replicas:-0}
if (( ready >= 1 && replicas >= 1 )); then
plugin_deploy_ready=1
plugin_ns="$ns"
break
fi
done <<< "$deployment_rows"
if (( plugin_deploy_ready == 1 )); then
svc_ns="${plugin_ns:-$CNPG_OPERATOR_NAMESPACE}"
svc_name=$(kubectl -n "$svc_ns" get svc -l "cnpg.io/pluginName=$BARMAN_PLUGIN_NAME" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
if [[ -z "$svc_name" ]]; then
svc_name=$(kubectl -n "$svc_ns" get svc barman-cloud -o jsonpath='{.metadata.name}' 2>/dev/null || true)
fi
if [[ -n "$svc_name" ]]; then
client_secret=$(kubectl -n "$svc_ns" get svc "$svc_name" -o jsonpath='{.metadata.annotations.cnpg\.io/pluginClientSecret}' 2>/dev/null || true)
server_secret=$(kubectl -n "$svc_ns" get svc "$svc_name" -o jsonpath='{.metadata.annotations.cnpg\.io/pluginServerSecret}' 2>/dev/null || true)
plugin_port=$(kubectl -n "$svc_ns" get svc "$svc_name" -o jsonpath='{.metadata.annotations.cnpg\.io/pluginPort}' 2>/dev/null || true)
if [[ -n "$client_secret" && -n "$server_secret" && -n "$plugin_port" ]]; then
if kubectl -n "$svc_ns" get secret "$client_secret" >/dev/null 2>&1 && kubectl -n "$svc_ns" get secret "$server_secret" >/dev/null 2>&1; then
return 0
fi
fi
fi
fi
now=$(date +%s)
if (( now - start_time >= PLUGIN_READY_TIMEOUT )); then
echo "ERROR: Timed out waiting for Barman plugin infrastructure to become ready." >&2
return 1
fi
echo "Waiting for Barman plugin infrastructure (deployment + service registration) ..."
sleep 5
done
}
wait_for_objectstore_ready() {
local timeout=${1:-$OBJECTSTORE_READY_TIMEOUT}
local start_time now elapsed
start_time=$(date +%s)
echo "Waiting for ObjectStore '$BARMAN_OBJECT_NAME' to be Ready (timeout: ${timeout}s)..."
while true; do
local ready_cond phase
ready_cond=$(kubectl -n "$NAMESPACE" get objectstore "$BARMAN_OBJECT_NAME" \
-o jsonpath='{range .status.conditions[?(@.type=="Ready")]}{.status}{end}' 2>/dev/null || true)
phase=$(kubectl -n "$NAMESPACE" get objectstore "$BARMAN_OBJECT_NAME" \
-o jsonpath='{.status.phase}' 2>/dev/null || true)
if [[ "$ready_cond" == "True" || "$ready_cond" == "true" || "$phase" == "Ready" || "$phase" == "ready" ]]; then
return 0
fi
now=$(date +%s)
elapsed=$((now - start_time))
if (( elapsed >= timeout )); then
echo "ERROR: Timed out waiting for ObjectStore '$BARMAN_OBJECT_NAME' to become Ready." >&2
kubectl -n "$NAMESPACE" get objectstore "$BARMAN_OBJECT_NAME" -o yaml 2>/dev/null >&2 || true
return 1
fi
sleep "$OBJECTSTORE_READY_INTERVAL"
done
}
has_successful_base_backup() { has_successful_base_backup() {
local rows name phase method backup_type phase_lc method_lc backup_type_lc local rows name phase method backup_type phase_lc method_lc backup_type_lc
rows=$(kubectl get backup -n "$NAMESPACE" -o jsonpath="{range .items[?(@.spec.cluster.name=='$CNPG_CLUSTER_NAME')]}{.metadata.name}{\"\t\"}{.status.phase}{\"\t\"}{.spec.method}{\"\t\"}{.spec.pluginConfiguration.parameters.backupType}{\"\n\"}{end}" 2>/dev/null || true) rows=$(kubectl get backup -n "$NAMESPACE" -o jsonpath="{range .items[?(@.spec.cluster.name=='$CNPG_CLUSTER_NAME')]}{.metadata.name}{\"\t\"}{.status.phase}{\"\t\"}{.spec.method}{\"\t\"}{.spec.pluginConfiguration.parameters.backupType}{\"\n\"}{end}" 2>/dev/null || true)
@ -619,7 +709,9 @@ case "$ACTION" in
wait_for_apiserver_ready 180 wait_for_apiserver_ready 180
ensure_cluster ensure_cluster
ensure_garage_bucket_and_key ensure_garage_bucket_and_key
wait_for_barman_plugin_infra_ready
apply_barman_object_store apply_barman_object_store
wait_for_objectstore_ready
ensure_barman_plugin_config ensure_barman_plugin_config
ensure_cluster_backup_config ensure_cluster_backup_config
wait_for_plugin_ready wait_for_plugin_ready
@ -642,7 +734,9 @@ case "$ACTION" in
ensure_namespace ensure_namespace
wait_for_apiserver_ready 180 wait_for_apiserver_ready 180
ensure_cluster ensure_cluster
wait_for_barman_plugin_infra_ready
apply_barman_object_store apply_barman_object_store
wait_for_objectstore_ready
ensure_barman_plugin_config ensure_barman_plugin_config
ensure_cluster_backup_config ensure_cluster_backup_config
wait_for_plugin_ready wait_for_plugin_ready

View File

@ -49,11 +49,26 @@ k3s_service_node_labels:
- "prole.org/node-role=general" - "prole.org/node-role=general"
k3s_node_taints: [] k3s_node_taints: []
k3s_required_mounts:
- /synology/d002
- /synology/d004
# iSCSI # iSCSI
# `/var/lib/rancher` is local host storage (do not manage it via iSCSI). # `/var/lib/rancher` is local host storage (do not manage it via iSCSI).
iscsi_portal: 10.0.0.203:3260 iscsi_portal: 10.0.0.203:3260
iscsi_targets: iscsi_targets:
# PROLE-DATA-2
- iqn: "iqn.2000-01.com.synology:synology.Target-12.292d45194a1"
chap_user: "prole"
chap_password: "{{ vault_iscsi_prole_password }}"
mounts:
- name: d002
path: /synology/d002
fstype: xfs
opts: "_netdev,noatime"
src: "UUID=fe087fb0-a321-4767-b838-b4385e81693e"
# PROLE-DATA-4 # PROLE-DATA-4
# Repurposed from myrddin's former `/var/lib/rancher` Synology LUN. # Repurposed from myrddin's former `/var/lib/rancher` Synology LUN.
- iqn: "iqn.2000-01.com.synology:synology.Target-15.292d45194a1" - iqn: "iqn.2000-01.com.synology:synology.Target-15.292d45194a1"
@ -61,7 +76,7 @@ iscsi_targets:
chap_password: "{{ vault_iscsi_prole_password }}" chap_password: "{{ vault_iscsi_prole_password }}"
mounts: mounts:
- name: d004 - name: d004
path: /prole/d004 path: /synology/d004
fstype: xfs fstype: xfs
opts: "_netdev,noatime" opts: "_netdev,noatime"
mkfs_if_missing: true mkfs_if_missing: true

View File

@ -1,36 +1,16 @@
iscsi_portal: 10.0.0.203 iscsi_portal: 10.0.0.203
iscsi_targets: iscsi_targets:
# PROLE-DB-1 # PROLE-DATA-1
- iqn: "iqn.2000-01.com.synology:synology.Target-11.292d45194a1" - iqn: "iqn.2000-01.com.synology:synology.Target-11.292d45194a1"
chap_user: "prole" chap_user: "prole"
chap_password: "{{ vault_iscsi_prole_password }}" chap_password: "{{ vault_iscsi_prole_password }}"
mounts: mounts:
- path: /prole/d001 - path: /synology/d001
fstype: xfs fstype: xfs
opts: "_netdev,noatime" opts: "_netdev,noatime"
src: "UUID=c07dc0f2-cf60-4da5-b68e-ea45e86d473e" src: "UUID=c07dc0f2-cf60-4da5-b68e-ea45e86d473e"
# PROLE-DB-2
- iqn: "iqn.2000-01.com.synology:synology.Target-12.292d45194a1"
chap_user: "prole"
chap_password: "{{ vault_iscsi_prole_password }}"
mounts:
- path: /prole/d002
fstype: xfs
opts: "_netdev,noatime"
src: "UUID=fe087fb0-a321-4767-b838-b4385e81693e"
# PROLE-DB-3
- iqn: "iqn.2000-01.com.synology:synology.Target-13.292d45194a1"
chap_user: "prole"
chap_password: "{{ vault_iscsi_prole_password }}"
mounts:
- path: /prole/d003
fstype: xfs
opts: "_netdev,noatime"
src: "UUID=757f1ee4-dc23-414b-b595-e3058c0744f0"
# PROLE-HOME # PROLE-HOME
- iqn: "iqn.2000-01.com.synology:synology.Target-17.292d45194a1" - iqn: "iqn.2000-01.com.synology:synology.Target-17.292d45194a1"
chap_user: "prole" chap_user: "prole"
@ -53,6 +33,18 @@ iscsi_targets:
iscsi_absent_mounts: iscsi_absent_mounts:
- /opt/prole/logs/chrisfu - /opt/prole/logs/chrisfu
# Migration cleanup: PROLE-DATA-2/3 are moving off myrddin
- /prole/d002
- /prole/d003
- /synology/d002
- /synology/d003
# Migration cleanup: ensure myrddin fully logs out of LUNs that are moving to other hosts
iscsi_absent_targets:
# PROLE-DATA-2
- "iqn.2000-01.com.synology:synology.Target-12.292d45194a1"
# PROLE-DATA-3
- "iqn.2000-01.com.synology:synology.Target-13.292d45194a1"
ad_dc_enabled: true ad_dc_enabled: true
@ -122,6 +114,4 @@ k3s_kubeconfig_users:
- pi - pi
k3s_required_mounts: k3s_required_mounts:
- /prole/d001 - /synology/d001
- /prole/d002
- /prole/d003

View File

@ -1,8 +1,21 @@
iscsi_portal: 10.0.0.203 iscsi_portal: 10.0.0.203
k3s_required_mounts:
- /synology/d003
iscsi_targets: iscsi_targets:
# `/var/lib/rancher` is local host storage (do not manage it via iSCSI). # `/var/lib/rancher` is local host storage (do not manage it via iSCSI).
# PROLE-DATA-3
- iqn: "iqn.2000-01.com.synology:synology.Target-13.292d45194a1"
chap_user: "prole"
chap_password: "{{ vault_iscsi_prole_password }}"
mounts:
- path: /synology/d003
fstype: xfs
opts: "_netdev,noatime"
src: "UUID=757f1ee4-dc23-414b-b595-e3058c0744f0"
# PROLE-PI-2 # PROLE-PI-2
- iqn: "iqn.2000-01.com.synology:synology.Target-19.292d45194a1" - iqn: "iqn.2000-01.com.synology:synology.Target-19.292d45194a1"
chap_user: "prole" chap_user: "prole"

View File

@ -23,6 +23,37 @@
roles: roles:
- cgroups - cgroups
- name: Tier 1.5 - Quiesce k3s and detach migrated iSCSI LUNs (myrddin)
hosts: myrddin.prole.org
gather_facts: false
become: true
pre_tasks:
- name: Gather minimal facts
ansible.builtin.setup:
gather_subset:
- min
tasks:
- name: Check if k3s systemd unit exists
ansible.builtin.stat:
path: /etc/systemd/system/k3s.service
register: _k3s_unit
- name: Stop k3s (quiesce before iSCSI detach)
ansible.builtin.systemd:
name: k3s
state: stopped
when:
- not ansible_check_mode
- _k3s_unit.stat.exists | default(false)
- (iscsi_absent_mounts | default([]) | length) > 0 or (iscsi_absent_targets | default([]) | length) > 0
- name: Detach migrated iSCSI targets/mounts (best-effort cleanup)
ansible.builtin.import_role:
name: iscsi
tasks_from: detach
when:
- (iscsi_absent_mounts | default([]) | length) > 0 or (iscsi_absent_targets | default([]) | length) > 0
- name: Tier 2 - Storage (iSCSI auth/login) - name: Tier 2 - Storage (iSCSI auth/login)
hosts: iscsi hosts: iscsi
gather_facts: false gather_facts: false

View File

@ -4,6 +4,6 @@
tasks: tasks:
- name: Ensure OpenBao storage directory exists - name: Ensure OpenBao storage directory exists
ansible.builtin.file: ansible.builtin.file:
path: /prole/d001/openbao path: /synology/d001/openbao
state: directory state: directory
mode: "0777" # OpenBao pod runs as non-root usually, but k3s local-path often needs this or specific UID mode: "0777" # OpenBao pod runs as non-root usually, but k3s local-path often needs this or specific UID

View File

@ -0,0 +1,69 @@
---
- name: Require iscsi_portal when logging out absent targets
ansible.builtin.assert:
that:
- iscsi_portal is defined
- (iscsi_portal | string | length) > 0
fail_msg: "iscsi_absent_targets is set but iscsi_portal is missing/empty."
when: (iscsi_absent_targets | default([]) | length) > 0
- name: Unmount absent iSCSI mountpoints
ansible.builtin.mount:
path: "{{ item.path | default(item) }}"
state: unmounted
loop: "{{ iscsi_absent_mounts | default([]) }}"
loop_control:
label: "{{ item.path | default(item) }}"
tags:
- iscsi_cleanup
- name: Remove absent iSCSI fstab entries
ansible.builtin.lineinfile:
path: /etc/fstab
state: absent
regexp: "^\\s*\\S+\\s+{{ (item.path | default(item)) | regex_escape }}\\s+"
loop: "{{ iscsi_absent_mounts | default([]) }}"
loop_control:
label: "{{ item.path | default(item) }}"
tags:
- iscsi_cleanup
- name: Check for active sessions for absent targets
ansible.builtin.shell: >-
iscsiadm -m session 2>/dev/null | grep -Fq -- {{ item | quote }}
register: _iscsi_absent_target_session_checks
changed_when: false
failed_when: false
loop: "{{ iscsi_absent_targets | default([]) }}"
loop_control:
label: "{{ item }}"
when: (iscsi_absent_targets | default([]) | length) > 0
tags:
- iscsi_cleanup
- name: Logout absent iSCSI targets (active sessions only)
ansible.builtin.command: >-
iscsiadm -m node -T {{ item.item }} -p {{ iscsi_portal }} --logout
register: _iscsi_absent_target_logout
changed_when: false
failed_when: false
loop: "{{ _iscsi_absent_target_session_checks.results | default([]) }}"
loop_control:
label: "{{ item.item }}"
when:
- (iscsi_absent_targets | default([]) | length) > 0
- item.rc == 0
tags:
- iscsi_cleanup
- name: Delete node records for absent iSCSI targets (best-effort)
ansible.builtin.command: >-
iscsiadm -m node -o delete -T {{ item }} -p {{ iscsi_portal }}
changed_when: false
failed_when: false
loop: "{{ iscsi_absent_targets | default([]) }}"
loop_control:
label: "{{ item }}"
when: (iscsi_absent_targets | default([]) | length) > 0
tags:
- iscsi_cleanup

View File

@ -111,6 +111,7 @@
register: _iscsi_blkid_type register: _iscsi_blkid_type
changed_when: false changed_when: false
failed_when: false failed_when: false
check_mode: no
when: when:
- _iscsi_need_blockdev | default(false) | bool - _iscsi_need_blockdev | default(false) | bool
- _iscsi_blockdev is defined - _iscsi_blockdev is defined
@ -287,6 +288,7 @@
ansible.builtin.command: "blkid -o value -s UUID {{ _iscsi_blockdev }}" ansible.builtin.command: "blkid -o value -s UUID {{ _iscsi_blockdev }}"
register: _iscsi_blkid_uuid register: _iscsi_blkid_uuid
changed_when: false changed_when: false
check_mode: no
when: when:
- (_iscsi_mount_src | default('') | string | length) == 0 - (_iscsi_mount_src | default('') | string | length) == 0
- _iscsi_blockdev is defined - _iscsi_blockdev is defined
@ -302,7 +304,9 @@
- _iscsi_blkid_uuid is defined - _iscsi_blkid_uuid is defined
- (_iscsi_blkid_uuid.stdout | default('') | trim) != '' - (_iscsi_blkid_uuid.stdout | default('') | trim) != ''
fail_msg: "Unable to resolve a filesystem UUID for {{ m.path }} from {{ _iscsi_blockdev }}." fail_msg: "Unable to resolve a filesystem UUID for {{ m.path }} from {{ _iscsi_blockdev }}."
when: (_iscsi_mount_src | default('') | string | length) == 0 when:
- (_iscsi_mount_src | default('') | string | length) == 0
- not (ansible_check_mode and ((m.mkfs_once | default(false) | bool) or (m.mkfs_if_missing | default(false) | bool)))
tags: tags:
- iscsi - iscsi
- iscsi_mount - iscsi_mount
@ -311,7 +315,10 @@
- name: Set mount source to UUID - name: Set mount source to UUID
ansible.builtin.set_fact: ansible.builtin.set_fact:
_iscsi_mount_src: "UUID={{ _iscsi_blkid_uuid.stdout | trim }}" _iscsi_mount_src: "UUID={{ _iscsi_blkid_uuid.stdout | trim }}"
when: (_iscsi_mount_src | default('') | string | length) == 0 when:
- (_iscsi_mount_src | default('') | string | length) == 0
- _iscsi_blkid_uuid is defined
- (_iscsi_blkid_uuid.stdout | default('') | trim) != ''
tags: tags:
- iscsi - iscsi
- iscsi_mount - iscsi_mount
@ -324,6 +331,7 @@
fstype: "{{ m.fstype | default('ext4') }}" fstype: "{{ m.fstype | default('ext4') }}"
opts: "{{ m.opts | default('_netdev,noatime') }}" opts: "{{ m.opts | default('_netdev,noatime') }}"
state: mounted state: mounted
when: (_iscsi_mount_src | default('') | string | trim | length) > 0
tags: tags:
- iscsi - iscsi
- iscsi_mount - iscsi_mount

View File

@ -31,8 +31,8 @@
no_log: true no_log: true
- name: Check if target session is already logged in - name: Check if target session is already logged in
ansible.builtin.command: > ansible.builtin.shell: >-
iscsiadm -m session -T {{ t.iqn }} -p {{ iscsi_portal }} iscsiadm -m session 2>/dev/null | grep -Fq -- {{ t.iqn | quote }}
register: session_check register: session_check
changed_when: false changed_when: false
failed_when: false failed_when: false
@ -46,8 +46,8 @@
when: session_check.rc != 0 when: session_check.rc != 0
- name: Re-check session after login attempt - name: Re-check session after login attempt
ansible.builtin.command: > ansible.builtin.shell: >-
iscsiadm -m session -T {{ t.iqn }} -p {{ iscsi_portal }} iscsiadm -m session 2>/dev/null | grep -Fq -- {{ t.iqn | quote }}
register: session_after_login register: session_after_login
changed_when: false changed_when: false
failed_when: false failed_when: false
@ -55,7 +55,12 @@
- name: Fail if login did not establish a session - name: Fail if login did not establish a session
ansible.builtin.fail: ansible.builtin.fail:
msg: "iSCSI login failed for {{ t.iqn }} at {{ iscsi_portal }}" msg: |-
iSCSI login failed for {{ t.iqn }} at {{ iscsi_portal }}.
login.rc={{ login.rc | default('') }}
login.stdout={{ (login.stdout | default('')) | trim }}
login.stderr={{ (login.stderr | default('')) | trim }}
when: when:
- session_check.rc != 0 - session_check.rc != 0
- session_after_login is defined - session_after_login is defined

View File

@ -1,4 +1,41 @@
--- ---
- name: Compute legacy mountpoints to clean up (/prole/d00x -> /synology/d00x)
ansible.builtin.set_fact:
_iscsi_legacy_absent_mounts: >-
{{
(iscsi_targets | default([])
| map(attribute='mounts') | list | flatten
| selectattr('path', 'defined')
| map(attribute='path') | list
| select('match', '^/synology/d[0-9]{3}$')
| map('regex_replace', '^/synology/', '/prole/')
| list)
}}
changed_when: false
tags:
- iscsi_cleanup
- name: Unmount legacy iSCSI mounts (old /prole/d00x layout)
ansible.builtin.mount:
path: "{{ item }}"
state: unmounted
loop: "{{ _iscsi_legacy_absent_mounts | default([]) }}"
loop_control:
label: "{{ item }}"
tags:
- iscsi_cleanup
- name: Remove legacy iSCSI fstab entries (old /prole/d00x layout)
ansible.builtin.lineinfile:
path: /etc/fstab
state: absent
regexp: "^\\s*\\S+\\s+{{ item | regex_escape }}\\s+"
loop: "{{ _iscsi_legacy_absent_mounts | default([]) }}"
loop_control:
label: "{{ item }}"
tags:
- iscsi_cleanup
- name: Unmount stale iSCSI mounts - name: Unmount stale iSCSI mounts
ansible.builtin.mount: ansible.builtin.mount:
path: "{{ item.path | default(item) }}" path: "{{ item.path | default(item) }}"

View File

@ -99,28 +99,48 @@
- k3s_prestage_images | bool - k3s_prestage_images | bool
tags: [images] tags: [images]
- name: Create ArgoCD hostPath directories on myrddin.prole.org - name: Create ArgoCD hostPath directories on myrddin.prole.org (/synology/d001)
ansible.builtin.file: ansible.builtin.file:
path: "{{ item }}" path: "{{ item }}"
state: directory state: directory
mode: "0777" mode: "0777"
loop: loop:
- /prole/d001/argocd - /synology/d001/argocd
- /prole/d001/argocd/home - /synology/d001/argocd/home
- /prole/d001/argocd/tmp - /synology/d001/argocd/tmp
- /prole/d002/argocd
- /prole/d002/argocd/data
- /prole/d002/argocd/tmp
- /prole/d003/argocd
- /prole/d003/argocd/gpg-keyring
- /prole/d003/argocd/tmp
- /prole/d003/argocd/helm-working-dir
- /prole/d003/argocd/var-files
- /prole/d003/argocd/plugins
when: when:
- k3s_state == "present" - k3s_state == "present"
- inventory_hostname == 'myrddin.prole.org' - inventory_hostname == 'myrddin.prole.org'
- name: Create ArgoCD hostPath directories on merlin.prole.org (/synology/d002)
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0777"
loop:
- /synology/d002/argocd
- /synology/d002/argocd/data
- /synology/d002/argocd/tmp
when:
- k3s_state == "present"
- inventory_hostname == 'merlin.prole.org'
- name: Create ArgoCD hostPath directories on pi.prole.org (/synology/d003)
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0777"
loop:
- /synology/d003/argocd
- /synology/d003/argocd/gpg-keyring
- /synology/d003/argocd/tmp
- /synology/d003/argocd/helm-working-dir
- /synology/d003/argocd/var-files
- /synology/d003/argocd/plugins
when:
- k3s_state == "present"
- inventory_hostname == 'pi.prole.org'
- name: Wait for k3s Kubernetes API to become ready - name: Wait for k3s Kubernetes API to become ready
ansible.builtin.command: k3s kubectl get --raw='/readyz' ansible.builtin.command: k3s kubectl get --raw='/readyz'
register: _k3s_readyz register: _k3s_readyz

View File

@ -71,7 +71,7 @@
RequiresMountsFor={{ k3s_rancher_mountpoint }} RequiresMountsFor={{ k3s_rancher_mountpoint }}
[Service] [Service]
ExecStartPre=/bin/sh -ec 'rancher_src="$(findmnt -n -o SOURCE -T {{ k3s_rancher_mountpoint }})"; root_src="$(findmnt -n -o SOURCE -T /)"; if [ "${rancher_src}" = "${root_src}" ]; then echo "Refusing to start k3s: {{ k3s_rancher_mountpoint }} resolves to rootfs (${root_src}). Expected a dedicated mount." >&2; exit 1; fi' ExecStartPre=/bin/sh -ec 'rancher_src="$(findmnt -n -o SOURCE -T {{ k3s_rancher_mountpoint }})"; root_src="$(findmnt -n -o SOURCE -T /)"; if [ "$${rancher_src}" = "$${root_src}" ]; then echo "Refusing to start k3s: {{ k3s_rancher_mountpoint }} resolves to rootfs ($${root_src}). Expected a dedicated mount." >&2; exit 1; fi'
register: _k3s_rancher_mount_guard_dropin register: _k3s_rancher_mount_guard_dropin
when: ansible_service_mgr | default('') == 'systemd' when: ansible_service_mgr | default('') == 'systemd'

View File

@ -32161,11 +32161,11 @@ spec:
serviceAccountName: argocd-redis serviceAccountName: argocd-redis
volumes: volumes:
- hostPath: - hostPath:
path: /prole/d002/argocd/data path: /synology/d002/argocd/data
type: DirectoryOrCreate type: DirectoryOrCreate
name: argocd-redis-data name: argocd-redis-data
- hostPath: - hostPath:
path: /prole/d002/argocd/tmp path: /synology/d002/argocd/tmp
type: DirectoryOrCreate type: DirectoryOrCreate
name: argocd-redis-tmp name: argocd-redis-tmp
@ -32582,15 +32582,15 @@ spec:
name: argocd-gpg-keys-cm name: argocd-gpg-keys-cm
name: gpg-keys name: gpg-keys
- hostPath: - hostPath:
path: /prole/d003/argocd/gpg-keyring path: /synology/d003/argocd/gpg-keyring
type: DirectoryOrCreate type: DirectoryOrCreate
name: gpg-keyring name: gpg-keyring
- hostPath: - hostPath:
path: /prole/d003/argocd/tmp path: /synology/d003/argocd/tmp
type: DirectoryOrCreate type: DirectoryOrCreate
name: tmp name: tmp
- hostPath: - hostPath:
path: /prole/d003/argocd/helm-working-dir path: /synology/d003/argocd/helm-working-dir
type: DirectoryOrCreate type: DirectoryOrCreate
name: helm-working-dir name: helm-working-dir
- name: argocd-repo-server-tls - name: argocd-repo-server-tls
@ -32605,11 +32605,11 @@ spec:
optional: true optional: true
secretName: argocd-repo-server-tls secretName: argocd-repo-server-tls
- hostPath: - hostPath:
path: /prole/d003/argocd/var-files path: /synology/d003/argocd/var-files
type: DirectoryOrCreate type: DirectoryOrCreate
name: var-files name: var-files
- hostPath: - hostPath:
path: /prole/d003/argocd/plugins path: /synology/d003/argocd/plugins
type: DirectoryOrCreate type: DirectoryOrCreate
name: plugins name: plugins
@ -33359,11 +33359,11 @@ spec:
serviceAccountName: argocd-application-controller serviceAccountName: argocd-application-controller
volumes: volumes:
- hostPath: - hostPath:
path: /prole/d001/argocd/home path: /synology/d001/argocd/home
type: DirectoryOrCreate type: DirectoryOrCreate
name: argocd-home name: argocd-home
- hostPath: - hostPath:
path: /prole/d001/argocd/tmp path: /synology/d001/argocd/tmp
type: DirectoryOrCreate type: DirectoryOrCreate
name: argocd-application-controller-tmp name: argocd-application-controller-tmp
- name: argocd-repo-server-tls - name: argocd-repo-server-tls

View File

@ -14,7 +14,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d001/data path: /synology/d001/data
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -40,7 +40,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d001/wal path: /synology/d001/wal
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -66,7 +66,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d001/garage path: /synology/d001/garage
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -92,7 +92,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d002/data path: /synology/d002/data
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -100,7 +100,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - merlin.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -118,7 +118,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d002/wal path: /synology/d002/wal
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -126,7 +126,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - merlin.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -144,7 +144,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d003/data path: /synology/d003/data
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -152,7 +152,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - pi.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -170,7 +170,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d003/wal path: /synology/d003/wal
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -178,7 +178,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - pi.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -196,7 +196,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d003/grafana path: /synology/d003/grafana
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -204,7 +204,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - pi.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -222,7 +222,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d002/prometheus path: /synology/d002/prometheus
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -230,7 +230,7 @@ spec:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- myrddin.prole.org - merlin.prole.org
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolume kind: PersistentVolume
@ -248,7 +248,7 @@ spec:
storageClassName: prole-iscsi storageClassName: prole-iscsi
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d001/openbao path: /synology/d001/openbao
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:

View File

@ -14,7 +14,7 @@ spec:
storageClassName: prole-monitoring-d004 storageClassName: prole-monitoring-d004
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d004/prometheus path: /synology/d004/prometheus
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -40,7 +40,7 @@ spec:
storageClassName: prole-monitoring-d004 storageClassName: prole-monitoring-d004
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d004/alertmanager path: /synology/d004/alertmanager
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:
@ -66,7 +66,7 @@ spec:
storageClassName: prole-monitoring-d004 storageClassName: prole-monitoring-d004
persistentVolumeReclaimPolicy: Retain persistentVolumeReclaimPolicy: Retain
local: local:
path: /prole/d004/grafana path: /synology/d004/grafana
nodeAffinity: nodeAffinity:
required: required:
nodeSelectorTerms: nodeSelectorTerms:

View File

@ -5,7 +5,7 @@ metadata:
name: prole-db name: prole-db
spec: spec:
instances: 3 instances: 3
imageName: myrddin.prole.org:5000/prole-db:18-129 imageName: myrddin.prole.org:5000/prole-db:0.0.0
postgresUID: 100 postgresUID: 100
postgresGID: 101 postgresGID: 101
maxSyncReplicas: 1 maxSyncReplicas: 1

View File

@ -1 +1 @@
129 130

View File

@ -105,6 +105,32 @@ if [[ "$args" == *"-n cnpg-system get secret"*"barman-cloud-"*"-o jsonpath="* ]]
exit 0 exit 0
fi fi
# Barman plugin service registration readiness (strict gating)
if [[ "$args" == *"-n cnpg-system get svc"*"-l cnpg.io/pluginName=barman-cloud.cloudnative-pg.io"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "$args" == *"-n cnpg-system get svc barman-cloud"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "$args" == *"-n cnpg-system get svc barman-cloud"*"pluginClientSecret"* ]]; then
echo "barman-cloud-client-tls"
exit 0
fi
if [[ "$args" == *"-n cnpg-system get svc barman-cloud"*"pluginServerSecret"* ]]; then
echo "barman-cloud-server-tls"
exit 0
fi
if [[ "$args" == *"-n cnpg-system get svc barman-cloud"*"pluginPort"* ]]; then
echo "9090"
exit 0
fi
if [[ "$args" == *"-n cnpg-system get endpoints barman-cloud"* ]]; then
echo "10.42.0.11"
exit 0
fi
# DB node availability checks (bootstrap safety) # DB node availability checks (bootstrap safety)
if [[ "$args" == *"get nodes"*"--no-headers"* ]]; then if [[ "$args" == *"get nodes"*"--no-headers"* ]]; then
# Name Status Roles Age Version # Name Status Roles Age Version

View File

@ -68,6 +68,47 @@ if [[ "${args}" == *"get endpoints"*"cnpg-webhook-service"* ]]; then
exit 0 exit 0
fi fi
# Avoid barman plugin waits (strict readiness now includes service registration)
if [[ "${args}" == *"get crd"*"objectstores.barmancloud.cnpg.io"* ]]; then
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get secret"*"barman-cloud-client-tls"* ]]; then
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get secret"*"barman-cloud-server-tls"* ]]; then
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get deploy"*"barman-cloud"* ]]; then
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"rollout status"*"deploy/barman-cloud"* ]]; then
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc"*"-l cnpg.io/pluginName=barman-cloud.cloudnative-pg.io"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginClientSecret"* ]]; then
echo "barman-cloud-client-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginServerSecret"* ]]; then
echo "barman-cloud-server-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginPort"* ]]; then
echo "9090"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get endpoints barman-cloud"* ]]; then
echo "10.42.0.11"
exit 0
fi
# CNPG pods listing and readiness queries # CNPG pods listing and readiness queries
if [[ "${args}" == *"get pods"* && "${args}" == *"cnpg.io/cluster="* ]]; then if [[ "${args}" == *"get pods"* && "${args}" == *"cnpg.io/cluster="* ]]; then
if [[ "${args}" == *"--no-headers"* ]]; then if [[ "${args}" == *"--no-headers"* ]]; then

View File

@ -65,7 +65,7 @@ if [[ "${args}" == *"-n cert-manager"*"get deploy"* ]]; then
fi fi
# Avoid barman plugin waits # Avoid barman plugin waits
if [[ "${args}" == *"get crd"*"barmanobjectstores.barmancloud.cnpg.io"* ]]; then if [[ "${args}" == *"get crd"*"objectstores.barmancloud.cnpg.io"* ]]; then
exit 0 exit 0
fi fi
if [[ "${args}" == *"-n cnpg-system"*"get secret"*"barman-cloud-client-tls"* ]]; then if [[ "${args}" == *"-n cnpg-system"*"get secret"*"barman-cloud-client-tls"* ]]; then
@ -74,19 +74,90 @@ fi
if [[ "${args}" == *"-n cnpg-system"*"get secret"*"barman-cloud-server-tls"* ]]; then if [[ "${args}" == *"-n cnpg-system"*"get secret"*"barman-cloud-server-tls"* ]]; then
exit 0 exit 0
fi fi
if [[ "${args}" == *"-n cnpg-system"*"get deploy"*"barman-cloud"* ]]; then
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"rollout status"*"deploy/barman-cloud"* ]]; then
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc"*"-l cnpg.io/pluginName=barman-cloud.cloudnative-pg.io"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginClientSecret"* ]]; then
echo "barman-cloud-client-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginServerSecret"* ]]; then
echo "barman-cloud-server-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginPort"* ]]; then
echo "9090"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get endpoints barman-cloud"* ]]; then
echo "10.42.0.11"
exit 0
fi
# Namespace check in ensure_namespace() # Namespace check in ensure_namespace()
if [[ "${args}" == "get namespace test-ns" ]]; then if [[ "${args}" == "get namespace test-ns" ]]; then
exit 0 exit 0
fi fi
# Capture applied CNPG manifest # Capture applied CNPG manifest (stdin or file)
if [[ "${args}" == apply* && "${args}" == *"-n"*"test-ns"* && "${args}" == *"-f"*"-"* ]]; then if [[ "${args}" == apply* && "${args}" == *"-n"*"test-ns"* && "${args}" == *" -f "* ]]; then
cat > "${TMP_DIR}/applied.yaml" f=""
if [[ "$#" -ge 2 ]]; then
for ((i=1; i<=$#; i++)); do
if [[ "${!i}" == "-f" ]]; then
j=$((i+1))
f="${!j:-}"
break
fi
done
fi
if [[ "${f}" == "-" ]]; then
cat > "${TMP_DIR}/applied.yaml"
elif [[ -n "${f}" && -f "${f}" ]]; then
cat "${f}" > "${TMP_DIR}/applied.yaml"
else
: > "${TMP_DIR}/applied.yaml"
fi
echo "cluster.postgresql.cnpg.io/prole-db configured" echo "cluster.postgresql.cnpg.io/prole-db configured"
exit 0 exit 0
fi 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"}}}}}}
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"}}
]}
JSON
exit 0
fi
if [[ "${args}" == "get pv pv-data -o json"* ]]; then
echo '{"spec":{"storageClassName":"prole-iscsi","local":{"path":"/prole/d001/data"}}}'
exit 0
fi
if [[ "${args}" == "get pv pv-wal -o json"* ]]; then
echo '{"spec":{"storageClassName":"prole-iscsi","local":{"path":"/prole/d001/wal"}}}'
exit 0
fi
# Node inventory used by reconcile_cnpg_instances # Node inventory used by reconcile_cnpg_instances
if [[ "${args}" == "get nodes --no-headers" ]]; then if [[ "${args}" == "get nodes --no-headers" ]]; then
n="${MOCK_READY_NODES:-1}" n="${MOCK_READY_NODES:-1}"
@ -133,7 +204,6 @@ mock_tool ansible-playbook
mock_tool tofu mock_tool tofu
mock_tool terraform mock_tool terraform
mock_tool ollama mock_tool ollama
mock_tool jq
export PATH="$BIN_DIR:$PATH" export PATH="$BIN_DIR:$PATH"
export TMP_DIR export TMP_DIR
@ -178,7 +248,10 @@ E_EOF
rm -f "$TMP_DIR/applied.yaml" rm -f "$TMP_DIR/applied.yaml"
rm -f "$TMP_DIR/mock_cluster_instances" rm -f "$TMP_DIR/mock_cluster_instances"
set +e set +e
MOCK_READY_NODES="$mock_ready_nodes" MOCK_DB_NODES="$mock_db_nodes" PROLE_HOME="$env_home" \ MOCK_READY_NODES="$mock_ready_nodes" MOCK_DB_NODES="$mock_db_nodes" \
BARMAN_CRD_TIMEOUT=5 BARMAN_TLS_TIMEOUT=5 BARMAN_DEPLOY_TIMEOUT=5 BARMAN_SERVICE_TIMEOUT=5 \
CNPG_STORAGE_VALIDATE_TIMEOUT=5 \
PROLE_HOME="$env_home" \
bash "$SCRIPT_UNDER_TEST" --mode k3s start >/dev/null 2>"$TMP_DIR/stderr-${label}" bash "$SCRIPT_UNDER_TEST" --mode k3s start >/dev/null 2>"$TMP_DIR/stderr-${label}"
local rc=$? local rc=$?
set -e set -e

View File

@ -117,6 +117,38 @@ if [[ "${args}" == *"get crd"* ]]; then
exit 0 exit 0
fi fi
# Barman plugin strict readiness checks (service registration)
if [[ "${args}" == *"-n cnpg-system"*"get deploy"*"barman-cloud"* ]]; then
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"rollout status"*"deploy/barman-cloud"* ]]; then
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc"*"-l cnpg.io/pluginName=barman-cloud.cloudnative-pg.io"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginClientSecret"* ]]; then
echo "barman-cloud-client-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginServerSecret"* ]]; then
echo "barman-cloud-server-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginPort"* ]]; then
echo "9090"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get endpoints barman-cloud"* ]]; then
echo "10.42.0.11"
exit 0
fi
# CNPG cluster instances lookup in wait_for_cnpg_pods() # CNPG cluster instances lookup in wait_for_cnpg_pods()
if [[ "${args}" == *"-n test-ns"*"get cluster"*"-o jsonpath="*".spec.instances"* ]]; then if [[ "${args}" == *"-n test-ns"*"get cluster"*"-o jsonpath="*".spec.instances"* ]]; then
echo "3" echo "3"

View File

@ -64,6 +64,32 @@ if [[ "${args}" == "get deployment -A -l app.kubernetes.io/name=barman-cloud -o
exit 0 exit 0
fi fi
# Barman plugin service registration (used by wait_for_barman_plugin_infra_ready)
if [[ "${args}" == *"-n cnpg-system"*"get svc"*"-l cnpg.io/pluginName=barman-cloud.cloudnative-pg.io"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginClientSecret"* ]]; then
echo "barman-cloud-client-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginServerSecret"* ]]; then
echo "barman-cloud-server-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginPort"* ]]; then
echo "9090"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get endpoints barman-cloud"* ]]; then
echo "10.42.0.11"
exit 0
fi
# CNPG cluster pod selection for socket check # CNPG cluster pod selection for socket check
if [[ "${args}" == *"-n test-ns"*"get pods"*"-l cnpg.io/cluster=prole-db,cnpg.io/instanceRole=primary"*"-o jsonpath={.items[0].metadata.name"* ]]; then if [[ "${args}" == *"-n test-ns"*"get pods"*"-l cnpg.io/cluster=prole-db,cnpg.io/instanceRole=primary"*"-o jsonpath={.items[0].metadata.name"* ]]; then
echo "prole-db-1" echo "prole-db-1"
@ -102,6 +128,16 @@ if [[ "${args}" == apply*"-n"*"test-ns"*"-f"*"-"* ]]; then
exit 0 exit 0
fi fi
# ObjectStore readiness checks
if [[ "${args}" == *"-n test-ns"*"get objectstore"*"prole-db-barman-objectstore"*"-o jsonpath="*"conditions"*"Ready"* ]]; then
echo "True"
exit 0
fi
if [[ "${args}" == *"-n test-ns"*"get objectstore"*"prole-db-barman-objectstore"*"-o jsonpath={.status.phase"* ]]; then
echo "Ready"
exit 0
fi
exit 0 exit 0
K_EOF K_EOF
chmod +x "$BIN_DIR/kubectl" chmod +x "$BIN_DIR/kubectl"

View File

@ -74,6 +74,32 @@ if [[ "${args}" == "get deployment -A -l app.kubernetes.io/name=barman-cloud -o
exit 0 exit 0
fi fi
# Barman plugin service registration (used by wait_for_barman_plugin_infra_ready)
if [[ "${args}" == *"-n cnpg-system"*"get svc"*"-l cnpg.io/pluginName=barman-cloud.cloudnative-pg.io"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"-o jsonpath="*"metadata.name"* ]]; then
echo "barman-cloud"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginClientSecret"* ]]; then
echo "barman-cloud-client-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginServerSecret"* ]]; then
echo "barman-cloud-server-tls"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get svc barman-cloud"*"pluginPort"* ]]; then
echo "9090"
exit 0
fi
if [[ "${args}" == *"-n cnpg-system"*"get endpoints barman-cloud"* ]]; then
echo "10.42.0.11"
exit 0
fi
# CNPG cluster pod selection for socket check # CNPG cluster pod selection for socket check
if [[ "${args}" == *"-n test-ns"*"get pods"*"-l cnpg.io/cluster=prole-db,cnpg.io/instanceRole=primary"*"-o jsonpath={.items[0].metadata.name"* ]]; then if [[ "${args}" == *"-n test-ns"*"get pods"*"-l cnpg.io/cluster=prole-db,cnpg.io/instanceRole=primary"*"-o jsonpath={.items[0].metadata.name"* ]]; then
echo "prole-db-1" echo "prole-db-1"
@ -157,6 +183,16 @@ if [[ "${args}" == "get backup -n test-ns"* ]]; then
exit 0 exit 0
fi fi
# ObjectStore readiness checks
if [[ "${args}" == *"-n test-ns"*"get objectstore"*"prole-db-barman-objectstore"*"-o jsonpath="*"conditions"*"Ready"* ]]; then
echo "True"
exit 0
fi
if [[ "${args}" == *"-n test-ns"*"get objectstore"*"prole-db-barman-objectstore"*"-o jsonpath={.status.phase"* ]]; then
echo "Ready"
exit 0
fi
exit 0 exit 0
K_EOF K_EOF
chmod +x "$BIN_DIR/kubectl" chmod +x "$BIN_DIR/kubectl"