mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 14:54:34 +00:00
checkpoint: tighten CNPG k3s storage flow and policy updates
- normalize K3S/CNPG/OpenTofu env keys across installer and configs; - harden CNPG storage validation/provisioning and fail fast in init script; - update DB manifests/recovery template and port/network mapping data; - expand cluster node policy UI coverage and shell/python tests; - add k3s mode version marker files Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
parent
9791a6663e
commit
0e806a9233
@ -1,5 +1,5 @@
|
|||||||
; Prole Master Configuration File
|
; Prole Master Configuration File
|
||||||
; Generated by install.py on 2026-03-23 01:05:45
|
; Generated by install.py on 2026-03-23 05:47:45
|
||||||
; 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]
|
||||||
@ -49,6 +49,8 @@ init_cluster.at_rest_encryption_enabled = true
|
|||||||
init_cluster.cluster_env = dev
|
init_cluster.cluster_env = dev
|
||||||
init_cluster.deployment_target = prole-dev-cluster
|
init_cluster.deployment_target = prole-dev-cluster
|
||||||
init_cluster.gitops_enabled = false
|
init_cluster.gitops_enabled = false
|
||||||
|
init_cluster.k3s_server_url = https://myrddin.prole.org:6443
|
||||||
|
init_cluster.k3s_token = ${PROLE_SECRET:v1:_SxOPUoevrdHL-EI:oQ8F1jKsGCyqYd2SQknjAb2EwrY3FIkW6e-QpoGAtIZB-kDqhWvQ3eWM5pOYMZ2lSLBvMGlIB-L6I2wI1uODnLi-R25wkZm6-n7zcs6R_ofK9F6la_TgzaDXoWFt_WyTCmmK0yFUAQ5q7ejzbGwlQD_hxvwZmybuiKNjZg==}
|
||||||
init_cluster.kerberos_enabled = true
|
init_cluster.kerberos_enabled = true
|
||||||
init_cluster.mode = k3d
|
init_cluster.mode = k3d
|
||||||
init_cluster.start_cluster = true
|
init_cluster.start_cluster = true
|
||||||
@ -58,14 +60,14 @@ 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 = ${PROLE_SECRET:v1:6--EY4qcxPxnM3dj:5giZ7B2OWXvivFwj-NQiogFb-BgrD43u}
|
init_password.db_password = ${PROLE_SECRET:v1:VtMK1Hb7NuUwl8vg:z7l5pjx2HVX9RIF_T_G6GfgDPSGu88Rq}
|
||||||
init_password.db_password_confirm = ${PROLE_SECRET:v1:6--EY4qcxPxnM3dj:5giZ7B2OWXvivFwj-NQiogFb-BgrD43u}
|
init_password.db_password_confirm = ${PROLE_SECRET:v1:VtMK1Hb7NuUwl8vg:z7l5pjx2HVX9RIF_T_G6GfgDPSGu88Rq}
|
||||||
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 = true
|
kerberos_config.enabled = true
|
||||||
kerberos_config.kdc = 10.0.0.3
|
kerberos_config.kdc = 10.0.0.3
|
||||||
kerberos_config.password = ${PROLE_SECRET:v1:lgRUp-FYr0mxvPI1:cXJTJLEzECF20A62qJIcG2RblRXrxS3VEv4JJs9GtCxa4tUK}
|
kerberos_config.password = ${PROLE_SECRET:v1:M_ktrIO8qCCNi00s:fdjylvLlSzeFo_CW-1YPAd3kQU_IKEzIW3JCh_pWPQBubtG_}
|
||||||
kerberos_config.realm = PROLE.ORG
|
kerberos_config.realm = PROLE.ORG
|
||||||
kerberos_config.test_connection = false
|
kerberos_config.test_connection = false
|
||||||
kerberos_config.user = administrator
|
kerberos_config.user = administrator
|
||||||
@ -77,18 +79,16 @@ 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
|
||||||
CLUSTER_ENV = dev
|
CLUSTER_ENV = dev
|
||||||
CNPG_PLACEMENT_PLAN_FILE = /Users/chrisfu/dev/prole/conf/cnpg-placement/knoe-system-knoe-db.json
|
|
||||||
CNPG_PLACEMENT_PLAN_HASH = 965d2fde4035059f
|
|
||||||
CNPG_PLACEMENT_PLAN_ID = cnpg-placement-965d2fde4035059f
|
|
||||||
DB_HOST_PORT = 5432
|
DB_HOST_PORT = 5432
|
||||||
DB_PASSWORD = ${PROLE_SECRET:v1:wQ8-XBE35Upcd1ob:AoFolm6Hq3WsleBZ6S5q9zFp3ObF5Vis}
|
DB_PASSWORD = ${PROLE_SECRET:v1:JaHrcyC4uDthLlMB:k6aqQY7JXnSffQagK7CfJTqkzwEEYwGu}
|
||||||
DEPLOYMENT_MODE = k3s
|
DEPLOYMENT_MODE = k3d
|
||||||
MODE = k3s
|
|
||||||
DEPLOYMENT_TARGET = prole-dev-cluster
|
DEPLOYMENT_TARGET = prole-dev-cluster
|
||||||
DOCKER_PRELOAD = false
|
DOCKER_PRELOAD = false
|
||||||
KNOE_DB_USER = root
|
KNOE_DB_USER = root
|
||||||
NAMESPACE = knoe-db-17-7-043-18-140
|
NAMESPACE = knoe-db-17-7-043-18-140
|
||||||
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2
|
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2
|
||||||
|
PROLE_K3S_SERVER = https://myrddin.prole.org:6443
|
||||||
|
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:cU7I0vEJL7AppE-x:Kwu5vGmJi7bKAKyKT8_fqYS-p241IM5u6DxAcI2M0HuD_sRIsIsSDtv5u3Y79OPX6rFTOEwyxPtCK44GYRQgxHNPnq2ltRe60yHXIUwBY-jBFRX864e2_c-vXUvzjkYJvruxBHhTgOa4nFavIRs_0cBPmjwP5qrVBJkpXg==}
|
||||||
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
|
||||||
|
|
||||||
@ -133,10 +133,7 @@ OLLAMA_SERVER_HOST = fairyland.prole.org
|
|||||||
OLLAMA_SERVER_PORT = 11434
|
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.
|
||||||
@ -145,20 +142,34 @@ SUPABASE_ENABLED = False
|
|||||||
; No configuration values captured yet for this section.
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Initialize Cluster]
|
[Initialize Cluster]
|
||||||
ENVIRONMENT = dev
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Dev Cluster (k3d)]
|
[Dev Cluster (k3d)]
|
||||||
CLUSTER_ENV = dev
|
CLUSTER_ENV = dev
|
||||||
DISPLAY_NAME = knoe-dev-cluster
|
DISPLAY_NAME = knoe-dev-cluster
|
||||||
KUBECTL_CONTEXT = k3d-knoe-dev-cluster
|
KUBECTL_CONTEXT =
|
||||||
MODE = k3d
|
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:t94Mb2O9TNzuCiKH:BoUY6Yj7a4NtsVmulT1MQb8aQ5w4ltS7SZDCFf9o3MfwB0YVem9QhDUin1mZGoFDVtRFOo3iF2l8PKvDSDRxajI-ENqLuFve41Y6s7tF7Wp4xH2-mNkf3xPeN2XX2iOqBhZ3UvceScSp_2iV_8bR7kaPqtNtoshr0icNxw==}
|
||||||
|
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]
|
[Docker Build]
|
||||||
LOCAL_REGISTRY = localhost:5000
|
; No configuration values captured yet for this section.
|
||||||
LOCAL_REGISTRY_INTERNAL = k3d-prole-registry.localhost:5000
|
|
||||||
|
|
||||||
[Initialization Scripts]
|
[Initialization Scripts]
|
||||||
STATUS = Attempted
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Deployment]
|
[Deployment]
|
||||||
MODE = k3d
|
MODE = k3d
|
||||||
|
|||||||
@ -3,10 +3,9 @@
|
|||||||
|
|
||||||
argocd: local=8081 remote=80 ns=argocd svc=argocd-server address=0.0.0.0
|
argocd: local=8081 remote=80 ns=argocd svc=argocd-server address=0.0.0.0
|
||||||
garage: local=3900 remote=3900 ns=knoe-system svc=garage address=0.0.0.0
|
garage: local=3900 remote=3900 ns=knoe-system svc=garage address=0.0.0.0
|
||||||
openbao: local=8200 remote=8200 ns=knoe-system svc=openbao address=127.0.0.1
|
openbao: local=8200 remote=8200 ns=knoe-system svc=openbao address=0.0.0.0
|
||||||
opentofu: local=8080 remote=8080 ns=knoe-system svc=opentofu address=0.0.0.0
|
opentofu: local=8080 remote=8080 ns=knoe-system svc=opentofu address=0.0.0.0
|
||||||
dashboard: local=8443 remote=443 ns=kubernetes-dashboard svc=kubernetes-dashboard-kong-proxy address=127.0.0.1
|
dashboard: local=8443 remote=443 ns=kubernetes-dashboard svc=kubernetes-dashboard-kong-proxy address=127.0.0.1
|
||||||
postgres: local=5432 remote=5432 ns=knoe-db-17-7-043-18-140 svc=knoe-db-rw address=0.0.0.0
|
postgres: local=5432 remote=5432 ns=knoe-db svc=knoe-db-rw address=0.0.0.0
|
||||||
prometheus: local=9090 remote=9090 ns=monitoring svc=kps-kube-prometheus-stack-prometheus address=127.0.0.1
|
prometheus: local=9090 remote=9090 ns=monitoring svc=kps-kube-prometheus-stack-prometheus address=127.0.0.1
|
||||||
grafana: local=3000 remote=80 ns=monitoring svc=kps-grafana address=0.0.0.0
|
grafana: local=3000 remote=80 ns=monitoring svc=kps-grafana address=0.0.0.0
|
||||||
certmgr: local=9443 remote=9443 ns=cert-manager svc=cert-manager-webhook address=127.0.0.1
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
; Prole Master Configuration File
|
; Prole Master Configuration File
|
||||||
; Generated by install.py on 2026-03-22 20:46:43
|
; Generated by install.py on 2026-03-23 11:51:58
|
||||||
; 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]
|
||||||
@ -29,28 +29,28 @@ dependencies.ansible.install = true
|
|||||||
dependencies.auto_install_missing = true
|
dependencies.auto_install_missing = true
|
||||||
dependencies.brew.install = true
|
dependencies.brew.install = true
|
||||||
dependencies.docker.install = true
|
dependencies.docker.install = true
|
||||||
dependencies.k3d.install = false
|
dependencies.k3d.install = true
|
||||||
dependencies.kubectl.install = true
|
dependencies.kubectl.install = true
|
||||||
dependencies.kubectx.install = true
|
dependencies.kubectx.install = true
|
||||||
dependencies.opentofu.install = true
|
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 = $HOME/dev/prole/prole-tools-app/dist
|
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 = $HOME/dev/prole/conf
|
env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf
|
||||||
env_setup.PROLE_DATA = $HOME/dev/prole/data
|
env_setup.PROLE_DATA = /Users/chrisfu/dev/prole/data
|
||||||
env_setup.PROLE_HOME = $HOME/dev/prole
|
env_setup.PROLE_HOME = /Users/chrisfu/dev/prole
|
||||||
env_setup.PROLE_LOGS = $HOME/dev/prole/logs
|
env_setup.PROLE_LOGS = /Users/chrisfu/dev/prole/logs
|
||||||
env_setup.PROLE_SERVICE = $HOME/dev/prole/etc
|
env_setup.PROLE_SERVICE = /Users/chrisfu/dev/prole/etc
|
||||||
init_cluster.argocd_enabled = false
|
init_cluster.argocd_enabled = false
|
||||||
init_cluster.at_rest_encryption_enabled = true
|
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 = https://myrddin.prole.org:6443
|
init_cluster.k3s_server_url = https://myrddin.prole.org:6443
|
||||||
init_cluster.k3s_token = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==}
|
init_cluster.k3s_token = ${PROLE_SECRET:v1:WjD201Yr7MHE56cs:wackF_EDDde7KmJp2SwbMlJi5mLdoHATkoyolRkPVqvnOVVScriN55ZHOO0cec-RvMIFQzhkL_yZbKhNkN9IIfkRdnAeGPDVELtM-wEDv_YlymTsrpYL_u79VR-nX3gKTCfcAha44ZyoBzObKTShzUx8eLCJMeLKYoPEKg==}
|
||||||
init_cluster.kerberos_enabled = true
|
init_cluster.kerberos_enabled = true
|
||||||
init_cluster.mode = k3s
|
init_cluster.mode = k3s
|
||||||
init_cluster.start_cluster = true
|
init_cluster.start_cluster = true
|
||||||
@ -60,56 +60,51 @@ 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 = ${PROLE_SECRET:v1:vz4oaR5QA3zIJWDs:blYR6vqpY0e_GqGWFufDwlY4yxXWXrGF}
|
init_password.db_password = ${PROLE_SECRET:v1:z2-5uGBCGoUaPEJm:RPb2Hoei9ikHHjLHbXQx3Ej2AxuXDrX2}
|
||||||
init_password.db_password_confirm = ${PROLE_SECRET:v1:vz4oaR5QA3zIJWDs:blYR6vqpY0e_GqGWFufDwlY4yxXWXrGF}
|
init_password.db_password_confirm = ${PROLE_SECRET:v1:z2-5uGBCGoUaPEJm:RPb2Hoei9ikHHjLHbXQx3Ej2AxuXDrX2}
|
||||||
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 = true
|
kerberos_config.enabled = true
|
||||||
kerberos_config.init_authority = false
|
|
||||||
kerberos_config.kdc = 10.0.0.3
|
kerberos_config.kdc = 10.0.0.3
|
||||||
kerberos_config.password = ${PROLE_SECRET:v1:71ROfYyYysQDJDpX:0K4cB3p9nnMb1yi2OImqUm-pa9Zg21JYTmYHzYB_7czTKMQJ}
|
kerberos_config.password = ${PROLE_SECRET:v1:ctSSZbqz2VXQSk7Y:eVFtcFMoru50wpG7_1wgl1mQbsxEKfMsehQHpKcalJolggZK}
|
||||||
kerberos_config.realm = PROLE.ORG
|
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 = fairyland.prole.org
|
ollama_config.server_host =
|
||||||
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 = ${PROLE_SECRET:v1:NmgNZUhCsk-p0_DF:VB1tKREj45eEWcnN1GAlcGV6b_Lrl9sx}
|
DB_PASSWORD = ${PROLE_SECRET:v1:eo18GXaCA2dv5ner:paML1Ebi5MfO_F2gnLTiAVaXdJoeRN9U}
|
||||||
DEPLOYMENT_MODE = k3s
|
DEPLOYMENT_MODE = k3s
|
||||||
MODE = k3s
|
|
||||||
DEPLOYMENT_TARGET = prole-service-cluster
|
DEPLOYMENT_TARGET = prole-service-cluster
|
||||||
DOCKER_IMPORT_DIR =
|
|
||||||
DOCKER_PRELOAD = false
|
DOCKER_PRELOAD = false
|
||||||
|
KNOE_DB_USER = root
|
||||||
|
KUBECONTEXT = prole-k3s
|
||||||
NAMESPACE = knoe-db
|
NAMESPACE = knoe-db
|
||||||
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2
|
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2
|
||||||
KNOE_DB_USER = root
|
|
||||||
PROLE_HOME = $HOME/dev/prole
|
|
||||||
PROLE_K3S_SERVER = https://myrddin.prole.org:6443
|
PROLE_K3S_SERVER = https://myrddin.prole.org:6443
|
||||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==}
|
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:WjD201Yr7MHE56cs:wackF_EDDde7KmJp2SwbMlJi5mLdoHATkoyolRkPVqvnOVVScriN55ZHOO0cec-RvMIFQzhkL_yZbKhNkN9IIfkRdnAeGPDVELtM-wEDv_YlymTsrpYL_u79VR-nX3gKTCfcAha44ZyoBzObKTShzUx8eLCJMeLKYoPEKg==}
|
||||||
PROLE_OPENTOFU_URL = http://myrddin.prole.org:8080
|
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
||||||
REGISTRY_NAMESPACE = knoe-system
|
|
||||||
SERVICE_NAMESPACE = knoe-system
|
SERVICE_NAMESPACE = knoe-system
|
||||||
|
|
||||||
[Welcome]
|
[Welcome]
|
||||||
; No configuration values captured yet for this section.
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Dependencies]
|
[Dependencies]
|
||||||
STATUS = All installed
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Network]
|
[Network]
|
||||||
AD_DC_HOST = myrddin.prole.org
|
AD_DC_HOST = myrddin.prole.org
|
||||||
AD_DC_IP = 10.0.0.3
|
AD_DC_IP = 10.0.0.3
|
||||||
ANSIBLE_DOMAIN = prole.org
|
ANSIBLE_DOMAIN = prole.org
|
||||||
ANSIBLE_INFRASTRUCTURE = $HOME/dev/prole/infrastructure
|
ANSIBLE_INFRASTRUCTURE = /Users/chrisfu/dev/prole/infrastructure
|
||||||
ANSIBLE_INVENTORY = $HOME/dev/prole/infrastructure/inventory
|
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
|
||||||
@ -117,22 +112,14 @@ KDC_AUTO_DETECTED = 10.0.0.3
|
|||||||
KERBEROS_AUTO_ENABLED = True
|
KERBEROS_AUTO_ENABLED = True
|
||||||
|
|
||||||
[Port Forwards]
|
[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
|
; No configuration values captured yet for this section.
|
||||||
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/knoe-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]
|
[System Environment]
|
||||||
NAMESPACE = knoe-db
|
PROLE_CONF = /Users/chrisfu/dev/prole/conf
|
||||||
PROLE_CONF = $HOME/dev/prole/conf
|
PROLE_DATA = /Users/chrisfu/dev/prole/data
|
||||||
PROLE_DATA = $HOME/dev/prole/data
|
PROLE_HOME = /Users/chrisfu/dev/prole
|
||||||
PROLE_HOME = $HOME/dev/prole
|
PROLE_LOGS = /Users/chrisfu/dev/prole/logs
|
||||||
PROLE_LOGS = $HOME/dev/prole/logs
|
PROLE_SERVICE = /Users/chrisfu/dev/prole/etc
|
||||||
PROLE_SERVICE = $HOME/dev/prole/etc
|
|
||||||
|
|
||||||
[Monitoring]
|
[Monitoring]
|
||||||
; No configuration values captured yet for this section.
|
; No configuration values captured yet for this section.
|
||||||
@ -141,43 +128,55 @@ PROLE_SERVICE = $HOME/dev/prole/etc
|
|||||||
; No configuration values captured yet for this section.
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Ollama]
|
[Ollama]
|
||||||
OLLAMA_HOST = http://fairyland.prole.org:11434
|
; No configuration values captured yet for this section.
|
||||||
OLLAMA_SERVERS = fairyland.prole.org:11434,morgoth.prole.org:11434
|
|
||||||
OLLAMA_SERVER_HOST = fairyland.prole.org
|
|
||||||
OLLAMA_SERVER_PORT = 11434
|
|
||||||
|
|
||||||
[Optional Features]
|
[Optional Features]
|
||||||
AT_REST_ENCRYPTION_ENABLED = true
|
AT_REST_ENCRYPTION_ENABLED = True
|
||||||
GITOPS_ENABLED = false
|
GITOPS_ENABLED = False
|
||||||
KERBEROS_ENABLED = true
|
KERBEROS_ENABLED = True
|
||||||
SUPABASE_ENABLED = false
|
SUPABASE_ENABLED = False
|
||||||
|
|
||||||
[GitOps]
|
[GitOps]
|
||||||
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Database Creation]
|
[Database Creation]
|
||||||
DB_NAME = knoe-db-0
|
; No configuration values captured yet for this section.
|
||||||
DB_USER = root
|
|
||||||
|
|
||||||
[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:WjD201Yr7MHE56cs:wackF_EDDde7KmJp2SwbMlJi5mLdoHATkoyolRkPVqvnOVVScriN55ZHOO0cec-RvMIFQzhkL_yZbKhNkN9IIfkRdnAeGPDVELtM-wEDv_YlymTsrpYL_u79VR-nX3gKTCfcAha44ZyoBzObKTShzUx8eLCJMeLKYoPEKg==}
|
||||||
|
|
||||||
|
[Dev Cluster (k3d)]
|
||||||
|
CLUSTER_ENV = dev
|
||||||
|
DISPLAY_NAME = knoe-dev-cluster
|
||||||
|
KUBECTL_CONTEXT = prole-k3s
|
||||||
|
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 = https://myrddin.prole.org:6443
|
K3S_SERVER_URL = https://myrddin.prole.org:6443
|
||||||
K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==}
|
K3S_TOKEN = ${PROLE_SECRET:v1:WjD201Yr7MHE56cs:wackF_EDDde7KmJp2SwbMlJi5mLdoHATkoyolRkPVqvnOVVScriN55ZHOO0cec-RvMIFQzhkL_yZbKhNkN9IIfkRdnAeGPDVELtM-wEDv_YlymTsrpYL_u79VR-nX3gKTCfcAha44ZyoBzObKTShzUx8eLCJMeLKYoPEKg==}
|
||||||
MODE = k3s
|
MODE = k3s
|
||||||
PIPELINE_URL = http://myrddin.prole.org:8080
|
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]
|
[Docker Build]
|
||||||
; No configuration values captured yet for this section.
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Initialization Scripts]
|
[Initialization Scripts]
|
||||||
; No configuration values captured yet for this section.
|
STATUS = Attempted
|
||||||
|
|
||||||
[Deployment]
|
[Deployment]
|
||||||
MODE = k3s
|
MODE = k3s
|
||||||
TARGET = prole-service-cluster
|
TARGET = prole-service-cluster
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
STATUS = Failed
|
; No configuration values captured yet for this section.
|
||||||
|
|||||||
@ -4,7 +4,7 @@ metadata:
|
|||||||
name: knoe-db
|
name: knoe-db
|
||||||
spec:
|
spec:
|
||||||
instances: 3
|
instances: 3
|
||||||
imageName: k3d-prole-registry:5000/knoe-db:18-140
|
imageName: registry.test-system.svc.cluster.local:5000/knoe-db:18-140
|
||||||
postgresUID: 100
|
postgresUID: 100
|
||||||
postgresGID: 101
|
postgresGID: 101
|
||||||
maxSyncReplicas: 1
|
maxSyncReplicas: 1
|
||||||
|
|||||||
@ -106,10 +106,12 @@ BARMAN_PLUGIN_FALLBACK_VERSION=${BARMAN_PLUGIN_FALLBACK_VERSION:-0.9.0}
|
|||||||
CERT_MANAGER_MANIFEST_URL=${CERT_MANAGER_MANIFEST_URL:-}
|
CERT_MANAGER_MANIFEST_URL=${CERT_MANAGER_MANIFEST_URL:-}
|
||||||
CERT_MANAGER_FALLBACK_VERSION=${CERT_MANAGER_FALLBACK_VERSION:-1.19.3}
|
CERT_MANAGER_FALLBACK_VERSION=${CERT_MANAGER_FALLBACK_VERSION:-1.19.3}
|
||||||
|
|
||||||
# Protected storage requirements (k3s only)
|
# CNPG storage requirements (k3s only)
|
||||||
PROLE_CNPG_STORAGE_CLASS=${PROLE_CNPG_STORAGE_CLASS:-synology-iscsi}
|
CNPG_STORAGE_CLASS=${CNPG_STORAGE_CLASS:-synology-iscsi}
|
||||||
PROLE_PROTECTED_DATA_PATH=${PROLE_PROTECTED_DATA_PATH:-/synology/d001/data}
|
SYNOLOGY_ROOTS=${SYNOLOGY_ROOTS:-/synology/d001,/synology/d002,/synology/d003,/synology/d004}
|
||||||
PROLE_PROTECTED_WAL_PATH=${PROLE_PROTECTED_WAL_PATH:-/synology/d001/wal}
|
# Legacy compatibility vars kept for callers that still export them.
|
||||||
|
PROLE_PROTECTED_DATA_PATH=${PROLE_PROTECTED_DATA_PATH:-}
|
||||||
|
PROLE_PROTECTED_WAL_PATH=${PROLE_PROTECTED_WAL_PATH:-}
|
||||||
|
|
||||||
if [[ "$ACTION" != "deploy" && "$ACTION" != "rollout" && "$ACTION" != "force-rollout" ]]; then
|
if [[ "$ACTION" != "deploy" && "$ACTION" != "rollout" && "$ACTION" != "force-rollout" ]]; then
|
||||||
if [[ -n "${2:-}" ]]; then
|
if [[ -n "${2:-}" ]]; then
|
||||||
@ -169,18 +171,17 @@ _require_tool() {
|
|||||||
command -v "$t" >/dev/null 2>&1 || { echo "ERROR: Missing required tool: $t" >&2; return 1; }
|
command -v "$t" >/dev/null 2>&1 || { echo "ERROR: Missing required tool: $t" >&2; return 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
_require_prole_protected_mount() {
|
_require_non_root_mount() {
|
||||||
local path="$1" label="$2"
|
local path="$1" label="$2"
|
||||||
local findmnt_bin="${PROLE_FINDMNT_BIN:-findmnt}"
|
local findmnt_bin="${PROLE_FINDMNT_BIN:-findmnt}"
|
||||||
|
|
||||||
if [[ ! -d "$path" ]]; then
|
if [[ ! -d "$path" ]]; then
|
||||||
echo "ERROR: Protected ${label} path missing: ${path}" >&2
|
echo "ERROR: ${label} path missing: ${path}" >&2
|
||||||
echo "Refusing CNPG deployment: Prole protected storage must be mounted at ${PROLE_PROTECTED_DATA_PATH} and ${PROLE_PROTECTED_WAL_PATH}." >&2
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_require_tool "$findmnt_bin" || {
|
_require_tool "$findmnt_bin" || {
|
||||||
echo "ERROR: '${findmnt_bin}' is required to validate protected storage mounts (k3s mode)." >&2
|
echo "ERROR: '${findmnt_bin}' is required to validate storage mounts (k3s mode)." >&2
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,21 +197,104 @@ _require_prole_protected_mount() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$path_target" == "/" || "$path_src" == "$root_src" ]]; then
|
if [[ "$path_target" == "/" || "$path_src" == "$root_src" ]]; then
|
||||||
echo "ERROR: Protected ${label} path '${path}' is backed by the root filesystem (target='${path_target}', source='${path_src}')." >&2
|
echo "ERROR: ${label} path '${path}' is backed by the root filesystem (target='${path_target}', source='${path_src}')." >&2
|
||||||
echo "Refusing CNPG deployment to prevent root/SD-backed storage from being used." >&2
|
echo "Refusing CNPG deployment to prevent root/SD-backed storage from being used." >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_validate_cnpg_host_path() {
|
||||||
|
local path="$1" role="$2"
|
||||||
|
shift 2
|
||||||
|
local roots=("$@")
|
||||||
|
|
||||||
|
if [[ -z "$path" ]]; then
|
||||||
|
echo "ERROR: Missing computed CNPG ${role} host path for k3s storage validation." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! _require_non_root_mount "$path" "CNPG ${role}"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local expected_suffix="/${NAMESPACE}/${CNPG_CLUSTER_NAME}/${role}"
|
||||||
|
if [[ "$path" != *"$expected_suffix" ]]; then
|
||||||
|
echo "ERROR: Computed CNPG ${role} host path '${path}' does not match expected suffix '${expected_suffix}'." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local root
|
||||||
|
for root in "${roots[@]}"; do
|
||||||
|
if [[ "$path" == "$root" || "$path" == "$root"/* ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "ERROR: Computed CNPG ${role} host path '${path}' is not under configured Synology roots (${roots[*]})." >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
ensure_prole_protected_storage() {
|
ensure_prole_protected_storage() {
|
||||||
if [[ "${PROLE_MODE:-}" != "k3s" ]]; then
|
if [[ "${PROLE_MODE:-}" != "k3s" ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Validating protected Prole storage mounts (k3s mode) ..."
|
echo "Validating Synology storage roots and CNPG host paths (k3s mode) ..."
|
||||||
_require_prole_protected_mount "$PROLE_PROTECTED_DATA_PATH" "data" || return 1
|
|
||||||
_require_prole_protected_mount "$PROLE_PROTECTED_WAL_PATH" "WAL" || return 1
|
local target_node="${CNPG_STAGE1_NODE:-}"
|
||||||
|
local local_host="" local_short="" local_fqdn=""
|
||||||
|
local_host=$(hostname 2>/dev/null || true)
|
||||||
|
local_short=$(hostname -s 2>/dev/null || true)
|
||||||
|
local_fqdn=$(hostname -f 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [[ -z "$local_short" && -n "$local_host" ]]; then
|
||||||
|
local_short="$local_host"
|
||||||
|
fi
|
||||||
|
if [[ -z "$local_fqdn" && -n "$local_host" ]]; then
|
||||||
|
local_fqdn="$local_host"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local run_local_checks=0
|
||||||
|
if [[ -z "$target_node" ]]; then
|
||||||
|
run_local_checks=1
|
||||||
|
elif [[ "$target_node" == "$local_fqdn" || "$target_node" == "$local_short" || "$target_node" == "$local_host" ]]; then
|
||||||
|
run_local_checks=1
|
||||||
|
elif [[ -n "$local_fqdn" && "$local_fqdn" == "$target_node".* ]]; then
|
||||||
|
run_local_checks=1
|
||||||
|
elif [[ -n "$local_short" && "$target_node" == "$local_short".* ]]; then
|
||||||
|
run_local_checks=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( run_local_checks == 0 )); then
|
||||||
|
echo "INFO: Skipping local Synology storage preflight because CNPG target node '${target_node}' is remote from this host ('${local_fqdn:-${local_short:-unknown}}')." >&2
|
||||||
|
echo "INFO: Remote node storage validation is not available yet; continuing with configured CNPG storage paths." >&2
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local root roots=()
|
||||||
|
for root in ${SYNOLOGY_ROOTS//,/ }; do
|
||||||
|
[[ -n "$root" ]] && roots+=("$root")
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ${#roots[@]} == 0 )); then
|
||||||
|
echo "ERROR: SYNOLOGY_ROOTS is empty; expected one or more Synology storage roots." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for root in "${roots[@]}"; do
|
||||||
|
_require_non_root_mount "$root" "Synology storage root" || return 1
|
||||||
|
done
|
||||||
|
|
||||||
|
local data_host_path="${CNPG_DATA_HOST_PATH:-}"
|
||||||
|
local wal_host_path="${CNPG_WAL_HOST_PATH:-}"
|
||||||
|
if [[ -n "$data_host_path" || -n "$wal_host_path" ]]; then
|
||||||
|
_validate_cnpg_host_path "$data_host_path" "data" "${roots[@]}" || return 1
|
||||||
|
_validate_cnpg_host_path "$wal_host_path" "wal" "${roots[@]}" || return 1
|
||||||
|
else
|
||||||
|
echo "INFO: Computed CNPG host paths were not provided; validated Synology storage roots only." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,14 +310,29 @@ apply_cnpg_selector_overrides() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local data_selector_json="${PROLE_CNPG_DATA_SELECTOR_JSON:-}"
|
local expected_ns="$NAMESPACE"
|
||||||
local wal_selector_json="${PROLE_CNPG_WAL_SELECTOR_JSON:-}"
|
local expected_cluster="$CNPG_CLUSTER_NAME"
|
||||||
if [[ -z "$data_selector_json" || -z "$wal_selector_json" ]]; then
|
if [[ -z "$expected_ns" || -z "$expected_cluster" ]]; then
|
||||||
return 0
|
echo "ERROR: Unable to derive namespace/cluster for CNPG selector injection (namespace='${expected_ns:-<missing>}', cluster='${expected_cluster:-<missing>}')." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local data_selector_json="${CNPG_DATA_SELECTOR_JSON:-}"
|
||||||
|
local wal_selector_json="${CNPG_WAL_SELECTOR_JSON:-}"
|
||||||
|
if [[ -z "$data_selector_json" ]]; then
|
||||||
|
data_selector_json='{}'
|
||||||
|
fi
|
||||||
|
if [[ -z "$wal_selector_json" ]]; then
|
||||||
|
wal_selector_json='{}'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local patch_json patched
|
local patch_json patched
|
||||||
if ! patch_json=$(jq -cn --argjson data "$data_selector_json" --argjson wal "$wal_selector_json" '{"spec":{"storage":{"pvcTemplate":{"selector":{"matchLabels":$data}}},"walStorage":{"pvcTemplate":{"selector":{"matchLabels":$wal}}}}}'); then
|
if ! patch_json=$(jq -cn \
|
||||||
|
--argjson data "$data_selector_json" \
|
||||||
|
--argjson wal "$wal_selector_json" \
|
||||||
|
--arg ns "$expected_ns" \
|
||||||
|
--arg cluster "$expected_cluster" \
|
||||||
|
'{"spec":{"storage":{"pvcTemplate":{"selector":{"matchLabels":((if ($data|type)=="object" then $data else {} end) + {"synology.storage/role":"data","prole.io/namespace":$ns,"prole.io/cluster":$cluster})}}},"walStorage":{"pvcTemplate":{"selector":{"matchLabels":((if ($wal|type)=="object" then $wal else {} end) + {"synology.storage/role":"wal","prole.io/namespace":$ns,"prole.io/cluster":$cluster})}}}}}'); then
|
||||||
echo "ERROR: Invalid CNPG selector JSON provided by installer (data/wal)." >&2
|
echo "ERROR: Invalid CNPG selector JSON provided by installer (data/wal)." >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -338,8 +437,8 @@ validate_cnpg_manifest_storage() {
|
|||||||
echo "CNPG manifest storage selector (final data): role='${data_role:-<missing>}', namespace='${data_ns:-<missing>}', cluster='${data_cluster:-<missing>}'"
|
echo "CNPG manifest storage selector (final data): role='${data_role:-<missing>}', namespace='${data_ns:-<missing>}', cluster='${data_cluster:-<missing>}'"
|
||||||
echo "CNPG manifest storage selector (final wal): role='${wal_role:-<missing>}', namespace='${wal_ns:-<missing>}', cluster='${wal_cluster:-<missing>}'"
|
echo "CNPG manifest storage selector (final wal): role='${wal_role:-<missing>}', namespace='${wal_ns:-<missing>}', cluster='${wal_cluster:-<missing>}'"
|
||||||
|
|
||||||
if [[ "$data_sc" != "$PROLE_CNPG_STORAGE_CLASS" || "$wal_sc" != "$PROLE_CNPG_STORAGE_CLASS" ]]; then
|
if [[ "$data_sc" != "$CNPG_STORAGE_CLASS" || "$wal_sc" != "$CNPG_STORAGE_CLASS" ]]; then
|
||||||
echo "ERROR: CNPG Cluster manifest must set storageClassName=${PROLE_CNPG_STORAGE_CLASS} for both data and walStorage (got data='${data_sc:-<missing>}', wal='${wal_sc:-<missing>}')." >&2
|
echo "ERROR: CNPG Cluster manifest must set storageClassName=${CNPG_STORAGE_CLASS} for both data and walStorage (got data='${data_sc:-<missing>}', wal='${wal_sc:-<missing>}')." >&2
|
||||||
echo "Refusing deployment to prevent fallback to default storage class." >&2
|
echo "Refusing deployment to prevent fallback to default storage class." >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@ -370,7 +469,7 @@ validate_cnpg_runtime_storage() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local sc="$PROLE_CNPG_STORAGE_CLASS"
|
local sc="$CNPG_STORAGE_CLASS"
|
||||||
local cluster_json
|
local cluster_json
|
||||||
cluster_json=$(kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" -o json 2>/dev/null || true)
|
cluster_json=$(kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" -o json 2>/dev/null || true)
|
||||||
if [[ -z "$cluster_json" ]]; then
|
if [[ -z "$cluster_json" ]]; then
|
||||||
@ -1840,7 +1939,10 @@ cleanup_unintended_cnpg_services() {
|
|||||||
ensure_prole_stack_resources() {
|
ensure_prole_stack_resources() {
|
||||||
echo "Applying CloudNative-PG cluster and related resources ..."
|
echo "Applying CloudNative-PG cluster and related resources ..."
|
||||||
wait_for_apiserver_ready 180
|
wait_for_apiserver_ready 180
|
||||||
ensure_prole_protected_storage
|
if ! ensure_prole_protected_storage; then
|
||||||
|
echo "ERROR: CNPG storage validation failed; skipping CNPG deployment path." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
cleanup_unintended_cnpg_services || true
|
cleanup_unintended_cnpg_services || true
|
||||||
local image_override="${CNPG_IMAGE:-${KNOE_DB_IMAGE:-}}"
|
local image_override="${CNPG_IMAGE:-${KNOE_DB_IMAGE:-}}"
|
||||||
local image=""
|
local image=""
|
||||||
@ -2888,7 +2990,10 @@ restore_cnpg_cluster() {
|
|||||||
ensure_cnpg_operator
|
ensure_cnpg_operator
|
||||||
pin_cnpg_controller
|
pin_cnpg_controller
|
||||||
ensure_barman_plugin
|
ensure_barman_plugin
|
||||||
ensure_prole_protected_storage
|
if ! ensure_prole_protected_storage; then
|
||||||
|
echo "ERROR: CNPG storage validation failed; aborting CNPG restore." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then
|
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 "ERROR: CNPG cluster '$CNPG_CLUSTER_NAME' already exists in namespace '$NAMESPACE'." >&2
|
||||||
@ -3348,7 +3453,10 @@ deploy_cluster() {
|
|||||||
pin_cnpg_controller
|
pin_cnpg_controller
|
||||||
ensure_barman_plugin
|
ensure_barman_plugin
|
||||||
|
|
||||||
ensure_prole_protected_storage
|
if ! ensure_prole_protected_storage; then
|
||||||
|
echo "ERROR: CNPG storage validation failed; aborting CNPG deploy." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
sync_manifest_image "$image"
|
sync_manifest_image "$image"
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ metadata:
|
|||||||
name: knoe-db
|
name: knoe-db
|
||||||
spec:
|
spec:
|
||||||
instances: 3
|
instances: 3
|
||||||
imageName: k3d-prole-registry:5000/knoe-db:18-140
|
imageName: example.invalid:5000/knoe-db:0.0.0
|
||||||
postgresUID: 100
|
postgresUID: 100
|
||||||
postgresGID: 101
|
postgresGID: 101
|
||||||
maxSyncReplicas: 1
|
maxSyncReplicas: 1
|
||||||
|
|||||||
@ -1510,9 +1510,9 @@ class ProleInstaller:
|
|||||||
|
|
||||||
raw_server, raw_token = self._resolve_k3s_connection()
|
raw_server, raw_token = self._resolve_k3s_connection()
|
||||||
if raw_server:
|
if raw_server:
|
||||||
env["PROLE_K3S_SERVER"] = raw_server
|
env["K3S_SERVER"] = raw_server
|
||||||
if raw_token:
|
if raw_token:
|
||||||
env["PROLE_K3S_TOKEN"] = raw_token
|
env["K3S_TOKEN"] = raw_token
|
||||||
|
|
||||||
elif mode == "k3d":
|
elif mode == "k3d":
|
||||||
# Ensure k3d dev clusters have a resolvable KUBECONFIG.
|
# Ensure k3d dev clusters have a resolvable KUBECONFIG.
|
||||||
@ -1781,24 +1781,28 @@ class ProleInstaller:
|
|||||||
)
|
)
|
||||||
provisioned = provision_cluster_storage(spec)
|
provisioned = provision_cluster_storage(spec)
|
||||||
|
|
||||||
env["PROLE_CNPG_STORAGE_CLASS"] = spec.storage_class_name
|
env["CNPG_STORAGE_CLASS"] = spec.storage_class_name
|
||||||
env["PROLE_CNPG_DATA_SELECTOR_JSON"] = json.dumps(
|
env["CNPG_DATA_SELECTOR_JSON"] = json.dumps(
|
||||||
provisioned.data_selector,
|
provisioned.data_selector,
|
||||||
separators=(",", ":"),
|
separators=(",", ":"),
|
||||||
sort_keys=True,
|
sort_keys=True,
|
||||||
)
|
)
|
||||||
env["PROLE_CNPG_WAL_SELECTOR_JSON"] = json.dumps(
|
env["CNPG_WAL_SELECTOR_JSON"] = json.dumps(
|
||||||
provisioned.wal_selector,
|
provisioned.wal_selector,
|
||||||
separators=(",", ":"),
|
separators=(",", ":"),
|
||||||
sort_keys=True,
|
sort_keys=True,
|
||||||
)
|
)
|
||||||
env["PROLE_CNPG_DATA_PV_NAME"] = provisioned.data_pv_name
|
env["CNPG_DATA_PV_NAME"] = provisioned.data_pv_name
|
||||||
env["PROLE_CNPG_WAL_PV_NAME"] = provisioned.wal_pv_name
|
env["CNPG_WAL_PV_NAME"] = provisioned.wal_pv_name
|
||||||
|
env["CNPG_DATA_HOST_PATH"] = provisioned.data_path
|
||||||
|
env["CNPG_WAL_HOST_PATH"] = provisioned.wal_path
|
||||||
|
env["SYNOLOGY_ROOTS"] = ",".join(spec.synology_roots)
|
||||||
|
|
||||||
self.log(
|
self.log(
|
||||||
"[CNPG] Provisioned cluster storage "
|
"[CNPG] Provisioned cluster storage "
|
||||||
f"namespace={namespace} cluster={cluster_name} node={node_name} "
|
f"namespace={namespace} cluster={cluster_name} node={node_name} "
|
||||||
f"data_pv={provisioned.data_pv_name} wal_pv={provisioned.wal_pv_name}"
|
f"data_pv={provisioned.data_pv_name} wal_pv={provisioned.wal_pv_name} "
|
||||||
|
f"data_path={provisioned.data_path} wal_path={provisioned.wal_path}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# --------------------------------------------- authority / repair
|
# --------------------------------------------- authority / repair
|
||||||
@ -3302,17 +3306,17 @@ class ProleConsoleInstaller(ProleInstaller):
|
|||||||
legacy["init_cluster.cluster_env"] = _expand_cfg_value(
|
legacy["init_cluster.cluster_env"] = _expand_cfg_value(
|
||||||
sec.get("CLUSTER_ENV", ""), cfg_vars
|
sec.get("CLUSTER_ENV", ""), cfg_vars
|
||||||
)
|
)
|
||||||
if "PROLE_K3S_SERVER" in sec:
|
if "K3S_SERVER" in sec:
|
||||||
legacy["init_cluster.k3s_server_url"] = _expand_cfg_value(
|
legacy["init_cluster.k3s_server_url"] = _expand_cfg_value(
|
||||||
sec.get("PROLE_K3S_SERVER", ""), cfg_vars
|
sec.get("K3S_SERVER", ""), cfg_vars
|
||||||
)
|
)
|
||||||
if "K3S_SERVER_URL" in sec and "init_cluster.k3s_server_url" not in legacy:
|
if "K3S_SERVER_URL" in sec and "init_cluster.k3s_server_url" not in legacy:
|
||||||
legacy["init_cluster.k3s_server_url"] = _expand_cfg_value(
|
legacy["init_cluster.k3s_server_url"] = _expand_cfg_value(
|
||||||
sec.get("K3S_SERVER_URL", ""), cfg_vars
|
sec.get("K3S_SERVER_URL", ""), cfg_vars
|
||||||
)
|
)
|
||||||
if "PROLE_K3S_TOKEN" in sec:
|
if "K3S_TOKEN" in sec:
|
||||||
legacy["init_cluster.k3s_token"] = _expand_cfg_value(
|
legacy["init_cluster.k3s_token"] = _expand_cfg_value(
|
||||||
sec.get("PROLE_K3S_TOKEN", ""), cfg_vars
|
sec.get("K3S_TOKEN", ""), cfg_vars
|
||||||
)
|
)
|
||||||
if "K3S_TOKEN" in sec and "init_cluster.k3s_token" not in legacy:
|
if "K3S_TOKEN" in sec and "init_cluster.k3s_token" not in legacy:
|
||||||
legacy["init_cluster.k3s_token"] = _expand_cfg_value(
|
legacy["init_cluster.k3s_token"] = _expand_cfg_value(
|
||||||
@ -3671,7 +3675,7 @@ class ProleConsoleInstaller(ProleInstaller):
|
|||||||
host = _host_from_url(server)
|
host = _host_from_url(server)
|
||||||
if not host:
|
if not host:
|
||||||
self.err(
|
self.err(
|
||||||
"[WARN] k3s mode but K3S_SERVER_URL/PROLE_K3S_SERVER not set; cannot resolve registry."
|
"[WARN] k3s mode but K3S_SERVER_URL/K3S_SERVER not set; cannot resolve registry."
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
host_registry = f"{host}:5000"
|
host_registry = f"{host}:5000"
|
||||||
@ -3965,13 +3969,22 @@ class ProleConsoleInstaller(ProleInstaller):
|
|||||||
if cnpg_eligible_nodes:
|
if cnpg_eligible_nodes:
|
||||||
glob["CNPG_ELIGIBLE_NODES"] = ",".join(cnpg_eligible_nodes)
|
glob["CNPG_ELIGIBLE_NODES"] = ",".join(cnpg_eligible_nodes)
|
||||||
|
|
||||||
|
synology_roots = sorted(
|
||||||
|
{
|
||||||
|
str((details or {}).get("path") or "").strip()
|
||||||
|
for details in mounts.values()
|
||||||
|
if isinstance(details, dict)
|
||||||
|
and str((details or {}).get("path") or "").strip()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if synology_roots:
|
||||||
|
glob["SYNOLOGY_ROOTS"] = ",".join(synology_roots)
|
||||||
|
|
||||||
# CNPG protected storage defaults (k3s local PV)
|
# CNPG protected storage defaults (k3s local PV)
|
||||||
d001 = mounts.get("d001") or {}
|
d001 = mounts.get("d001") or {}
|
||||||
d001_base = (d001.get("path") or "").strip()
|
d001_base = (d001.get("path") or "").strip()
|
||||||
d001_host = (d001.get("host") or "").strip()
|
d001_host = (d001.get("host") or "").strip()
|
||||||
if d001_base and d001_host:
|
if d001_base and d001_host:
|
||||||
glob["PROLE_PROTECTED_DATA_PATH"] = f"{d001_base}/data"
|
|
||||||
glob["PROLE_PROTECTED_WAL_PATH"] = f"{d001_base}/wal"
|
|
||||||
glob["CNPG_STAGE1_NODE"] = d001_host
|
glob["CNPG_STAGE1_NODE"] = d001_host
|
||||||
glob["CNPG_DB_NODE_SELECTOR"] = f"kubernetes.io/hostname={d001_host}"
|
glob["CNPG_DB_NODE_SELECTOR"] = f"kubernetes.io/hostname={d001_host}"
|
||||||
|
|
||||||
@ -3985,7 +3998,7 @@ class ProleConsoleInstaller(ProleInstaller):
|
|||||||
mon_base = (mon_choice.get("path") or "").strip()
|
mon_base = (mon_choice.get("path") or "").strip()
|
||||||
mon_host = (mon_choice.get("host") or "").strip()
|
mon_host = (mon_choice.get("host") or "").strip()
|
||||||
if mon_base:
|
if mon_base:
|
||||||
glob["PROLE_MONITORING_DATA_DIR"] = mon_base
|
glob["MONITORING_DATA_DIR"] = mon_base
|
||||||
if mon_host:
|
if mon_host:
|
||||||
self.prole_cfg_data.setdefault("Monitoring", {})[
|
self.prole_cfg_data.setdefault("Monitoring", {})[
|
||||||
"MONITORING_PRIMARY_NODE"
|
"MONITORING_PRIMARY_NODE"
|
||||||
@ -4072,17 +4085,17 @@ class ProleConsoleInstaller(ProleInstaller):
|
|||||||
self._get_input("init_password.db_host_port", "5432") or "5432"
|
self._get_input("init_password.db_host_port", "5432") or "5432"
|
||||||
).strip(),
|
).strip(),
|
||||||
"DOCKER_IMPORT_DIR": self.docker_import_dir or "",
|
"DOCKER_IMPORT_DIR": self.docker_import_dir or "",
|
||||||
"PROLE_K3S_SERVER": (
|
"K3S_SERVER": (
|
||||||
self._get_input("init_cluster.k3s_server_url", "") or ""
|
self._get_input("init_cluster.k3s_server_url", "") or ""
|
||||||
).strip(),
|
).strip(),
|
||||||
"PROLE_K3S_TOKEN": self._secret_cfg_value(
|
"K3S_TOKEN": self._secret_cfg_value(
|
||||||
"Global",
|
"Global",
|
||||||
"PROLE_K3S_TOKEN",
|
"K3S_TOKEN",
|
||||||
self._get_input("init_cluster.k3s_token", ""),
|
self._get_input("init_cluster.k3s_token", ""),
|
||||||
"k3s",
|
"k3s",
|
||||||
"token",
|
"token",
|
||||||
),
|
),
|
||||||
"PROLE_OPENTOFU_URL": _default_opentofu_pipeline_url(),
|
"OPENTOFU_URL": _default_opentofu_pipeline_url(),
|
||||||
"SERVICE_NAMESPACE": self._service_namespace(),
|
"SERVICE_NAMESPACE": self._service_namespace(),
|
||||||
}
|
}
|
||||||
# Merge pre-existing Global values without overriding explicit inputs
|
# Merge pre-existing Global values without overriding explicit inputs
|
||||||
@ -4432,14 +4445,14 @@ class ProleConsoleInstaller(ProleInstaller):
|
|||||||
or _cfg_val(sys_sec, "PROLE_SERVICE")
|
or _cfg_val(sys_sec, "PROLE_SERVICE")
|
||||||
or _cfg_val(global_sec, "PROLE_SERVICE")
|
or _cfg_val(global_sec, "PROLE_SERVICE")
|
||||||
),
|
),
|
||||||
"PROLE_OPENTOFU_URL": (os.environ.get("PROLE_OPENTOFU_URL") or "").strip(),
|
"OPENTOFU_URL": (os.environ.get("OPENTOFU_URL") or "").strip(),
|
||||||
}
|
}
|
||||||
defaults = self._env_defaults(ns_hint)
|
defaults = self._env_defaults(ns_hint)
|
||||||
for k in vals:
|
for k in vals:
|
||||||
if not vals[k]:
|
if not vals[k]:
|
||||||
vals[k] = defaults.get(k, "")
|
vals[k] = defaults.get(k, "")
|
||||||
if not vals.get("PROLE_OPENTOFU_URL"):
|
if not vals.get("OPENTOFU_URL"):
|
||||||
vals["PROLE_OPENTOFU_URL"] = _default_opentofu_pipeline_url()
|
vals["OPENTOFU_URL"] = _default_opentofu_pipeline_url()
|
||||||
vals["NAMESPACE"] = self._get_input("env_setup.NAMESPACE", "") or ns_hint
|
vals["NAMESPACE"] = self._get_input("env_setup.NAMESPACE", "") or ns_hint
|
||||||
|
|
||||||
if not vals.get("PROLE_HOME"):
|
if not vals.get("PROLE_HOME"):
|
||||||
@ -4450,7 +4463,7 @@ class ProleConsoleInstaller(ProleInstaller):
|
|||||||
"PROLE_CONF",
|
"PROLE_CONF",
|
||||||
"PROLE_DATA",
|
"PROLE_DATA",
|
||||||
"PROLE_LOGS",
|
"PROLE_LOGS",
|
||||||
"PROLE_SERVICE",
|
"ROLE_SERVICE",
|
||||||
):
|
):
|
||||||
self.inputs[f"env_setup.{k}"] = vals[k]
|
self.inputs[f"env_setup.{k}"] = vals[k]
|
||||||
self.inputs["env_setup.NAMESPACE"] = vals.get("NAMESPACE", "")
|
self.inputs["env_setup.NAMESPACE"] = vals.get("NAMESPACE", "")
|
||||||
@ -4463,7 +4476,7 @@ class ProleConsoleInstaller(ProleInstaller):
|
|||||||
"PROLE_DATA",
|
"PROLE_DATA",
|
||||||
"PROLE_LOGS",
|
"PROLE_LOGS",
|
||||||
"PROLE_SERVICE",
|
"PROLE_SERVICE",
|
||||||
"PROLE_OPENTOFU_URL",
|
"OPENTOFU_URL",
|
||||||
):
|
):
|
||||||
self.prole_cfg_data["System Environment"][k] = vals[k]
|
self.prole_cfg_data["System Environment"][k] = vals[k]
|
||||||
|
|
||||||
@ -5364,6 +5377,12 @@ class ProleConsoleInstaller(ProleInstaller):
|
|||||||
self.err(f"[ERROR] Failed to ensure DB secrets before CNPG init: {e}")
|
self.err(f"[ERROR] Failed to ensure DB secrets before CNPG init: {e}")
|
||||||
overall_success = False
|
overall_success = False
|
||||||
break
|
break
|
||||||
|
try:
|
||||||
|
self._ensure_cnpg_storage_provisioned(ns, env)
|
||||||
|
except Exception as e:
|
||||||
|
self.err(f"[ERROR] Failed to provision CNPG storage before init: {e}")
|
||||||
|
overall_success = False
|
||||||
|
break
|
||||||
|
|
||||||
# Use custom line handler for monitoring to capture Grafana password and port mappings
|
# Use custom line handler for monitoring to capture Grafana password and port mappings
|
||||||
rc = self._run_script(
|
rc = self._run_script(
|
||||||
@ -6124,7 +6143,7 @@ def _prepare_k3s_pipeline(
|
|||||||
|
|
||||||
attempt = 1
|
attempt = 1
|
||||||
total_attempts = 0
|
total_attempts = 0
|
||||||
max_attempts_env = os.environ.get("PROLE_SILENT_TEST_MAX_ATTEMPTS", "").strip()
|
max_attempts_env = os.environ.get("SILENT_TEST_MAX_ATTEMPTS", "").strip()
|
||||||
max_attempts = int(max_attempts_env) if max_attempts_env.isdigit() else 0
|
max_attempts = int(max_attempts_env) if max_attempts_env.isdigit() else 0
|
||||||
|
|
||||||
_log("==> Running silent install validation...\n")
|
_log("==> Running silent install validation...\n")
|
||||||
|
|||||||
@ -27,6 +27,7 @@ class ClusterNodesScreenMixin:
|
|||||||
("cloudnativepg", "CloudNative-PG"),
|
("cloudnativepg", "CloudNative-PG"),
|
||||||
("pihole", "Pi-hole"),
|
("pihole", "Pi-hole"),
|
||||||
]
|
]
|
||||||
|
ADVANCED_ASSIGNMENT_GRID_KEY = "ADVANCED_ASSIGNMENT_GRID"
|
||||||
|
|
||||||
def _render_cluster_nodes_page(self):
|
def _render_cluster_nodes_page(self):
|
||||||
# Gate: only for multi-node, non-k3d.
|
# Gate: only for multi-node, non-k3d.
|
||||||
@ -67,6 +68,79 @@ class ClusterNodesScreenMixin:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
advanced_enabled = self._cluster_nodes_advanced_enabled()
|
||||||
|
advanced_var = tk.BooleanVar(master=self.root, value=advanced_enabled)
|
||||||
|
|
||||||
|
def _toggle_advanced_grid():
|
||||||
|
enabled = bool(advanced_var.get())
|
||||||
|
self._cluster_nodes_set_advanced_enabled(enabled)
|
||||||
|
if not enabled:
|
||||||
|
default_policy = self._cluster_nodes_default_policy(hosts)
|
||||||
|
self._cluster_nodes_current_policy = default_policy
|
||||||
|
self._validate_and_save_cluster_nodes_policy(default_policy)
|
||||||
|
self._render_cluster_nodes_page()
|
||||||
|
|
||||||
|
advanced_chk = tk.Checkbutton(
|
||||||
|
self.bg_canvas,
|
||||||
|
text="Enable advanced assignment grid",
|
||||||
|
variable=advanced_var,
|
||||||
|
command=_toggle_advanced_grid,
|
||||||
|
bg="white",
|
||||||
|
activebackground="white",
|
||||||
|
font=("SF Pro Text", 11),
|
||||||
|
anchor="w",
|
||||||
|
)
|
||||||
|
advanced_win = self.bg_canvas.create_window(48, 238, window=advanced_chk, anchor="nw", width=360)
|
||||||
|
self._canvas_items.append(advanced_win)
|
||||||
|
self._overlay_widgets.append(advanced_chk)
|
||||||
|
|
||||||
|
if not advanced_enabled:
|
||||||
|
policy = self._cluster_nodes_default_policy(hosts)
|
||||||
|
self._cluster_nodes_current_policy = policy
|
||||||
|
monitoring_host = ((policy.get("defaults") or {}).get("monitoring_host") or "last host")
|
||||||
|
|
||||||
|
self._render_paragraph(
|
||||||
|
"Simple placement mode is active. The advanced assignment grid is disabled by default.",
|
||||||
|
y=280,
|
||||||
|
wrap=860,
|
||||||
|
)
|
||||||
|
ui.canvas_text(
|
||||||
|
self,
|
||||||
|
48,
|
||||||
|
330,
|
||||||
|
f"Default policy: CloudNative-PG uses round-robin allocation; monitoring defaults follow /synology/d004 (host: {monitoring_host}).",
|
||||||
|
fill="#1d1d1f",
|
||||||
|
font=("SF Pro Text", 11),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _save_simple():
|
||||||
|
self._cluster_nodes_set_advanced_enabled(False)
|
||||||
|
if not self._validate_and_save_cluster_nodes_policy(policy):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
messagebox.showinfo("Cluster Nodes", "Saved default cluster node policy.")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
btn = tk.Button(
|
||||||
|
self.bg_canvas,
|
||||||
|
text="Save",
|
||||||
|
command=_save_simple,
|
||||||
|
bg="#F5F5DC",
|
||||||
|
fg="black",
|
||||||
|
activebackground="#E5E5D5",
|
||||||
|
highlightbackground="#F5F5DC",
|
||||||
|
highlightthickness=0,
|
||||||
|
relief="flat",
|
||||||
|
font=("SF Pro Text", 11),
|
||||||
|
padx=14,
|
||||||
|
pady=6,
|
||||||
|
)
|
||||||
|
btn_win = self.bg_canvas.create_window(48, 380, window=btn, anchor="nw", width=180)
|
||||||
|
self._canvas_items.append(btn_win)
|
||||||
|
self._overlay_widgets.append(btn)
|
||||||
|
return
|
||||||
|
|
||||||
policy = self._cluster_nodes_load_policy(hosts)
|
policy = self._cluster_nodes_load_policy(hosts)
|
||||||
# Keep in instance state for navigation (Next button) saves.
|
# Keep in instance state for navigation (Next button) saves.
|
||||||
self._cluster_nodes_current_policy = policy
|
self._cluster_nodes_current_policy = policy
|
||||||
@ -362,19 +436,55 @@ class ClusterNodesScreenMixin:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Default: enabled everywhere, no primary.
|
return self._cluster_nodes_default_policy(hosts)
|
||||||
|
|
||||||
|
def _cluster_nodes_advanced_enabled(self) -> bool:
|
||||||
|
sec = (getattr(self, "prole_cfg_data", None) or {}).get("Cluster Nodes", {})
|
||||||
|
raw = (sec or {}).get(self.ADVANCED_ASSIGNMENT_GRID_KEY, False)
|
||||||
|
if isinstance(raw, bool):
|
||||||
|
return raw
|
||||||
|
return str(raw).strip().lower() in {"1", "true", "yes", "on"}
|
||||||
|
|
||||||
|
def _cluster_nodes_set_advanced_enabled(self, enabled: bool) -> None:
|
||||||
|
if "Cluster Nodes" not in self.prole_cfg_data:
|
||||||
|
self.prole_cfg_data["Cluster Nodes"] = {}
|
||||||
|
self.prole_cfg_data["Cluster Nodes"][self.ADVANCED_ASSIGNMENT_GRID_KEY] = "true" if enabled else "false"
|
||||||
|
try:
|
||||||
|
self._save_prole_cfg()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _cluster_nodes_monitoring_host(self, hosts: list[str]) -> str:
|
||||||
|
for host in hosts:
|
||||||
|
if "d004" in host.lower():
|
||||||
|
return host
|
||||||
|
return hosts[-1] if hosts else ""
|
||||||
|
|
||||||
|
def _cluster_nodes_default_policy(self, hosts: list[str]) -> dict:
|
||||||
services = {}
|
services = {}
|
||||||
for sid, _title in self.CLUSTER_NODE_SERVICES:
|
for sid, _title in self.CLUSTER_NODE_SERVICES:
|
||||||
services[sid] = {"primary_host": "", "enabled_hosts": list(hosts)}
|
services[sid] = {"primary_host": "", "enabled_hosts": list(hosts)}
|
||||||
return {
|
return {
|
||||||
|
"defaults": {
|
||||||
|
"cnpg_allocation": "round-robin",
|
||||||
|
"monitoring_storage_root": "/synology/d004",
|
||||||
|
"monitoring_host": self._cluster_nodes_monitoring_host(hosts),
|
||||||
|
},
|
||||||
"hosts": {h: {"protected": False, "reserved_hostports": False} for h in hosts},
|
"hosts": {h: {"protected": False, "reserved_hostports": False} for h in hosts},
|
||||||
"services": services,
|
"services": services,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _cluster_nodes_normalize_policy(self, pol: dict, hosts: list[str]) -> dict:
|
def _cluster_nodes_normalize_policy(self, pol: dict, hosts: list[str]) -> dict:
|
||||||
out = {"hosts": {}, "services": {}}
|
out = {"defaults": {}, "hosts": {}, "services": {}}
|
||||||
host_set = set(hosts)
|
host_set = set(hosts)
|
||||||
|
|
||||||
|
defaults_in = pol.get("defaults") if isinstance(pol.get("defaults"), dict) else {}
|
||||||
|
out["defaults"] = {
|
||||||
|
"cnpg_allocation": str(defaults_in.get("cnpg_allocation") or "round-robin"),
|
||||||
|
"monitoring_storage_root": str(defaults_in.get("monitoring_storage_root") or "/synology/d004"),
|
||||||
|
"monitoring_host": str(defaults_in.get("monitoring_host") or self._cluster_nodes_monitoring_host(hosts)),
|
||||||
|
}
|
||||||
|
|
||||||
for h in hosts:
|
for h in hosts:
|
||||||
hpol = ((pol.get("hosts") or {}).get(h) or {}) if isinstance(pol.get("hosts"), dict) else {}
|
hpol = ((pol.get("hosts") or {}).get(h) or {}) if isinstance(pol.get("hosts"), dict) else {}
|
||||||
out["hosts"][h] = {
|
out["hosts"][h] = {
|
||||||
|
|||||||
1
modes/k3s/conf/postgresql/.version
Normal file
1
modes/k3s/conf/postgresql/.version
Normal file
@ -0,0 +1 @@
|
|||||||
|
18
|
||||||
1
modes/k3s/knoe-db/.version
Normal file
1
modes/k3s/knoe-db/.version
Normal file
@ -0,0 +1 @@
|
|||||||
|
2
|
||||||
@ -1,53 +1,52 @@
|
|||||||
Network Discovery Summary:
|
Network Discovery Summary:
|
||||||
Primary Router: 10.0.0.1 (eero_5d:50:f2)
|
Primary Router: 10.0.0.1 (eero_5d:50:f2)
|
||||||
DNS Servers: 100.100.100.100, 10.0.0.5, 10.0.0.4
|
DNS Servers: 10.0.0.5, 10.0.0.4, 100.100.100.100
|
||||||
Detected Devices:
|
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.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.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.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.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.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.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.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.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.6 [2c:cf:67:45:8d:97] (RaspberryPi_45:8d:97): Ports [22, 80, 443], Services: ['SSH', 'Web Server']
|
- 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.2 [dc:a4:ca:ea:1a:2f] (Apple_ea:1a:2f): Ports [], Services: []
|
- 10.0.0.2 [dc:a4:ca:ea:1a:2f] (Apple_ea:1a:2f): Ports [], Services: []
|
||||||
- 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.26 [c8:db:26:08:52:f8] (Logitech_08:52:f8): Ports [], Services: []
|
- 10.0.0.26 [c8:db:26:08:52:f8] (Logitech_08:52:f8): Ports [], Services: []
|
||||||
- 10.0.0.100 [24:fc:e5:51:cf:74] (SamsungElect_51:cf:74): Ports [], Services: []
|
- 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.33 [ec:b5:fa:b0:76:e4] (PhilipsLight_b0:76:e4): Ports [80, 443], Services: ['Web Server']
|
- 10.0.0.33 [ec:b5:fa:b0:76:e4] (PhilipsLight_b0:76:e4): Ports [80, 443], Services: ['Web Server']
|
||||||
- 10.0.0.37 [54:07:7d:22:c0:b2] (Netgear_22:c0:b2): Ports [80], Services: ['Web Server']
|
- 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.45 [f8:b4:6a:30:a4:1f] (HewlettPacka_30:a4:1f): Ports [80, 443], Services: ['Web Server']
|
||||||
- 10.0.0.48 [0c:fe:45:53:f4:3c] (SonyInteract_53:f4:3c): 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.55 [48:a6:b8:a7:50:60] (Sonos_a7:50:60): Ports [], Services: []
|
||||||
- 10.0.0.46 [d4:f7:d5:40:ab:17] (SonyInteract_40:ab:17): Ports [], Services: []
|
|
||||||
- 10.0.0.41 [b8:27:eb:57:10:d9] (RaspberryPiF_57:10:d9): Ports [22], Services: ['SSH']
|
- 10.0.0.41 [b8:27:eb:57:10:d9] (RaspberryPiF_57:10:d9): Ports [22], Services: ['SSH']
|
||||||
- 10.0.0.206 [00:a0:de:a2:0b:ef] (Yamaha_a2:0b:ef): Ports [80], Services: ['Web Server']
|
- 10.0.0.46 [d4:f7:d5:40:ab:17] (SonyInteract_40:ab:17): Ports [], Services: []
|
||||||
- 10.0.0.73 [4c:a9:19:b3:12:f8] (TuyaSmart_b3:12:f8): Ports [], Services: []
|
- 10.0.0.73 [4c:a9:19:b3:12:f8] (TuyaSmart_b3:12:f8): Ports [], Services: []
|
||||||
|
- 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.107 [b8:06:0d:b7:7c:56] (TuyaSmart_b7:7c:56): Ports [], Services: []
|
- 10.0.0.107 [b8:06:0d:b7:7c:56] (TuyaSmart_b7:7c:56): Ports [], Services: []
|
||||||
|
- 10.0.0.189 [00:17:88:a3:2f:cc] (PhilipsLight_a3:2f:cc): Ports [80, 443], Services: ['Web Server']
|
||||||
|
- 10.0.0.100 [24:fc:e5:51:cf:74] (SamsungElect_51:cf:74): 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.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.99 [20:28:bc:f1:dc:ba] (Visionscape_f1:dc:ba): Ports [], Services: []
|
- 10.0.0.99 [20:28:bc:f1:dc:ba] (Visionscape_f1:dc:ba): Ports [], Services: []
|
||||||
|
- 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.123 [3c:ef:8c:96:e3:3c] (ZhejiangDahu_96:e3:3c): Ports [80], Services: ['Web Server']
|
- 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.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.155 [62:17:97:b3:06:a1] (62:17:97:b3:06:a1): Ports [], Services: []
|
|
||||||
- 10.0.0.196 [60:5f:8d:88:08:f2] (eero_88:08:f2): Ports [53], Services: ['DNS']
|
|
||||||
- 10.0.0.143 [60:5f:8d:7a:aa:32] (eero_7a:aa:32): 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.117 [40:f5:20:1e:5e:91] (Espressif_1e:5e:91): Ports [80], Services: ['Web Server']
|
|
||||||
- 10.0.0.130 [f8:bb:bf:a3:84:eb] (eero_a3:84:eb): Ports [53], Services: ['DNS']
|
|
||||||
- 10.0.0.113 [60:81:10:92:02:4e] (Apple_92:02:4e): Ports [], Services: []
|
- 10.0.0.113 [60:81:10:92:02:4e] (Apple_92:02:4e): 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.125 [a0:60:32:04:73:ba] (AmcrestTechn_04:73:ba): Ports [80], Services: ['Web Server']
|
||||||
|
- 10.0.0.128 [20:c9:d0:94:30:dd] (Apple_94:30:dd): Ports [22, 445, 5900, 88], Services: ['SSH', 'VNC', 'SMB/CIFS (Possible Windows/AD)', 'Active Directory Related']
|
||||||
|
- 10.0.0.130 [f8:bb:bf:a3:84:eb] (eero_a3:84:eb): Ports [53], Services: ['DNS']
|
||||||
- 10.0.0.112 [f8:33:31:40:58:c0] (TexasInstrum_40:58:c0): Ports [80, 443], Services: ['Web Server']
|
- 10.0.0.112 [f8:33:31:40:58:c0] (TexasInstrum_40:58:c0): Ports [80, 443], Services: ['Web Server']
|
||||||
- 10.0.0.128 [20:c9:d0:94:30:dd] (Apple_94:30:dd): Ports [88], Services: ['Active Directory Related']
|
- 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.179 [48:a6:b8:a4:8e:cc] (Sonos_a4:8e:cc): Ports [], Services: []
|
||||||
|
- 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.145 [7c:a6:b0:04:db:1c] (7c:a6:b0:04:db:1c): Ports [], Services: []
|
||||||
|
- 10.0.0.206 [00:a0:de:a2:0b:ef] (Yamaha_a2:0b:ef): Ports [80], Services: ['Web Server']
|
||||||
|
- 10.0.0.1\ [9c:57:bc:5d:50:f2] (eero_5d:50:f2): Ports [], Services: []
|
||||||
- 10.0.0.170 [b0:ee:7b:ca:73:99] (Roku_ca:73:99): Ports [], Services: []
|
- 10.0.0.170 [b0:ee:7b:ca:73:99] (Roku_ca:73:99): Ports [], Services: []
|
||||||
- 10.0.0.175 [28:80:88:e4:10:1a] (Netgear_e4:10:1a): Ports [80], Services: ['Web Server']
|
- 10.0.0.175 [28:80:88:e4:10:1a] (Netgear_e4:10:1a): Ports [80], Services: ['Web Server']
|
||||||
- 10.0.0.179 [48:a6:b8:a4:8e:cc] (Sonos_a4:8e:cc): Ports [], Services: []
|
- 10.0.0.196 [60:5f:8d:88:08:f2] (eero_88:08:f2): Ports [53], Services: ['DNS']
|
||||||
- 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.188 [00:16:6c:c5:1f:54] (SamsungElect_c5:1f:54): Ports [443], Services: ['Web Server']
|
|
||||||
- 10.0.0.208 [a0:ad:9f:30:84:5f] (ASUSTekCOMPU_30:84:5f): Ports [], Services: []
|
|
||||||
- 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.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.38\ [7e:19:9a:fe:9f:ed] (7e:19:9a:fe:9f:ed): Ports [], Services: []
|
- 10.0.0.38\ [7e:19:9a:fe:9f:ed] (7e:19:9a:fe:9f:ed): Ports [], Services: []
|
||||||
|
- 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
|
Ollama Instances found at: 10.0.0.38, 10.0.0.204
|
||||||
|
|
||||||
Ansible Inventory Summary:
|
Ansible Inventory Summary:
|
||||||
|
|||||||
@ -3,9 +3,10 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Regression tests for k3s CNPG storage guardrails in etc/init_cloudnative_pg.sh
|
# Regression tests for k3s CNPG storage guardrails in etc/init_cloudnative_pg.sh
|
||||||
# - must fail if protected mounts are root-backed
|
# - must fail if Synology storage roots are root-backed
|
||||||
# - must fail if manifest lacks explicit synology-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
|
# - must fail if a required Synology root is missing
|
||||||
|
# - must succeed (with mocks) when mounts + computed paths + manifest + runtime objects are correct
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
REPO_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd)
|
REPO_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd)
|
||||||
@ -61,10 +62,25 @@ if [[ "$field" == "TARGET" ]]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$path" in
|
if [[ -n "${PROLE_TEST_ROOT_D001:-}" && "$path" == "${PROLE_TEST_ROOT_D001}"* ]]; then
|
||||||
"${PROLE_PROTECTED_DATA_PATH}") printf '%s\n' "${PROLE_TEST_FINDMNT_DATA_SRC:-/dev/iscsi-data}"; exit 0 ;;
|
printf '%s\n' "${PROLE_TEST_FINDMNT_D001_SRC:-/dev/iscsi-d001}"
|
||||||
"${PROLE_PROTECTED_WAL_PATH}") printf '%s\n' "${PROLE_TEST_FINDMNT_WAL_SRC:-/dev/iscsi-wal}"; exit 0 ;;
|
exit 0
|
||||||
esac
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${PROLE_TEST_ROOT_D002:-}" && "$path" == "${PROLE_TEST_ROOT_D002}"* ]]; then
|
||||||
|
printf '%s\n' "${PROLE_TEST_FINDMNT_D002_SRC:-/dev/iscsi-d002}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${PROLE_TEST_ROOT_D003:-}" && "$path" == "${PROLE_TEST_ROOT_D003}"* ]]; then
|
||||||
|
printf '%s\n' "${PROLE_TEST_FINDMNT_D003_SRC:-/dev/iscsi-d003}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${PROLE_TEST_ROOT_D004:-}" && "$path" == "${PROLE_TEST_ROOT_D004}"* ]]; then
|
||||||
|
printf '%s\n' "${PROLE_TEST_FINDMNT_D004_SRC:-/dev/iscsi-d004}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Default: pretend it's root-backed
|
# Default: pretend it's root-backed
|
||||||
printf '%s\n' "${PROLE_TEST_FINDMNT_ROOT_SRC:-/dev/root}"
|
printf '%s\n' "${PROLE_TEST_FINDMNT_ROOT_SRC:-/dev/root}"
|
||||||
@ -82,6 +98,40 @@ fi
|
|||||||
|
|
||||||
args="$*"
|
args="$*"
|
||||||
|
|
||||||
|
# Simulate kubectl local merge patch used for selector injection.
|
||||||
|
if [[ "${args}" == patch*" --local "*" -o yaml"* ]]; then
|
||||||
|
src=""
|
||||||
|
if [[ "$#" -ge 2 ]]; then
|
||||||
|
for ((i=1; i<=$#; i++)); do
|
||||||
|
if [[ "${!i}" == "-f" ]]; then
|
||||||
|
j=$((i+1))
|
||||||
|
src="${!j:-}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [[ -n "${src}" && -f "${src}" ]]; then
|
||||||
|
awk '
|
||||||
|
{
|
||||||
|
print
|
||||||
|
if ($0 ~ /^[[:space:]]*synology\.storage\/role:[[:space:]]*data[[:space:]]*$/) {
|
||||||
|
match($0, /^[[:space:]]*/)
|
||||||
|
indent = substr($0, RSTART, RLENGTH)
|
||||||
|
print indent "prole.io/namespace: test-ns"
|
||||||
|
print indent "prole.io/cluster: knoe-db"
|
||||||
|
}
|
||||||
|
if ($0 ~ /^[[:space:]]*synology\.storage\/role:[[:space:]]*wal[[:space:]]*$/) {
|
||||||
|
match($0, /^[[:space:]]*/)
|
||||||
|
indent = substr($0, RSTART, RLENGTH)
|
||||||
|
print indent "prole.io/namespace: test-ns"
|
||||||
|
print indent "prole.io/cluster: knoe-db"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "${src}"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$args" == *"get --raw='/readyz'"* || "$args" == *"get --raw=/readyz"* ]]; then
|
if [[ "$args" == *"get --raw='/readyz'"* || "$args" == *"get --raw=/readyz"* ]]; then
|
||||||
echo "ok"
|
echo "ok"
|
||||||
exit 0
|
exit 0
|
||||||
@ -145,7 +195,7 @@ if [[ "$args" == *" get cluster knoe-db -o jsonpath="* ]]; then
|
|||||||
fi
|
fi
|
||||||
if [[ "$args" == *" get cluster knoe-db -o json"* ]]; then
|
if [[ "$args" == *" get cluster knoe-db -o json"* ]]; then
|
||||||
cat <<JSON
|
cat <<JSON
|
||||||
{"apiVersion":"postgresql.cnpg.io/v1","kind":"Cluster","metadata":{"name":"knoe-db"},"spec":{"storage":{"pvcTemplate":{"storageClassName":"synology-iscsi","selector":{"matchLabels":{"synology.storage/role":"data"}}}},"walStorage":{"pvcTemplate":{"storageClassName":"synology-iscsi","selector":{"matchLabels":{"synology.storage/role":"wal"}}}}}}
|
{"apiVersion":"postgresql.cnpg.io/v1","kind":"Cluster","metadata":{"name":"knoe-db"},"spec":{"storage":{"pvcTemplate":{"storageClassName":"synology-iscsi","selector":{"matchLabels":{"synology.storage/role":"data","prole.io/namespace":"test-ns","prole.io/cluster":"knoe-db"}}}},"walStorage":{"pvcTemplate":{"storageClassName":"synology-iscsi","selector":{"matchLabels":{"synology.storage/role":"wal","prole.io/namespace":"test-ns","prole.io/cluster":"knoe-db"}}}}}}
|
||||||
JSON
|
JSON
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
@ -216,15 +266,25 @@ EOF
|
|||||||
local MANIFEST_DIR="$PROLE_HOME/deploy/opentofu/k3s/manifests/prole"
|
local MANIFEST_DIR="$PROLE_HOME/deploy/opentofu/k3s/manifests/prole"
|
||||||
mkdir -p "$MANIFEST_DIR"
|
mkdir -p "$MANIFEST_DIR"
|
||||||
|
|
||||||
# Storage paths (override to tmp)
|
# Synology roots + computed host paths (override to tmp)
|
||||||
export PROLE_PROTECTED_DATA_PATH="$TMP_DIR/prole-mount/data"
|
export PROLE_TEST_ROOT_D001="$TMP_DIR/synology/d001"
|
||||||
export PROLE_PROTECTED_WAL_PATH="$TMP_DIR/prole-mount/wal"
|
export PROLE_TEST_ROOT_D002="$TMP_DIR/synology/d002"
|
||||||
mkdir -p "$PROLE_PROTECTED_DATA_PATH" "$PROLE_PROTECTED_WAL_PATH"
|
export PROLE_TEST_ROOT_D003="$TMP_DIR/synology/d003"
|
||||||
|
export PROLE_TEST_ROOT_D004="$TMP_DIR/synology/d004"
|
||||||
|
mkdir -p "$PROLE_TEST_ROOT_D001" "$PROLE_TEST_ROOT_D002" "$PROLE_TEST_ROOT_D003" "$PROLE_TEST_ROOT_D004"
|
||||||
|
|
||||||
|
export SYNOLOGY_ROOTS="$PROLE_TEST_ROOT_D001,$PROLE_TEST_ROOT_D002,$PROLE_TEST_ROOT_D003,$PROLE_TEST_ROOT_D004"
|
||||||
|
export CNPG_DATA_HOST_PATH="$PROLE_TEST_ROOT_D001/knoe/knoe-db/test-ns/knoe-db/data"
|
||||||
|
export CNPG_WAL_HOST_PATH="$PROLE_TEST_ROOT_D002/knoe/knoe-db/test-ns/knoe-db/wal"
|
||||||
|
mkdir -p "$CNPG_DATA_HOST_PATH" "$CNPG_WAL_HOST_PATH"
|
||||||
|
export CNPG_STAGE1_NODE="$(hostname -f 2>/dev/null || hostname)"
|
||||||
|
|
||||||
# Defaults: non-root-backed mounts
|
# Defaults: non-root-backed mounts
|
||||||
export PROLE_TEST_FINDMNT_ROOT_SRC="/dev/root"
|
export PROLE_TEST_FINDMNT_ROOT_SRC="/dev/root"
|
||||||
export PROLE_TEST_FINDMNT_DATA_SRC="/dev/iscsi0"
|
export PROLE_TEST_FINDMNT_D001_SRC="/dev/iscsi0"
|
||||||
export PROLE_TEST_FINDMNT_WAL_SRC="/dev/iscsi1"
|
export PROLE_TEST_FINDMNT_D002_SRC="/dev/iscsi1"
|
||||||
|
export PROLE_TEST_FINDMNT_D003_SRC="/dev/iscsi2"
|
||||||
|
export PROLE_TEST_FINDMNT_D004_SRC="/dev/iscsi3"
|
||||||
|
|
||||||
# Run the case-provided setup (writes manifest + adjusts mocks)
|
# Run the case-provided setup (writes manifest + adjusts mocks)
|
||||||
"$@" "$MANIFEST_DIR" "$TMP_DIR"
|
"$@" "$MANIFEST_DIR" "$TMP_DIR"
|
||||||
@ -264,8 +324,32 @@ spec:
|
|||||||
synology.storage/role: wal
|
synology.storage/role: wal
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
export PROLE_TEST_FINDMNT_DATA_SRC="/dev/root"
|
export PROLE_TEST_FINDMNT_D001_SRC="/dev/root"
|
||||||
export PROLE_TEST_FINDMNT_WAL_SRC="/dev/root"
|
}
|
||||||
|
|
||||||
|
case_missing_root() {
|
||||||
|
local manifest_dir="$1"
|
||||||
|
cat >"$manifest_dir/knoe-db.yaml" <<'EOF'
|
||||||
|
apiVersion: postgresql.cnpg.io/v1
|
||||||
|
kind: Cluster
|
||||||
|
metadata:
|
||||||
|
name: knoe-db
|
||||||
|
spec:
|
||||||
|
storage:
|
||||||
|
pvcTemplate:
|
||||||
|
storageClassName: synology-iscsi
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
synology.storage/role: data
|
||||||
|
walStorage:
|
||||||
|
pvcTemplate:
|
||||||
|
storageClassName: synology-iscsi
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
synology.storage/role: wal
|
||||||
|
EOF
|
||||||
|
|
||||||
|
rm -rf "$PROLE_TEST_ROOT_D004"
|
||||||
}
|
}
|
||||||
|
|
||||||
case_invalid_manifest_missing_sc() {
|
case_invalid_manifest_missing_sc() {
|
||||||
@ -323,19 +407,25 @@ spec:
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
# 1) Root-backed mounts must hard-fail.
|
# 1) Root-backed storage roots must hard-fail.
|
||||||
if run_case "root-backed mounts" case_root_backed; then
|
if run_case "root-backed mounts" case_root_backed; then
|
||||||
echo "FAILURE: expected root-backed mounts to fail" >&2
|
echo "FAILURE: expected root-backed mounts to fail" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2) Missing explicit storageClassName/selector must hard-fail.
|
# 2) Missing storage roots must hard-fail.
|
||||||
|
if run_case "missing Synology root" case_missing_root; then
|
||||||
|
echo "FAILURE: expected missing Synology root to fail" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3) Missing explicit storageClassName/selector must hard-fail.
|
||||||
if run_case "invalid manifest (missing storageClassName/selector)" case_invalid_manifest_missing_sc; then
|
if run_case "invalid manifest (missing storageClassName/selector)" case_invalid_manifest_missing_sc; then
|
||||||
echo "FAILURE: expected invalid manifest to fail" >&2
|
echo "FAILURE: expected invalid manifest to fail" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3) Happy path should succeed with mocks.
|
# 4) Happy path should succeed with mocks.
|
||||||
run_case "success" case_success
|
run_case "success" case_success
|
||||||
|
|
||||||
echo "SUCCESS"
|
echo "SUCCESS"
|
||||||
|
|||||||
@ -251,6 +251,14 @@ run_case() {
|
|||||||
local cnpg_instances_cfg="${2:-}"
|
local cnpg_instances_cfg="${2:-}"
|
||||||
local mock_ready_nodes="${3:-1}"
|
local mock_ready_nodes="${3:-1}"
|
||||||
local mock_db_nodes="${4:-1}"
|
local mock_db_nodes="${4:-1}"
|
||||||
|
local selector_mode="${5:-provided}"
|
||||||
|
|
||||||
|
local data_selector_env='{"synology.storage/role":"data","prole.io/namespace":"test-ns","prole.io/cluster":"knoe-db"}'
|
||||||
|
local wal_selector_env='{"synology.storage/role":"wal","prole.io/namespace":"test-ns","prole.io/cluster":"knoe-db"}'
|
||||||
|
if [[ "$selector_mode" == "missing" ]]; then
|
||||||
|
data_selector_env=""
|
||||||
|
wal_selector_env=""
|
||||||
|
fi
|
||||||
|
|
||||||
local conf_dir="$TMP_DIR/conf-${label}"
|
local conf_dir="$TMP_DIR/conf-${label}"
|
||||||
local env_home="$TMP_DIR/env-${label}"
|
local env_home="$TMP_DIR/env-${label}"
|
||||||
@ -281,10 +289,11 @@ 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"
|
||||||
|
rm -f "$TMP_DIR/mock_calls.log"
|
||||||
set +e
|
set +e
|
||||||
MOCK_READY_NODES="$mock_ready_nodes" MOCK_DB_NODES="$mock_db_nodes" \
|
MOCK_READY_NODES="$mock_ready_nodes" MOCK_DB_NODES="$mock_db_nodes" \
|
||||||
PROLE_CNPG_DATA_SELECTOR_JSON='{"synology.storage/role":"data","prole.io/namespace":"test-ns","prole.io/cluster":"knoe-db"}' \
|
CNPG_DATA_SELECTOR_JSON="$data_selector_env" \
|
||||||
PROLE_CNPG_WAL_SELECTOR_JSON='{"synology.storage/role":"wal","prole.io/namespace":"test-ns","prole.io/cluster":"knoe-db"}' \
|
CNPG_WAL_SELECTOR_JSON="$wal_selector_env" \
|
||||||
BARMAN_CRD_TIMEOUT=5 BARMAN_TLS_TIMEOUT=5 BARMAN_DEPLOY_TIMEOUT=5 BARMAN_SERVICE_TIMEOUT=5 \
|
BARMAN_CRD_TIMEOUT=5 BARMAN_TLS_TIMEOUT=5 BARMAN_DEPLOY_TIMEOUT=5 BARMAN_SERVICE_TIMEOUT=5 \
|
||||||
CNPG_STORAGE_VALIDATE_TIMEOUT=5 \
|
CNPG_STORAGE_VALIDATE_TIMEOUT=5 \
|
||||||
PROLE_HOME="$env_home" \
|
PROLE_HOME="$env_home" \
|
||||||
@ -307,9 +316,30 @@ E_EOF
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_applied_has_namespace_cluster_selectors() {
|
||||||
|
local label="$1"
|
||||||
|
local ns_count cluster_count
|
||||||
|
|
||||||
|
ns_count=$(grep -cE '^\s*prole\.io/namespace:\s*test-ns\s*$' "$TMP_DIR/applied.yaml" || true)
|
||||||
|
cluster_count=$(grep -cE '^\s*prole\.io/cluster:\s*knoe-db\s*$' "$TMP_DIR/applied.yaml" || true)
|
||||||
|
|
||||||
|
if (( ns_count < 2 )); then
|
||||||
|
echo "FAILURE: expected namespace selectors for both data and wal in case '${label}'"
|
||||||
|
sed -n '1,200p' "$TMP_DIR/applied.yaml" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( cluster_count < 2 )); then
|
||||||
|
echo "FAILURE: expected cluster selectors for both data and wal in case '${label}'"
|
||||||
|
sed -n '1,200p' "$TMP_DIR/applied.yaml" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Default: manifest must be label-based (no hostname pinning) and reconcile must scale down to 1
|
# Default: manifest must be label-based (no hostname pinning) and reconcile must scale down to 1
|
||||||
# when only one node is Ready/schedulable.
|
# when only one node is Ready/schedulable.
|
||||||
run_case "default" "" 1 1
|
run_case "default" "" 1 1
|
||||||
|
assert_applied_has_namespace_cluster_selectors "default"
|
||||||
if grep -qE '^\s*- key:\s*kubernetes\\.io/hostname\s*$|myrddin\\.prole\\.org' "$TMP_DIR/applied.yaml"; then
|
if grep -qE '^\s*- key:\s*kubernetes\\.io/hostname\s*$|myrddin\\.prole\\.org' "$TMP_DIR/applied.yaml"; then
|
||||||
echo "FAILURE: expected no hostname pinning in applied manifest (default)"
|
echo "FAILURE: expected no hostname pinning in applied manifest (default)"
|
||||||
sed -n '1,160p' "$TMP_DIR/applied.yaml" || true
|
sed -n '1,160p' "$TMP_DIR/applied.yaml" || true
|
||||||
@ -341,6 +371,16 @@ if [[ "$(cat "$TMP_DIR/mock_cluster_instances" 2>/dev/null || true)" != "1" ]];
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Regression: selector JSON env vars may be absent in direct shell invocation, but
|
||||||
|
# manifest must still be patched with namespace+cluster selector labels before validation.
|
||||||
|
run_case "no-selector-env" "" 1 1 "missing"
|
||||||
|
assert_applied_has_namespace_cluster_selectors "no-selector-env"
|
||||||
|
if ! grep -q "patch --local" "$TMP_DIR/mock_calls.log"; then
|
||||||
|
echo "FAILURE: expected local CNPG selector patch to run in case 'no-selector-env'"
|
||||||
|
sed -n '1,200p' "$TMP_DIR/mock_calls.log" || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Override: when capacity allows (2 ready db nodes), CNPG_INSTANCES must be honored (cap=2)
|
# Override: when capacity allows (2 ready db nodes), CNPG_INSTANCES must be honored (cap=2)
|
||||||
run_case "override" "CNPG_INSTANCES = 2" 2 2
|
run_case "override" "CNPG_INSTANCES = 2" 2 2
|
||||||
if [[ "$(cat "$TMP_DIR/mock_cluster_instances" 2>/dev/null || true)" != "2" ]]; then
|
if [[ "$(cat "$TMP_DIR/mock_cluster_instances" 2>/dev/null || true)" != "2" ]]; then
|
||||||
|
|||||||
@ -235,6 +235,10 @@ mkdir -p "$TMP_DIR/service/secrets"
|
|||||||
printf '%s' "dummy-private-key" > "$TMP_DIR/service/secrets/admin.key"
|
printf '%s' "dummy-private-key" > "$TMP_DIR/service/secrets/admin.key"
|
||||||
printf '%s' "dummy-public-key" > "$TMP_DIR/service/secrets/admin.pub"
|
printf '%s' "dummy-public-key" > "$TMP_DIR/service/secrets/admin.pub"
|
||||||
|
|
||||||
|
mkdir -p "$TMP_DIR/synology/d001" "$TMP_DIR/synology/d002" "$TMP_DIR/synology/d003" "$TMP_DIR/synology/d004"
|
||||||
|
mkdir -p "$TMP_DIR/synology/d001/knoe/knoe-db/test-ns/knoe-db/data"
|
||||||
|
mkdir -p "$TMP_DIR/synology/d002/knoe/knoe-db/test-ns/knoe-db/wal"
|
||||||
|
|
||||||
CONF_DIR="$TMP_DIR/conf"
|
CONF_DIR="$TMP_DIR/conf"
|
||||||
ENV_HOME="$TMP_DIR/env"
|
ENV_HOME="$TMP_DIR/env"
|
||||||
mkdir -p "$CONF_DIR" "$ENV_HOME"
|
mkdir -p "$CONF_DIR" "$ENV_HOME"
|
||||||
@ -260,6 +264,9 @@ chmod +x "$ENV_HOME/env.sh"
|
|||||||
|
|
||||||
set +e
|
set +e
|
||||||
PROLE_HOME="$ENV_HOME" \
|
PROLE_HOME="$ENV_HOME" \
|
||||||
|
SYNOLOGY_ROOTS="$TMP_DIR/synology/d001,$TMP_DIR/synology/d002,$TMP_DIR/synology/d003,$TMP_DIR/synology/d004" \
|
||||||
|
CNPG_DATA_HOST_PATH="$TMP_DIR/synology/d001/knoe/knoe-db/test-ns/knoe-db/data" \
|
||||||
|
CNPG_WAL_HOST_PATH="$TMP_DIR/synology/d002/knoe/knoe-db/test-ns/knoe-db/wal" \
|
||||||
BARMAN_CRD_TIMEOUT=5 BARMAN_TLS_TIMEOUT=5 BARMAN_DEPLOY_TIMEOUT=5 BARMAN_SERVICE_TIMEOUT=5 \
|
BARMAN_CRD_TIMEOUT=5 BARMAN_TLS_TIMEOUT=5 BARMAN_DEPLOY_TIMEOUT=5 BARMAN_SERVICE_TIMEOUT=5 \
|
||||||
CNPG_WAIT_TIMEOUT=5 CNPG_STORAGE_VALIDATE_TIMEOUT=5 \
|
CNPG_WAIT_TIMEOUT=5 CNPG_STORAGE_VALIDATE_TIMEOUT=5 \
|
||||||
bash "$SCRIPT_UNDER_TEST" --mode k3s initialize >"$TMP_DIR/stdout.log" 2>"$TMP_DIR/stderr.log"
|
bash "$SCRIPT_UNDER_TEST" --mode k3s initialize >"$TMP_DIR/stdout.log" 2>"$TMP_DIR/stderr.log"
|
||||||
|
|||||||
@ -781,15 +781,15 @@ class TestStepCnpgDeploy:
|
|||||||
calls["provision"] += 1
|
calls["provision"] += 1
|
||||||
assert ns == "knoe-db"
|
assert ns == "knoe-db"
|
||||||
assert env_arg is env
|
assert env_arg is env
|
||||||
env_arg["PROLE_CNPG_DATA_SELECTOR_JSON"] = '{"synology.storage/role":"data"}'
|
env_arg["CNPG_DATA_SELECTOR_JSON"] = '{"synology.storage/role":"data"}'
|
||||||
env_arg["PROLE_CNPG_WAL_SELECTOR_JSON"] = '{"synology.storage/role":"wal"}'
|
env_arg["CNPG_WAL_SELECTOR_JSON"] = '{"synology.storage/role":"wal"}'
|
||||||
|
|
||||||
def _fake_run(cmd, env=None, **_kwargs):
|
def _fake_run(cmd, env=None, **_kwargs):
|
||||||
calls["run"] += 1
|
calls["run"] += 1
|
||||||
assert "init_cloudnative_pg.sh" in str(cmd[1])
|
assert "init_cloudnative_pg.sh" in str(cmd[1])
|
||||||
assert env is not None
|
assert env is not None
|
||||||
assert env["PROLE_CNPG_DATA_SELECTOR_JSON"]
|
assert env["CNPG_DATA_SELECTOR_JSON"]
|
||||||
assert env["PROLE_CNPG_WAL_SELECTOR_JSON"]
|
assert env["CNPG_WAL_SELECTOR_JSON"]
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
monkeypatch.setattr(inst, "_ensure_cnpg_storage_provisioned", _fake_provision)
|
monkeypatch.setattr(inst, "_ensure_cnpg_storage_provisioned", _fake_provision)
|
||||||
|
|||||||
52
tests/installer/test_cluster_nodes_policy.py
Normal file
52
tests/installer/test_cluster_nodes_policy.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from knoe.ui.screens.cluster_nodes import ClusterNodesScreenMixin
|
||||||
|
|
||||||
|
|
||||||
|
class _DummyClusterNodesScreen(ClusterNodesScreenMixin):
|
||||||
|
def __init__(self, prole_cfg_data: dict | None = None):
|
||||||
|
self.prole_cfg_data = prole_cfg_data or {}
|
||||||
|
self._save_calls = 0
|
||||||
|
|
||||||
|
def _save_prole_cfg(self):
|
||||||
|
self._save_calls += 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_advanced_assignment_grid_disabled_by_default():
|
||||||
|
app = _DummyClusterNodesScreen(prole_cfg_data={"Cluster Nodes": {}})
|
||||||
|
assert app._cluster_nodes_advanced_enabled() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_advanced_assignment_grid_flag_persists_setting():
|
||||||
|
app = _DummyClusterNodesScreen(prole_cfg_data={"Cluster Nodes": {}})
|
||||||
|
app._cluster_nodes_set_advanced_enabled(True)
|
||||||
|
|
||||||
|
assert app.prole_cfg_data["Cluster Nodes"]["ADVANCED_ASSIGNMENT_GRID"] == "true"
|
||||||
|
assert app._save_calls == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_policy_uses_round_robin_cnpg_and_prefers_d004_for_monitoring():
|
||||||
|
app = _DummyClusterNodesScreen(prole_cfg_data={})
|
||||||
|
|
||||||
|
policy = app._cluster_nodes_default_policy(["node-a", "node-d004", "node-c"])
|
||||||
|
|
||||||
|
assert policy["defaults"]["cnpg_allocation"] == "round-robin"
|
||||||
|
assert policy["defaults"]["monitoring_storage_root"] == "/synology/d004"
|
||||||
|
assert policy["defaults"]["monitoring_host"] == "node-d004"
|
||||||
|
assert policy["services"]["cloudnativepg"]["primary_host"] == ""
|
||||||
|
assert policy["services"]["cloudnativepg"]["enabled_hosts"] == [
|
||||||
|
"node-a",
|
||||||
|
"node-d004",
|
||||||
|
"node-c",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_policy_falls_back_to_default_when_policy_json_missing():
|
||||||
|
app = _DummyClusterNodesScreen(prole_cfg_data={"Cluster Nodes": {}})
|
||||||
|
|
||||||
|
policy = app._cluster_nodes_load_policy(["node-a", "node-b"])
|
||||||
|
|
||||||
|
assert policy["defaults"]["cnpg_allocation"] == "round-robin"
|
||||||
|
assert policy["defaults"]["monitoring_storage_root"] == "/synology/d004"
|
||||||
|
assert policy["defaults"]["monitoring_host"] == "node-b"
|
||||||
|
assert policy["services"]["cloudnativepg"]["primary_host"] == ""
|
||||||
Loading…
Reference in New Issue
Block a user