mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 10:13:58 +00:00
Remove prole-db-manager; simplify deployment via prole-authority; fix pg18 downgrade & cluster name
Summary: Removed the prole-db-manager microservice and simplified deployment to use prole-authority as the internal management and authorization point. Fixed two blocking bugs that prevented silent install from completing on knoe-dev-cluster. Removed: prole-db-manager - Deleted db-manager-deployment.yaml and db-manager-service.yaml from opentofu manifests - Deleted src/db-manager/ (Dockerfile, server.js, package.json, tests) - Removed prole-db-manager port-forward mapping from installer/core/env.py - Removed init_db_manager.sh from Initialization Scripts (milestones.py, actions.py) - Removed init_certmgr.sh and init_db_manager.sh tabs from services screen (services.py) - Removed live k8s Deployment/Service from knoe-dev-cluster Fixed: PostgreSQL version downgrade error (pg17 -> pg18) - Created conf/postgresql/.version with value 18 - Updated k8s/prole/prole-db.yaml and prole-db-recovery.yaml.tpl imageName to prole-db:18-089 - Fixed _init_database_options_state() to restore saved version_type from prole.cfg so db_version_type defaults to v18 (pg18) instead of silently reverting to pg17 - Added database_options.* keys to _collect_input_snapshot() in cfg.py so distribution, version_type, and all extension toggles persist to prole.cfg Fixed: Cluster name inconsistency - Removed stale prole-dev-cluster references; all scripts now use knoe-dev-cluster - Added knoe-dev-cluster to mode-detection case in etc/prole_cfg.sh Config: conf/prole.cfg - Set kerberos_config.enabled = False, KERBEROS_AUTO_ENABLED = False - Added database_options.distribution = percona, version_type = v18 - Added all 13 extension flags set to True (postgis, pgvector, pgcrypto, pgaudit, pg_repack, pg_stat_statements, pg_buffercache, pg_freespacemap, pgrowlocks, postgres_fdw, dblink, pg_stat_monitor, pgbadger) Verification: ./install.py -s -l -v -c conf/prole.cfg completed successfully. CNPG deployed prole-db:18-089 to knoe-dev-cluster; all milestones passed. Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
parent
46f31843c2
commit
5618b662dd
@ -1,199 +0,0 @@
|
|||||||
; Prole Master Configuration File
|
|
||||||
; Generated by install.py on 2026-02-26 00:56:29
|
|
||||||
; This file is used as input for Ansible deployment and k8s cluster creation.
|
|
||||||
|
|
||||||
[User]
|
|
||||||
; User-editable values; derived values below reference these by default.
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
PROLE_CONF = ${PROLE_HOME}/conf
|
|
||||||
PROLE_DATA = /opt/prole/data/chrisfu
|
|
||||||
PROLE_HOME = /Users/chrisfu/dev/prole
|
|
||||||
PROLE_LOGS = /opt/prole/logs/chrisfu
|
|
||||||
PROLE_SERVICE = ${PROLE_HOME}/etc
|
|
||||||
SERVICE_NAMESPACE = default
|
|
||||||
|
|
||||||
[Inputs]
|
|
||||||
; Screen-scoped inputs used for unattended replays (-S)
|
|
||||||
build.deploy_env = Dev
|
|
||||||
build.run_build = false
|
|
||||||
dependencies.ansible.install = true
|
|
||||||
dependencies.auto_install_missing = true
|
|
||||||
dependencies.brew.install = true
|
|
||||||
dependencies.docker.install = true
|
|
||||||
dependencies.k3d.install = true
|
|
||||||
dependencies.kubectl.install = true
|
|
||||||
dependencies.opentofu.install = true
|
|
||||||
dependencies.python.install = true
|
|
||||||
dependencies.verify_all = false
|
|
||||||
disk_selection.disk_type = local
|
|
||||||
disk_selection.local_path = ${PROLE_HOME}/prole-tools-app/dist
|
|
||||||
disk_selection.removable_mount =
|
|
||||||
env_setup.NAMESPACE = ${NAMESPACE}
|
|
||||||
env_setup.PROLE_CONF = ${PROLE_CONF}
|
|
||||||
env_setup.PROLE_DATA = ${PROLE_DATA}
|
|
||||||
env_setup.PROLE_HOME = ${PROLE_HOME}
|
|
||||||
env_setup.PROLE_LOGS = ${PROLE_LOGS}
|
|
||||||
env_setup.PROLE_SERVICE = ${PROLE_SERVICE}
|
|
||||||
init_cluster.at_rest_encryption_enabled = true
|
|
||||||
init_cluster.cluster_env = k3d-prole-dev-cluster
|
|
||||||
init_cluster.gitops_enabled = false
|
|
||||||
init_cluster.k3s_server_url = https://pi.prole.org:6443
|
|
||||||
init_cluster.k3s_token = ${OPENBAO:kv/prole/${NAMESPACE}/k3s#token}
|
|
||||||
init_cluster.kerberos_enabled = false
|
|
||||||
init_cluster.start_cluster = true
|
|
||||||
init_cluster.supabase_enabled = true
|
|
||||||
init_cnpg_deploy.force_rollout = false
|
|
||||||
init_cnpg_deploy.run_deploy = true
|
|
||||||
init_db_build.run_build = true
|
|
||||||
init_password.db_host_port = 5432
|
|
||||||
init_password.db_namespace = ${NAMESPACE}
|
|
||||||
init_password.db_password = ${OPENBAO:kv/prole/${NAMESPACE}/db#password}
|
|
||||||
init_password.db_password_confirm = ${OPENBAO:kv/prole/${NAMESPACE}/db#password}
|
|
||||||
init_password.db_username = chrisfu
|
|
||||||
init_password.generate_ssh_key = true
|
|
||||||
init_scripts.run_scripts = true
|
|
||||||
kerberos_config.enabled = false
|
|
||||||
kerberos_config.init_authority = false
|
|
||||||
kerberos_config.kdc = 10.0.0.3
|
|
||||||
kerberos_config.password = ${OPENBAO:kv/prole/${NAMESPACE}/kerberos#password}
|
|
||||||
kerberos_config.realm = PROLE.ORG
|
|
||||||
kerberos_config.test_connection = false
|
|
||||||
kerberos_config.user =
|
|
||||||
network_scan.run = true
|
|
||||||
ollama_config.model =
|
|
||||||
ollama_config.server_host =
|
|
||||||
ollama_config.server_port = 11434
|
|
||||||
|
|
||||||
[Global]
|
|
||||||
; Variables used by name in more than one place or assumed global scope
|
|
||||||
ARGOCD_NAMESPACE = argocd
|
|
||||||
CLUSTER_ENV = k3d-prole-dev-cluster
|
|
||||||
DB_HOST_PORT = 5432
|
|
||||||
DB_PASSWORD = ${OPENBAO:kv/prole/${NAMESPACE}/db#password}
|
|
||||||
DEPLOYMENT_MODE = k3d
|
|
||||||
DEPLOYMENT_TARGET = prole-dev-cluster
|
|
||||||
DOCKER_IMPORT_DIR =
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
PROLE_DB_USER = chrisfu
|
|
||||||
PROLE_ENABLE_LOCAL_REGISTRY = true
|
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
|
||||||
PROLE_K3S_SERVER = https://pi.prole.org:6443
|
|
||||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:7PxbzYh-J2MWFvEr:4HQYe1HI1OOkYObBQ3b5gdjWAUXWGootLltgL4mPVv2KZUMaaWa8rFQ4j-Re_PKl0GRX5MeyCn7GVgN6RxPAx0HnBcdMq5glD66h3b6egLE4yZ5-6SBa3pOBG0MHZr_tqeGhMRobVkJ3eAnleHuqr9lcMFDrRHcm1BingJFNsA9N6MMI9lrwZCWZMT-jrkg59JZcRO5MPhfTLysKvlJ_YP2WeX0fdqeTVFWCQveN8zhZrf8h6o-gloBWzdDGeruHlDZ7ipGQGSCNPA4z2rpqwyNIOqersbsfBbL6wbieDs3tWwgYMiH-U4NSp7W1cO1XUg7UC5SK7TtrQC3R9-Q8wxNZ1LxDoF47tszAwws2JlDPRUE7ay90MXE-fxqWM_JKbhfBll9j7zSsrD5EY8jE21qCFji2F0PMouUADZEgdlXcAlRDDtRAGN-_TQpPcc5CEk1uw6o2iV8NvR8PeaBjowJrsPVxo167CFNWO9yz4DVacnx6v_mJ1H3YFs3MznJCq7e0Ajo-bD8OoYlYCelhi2dyBaelUYi7wXlegfallQLIHFc6B4V3x5snvUo1Ov3YM5KPSKsrLdJ9t8gk-_Y6UTPVZp1Zf5o3rzGzpoAxypGVA8V4scOeBAIZHJN-Na_gpsXRl0tNoykf7v-jQP6qaHYIkPOsJt0zEn3Us8twRTc3S__surX8udu6M69objgsDYo1BfUervaapII2AmiqDjoJbUKMmOJ6UuAc2ltMuHsEt2mJTNJ1sUrd-r_7abnvRpI48zQYuBklod6fHdLtv2uqBNUcdN7AkTlKLXd1hphZBqUCc4UlRmaRn9KQD6u0oJKoV3Pn5JvldfwnYjs2m6FrbAh6MrdLTsA_dNE9_7DDN2SGrhFw09HpDY3Dheu4MZkb82hM9A5YhygAbo69DvRh6vjaXzT0qL6_DPjrsrRFKx1ZW5qSFp0DEDgb5D_sFYiC1gDwF2wWcVthoG-lOs3TdU4QntMYgvdbKiP0UTLqNJQpNEy2DjwBqJghZWmJKanHOjo9rlXCctiefBLxhXemPwPZxVBexKYw8VQj-S3Au3cPYqnRupJfKnJyfqATtcnFyyrZkLvLCb4UeeCeRIc5-ze5lWUiKlyuxs-lkyKSV-c7nrw2ftIb4f6PDl9XCb_NouDlz3en6rIXI8pI_KzK8T5yd0RbQ2ciRIZKsoy-VSJXfDuWIMBcExG0UNJUEbIGkg549XcB4bF_R5l_DsEepJED92qdc3qccLBh5lACI1HcN9X1EOZ6k_2HfP5CEzfVw-wBFc_mR7Bb41deRPH8g4gCrBgijOw=}
|
|
||||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
|
||||||
REGISTRY_NAMESPACE = default
|
|
||||||
SERVICE_NAMESPACE = default
|
|
||||||
|
|
||||||
[Welcome]
|
|
||||||
; No configuration values captured yet for this section.
|
|
||||||
|
|
||||||
[Dependencies]
|
|
||||||
STATUS = All installed
|
|
||||||
|
|
||||||
[Network]
|
|
||||||
AD_DC_HOST = myrddin.prole.org
|
|
||||||
AD_DC_IP = 10.0.0.3
|
|
||||||
ANSIBLE_DOMAIN = prole.org
|
|
||||||
ANSIBLE_INFRASTRUCTURE = ${PROLE_HOME}/infrastructure
|
|
||||||
ANSIBLE_INVENTORY = ${PROLE_HOME}/infrastructure/inventory
|
|
||||||
ANSIBLE_REALM = PROLE.ORG
|
|
||||||
ANSIBLE_TOPOLOGY = {"domain":"prole.org","realm":"PROLE.ORG","internal_records":{"aventage.prole.org":"10.0.0.206","fairyland.prole.org":"10.0.0.208","loghost.prole.org":"10.0.0.3","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://pi.prole.org:6443","server_host":"pi.prole.org","token_present":true},"groups":{"iscsi":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_hosts":["pi.prole.org","myrddin.prole.org","retropie.prole.org"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"]},"hosts":{"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":[]}
|
|
||||||
KDC_ANSIBLE_DETECTED = 10.0.0.3
|
|
||||||
|
|
||||||
[Port Forwards]
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_HOST = myrddin.prole.org
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_IP = 10.0.0.3
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_PORT = 5432
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_SERVICE = prole-db-rw
|
|
||||||
PORT_FORWARD_K3D_ENABLED = true
|
|
||||||
PORT_FORWARD_K3D_MAPPING_1 = 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_K3D_MAPPING_10 = id=supabase-kong;namespace=supabase;target=svc/kong;address=0.0.0.0;hostPort=8000;servicePort=8000;protocol=TCP;description=Supabase API (Kong)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_11 = id=supabase-studio;namespace=supabase;target=svc/studio;address=0.0.0.0;hostPort=18080;servicePort=3000;protocol=TCP;description=Supabase Studio
|
|
||||||
PORT_FORWARD_K3D_MAPPING_12 = id=supabase-auth;namespace=supabase;target=svc/auth;address=0.0.0.0;hostPort=9999;servicePort=9999;protocol=TCP;description=Supabase Auth (GoTrue)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_13 = id=supabase-rest;namespace=supabase;target=svc/rest;address=0.0.0.0;hostPort=3001;servicePort=3000;protocol=TCP;description=Supabase REST (PostgREST)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_14 = id=supabase-realtime;namespace=supabase;target=svc/realtime;address=0.0.0.0;hostPort=4000;servicePort=4000;protocol=TCP;description=Supabase Realtime
|
|
||||||
PORT_FORWARD_K3D_MAPPING_2 = 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_K3D_MAPPING_3 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=80;protocol=TCP;description=Grafana UI
|
|
||||||
PORT_FORWARD_K3D_MAPPING_4 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL (primary)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_5 = id=openbao;namespace=default;target=svc/openbao;address=127.0.0.1;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenBao
|
|
||||||
PORT_FORWARD_K3D_MAPPING_6 = id=argocd;namespace=argocd;target=svc/argocd-server;address=0.0.0.0;hostPort=8081;servicePort=80;protocol=TCP;description=ArgoCD
|
|
||||||
PORT_FORWARD_K3D_MAPPING_7 = id=garage;namespace=default;target=svc/garage;address=0.0.0.0;hostPort=3900;servicePort=3900;protocol=TCP;description=Garage S3
|
|
||||||
PORT_FORWARD_K3D_MAPPING_8 = id=opentofu;namespace=default;target=svc/opentofu;address=0.0.0.0;hostPort=8080;servicePort=8080;protocol=TCP;description=OpenTofu
|
|
||||||
PORT_FORWARD_K3D_MAPPING_9 = id=prole-db-manager;namespace=${NAMESPACE};target=svc/prole-db-manager;address=0.0.0.0;hostPort=80;servicePort=80;protocol=TCP;description=DB Manager UI
|
|
||||||
PORT_FORWARD_K3S_ENABLED = true
|
|
||||||
PORT_FORWARD_K3S_MAPPING_1 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=3000;protocol=TCP;description=Grafana UI (0.0.0.0:3000)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_2 = id=opentofu;namespace=${NAMESPACE};target=svc/opentofu;address=0.0.0.0;hostPort=8080;servicePort=8080;protocol=TCP;description=OpenTofu UI/API (0.0.0.0:8080)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_3 = id=openbao;namespace=${NAMESPACE};target=svc/openbao;address=0.0.0.0;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenVault/OpenBao UI/API (0.0.0.0:8200)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_4 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL external (0.0.0.0:5432)
|
|
||||||
|
|
||||||
[System Environment]
|
|
||||||
PROLE_CONF = ${PROLE_CONF}
|
|
||||||
PROLE_DATA = ${PROLE_DATA}
|
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
|
||||||
PROLE_LOGS = ${PROLE_LOGS}
|
|
||||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
|
||||||
PROLE_SERVICE = ${PROLE_SERVICE}
|
|
||||||
|
|
||||||
[Monitoring]
|
|
||||||
GRAFANA_ADMIN_PASSWORD = ${OPENBAO:kv/prole/${NAMESPACE}/monitoring#grafana_admin_password}
|
|
||||||
|
|
||||||
[Kerberos Authentication]
|
|
||||||
PASSWORD = ${OPENBAO:kv/prole/${NAMESPACE}/kerberos#password}
|
|
||||||
|
|
||||||
[Ollama]
|
|
||||||
; No configuration values captured yet for this section.
|
|
||||||
|
|
||||||
[Optional Features]
|
|
||||||
AT_REST_ENCRYPTION_ENABLED = true
|
|
||||||
GITOPS_ENABLED = false
|
|
||||||
KERBEROS_ENABLED = false
|
|
||||||
SUPABASE_ENABLED = true
|
|
||||||
|
|
||||||
[GitOps]
|
|
||||||
|
|
||||||
[Database Creation]
|
|
||||||
DB_NAME = prole-chrisfu-abracadabra
|
|
||||||
DB_PASSWORD_SET = true
|
|
||||||
DB_USER = chrisfu
|
|
||||||
NAMESPACE = prole-chrisfu-abracadabra
|
|
||||||
|
|
||||||
[Initialize Cluster]
|
|
||||||
ENVIRONMENT = k3d-prole-dev-cluster
|
|
||||||
K3S_SERVER_URL = https://pi.prole.org:6443
|
|
||||||
K3S_TOKEN = ${PROLE_SECRET:v1:-sPPStEN7GQEkuhI:ozUsHxoQiHCTmYxfZNJHp3yBf91OW-bGtBT1WtuYYvDEUtC4psiUiVU_KJVFlZHdS7iWCEsIitqf_JdygH3aCMloGbh01yVzyswEGVeNiaXGxE8tZ9H5pZ3fENwioPe-gAwXE_sp7yC8sWlfBMrIrRiLo8VAMxYaIQ1Icizvow9fqz38tBCZdQwsefNBoy_mhcbgLEVhCfYdch8EeC2LgmplFj9lPmyW9M22O-5l5EW3IEv901yma__at5nNDbj9Tie9ZPhoGRNnXp68lcNCsfVqS9kevvts-DS-eY5Yo1MH1pDmY_IvC_GME0bNw467lCQ4QFNK8U7R_XSMyYpMreUdCs7p4tvz2vWBJB92bgc7F-FzoAYviTNzsvFyoOiWQ44W4IzJ1ze-dTuSzP70MspglX6yZr18Nc0n1iqS3oTC9240NYgDMGGU2AoMJvb1ejJFmAFAvZketI0OD3bQvkQmk4a9Ej9Z01Fwp1TUH1DHAqmwkV70LrnKrmO7ptmdWTsWll8uE4M755KB3quSQhMzzFhFufXRAFWXTETgP9r-qkVRNCR4gSP-_8AMWUciDBUGjRP3S2xmTXKZwbdvTsaDWylCppqlRFKrqx4hDqtjoKxaYC1LzCEl945BEMTI604gpVfsPy62Vcjikb95EeFoTEM3_nt6c_ROR52U1Hv-V3lOFerSbsj0O5z9PNjwm2216-cPbDiS0-CRBW2XauK0aQjicjOOPyR8__XIbykWuXVfmcVY8pPFDUVjtX3GzsK3ym4l1pUbJ1iQ_L2oFEF6rhOXciaKFhQsy8WIn2SxWQwbYnIkONlc-vHAq1cMIH2AkeMZIteUelKnLSqiC_czmz7bDM4G9L6cO3O0oTVeQ3PLsf6vTsZQUGs6GDKwzPu4DwRpWq0rZqcCTcoC0vDuCMTmGhpBbqLxeApY1jj6QRT-w1-FjEh9ttk_VTwqqm7Lgz8BfdEeBGgsPLRYjqV_cKgQJ5IX0qNJ2To8ekrGdoKkN3h_JTAMBRwU-5PUJmx-ct972hwv8ScCf0-Ap_I7eUKjdQ5UBXv0laK0grFF4SMqmOs5OG5F3kY3Z-9AEi6vDYZzB-NKBTGS8fB0aSz4TrSVp171S5DvkU76HQ23d0tce_358hbD-GzX8i5U9q8YXvgsyVbvFpJMdth1MYmvOa42qGQm8-iYfGHXC0m5CrAPrxXakwupoMYUr1hmhXDSkFWNon02cWWZgK8Fh8gKvdscWCZCZtzijoB27SlxICWav9Ofow4Hb0sYeBg-f8YEJBAjnrKgX1B46OHTZ2Nm76pgDzR24xI=}
|
|
||||||
|
|
||||||
[Dev Cluster (k3d)]
|
|
||||||
CLUSTER_ENV = k3d-prole-dev-cluster
|
|
||||||
DISPLAY_NAME = prole-dev-cluster
|
|
||||||
KUBECTL_CONTEXT = k3d-prole-dev-cluster
|
|
||||||
MODE = k3d
|
|
||||||
|
|
||||||
[Service Cluster (k3s)]
|
|
||||||
CLUSTER_ENV = prole-service-cluster
|
|
||||||
DISPLAY_NAME = prole-service-cluster
|
|
||||||
K3S_SERVER_URL = https://pi.prole.org:6443
|
|
||||||
K3S_TOKEN = ${OPENBAO:kv/prole/${NAMESPACE}/k3s#token}
|
|
||||||
MODE = k3s
|
|
||||||
PIPELINE_URL = http://127.0.0.1:8080
|
|
||||||
|
|
||||||
[Prod Cluster (k8s)]
|
|
||||||
ARTIFACTS_DIR = ${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]
|
|
||||||
LOCAL_REGISTRY = localhost:5000
|
|
||||||
LOCAL_REGISTRY_INTERNAL = k3d-prole-registry.localhost:5000
|
|
||||||
STATUS = Built
|
|
||||||
|
|
||||||
[Initialization Scripts]
|
|
||||||
STATUS = Attempted
|
|
||||||
|
|
||||||
[Deployment]
|
|
||||||
MODE = k3d
|
|
||||||
STATUS = Deployed
|
|
||||||
TARGET = prole-dev-cluster
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
STATUS = Finished
|
|
||||||
@ -1,202 +0,0 @@
|
|||||||
; Prole Master Configuration File
|
|
||||||
; Generated by install.py on 2026-02-26 01:31:16
|
|
||||||
; This file is used as input for Ansible deployment and k8s cluster creation.
|
|
||||||
|
|
||||||
[User]
|
|
||||||
; User-editable values; derived values below reference these by default.
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
PROLE_CONF = ${PROLE_HOME}/conf
|
|
||||||
PROLE_DATA = /Users/chrisfu/.prole/data
|
|
||||||
PROLE_HOME = /Users/chrisfu/dev/prole
|
|
||||||
PROLE_LOGS = /opt/prole/logs/chrisfu
|
|
||||||
PROLE_SERVICE = ${PROLE_HOME}/etc
|
|
||||||
SERVICE_NAMESPACE = default
|
|
||||||
|
|
||||||
[Inputs]
|
|
||||||
; Screen-scoped inputs used for unattended replays (-S)
|
|
||||||
build.deploy_env = Dev
|
|
||||||
build.run_build = false
|
|
||||||
dependencies.ansible.install = true
|
|
||||||
dependencies.auto_install_missing = true
|
|
||||||
dependencies.brew.install = true
|
|
||||||
dependencies.docker.install = true
|
|
||||||
dependencies.k3d.install = true
|
|
||||||
dependencies.kubectl.install = true
|
|
||||||
dependencies.opentofu.install = true
|
|
||||||
dependencies.python.install = true
|
|
||||||
dependencies.verify_all = false
|
|
||||||
disk_selection.disk_type = local
|
|
||||||
disk_selection.local_path = ${PROLE_HOME}/prole-tools-app/dist
|
|
||||||
disk_selection.removable_mount =
|
|
||||||
env_setup.NAMESPACE = ${NAMESPACE}
|
|
||||||
env_setup.PROLE_CONF = ${PROLE_CONF}
|
|
||||||
env_setup.PROLE_DATA = ${PROLE_DATA}
|
|
||||||
env_setup.PROLE_HOME = ${PROLE_HOME}
|
|
||||||
env_setup.PROLE_LOGS = ${PROLE_LOGS}
|
|
||||||
env_setup.PROLE_SERVICE = ${PROLE_SERVICE}
|
|
||||||
init_cluster.at_rest_encryption_enabled = true
|
|
||||||
init_cluster.cluster_env = k3d-prole-dev-cluster
|
|
||||||
init_cluster.gitops_enabled = false
|
|
||||||
init_cluster.k3s_server_url = https://pi.prole.org:6443
|
|
||||||
init_cluster.k3s_token = ${OPENBAO:kv/prole/${NAMESPACE}/k3s#token}
|
|
||||||
init_cluster.kerberos_enabled = false
|
|
||||||
init_cluster.start_cluster = true
|
|
||||||
init_cluster.supabase_enabled = true
|
|
||||||
init_cnpg_deploy.force_rollout = false
|
|
||||||
init_cnpg_deploy.run_deploy = true
|
|
||||||
init_db_build.run_build = true
|
|
||||||
init_password.db_host_port = 5432
|
|
||||||
init_password.db_namespace = ${NAMESPACE}
|
|
||||||
init_password.db_password = ${OPENBAO:kv/prole/${NAMESPACE}/db#password}
|
|
||||||
init_password.db_password_confirm = ${OPENBAO:kv/prole/${NAMESPACE}/db#password}
|
|
||||||
init_password.db_username = chrisfu
|
|
||||||
init_password.generate_ssh_key = true
|
|
||||||
init_scripts.run_scripts = true
|
|
||||||
kerberos_config.enabled = True
|
|
||||||
kerberos_config.init_authority = false
|
|
||||||
kerberos_config.kdc = 10.0.0.205
|
|
||||||
kerberos_config.password = ${OPENBAO:kv/prole/${NAMESPACE}/kerberos#password}
|
|
||||||
kerberos_config.realm = PROLE.ORG
|
|
||||||
kerberos_config.test_connection = false
|
|
||||||
kerberos_config.user =
|
|
||||||
network_scan.run = true
|
|
||||||
ollama_config.model =
|
|
||||||
ollama_config.server_host =
|
|
||||||
ollama_config.server_port = 11434
|
|
||||||
|
|
||||||
[Global]
|
|
||||||
; Variables used by name in more than one place or assumed global scope
|
|
||||||
ARGOCD_NAMESPACE = argocd
|
|
||||||
CLUSTER_ENV = k3d-prole-dev-cluster
|
|
||||||
DB_HOST_PORT = 5432
|
|
||||||
DB_PASSWORD = ${OPENBAO:kv/prole/${NAMESPACE}/db#password}
|
|
||||||
DEPLOYMENT_MODE = k3d
|
|
||||||
DEPLOYMENT_TARGET = prole-dev-cluster
|
|
||||||
DOCKER_IMPORT_DIR =
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
PROLE_DB_USER = chrisfu
|
|
||||||
PROLE_ENABLE_LOCAL_REGISTRY = true
|
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
|
||||||
PROLE_K3S_SERVER = https://pi.prole.org:6443
|
|
||||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:7PxbzYh-J2MWFvEr:4HQYe1HI1OOkYObBQ3b5gdjWAUXWGootLltgL4mPVv2KZUMaaWa8rFQ4j-Re_PKl0GRX5MeyCn7GVgN6RxPAx0HnBcdMq5glD66h3b6egLE4yZ5-6SBa3pOBG0MHZr_tqeGhMRobVkJ3eAnleHuqr9lcMFDrRHcm1BingJFNsA9N6MMI9lrwZCWZMT-jrkg59JZcRO5MPhfTLysKvlJ_YP2WeX0fdqeTVFWCQveN8zhZrf8h6o-gloBWzdDGeruHlDZ7ipGQGSCNPA4z2rpqwyNIOqersbsfBbL6wbieDs3tWwgYMiH-U4NSp7W1cO1XUg7UC5SK7TtrQC3R9-Q8wxNZ1LxDoF47tszAwws2JlDPRUE7ay90MXE-fxqWM_JKbhfBll9j7zSsrD5EY8jE21qCFji2F0PMouUADZEgdlXcAlRDDtRAGN-_TQpPcc5CEk1uw6o2iV8NvR8PeaBjowJrsPVxo167CFNWO9yz4DVacnx6v_mJ1H3YFs3MznJCq7e0Ajo-bD8OoYlYCelhi2dyBaelUYi7wXlegfallQLIHFc6B4V3x5snvUo1Ov3YM5KPSKsrLdJ9t8gk-_Y6UTPVZp1Zf5o3rzGzpoAxypGVA8V4scOeBAIZHJN-Na_gpsXRl0tNoykf7v-jQP6qaHYIkPOsJt0zEn3Us8twRTc3S__surX8udu6M69objgsDYo1BfUervaapII2AmiqDjoJbUKMmOJ6UuAc2ltMuHsEt2mJTNJ1sUrd-r_7abnvRpI48zQYuBklod6fHdLtv2uqBNUcdN7AkTlKLXd1hphZBqUCc4UlRmaRn9KQD6u0oJKoV3Pn5JvldfwnYjs2m6FrbAh6MrdLTsA_dNE9_7DDN2SGrhFw09HpDY3Dheu4MZkb82hM9A5YhygAbo69DvRh6vjaXzT0qL6_DPjrsrRFKx1ZW5qSFp0DEDgb5D_sFYiC1gDwF2wWcVthoG-lOs3TdU4QntMYgvdbKiP0UTLqNJQpNEy2DjwBqJghZWmJKanHOjo9rlXCctiefBLxhXemPwPZxVBexKYw8VQj-S3Au3cPYqnRupJfKnJyfqATtcnFyyrZkLvLCb4UeeCeRIc5-ze5lWUiKlyuxs-lkyKSV-c7nrw2ftIb4f6PDl9XCb_NouDlz3en6rIXI8pI_KzK8T5yd0RbQ2ciRIZKsoy-VSJXfDuWIMBcExG0UNJUEbIGkg549XcB4bF_R5l_DsEepJED92qdc3qccLBh5lACI1HcN9X1EOZ6k_2HfP5CEzfVw-wBFc_mR7Bb41deRPH8g4gCrBgijOw=}
|
|
||||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
|
||||||
REGISTRY_NAMESPACE = default
|
|
||||||
SERVICE_NAMESPACE = default
|
|
||||||
|
|
||||||
[Welcome]
|
|
||||||
; No configuration values captured yet for this section.
|
|
||||||
|
|
||||||
[Dependencies]
|
|
||||||
STATUS = All installed
|
|
||||||
|
|
||||||
[Network]
|
|
||||||
AD_DC_HOST = myrddin.prole.org
|
|
||||||
AD_DC_IP = 10.0.0.3
|
|
||||||
ANSIBLE_DOMAIN = prole.org
|
|
||||||
ANSIBLE_INFRASTRUCTURE = ${PROLE_HOME}/infrastructure
|
|
||||||
ANSIBLE_INVENTORY = ${PROLE_HOME}/infrastructure/inventory
|
|
||||||
ANSIBLE_REALM = PROLE.ORG
|
|
||||||
ANSIBLE_TOPOLOGY = {"domain":"prole.org","realm":"PROLE.ORG","internal_records":{"aventage.prole.org":"10.0.0.206","fairyland.prole.org":"10.0.0.208","loghost.prole.org":"10.0.0.3","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://pi.prole.org:6443","server_host":"pi.prole.org","token_present":true},"groups":{"iscsi":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_hosts":["pi.prole.org","myrddin.prole.org","retropie.prole.org"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"]},"hosts":{"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":[]}
|
|
||||||
KDC_ANSIBLE_DETECTED = 10.0.0.3
|
|
||||||
KDC_AUTO_DETECTED = 10.0.0.205
|
|
||||||
KERBEROS_AUTO_ENABLED = True
|
|
||||||
|
|
||||||
[Port Forwards]
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_HOST = myrddin.prole.org
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_IP = 10.0.0.3
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_PORT = 5432
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_SERVICE = prole-db-rw
|
|
||||||
PORT_FORWARD_K3D_ENABLED = true
|
|
||||||
PORT_FORWARD_K3D_MAPPING_1 = 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_K3D_MAPPING_10 = id=supabase-kong;namespace=supabase;target=svc/kong;address=0.0.0.0;hostPort=8000;servicePort=8000;protocol=TCP;description=Supabase API (Kong)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_11 = id=supabase-studio;namespace=supabase;target=svc/studio;address=0.0.0.0;hostPort=18080;servicePort=3000;protocol=TCP;description=Supabase Studio
|
|
||||||
PORT_FORWARD_K3D_MAPPING_12 = id=supabase-auth;namespace=supabase;target=svc/auth;address=0.0.0.0;hostPort=9999;servicePort=9999;protocol=TCP;description=Supabase Auth (GoTrue)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_13 = id=supabase-rest;namespace=supabase;target=svc/rest;address=0.0.0.0;hostPort=3001;servicePort=3000;protocol=TCP;description=Supabase REST (PostgREST)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_14 = id=supabase-realtime;namespace=supabase;target=svc/realtime;address=0.0.0.0;hostPort=4000;servicePort=4000;protocol=TCP;description=Supabase Realtime
|
|
||||||
PORT_FORWARD_K3D_MAPPING_2 = 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_K3D_MAPPING_3 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=80;protocol=TCP;description=Grafana UI
|
|
||||||
PORT_FORWARD_K3D_MAPPING_4 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL (primary)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_5 = id=openbao;namespace=default;target=svc/openbao;address=127.0.0.1;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenBao
|
|
||||||
PORT_FORWARD_K3D_MAPPING_6 = id=argocd;namespace=argocd;target=svc/argocd-server;address=0.0.0.0;hostPort=8081;servicePort=80;protocol=TCP;description=ArgoCD
|
|
||||||
PORT_FORWARD_K3D_MAPPING_7 = id=garage;namespace=default;target=svc/garage;address=0.0.0.0;hostPort=3900;servicePort=3900;protocol=TCP;description=Garage S3
|
|
||||||
PORT_FORWARD_K3D_MAPPING_8 = id=opentofu;namespace=default;target=svc/opentofu;address=0.0.0.0;hostPort=8080;servicePort=8080;protocol=TCP;description=OpenTofu
|
|
||||||
PORT_FORWARD_K3D_MAPPING_9 = id=prole-db-manager;namespace=${NAMESPACE};target=svc/prole-db-manager;address=0.0.0.0;hostPort=80;servicePort=80;protocol=TCP;description=DB Manager UI
|
|
||||||
PORT_FORWARD_K3S_ENABLED = true
|
|
||||||
PORT_FORWARD_K3S_MAPPING_1 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=3000;protocol=TCP;description=Grafana UI (0.0.0.0:3000)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_2 = id=opentofu;namespace=${NAMESPACE};target=svc/opentofu;address=0.0.0.0;hostPort=8080;servicePort=8080;protocol=TCP;description=OpenTofu UI/API (0.0.0.0:8080)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_3 = id=openbao;namespace=${NAMESPACE};target=svc/openbao;address=0.0.0.0;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenVault/OpenBao UI/API (0.0.0.0:8200)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_4 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL external (0.0.0.0:5432)
|
|
||||||
|
|
||||||
[System Environment]
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
PROLE_CONF = ${PROLE_CONF}
|
|
||||||
PROLE_DATA = ${PROLE_DATA}
|
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
|
||||||
PROLE_LOGS = ${PROLE_LOGS}
|
|
||||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
|
||||||
PROLE_SERVICE = ${PROLE_SERVICE}
|
|
||||||
|
|
||||||
[Monitoring]
|
|
||||||
GRAFANA_ADMIN_PASSWORD = ${OPENBAO:kv/prole/${NAMESPACE}/monitoring#grafana_admin_password}
|
|
||||||
|
|
||||||
[Kerberos Authentication]
|
|
||||||
PASSWORD = ${OPENBAO:kv/prole/${NAMESPACE}/kerberos#password}
|
|
||||||
|
|
||||||
[Ollama]
|
|
||||||
; No configuration values captured yet for this section.
|
|
||||||
|
|
||||||
[Optional Features]
|
|
||||||
AT_REST_ENCRYPTION_ENABLED = true
|
|
||||||
GITOPS_ENABLED = false
|
|
||||||
KERBEROS_ENABLED = false
|
|
||||||
SUPABASE_ENABLED = true
|
|
||||||
|
|
||||||
[GitOps]
|
|
||||||
|
|
||||||
[Database Creation]
|
|
||||||
DB_NAME = ${NAMESPACE}
|
|
||||||
DB_PASSWORD_SET = true
|
|
||||||
DB_USER = chrisfu
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
|
|
||||||
[Initialize Cluster]
|
|
||||||
ENVIRONMENT = k3d-prole-dev-cluster
|
|
||||||
K3S_SERVER_URL = https://pi.prole.org:6443
|
|
||||||
K3S_TOKEN = ${PROLE_SECRET:v1:-sPPStEN7GQEkuhI:ozUsHxoQiHCTmYxfZNJHp3yBf91OW-bGtBT1WtuYYvDEUtC4psiUiVU_KJVFlZHdS7iWCEsIitqf_JdygH3aCMloGbh01yVzyswEGVeNiaXGxE8tZ9H5pZ3fENwioPe-gAwXE_sp7yC8sWlfBMrIrRiLo8VAMxYaIQ1Icizvow9fqz38tBCZdQwsefNBoy_mhcbgLEVhCfYdch8EeC2LgmplFj9lPmyW9M22O-5l5EW3IEv901yma__at5nNDbj9Tie9ZPhoGRNnXp68lcNCsfVqS9kevvts-DS-eY5Yo1MH1pDmY_IvC_GME0bNw467lCQ4QFNK8U7R_XSMyYpMreUdCs7p4tvz2vWBJB92bgc7F-FzoAYviTNzsvFyoOiWQ44W4IzJ1ze-dTuSzP70MspglX6yZr18Nc0n1iqS3oTC9240NYgDMGGU2AoMJvb1ejJFmAFAvZketI0OD3bQvkQmk4a9Ej9Z01Fwp1TUH1DHAqmwkV70LrnKrmO7ptmdWTsWll8uE4M755KB3quSQhMzzFhFufXRAFWXTETgP9r-qkVRNCR4gSP-_8AMWUciDBUGjRP3S2xmTXKZwbdvTsaDWylCppqlRFKrqx4hDqtjoKxaYC1LzCEl945BEMTI604gpVfsPy62Vcjikb95EeFoTEM3_nt6c_ROR52U1Hv-V3lOFerSbsj0O5z9PNjwm2216-cPbDiS0-CRBW2XauK0aQjicjOOPyR8__XIbykWuXVfmcVY8pPFDUVjtX3GzsK3ym4l1pUbJ1iQ_L2oFEF6rhOXciaKFhQsy8WIn2SxWQwbYnIkONlc-vHAq1cMIH2AkeMZIteUelKnLSqiC_czmz7bDM4G9L6cO3O0oTVeQ3PLsf6vTsZQUGs6GDKwzPu4DwRpWq0rZqcCTcoC0vDuCMTmGhpBbqLxeApY1jj6QRT-w1-FjEh9ttk_VTwqqm7Lgz8BfdEeBGgsPLRYjqV_cKgQJ5IX0qNJ2To8ekrGdoKkN3h_JTAMBRwU-5PUJmx-ct972hwv8ScCf0-Ap_I7eUKjdQ5UBXv0laK0grFF4SMqmOs5OG5F3kY3Z-9AEi6vDYZzB-NKBTGS8fB0aSz4TrSVp171S5DvkU76HQ23d0tce_358hbD-GzX8i5U9q8YXvgsyVbvFpJMdth1MYmvOa42qGQm8-iYfGHXC0m5CrAPrxXakwupoMYUr1hmhXDSkFWNon02cWWZgK8Fh8gKvdscWCZCZtzijoB27SlxICWav9Ofow4Hb0sYeBg-f8YEJBAjnrKgX1B46OHTZ2Nm76pgDzR24xI=}
|
|
||||||
|
|
||||||
[Dev Cluster (k3d)]
|
|
||||||
CLUSTER_ENV = k3d-prole-dev-cluster
|
|
||||||
DISPLAY_NAME = prole-dev-cluster
|
|
||||||
KUBECTL_CONTEXT = k3d-prole-dev-cluster
|
|
||||||
MODE = k3d
|
|
||||||
|
|
||||||
[Service Cluster (k3s)]
|
|
||||||
CLUSTER_ENV = prole-service-cluster
|
|
||||||
DISPLAY_NAME = prole-service-cluster
|
|
||||||
K3S_SERVER_URL = https://pi.prole.org:6443
|
|
||||||
K3S_TOKEN = ${OPENBAO:kv/prole/${NAMESPACE}/k3s#token}
|
|
||||||
MODE = k3s
|
|
||||||
PIPELINE_URL = http://127.0.0.1:8080
|
|
||||||
|
|
||||||
[Prod Cluster (k8s)]
|
|
||||||
ARTIFACTS_DIR = ${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]
|
|
||||||
LOCAL_REGISTRY = localhost:5000
|
|
||||||
LOCAL_REGISTRY_INTERNAL = k3d-prole-registry.localhost:5000
|
|
||||||
STATUS = Built
|
|
||||||
|
|
||||||
[Initialization Scripts]
|
|
||||||
STATUS = Attempted
|
|
||||||
|
|
||||||
[Deployment]
|
|
||||||
MODE = k3d
|
|
||||||
STATUS = Deployed
|
|
||||||
TARGET = prole-dev-cluster
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
STATUS = Failed
|
|
||||||
@ -1,176 +0,0 @@
|
|||||||
; Prole Master Configuration File
|
|
||||||
; Generated by install.py on 2026-02-06 00:16:41
|
|
||||||
; This file is used as input for Ansible deployment and k8s cluster creation.
|
|
||||||
|
|
||||||
[User]
|
|
||||||
; User-editable values; derived values below reference these by default.
|
|
||||||
PROLE_HOME = /Users/chrisfu/dev/prole
|
|
||||||
PROLE_CONF = ${PROLE_HOME}/conf
|
|
||||||
PROLE_SERVICE = ${PROLE_HOME}/etc
|
|
||||||
PROLE_DATA = /opt/prole/data/chrisfu
|
|
||||||
PROLE_LOGS = /opt/prole/logs/chrisfu
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
SERVICE_NAMESPACE = default
|
|
||||||
|
|
||||||
[Inputs]
|
|
||||||
; Screen-scoped inputs used for unattended replays (-S)
|
|
||||||
build.deploy_env = Dev
|
|
||||||
build.run_build = false
|
|
||||||
dependencies.auto_install_missing = true
|
|
||||||
dependencies.brew.install = true
|
|
||||||
dependencies.docker.install = true
|
|
||||||
dependencies.k3d.install = true
|
|
||||||
dependencies.verify_all = false
|
|
||||||
disk_selection.disk_type = local
|
|
||||||
disk_selection.local_path = ${PROLE_HOME}/prole-tools-app/dist
|
|
||||||
disk_selection.removable_mount =
|
|
||||||
env_setup.NAMESPACE = ${NAMESPACE}
|
|
||||||
env_setup.PROLE_CONF = ${PROLE_CONF}
|
|
||||||
env_setup.PROLE_DATA = ${PROLE_DATA}
|
|
||||||
env_setup.PROLE_HOME = ${PROLE_HOME}
|
|
||||||
env_setup.PROLE_LOGS = ${PROLE_LOGS}
|
|
||||||
env_setup.PROLE_SERVICE = ${PROLE_SERVICE}
|
|
||||||
init_cluster.at_rest_encryption_enabled = true
|
|
||||||
init_cluster.cluster_env = prole-service-cluster
|
|
||||||
init_cluster.k3s_server_url = https://pi.prole.org:6443
|
|
||||||
init_cluster.k3s_token = ${PROLE_SECRET:v1:D0LdPKBDiJ7v_-Ki:bcz3775FNoxa-EH8iuCF2xrGu7iBHf7ZLPCNojWKYNVDKi85PpRa6NeTWwFLfJjq0mkpNvUSPgv-7xqjCjt4rZ-PW-jb3HLGkxQ1jTo-O_nLSAWPX9ltbkU-rZqohCmzQspJhqGgGpSnYo_1QP78QqOhMHzpRCg2Jnoyocd7S_mJdIR4scTWa0mZUmWKqjHcGKAalfdWqTSVtcexiWYJMuw7CnsNDRLFT-B_qQAmtrb-9skUcmAeLf19WwNxiE9Hw5RAfkF-7lbcA4jr4DyYesg5ZNSN3cGbMscXhs1ml4WpK1gOomiGO4JKegRv5Br4yKrdbfOIIG3bSdlBytbtX4oLmQB76dtKzka7QqBre2e30EEKV8vLsDrm62_0tYTeSfXWqzsrfeMBwz4cL20ihxAM7u1RjbigtRIndggARGq0SJUnffw4diz6T6xHEx5EFuMgObpOrOwbm5YMZqrvOsEEgtT-IAmrVjGkLpZqkyaLUfrbqMkaG8jgKn6TR7bNfW0fHnFzmBHc3lLKOgUtIHGMbEYTpWijVARQx4YcsSejSPsnIf-p5dhpQrRZbw4XFxMQRzN70eelYDx5LhekkBlSUiRutcs8_lTKYrmP7QKtGnxXSxqh9cZYD4hacFhq8gvOk8QppBRa-tHt3dE-CRnULRd7g0exoCKaex8kbldMs3HUhTTFGFlNKBS2xoSB1DBG0lcfDEAIF0AbsZ6Zz7iW3K72DvpyxsYhVevMzC57ExRBVnuezSzkkuvAdQpQs_07yMDRURDdxOxbH67lySjrDbwLH70s1AvA_MflcTj4WmrbEOycvTVRh5YUdgKkDnuteN4Qy6xdUpHr2xquNq_JKy3d90ZaOZeSiR4XhB8O2fxtwM5ArrAUpATJtxsYcZeFM7RW_6arfRhbt_XMeOXk-T1J5dtGMAHfOp5MOQuwrW-k7nAunr8X85N2SkmEi9feYNgGV-UFlb-ME0sAi5ejS-W8bmYgCU6Xfc0qUwVYFnXcjoEXV9bjnQkcDRtJXh2WQ1XjxJPOMWhmmoW9mCnvfwxH29io19DxPfECTj-gN8bshyK1Va59iN70YG39Uz4-cEshZG83OJTXdbkDiaQk7HpJJT31hiW8scgTV2rSFhT19AmO_aQ0XSfXzzQ2QQu79q1fkTpz5_6Jrgzs3oVTaT6jvLMSVJzYoEmZuqkuVagOJBCO8woLE8qN1-2SfMWCYkTU7p9yfVFilT12Se8M0ebk2-M0Lnr8NWexmqI6SJxRVygz6c8pck8TBKZL2D3PrOliV_Zk3kVhZ48oN6fGScJdC8djJCI=}
|
|
||||||
init_cluster.kerberos_enabled = false
|
|
||||||
init_cluster.start_cluster = true
|
|
||||||
init_cluster.supabase_enabled = false
|
|
||||||
init_cnpg_deploy.force_rollout = false
|
|
||||||
init_cnpg_deploy.run_deploy = true
|
|
||||||
init_db_build.run_build = true
|
|
||||||
init_password.db_host_port = 5432
|
|
||||||
init_password.db_namespace = ${NAMESPACE}
|
|
||||||
init_password.db_password = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/db#password}
|
|
||||||
init_password.db_password_confirm = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/db#password}
|
|
||||||
init_password.db_username = chrisfu
|
|
||||||
init_password.generate_ssh_key = true
|
|
||||||
init_scripts.run_scripts = true
|
|
||||||
kerberos_config.enabled = false
|
|
||||||
kerberos_config.kdc = 10.0.0.3
|
|
||||||
kerberos_config.password = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/kerberos#password}
|
|
||||||
kerberos_config.realm = PROLE.ORG
|
|
||||||
kerberos_config.test_connection = false
|
|
||||||
kerberos_config.user =
|
|
||||||
network_scan.run = true
|
|
||||||
|
|
||||||
[Global]
|
|
||||||
; Variables used by name in more than one place or assumed global scope
|
|
||||||
CLUSTER_ENV = prole-service-cluster
|
|
||||||
DB_HOST_PORT = 5432
|
|
||||||
DB_PASSWORD = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/db#password}
|
|
||||||
DEPLOYMENT_MODE = k3s
|
|
||||||
DEPLOYMENT_TARGET = prole-service-cluster
|
|
||||||
DOCKER_IMPORT_DIR =
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
PROLE_DB_USER = chrisfu
|
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
|
||||||
PROLE_K3S_SERVER = https://pi.prole.org:6443
|
|
||||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:ZD9jqhOX6ouE2aLU:u4zRLPSMHFIfJYoLq431CXWYEorGuWSD4QU8VjArBdVFRXUuw57wayOzVDY82d_kREyGqtl95qIR3W-LXh05xXCh2NIpw15vLwlHBToW15tguDU1PzXRFLA669ZPYdPDFw_02fyKnPOYpELVmxWwP1U3DAGhKiTMqBDNt4oWc5pVzhTf7SO96uzUXoNG4_tQQ8H3NuSpKcg4eFbuh3lwnKmqY7m-9ofT-veIHGdXUEh0bTTamI9Qa-x1VjhwP6ZUv-VrobxTIUGd-8qUmfSPBr-U-jaOk_gvUvuyG-aTROcFrh36flmP__fLHCUwW_kAy6PZJNEPLIjgaPVBp4esjcVbPqJyiArlH8vr_hkDKhRoFJ9uagbABxuyzemTgNylVrSBHxEqIlFiQ6HncILm9s9FpLvotfDZUcG03V45vE3aV9BQsFJMfPtrkz_dVm5AuubGdG8NclV8XyRSfRy3efwPSz3wNuEkYDFJ7ZM-CGFMBz-DTlcAWVTxNXnXMyD7uK4a-13XKj_9IKijKIJ4EKuuxUr5dozvmhB90jmzFV4DYM4aiZIT6zrhARnrWkGtgRZDx89tssT20aXvPNjKy1wFcAgvqdZuqCQYlDTnhxPoJt-BEAC1xcJVJp5mnv8YW7hjJy1hA7u8S0AvCdB4eoh7vnWqLWVG1GMkgpU06641EEl3DksNZH1bbud6jV08T2SrmkRFnnj8RyoDRJ4aH8Z7uyyINV57A8bgvDsDECfQ6Iu-4WXo4ERhKdA5AJPMNW8LZexnHq1DDU4F9zDyNoaMOpBBLjyWKMsUo3fAIe1BF5PUQi4Ea4QqJq560GaoUxk5DhT38kSaDYmX3z1BB-QxzN66FQJ6QAPTLoTCllilc5j5RaSGbHiFPxyYB4H1w1boQv8sNfTi7GETkcjcCoJ4wpHwJLxDcaKvZvNxoJBACS8s-3YHEWnFe4APrDTRNCiKu1UbadbWRDK-dkNoM0KLta4SWG8eXXkDVwPV23q_Y6OJw_71g2JH2i2iT22lP3WcrsZfmpeVwGJYVsZlvbGAC_mH33qljdYtXyIfTBihh6x_flm-ogJEPuFUGIn92cF76hIqIjyz6VBf-W4y0sheU_pBd2eOZSKEFV1j4Xz8CHC1HtrtV0IFDGaXUS1kfccYoU40mczaXQxTFYtqDf-_azZyoh6I2_oQLtRX_A49NgTBpbrT0NLxFVEqXcce3QtiMsiMhV7G4K_HLPRalolqfr9x3WghS6ac8F_7KbINkXywDAgZGE7dgzr8V8Cf4ezXSNkJ7sZmDSkEBp8DO-WjZg5NIr1TMy0=}
|
|
||||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
|
||||||
SERVICE_NAMESPACE = default
|
|
||||||
ARGOCD_NAMESPACE = argocd
|
|
||||||
REGISTRY_NAMESPACE = default
|
|
||||||
PROLE_ENABLE_LOCAL_REGISTRY = true
|
|
||||||
|
|
||||||
[Welcome]
|
|
||||||
; No configuration values captured yet for this section.
|
|
||||||
|
|
||||||
[Dependencies]
|
|
||||||
STATUS = All installed
|
|
||||||
|
|
||||||
[Network]
|
|
||||||
AD_DC_HOST = myrddin.prole.org
|
|
||||||
AD_DC_IP = 10.0.0.3
|
|
||||||
ANSIBLE_DOMAIN = prole.org
|
|
||||||
ANSIBLE_INFRASTRUCTURE = ${PROLE_HOME}/infrastructure
|
|
||||||
ANSIBLE_INVENTORY = ${PROLE_HOME}/infrastructure/inventory
|
|
||||||
ANSIBLE_REALM = PROLE.ORG
|
|
||||||
ANSIBLE_TOPOLOGY = {"domain":"prole.org","realm":"PROLE.ORG","internal_records":{"aventage.prole.org":"10.0.0.206","fairyland.prole.org":"10.0.0.208","loghost.prole.org":"10.0.0.3","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://pi.prole.org:6443","server_host":"pi.prole.org","token_present":false},"groups":{"iscsi":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_hosts":["pi.prole.org","myrddin.prole.org","retropie.prole.org"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"]},"hosts":{"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":[]}
|
|
||||||
KDC_ANSIBLE_DETECTED = 10.0.0.3
|
|
||||||
|
|
||||||
[Port Forwards]
|
|
||||||
; Port-forward defaults for local k3d and external k3s access.
|
|
||||||
PORT_FORWARD_K3D_ENABLED = true
|
|
||||||
PORT_FORWARD_K3S_ENABLED = true
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_HOST = myrddin.prole.org
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_IP = 10.0.0.3
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_PORT = 5432
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_SERVICE = prole-db-rw
|
|
||||||
; Mapping format: id=...;namespace=...;target=...;address=...;hostPort=...;servicePort=...;protocol=TCP;description=...
|
|
||||||
PORT_FORWARD_K3D_MAPPING_1 = 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 (https://127.0.0.1:8443)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_2 = 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 (http://127.0.0.1:9090)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_3 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=3000;protocol=TCP;description=Grafana UI (0.0.0.0:3000)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_4 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL (primary) (0.0.0.0:5432)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_5 = id=openbao;namespace=${NAMESPACE};target=svc/openbao;address=127.0.0.1;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenBao UI (http://127.0.0.1:8200)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_1 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=3000;protocol=TCP;description=Grafana UI (0.0.0.0:3000)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_2 = id=opentofu;namespace=${NAMESPACE};target=svc/opentofu;address=0.0.0.0;hostPort=8080;servicePort=8080;protocol=TCP;description=OpenTofu UI/API (0.0.0.0:8080)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_3 = id=openbao;namespace=${NAMESPACE};target=svc/openbao;address=0.0.0.0;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenVault/OpenBao UI/API (0.0.0.0:8200)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_4 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL external (0.0.0.0:5432)
|
|
||||||
|
|
||||||
[System Environment]
|
|
||||||
PROLE_CONF = ${PROLE_CONF}
|
|
||||||
PROLE_DATA = ${PROLE_DATA}
|
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
|
||||||
PROLE_LOGS = ${PROLE_LOGS}
|
|
||||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
|
||||||
PROLE_SERVICE = ${PROLE_SERVICE}
|
|
||||||
|
|
||||||
[Monitoring]
|
|
||||||
GRAFANA_ADMIN_PASSWORD = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/monitoring#grafana_admin_password}
|
|
||||||
|
|
||||||
[Kerberos Authentication]
|
|
||||||
PASSWORD = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/kerberos#password}
|
|
||||||
|
|
||||||
[Optional Features]
|
|
||||||
AT_REST_ENCRYPTION_ENABLED = true
|
|
||||||
KERBEROS_ENABLED = false
|
|
||||||
SUPABASE_ENABLED = false
|
|
||||||
|
|
||||||
[Database Creation]
|
|
||||||
DB_NAME = prole-chrisfu-abracadabra
|
|
||||||
DB_PASSWORD_SET = true
|
|
||||||
DB_USER = chrisfu
|
|
||||||
NAMESPACE = prole-chrisfu-abracadabra
|
|
||||||
|
|
||||||
[Initialize Cluster]
|
|
||||||
ENVIRONMENT = prole-service-cluster
|
|
||||||
K3S_SERVER_URL = https://pi.prole.org:6443
|
|
||||||
K3S_TOKEN = ${PROLE_SECRET:v1:ZdE0r6mZSNW939fS:9OMnHHaxB8SpdJIf_8hLu5G675IbqVXkB0RfeAlZz8sicJCIsccAdkdIouSCPNeWPwxZb_D6jPj2ARF_2fEYyFGO4k809M6jVNyj44it43of5b_-vMhFYcH36lmnMaeY3WdEWsZ6Xitojy0od5QRm-ovGdPeyLpNvYuUYhLND0HML-Yat8SH-6hGDhoXJWrWJbxD0f4dY7kBDQYeucQZJPmxBgcQARs2LWzmnaEkJQAMJxrqL0VGHVC_DVdFrKgRPJukSQGpw8hI9fw40400q1MIcHYbdJO4Up5reEo-92ZOhkdc2jx35MQrcoq1_3BpEOiwZtBgnX5k98Iccm9NVSqVmH9RqTFnDiIHuolpz2NRoHhaqawp8fDLcewOL4q2UjtqjvQMG7wa1zEkJAURdBGAGfMx607R6OJ48xRHuOz3Pr_C3qouXtn5t9_vr-byJZh6AdKbCotwr93vON3NgS5kStn40ucqO3Ess4d2sLyssekjuktBKLMYjL90UOCgzdv3BxkdCi4ULXgu_6fshf2pCLHwAdjRiJLZkzeWT5bu8z4t60WvBxKqz2KoGny601Vn5E2SdGKtT0IBzW5Wy3oPuDyeQ6sE3xIr3_lcaBr7madtIIwKBTJzLuunFXcY9NaBzwqvgMMGYZoDPEgwpEN7oMJ7Xngf5j9vxWKLXBQPGpeWrqTm0hpGCP2wu3ZIfDM2cd04Au2gRXYpAlRn_ItXHHFIsp8W9LS5pAa7yDe3I1y4IvH67lDD1ncBJi4eEzIAnjSyeHvodnwMGmwYXqgsDmr1dqoYJgF98iDZRQ71tnTZbQWpw6jnhBp2SwTlzmdiG_vE2HbBkLjkF7E0NQiR9hQO6FxjV5qf0UGCSYl_biaYfoIQiNaukneBZAxljZJqMaQzcpw4JoDs82n8SFt5af9ajA_4uD7-ydiBO86v0hhkp6RKvX2Xbi9lnrUmJCkc0d9cUegzwa0F4_9K0e6u0fmGN8nczt9o46L6XlXyUkmbtlMRpGakfSsHOVrpAOuEZW3osXNfnMJ2pdjs_RSuKc9wxQua6yTQbzNWCtmbjYXhApwE_a7YxVwA77Eon-p_uTIiPRnZ3ECxidTR0vJMXJBj-4QtkEo8f3GVSIg_BbHbJjfyZj9KQ7GK7zrUcJTDCOd8h3Nka9ydxivPU3wLG01h-8nSJTlww_9wh3jjyXcu2-bQU2bnmnMAbWPpuQYR99Zfu8mzXK6UpQ-lC7oNFmc6L7WGdhgspWF0yQZLgYtuHQRScscCADpCLE0oxCjMQ0sfrmlxJBHkB_9OVCVna2EOfXL9_x8=}
|
|
||||||
|
|
||||||
[Dev Cluster (k3d)]
|
|
||||||
CLUSTER_ENV = k3d-prole-dev-cluster
|
|
||||||
DISPLAY_NAME = prole-dev-cluster
|
|
||||||
KUBECTL_CONTEXT = prole-service-cluster
|
|
||||||
MODE = k3d
|
|
||||||
|
|
||||||
[Service Cluster (k3s)]
|
|
||||||
CLUSTER_ENV = prole-service-cluster
|
|
||||||
DISPLAY_NAME = prole-service-cluster
|
|
||||||
K3S_SERVER_URL = https://pi.prole.org:6443
|
|
||||||
K3S_TOKEN = ${PROLE_SECRET:v1:kVESNMHu2Zlj7wMY:LD8gNX0Pc81llPAAB73o8Ytb91VZO6RZIwikfpQ55aW9xDtzRatNMY3hwYYyPvdfCmbJm8sQjckowNrxNYeFbiBsbEL6SDVx510aYabi_VgkLTWiyecr4SpCKRhtsZXJ6tKN3PGfRP6e6_jT1M0npeP_wYYXVxfCDdiyGKzZd-Jr2XYDZnyI0A6iyNVc7KZIpE0MNXsI0VKpPckLX8nI2i9WraeNlx5DTcbhbT3Ne_p75vNgVGvWaGFJLStDfnGHhkThWiC5nFp0oqckGzUkUqP1afrzdon2wqiflwTfamN-AFnw1gnwkS9z8LY7R8UhJQrxb5NYCfZVbQn-XJbdHMecujPOdpVP97gHLnwD60qizT4YCs9u6iD-T7ARvxjscpHX7dGocj3ZVeXYFK5ypmEIVT6U20gL674yebMniMz3hHMq4AEDKGfEWGZXiaY3o-y9_dorC7VmGZAn-1_FHvg6h_LP3ybCKybohm8EdmKFKfOYC_RbaNGk43Xz_aGfeq6GNZ11XmBP-5rQwHXAl0vO8aJyDD_Eruci5aSZAg0Y5fevmSszsxI0DJQYgZEB_w1yUtX6jCk5fJLdXnKartU0sYVYqtUVuCvWxmlTXBjo6IaGVnH48Pwy_yDXmjhI1aSGax-7wTFVOH5cfyvMs-vvFsw1WyF1r71a1kRaSpK80zJdDHSoxc5GAfV_P1L1cbgRbB2s08VF4r_CaL3jQ3KDc07rEDJ-Jpxe7VyH7p_beOFzbZfUvCrLswTHyi068gIsxVoMjMxRZJ7tlkM_EqoY2omZukF9564Lq0kZrkyRHdunKHOMhxr0mkFk7pos0gHE5psylgjMqQtcIrPeEYMpqlJt6jGoAmyN2iM9YaZT7ZRKWPEzonBvGx0HwktEWrOqobLn0BHy1gwdikaW80vC4Ao6F8-oGaqpEL4wAMRZTzDnCx6g2BophU5AFfD9DCjT5C5Pk4UunPh2sLFrtnFIsY8_UKt8u9L5KxPV_ty9OSxMqxCBh1Xq-QSHG7yhKkZaLudX9srlJkTJp5BHtIkAqzeO3LKwNTJDWcw29tG9E_ak-qM4mBl65V_XiPKW7Trai8zfwS1KBsgEO8aGVpn9fjDm3UfwD--q5Xl3PMBVTmrpLqTla1RCPLD-IpvAOlssNoTeoGo7cjDmXEwu110Wu1m6W0snsRxVNY5DniHyw6HDPvZqsyLLz5PojQ06jnTvdIYoKorT4CBbCyHtFhuvo_jlrHnc-N1YVSdGD1Bi7nKoXtgeEn3gn9B0XiOY3XFjN2HU4Z1OqQDoksP979Ch8yH0_U6U-D8=}
|
|
||||||
MODE = k3s
|
|
||||||
PIPELINE_URL = http://127.0.0.1:8080
|
|
||||||
|
|
||||||
[Prod Cluster (k8s)]
|
|
||||||
ARTIFACTS_DIR = ${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]
|
|
||||||
LOCAL_REGISTRY = localhost:5000
|
|
||||||
LOCAL_REGISTRY_INTERNAL =
|
|
||||||
STATUS = Built
|
|
||||||
|
|
||||||
[Initialization Scripts]
|
|
||||||
; No configuration values captured yet for this section.
|
|
||||||
|
|
||||||
[Deployment]
|
|
||||||
MODE = k3s
|
|
||||||
TARGET = prole-service-cluster
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
; No configuration values captured yet for this section.
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
# Port forward configuration for Prole services (generated).
|
|
||||||
# Format: id: local=<hostPort> remote=<servicePort> ns=<namespace> svc=<service> address=<bind>
|
|
||||||
|
|
||||||
argocd: local=8081 remote=80 ns=argocd svc=argocd-server address=0.0.0.0
|
|
||||||
garage: local=3900 remote=3900 ns=default svc=garage address=0.0.0.0
|
|
||||||
openbao: local=8200 remote=8200 ns=default svc=openbao address=127.0.0.1
|
|
||||||
opentofu: local=8080 remote=8080 ns=default 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
|
|
||||||
postgres: local=5432 remote=5432 ns=prole-db-a0001 svc=prole-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
|
|
||||||
grafana: local=3000 remote=80 ns=monitoring svc=kps-grafana address=0.0.0.0
|
|
||||||
prole-db-manager: local=80 remote=80 ns=prole-db-a0001 svc=prole-db-manager address=0.0.0.0
|
|
||||||
@ -1,3 +1,11 @@
|
|||||||
# Port mappings for Prole Tools (generated).
|
# Port mappings for Prole Tools (generated).
|
||||||
# Format: key: local=... remote=... ns=... svc=... address=...
|
# Format: key: local=... remote=... ns=... svc=... address=...
|
||||||
|
|
||||||
|
argocd: local=8081 remote=80 ns=argocd svc=argocd-server address=0.0.0.0
|
||||||
|
garage: local=3900 remote=3900 ns=default svc=garage address=0.0.0.0
|
||||||
|
openbao: local=8200 remote=8200 ns=default svc=openbao address=127.0.0.1
|
||||||
|
opentofu: local=8080 remote=8080 ns=default 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
|
||||||
|
postgres: local=5432 remote=5432 ns=prole-db001 svc=prole-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
|
||||||
|
grafana: local=3000 remote=80 ns=monitoring svc=kps-grafana address=0.0.0.0
|
||||||
|
|||||||
@ -1,132 +0,0 @@
|
|||||||
# PostgreSQL Client Authentication Configuration File
|
|
||||||
# ===================================================
|
|
||||||
#
|
|
||||||
# Refer to the "Client Authentication" section in the PostgreSQL
|
|
||||||
# documentation for a complete description of this file. A short
|
|
||||||
# synopsis follows.
|
|
||||||
#
|
|
||||||
# ----------------------
|
|
||||||
# Authentication Records
|
|
||||||
# ----------------------
|
|
||||||
#
|
|
||||||
# This file controls: which hosts are allowed to connect, how clients
|
|
||||||
# are authenticated, which PostgreSQL user names they can use, which
|
|
||||||
# databases they can access. Records take one of these forms:
|
|
||||||
#
|
|
||||||
# local DATABASE USER METHOD [OPTIONS]
|
|
||||||
# host DATABASE USER ADDRESS METHOD [OPTIONS]
|
|
||||||
# hostssl DATABASE USER ADDRESS METHOD [OPTIONS]
|
|
||||||
# hostnossl DATABASE USER ADDRESS METHOD [OPTIONS]
|
|
||||||
# hostgssenc DATABASE USER ADDRESS METHOD [OPTIONS]
|
|
||||||
# hostnogssenc DATABASE USER ADDRESS METHOD [OPTIONS]
|
|
||||||
#
|
|
||||||
# (The uppercase items must be replaced by actual values.)
|
|
||||||
#
|
|
||||||
# The first field is the connection type:
|
|
||||||
# - "local" is a Unix-domain socket
|
|
||||||
# - "host" is a TCP/IP socket (encrypted or not)
|
|
||||||
# - "hostssl" is a TCP/IP socket that is SSL-encrypted
|
|
||||||
# - "hostnossl" is a TCP/IP socket that is not SSL-encrypted
|
|
||||||
# - "hostgssenc" is a TCP/IP socket that is GSSAPI-encrypted
|
|
||||||
# - "hostnogssenc" is a TCP/IP socket that is not GSSAPI-encrypted
|
|
||||||
#
|
|
||||||
# DATABASE can be "all", "sameuser", "samerole", "replication", a
|
|
||||||
# database name, a regular expression (if it starts with a slash (/))
|
|
||||||
# or a comma-separated list thereof. The "all" keyword does not match
|
|
||||||
# "replication". Access to replication must be enabled in a separate
|
|
||||||
# record (see example below).
|
|
||||||
#
|
|
||||||
# USER can be "all", a user name, a group name prefixed with "+", a
|
|
||||||
# regular expression (if it starts with a slash (/)) or a comma-separated
|
|
||||||
# list thereof. In both the DATABASE and USER fields you can also write
|
|
||||||
# a file name prefixed with "@" to include names from a separate file.
|
|
||||||
#
|
|
||||||
# ADDRESS specifies the set of hosts the record matches. It can be a
|
|
||||||
# host name, or it is made up of an IP address and a CIDR mask that is
|
|
||||||
# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that
|
|
||||||
# specifies the number of significant bits in the mask. A host name
|
|
||||||
# that starts with a dot (.) matches a suffix of the actual host name.
|
|
||||||
# Alternatively, you can write an IP address and netmask in separate
|
|
||||||
# columns to specify the set of hosts. Instead of a CIDR-address, you
|
|
||||||
# can write "samehost" to match any of the server's own IP addresses,
|
|
||||||
# or "samenet" to match any address in any subnet that the server is
|
|
||||||
# directly connected to.
|
|
||||||
#
|
|
||||||
# METHOD can be "trust", "reject", "md5", "password", "scram-sha-256",
|
|
||||||
# "gss", "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert".
|
|
||||||
# Note that "password" sends passwords in clear text; "md5" or
|
|
||||||
# "scram-sha-256" are preferred since they send encrypted passwords.
|
|
||||||
#
|
|
||||||
# OPTIONS are a set of options for the authentication in the format
|
|
||||||
# NAME=VALUE. The available options depend on the different
|
|
||||||
# authentication methods -- refer to the "Client Authentication"
|
|
||||||
# section in the documentation for a list of which options are
|
|
||||||
# available for which authentication methods.
|
|
||||||
#
|
|
||||||
# Database and user names containing spaces, commas, quotes and other
|
|
||||||
# special characters must be quoted. Quoting one of the keywords
|
|
||||||
# "all", "sameuser", "samerole" or "replication" makes the name lose
|
|
||||||
# its special character, and just match a database or username with
|
|
||||||
# that name.
|
|
||||||
#
|
|
||||||
# ---------------
|
|
||||||
# Include Records
|
|
||||||
# ---------------
|
|
||||||
#
|
|
||||||
# This file allows the inclusion of external files or directories holding
|
|
||||||
# more records, using the following keywords:
|
|
||||||
#
|
|
||||||
# include FILE
|
|
||||||
# include_if_exists FILE
|
|
||||||
# include_dir DIRECTORY
|
|
||||||
#
|
|
||||||
# FILE is the file name to include, and DIR is the directory name containing
|
|
||||||
# the file(s) to include. Any file in a directory will be loaded if suffixed
|
|
||||||
# with ".conf". The files of a directory are ordered by name.
|
|
||||||
# include_if_exists ignores missing files. FILE and DIRECTORY can be
|
|
||||||
# specified as a relative or an absolute path, and can be double-quoted if
|
|
||||||
# they contain spaces.
|
|
||||||
#
|
|
||||||
# -------------
|
|
||||||
# Miscellaneous
|
|
||||||
# -------------
|
|
||||||
#
|
|
||||||
# This file is read on server startup and when the server receives a
|
|
||||||
# SIGHUP signal. If you edit the file on a running system, you have to
|
|
||||||
# SIGHUP the server for the changes to take effect, run "pg_ctl reload",
|
|
||||||
# or execute "SELECT pg_reload_conf()".
|
|
||||||
#
|
|
||||||
# ----------------------------------
|
|
||||||
# Put your actual configuration here
|
|
||||||
# ----------------------------------
|
|
||||||
#
|
|
||||||
# If you want to allow non-local connections, you need to add more
|
|
||||||
# "host" records. In that case you will also need to make PostgreSQL
|
|
||||||
# listen on a non-local interface via the listen_addresses
|
|
||||||
# configuration parameter, or via the -i or -h command line switches.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# DO NOT DISABLE!
|
|
||||||
# If you change this first entry you will need to make sure that the
|
|
||||||
# database superuser can access the database using some other method.
|
|
||||||
# Noninteractive access to all databases is required during automatic
|
|
||||||
# maintenance (custom daily cronjobs, replication, and similar tasks).
|
|
||||||
#
|
|
||||||
# Database administrative login by Unix domain socket
|
|
||||||
local all postgres peer
|
|
||||||
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
|
|
||||||
# "local" is for Unix domain socket connections only
|
|
||||||
local all all peer
|
|
||||||
# IPv4 local connections:
|
|
||||||
host all all 127.0.0.1/32 scram-sha-256
|
|
||||||
# IPv6 local connections:
|
|
||||||
host all all ::1/128 scram-sha-256
|
|
||||||
# Allow replication connections from localhost, by a user with the
|
|
||||||
# replication privilege.
|
|
||||||
local replication all peer
|
|
||||||
host replication all 127.0.0.1/32 scram-sha-256
|
|
||||||
host replication all ::1/128 scram-sha-256
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
# PostgreSQL User Name Maps
|
|
||||||
# =========================
|
|
||||||
#
|
|
||||||
# ---------------
|
|
||||||
# Mapping Records
|
|
||||||
# ---------------
|
|
||||||
#
|
|
||||||
# Refer to the PostgreSQL documentation, chapter "Client
|
|
||||||
# Authentication" for a complete description. A short synopsis
|
|
||||||
# follows.
|
|
||||||
#
|
|
||||||
# This file controls PostgreSQL user name mapping. It maps external
|
|
||||||
# user names to their corresponding PostgreSQL user names. Records
|
|
||||||
# are of the form:
|
|
||||||
#
|
|
||||||
# MAPNAME SYSTEM-USERNAME PG-USERNAME
|
|
||||||
#
|
|
||||||
# (The uppercase quantities must be replaced by actual values.)
|
|
||||||
#
|
|
||||||
# MAPNAME is the (otherwise freely chosen) map name that was used in
|
|
||||||
# pg_hba.conf. SYSTEM-USERNAME is the detected user name of the
|
|
||||||
# client. PG-USERNAME is the requested PostgreSQL user name. The
|
|
||||||
# existence of a record specifies that SYSTEM-USERNAME may connect as
|
|
||||||
# PG-USERNAME.
|
|
||||||
#
|
|
||||||
# If SYSTEM-USERNAME starts with a slash (/), it will be treated as a
|
|
||||||
# regular expression. Optionally this can contain a capture (a
|
|
||||||
# parenthesized subexpression). The substring matching the capture
|
|
||||||
# will be substituted for \1 (backslash-one) if present in
|
|
||||||
# PG-USERNAME.
|
|
||||||
#
|
|
||||||
# PG-USERNAME can be "all", a user name, a group name prefixed with "+", or
|
|
||||||
# a regular expression (if it starts with a slash (/)). If it is a regular
|
|
||||||
# expression, the substring matching with \1 has no effect.
|
|
||||||
#
|
|
||||||
# Multiple maps may be specified in this file and used by pg_hba.conf.
|
|
||||||
#
|
|
||||||
# No map names are defined in the default configuration. If all
|
|
||||||
# system user names and PostgreSQL user names are the same, you don't
|
|
||||||
# need anything in this file.
|
|
||||||
#
|
|
||||||
# ---------------
|
|
||||||
# Include Records
|
|
||||||
# ---------------
|
|
||||||
#
|
|
||||||
# This file allows the inclusion of external files or directories holding
|
|
||||||
# more records, using the following keywords:
|
|
||||||
#
|
|
||||||
# include FILE
|
|
||||||
# include_if_exists FILE
|
|
||||||
# include_dir DIRECTORY
|
|
||||||
#
|
|
||||||
# FILE is the file name to include, and DIR is the directory name containing
|
|
||||||
# the file(s) to include. Any file in a directory will be loaded if suffixed
|
|
||||||
# with ".conf". The files of a directory are ordered by name.
|
|
||||||
# include_if_exists ignores missing files. FILE and DIRECTORY can be
|
|
||||||
# specified as a relative or an absolute path, and can be double-quoted if
|
|
||||||
# they contain spaces.
|
|
||||||
#
|
|
||||||
# -------------------------------
|
|
||||||
# Miscellaneous
|
|
||||||
# -------------------------------
|
|
||||||
#
|
|
||||||
# This file is read on server startup and when the postmaster receives
|
|
||||||
# a SIGHUP signal. If you edit the file on a running system, you have
|
|
||||||
# to SIGHUP the postmaster for the changes to take effect. You can
|
|
||||||
# use "pg_ctl reload" to do that.
|
|
||||||
|
|
||||||
# Put your actual configuration here
|
|
||||||
# ----------------------------------
|
|
||||||
|
|
||||||
# MAPNAME SYSTEM-USERNAME PG-USERNAME
|
|
||||||
@ -1,844 +0,0 @@
|
|||||||
# -----------------------------
|
|
||||||
# PostgreSQL configuration file
|
|
||||||
# -----------------------------
|
|
||||||
#
|
|
||||||
# This file consists of lines of the form:
|
|
||||||
#
|
|
||||||
# name = value
|
|
||||||
#
|
|
||||||
# (The "=" is optional.) Whitespace may be used. Comments are introduced with
|
|
||||||
# "#" anywhere on a line. The complete list of parameter names and allowed
|
|
||||||
# values can be found in the PostgreSQL documentation.
|
|
||||||
#
|
|
||||||
# The commented-out settings shown in this file represent the default values.
|
|
||||||
# Re-commenting a setting is NOT sufficient to revert it to the default value;
|
|
||||||
# you need to reload the server.
|
|
||||||
#
|
|
||||||
# This file is read on server startup and when the server receives a SIGHUP
|
|
||||||
# signal. If you edit the file on a running system, you have to SIGHUP the
|
|
||||||
# server for the changes to take effect, run "pg_ctl reload", or execute
|
|
||||||
# "SELECT pg_reload_conf()". Some parameters, which are marked below,
|
|
||||||
# require a server shutdown and restart to take effect.
|
|
||||||
#
|
|
||||||
# Any parameter can also be given as a command-line option to the server, e.g.,
|
|
||||||
# "postgres -c log_connections=on". Some parameters can be changed at run time
|
|
||||||
# with the "SET" SQL command.
|
|
||||||
#
|
|
||||||
# Memory units: B = bytes Time units: us = microseconds
|
|
||||||
# kB = kilobytes ms = milliseconds
|
|
||||||
# MB = megabytes s = seconds
|
|
||||||
# GB = gigabytes min = minutes
|
|
||||||
# TB = terabytes h = hours
|
|
||||||
# d = days
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# FILE LOCATIONS
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# The default values of these variables are driven from the -D command-line
|
|
||||||
# option or PGDATA environment variable, represented here as ConfigDir.
|
|
||||||
|
|
||||||
data_directory = '/var/lib/postgresql/17/main' # use data in another directory
|
|
||||||
# (change requires restart)
|
|
||||||
hba_file = '/etc/postgresql/17/main/pg_hba.conf' # host-based authentication file
|
|
||||||
# (change requires restart)
|
|
||||||
ident_file = '/etc/postgresql/17/main/pg_ident.conf' # ident configuration file
|
|
||||||
# (change requires restart)
|
|
||||||
|
|
||||||
# If external_pid_file is not explicitly set, no extra PID file is written.
|
|
||||||
external_pid_file = '/var/run/postgresql/17-main.pid' # write an extra PID file
|
|
||||||
# (change requires restart)
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# CONNECTIONS AND AUTHENTICATION
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Connection Settings -
|
|
||||||
|
|
||||||
#listen_addresses = 'localhost' # what IP address(es) to listen on;
|
|
||||||
# comma-separated list of addresses;
|
|
||||||
# defaults to 'localhost'; use '*' for all
|
|
||||||
# (change requires restart)
|
|
||||||
port = 5432 # (change requires restart)
|
|
||||||
max_connections = 100 # (change requires restart)
|
|
||||||
#reserved_connections = 0 # (change requires restart)
|
|
||||||
#superuser_reserved_connections = 3 # (change requires restart)
|
|
||||||
unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories
|
|
||||||
# (change requires restart)
|
|
||||||
#unix_socket_group = '' # (change requires restart)
|
|
||||||
#unix_socket_permissions = 0777 # begin with 0 to use octal notation
|
|
||||||
# (change requires restart)
|
|
||||||
#bonjour = off # advertise server via Bonjour
|
|
||||||
# (change requires restart)
|
|
||||||
#bonjour_name = '' # defaults to the computer name
|
|
||||||
# (change requires restart)
|
|
||||||
|
|
||||||
# - TCP settings -
|
|
||||||
# see "man tcp" for details
|
|
||||||
|
|
||||||
#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
|
|
||||||
# 0 selects the system default
|
|
||||||
#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
|
|
||||||
# 0 selects the system default
|
|
||||||
#tcp_keepalives_count = 0 # TCP_KEEPCNT;
|
|
||||||
# 0 selects the system default
|
|
||||||
#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds;
|
|
||||||
# 0 selects the system default
|
|
||||||
|
|
||||||
#client_connection_check_interval = 0 # time between checks for client
|
|
||||||
# disconnection while running queries;
|
|
||||||
# 0 for never
|
|
||||||
|
|
||||||
# - Authentication -
|
|
||||||
|
|
||||||
#authentication_timeout = 1min # 1s-600s
|
|
||||||
#password_encryption = scram-sha-256 # scram-sha-256 or md5
|
|
||||||
#scram_iterations = 4096
|
|
||||||
|
|
||||||
# GSSAPI using Kerberos
|
|
||||||
#krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab'
|
|
||||||
#krb_caseins_users = off
|
|
||||||
#gss_accept_delegation = off
|
|
||||||
|
|
||||||
# - SSL -
|
|
||||||
|
|
||||||
ssl = on
|
|
||||||
#ssl_ca_file = ''
|
|
||||||
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
|
|
||||||
#ssl_crl_file = ''
|
|
||||||
#ssl_crl_dir = ''
|
|
||||||
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
|
|
||||||
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
|
|
||||||
#ssl_prefer_server_ciphers = on
|
|
||||||
#ssl_ecdh_curve = 'prime256v1'
|
|
||||||
#ssl_min_protocol_version = 'TLSv1.2'
|
|
||||||
#ssl_max_protocol_version = ''
|
|
||||||
#ssl_dh_params_file = ''
|
|
||||||
#ssl_passphrase_command = ''
|
|
||||||
#ssl_passphrase_command_supports_reload = off
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# RESOURCE USAGE (except WAL)
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Memory -
|
|
||||||
|
|
||||||
shared_buffers = 128MB # min 128kB
|
|
||||||
# (change requires restart)
|
|
||||||
#huge_pages = try # on, off, or try
|
|
||||||
# (change requires restart)
|
|
||||||
#huge_page_size = 0 # zero for system default
|
|
||||||
# (change requires restart)
|
|
||||||
#temp_buffers = 8MB # min 800kB
|
|
||||||
#max_prepared_transactions = 0 # zero disables the feature
|
|
||||||
# (change requires restart)
|
|
||||||
# Caution: it is not advisable to set max_prepared_transactions nonzero unless
|
|
||||||
# you actively intend to use prepared transactions.
|
|
||||||
#work_mem = 4MB # min 64kB
|
|
||||||
#hash_mem_multiplier = 2.0 # 1-1000.0 multiplier on hash table work_mem
|
|
||||||
#maintenance_work_mem = 64MB # min 64kB
|
|
||||||
#autovacuum_work_mem = -1 # min 64kB, or -1 to use maintenance_work_mem
|
|
||||||
#logical_decoding_work_mem = 64MB # min 64kB
|
|
||||||
#max_stack_depth = 2MB # min 100kB
|
|
||||||
#shared_memory_type = mmap # the default is the first option
|
|
||||||
# supported by the operating system:
|
|
||||||
# mmap
|
|
||||||
# sysv
|
|
||||||
# windows
|
|
||||||
# (change requires restart)
|
|
||||||
dynamic_shared_memory_type = posix # the default is usually the first option
|
|
||||||
# supported by the operating system:
|
|
||||||
# posix
|
|
||||||
# sysv
|
|
||||||
# windows
|
|
||||||
# mmap
|
|
||||||
# (change requires restart)
|
|
||||||
#min_dynamic_shared_memory = 0MB # (change requires restart)
|
|
||||||
#vacuum_buffer_usage_limit = 2MB # size of vacuum and analyze buffer access strategy ring;
|
|
||||||
# 0 to disable vacuum buffer access strategy;
|
|
||||||
# range 128kB to 16GB
|
|
||||||
|
|
||||||
# SLRU buffers (change requires restart)
|
|
||||||
#commit_timestamp_buffers = 0 # memory for pg_commit_ts (0 = auto)
|
|
||||||
#multixact_offset_buffers = 16 # memory for pg_multixact/offsets
|
|
||||||
#multixact_member_buffers = 32 # memory for pg_multixact/members
|
|
||||||
#notify_buffers = 16 # memory for pg_notify
|
|
||||||
#serializable_buffers = 32 # memory for pg_serial
|
|
||||||
#subtransaction_buffers = 0 # memory for pg_subtrans (0 = auto)
|
|
||||||
#transaction_buffers = 0 # memory for pg_xact (0 = auto)
|
|
||||||
|
|
||||||
# - Disk -
|
|
||||||
|
|
||||||
#temp_file_limit = -1 # limits per-process temp file space
|
|
||||||
# in kilobytes, or -1 for no limit
|
|
||||||
|
|
||||||
#max_notify_queue_pages = 1048576 # limits the number of SLRU pages allocated
|
|
||||||
# for NOTIFY / LISTEN queue
|
|
||||||
|
|
||||||
# - Kernel Resources -
|
|
||||||
|
|
||||||
#max_files_per_process = 1000 # min 64
|
|
||||||
# (change requires restart)
|
|
||||||
|
|
||||||
# - Cost-Based Vacuum Delay -
|
|
||||||
|
|
||||||
#vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables)
|
|
||||||
#vacuum_cost_page_hit = 1 # 0-10000 credits
|
|
||||||
#vacuum_cost_page_miss = 2 # 0-10000 credits
|
|
||||||
#vacuum_cost_page_dirty = 20 # 0-10000 credits
|
|
||||||
#vacuum_cost_limit = 200 # 1-10000 credits
|
|
||||||
|
|
||||||
# - Background Writer -
|
|
||||||
|
|
||||||
#bgwriter_delay = 200ms # 10-10000ms between rounds
|
|
||||||
#bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables
|
|
||||||
#bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round
|
|
||||||
#bgwriter_flush_after = 512kB # measured in pages, 0 disables
|
|
||||||
|
|
||||||
# - Asynchronous Behavior -
|
|
||||||
|
|
||||||
#backend_flush_after = 0 # measured in pages, 0 disables
|
|
||||||
#effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
|
|
||||||
#maintenance_io_concurrency = 10 # 1-1000; 0 disables prefetching
|
|
||||||
#io_combine_limit = 128kB # usually 1-32 blocks (depends on OS)
|
|
||||||
#max_worker_processes = 8 # (change requires restart)
|
|
||||||
#max_parallel_workers_per_gather = 2 # limited by max_parallel_workers
|
|
||||||
#max_parallel_maintenance_workers = 2 # limited by max_parallel_workers
|
|
||||||
#max_parallel_workers = 8 # number of max_worker_processes that
|
|
||||||
# can be used in parallel operations
|
|
||||||
#parallel_leader_participation = on
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# WRITE-AHEAD LOG
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Settings -
|
|
||||||
|
|
||||||
#wal_level = replica # minimal, replica, or logical
|
|
||||||
# (change requires restart)
|
|
||||||
#fsync = on # flush data to disk for crash safety
|
|
||||||
# (turning this off can cause
|
|
||||||
# unrecoverable data corruption)
|
|
||||||
#synchronous_commit = on # synchronization level;
|
|
||||||
# off, local, remote_write, remote_apply, or on
|
|
||||||
#wal_sync_method = fsync # the default is the first option
|
|
||||||
# supported by the operating system:
|
|
||||||
# open_datasync
|
|
||||||
# fdatasync (default on Linux and FreeBSD)
|
|
||||||
# fsync
|
|
||||||
# fsync_writethrough
|
|
||||||
# open_sync
|
|
||||||
#full_page_writes = on # recover from partial page writes
|
|
||||||
#wal_log_hints = off # also do full page writes of non-critical updates
|
|
||||||
# (change requires restart)
|
|
||||||
#wal_compression = off # enables compression of full-page writes;
|
|
||||||
# off, pglz, lz4, zstd, or on
|
|
||||||
#wal_init_zero = on # zero-fill new WAL files
|
|
||||||
#wal_recycle = on # recycle WAL files
|
|
||||||
#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
|
|
||||||
# (change requires restart)
|
|
||||||
#wal_writer_delay = 200ms # 1-10000 milliseconds
|
|
||||||
#wal_writer_flush_after = 1MB # measured in pages, 0 disables
|
|
||||||
#wal_skip_threshold = 2MB
|
|
||||||
|
|
||||||
#commit_delay = 0 # range 0-100000, in microseconds
|
|
||||||
#commit_siblings = 5 # range 1-1000
|
|
||||||
|
|
||||||
# - Checkpoints -
|
|
||||||
|
|
||||||
#checkpoint_timeout = 5min # range 30s-1d
|
|
||||||
#checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0
|
|
||||||
#checkpoint_flush_after = 256kB # measured in pages, 0 disables
|
|
||||||
#checkpoint_warning = 30s # 0 disables
|
|
||||||
max_wal_size = 1GB
|
|
||||||
min_wal_size = 80MB
|
|
||||||
|
|
||||||
# - Prefetching during recovery -
|
|
||||||
|
|
||||||
#recovery_prefetch = try # prefetch pages referenced in the WAL?
|
|
||||||
#wal_decode_buffer_size = 512kB # lookahead window used for prefetching
|
|
||||||
# (change requires restart)
|
|
||||||
|
|
||||||
# - Archiving -
|
|
||||||
|
|
||||||
#archive_mode = off # enables archiving; off, on, or always
|
|
||||||
# (change requires restart)
|
|
||||||
#archive_library = '' # library to use to archive a WAL file
|
|
||||||
# (empty string indicates archive_command should
|
|
||||||
# be used)
|
|
||||||
#archive_command = '' # command to use to archive a WAL file
|
|
||||||
# placeholders: %p = path of file to archive
|
|
||||||
# %f = file name only
|
|
||||||
# e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
|
|
||||||
#archive_timeout = 0 # force a WAL file switch after this
|
|
||||||
# number of seconds; 0 disables
|
|
||||||
|
|
||||||
# - Archive Recovery -
|
|
||||||
|
|
||||||
# These are only used in recovery mode.
|
|
||||||
|
|
||||||
#restore_command = '' # command to use to restore an archived WAL file
|
|
||||||
# placeholders: %p = path of file to restore
|
|
||||||
# %f = file name only
|
|
||||||
# e.g. 'cp /mnt/server/archivedir/%f %p'
|
|
||||||
#archive_cleanup_command = '' # command to execute at every restartpoint
|
|
||||||
#recovery_end_command = '' # command to execute at completion of recovery
|
|
||||||
|
|
||||||
# - Recovery Target -
|
|
||||||
|
|
||||||
# Set these only when performing a targeted recovery.
|
|
||||||
|
|
||||||
#recovery_target = '' # 'immediate' to end recovery as soon as a
|
|
||||||
# consistent state is reached
|
|
||||||
# (change requires restart)
|
|
||||||
#recovery_target_name = '' # the named restore point to which recovery will proceed
|
|
||||||
# (change requires restart)
|
|
||||||
#recovery_target_time = '' # the time stamp up to which recovery will proceed
|
|
||||||
# (change requires restart)
|
|
||||||
#recovery_target_xid = '' # the transaction ID up to which recovery will proceed
|
|
||||||
# (change requires restart)
|
|
||||||
#recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed
|
|
||||||
# (change requires restart)
|
|
||||||
#recovery_target_inclusive = on # Specifies whether to stop:
|
|
||||||
# just after the specified recovery target (on)
|
|
||||||
# just before the recovery target (off)
|
|
||||||
# (change requires restart)
|
|
||||||
#recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID
|
|
||||||
# (change requires restart)
|
|
||||||
#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown'
|
|
||||||
# (change requires restart)
|
|
||||||
|
|
||||||
# - WAL Summarization -
|
|
||||||
|
|
||||||
#summarize_wal = off # run WAL summarizer process?
|
|
||||||
#wal_summary_keep_time = '10d' # when to remove old summary files, 0 = never
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# REPLICATION
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Sending Servers -
|
|
||||||
|
|
||||||
# Set these on the primary and on any standby that will send replication data.
|
|
||||||
|
|
||||||
#max_wal_senders = 10 # max number of walsender processes
|
|
||||||
# (change requires restart)
|
|
||||||
#max_replication_slots = 10 # max number of replication slots
|
|
||||||
# (change requires restart)
|
|
||||||
#wal_keep_size = 0 # in megabytes; 0 disables
|
|
||||||
#max_slot_wal_keep_size = -1 # in megabytes; -1 disables
|
|
||||||
#wal_sender_timeout = 60s # in milliseconds; 0 disables
|
|
||||||
#track_commit_timestamp = off # collect timestamp of transaction commit
|
|
||||||
# (change requires restart)
|
|
||||||
|
|
||||||
# - Primary Server -
|
|
||||||
|
|
||||||
# These settings are ignored on a standby server.
|
|
||||||
|
|
||||||
#synchronous_standby_names = '' # standby servers that provide sync rep
|
|
||||||
# method to choose sync standbys, number of sync standbys,
|
|
||||||
# and comma-separated list of application_name
|
|
||||||
# from standby(s); '*' = all
|
|
||||||
#synchronized_standby_slots = '' # streaming replication standby server slot
|
|
||||||
# names that logical walsender processes will wait for
|
|
||||||
|
|
||||||
# - Standby Servers -
|
|
||||||
|
|
||||||
# These settings are ignored on a primary server.
|
|
||||||
|
|
||||||
#primary_conninfo = '' # connection string to sending server
|
|
||||||
#primary_slot_name = '' # replication slot on sending server
|
|
||||||
#hot_standby = on # "off" disallows queries during recovery
|
|
||||||
# (change requires restart)
|
|
||||||
#max_standby_archive_delay = 30s # max delay before canceling queries
|
|
||||||
# when reading WAL from archive;
|
|
||||||
# -1 allows indefinite delay
|
|
||||||
#max_standby_streaming_delay = 30s # max delay before canceling queries
|
|
||||||
# when reading streaming WAL;
|
|
||||||
# -1 allows indefinite delay
|
|
||||||
#wal_receiver_create_temp_slot = off # create temp slot if primary_slot_name
|
|
||||||
# is not set
|
|
||||||
#wal_receiver_status_interval = 10s # send replies at least this often
|
|
||||||
# 0 disables
|
|
||||||
#hot_standby_feedback = off # send info from standby to prevent
|
|
||||||
# query conflicts
|
|
||||||
#wal_receiver_timeout = 60s # time that receiver waits for
|
|
||||||
# communication from primary
|
|
||||||
# in milliseconds; 0 disables
|
|
||||||
#wal_retrieve_retry_interval = 5s # time to wait before retrying to
|
|
||||||
# retrieve WAL after a failed attempt
|
|
||||||
#recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery
|
|
||||||
#sync_replication_slots = off # enables slot synchronization on the physical standby from the primary
|
|
||||||
|
|
||||||
# - Subscribers -
|
|
||||||
|
|
||||||
# These settings are ignored on a publisher.
|
|
||||||
|
|
||||||
#max_logical_replication_workers = 4 # taken from max_worker_processes
|
|
||||||
# (change requires restart)
|
|
||||||
#max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers
|
|
||||||
#max_parallel_apply_workers_per_subscription = 2 # taken from max_logical_replication_workers
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# QUERY TUNING
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Planner Method Configuration -
|
|
||||||
|
|
||||||
#enable_async_append = on
|
|
||||||
#enable_bitmapscan = on
|
|
||||||
#enable_gathermerge = on
|
|
||||||
#enable_hashagg = on
|
|
||||||
#enable_hashjoin = on
|
|
||||||
#enable_incremental_sort = on
|
|
||||||
#enable_indexscan = on
|
|
||||||
#enable_indexonlyscan = on
|
|
||||||
#enable_material = on
|
|
||||||
#enable_memoize = on
|
|
||||||
#enable_mergejoin = on
|
|
||||||
#enable_nestloop = on
|
|
||||||
#enable_parallel_append = on
|
|
||||||
#enable_parallel_hash = on
|
|
||||||
#enable_partition_pruning = on
|
|
||||||
#enable_partitionwise_join = off
|
|
||||||
#enable_partitionwise_aggregate = off
|
|
||||||
#enable_presorted_aggregate = on
|
|
||||||
#enable_seqscan = on
|
|
||||||
#enable_sort = on
|
|
||||||
#enable_tidscan = on
|
|
||||||
#enable_group_by_reordering = on
|
|
||||||
|
|
||||||
# - Planner Cost Constants -
|
|
||||||
|
|
||||||
#seq_page_cost = 1.0 # measured on an arbitrary scale
|
|
||||||
#random_page_cost = 4.0 # same scale as above
|
|
||||||
#cpu_tuple_cost = 0.01 # same scale as above
|
|
||||||
#cpu_index_tuple_cost = 0.005 # same scale as above
|
|
||||||
#cpu_operator_cost = 0.0025 # same scale as above
|
|
||||||
#parallel_setup_cost = 1000.0 # same scale as above
|
|
||||||
#parallel_tuple_cost = 0.1 # same scale as above
|
|
||||||
#min_parallel_table_scan_size = 8MB
|
|
||||||
#min_parallel_index_scan_size = 512kB
|
|
||||||
#effective_cache_size = 4GB
|
|
||||||
|
|
||||||
#jit_above_cost = 100000 # perform JIT compilation if available
|
|
||||||
# and query more expensive than this;
|
|
||||||
# -1 disables
|
|
||||||
#jit_inline_above_cost = 500000 # inline small functions if query is
|
|
||||||
# more expensive than this; -1 disables
|
|
||||||
#jit_optimize_above_cost = 500000 # use expensive JIT optimizations if
|
|
||||||
# query is more expensive than this;
|
|
||||||
# -1 disables
|
|
||||||
|
|
||||||
# - Genetic Query Optimizer -
|
|
||||||
|
|
||||||
#geqo = on
|
|
||||||
#geqo_threshold = 12
|
|
||||||
#geqo_effort = 5 # range 1-10
|
|
||||||
#geqo_pool_size = 0 # selects default based on effort
|
|
||||||
#geqo_generations = 0 # selects default based on effort
|
|
||||||
#geqo_selection_bias = 2.0 # range 1.5-2.0
|
|
||||||
#geqo_seed = 0.0 # range 0.0-1.0
|
|
||||||
|
|
||||||
# - Other Planner Options -
|
|
||||||
|
|
||||||
#default_statistics_target = 100 # range 1-10000
|
|
||||||
#constraint_exclusion = partition # on, off, or partition
|
|
||||||
#cursor_tuple_fraction = 0.1 # range 0.0-1.0
|
|
||||||
#from_collapse_limit = 8
|
|
||||||
#jit = on # allow JIT compilation
|
|
||||||
#join_collapse_limit = 8 # 1 disables collapsing of explicit
|
|
||||||
# JOIN clauses
|
|
||||||
#plan_cache_mode = auto # auto, force_generic_plan or
|
|
||||||
# force_custom_plan
|
|
||||||
#recursive_worktable_factor = 10.0 # range 0.001-1000000
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# REPORTING AND LOGGING
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Where to Log -
|
|
||||||
|
|
||||||
#log_destination = 'stderr' # Valid values are combinations of
|
|
||||||
# stderr, csvlog, jsonlog, syslog, and
|
|
||||||
# eventlog, depending on platform.
|
|
||||||
# csvlog and jsonlog require
|
|
||||||
# logging_collector to be on.
|
|
||||||
|
|
||||||
# This is used when logging to stderr:
|
|
||||||
#logging_collector = off # Enable capturing of stderr, jsonlog,
|
|
||||||
# and csvlog into log files. Required
|
|
||||||
# to be on for csvlogs and jsonlogs.
|
|
||||||
# (change requires restart)
|
|
||||||
|
|
||||||
# These are only used if logging_collector is on:
|
|
||||||
#log_directory = 'log' # directory where log files are written,
|
|
||||||
# can be absolute or relative to PGDATA
|
|
||||||
#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern,
|
|
||||||
# can include strftime() escapes
|
|
||||||
#log_file_mode = 0600 # creation mode for log files,
|
|
||||||
# begin with 0 to use octal notation
|
|
||||||
#log_rotation_age = 1d # Automatic rotation of logfiles will
|
|
||||||
# happen after that time. 0 disables.
|
|
||||||
#log_rotation_size = 10MB # Automatic rotation of logfiles will
|
|
||||||
# happen after that much log output.
|
|
||||||
# 0 disables.
|
|
||||||
#log_truncate_on_rotation = off # If on, an existing log file with the
|
|
||||||
# same name as the new log file will be
|
|
||||||
# truncated rather than appended to.
|
|
||||||
# But such truncation only occurs on
|
|
||||||
# time-driven rotation, not on restarts
|
|
||||||
# or size-driven rotation. Default is
|
|
||||||
# off, meaning append to existing files
|
|
||||||
# in all cases.
|
|
||||||
|
|
||||||
# These are relevant when logging to syslog:
|
|
||||||
#syslog_facility = 'LOCAL0'
|
|
||||||
#syslog_ident = 'postgres'
|
|
||||||
#syslog_sequence_numbers = on
|
|
||||||
#syslog_split_messages = on
|
|
||||||
|
|
||||||
# This is only relevant when logging to eventlog (Windows):
|
|
||||||
# (change requires restart)
|
|
||||||
#event_source = 'PostgreSQL'
|
|
||||||
|
|
||||||
# - When to Log -
|
|
||||||
|
|
||||||
#log_min_messages = warning # values in order of decreasing detail:
|
|
||||||
# debug5
|
|
||||||
# debug4
|
|
||||||
# debug3
|
|
||||||
# debug2
|
|
||||||
# debug1
|
|
||||||
# info
|
|
||||||
# notice
|
|
||||||
# warning
|
|
||||||
# error
|
|
||||||
# log
|
|
||||||
# fatal
|
|
||||||
# panic
|
|
||||||
|
|
||||||
#log_min_error_statement = error # values in order of decreasing detail:
|
|
||||||
# debug5
|
|
||||||
# debug4
|
|
||||||
# debug3
|
|
||||||
# debug2
|
|
||||||
# debug1
|
|
||||||
# info
|
|
||||||
# notice
|
|
||||||
# warning
|
|
||||||
# error
|
|
||||||
# log
|
|
||||||
# fatal
|
|
||||||
# panic (effectively off)
|
|
||||||
|
|
||||||
#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
|
|
||||||
# and their durations, > 0 logs only
|
|
||||||
# statements running at least this number
|
|
||||||
# of milliseconds
|
|
||||||
|
|
||||||
#log_min_duration_sample = -1 # -1 is disabled, 0 logs a sample of statements
|
|
||||||
# and their durations, > 0 logs only a sample of
|
|
||||||
# statements running at least this number
|
|
||||||
# of milliseconds;
|
|
||||||
# sample fraction is determined by log_statement_sample_rate
|
|
||||||
|
|
||||||
#log_statement_sample_rate = 1.0 # fraction of logged statements exceeding
|
|
||||||
# log_min_duration_sample to be logged;
|
|
||||||
# 1.0 logs all such statements, 0.0 never logs
|
|
||||||
|
|
||||||
|
|
||||||
#log_transaction_sample_rate = 0.0 # fraction of transactions whose statements
|
|
||||||
# are logged regardless of their duration; 1.0 logs all
|
|
||||||
# statements from all transactions, 0.0 never logs
|
|
||||||
|
|
||||||
#log_startup_progress_interval = 10s # Time between progress updates for
|
|
||||||
# long-running startup operations.
|
|
||||||
# 0 disables the feature, > 0 indicates
|
|
||||||
# the interval in milliseconds.
|
|
||||||
|
|
||||||
# - What to Log -
|
|
||||||
|
|
||||||
#debug_print_parse = off
|
|
||||||
#debug_print_rewritten = off
|
|
||||||
#debug_print_plan = off
|
|
||||||
#debug_pretty_print = on
|
|
||||||
#log_autovacuum_min_duration = 10min # log autovacuum activity;
|
|
||||||
# -1 disables, 0 logs all actions and
|
|
||||||
# their durations, > 0 logs only
|
|
||||||
# actions running at least this number
|
|
||||||
# of milliseconds.
|
|
||||||
#log_checkpoints = on
|
|
||||||
#log_connections = off
|
|
||||||
#log_disconnections = off
|
|
||||||
#log_duration = off
|
|
||||||
#log_error_verbosity = default # terse, default, or verbose messages
|
|
||||||
#log_hostname = off
|
|
||||||
log_line_prefix = '%m [%p] %q%u@%d ' # special values:
|
|
||||||
# %a = application name
|
|
||||||
# %u = user name
|
|
||||||
# %d = database name
|
|
||||||
# %r = remote host and port
|
|
||||||
# %h = remote host
|
|
||||||
# %b = backend type
|
|
||||||
# %p = process ID
|
|
||||||
# %P = process ID of parallel group leader
|
|
||||||
# %t = timestamp without milliseconds
|
|
||||||
# %m = timestamp with milliseconds
|
|
||||||
# %n = timestamp with milliseconds (as a Unix epoch)
|
|
||||||
# %Q = query ID (0 if none or not computed)
|
|
||||||
# %i = command tag
|
|
||||||
# %e = SQL state
|
|
||||||
# %c = session ID
|
|
||||||
# %l = session line number
|
|
||||||
# %s = session start timestamp
|
|
||||||
# %v = virtual transaction ID
|
|
||||||
# %x = transaction ID (0 if none)
|
|
||||||
# %q = stop here in non-session
|
|
||||||
# processes
|
|
||||||
# %% = '%'
|
|
||||||
# e.g. '<%u%%%d> '
|
|
||||||
#log_lock_waits = off # log lock waits >= deadlock_timeout
|
|
||||||
#log_recovery_conflict_waits = off # log standby recovery conflict waits
|
|
||||||
# >= deadlock_timeout
|
|
||||||
#log_parameter_max_length = -1 # when logging statements, limit logged
|
|
||||||
# bind-parameter values to N bytes;
|
|
||||||
# -1 means print in full, 0 disables
|
|
||||||
#log_parameter_max_length_on_error = 0 # when logging an error, limit logged
|
|
||||||
# bind-parameter values to N bytes;
|
|
||||||
# -1 means print in full, 0 disables
|
|
||||||
#log_statement = 'none' # none, ddl, mod, all
|
|
||||||
#log_replication_commands = off
|
|
||||||
#log_temp_files = -1 # log temporary files equal or larger
|
|
||||||
# than the specified size in kilobytes;
|
|
||||||
# -1 disables, 0 logs all temp files
|
|
||||||
log_timezone = 'Etc/UTC'
|
|
||||||
|
|
||||||
# - Process Title -
|
|
||||||
|
|
||||||
cluster_name = '17/main' # added to process titles if nonempty
|
|
||||||
# (change requires restart)
|
|
||||||
#update_process_title = on
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# STATISTICS
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Cumulative Query and Index Statistics -
|
|
||||||
|
|
||||||
#track_activities = on
|
|
||||||
#track_activity_query_size = 1024 # (change requires restart)
|
|
||||||
#track_counts = on
|
|
||||||
#track_io_timing = off
|
|
||||||
#track_wal_io_timing = off
|
|
||||||
#track_functions = none # none, pl, all
|
|
||||||
#stats_fetch_consistency = cache # cache, none, snapshot
|
|
||||||
|
|
||||||
|
|
||||||
# - Monitoring -
|
|
||||||
|
|
||||||
#compute_query_id = auto
|
|
||||||
#log_statement_stats = off
|
|
||||||
#log_parser_stats = off
|
|
||||||
#log_planner_stats = off
|
|
||||||
#log_executor_stats = off
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# AUTOVACUUM
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#autovacuum = on # Enable autovacuum subprocess? 'on'
|
|
||||||
# requires track_counts to also be on.
|
|
||||||
#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
|
|
||||||
# (change requires restart)
|
|
||||||
#autovacuum_naptime = 1min # time between autovacuum runs
|
|
||||||
#autovacuum_vacuum_threshold = 50 # min number of row updates before
|
|
||||||
# vacuum
|
|
||||||
#autovacuum_vacuum_insert_threshold = 1000 # min number of row inserts
|
|
||||||
# before vacuum; -1 disables insert
|
|
||||||
# vacuums
|
|
||||||
#autovacuum_analyze_threshold = 50 # min number of row updates before
|
|
||||||
# analyze
|
|
||||||
#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
|
|
||||||
#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table
|
|
||||||
# size before insert vacuum
|
|
||||||
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
|
|
||||||
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
|
|
||||||
# (change requires restart)
|
|
||||||
#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
|
|
||||||
# before forced vacuum
|
|
||||||
# (change requires restart)
|
|
||||||
#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for
|
|
||||||
# autovacuum, in milliseconds;
|
|
||||||
# -1 means use vacuum_cost_delay
|
|
||||||
#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
|
|
||||||
# autovacuum, -1 means use
|
|
||||||
# vacuum_cost_limit
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# CLIENT CONNECTION DEFAULTS
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Statement Behavior -
|
|
||||||
|
|
||||||
#client_min_messages = notice # values in order of decreasing detail:
|
|
||||||
# debug5
|
|
||||||
# debug4
|
|
||||||
# debug3
|
|
||||||
# debug2
|
|
||||||
# debug1
|
|
||||||
# log
|
|
||||||
# notice
|
|
||||||
# warning
|
|
||||||
# error
|
|
||||||
#search_path = '"$user", public' # schema names
|
|
||||||
#row_security = on
|
|
||||||
#default_table_access_method = 'heap'
|
|
||||||
#default_tablespace = '' # a tablespace name, '' uses the default
|
|
||||||
#default_toast_compression = 'pglz' # 'pglz' or 'lz4'
|
|
||||||
#temp_tablespaces = '' # a list of tablespace names, '' uses
|
|
||||||
# only default tablespace
|
|
||||||
#check_function_bodies = on
|
|
||||||
#default_transaction_isolation = 'read committed'
|
|
||||||
#default_transaction_read_only = off
|
|
||||||
#default_transaction_deferrable = off
|
|
||||||
#session_replication_role = 'origin'
|
|
||||||
#statement_timeout = 0 # in milliseconds, 0 is disabled
|
|
||||||
#transaction_timeout = 0 # in milliseconds, 0 is disabled
|
|
||||||
#lock_timeout = 0 # in milliseconds, 0 is disabled
|
|
||||||
#idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
|
|
||||||
#idle_session_timeout = 0 # in milliseconds, 0 is disabled
|
|
||||||
#vacuum_freeze_table_age = 150000000
|
|
||||||
#vacuum_freeze_min_age = 50000000
|
|
||||||
#vacuum_failsafe_age = 1600000000
|
|
||||||
#vacuum_multixact_freeze_table_age = 150000000
|
|
||||||
#vacuum_multixact_freeze_min_age = 5000000
|
|
||||||
#vacuum_multixact_failsafe_age = 1600000000
|
|
||||||
#bytea_output = 'hex' # hex, escape
|
|
||||||
#xmlbinary = 'base64'
|
|
||||||
#xmloption = 'content'
|
|
||||||
#gin_pending_list_limit = 4MB
|
|
||||||
#createrole_self_grant = '' # set and/or inherit
|
|
||||||
#event_triggers = on
|
|
||||||
|
|
||||||
# - Locale and Formatting -
|
|
||||||
|
|
||||||
datestyle = 'iso, mdy'
|
|
||||||
#intervalstyle = 'postgres'
|
|
||||||
timezone = 'Etc/UTC'
|
|
||||||
#timezone_abbreviations = 'Default' # Select the set of available time zone
|
|
||||||
# abbreviations. Currently, there are
|
|
||||||
# Default
|
|
||||||
# Australia (historical usage)
|
|
||||||
# India
|
|
||||||
# You can create your own file in
|
|
||||||
# share/timezonesets/.
|
|
||||||
#extra_float_digits = 1 # min -15, max 3; any value >0 actually
|
|
||||||
# selects precise output mode
|
|
||||||
#client_encoding = sql_ascii # actually, defaults to database
|
|
||||||
# encoding
|
|
||||||
|
|
||||||
# These settings are initialized by initdb, but they can be changed.
|
|
||||||
lc_messages = 'C.UTF-8' # locale for system error message
|
|
||||||
# strings
|
|
||||||
lc_monetary = 'C.UTF-8' # locale for monetary formatting
|
|
||||||
lc_numeric = 'C.UTF-8' # locale for number formatting
|
|
||||||
lc_time = 'C.UTF-8' # locale for time formatting
|
|
||||||
|
|
||||||
#icu_validation_level = warning # report ICU locale validation
|
|
||||||
# errors at the given level
|
|
||||||
|
|
||||||
# default configuration for text search
|
|
||||||
default_text_search_config = 'pg_catalog.english'
|
|
||||||
|
|
||||||
# - Shared Library Preloading -
|
|
||||||
|
|
||||||
#local_preload_libraries = ''
|
|
||||||
#session_preload_libraries = ''
|
|
||||||
#shared_preload_libraries = '' # (change requires restart)
|
|
||||||
#jit_provider = 'llvmjit' # JIT library to use
|
|
||||||
|
|
||||||
# - Other Defaults -
|
|
||||||
|
|
||||||
#dynamic_library_path = '$libdir'
|
|
||||||
#extension_destdir = '' # prepend path when loading extensions
|
|
||||||
# and shared objects (added by Debian)
|
|
||||||
#gin_fuzzy_search_limit = 0
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# LOCK MANAGEMENT
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#deadlock_timeout = 1s
|
|
||||||
#max_locks_per_transaction = 64 # min 10
|
|
||||||
# (change requires restart)
|
|
||||||
#max_pred_locks_per_transaction = 64 # min 10
|
|
||||||
# (change requires restart)
|
|
||||||
#max_pred_locks_per_relation = -2 # negative values mean
|
|
||||||
# (max_pred_locks_per_transaction
|
|
||||||
# / -max_pred_locks_per_relation) - 1
|
|
||||||
#max_pred_locks_per_page = 2 # min 0
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# VERSION AND PLATFORM COMPATIBILITY
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# - Previous PostgreSQL Versions -
|
|
||||||
|
|
||||||
#array_nulls = on
|
|
||||||
#backslash_quote = safe_encoding # on, off, or safe_encoding
|
|
||||||
#escape_string_warning = on
|
|
||||||
#lo_compat_privileges = off
|
|
||||||
#quote_all_identifiers = off
|
|
||||||
#standard_conforming_strings = on
|
|
||||||
#synchronize_seqscans = on
|
|
||||||
|
|
||||||
# - Other Platforms and Clients -
|
|
||||||
|
|
||||||
#transform_null_equals = off
|
|
||||||
#allow_alter_system = on
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# ERROR HANDLING
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#exit_on_error = off # terminate session on any error?
|
|
||||||
#restart_after_crash = on # reinitialize after backend crash?
|
|
||||||
#data_sync_retry = off # retry or panic on failure to fsync
|
|
||||||
# data?
|
|
||||||
# (change requires restart)
|
|
||||||
#recovery_init_sync_method = fsync # fsync, syncfs (Linux 5.8+)
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# CONFIG FILE INCLUDES
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# These options allow settings to be loaded from files other than the
|
|
||||||
# default postgresql.conf. Note that these are directives, not variable
|
|
||||||
# assignments, so they can usefully be given more than once.
|
|
||||||
|
|
||||||
include_dir = 'conf.d' # include files ending in '.conf' from
|
|
||||||
# a directory, e.g., 'conf.d'
|
|
||||||
#include_if_exists = '...' # include file only if it exists
|
|
||||||
#include = '...' # include file
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# CUSTOMIZED OPTIONS
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Add settings for extensions here
|
|
||||||
151
conf/prole.cfg
151
conf/prole.cfg
@ -1,21 +1,36 @@
|
|||||||
; Prole Master Configuration File
|
; Prole Master Configuration File
|
||||||
; Generated by install.py on 2026-03-01 00:30:19
|
; Generated by install.py on 2026-03-01 19:22:14
|
||||||
; 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]
|
||||||
; User-editable values; derived values below reference these by default.
|
; User-editable values; derived values below reference these by default.
|
||||||
NAMESPACE = prole-db-a0001
|
NAMESPACE = prole-db001
|
||||||
PROLE_CONF = ${PROLE_HOME}/conf
|
PROLE_CONF = /Users/chrisfu/dev/prole/conf
|
||||||
PROLE_DATA = /Users/chrisfu/.prole/data
|
PROLE_DATA = /Users/chrisfu/.prole/data
|
||||||
PROLE_HOME = /Users/chrisfu/dev/prole
|
PROLE_HOME = mock_val
|
||||||
PROLE_LOGS = /opt/prole/logs/chrisfu
|
PROLE_LOGS = /opt/prole/logs/chrisfu
|
||||||
PROLE_SERVICE = ${PROLE_HOME}/etc
|
PROLE_SERVICE = /Users/chrisfu/dev/prole/etc
|
||||||
SERVICE_NAMESPACE = default
|
SERVICE_NAMESPACE = default
|
||||||
|
|
||||||
[Inputs]
|
[Inputs]
|
||||||
; Screen-scoped inputs used for unattended replays (-S)
|
; Screen-scoped inputs used for unattended replays (-S)
|
||||||
build.deploy_env = Dev
|
build.deploy_env = Dev
|
||||||
build.run_build = false
|
build.run_build = false
|
||||||
|
database_options.distribution = percona
|
||||||
|
database_options.ext.dblink = True
|
||||||
|
database_options.ext.pg_buffercache = True
|
||||||
|
database_options.ext.pg_freespacemap = True
|
||||||
|
database_options.ext.pg_repack = True
|
||||||
|
database_options.ext.pg_stat_monitor = True
|
||||||
|
database_options.ext.pg_stat_statements = True
|
||||||
|
database_options.ext.pgaudit = True
|
||||||
|
database_options.ext.pgbadger = True
|
||||||
|
database_options.ext.pgcrypto = True
|
||||||
|
database_options.ext.pgrowlocks = True
|
||||||
|
database_options.ext.pgvector = True
|
||||||
|
database_options.ext.postgis = True
|
||||||
|
database_options.ext.postgres_fdw = True
|
||||||
|
database_options.version_type = v18
|
||||||
dependencies.ansible.install = true
|
dependencies.ansible.install = true
|
||||||
dependencies.auto_install_missing = true
|
dependencies.auto_install_missing = true
|
||||||
dependencies.brew.install = true
|
dependencies.brew.install = true
|
||||||
@ -27,40 +42,41 @@ 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 = ${PROLE_HOME}/prole-tools-app/dist
|
disk_selection.local_path = /Users/chrisfu
|
||||||
disk_selection.removable_mount =
|
disk_selection.removable_mount =
|
||||||
env_setup.NAMESPACE = prole-hq0-db0
|
env_setup.NAMESPACE = ${NAMESPACE}
|
||||||
env_setup.PROLE_CONF = ${PROLE_CONF}
|
env_setup.PROLE_CONF = ${PROLE_CONF}
|
||||||
env_setup.PROLE_DATA = ${PROLE_DATA}
|
env_setup.PROLE_DATA = ${PROLE_DATA}
|
||||||
env_setup.PROLE_HOME = ${PROLE_HOME}
|
env_setup.PROLE_HOME = ${PROLE_HOME}
|
||||||
env_setup.PROLE_LOGS = ${PROLE_LOGS}
|
env_setup.PROLE_LOGS = ${PROLE_LOGS}
|
||||||
env_setup.PROLE_SERVICE = ${PROLE_SERVICE}
|
env_setup.PROLE_SERVICE = ${PROLE_SERVICE}
|
||||||
init_cluster.at_rest_encryption_enabled = true
|
init_cluster.at_rest_encryption_enabled = true
|
||||||
init_cluster.cluster_env = service
|
init_cluster.cluster_env = dev
|
||||||
init_cluster.deployment_target = prole-service-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_server_url =
|
||||||
init_cluster.k3s_token = ${OPENBAO:REDACTED}
|
init_cluster.k3s_token =
|
||||||
init_cluster.kerberos_enabled = true
|
init_cluster.kerberos_enabled = false
|
||||||
init_cluster.mode = k3s
|
init_cluster.mode = k3d
|
||||||
init_cluster.start_cluster = true
|
init_cluster.start_cluster = true
|
||||||
init_cluster.supabase_enabled = false
|
init_cluster.supabase_enabled = false
|
||||||
init_cnpg_deploy.force_rollout = false
|
init_cnpg_deploy.force_rollout = false
|
||||||
init_cnpg_deploy.run_deploy = true
|
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 = prole-hq0-db0
|
init_password.db_namespace = ${NAMESPACE}
|
||||||
init_password.db_password = ${OPENBAO:kv/prole/${NAMESPACE}/db#password}
|
init_password.db_password = ${OPENBAO:kv/prole/prole-db001/db#password}
|
||||||
init_password.db_password_confirm = ${OPENBAO:kv/prole/${NAMESPACE}/db#password}
|
init_password.db_password_confirm = ${OPENBAO:kv/prole/prole-db001/db#password}
|
||||||
init_password.db_username = chrisfu
|
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 = False
|
||||||
kerberos_config.kdc = 10.0.0.3
|
kerberos_config.init_authority = false
|
||||||
kerberos_config.password = ${OPENBAO:kv/prole/${NAMESPACE}/kerberos#password}
|
kerberos_config.kdc = 10.0.0.205
|
||||||
kerberos_config.realm = PROLE.ORG
|
kerberos_config.password = ${OPENBAO:kv/prole/prole-db001/kerberos#password}
|
||||||
kerberos_config.test_connection = true
|
kerberos_config.realm =
|
||||||
kerberos_config.user = chrisfu
|
kerberos_config.test_connection = false
|
||||||
|
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 =
|
||||||
@ -68,41 +84,53 @@ 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 = service
|
ARGOCD_NAMESPACE = argocd
|
||||||
|
CLUSTER_ENV = dev
|
||||||
DB_HOST_PORT = 5432
|
DB_HOST_PORT = 5432
|
||||||
DB_PASSWORD = ${OPENBAO:kv/prole/${NAMESPACE}/db#password}
|
DB_PASSWORD = ${OPENBAO:kv/prole/prole-db001/db#password}
|
||||||
DEPLOYMENT_MODE = k3s
|
DEPLOYMENT_MODE = k3d
|
||||||
DEPLOYMENT_TARGET = prole-service-cluster
|
DEPLOYMENT_TARGET = knoe-dev-cluster
|
||||||
|
DOCKER_IMPORT_DIR =
|
||||||
NAMESPACE = ${NAMESPACE}
|
NAMESPACE = ${NAMESPACE}
|
||||||
PROLE_DB_USER = chrisfu
|
PROLE_DB_USER = root
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
PROLE_HOME = ${PROLE_HOME}
|
||||||
PROLE_K3S_SERVER = https://myrddin.prole.org:6443
|
PROLE_K3S_SERVER =
|
||||||
PROLE_K3S_TOKEN = ${OPENBAO:REDACTED}
|
PROLE_K3S_TOKEN = ${OPENBAO:kv/prole/prole-db001/k3s#token}
|
||||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
||||||
|
REGISTRY_NAMESPACE = default
|
||||||
SERVICE_NAMESPACE = default
|
SERVICE_NAMESPACE = default
|
||||||
|
|
||||||
[Welcome]
|
[Welcome]
|
||||||
; No configuration values captured yet for this section.
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Dependencies]
|
[Dependencies]
|
||||||
; No configuration values captured yet for this section.
|
STATUS = All installed
|
||||||
|
|
||||||
[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 = ${PROLE_HOME}/infrastructure
|
ANSIBLE_INFRASTRUCTURE = /Users/chrisfu/dev/prole/infrastructure
|
||||||
ANSIBLE_INVENTORY = ${PROLE_HOME}/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","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"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_hosts":["myrddin.prole.org","pi.prole.org","retropie.prole.org"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"]},"hosts":{"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":[]}
|
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","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"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_hosts":["myrddin.prole.org","pi.prole.org","retropie.prole.org"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"]},"hosts":{"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":[]}
|
||||||
KDC_ANSIBLE_DETECTED = 10.0.0.3
|
KDC_ANSIBLE_DETECTED = 10.0.0.3
|
||||||
KDC_AUTO_DETECTED = 10.0.0.3
|
KDC_AUTO_DETECTED = 10.0.0.205
|
||||||
KERBEROS_AUTO_ENABLED = True
|
KERBEROS_AUTO_ENABLED = False
|
||||||
|
|
||||||
[Port Forwards]
|
[Port Forwards]
|
||||||
; No configuration values captured yet for this section.
|
PORT_FORWARD_K3D_MAPPING_1 = id=argocd;namespace=argocd;target=svc/argocd-server;address=0.0.0.0;hostPort=8081;servicePort=80;protocol=TCP;description=ArgoCD
|
||||||
|
PORT_FORWARD_K3D_MAPPING_2 = id=garage;namespace=default;target=svc/garage;address=0.0.0.0;hostPort=3900;servicePort=3900;protocol=TCP;description=Garage S3
|
||||||
|
PORT_FORWARD_K3D_MAPPING_3 = id=openbao;namespace=default;target=svc/openbao;address=127.0.0.1;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenBao
|
||||||
|
PORT_FORWARD_K3D_MAPPING_4 = id=opentofu;namespace=default;target=svc/opentofu;address=0.0.0.0;hostPort=8080;servicePort=8080;protocol=TCP;description=OpenTofu
|
||||||
|
PORT_FORWARD_K3D_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_K3D_MAPPING_6 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL (primary)
|
||||||
|
PORT_FORWARD_K3D_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_K3D_MAPPING_8 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=80;protocol=TCP;description=Grafana UI
|
||||||
|
STATUS = Running
|
||||||
|
|
||||||
[System Environment]
|
[System Environment]
|
||||||
|
NAMESPACE = prole-db001
|
||||||
PROLE_CONF = ${PROLE_CONF}
|
PROLE_CONF = ${PROLE_CONF}
|
||||||
PROLE_DATA = ${PROLE_DATA}
|
PROLE_DATA = ${PROLE_DATA}
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
PROLE_HOME = ${PROLE_HOME}
|
||||||
@ -113,70 +141,59 @@ PROLE_SERVICE = ${PROLE_SERVICE}
|
|||||||
; No configuration values captured yet for this section.
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Kerberos Authentication]
|
[Kerberos Authentication]
|
||||||
AD_PORT_FORWARD = 1
|
PASSWORD = ${OPENBAO:kv/prole/prole-db001/kerberos#password}
|
||||||
AD_PROXY_HOST_NETWORK = 1
|
STATUS = Skipped
|
||||||
AD_PROXY_IMAGE = alpine/socat
|
|
||||||
AD_PROXY_SERVICE = prole-kerberos-ad-dc
|
|
||||||
AD_TCP_PORTS = 88 389 445 464 636
|
|
||||||
AD_UDP_PORTS = 88 464
|
|
||||||
ENABLED = True
|
|
||||||
KDC = 10.0.0.3
|
|
||||||
PASSWORD = ${OPENBAO:kv/prole/${NAMESPACE}/kerberos#password}
|
|
||||||
REALM = PROLE.ORG
|
|
||||||
SERVER = 10.0.0.3
|
|
||||||
USER = chrisfu
|
|
||||||
|
|
||||||
[Ollama]
|
[Ollama]
|
||||||
; No configuration values captured yet for this section.
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[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 = false
|
||||||
SUPABASE_ENABLED = False
|
SUPABASE_ENABLED = false
|
||||||
|
|
||||||
[GitOps]
|
[GitOps]
|
||||||
; No configuration values captured yet for this section.
|
STATUS = Skipped
|
||||||
|
|
||||||
[Database Creation]
|
[Database Creation]
|
||||||
|
DB_NAME = prole-db001
|
||||||
|
DB_USER = root
|
||||||
NAMESPACE = ${NAMESPACE}
|
NAMESPACE = ${NAMESPACE}
|
||||||
|
|
||||||
[Initialize Cluster]
|
[Initialize Cluster]
|
||||||
ENVIRONMENT = service
|
; No configuration values captured yet for this section.
|
||||||
K3S_SERVER_URL = https://myrddin.prole.org:6443
|
|
||||||
K3S_TOKEN = ${OPENBAO:REDACTED}
|
|
||||||
|
|
||||||
[Dev Cluster (k3d)]
|
[Dev Cluster (k3d)]
|
||||||
CLUSTER_ENV = dev
|
CLUSTER_ENV = k3d-knoe-dev-cluster
|
||||||
DISPLAY_NAME = prole-dev-cluster
|
DISPLAY_NAME = knoe-dev-cluster
|
||||||
KUBECTL_CONTEXT = prole-k3s
|
KUBECTL_CONTEXT = dev
|
||||||
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 = https://myrddin.prole.org:6443
|
K3S_SERVER_URL =
|
||||||
K3S_TOKEN = ${OPENBAO:REDACTED}
|
K3S_TOKEN = ${OPENBAO:kv/prole/prole-db001/k3s#token}
|
||||||
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 = /Users/chrisfu/dev/prole/data/staging
|
ARTIFACTS_DIR = ${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
|
||||||
PIPELINE_URL = http://127.0.0.1:8080
|
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]
|
||||||
; No configuration values captured yet for this section.
|
; No configuration values captured yet for this section.
|
||||||
|
|
||||||
[Deployment]
|
[Deployment]
|
||||||
MODE = k3s
|
MODE = k3d
|
||||||
TARGET = prole-service-cluster
|
TARGET = prole-dev-cluster
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
; No configuration values captured yet for this section.
|
STATUS = Finished
|
||||||
|
|||||||
@ -1,175 +0,0 @@
|
|||||||
; Prole Master Configuration File
|
|
||||||
; Generated by install.py on 2026-02-05 15:29:20
|
|
||||||
; This file is used as input for Ansible deployment and k8s cluster creation.
|
|
||||||
|
|
||||||
[User]
|
|
||||||
; User-editable values; derived values below reference these by default.
|
|
||||||
PROLE_HOME = /Users/chrisfu/dev/prole
|
|
||||||
PROLE_CONF = ${PROLE_HOME}/conf
|
|
||||||
PROLE_SERVICE = ${PROLE_HOME}/etc
|
|
||||||
PROLE_DATA = /opt/prole/data/chrisfu
|
|
||||||
PROLE_LOGS = /opt/prole/logs/chrisfu
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
SERVICE_NAMESPACE = default
|
|
||||||
|
|
||||||
[Inputs]
|
|
||||||
; Screen-scoped inputs used for unattended replays (-S)
|
|
||||||
build.deploy_env = Dev
|
|
||||||
build.run_build = false
|
|
||||||
dependencies.auto_install_missing = true
|
|
||||||
dependencies.brew.install = true
|
|
||||||
dependencies.docker.install = true
|
|
||||||
dependencies.k3d.install = true
|
|
||||||
dependencies.verify_all = false
|
|
||||||
disk_selection.disk_type = local
|
|
||||||
disk_selection.local_path = ${PROLE_HOME}/prole-tools-app/dist
|
|
||||||
disk_selection.removable_mount =
|
|
||||||
env_setup.NAMESPACE = ${NAMESPACE}
|
|
||||||
env_setup.PROLE_CONF = ${PROLE_CONF}
|
|
||||||
env_setup.PROLE_DATA = ${PROLE_DATA}
|
|
||||||
env_setup.PROLE_HOME = ${PROLE_HOME}
|
|
||||||
env_setup.PROLE_LOGS = ${PROLE_LOGS}
|
|
||||||
env_setup.PROLE_SERVICE = ${PROLE_SERVICE}
|
|
||||||
init_cluster.at_rest_encryption_enabled = true
|
|
||||||
init_cluster.cluster_env = prole-service-cluster
|
|
||||||
init_cluster.k3s_server_url = https://pi.prole.org:6443
|
|
||||||
init_cluster.k3s_token = ${PROLE_SECRET:v1:fsX0MfG2qqQpKoPi:JqND7OComMcPDaRCpw6iiXL8w9If-70rvmbQA2V6lz9hK2MpPTX796dp-r3-qiIQYMc-sax9OobVoZLXdfBbzMmkR270uzl_CeDNuVBWnPmXcd00DYnBTsfs8hcGvJ6EuswizHsNVcIBxIUftPuyXr2ZQQVcC8VBuRlphw==}
|
|
||||||
init_cluster.kerberos_enabled = false
|
|
||||||
init_cluster.start_cluster = true
|
|
||||||
init_cluster.supabase_enabled = false
|
|
||||||
init_cnpg_deploy.force_rollout = false
|
|
||||||
init_cnpg_deploy.run_deploy = true
|
|
||||||
init_db_build.run_build = true
|
|
||||||
init_password.db_host_port = 5432
|
|
||||||
init_password.db_namespace = ${NAMESPACE}
|
|
||||||
init_password.db_password = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/db#password}
|
|
||||||
init_password.db_password_confirm = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/db#password}
|
|
||||||
init_password.db_username = chrisfu
|
|
||||||
init_password.generate_ssh_key = true
|
|
||||||
init_scripts.run_scripts = true
|
|
||||||
kerberos_config.enabled = false
|
|
||||||
kerberos_config.kdc = 10.0.0.3
|
|
||||||
kerberos_config.password = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/kerberos#password}
|
|
||||||
kerberos_config.realm = PROLE.ORG
|
|
||||||
kerberos_config.test_connection = false
|
|
||||||
kerberos_config.user =
|
|
||||||
network_scan.run = true
|
|
||||||
|
|
||||||
[Global]
|
|
||||||
; Variables used by name in more than one place or assumed global scope
|
|
||||||
CLUSTER_ENV = prole-service-cluster
|
|
||||||
DB_HOST_PORT = 5432
|
|
||||||
DB_PASSWORD = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/db#password}
|
|
||||||
DEPLOYMENT_MODE = k3s
|
|
||||||
DEPLOYMENT_TARGET = prole-service-cluster
|
|
||||||
DOCKER_IMPORT_DIR =
|
|
||||||
NAMESPACE = ${NAMESPACE}
|
|
||||||
PROLE_DB_USER = chrisfu
|
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
|
||||||
PROLE_K3S_SERVER = https://pi.prole.org:6443
|
|
||||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:EpUBQqBLpN0hKPcy:0MdKwHBxLbrY71XZSsbvhHSudotpC_hCmyO98HfDG08pCDzCzFiVzHzuO_LoRclwViNEipmqyl0JjwPQsVEaIBt5B1AjGeVcU1iFoDKvYf5pl2bAjTWIXKtNg8eq6LFTMGoe-I72bFcwuRJ17ykzoCaXdwC6c7Ru_UZxwQ==}
|
|
||||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
|
||||||
SERVICE_NAMESPACE = default
|
|
||||||
ARGOCD_NAMESPACE = argocd
|
|
||||||
REGISTRY_NAMESPACE = default
|
|
||||||
PROLE_ENABLE_LOCAL_REGISTRY = true
|
|
||||||
|
|
||||||
[Welcome]
|
|
||||||
; No configuration values captured yet for this section.
|
|
||||||
|
|
||||||
[Dependencies]
|
|
||||||
STATUS = All installed
|
|
||||||
|
|
||||||
[Network]
|
|
||||||
AD_DC_HOST = myrddin.prole.org
|
|
||||||
AD_DC_IP = 10.0.0.3
|
|
||||||
ANSIBLE_DOMAIN = prole.org
|
|
||||||
ANSIBLE_INFRASTRUCTURE = ${PROLE_HOME}/infrastructure
|
|
||||||
ANSIBLE_INVENTORY = ${PROLE_HOME}/infrastructure/inventory
|
|
||||||
ANSIBLE_REALM = PROLE.ORG
|
|
||||||
ANSIBLE_TOPOLOGY = {"domain":"prole.org","realm":"PROLE.ORG","internal_records":{"aventage.prole.org":"10.0.0.206","fairyland.prole.org":"10.0.0.208","loghost.prole.org":"10.0.0.3","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://pi.prole.org:6443","server_host":"pi.prole.org","token_present":true},"groups":{"iscsi":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_hosts":["pi.prole.org","myrddin.prole.org","retropie.prole.org"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org"]},"hosts":{"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":[]}
|
|
||||||
KDC_ANSIBLE_DETECTED = 10.0.0.3
|
|
||||||
|
|
||||||
[Port Forwards]
|
|
||||||
; Port-forward defaults for local k3d and external k3s access.
|
|
||||||
PORT_FORWARD_K3D_ENABLED = true
|
|
||||||
PORT_FORWARD_K3S_ENABLED = true
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_HOST = myrddin.prole.org
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_IP = 10.0.0.3
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_PORT = 5432
|
|
||||||
PORT_FORWARD_EXTERNAL_DB_SERVICE = prole-db-rw
|
|
||||||
; Mapping format: id=...;namespace=...;target=...;address=...;hostPort=...;servicePort=...;protocol=TCP;description=...
|
|
||||||
PORT_FORWARD_K3D_MAPPING_1 = 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 (https://127.0.0.1:8443)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_2 = 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 (http://127.0.0.1:9090)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_3 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=3000;protocol=TCP;description=Grafana UI (0.0.0.0:3000)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_4 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL (primary) (0.0.0.0:5432)
|
|
||||||
PORT_FORWARD_K3D_MAPPING_5 = id=openbao;namespace=${NAMESPACE};target=svc/openbao;address=127.0.0.1;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenBao UI (http://127.0.0.1:8200)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_1 = id=grafana;namespace=monitoring;target=svc/kps-grafana;address=0.0.0.0;hostPort=3000;servicePort=3000;protocol=TCP;description=Grafana UI (0.0.0.0:3000)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_2 = id=opentofu;namespace=${NAMESPACE};target=svc/opentofu;address=0.0.0.0;hostPort=8080;servicePort=8080;protocol=TCP;description=OpenTofu UI/API (0.0.0.0:8080)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_3 = id=openbao;namespace=${NAMESPACE};target=svc/openbao;address=0.0.0.0;hostPort=8200;servicePort=8200;protocol=TCP;description=OpenVault/OpenBao UI/API (0.0.0.0:8200)
|
|
||||||
PORT_FORWARD_K3S_MAPPING_4 = id=postgres;namespace=${NAMESPACE};target=svc/prole-db-rw;address=0.0.0.0;hostPort=5432;servicePort=5432;protocol=TCP;description=PostgreSQL external (0.0.0.0:5432)
|
|
||||||
|
|
||||||
[System Environment]
|
|
||||||
PROLE_CONF = ${PROLE_CONF}
|
|
||||||
PROLE_DATA = ${PROLE_DATA}
|
|
||||||
PROLE_HOME = ${PROLE_HOME}
|
|
||||||
PROLE_LOGS = ${PROLE_LOGS}
|
|
||||||
PROLE_SERVICE = ${PROLE_SERVICE}
|
|
||||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
|
||||||
|
|
||||||
[Monitoring]
|
|
||||||
GRAFANA_ADMIN_PASSWORD = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/monitoring#grafana_admin_password}
|
|
||||||
|
|
||||||
[Kerberos Authentication]
|
|
||||||
PASSWORD = ${OPENBAO:kv/prole/prole-chrisfu-abracadabra/kerberos#password}
|
|
||||||
|
|
||||||
[Optional Features]
|
|
||||||
AT_REST_ENCRYPTION_ENABLED = true
|
|
||||||
KERBEROS_ENABLED = false
|
|
||||||
SUPABASE_ENABLED = false
|
|
||||||
|
|
||||||
[Database Creation]
|
|
||||||
DB_NAME = prole-chrisfu-abracadabra
|
|
||||||
DB_PASSWORD_SET = true
|
|
||||||
DB_USER = chrisfu
|
|
||||||
NAMESPACE = prole-chrisfu-abracadabra
|
|
||||||
|
|
||||||
[Initialize Cluster]
|
|
||||||
ENVIRONMENT = prole-service-cluster
|
|
||||||
K3S_SERVER_URL = https://pi.prole.org:6443
|
|
||||||
K3S_TOKEN = ${PROLE_SECRET:v1:87PgULFcPofp_fez:sWTKxPhs0ykJEtWLlAj_cWFGK5pe1ttmeQhOfSnPfNLDe8L6qL7U5mv6OQfbT9n1z46zoJ9QKaYCCvwjIgdTjOkdjE9YGrHs1IhkSVGbnFZ531LCZ0eoj_ouhutPYcFpC5b7L990zbcwMYGyZ8Cr-ohIMRk7mN-Xp_PB4w==}
|
|
||||||
|
|
||||||
[Dev Cluster (k3d)]
|
|
||||||
CLUSTER_ENV = k3d-prole-dev-cluster
|
|
||||||
DISPLAY_NAME = prole-dev-cluster
|
|
||||||
KUBECTL_CONTEXT = prole-service-cluster
|
|
||||||
MODE = k3d
|
|
||||||
|
|
||||||
[Service Cluster (k3s)]
|
|
||||||
CLUSTER_ENV = prole-service-cluster
|
|
||||||
DISPLAY_NAME = prole-service-cluster
|
|
||||||
K3S_SERVER_URL = https://pi.prole.org:6443
|
|
||||||
K3S_TOKEN = ${PROLE_SECRET:v1:gLFC-kFjv_0LzEO7:fTdBIU1aFqYM1f2eILrkzYCay1A0ITp_1cfHMYlX47The7ritLEV8R3_dxKgx3L34kCw0dptoqfQL65VoLTbBLN55pus7CQs-3bb-XCJLJ0VUDPfavI9yEJ7u3VJw4SYvSGIrRa6U4YTAnFCcICMly7g6XfFnOsGoz4W2w==}
|
|
||||||
MODE = k3s
|
|
||||||
PIPELINE_URL = http://127.0.0.1:8080
|
|
||||||
|
|
||||||
[Prod Cluster (k8s)]
|
|
||||||
ARTIFACTS_DIR = ${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]
|
|
||||||
LOCAL_REGISTRY = localhost:5000
|
|
||||||
LOCAL_REGISTRY_INTERNAL =
|
|
||||||
|
|
||||||
[Initialization Scripts]
|
|
||||||
; No configuration values captured yet for this section.
|
|
||||||
|
|
||||||
[Deployment]
|
|
||||||
MODE = k3s
|
|
||||||
TARGET = prole-service-cluster
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
STATUS = Failed
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: prole-db-manager
|
|
||||||
labels:
|
|
||||||
app: prole-db-manager
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: prole-db-manager
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: prole-db-manager
|
|
||||||
spec:
|
|
||||||
serviceAccountName: prole-db-manager
|
|
||||||
containers:
|
|
||||||
- name: db-manager
|
|
||||||
image: prole-db-manager:latest
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
ports:
|
|
||||||
- containerPort: 80
|
|
||||||
name: http
|
|
||||||
env:
|
|
||||||
- name: PORT
|
|
||||||
value: "80"
|
|
||||||
- name: NAMESPACE
|
|
||||||
valueFrom:
|
|
||||||
fieldRef:
|
|
||||||
fieldPath: metadata.namespace
|
|
||||||
- name: BARMAN_PLUGIN_NAME
|
|
||||||
value: "barman-cloud.cloudnative-pg.io"
|
|
||||||
- name: BARMAN_OBJECT_NAME
|
|
||||||
value: "prole-db-barman-objectstore"
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: http
|
|
||||||
initialDelaySeconds: 3
|
|
||||||
periodSeconds: 10
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: http
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 30
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 50m
|
|
||||||
memory: 64Mi
|
|
||||||
limits:
|
|
||||||
cpu: 200m
|
|
||||||
memory: 128Mi
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: prole-db-manager
|
|
||||||
labels:
|
|
||||||
app: prole-db-manager
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: Role
|
|
||||||
metadata:
|
|
||||||
name: prole-db-manager
|
|
||||||
labels:
|
|
||||||
app: prole-db-manager
|
|
||||||
rules:
|
|
||||||
- apiGroups: ["postgresql.cnpg.io"]
|
|
||||||
resources: ["backups"]
|
|
||||||
verbs: ["create", "get", "list", "watch"]
|
|
||||||
- apiGroups: ["postgresql.cnpg.io"]
|
|
||||||
resources: ["clusters"]
|
|
||||||
verbs: ["get", "list"]
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: RoleBinding
|
|
||||||
metadata:
|
|
||||||
name: prole-db-manager
|
|
||||||
labels:
|
|
||||||
app: prole-db-manager
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: prole-db-manager
|
|
||||||
roleRef:
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: Role
|
|
||||||
name: prole-db-manager
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: prole-db-manager
|
|
||||||
labels:
|
|
||||||
app: prole-db-manager
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: prole-db-manager
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 80
|
|
||||||
targetPort: http
|
|
||||||
type: ClusterIP
|
|
||||||
2
env.sh
2
env.sh
@ -8,7 +8,7 @@ export PROLE_CONF="/Users/chrisfu/dev/prole/conf"
|
|||||||
export PROLE_DATA="/Users/chrisfu/.prole/data"
|
export PROLE_DATA="/Users/chrisfu/.prole/data"
|
||||||
export PROLE_LOGS="/opt/prole/logs/chrisfu"
|
export PROLE_LOGS="/opt/prole/logs/chrisfu"
|
||||||
export PROLE_SERVICE="/Users/chrisfu/dev/prole/etc"
|
export PROLE_SERVICE="/Users/chrisfu/dev/prole/etc"
|
||||||
export NAMESPACE="prole-db-a0001"
|
export NAMESPACE="prole-db001"
|
||||||
|
|
||||||
# Ensure PATH works for GUI-launched shells (Docker, etc.)
|
# Ensure PATH works for GUI-launched shells (Docker, etc.)
|
||||||
_prole_add_path() { case ":${PATH}:" in *":$1:"*) ;; *) PATH="$1:${PATH:-}" ;; esac; }
|
_prole_add_path() { case ":${PATH}:" in *":$1:"*) ;; *) PATH="$1:${PATH:-}" ;; esac; }
|
||||||
|
|||||||
2
etc/home.sh
Executable file
2
etc/home.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo hometest
|
||||||
@ -47,7 +47,9 @@ if [[ -n "${COMMON_CORE_PARSE_ERROR:-}" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
ACTION="$COMMON_CORE_ACTION"
|
ACTION="$COMMON_CORE_ACTION"
|
||||||
CERTMGR_NAMESPACE="$(common_core_resolve_namespace "default")"
|
# cert-manager always runs in its own dedicated namespace; never follows the
|
||||||
|
# common-services namespace override.
|
||||||
|
CERTMGR_NAMESPACE="cert-manager"
|
||||||
common_core_apply_namespace "$CERTMGR_NAMESPACE"
|
common_core_apply_namespace "$CERTMGR_NAMESPACE"
|
||||||
|
|
||||||
CERTMGR_VERSION="${CERTMGR_VERSION:-v1.14.6}"
|
CERTMGR_VERSION="${CERTMGR_VERSION:-v1.14.6}"
|
||||||
@ -84,38 +86,16 @@ ensure_namespace() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_patch_manifest_namespace() {
|
_apply_manifest() {
|
||||||
local ns="$1"
|
curl -fsSL "$CERTMGR_RELEASE_URL" | kubectl apply -f -
|
||||||
if [[ "$ns" == "cert-manager" ]]; then
|
|
||||||
cat
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
awk -v ns="$ns" '
|
|
||||||
$1=="kind:" && $2=="Namespace" {in_ns=1}
|
|
||||||
in_ns && $1=="name:" && $2=="cert-manager" {
|
|
||||||
match($0, /^[[:space:]]*/);
|
|
||||||
indent = substr($0, RSTART, RLENGTH);
|
|
||||||
print indent "name: " ns;
|
|
||||||
in_ns=0;
|
|
||||||
next
|
|
||||||
}
|
|
||||||
$1=="namespace:" && $2=="cert-manager" {
|
|
||||||
match($0, /^[[:space:]]*/);
|
|
||||||
indent = substr($0, RSTART, RLENGTH);
|
|
||||||
print indent "namespace: " ns;
|
|
||||||
next
|
|
||||||
}
|
|
||||||
{print}
|
|
||||||
'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_apply_manifest() {
|
_is_already_deployed() {
|
||||||
curl -fsSL "$CERTMGR_RELEASE_URL" | _patch_manifest_namespace "$CERTMGR_NAMESPACE" | kubectl apply -f -
|
kubectl -n "$CERTMGR_NAMESPACE" get deployment cert-manager cert-manager-cainjector cert-manager-webhook >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
_delete_manifest() {
|
_delete_manifest() {
|
||||||
curl -fsSL "$CERTMGR_RELEASE_URL" | _patch_manifest_namespace "$CERTMGR_NAMESPACE" | kubectl delete -f - --ignore-not-found=true
|
curl -fsSL "$CERTMGR_RELEASE_URL" | kubectl delete -f - --ignore-not-found=true
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_rollout() {
|
wait_rollout() {
|
||||||
@ -143,8 +123,12 @@ restart() {
|
|||||||
action_update() {
|
action_update() {
|
||||||
ensure_tools
|
ensure_tools
|
||||||
ensure_namespace
|
ensure_namespace
|
||||||
_apply_manifest
|
if _is_already_deployed; then
|
||||||
wait_rollout
|
echo "cert-manager already deployed in namespace '$CERTMGR_NAMESPACE'; skipping manifest apply."
|
||||||
|
else
|
||||||
|
_apply_manifest
|
||||||
|
wait_rollout
|
||||||
|
fi
|
||||||
prole_register_port_forward "certmgr" "$CERTMGR_NAMESPACE" "svc/cert-manager-webhook" "9443" "9443" "${CERTMGR_PF_ADDR}" "TCP" "CertManager Webhook"
|
prole_register_port_forward "certmgr" "$CERTMGR_NAMESPACE" "svc/cert-manager-webhook" "9443" "9443" "${CERTMGR_PF_ADDR}" "TCP" "CertManager Webhook"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -577,6 +577,137 @@ apply_prole_manifest_file() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Push image to local registry (via LOCAL_REGISTRY host address) with k3d import fallback.
|
||||||
|
_push_to_k3d_registry() {
|
||||||
|
local image="$1"
|
||||||
|
local cluster_name="$2"
|
||||||
|
local push_host="${LOCAL_REGISTRY:-localhost:5000}"
|
||||||
|
local plain_image="${image##*/}" # strip registry prefix, e.g. prole-db:18-088
|
||||||
|
|
||||||
|
if [[ -n "$push_host" ]]; then
|
||||||
|
local push_ref="${push_host}/${plain_image}"
|
||||||
|
docker tag "$image" "$push_ref" 2>/dev/null || true
|
||||||
|
if docker push "$push_ref" 2>/dev/null; then
|
||||||
|
echo " ✓ Image pushed to registry at '${push_host}'."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo " WARN: push to '${push_host}' failed; falling back to k3d image import ..." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if k3d image import "$image" -c "$cluster_name" 2>/dev/null; then
|
||||||
|
echo " ✓ Image '$image' imported directly into k3d cluster '$cluster_name'."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "ERROR: Failed to push or import image '$image'." >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pre-flight: ensure the prole-db image is available in the k3d cluster before
|
||||||
|
# the CNPG operator ever tries to pull it, avoiding ErrImagePull backoff loops.
|
||||||
|
# Steps: containerd cache → Docker daemon (registry tag) → Docker daemon (plain tag)
|
||||||
|
# → tar import → docker build + push/import.
|
||||||
|
_ensure_prole_db_image() {
|
||||||
|
[[ "${PROLE_MODE:-}" != "k3d" ]] && return 0
|
||||||
|
|
||||||
|
local image_override="${CNPG_IMAGE:-${PROLE_DB_IMAGE:-}}"
|
||||||
|
local image=""
|
||||||
|
if [[ -n "$image_override" ]]; then
|
||||||
|
image="$image_override"
|
||||||
|
else
|
||||||
|
if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then
|
||||||
|
image=$(get_latest_image)
|
||||||
|
else
|
||||||
|
image="prole-db:$VERSION"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
image=$(resolve_cnpg_image "$image")
|
||||||
|
|
||||||
|
# Auto-detect active k3d cluster name
|
||||||
|
local cluster_name="${K3D_CLUSTER_NAME:-}"
|
||||||
|
if [[ -z "$cluster_name" ]]; then
|
||||||
|
cluster_name=$(k3d cluster list --no-headers 2>/dev/null | awk '{print $1}' | head -1)
|
||||||
|
fi
|
||||||
|
cluster_name="${cluster_name:-knoe-dev-cluster}"
|
||||||
|
|
||||||
|
local prole_db_dir="${PROLE_HOME:-$SCRIPT_DIR/..}/prole-db"
|
||||||
|
local plain_image="${image##*/}" # e.g. prole-db:18-088
|
||||||
|
|
||||||
|
echo "Pre-flight: verifying image '$image' is available in k3d cluster '$cluster_name' ..."
|
||||||
|
|
||||||
|
# Step 1: check if already present in k3d containerd with matching digest
|
||||||
|
local containerd_digest local_digest containerd_sha
|
||||||
|
containerd_digest=$(docker exec "k3d-${cluster_name}-server-0" \
|
||||||
|
ctr images ls -q 2>/dev/null | grep -F "$image" | head -1 || true)
|
||||||
|
if [[ -n "$containerd_digest" ]]; then
|
||||||
|
local_digest=$(docker inspect --format='{{index .RepoDigests 0}}' "$image" 2>/dev/null \
|
||||||
|
| awk -F@ '{print $2}' || true)
|
||||||
|
containerd_sha=$(docker exec "k3d-${cluster_name}-server-0" \
|
||||||
|
ctr images ls 2>/dev/null | grep -F "$image" | awk '{print $3}' | head -1 || true)
|
||||||
|
if [[ -z "$local_digest" || "$containerd_sha" == "$local_digest" ]]; then
|
||||||
|
echo " ✓ Image '$image' already in k3d containerd (digest match); no import needed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo " Image '$image' in containerd but digest mismatch (local: ${local_digest:-unknown}, containerd: ${containerd_sha:-unknown}); re-importing ..."
|
||||||
|
docker exec "k3d-${cluster_name}-server-0" ctr images rm "$image" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 2a: registry-tagged image in local Docker daemon → push + import
|
||||||
|
if docker image inspect "$image" >/dev/null 2>&1; then
|
||||||
|
echo " Image '$image' found in Docker daemon; pushing to registry ..."
|
||||||
|
_push_to_k3d_registry "$image" "$cluster_name"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 2b: plain-tagged image in local Docker daemon → tag + push + import
|
||||||
|
if docker image inspect "$plain_image" >/dev/null 2>&1; then
|
||||||
|
echo " Plain image '$plain_image' found in Docker daemon; tagging as '$image' and pushing ..."
|
||||||
|
docker tag "$plain_image" "$image"
|
||||||
|
_push_to_k3d_registry "$image" "$cluster_name"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 3: look for a matching tar in the docker-import directory
|
||||||
|
local prole_data="${PROLE_DATA:-$HOME/.prole/data}"
|
||||||
|
local docker_import_dir="${DOCKER_IMPORT_DIR:-${prole_data}/docker-import}"
|
||||||
|
local name_part="${plain_image%%:*}" # e.g. prole-db
|
||||||
|
local tag_part="${plain_image##*:}" # e.g. 18-088
|
||||||
|
local found_tar=""
|
||||||
|
if [[ -d "$docker_import_dir" ]]; then
|
||||||
|
for _t in "$docker_import_dir"/*.tar; do
|
||||||
|
[[ -f "$_t" ]] || continue
|
||||||
|
local _bn
|
||||||
|
_bn=$(basename "$_t" .tar)
|
||||||
|
if [[ "$_bn" == *"$name_part"* && "$_bn" == *"$tag_part"* ]]; then
|
||||||
|
found_tar="$_t"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [[ -n "$found_tar" ]]; then
|
||||||
|
echo " Loading tar '$(basename "$found_tar")' into Docker daemon ..."
|
||||||
|
docker load -i "$found_tar"
|
||||||
|
docker tag "$plain_image" "$image" 2>/dev/null || true
|
||||||
|
_push_to_k3d_registry "$image" "$cluster_name"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 4: image not found anywhere — build from source then push + import
|
||||||
|
if [[ ! -f "$prole_db_dir/Dockerfile" ]]; then
|
||||||
|
echo "ERROR: Dockerfile not found in '$prole_db_dir'; cannot build prole-db image." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo " Image '$image' not found in k3d, Docker daemon, or docker-import dir."
|
||||||
|
echo " Building prole-db image from '$prole_db_dir' ..."
|
||||||
|
if ! docker build -t "$plain_image" "$prole_db_dir"; then
|
||||||
|
echo "ERROR: docker build failed for image '$plain_image'." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
docker tag "$plain_image" "$image"
|
||||||
|
echo " Build complete. Pushing '$image' to registry ..."
|
||||||
|
_push_to_k3d_registry "$image" "$cluster_name"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
ensure_prole_stack_resources() {
|
ensure_prole_stack_resources() {
|
||||||
echo "Applying CloudNative-PG cluster and related resources ..."
|
echo "Applying CloudNative-PG cluster and related resources ..."
|
||||||
local image_override="${CNPG_IMAGE:-${PROLE_DB_IMAGE:-}}"
|
local image_override="${CNPG_IMAGE:-${PROLE_DB_IMAGE:-}}"
|
||||||
@ -726,6 +857,7 @@ wait_for_cnpg_pods() {
|
|||||||
echo " Timeout: ${timeout}s"
|
echo " Timeout: ${timeout}s"
|
||||||
local last_feedback=0
|
local last_feedback=0
|
||||||
local feedback_interval=30 # print detailed status every 30s
|
local feedback_interval=30 # print detailed status every 30s
|
||||||
|
local _image_pull_remediated="" # set after first k3d import attempt to avoid loops
|
||||||
while true; do
|
while true; do
|
||||||
local now elapsed
|
local now elapsed
|
||||||
now=$(date +%s)
|
now=$(date +%s)
|
||||||
@ -735,13 +867,47 @@ wait_for_cnpg_pods() {
|
|||||||
pods=$(kubectl -n "$NAMESPACE" get pods -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" --no-headers 2>/dev/null || true)
|
pods=$(kubectl -n "$NAMESPACE" get pods -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" --no-headers 2>/dev/null || true)
|
||||||
|
|
||||||
if [[ -n "$pods" ]]; then
|
if [[ -n "$pods" ]]; then
|
||||||
# Check for Error or CrashLoopBackOff
|
# Check for Error or CrashLoopBackOff (terminal failures)
|
||||||
if echo "$pods" | grep -E "Error|CrashLoopBackOff" >/dev/null; then
|
if echo "$pods" | grep -E "Error|CrashLoopBackOff" >/dev/null; then
|
||||||
echo "ERROR: Some CNPG pods are in Error or CrashLoopBackOff state:" >&2
|
echo "ERROR: Some CNPG pods are in Error or CrashLoopBackOff state:" >&2
|
||||||
echo "$pods" | grep -E "Error|CrashLoopBackOff" >&2
|
echo "$pods" | grep -E "Error|CrashLoopBackOff" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check for image pull failures — attempt remediation in k3d mode
|
||||||
|
if echo "$pods" | grep -E "ErrImagePull|ImagePullBackOff" >/dev/null; then
|
||||||
|
if [[ "${PROLE_MODE:-}" == "k3d" ]]; then
|
||||||
|
if [[ -z "$_image_pull_remediated" ]]; then
|
||||||
|
_image_pull_remediated=1
|
||||||
|
local fail_pod fail_image
|
||||||
|
fail_pod=$(echo "$pods" | grep -E "ErrImagePull|ImagePullBackOff" | awk '{print $1}' | head -1)
|
||||||
|
# Try containers then initContainers
|
||||||
|
fail_image=$(kubectl -n "$NAMESPACE" get pod "$fail_pod" \
|
||||||
|
-o jsonpath='{.spec.containers[0].image}' 2>/dev/null || true)
|
||||||
|
if [[ -z "$fail_image" ]]; then
|
||||||
|
fail_image=$(kubectl -n "$NAMESPACE" get pod "$fail_pod" \
|
||||||
|
-o jsonpath='{.spec.initContainers[0].image}' 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
echo "WARN: Pod '$fail_pod' cannot pull image '${fail_image:-unknown}' (ErrImagePull/ImagePullBackOff in k3d mode)." >&2
|
||||||
|
import_dir="${DOCKER_IMPORT_DIR:-${PROLE_DATA:+${PROLE_DATA}/docker-import}}"
|
||||||
|
cluster_name="${K3D_CLUSTER_NAME:-}"
|
||||||
|
if [[ -z "$cluster_name" ]]; then
|
||||||
|
cluster_name=$(k3d cluster list --no-headers 2>/dev/null | awk '{print $1}' | head -1)
|
||||||
|
fi
|
||||||
|
cluster_name="${cluster_name:-knoe-dev-cluster}"
|
||||||
|
# Re-run full image ensure logic to push/import the failing image
|
||||||
|
echo " Attempting image remediation via _ensure_prole_db_image ..." >&2
|
||||||
|
_ensure_prole_db_image >&2 || true
|
||||||
|
echo " Remediation complete; resuming wait ..." >&2
|
||||||
|
fi
|
||||||
|
# Continue the wait loop — do not return 1
|
||||||
|
else
|
||||||
|
echo "ERROR: Image pull failure in non-k3d mode — cannot auto-recover:" >&2
|
||||||
|
echo "$pods" | grep -E "ErrImagePull|ImagePullBackOff" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Count Running pods by name (exclude initdb)
|
# Count Running pods by name (exclude initdb)
|
||||||
local running_pods
|
local running_pods
|
||||||
running_pods=$(kubectl -n "$NAMESPACE" get pods --no-headers 2>/dev/null \
|
running_pods=$(kubectl -n "$NAMESPACE" get pods --no-headers 2>/dev/null \
|
||||||
@ -1432,6 +1598,11 @@ initialize() {
|
|||||||
fi
|
fi
|
||||||
echo "Pre-flight check passed: all required secrets present."
|
echo "Pre-flight check passed: all required secrets present."
|
||||||
|
|
||||||
|
if ! _ensure_prole_db_image; then
|
||||||
|
echo "ERROR: Pre-flight image check failed; aborting cluster initialization." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
ensure_prole_stack_resources
|
ensure_prole_stack_resources
|
||||||
|
|
||||||
if ! wait_for_cnpg_pods 300; then
|
if ! wait_for_cnpg_pods 300; then
|
||||||
@ -1486,6 +1657,7 @@ deploy_cluster() {
|
|||||||
generate_tls_if_missing
|
generate_tls_if_missing
|
||||||
|
|
||||||
echo "Deploying $image to cluster $CNPG_CLUSTER_NAME in namespace $NAMESPACE..."
|
echo "Deploying $image to cluster $CNPG_CLUSTER_NAME in namespace $NAMESPACE..."
|
||||||
|
_ensure_prole_db_image || true
|
||||||
apply_cnpg_cluster_manifest "$CNPG_MANIFEST"
|
apply_cnpg_cluster_manifest "$CNPG_MANIFEST"
|
||||||
|
|
||||||
local current_image
|
local current_image
|
||||||
@ -1503,6 +1675,20 @@ case "$ACTION" in
|
|||||||
recreate)
|
recreate)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
"$0" delete "$CNPG_CLUSTER_NAME"
|
"$0" delete "$CNPG_CLUSTER_NAME"
|
||||||
|
# Clean up PVCs left behind by the CNPG operator (not in static manifests)
|
||||||
|
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
|
||||||
|
echo "Waiting for pods to terminate in namespace '$NAMESPACE' ..."
|
||||||
|
_wait_term=0
|
||||||
|
while kubectl -n "$NAMESPACE" get pods --no-headers 2>/dev/null | grep -qv '^No resources'; do
|
||||||
|
sleep 3
|
||||||
|
_wait_term=$(( _wait_term + 3 ))
|
||||||
|
if (( _wait_term >= 60 )); then
|
||||||
|
echo "WARN: Pods still present after 60s; proceeding anyway." >&2
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
"$0" create "$CNPG_CLUSTER_NAME"
|
"$0" create "$CNPG_CLUSTER_NAME"
|
||||||
;;
|
;;
|
||||||
create)
|
create)
|
||||||
@ -1513,7 +1699,20 @@ case "$ACTION" in
|
|||||||
delete)
|
delete)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
echo "Deleting all resources for '$CNPG_CLUSTER_NAME' ..."
|
echo "Deleting all resources for '$CNPG_CLUSTER_NAME' ..."
|
||||||
kubectl delete -n "$NAMESPACE" -k "$SCRIPT_DIR/../k8s/prole" --ignore-not-found
|
# Delete each manifest individually, mirroring the apply pattern:
|
||||||
|
# ingress.yaml contains multi-namespace resources and must be deleted without -n.
|
||||||
|
for _del_f in "$SCRIPT_DIR/../k8s/prole"/*.yaml; do
|
||||||
|
_del_base=$(basename "$_del_f")
|
||||||
|
case "$_del_base" in
|
||||||
|
kustomization.yaml|ingress.yaml) continue ;;
|
||||||
|
*) kubectl delete -n "$NAMESPACE" -f "$_del_f" --ignore-not-found 2>&1 \
|
||||||
|
| grep -v "^Error from server (NotFound)" || true ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [[ -f "$SCRIPT_DIR/../k8s/prole/ingress.yaml" ]]; then
|
||||||
|
kubectl delete -f "$SCRIPT_DIR/../k8s/prole/ingress.yaml" --ignore-not-found 2>&1 \
|
||||||
|
| grep -v "^Error from server (NotFound)" || true
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
start)
|
start)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
|
|||||||
@ -147,6 +147,80 @@ _resolve_prole_data() {
|
|||||||
echo "${d:-}"
|
echo "${d:-}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Standard console execution element.
|
||||||
|
# Runs a command silently (output captured), displays a live elapsed-time footer
|
||||||
|
# on stderr when attached to a TTY, prints elapsed time on completion, and tails
|
||||||
|
# captured output for diagnostics on failure.
|
||||||
|
_exec_with_progress_footer() {
|
||||||
|
local label="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
local start_time
|
||||||
|
start_time=$(date +%s)
|
||||||
|
|
||||||
|
local tmp_out
|
||||||
|
tmp_out=$(mktemp 2>/dev/null || echo "/tmp/_prole_exec_$$.out")
|
||||||
|
|
||||||
|
# Spawn live footer only when stderr is a real TTY
|
||||||
|
local spinner_pid=""
|
||||||
|
if [[ -t 2 ]]; then
|
||||||
|
(
|
||||||
|
while true; do
|
||||||
|
now=$(date +%s)
|
||||||
|
elapsed=$(( now - start_time ))
|
||||||
|
h=$(( elapsed / 3600 ))
|
||||||
|
m=$(( (elapsed % 3600) / 60 ))
|
||||||
|
s=$(( elapsed % 60 ))
|
||||||
|
printf "\r\033[2K [%02d:%02d:%02d elapsed] %s" "$h" "$m" "$s" "$label" >&2
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
spinner_pid=$!
|
||||||
|
disown "$spinner_pid" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$@" >"$tmp_out" 2>&1
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
if [[ -n "$spinner_pid" ]]; then
|
||||||
|
kill "$spinner_pid" 2>/dev/null || true
|
||||||
|
wait "$spinner_pid" 2>/dev/null || true
|
||||||
|
printf "\r\033[2K" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
local end_time elapsed_total h m s
|
||||||
|
end_time=$(date +%s)
|
||||||
|
elapsed_total=$(( end_time - start_time ))
|
||||||
|
h=$(( elapsed_total / 3600 ))
|
||||||
|
m=$(( (elapsed_total % 3600) / 60 ))
|
||||||
|
s=$(( elapsed_total % 60 ))
|
||||||
|
printf " [%02dh:%02dm:%02ds elapsed] %s\n" "$h" "$m" "$s" "$label"
|
||||||
|
|
||||||
|
if [[ $rc -ne 0 && -s "$tmp_out" ]]; then
|
||||||
|
echo " [Last output]:" >&2
|
||||||
|
tail -n 10 "$tmp_out" >&2
|
||||||
|
fi
|
||||||
|
rm -f "$tmp_out"
|
||||||
|
return $rc
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract the primary image ref from a Docker tarball's manifest.json.
|
||||||
|
_image_name_from_tar() {
|
||||||
|
local tar_file="$1"
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
python3 - "$tar_file" <<'PYEOF' 2>/dev/null
|
||||||
|
import json, sys, tarfile
|
||||||
|
try:
|
||||||
|
with tarfile.open(sys.argv[1]) as t:
|
||||||
|
tags = json.load(t.extractfile("manifest.json"))[0].get("RepoTags") or []
|
||||||
|
if tags:
|
||||||
|
print(tags[0])
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
PYEOF
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_preload_docker_images() {
|
_preload_docker_images() {
|
||||||
local prole_data
|
local prole_data
|
||||||
prole_data=$(_resolve_prole_data)
|
prole_data=$(_resolve_prole_data)
|
||||||
@ -173,26 +247,125 @@ _preload_docker_images() {
|
|||||||
echo "WARN: k3d not available; skipping docker-import pre-load." >&2
|
echo "WARN: k3d not available; skipping docker-import pre-load." >&2
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
local cluster_name="${K3D_CLUSTER_NAME:-prole-dev-cluster}"
|
local cluster_name="${K3D_CLUSTER_NAME:-knoe-dev-cluster}"
|
||||||
echo " Importing all images into k3d cluster '$cluster_name' (bulk) ..."
|
|
||||||
local -a tar_array=()
|
# ── Step 1: build indexed arrays of tar paths + resolved image names ──
|
||||||
while IFS= read -r line; do
|
local -a tar_paths=()
|
||||||
[[ -n "$line" ]] && tar_array+=("$line")
|
local -a tar_images=()
|
||||||
|
local _tf
|
||||||
|
while IFS= read -r _tf; do
|
||||||
|
[[ -z "$_tf" ]] && continue
|
||||||
|
tar_paths+=("$_tf")
|
||||||
|
tar_images+=("$(_image_name_from_tar "$_tf")")
|
||||||
done <<< "$tar_files"
|
done <<< "$tar_files"
|
||||||
if [[ ${#tar_array[@]} -gt 0 ]]; then
|
local total_tars=${#tar_paths[@]}
|
||||||
if k3d image import "${tar_array[@]}" -c "$cluster_name" >/dev/null 2>&1; then
|
|
||||||
echo " [OK] Bulk import of ${#tar_array[@]} images successful."
|
if [[ $total_tars -eq 0 ]]; then
|
||||||
else
|
echo " [SKIP] No tar files to process."
|
||||||
echo " [WARN] Bulk import failed; falling back to sequential." >&2
|
return 0
|
||||||
for tar_file in "${tar_array[@]}"; do
|
fi
|
||||||
echo " Importing $(basename "$tar_file") into k3d cluster '$cluster_name' ..."
|
|
||||||
if k3d image import "$tar_file" -c "$cluster_name" >/dev/null 2>&1; then
|
# ── Step 2: query images already in the k3d cluster ───────────────────
|
||||||
echo " [OK] $(basename "$tar_file")"
|
echo " Checking k3d cluster '$cluster_name' for existing images ..."
|
||||||
else
|
local existing_images=""
|
||||||
echo " [WARN] Failed to import $(basename "$tar_file"); image will be pulled at deploy time." >&2
|
local node_name="k3d-${cluster_name}-server-0"
|
||||||
fi
|
if docker inspect "$node_name" >/dev/null 2>&1; then
|
||||||
done
|
existing_images=$(docker exec "$node_name" \
|
||||||
|
ctr --address /run/k3s/containerd/containerd.sock \
|
||||||
|
--namespace k8s.io images ls -q 2>/dev/null || true)
|
||||||
|
else
|
||||||
|
echo " [WARN] k3d node '$node_name' not reachable; will import all images."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Step 3: delta — split into already-present vs needs-import ─────────
|
||||||
|
local -a to_import_tars=()
|
||||||
|
local -a to_import_images=()
|
||||||
|
local -a already_present=()
|
||||||
|
local _i
|
||||||
|
for (( _i=0; _i<total_tars; _i++ )); do
|
||||||
|
local _tar="${tar_paths[$_i]}"
|
||||||
|
local _img="${tar_images[$_i]}"
|
||||||
|
local _found=0
|
||||||
|
if [[ -n "$_img" && -n "$existing_images" ]]; then
|
||||||
|
# Strip digest suffix before matching
|
||||||
|
local _img_ref="${_img%%@*}"
|
||||||
|
if echo "$existing_images" | grep -qF "$_img_ref"; then
|
||||||
|
_found=1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
if [[ $_found -eq 1 ]]; then
|
||||||
|
already_present+=("$(basename "$_tar")${_img:+ → $_img}")
|
||||||
|
else
|
||||||
|
to_import_tars+=("$_tar")
|
||||||
|
to_import_images+=("${_img:-}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Step 4: report delta to user ───────────────────────────────────────
|
||||||
|
if [[ ${#already_present[@]} -gt 0 ]]; then
|
||||||
|
echo " Already in cluster — skipping ${#already_present[@]} image(s):"
|
||||||
|
local _entry
|
||||||
|
for _entry in "${already_present[@]}"; do
|
||||||
|
echo " ✓ $_entry"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
local import_count=${#to_import_tars[@]}
|
||||||
|
if [[ $import_count -eq 0 ]]; then
|
||||||
|
echo " [OK] All images already present in k3d cluster '$cluster_name'; nothing to import."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " Bulk uploading $import_count image(s) into k3d cluster '$cluster_name':"
|
||||||
|
for (( _i=0; _i<import_count; _i++ )); do
|
||||||
|
local _lbl="${to_import_images[$_i]:-$(basename "${to_import_tars[$_i]}")}"
|
||||||
|
echo " → $(basename "${to_import_tars[$_i]}")${_lbl:+ ($_lbl)}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Step 5: bulk import with live progress footer ──────────────────────
|
||||||
|
local overall_start elapsed_bulk
|
||||||
|
overall_start=$(date +%s)
|
||||||
|
|
||||||
|
if _exec_with_progress_footer \
|
||||||
|
"k3d image import — bulk ($import_count images) → $cluster_name" \
|
||||||
|
k3d image import "${to_import_tars[@]}" -c "$cluster_name"; then
|
||||||
|
elapsed_bulk=$(( $(date +%s) - overall_start ))
|
||||||
|
local bm=$(( elapsed_bulk / 60 )) bs=$(( elapsed_bulk % 60 ))
|
||||||
|
echo " [OK] Bulk import of $import_count image(s) complete (${bm}m${bs}s)."
|
||||||
|
else
|
||||||
|
echo " [WARN] Bulk import failed; falling back to sequential import." >&2
|
||||||
|
local _idx
|
||||||
|
for (( _i=0; _i<import_count; _i++ )); do
|
||||||
|
_idx=$(( _i + 1 ))
|
||||||
|
local _tar="${to_import_tars[$_i]}"
|
||||||
|
local _img="${to_import_images[$_i]:-$(basename "$_tar")}"
|
||||||
|
|
||||||
|
# ETA based on average time per completed image
|
||||||
|
local _now _elapsed_so_far _eta_str=""
|
||||||
|
_now=$(date +%s)
|
||||||
|
_elapsed_so_far=$(( _now - overall_start ))
|
||||||
|
if [[ $_i -gt 0 ]]; then
|
||||||
|
local _avg=$(( _elapsed_so_far / _i ))
|
||||||
|
local _remaining=$(( _avg * (import_count - _i) ))
|
||||||
|
local _rm=$(( _remaining / 60 )) _rs=$(( _remaining % 60 ))
|
||||||
|
_eta_str=" | ETA ~${_rm}m${_rs}s"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " [$_idx/$import_count] $(basename "$_tar")${_img:+ ($_img)}${_eta_str}"
|
||||||
|
local _item_start
|
||||||
|
_item_start=$(date +%s)
|
||||||
|
if _exec_with_progress_footer \
|
||||||
|
"k3d image import [$_idx/$import_count] $(basename "$_tar")" \
|
||||||
|
k3d image import "$_tar" -c "$cluster_name"; then
|
||||||
|
local _item_elapsed=$(( $(date +%s) - _item_start ))
|
||||||
|
echo " [OK] $(basename "$_tar") (${_item_elapsed}s)"
|
||||||
|
else
|
||||||
|
echo " [WARN] Failed to import $(basename "$_tar"); will pull at deploy time." >&2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
local _total_elapsed=$(( $(date +%s) - overall_start ))
|
||||||
|
local _te_m=$(( _total_elapsed / 60 )) _te_s=$(( _total_elapsed % 60 ))
|
||||||
|
echo " [DONE] Sequential import complete. Total: ${_te_m}m${_te_s}s"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
k3s)
|
k3s)
|
||||||
|
|||||||
@ -16,7 +16,7 @@ source "$SCRIPT_DIR/prole_cfg.sh"
|
|||||||
VERBOSE=false
|
VERBOSE=false
|
||||||
ENVIRONMENT="prod"
|
ENVIRONMENT="prod"
|
||||||
HOST=""
|
HOST=""
|
||||||
CLUSTER_NAME_DEFAULT="prole-dev-cluster"
|
CLUSTER_NAME_DEFAULT="knoe-dev-cluster"
|
||||||
CLUSTER_PORT_DEFAULT="6443"
|
CLUSTER_PORT_DEFAULT="6443"
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
|
|||||||
@ -355,7 +355,7 @@ build_prole_kdc_image() {
|
|||||||
|
|
||||||
if [[ -z "$PROLE_KDC_REGISTRY_HOST" && "$(normalized_mode)" == "k3d" ]]; then
|
if [[ -z "$PROLE_KDC_REGISTRY_HOST" && "$(normalized_mode)" == "k3d" ]]; then
|
||||||
if command -v k3d >/dev/null 2>&1; then
|
if command -v k3d >/dev/null 2>&1; then
|
||||||
local cluster_name="${K3D_CLUSTER_NAME:-prole-dev-cluster}"
|
local cluster_name="${K3D_CLUSTER_NAME:-knoe-dev-cluster}"
|
||||||
log "Importing authority image into k3d cluster: ${cluster_name}"
|
log "Importing authority image into k3d cluster: ${cluster_name}"
|
||||||
k3d image import "$local_tag" -c "$cluster_name" >/dev/null 2>&1 || true
|
k3d image import "$local_tag" -c "$cluster_name" >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -255,7 +255,7 @@ apply_k8s() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configure_k3d_insecure_registry() {
|
configure_k3d_insecure_registry() {
|
||||||
local cluster_name="${1:-prole-dev-cluster}"
|
local cluster_name="${1:-knoe-dev-cluster}"
|
||||||
local reg_host="k3d-prole-registry"
|
local reg_host="k3d-prole-registry"
|
||||||
local reg_port="5000"
|
local reg_port="5000"
|
||||||
local registries_yaml
|
local registries_yaml
|
||||||
@ -291,7 +291,7 @@ apply_registry() {
|
|||||||
if ! k3d registry list prole-registry >/dev/null 2>&1; then
|
if ! k3d registry list prole-registry >/dev/null 2>&1; then
|
||||||
k3d registry create prole-registry --port 5000 || true
|
k3d registry create prole-registry --port 5000 || true
|
||||||
fi
|
fi
|
||||||
local cluster_name="${K3D_CLUSTER_NAME:-prole-dev-cluster}"
|
local cluster_name="${K3D_CLUSTER_NAME:-knoe-dev-cluster}"
|
||||||
if k3d cluster list "$cluster_name" >/dev/null 2>&1; then
|
if k3d cluster list "$cluster_name" >/dev/null 2>&1; then
|
||||||
echo "Connecting prole-registry to $cluster_name ..."
|
echo "Connecting prole-registry to $cluster_name ..."
|
||||||
# k3d v5+ uses 'k3d-NAME' as container name for registries
|
# k3d v5+ uses 'k3d-NAME' as container name for registries
|
||||||
|
|||||||
@ -166,7 +166,7 @@ deploy_service_layer() {
|
|||||||
ensure_namespace "$ns"
|
ensure_namespace "$ns"
|
||||||
label_namespace "$ns"
|
label_namespace "$ns"
|
||||||
|
|
||||||
local argocd_action opentofu_action garage_action kdc_action openbao_action certmgr_action kong_action
|
local argocd_action opentofu_action garage_action kdc_action openbao_action kong_action
|
||||||
case "$action" in
|
case "$action" in
|
||||||
start|initialize|update|reload) argocd_action="update" ;;
|
start|initialize|update|reload) argocd_action="update" ;;
|
||||||
restart) argocd_action="restart" ;;
|
restart) argocd_action="restart" ;;
|
||||||
@ -191,14 +191,6 @@ deploy_service_layer() {
|
|||||||
*) openbao_action="update" ;;
|
*) openbao_action="update" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
start|initialize|update|reload) certmgr_action="update" ;;
|
|
||||||
restart) certmgr_action="restart" ;;
|
|
||||||
stop) certmgr_action="stop" ;;
|
|
||||||
status) certmgr_action="status" ;;
|
|
||||||
*) certmgr_action="update" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "$action" in
|
case "$action" in
|
||||||
start|initialize|update|reload) garage_action="start" ;;
|
start|initialize|update|reload) garage_action="start" ;;
|
||||||
restart) garage_action="restart" ;;
|
restart) garage_action="restart" ;;
|
||||||
@ -230,8 +222,8 @@ deploy_service_layer() {
|
|||||||
OPENBAO_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
OPENBAO_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
||||||
"$SCRIPT_DIR/init_openbao.sh" -n "$ns" "$openbao_action" || rc=$?
|
"$SCRIPT_DIR/init_openbao.sh" -n "$ns" "$openbao_action" || rc=$?
|
||||||
|
|
||||||
CERTMGR_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
# cert-manager is cluster-scoped and managed independently via init_certmgr.sh
|
||||||
"$SCRIPT_DIR/init_certmgr.sh" -n "$ns" "$certmgr_action" || rc=$?
|
# in its own dedicated 'cert-manager' namespace; it is not part of the service layer.
|
||||||
|
|
||||||
NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" GARAGE_NAMESPACE="$ns" \
|
NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" GARAGE_NAMESPACE="$ns" \
|
||||||
"$SCRIPT_DIR/init_garage_store.sh" "$garage_action" || rc=$?
|
"$SCRIPT_DIR/init_garage_store.sh" "$garage_action" || rc=$?
|
||||||
|
|||||||
@ -15,7 +15,7 @@ source "$SCRIPT_DIR/prole_cfg.sh"
|
|||||||
log() { printf '%s\n' "$*"; }
|
log() { printf '%s\n' "$*"; }
|
||||||
err() { printf '%s\n' "$*" >&2; }
|
err() { printf '%s\n' "$*" >&2; }
|
||||||
|
|
||||||
cluster_name="${K3D_CLUSTER_NAME:-prole-dev-cluster}"
|
cluster_name="${K3D_CLUSTER_NAME:-knoe-dev-cluster}"
|
||||||
|
|
||||||
ad_dc_ip="${AD_DC_IP:-${KDC_ANSIBLE_DETECTED:-${KDC_AUTO_DETECTED:-}}}"
|
ad_dc_ip="${AD_DC_IP:-${KDC_ANSIBLE_DETECTED:-${KDC_AUTO_DETECTED:-}}}"
|
||||||
if [[ -z "$ad_dc_ip" ]]; then
|
if [[ -z "$ad_dc_ip" ]]; then
|
||||||
|
|||||||
@ -176,6 +176,7 @@ prole_normalize_mode() {
|
|||||||
service|k3s|k3s-*) echo "k3s"; return 0 ;;
|
service|k3s|k3s-*) echo "k3s"; return 0 ;;
|
||||||
prod|production|k8s|k8s-*) echo "k8s"; return 0 ;;
|
prod|production|k8s|k8s-*) echo "k8s"; return 0 ;;
|
||||||
prole-dev-cluster|k3d-prole-dev-cluster) echo "k3d"; return 0 ;;
|
prole-dev-cluster|k3d-prole-dev-cluster) echo "k3d"; return 0 ;;
|
||||||
|
knoe-dev-cluster|k3d-knoe-dev-cluster) echo "k3d"; return 0 ;;
|
||||||
prole-service-cluster) echo "k3s"; return 0 ;;
|
prole-service-cluster) echo "k3s"; return 0 ;;
|
||||||
prole-prod-cluster) echo "k8s"; return 0 ;;
|
prole-prod-cluster) echo "k8s"; return 0 ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@ -143,7 +143,7 @@ cluster_ready() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
k3d_cluster_ready() {
|
k3d_cluster_ready() {
|
||||||
local cluster="${K3D_CLUSTER:-prole-dev-cluster}"
|
local cluster="${K3D_CLUSTER:-knoe-dev-cluster}"
|
||||||
if have k3d; then
|
if have k3d; then
|
||||||
if k3d cluster list --no-headers 2>/dev/null | grep -q "^${cluster}[[:space:]]"; then
|
if k3d cluster list --no-headers 2>/dev/null | grep -q "^${cluster}[[:space:]]"; then
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@ -80,14 +80,14 @@ fi
|
|||||||
|
|
||||||
ARGOCD_NS="${ARGOCD_NAMESPACE:-argocd}"
|
ARGOCD_NS="${ARGOCD_NAMESPACE:-argocd}"
|
||||||
KONG_NS="${KONG_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-}}}"
|
KONG_NS="${KONG_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-}}}"
|
||||||
CERTMGR_NS="${CERTMGR_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-}}}"
|
CERTMGR_NS="${CERTMGR_NAMESPACE:-cert-manager}"
|
||||||
KONG_NAME="${KONG_NAME:-prole-svc-kong}"
|
KONG_NAME="${KONG_NAME:-prole-svc-kong}"
|
||||||
|
|
||||||
if [ -z "$KONG_NS" ]; then
|
if [ -z "$KONG_NS" ]; then
|
||||||
KONG_NS="default"
|
KONG_NS="default"
|
||||||
fi
|
fi
|
||||||
if [ -z "$CERTMGR_NS" ]; then
|
if [ -z "$CERTMGR_NS" ]; then
|
||||||
CERTMGR_NS="default"
|
CERTMGR_NS="cert-manager"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! command -v kubectl >/dev/null 2>&1; then
|
if ! command -v kubectl >/dev/null 2>&1; then
|
||||||
@ -275,64 +275,257 @@ fi
|
|||||||
|
|
||||||
if [ $missing -gt 0 ]; then
|
if [ $missing -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "Status: FAILED ($missing resources missing)"
|
# ── Attempt to repair a completely absent cert-manager before failing ──────
|
||||||
exit 1
|
if ! kubectl get namespace "$CERTMGR_NS" >/dev/null 2>&1 \
|
||||||
|
&& [[ -x "$SCRIPT_DIR/init_certmgr.sh" ]]; then
|
||||||
|
echo "== Repair: cert-manager namespace '$CERTMGR_NS' absent — re-initializing =="
|
||||||
|
"$SCRIPT_DIR/init_certmgr.sh" initialize 2>&1 || true
|
||||||
|
echo " Waiting for cert-manager to settle (20s)..."
|
||||||
|
sleep 20
|
||||||
|
echo " Re-checking cert-manager resources..."
|
||||||
|
_check_cm_resource() {
|
||||||
|
local _kind=$1 _name=$2 _ns=$3
|
||||||
|
if ! kubectl -n "$_ns" get "$_kind" "$_name" >/dev/null 2>&1; then
|
||||||
|
echo " [FAIL] $_kind/$_name still missing (ns=$_ns)"
|
||||||
|
else
|
||||||
|
missing=$((missing - 1))
|
||||||
|
echo " [OK] $_kind/$_name present (ns=$_ns)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
_check_cm_resource svc cert-manager "$CERTMGR_NS"
|
||||||
|
_check_cm_resource svc cert-manager-webhook "$CERTMGR_NS"
|
||||||
|
_check_cm_resource deploy cert-manager "$CERTMGR_NS"
|
||||||
|
_check_cm_resource deploy cert-manager-cainjector "$CERTMGR_NS"
|
||||||
|
_check_cm_resource deploy cert-manager-webhook "$CERTMGR_NS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $missing -gt 0 ]; then
|
||||||
|
echo "Status: FAILED ($missing resources missing)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Status: OK (missing resources restored by repair)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Repair helpers ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Emit "kind name ns" lines for every workload owned by a component.
|
||||||
|
_workloads_for_comp() {
|
||||||
|
local _comp="$1"
|
||||||
|
local _ns="${COMP_NAMESPACE[$_comp]:-$NS}"
|
||||||
|
case "$_comp" in
|
||||||
|
argocd)
|
||||||
|
echo "statefulset argocd-application-controller $ARGOCD_NS"
|
||||||
|
for _d in argocd-server argocd-repo-server argocd-dex-server \
|
||||||
|
argocd-applicationset-controller argocd-notifications-controller argocd-redis; do
|
||||||
|
echo "deployment $_d $ARGOCD_NS"
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
openbao)
|
||||||
|
if kubectl -n "$_ns" get statefulset openbao >/dev/null 2>&1; then
|
||||||
|
echo "statefulset openbao $_ns"
|
||||||
|
else
|
||||||
|
echo "deployment openbao $_ns"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
opentofu) echo "deployment opentofu $_ns" ;;
|
||||||
|
garage) echo "statefulset garage $_ns" ;;
|
||||||
|
auth) echo "deployment auth $_ns" ;;
|
||||||
|
kong) echo "deployment $KONG_NAME $KONG_NS" ;;
|
||||||
|
certmgr)
|
||||||
|
for _d in cert-manager cert-manager-cainjector cert-manager-webhook; do
|
||||||
|
echo "deployment $_d $CERTMGR_NS"
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reset all pod-tracking state and re-run analysis.
|
||||||
|
_recheck_pods() {
|
||||||
|
HEALTHY_COUNT=()
|
||||||
|
BLOCKED_COUNT=()
|
||||||
|
BLOCKED_PODS=()
|
||||||
|
COMP_NAMESPACE=()
|
||||||
|
blocked_lines=""
|
||||||
|
blocked=0
|
||||||
|
analyze_pods "$ARGOCD_NS" "argocd"
|
||||||
|
analyze_pods "$NS" "$pod_filter"
|
||||||
|
analyze_pods "$KONG_NS" "kong"
|
||||||
|
analyze_pods "$CERTMGR_NS" "cert-manager"
|
||||||
|
if [[ -n "$blocked_lines" ]]; then
|
||||||
|
blocked=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print the name of each component that currently has blocked pods.
|
||||||
|
_blocked_comps() {
|
||||||
|
for _bc in argocd openbao opentofu garage auth kong certmgr; do
|
||||||
|
[[ "$_bc" == "auth" && "$ENABLE_KERBEROS" != "1" ]] && continue
|
||||||
|
[[ ${BLOCKED_COUNT["$_bc"]:-0} -gt 0 ]] && echo "$_bc"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Escalating repair ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
if [ $blocked -gt 0 ]; then
|
if [ $blocked -gt 0 ]; then
|
||||||
repairable=()
|
|
||||||
fail_all=()
|
# ── Round 1: delete blocked pods ──────────────────────────────────────────
|
||||||
for comp in argocd openbao opentofu garage auth kong certmgr; do
|
echo ""
|
||||||
if [[ "$comp" == "auth" && "$ENABLE_KERBEROS" != "1" ]]; then
|
echo "== Repair Round 1: replacing blocked pods =="
|
||||||
continue
|
mapfile -t _r1_comps < <(_blocked_comps)
|
||||||
|
for _comp in "${_r1_comps[@]}"; do
|
||||||
|
_pods="${BLOCKED_PODS[$_comp]:-}"
|
||||||
|
_ns="${COMP_NAMESPACE[$_comp]:-$NS}"
|
||||||
|
if [[ -n "$_pods" ]]; then
|
||||||
|
echo " Deleting pod(s) for $_comp in $_ns:$_pods"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
kubectl -n "$_ns" delete pod $_pods --wait=false >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
hc=${HEALTHY_COUNT["$comp"]:-0}
|
done
|
||||||
bc=${BLOCKED_COUNT["$comp"]:-0}
|
echo " Waiting for pod replacement (15s)..."
|
||||||
if [[ "$bc" -gt 0 && "$hc" -gt 0 ]]; then
|
sleep 15
|
||||||
repairable+=("$comp")
|
_recheck_pods
|
||||||
elif [[ "$bc" -gt 0 && "$hc" -eq 0 ]]; then
|
|
||||||
fail_all+=("$comp")
|
if [[ $blocked -eq 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "Status: OK (resolved in repair round 1)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Pods still blocked after Round 1:"
|
||||||
|
printf '%b' "$blocked_lines"
|
||||||
|
|
||||||
|
# ── Round 2: rollout restart owning StatefulSet / Deployment ──────────────
|
||||||
|
echo ""
|
||||||
|
echo "== Repair Round 2: restarting workload controllers =="
|
||||||
|
mapfile -t _r2_comps < <(_blocked_comps)
|
||||||
|
for _comp in "${_r2_comps[@]}"; do
|
||||||
|
while IFS=' ' read -r _kind _name _wns; do
|
||||||
|
[[ -z "$_kind" ]] && continue
|
||||||
|
if kubectl -n "$_wns" get "$_kind" "$_name" >/dev/null 2>&1; then
|
||||||
|
echo " kubectl rollout restart $_kind/$_name -n $_wns"
|
||||||
|
kubectl -n "$_wns" rollout restart "$_kind/$_name" >/dev/null 2>&1 || true
|
||||||
|
kubectl -n "$_wns" rollout status "$_kind/$_name" --timeout=60s 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done < <(_workloads_for_comp "$_comp")
|
||||||
|
done
|
||||||
|
echo " Waiting for rollout to settle (20s)..."
|
||||||
|
sleep 20
|
||||||
|
_recheck_pods
|
||||||
|
|
||||||
|
if [[ $blocked -eq 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "Status: OK (resolved in repair round 2)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Pods still blocked after Round 2:"
|
||||||
|
printf '%b' "$blocked_lines"
|
||||||
|
|
||||||
|
# ── Round 3: clear affected namespaces and re-initialize ──────────────────
|
||||||
|
echo ""
|
||||||
|
echo "== Repair Round 3: clearing affected namespaces and re-initializing =="
|
||||||
|
mapfile -t _r3_comps < <(_blocked_comps)
|
||||||
|
declare -A _cleared_ns=()
|
||||||
|
for _comp in "${_r3_comps[@]}"; do
|
||||||
|
_ns="${COMP_NAMESPACE[$_comp]:-$NS}"
|
||||||
|
[[ -n "${_cleared_ns[$_ns]:-}" ]] && continue
|
||||||
|
_cleared_ns["$_ns"]=1
|
||||||
|
if [[ "$_ns" == "default" || "$_ns" == "kube-system" ]]; then
|
||||||
|
echo " [Round 3] Removing workload resources in protected namespace '$_ns' for: $_comp"
|
||||||
|
while IFS=' ' read -r _kind _name _wns; do
|
||||||
|
[[ -z "$_kind" ]] && continue
|
||||||
|
echo " kubectl delete $_kind $_name -n $_wns --ignore-not-found"
|
||||||
|
kubectl -n "$_wns" delete "$_kind" "$_name" --ignore-not-found >/dev/null 2>&1 || true
|
||||||
|
done < <(_workloads_for_comp "$_comp")
|
||||||
|
else
|
||||||
|
echo " [Round 3] Deleting namespace '$_ns' ..."
|
||||||
|
kubectl delete namespace "$_ns" --wait=true --timeout=90s >/dev/null 2>&1 || true
|
||||||
|
echo " [Round 3] Recreating namespace '$_ns' ..."
|
||||||
|
kubectl create namespace "$_ns" >/dev/null 2>&1 || true
|
||||||
|
echo " [OK] Namespace '$_ns' cleared and recreated."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ ${#fail_all[@]} -gt 0 ]; then
|
if [[ -x "$SCRIPT_DIR/init_service_layer.sh" ]]; then
|
||||||
echo ""
|
echo " [Round 3] Re-initializing service layer ..."
|
||||||
echo "Repair skipped: all pods blocked for ${fail_all[*]}."
|
_krb_flag=""
|
||||||
echo "Action: Deploy Missing Services."
|
[[ "$ENABLE_KERBEROS" == "1" ]] && _krb_flag="-k"
|
||||||
echo ""
|
# shellcheck disable=SC2086
|
||||||
echo "Status: NOTICE (blocked pods detected)"
|
"$SCRIPT_DIR/init_service_layer.sh" -n "$NS" $_krb_flag update 2>&1 || true
|
||||||
exit 2
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ${#repairable[@]} -gt 0 ]; then
|
echo " [Round 3] Re-checking status after namespace reset (30s)..."
|
||||||
|
sleep 30
|
||||||
|
_recheck_pods
|
||||||
|
|
||||||
|
if [[ $blocked -eq 0 ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "== Repair =="
|
echo "Status: OK (resolved in repair round 3)"
|
||||||
echo "Repairing blocked pods for: ${repairable[*]}"
|
exit 0
|
||||||
for comp in "${repairable[@]}"; do
|
|
||||||
pods="${BLOCKED_PODS["$comp"]:-}"
|
|
||||||
ns="${COMP_NAMESPACE["$comp"]:-$NS}"
|
|
||||||
if [[ -n "$pods" ]]; then
|
|
||||||
kubectl -n "$ns" delete pod $pods --wait=false >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo "Re-checking pods after repair..."
|
|
||||||
sleep 2
|
|
||||||
HEALTHY_COUNT=()
|
|
||||||
BLOCKED_COUNT=()
|
|
||||||
BLOCKED_PODS=()
|
|
||||||
COMP_NAMESPACE=()
|
|
||||||
blocked_lines=""
|
|
||||||
analyze_pods "$ARGOCD_NS" "argocd"
|
|
||||||
analyze_pods "$NS" "$pod_filter"
|
|
||||||
if [[ -n "$blocked_lines" ]]; then
|
|
||||||
echo ""
|
|
||||||
echo "Blocked pods still detected after repair:"
|
|
||||||
printf '%b' "$blocked_lines"
|
|
||||||
echo ""
|
|
||||||
echo "Status: NOTICE (blocked pods detected)"
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Pods still blocked after Round 3:"
|
||||||
|
printf '%b' "$blocked_lines"
|
||||||
|
|
||||||
|
# ── Round 4: reset k3d cluster, preserve registry ─────────────────────────
|
||||||
|
_prole_mode="${PROLE_MODE:-}"
|
||||||
|
if [[ "$_prole_mode" == "k3d" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "== Repair Round 4: resetting k3d cluster (preserving registry) =="
|
||||||
|
_cluster_name="${K3D_CLUSTER_NAME:-knoe-dev-cluster}"
|
||||||
|
|
||||||
|
_registry_args=()
|
||||||
|
if command -v k3d >/dev/null 2>&1; then
|
||||||
|
_reg_name=$(k3d registry list --no-headers 2>/dev/null | awk '{print $1}' | head -1 || true)
|
||||||
|
if [[ -n "$_reg_name" ]]; then
|
||||||
|
echo " Preserving registry: $_reg_name"
|
||||||
|
_registry_args=(--registry-use "$_reg_name")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " Deleting k3d cluster '$_cluster_name' ..."
|
||||||
|
k3d cluster delete "$_cluster_name" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo " Recreating k3d cluster '$_cluster_name' ..."
|
||||||
|
k3d cluster create "$_cluster_name" -a 2 \
|
||||||
|
"${_registry_args[@]}" \
|
||||||
|
--api-port 0.0.0.0:6443 >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
kubectl config use-context "k3d-${_cluster_name}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
if [[ -x "$SCRIPT_DIR/init_service_layer.sh" ]]; then
|
||||||
|
echo " [Round 4] Re-initializing service layer after cluster reset ..."
|
||||||
|
_krb_flag=""
|
||||||
|
[[ "$ENABLE_KERBEROS" == "1" ]] && _krb_flag="-k"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
"$SCRIPT_DIR/init_service_layer.sh" -n "$NS" $_krb_flag update 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " [Round 4] Re-checking status after cluster reset (30s)..."
|
||||||
|
sleep 30
|
||||||
|
_recheck_pods
|
||||||
|
|
||||||
|
if [[ $blocked -eq 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "Status: OK (resolved in repair round 4 — cluster reset)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Pods still blocked after Round 4 (cluster reset):"
|
||||||
|
printf '%b' "$blocked_lines"
|
||||||
|
echo ""
|
||||||
|
echo "Status: FAILED — unable to repair after full cluster reset. Manual intervention required."
|
||||||
|
exit 5
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Status: NOTICE (blocked pods remain; k3d cluster reset not applicable for mode '${_prole_mode:-unknown}')"
|
||||||
|
exit 3
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@ -949,13 +949,13 @@ class ProleInstallerBase:
|
|||||||
|
|
||||||
elif mode == "k3d":
|
elif mode == "k3d":
|
||||||
# Ensure k3d dev clusters have a resolvable KUBECONFIG.
|
# Ensure k3d dev clusters have a resolvable KUBECONFIG.
|
||||||
cluster_name = self._get_input("init_cluster.deployment_target", "prole-dev-cluster")
|
cluster_name = self._get_input("init_cluster.deployment_target", "knoe-dev-cluster")
|
||||||
if not cluster_name or cluster_name == "prole-dev-cluster":
|
if not cluster_name or cluster_name == "knoe-dev-cluster":
|
||||||
# Fallback to check if we have a more specific one from UI if available
|
# Fallback to check if we have a more specific one from UI if available
|
||||||
cluster_name = getattr(self, "selected_k3d_cluster", None)
|
cluster_name = getattr(self, "selected_k3d_cluster", None)
|
||||||
if hasattr(cluster_name, "get"):
|
if hasattr(cluster_name, "get"):
|
||||||
cluster_name = cluster_name.get()
|
cluster_name = cluster_name.get()
|
||||||
cluster_name = cluster_name or "prole-dev-cluster"
|
cluster_name = cluster_name or "knoe-dev-cluster"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
@ -2501,7 +2501,12 @@ class ProleSilentInstaller(ProleInstallerBase):
|
|||||||
"PROLE_OPENTOFU_URL": _default_opentofu_pipeline_url(),
|
"PROLE_OPENTOFU_URL": _default_opentofu_pipeline_url(),
|
||||||
"SERVICE_NAMESPACE": self._service_namespace(),
|
"SERVICE_NAMESPACE": self._service_namespace(),
|
||||||
}
|
}
|
||||||
globals_to_save.update(self.prole_cfg_data.get("Global", {}))
|
# Merge pre-existing Global values without overriding explicit inputs
|
||||||
|
existing_global = dict(self.prole_cfg_data.get("Global", {}))
|
||||||
|
for k, v in existing_global.items():
|
||||||
|
if k not in globals_to_save or not str(globals_to_save.get(k, "")).strip():
|
||||||
|
globals_to_save[k] = v
|
||||||
|
# Re-assert critical/dynamic values to avoid accidental overwrite
|
||||||
globals_to_save["DB_PASSWORD"] = self._secret_cfg_value(
|
globals_to_save["DB_PASSWORD"] = self._secret_cfg_value(
|
||||||
"Global", "DB_PASSWORD", db_pw, "db", "password"
|
"Global", "DB_PASSWORD", db_pw, "db", "password"
|
||||||
)
|
)
|
||||||
@ -2591,8 +2596,8 @@ class ProleSilentInstaller(ProleInstallerBase):
|
|||||||
sections["Dev Cluster (k3d)"] = {
|
sections["Dev Cluster (k3d)"] = {
|
||||||
**self.prole_cfg_data.get("Dev Cluster (k3d)", {}),
|
**self.prole_cfg_data.get("Dev Cluster (k3d)", {}),
|
||||||
"MODE": "k3d",
|
"MODE": "k3d",
|
||||||
"CLUSTER_ENV": "k3d-prole-dev-cluster",
|
"CLUSTER_ENV": "k3d-knoe-dev-cluster",
|
||||||
"DISPLAY_NAME": "prole-dev-cluster",
|
"DISPLAY_NAME": "knoe-dev-cluster",
|
||||||
"KUBECTL_CONTEXT": self._get_input("init_cluster.cluster_env", ""),
|
"KUBECTL_CONTEXT": self._get_input("init_cluster.cluster_env", ""),
|
||||||
}
|
}
|
||||||
sections["Service Cluster (k3s)"] = {
|
sections["Service Cluster (k3s)"] = {
|
||||||
@ -2993,7 +2998,7 @@ class ProleSilentInstaller(ProleInstallerBase):
|
|||||||
env_key = _normalize_cluster_env(
|
env_key = _normalize_cluster_env(
|
||||||
self._get_input("init_cluster.cluster_env", "dev")
|
self._get_input("init_cluster.cluster_env", "dev")
|
||||||
)
|
)
|
||||||
cluster_name = "prole-dev-cluster"
|
cluster_name = "knoe-dev-cluster"
|
||||||
|
|
||||||
# Check if image already exists
|
# Check if image already exists
|
||||||
rc_inspect = subprocess.run(
|
rc_inspect = subprocess.run(
|
||||||
@ -3047,7 +3052,7 @@ class ProleSilentInstaller(ProleInstallerBase):
|
|||||||
self.log("[OK] Build successful.")
|
self.log("[OK] Build successful.")
|
||||||
push_ok = True
|
push_ok = True
|
||||||
if env_key == "dev":
|
if env_key == "dev":
|
||||||
cluster_name = "prole-dev-cluster"
|
cluster_name = "knoe-dev-cluster"
|
||||||
self.log(f"Importing image to {cluster_name}...")
|
self.log(f"Importing image to {cluster_name}...")
|
||||||
self._run_cmd(
|
self._run_cmd(
|
||||||
["k3d", "image", "import", image_name, "-c", cluster_name]
|
["k3d", "image", "import", image_name, "-c", cluster_name]
|
||||||
@ -3141,7 +3146,7 @@ class ProleSilentInstaller(ProleInstallerBase):
|
|||||||
if not self.controller.check_docker_running():
|
if not self.controller.check_docker_running():
|
||||||
raise Exception("Docker is not running.")
|
raise Exception("Docker is not running.")
|
||||||
|
|
||||||
cluster_name = "prole-dev-cluster"
|
cluster_name = "knoe-dev-cluster"
|
||||||
res = subprocess.run(
|
res = subprocess.run(
|
||||||
["k3d", "cluster", "list", "--no-headers"],
|
["k3d", "cluster", "list", "--no-headers"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
@ -3542,7 +3547,6 @@ class ProleSilentInstaller(ProleInstallerBase):
|
|||||||
[
|
[
|
||||||
("init_prole-db-backup.sh", ["start"], False),
|
("init_prole-db-backup.sh", ["start"], False),
|
||||||
("init_kong.sh", ["start"], False),
|
("init_kong.sh", ["start"], False),
|
||||||
("init_db_manager.sh", ["start"], False),
|
|
||||||
("init_monitoring.sh", ["initialize"], False),
|
("init_monitoring.sh", ["initialize"], False),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -3821,7 +3825,7 @@ class ProleSilentInstaller(ProleInstallerBase):
|
|||||||
if not self.controller.check_docker_running():
|
if not self.controller.check_docker_running():
|
||||||
raise Exception("Docker is not running.")
|
raise Exception("Docker is not running.")
|
||||||
|
|
||||||
cluster_name = "prole-dev-cluster"
|
cluster_name = "knoe-dev-cluster"
|
||||||
rc = self._run_cmd(["k3d", "cluster", "delete", cluster_name])
|
rc = self._run_cmd(["k3d", "cluster", "delete", cluster_name])
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
self.err(
|
self.err(
|
||||||
|
|||||||
@ -844,7 +844,7 @@ def _normalize_cluster_env(env: str | None) -> str:
|
|||||||
return ""
|
return ""
|
||||||
s = str(env).strip().lower()
|
s = str(env).strip().lower()
|
||||||
if (
|
if (
|
||||||
s in ("dev", "k3d", "k3d-dev", "k3d-prole-dev-cluster", "prole-dev-cluster")
|
s in ("dev", "k3d", "k3d-dev", "k3d-knoe-dev-cluster", "knoe-dev-cluster")
|
||||||
or s.startswith("k3d-")
|
or s.startswith("k3d-")
|
||||||
or s.startswith("prole-dev-")
|
or s.startswith("prole-dev-")
|
||||||
):
|
):
|
||||||
@ -874,7 +874,7 @@ def _cluster_env_radio_value(env: str | None) -> str:
|
|||||||
def _deployment_target_label(env: str | None) -> str:
|
def _deployment_target_label(env: str | None) -> str:
|
||||||
key = _normalize_cluster_env(env)
|
key = _normalize_cluster_env(env)
|
||||||
if key == "dev":
|
if key == "dev":
|
||||||
return "prole-dev-cluster"
|
return "knoe-dev-cluster"
|
||||||
if key == "service":
|
if key == "service":
|
||||||
return "prole-service-cluster"
|
return "prole-service-cluster"
|
||||||
if key == "prod":
|
if key == "prod":
|
||||||
@ -1610,8 +1610,9 @@ def _render_prole_cfg(
|
|||||||
|
|
||||||
def first_non_empty(*vals: str) -> str:
|
def first_non_empty(*vals: str) -> str:
|
||||||
for val in vals:
|
for val in vals:
|
||||||
if val:
|
v = str(val or "").strip()
|
||||||
return val
|
if v and not (v.startswith("${") and v.endswith("}")):
|
||||||
|
return v
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def get_input(key: str) -> str:
|
def get_input(key: str) -> str:
|
||||||
@ -2066,16 +2067,6 @@ def _build_required_port_forwards(
|
|||||||
"TCP",
|
"TCP",
|
||||||
"Grafana UI",
|
"Grafana UI",
|
||||||
),
|
),
|
||||||
_pf_mapping_str(
|
|
||||||
"prole-db-manager",
|
|
||||||
db_ns,
|
|
||||||
"svc/prole-db-manager",
|
|
||||||
"80",
|
|
||||||
"80",
|
|
||||||
addr_all,
|
|
||||||
"TCP",
|
|
||||||
"DB Manager UI",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if supabase_enabled:
|
if supabase_enabled:
|
||||||
|
|||||||
@ -438,7 +438,7 @@ class ClusterLifecycleMilestone(Milestone):
|
|||||||
self.logger.error("Docker is not running.")
|
self.logger.error("Docker is not running.")
|
||||||
return
|
return
|
||||||
|
|
||||||
cluster_name = "prole-dev-cluster"
|
cluster_name = "knoe-dev-cluster"
|
||||||
res = subprocess.run(
|
res = subprocess.run(
|
||||||
["k3d", "cluster", "list", "--no-headers"],
|
["k3d", "cluster", "list", "--no-headers"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
@ -665,7 +665,6 @@ class InitializationScriptsMilestone(Milestone):
|
|||||||
[
|
[
|
||||||
"init_prole-db-backup.sh",
|
"init_prole-db-backup.sh",
|
||||||
"init_kong.sh",
|
"init_kong.sh",
|
||||||
"init_db_manager.sh",
|
|
||||||
"init_monitoring.sh",
|
"init_monitoring.sh",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -692,7 +691,7 @@ class InitializationScriptsMilestone(Milestone):
|
|||||||
args = mode_args + ["initialize"]
|
args = mode_args + ["initialize"]
|
||||||
elif script == "init_prole-db-backup.sh":
|
elif script == "init_prole-db-backup.sh":
|
||||||
args = mode_args + ["start"]
|
args = mode_args + ["start"]
|
||||||
elif script in ("init_kong.sh", "init_db_manager.sh"):
|
elif script == "init_kong.sh":
|
||||||
args = mode_args + ["start"]
|
args = mode_args + ["start"]
|
||||||
elif script in ("init_monitoring.sh", "init_nginx_ingress.sh"):
|
elif script in ("init_monitoring.sh", "init_nginx_ingress.sh"):
|
||||||
args = mode_args + ["initialize"]
|
args = mode_args + ["initialize"]
|
||||||
|
|||||||
@ -128,7 +128,7 @@ class Milestone(ABC):
|
|||||||
if not (env.get("KUBECONFIG") or "").strip():
|
if not (env.get("KUBECONFIG") or "").strip():
|
||||||
import subprocess as _sp
|
import subprocess as _sp
|
||||||
|
|
||||||
cluster_name = "prole-dev-cluster"
|
cluster_name = "knoe-dev-cluster"
|
||||||
try:
|
try:
|
||||||
_sp.run(
|
_sp.run(
|
||||||
[
|
[
|
||||||
|
|||||||
@ -52,9 +52,9 @@ class ProleNcursesInstaller:
|
|||||||
self.selected_local_path = None
|
self.selected_local_path = None
|
||||||
|
|
||||||
# Environment/config variables
|
# Environment/config variables
|
||||||
self.cluster_env = "k3d-prole-dev-cluster"
|
self.cluster_env = "k3d-knoe-dev-cluster"
|
||||||
self.cluster_environments = [
|
self.cluster_environments = [
|
||||||
"prole-dev-cluster",
|
"knoe-dev-cluster",
|
||||||
"prole-service-cluster",
|
"prole-service-cluster",
|
||||||
"prole-prod-cluster",
|
"prole-prod-cluster",
|
||||||
]
|
]
|
||||||
@ -386,7 +386,7 @@ class ProleNcursesInstaller:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
selected_env = self.cluster_environments[self.selected_env_index]
|
selected_env = self.cluster_environments[self.selected_env_index]
|
||||||
if selected_env == "prole-dev-cluster":
|
if selected_env == "knoe-dev-cluster":
|
||||||
if key == ord(" "): # Toggle/Select context
|
if key == ord(" "): # Toggle/Select context
|
||||||
pass # We could implement sub-selection
|
pass # We could implement sub-selection
|
||||||
elif key == curses.KEY_NPAGE: # Page Down to scroll context list?
|
elif key == curses.KEY_NPAGE: # Page Down to scroll context list?
|
||||||
@ -851,7 +851,7 @@ class ProleNcursesInstaller:
|
|||||||
y += 1
|
y += 1
|
||||||
selected_env = self.cluster_environments[self.selected_env_index]
|
selected_env = self.cluster_environments[self.selected_env_index]
|
||||||
|
|
||||||
if selected_env == "prole-dev-cluster":
|
if selected_env == "knoe-dev-cluster":
|
||||||
win.render_text(
|
win.render_text(
|
||||||
y,
|
y,
|
||||||
4,
|
4,
|
||||||
|
|||||||
@ -346,8 +346,8 @@ class ProleInstaller(
|
|||||||
("System Environment", "env_setup"),
|
("System Environment", "env_setup"),
|
||||||
("Cluster Environment", "init_cluster"),
|
("Cluster Environment", "init_cluster"),
|
||||||
("Database Options", "database_options"),
|
("Database Options", "database_options"),
|
||||||
("Database Creation", "init_password"),
|
|
||||||
("Docker Build", "init_db_build"),
|
("Docker Build", "init_db_build"),
|
||||||
|
("Database Creation", "init_password"),
|
||||||
("Initialization Scripts", "init_scripts"),
|
("Initialization Scripts", "init_scripts"),
|
||||||
("Kerberos Authentication", "kerberos_config"),
|
("Kerberos Authentication", "kerberos_config"),
|
||||||
("GitOps", "gitops_config"),
|
("GitOps", "gitops_config"),
|
||||||
@ -533,7 +533,7 @@ class ProleInstaller(
|
|||||||
|
|
||||||
# Cluster / k3d variables
|
# Cluster / k3d variables
|
||||||
self.cluster_env = tk.StringVar(value="dev")
|
self.cluster_env = tk.StringVar(value="dev")
|
||||||
self.k3d_cluster_name = tk.StringVar(value="prole-dev-cluster")
|
self.k3d_cluster_name = tk.StringVar(value="knoe-dev-cluster")
|
||||||
self.k3d_cluster_list = tk.Variable(value=[])
|
self.k3d_cluster_list = tk.Variable(value=[])
|
||||||
self.selected_k3d_cluster = tk.StringVar()
|
self.selected_k3d_cluster = tk.StringVar()
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -82,8 +82,12 @@ class ConfigMixin:
|
|||||||
"DB_HOST_PORT": (self.db_host_port.get() or "5432").strip(),
|
"DB_HOST_PORT": (self.db_host_port.get() or "5432").strip(),
|
||||||
"PROLE_OPENTOFU_URL": _default_opentofu_pipeline_url(),
|
"PROLE_OPENTOFU_URL": _default_opentofu_pipeline_url(),
|
||||||
}
|
}
|
||||||
# Add any other values already in self.prole_cfg_data['Global']
|
# Merge any other values already present in existing Global without overriding explicit UI values
|
||||||
globals_to_save.update(self.prole_cfg_data.get("Global", {}))
|
existing_global = dict(self.prole_cfg_data.get("Global", {}))
|
||||||
|
for k, v in existing_global.items():
|
||||||
|
if k not in globals_to_save or not str(globals_to_save.get(k, "")).strip():
|
||||||
|
globals_to_save[k] = v
|
||||||
|
# Re-assert dynamic/derived globals to ensure they are not overwritten by existing values
|
||||||
globals_to_save["DB_PASSWORD"] = self._secret_cfg_value(
|
globals_to_save["DB_PASSWORD"] = self._secret_cfg_value(
|
||||||
"Global", "DB_PASSWORD", self.db_password.get(), "db", "password"
|
"Global", "DB_PASSWORD", self.db_password.get(), "db", "password"
|
||||||
)
|
)
|
||||||
@ -135,7 +139,7 @@ class ConfigMixin:
|
|||||||
**self.prole_cfg_data.get("Dev Cluster (k3d)", {}),
|
**self.prole_cfg_data.get("Dev Cluster (k3d)", {}),
|
||||||
"MODE": "k3d",
|
"MODE": "k3d",
|
||||||
"CLUSTER_ENV": "dev",
|
"CLUSTER_ENV": "dev",
|
||||||
"DISPLAY_NAME": "prole-dev-cluster",
|
"DISPLAY_NAME": "knoe-dev-cluster",
|
||||||
"KUBECTL_CONTEXT": (self.selected_kubectx.get() or "").strip(),
|
"KUBECTL_CONTEXT": (self.selected_kubectx.get() or "").strip(),
|
||||||
}
|
}
|
||||||
sections["Service Cluster (k3s)"] = {
|
sections["Service Cluster (k3s)"] = {
|
||||||
@ -270,6 +274,15 @@ class ConfigMixin:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Database options (distribution, version, extensions)
|
||||||
|
_set("database_options.distribution", _get_var(self.db_distribution, "percona"))
|
||||||
|
_set("database_options.version_type", _get_var(self.db_version_type, "v18"))
|
||||||
|
for _ext in getattr(self, "extensions_list", []):
|
||||||
|
_set_bool(
|
||||||
|
f"database_options.ext.{_ext['id']}",
|
||||||
|
_get_var(self.db_extensions.get(_ext["id"]), False),
|
||||||
|
)
|
||||||
|
|
||||||
# Build DB image
|
# Build DB image
|
||||||
_set_bool(
|
_set_bool(
|
||||||
"init_db_build.run_build",
|
"init_db_build.run_build",
|
||||||
|
|||||||
@ -1182,7 +1182,7 @@ class ClusterScreenMixin:
|
|||||||
"""Verify the config then write the values to prole.cfg."""
|
"""Verify the config then write the values to prole.cfg."""
|
||||||
env_key = self._cluster_env_key()
|
env_key = self._cluster_env_key()
|
||||||
if env_key == "dev":
|
if env_key == "dev":
|
||||||
cluster_val = self.selected_k3d_cluster.get() or "prole-dev-cluster"
|
cluster_val = self.selected_k3d_cluster.get() or "knoe-dev-cluster"
|
||||||
if not cluster_val:
|
if not cluster_val:
|
||||||
messagebox.showwarning(
|
messagebox.showwarning(
|
||||||
"Validation", "Please select or create a k3d cluster."
|
"Validation", "Please select or create a k3d cluster."
|
||||||
@ -1266,7 +1266,7 @@ class ClusterScreenMixin:
|
|||||||
|
|
||||||
cluster_ok = False
|
cluster_ok = False
|
||||||
if env == "dev":
|
if env == "dev":
|
||||||
cluster_name = self.selected_k3d_cluster.get() or "prole-dev-cluster"
|
cluster_name = self.selected_k3d_cluster.get() or "knoe-dev-cluster"
|
||||||
try:
|
try:
|
||||||
res = subprocess.run(
|
res = subprocess.run(
|
||||||
["k3d", "cluster", "list", "--no-headers"],
|
["k3d", "cluster", "list", "--no-headers"],
|
||||||
@ -1457,7 +1457,7 @@ class ClusterScreenMixin:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 2. Manage or verify cluster
|
# 2. Manage or verify cluster
|
||||||
cluster_name = "prole-dev-cluster"
|
cluster_name = "knoe-dev-cluster"
|
||||||
res = subprocess.run(
|
res = subprocess.run(
|
||||||
["k3d", "cluster", "list", "--no-headers"],
|
["k3d", "cluster", "list", "--no-headers"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
@ -1612,20 +1612,6 @@ class ClusterScreenMixin:
|
|||||||
on_line=_on_line,
|
on_line=_on_line,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Also run status to show per-service output as requested
|
|
||||||
console.write("\n" + "="*60 + "\n")
|
|
||||||
console.write("Detailed Service Status:\n")
|
|
||||||
console.write("="*60 + "\n")
|
|
||||||
|
|
||||||
status_args = ["-n", ns, "status"]
|
|
||||||
|
|
||||||
self.controller.run_script(
|
|
||||||
"init_common_services.sh",
|
|
||||||
args=status_args,
|
|
||||||
env=env,
|
|
||||||
on_line=lambda line: console.write(line),
|
|
||||||
)
|
|
||||||
|
|
||||||
console.write(f"\nVerification Exit status: {rc}\n")
|
console.write(f"\nVerification Exit status: {rc}\n")
|
||||||
|
|
||||||
# Parse per-component traffic light status from [OK]/[FAIL] lines
|
# Parse per-component traffic light status from [OK]/[FAIL] lines
|
||||||
|
|||||||
@ -1282,7 +1282,7 @@ class DatabaseScreenMixin:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Import to k3d
|
# Import to k3d
|
||||||
cluster_name = "prole-dev-cluster"
|
cluster_name = "knoe-dev-cluster"
|
||||||
self._db_build_console.write(
|
self._db_build_console.write(
|
||||||
f"Importing image to {cluster_name} ({import_tag})...\n"
|
f"Importing image to {cluster_name} ({import_tag})...\n"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -14,14 +14,23 @@ class DatabaseOptionsScreenMixin:
|
|||||||
"""Mixin for the Database Options screen."""
|
"""Mixin for the Database Options screen."""
|
||||||
|
|
||||||
def _init_database_options_state(self):
|
def _init_database_options_state(self):
|
||||||
|
# Load saved inputs (available when prole.cfg was previously saved)
|
||||||
|
_saved = getattr(self, "prole_cfg_data", {}).get("Inputs", {})
|
||||||
|
|
||||||
self.db_at_rest_encryption = tk.BooleanVar(value=True)
|
self.db_at_rest_encryption = tk.BooleanVar(value=True)
|
||||||
self.db_distribution = tk.StringVar(value="percona")
|
_saved_dist = _saved.get("database_options.distribution", "percona")
|
||||||
self.db_version_type = tk.StringVar(value="latest") # stable, current, latest, v18
|
self.db_distribution = tk.StringVar(value=_saved_dist or "percona")
|
||||||
self.db_selected_version = tk.StringVar(value="17")
|
_saved_vtype = _saved.get("database_options.version_type", "v18")
|
||||||
|
self.db_version_type = tk.StringVar(value=_saved_vtype or "v18")
|
||||||
|
# Derive selected version from version_type; default to pg18
|
||||||
|
_vtype_to_ver = {"stable": "15", "current": "16", "latest": "17", "v18": "18"}
|
||||||
|
self.db_selected_version = tk.StringVar(
|
||||||
|
value=_vtype_to_ver.get(self.db_version_type.get(), "18")
|
||||||
|
)
|
||||||
|
|
||||||
# Extensions state: dict of id -> BooleanVar
|
# Extensions state: dict of id -> BooleanVar
|
||||||
self.db_extensions = {}
|
self.db_extensions = {}
|
||||||
|
|
||||||
self.extensions_list = [
|
self.extensions_list = [
|
||||||
{"id": "postgis", "name": "PostGIS", "description": "Spatial and geographic objects for PostgreSQL"},
|
{"id": "postgis", "name": "PostGIS", "description": "Spatial and geographic objects for PostgreSQL"},
|
||||||
{"id": "pgvector", "name": "pgvector", "description": "Vector similarity search"},
|
{"id": "pgvector", "name": "pgvector", "description": "Vector similarity search"},
|
||||||
@ -37,14 +46,18 @@ class DatabaseOptionsScreenMixin:
|
|||||||
{"id": "pg_stat_monitor", "name": "pg_stat_monitor", "description": "Statistics collector (Percona only)", "percona_only": True},
|
{"id": "pg_stat_monitor", "name": "pg_stat_monitor", "description": "Statistics collector (Percona only)", "percona_only": True},
|
||||||
{"id": "pgbadger", "name": "pgBadger", "description": "Log analyzer (Package only, no extension)", "no_extension": True},
|
{"id": "pgbadger", "name": "pgBadger", "description": "Log analyzer (Package only, no extension)", "no_extension": True},
|
||||||
]
|
]
|
||||||
|
|
||||||
# Default enabled extensions
|
# Default enabled extensions (all on for percona v18)
|
||||||
default_enabled = ["postgis", "pgvector", "pgcrypto", "pgaudit", "pg_repack", "pg_stat_statements", "pg_buffercache", "pg_freespacemap", "pgrowlocks", "postgres_fdw", "dblink"]
|
default_enabled = ["postgis", "pgvector", "pgcrypto", "pgaudit", "pg_repack", "pg_stat_statements", "pg_buffercache", "pg_freespacemap", "pgrowlocks", "postgres_fdw", "dblink", "pg_stat_monitor", "pgbadger"]
|
||||||
|
|
||||||
for ext in self.extensions_list:
|
for ext in self.extensions_list:
|
||||||
is_enabled = ext["id"] in default_enabled
|
saved_key = f"database_options.ext.{ext['id']}"
|
||||||
|
if saved_key in _saved:
|
||||||
|
is_enabled = _saved[saved_key].lower() == "true"
|
||||||
|
else:
|
||||||
|
is_enabled = ext["id"] in default_enabled
|
||||||
self.db_extensions[ext["id"]] = tk.BooleanVar(value=is_enabled)
|
self.db_extensions[ext["id"]] = tk.BooleanVar(value=is_enabled)
|
||||||
|
|
||||||
self.db_versions_data = {}
|
self.db_versions_data = {}
|
||||||
self._load_database_versions()
|
self._load_database_versions()
|
||||||
|
|
||||||
|
|||||||
@ -471,7 +471,7 @@ class DeployScreenMixin:
|
|||||||
env_combo.pack(side="left")
|
env_combo.pack(side="left")
|
||||||
env_help = tk.Label(
|
env_help = tk.Label(
|
||||||
env_frame,
|
env_frame,
|
||||||
text="Dev = local k3d (prole-dev-cluster), Service = remote k3s at pi.prole.org:6443, Prod = stretch of prole-service-cluster",
|
text="Dev = local k3d (knoe-dev-cluster), Service = remote k3s at pi.prole.org:6443, Prod = stretch of prole-service-cluster",
|
||||||
bg="#1a1a1a",
|
bg="#1a1a1a",
|
||||||
fg="#888888",
|
fg="#888888",
|
||||||
font=("Helvetica", 9),
|
font=("Helvetica", 9),
|
||||||
@ -776,7 +776,7 @@ kerberos.kdc={self.kerberos_kdc.get()}
|
|||||||
self.update_deploy_step_status(
|
self.update_deploy_step_status(
|
||||||
"Import image to k3d cluster (Dev only)", "running"
|
"Import image to k3d cluster (Dev only)", "running"
|
||||||
)
|
)
|
||||||
self.import_k3d_image(cluster_name="prole-dev-cluster")
|
self.import_k3d_image(cluster_name="knoe-dev-cluster")
|
||||||
self.update_deploy_step_status(
|
self.update_deploy_step_status(
|
||||||
"Import image to k3d cluster (Dev only)", "completed"
|
"Import image to k3d cluster (Dev only)", "completed"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -500,7 +500,7 @@ class DockerScreenMixin:
|
|||||||
if not _push_docker_image(remote_tag):
|
if not _push_docker_image(remote_tag):
|
||||||
raise Exception("Failed to push image to registry.")
|
raise Exception("Failed to push image to registry.")
|
||||||
|
|
||||||
def import_k3d_image(self, cluster_name="prole-dev-cluster"):
|
def import_k3d_image(self, cluster_name="knoe-dev-cluster"):
|
||||||
"""Import image to k3d cluster (only for Dev)."""
|
"""Import image to k3d cluster (only for Dev)."""
|
||||||
version = self.get_prole_db_version()
|
version = self.get_prole_db_version()
|
||||||
image_name = f"prole-db:{version}"
|
image_name = f"prole-db:{version}"
|
||||||
@ -580,7 +580,7 @@ class DockerScreenMixin:
|
|||||||
else:
|
else:
|
||||||
raise Exception(f"Unknown environment: {env}")
|
raise Exception(f"Unknown environment: {env}")
|
||||||
|
|
||||||
def create_or_recreate_k3d_dev_cluster(self, cluster_name="prole-dev-cluster"):
|
def create_or_recreate_k3d_dev_cluster(self, cluster_name="knoe-dev-cluster"):
|
||||||
"""Create or restart local k3d cluster and wire it to the chosen registry."""
|
"""Create or restart local k3d cluster and wire it to the chosen registry."""
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["k3d", "cluster", "list"], capture_output=True, text=True
|
["k3d", "cluster", "list"], capture_output=True, text=True
|
||||||
|
|||||||
@ -328,13 +328,13 @@ class NavigationMixin:
|
|||||||
self.show_page("init_cluster")
|
self.show_page("init_cluster")
|
||||||
return
|
return
|
||||||
if current_id == "init_password":
|
if current_id == "init_password":
|
||||||
self.show_page("database_options")
|
self.show_page("init_db_build")
|
||||||
return
|
return
|
||||||
if current_id == "init_db_build":
|
if current_id == "init_db_build":
|
||||||
self.show_page("init_password")
|
self.show_page("database_options")
|
||||||
return
|
return
|
||||||
if current_id == "init_scripts":
|
if current_id == "init_scripts":
|
||||||
self.show_page("init_db_build")
|
self.show_page("init_password")
|
||||||
return
|
return
|
||||||
if current_id == "kerberos_config":
|
if current_id == "kerberos_config":
|
||||||
self.show_page("init_scripts")
|
self.show_page("init_scripts")
|
||||||
@ -488,10 +488,7 @@ class NavigationMixin:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if current_id == "init_db_build":
|
if current_id == "init_db_build":
|
||||||
if self.kerberos_enabled.get():
|
self.show_page("init_password")
|
||||||
self.show_page("kerberos_config")
|
|
||||||
else:
|
|
||||||
self.show_page("init_scripts")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if current_id == "build":
|
if current_id == "build":
|
||||||
@ -519,7 +516,7 @@ class NavigationMixin:
|
|||||||
|
|
||||||
if current_id == "database_options":
|
if current_id == "database_options":
|
||||||
if self._generate_prole_db_dockerfile():
|
if self._generate_prole_db_dockerfile():
|
||||||
self.show_page("init_password")
|
self.show_page("init_db_build")
|
||||||
return
|
return
|
||||||
|
|
||||||
if current_id == "init_scripts":
|
if current_id == "init_scripts":
|
||||||
@ -527,7 +524,10 @@ class NavigationMixin:
|
|||||||
"Completed" if getattr(self, "_scripts_success", False) else "Attempted"
|
"Completed" if getattr(self, "_scripts_success", False) else "Attempted"
|
||||||
)
|
)
|
||||||
self._save_prole_cfg()
|
self._save_prole_cfg()
|
||||||
self.show_page("kerberos_config")
|
if self.kerberos_enabled.get():
|
||||||
|
self.show_page("kerberos_config")
|
||||||
|
else:
|
||||||
|
self.show_page("gitops_config")
|
||||||
return
|
return
|
||||||
|
|
||||||
if current_id == "kerberos_config":
|
if current_id == "kerberos_config":
|
||||||
|
|||||||
@ -160,7 +160,6 @@ class ServicesScreenMixin:
|
|||||||
]
|
]
|
||||||
scripts.append(("Prole DB Backup", "init_prole-db-backup.sh"))
|
scripts.append(("Prole DB Backup", "init_prole-db-backup.sh"))
|
||||||
scripts.append(("Kong API Gateway", "init_kong.sh"))
|
scripts.append(("Kong API Gateway", "init_kong.sh"))
|
||||||
scripts.append(("DB Manager", "init_db_manager.sh"))
|
|
||||||
scripts.append(("Monitoring", "init_monitoring.sh"))
|
scripts.append(("Monitoring", "init_monitoring.sh"))
|
||||||
mode = self._deployment_mode()
|
mode = self._deployment_mode()
|
||||||
if mode != "k3d":
|
if mode != "k3d":
|
||||||
@ -319,7 +318,7 @@ class ServicesScreenMixin:
|
|||||||
"Skipping common services initialization because previous steps failed.\n"
|
"Skipping common services initialization because previous steps failed.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. init_cloudnative_pg.sh initialize
|
# 3. init_cloudnative_pg.sh initialize
|
||||||
if overall_success:
|
if overall_success:
|
||||||
script = "init_cloudnative_pg.sh"
|
script = "init_cloudnative_pg.sh"
|
||||||
_select_tab(script)
|
_select_tab(script)
|
||||||
@ -370,7 +369,7 @@ class ServicesScreenMixin:
|
|||||||
"Skipping CloudNative-PG initialization because previous steps failed.\n"
|
"Skipping CloudNative-PG initialization because previous steps failed.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. init_prole-db-backup.sh start
|
# 4. init_prole-db-backup.sh start
|
||||||
if overall_success:
|
if overall_success:
|
||||||
script = "init_prole-db-backup.sh"
|
script = "init_prole-db-backup.sh"
|
||||||
_select_tab(script)
|
_select_tab(script)
|
||||||
@ -417,7 +416,7 @@ class ServicesScreenMixin:
|
|||||||
"Skipping Prole DB backup because previous steps failed.\n"
|
"Skipping Prole DB backup because previous steps failed.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 4. init_kong.sh start
|
# 5. init_kong.sh start
|
||||||
if overall_success:
|
if overall_success:
|
||||||
script = "init_kong.sh"
|
script = "init_kong.sh"
|
||||||
if script in self.script_consoles:
|
if script in self.script_consoles:
|
||||||
@ -466,56 +465,7 @@ class ServicesScreenMixin:
|
|||||||
"Skipping Kong because previous steps failed.\n"
|
"Skipping Kong because previous steps failed.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 5. init_db_manager.sh start
|
# 7. init_monitoring.sh initialize
|
||||||
if overall_success:
|
|
||||||
script = "init_db_manager.sh"
|
|
||||||
if script in self.script_consoles:
|
|
||||||
_select_tab(script)
|
|
||||||
self.script_consoles[script].clear()
|
|
||||||
self.script_consoles[script].write(f"Running {script} start...\n")
|
|
||||||
|
|
||||||
log_path = _log_path_for(script)
|
|
||||||
self._record_install_log(log_path)
|
|
||||||
try:
|
|
||||||
log_fp = log_path.open("a", encoding="utf-8")
|
|
||||||
except Exception:
|
|
||||||
log_fp = None
|
|
||||||
|
|
||||||
def _dbmgr_line(line):
|
|
||||||
self.script_consoles[script].write(line)
|
|
||||||
self._process_script_output_line(line)
|
|
||||||
if log_fp:
|
|
||||||
try:
|
|
||||||
log_fp.write(line)
|
|
||||||
log_fp.flush()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
rc_dbmgr = self.controller.run_script(
|
|
||||||
script, args=mode_args + ["start"], env=env, on_line=_dbmgr_line
|
|
||||||
)
|
|
||||||
if log_fp:
|
|
||||||
try:
|
|
||||||
log_fp.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if rc_dbmgr != 0:
|
|
||||||
self.script_consoles[script].write(
|
|
||||||
f"\nERROR: {script} start failed with code {rc_dbmgr}\n"
|
|
||||||
)
|
|
||||||
overall_success = False
|
|
||||||
else:
|
|
||||||
self.script_consoles[script].write(
|
|
||||||
f"\n{script} completed successfully.\n"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if "init_db_manager.sh" in self.script_consoles:
|
|
||||||
self.script_consoles["init_db_manager.sh"].write(
|
|
||||||
"Skipping DB Manager because previous steps failed.\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 6. init_monitoring.sh initialize
|
|
||||||
if overall_success:
|
if overall_success:
|
||||||
script = "init_monitoring.sh"
|
script = "init_monitoring.sh"
|
||||||
_select_tab(script)
|
_select_tab(script)
|
||||||
@ -574,7 +524,7 @@ class ServicesScreenMixin:
|
|||||||
"Skipping Monitoring initialization because previous steps failed.\n"
|
"Skipping Monitoring initialization because previous steps failed.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 7. init_nginx_ingress.sh initialize (k3s only — not used in k3d)
|
# 8. init_nginx_ingress.sh initialize (k3s only — not used in k3d)
|
||||||
if overall_success and mode != "k3d":
|
if overall_success and mode != "k3d":
|
||||||
script = "init_nginx_ingress.sh"
|
script = "init_nginx_ingress.sh"
|
||||||
_select_tab(script)
|
_select_tab(script)
|
||||||
@ -622,7 +572,7 @@ class ServicesScreenMixin:
|
|||||||
"Skipping Nginx Ingress because previous steps failed.\n"
|
"Skipping Nginx Ingress because previous steps failed.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 8. init_port_forwards.sh start (k3d only — not used in k3s)
|
# 9. init_port_forwards.sh start (k3d only — not used in k3s)
|
||||||
if overall_success and mode != "k3s":
|
if overall_success and mode != "k3s":
|
||||||
script = "init_port_forwards.sh"
|
script = "init_port_forwards.sh"
|
||||||
if script in self.script_consoles:
|
if script in self.script_consoles:
|
||||||
@ -967,7 +917,7 @@ class ServicesScreenMixin:
|
|||||||
)
|
)
|
||||||
self._canvas_items.append(mode_label)
|
self._canvas_items.append(mode_label)
|
||||||
mode_values = [
|
mode_values = [
|
||||||
"prole-dev-cluster",
|
"knoe-dev-cluster",
|
||||||
"prole-service-cluster",
|
"prole-service-cluster",
|
||||||
"prole-prod-cluster",
|
"prole-prod-cluster",
|
||||||
]
|
]
|
||||||
@ -1784,6 +1734,6 @@ class ServicesScreenMixin:
|
|||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.root.after(1000, lambda: self.show_page("init_db_build"))
|
self.root.after(1000, lambda: self.show_page("init_scripts"))
|
||||||
|
|
||||||
threading.Thread(target=worker, daemon=True).start()
|
threading.Thread(target=worker, daemon=True).start()
|
||||||
|
|||||||
@ -5,7 +5,7 @@ metadata:
|
|||||||
name: prole-db
|
name: prole-db
|
||||||
spec:
|
spec:
|
||||||
instances: 3
|
instances: 3
|
||||||
imageName: k3d-prole-registry:5000/prole-db:18-070
|
imageName: k3d-prole-registry:5000/prole-db:18-089
|
||||||
postgresUID: 100
|
postgresUID: 100
|
||||||
postgresGID: 101
|
postgresGID: 101
|
||||||
maxSyncReplicas: 1
|
maxSyncReplicas: 1
|
||||||
|
|||||||
@ -4,7 +4,7 @@ metadata:
|
|||||||
name: prole-db
|
name: prole-db
|
||||||
spec:
|
spec:
|
||||||
instances: 3
|
instances: 3
|
||||||
imageName: k3d-prole-registry:5000/prole-db:18-070
|
imageName: k3d-prole-registry:5000/prole-db:18-089
|
||||||
postgresUID: 100
|
postgresUID: 100
|
||||||
postgresGID: 101
|
postgresGID: 101
|
||||||
maxSyncReplicas: 1
|
maxSyncReplicas: 1
|
||||||
|
|||||||
132
mock_val/common_core_lib.sh
Normal file
132
mock_val/common_core_lib.sh
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Shared helpers for common core init scripts (ArgoCD, OpenBao, OpenTofu, Garage).
|
||||||
|
# Responsibilities:
|
||||||
|
# - handle [-c|--config] early so prole_cfg.sh loads the right prole.cfg
|
||||||
|
# - parse standard actions/options
|
||||||
|
# - resolve and apply namespaces consistently
|
||||||
|
|
||||||
|
# Actions supported by all common core scripts
|
||||||
|
COMMON_CORE_ACTIONS="start|stop|status|restart|initialize|update|reload"
|
||||||
|
|
||||||
|
# Parse -c/--config before loading prole_cfg.sh so PROLE_CONF is set in time.
|
||||||
|
# Sets:
|
||||||
|
# COMMON_CORE_CONFIG_PATH - path to prole.cfg (if provided)
|
||||||
|
# COMMON_CORE_ARGS - original arguments minus -c/--config
|
||||||
|
common_core_preparse_config() {
|
||||||
|
COMMON_CORE_CONFIG_PATH=""
|
||||||
|
COMMON_CORE_ARGS=()
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-c|--config)
|
||||||
|
shift
|
||||||
|
if [[ -z "${1:-}" ]]; then
|
||||||
|
echo "ERROR: -c/--config requires a file path" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
COMMON_CORE_CONFIG_PATH="$1"
|
||||||
|
;;
|
||||||
|
-c=*|--config=*)
|
||||||
|
COMMON_CORE_CONFIG_PATH="${1#*=}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMMON_CORE_ARGS+=("$1")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "$COMMON_CORE_CONFIG_PATH" ]]; then
|
||||||
|
if [[ ! -f "$COMMON_CORE_CONFIG_PATH" ]]; then
|
||||||
|
echo "ERROR: config file not found: $COMMON_CORE_CONFIG_PATH" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
local cfg_dir
|
||||||
|
cfg_dir=$(cd "$(dirname "$COMMON_CORE_CONFIG_PATH")" && pwd)
|
||||||
|
PROLE_CONF="$cfg_dir"
|
||||||
|
export PROLE_CONF
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse standard options and action.
|
||||||
|
# Sets (always):
|
||||||
|
# COMMON_CORE_ACTION
|
||||||
|
# COMMON_CORE_NAMESPACE
|
||||||
|
# COMMON_CORE_HELP (0/1)
|
||||||
|
# COMMON_CORE_PARSE_ERROR (empty or message)
|
||||||
|
common_core_parse_args() {
|
||||||
|
COMMON_CORE_ACTION=""
|
||||||
|
COMMON_CORE_NAMESPACE=""
|
||||||
|
COMMON_CORE_HELP=0
|
||||||
|
COMMON_CORE_PARSE_ERROR=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-m|--mode)
|
||||||
|
shift
|
||||||
|
prole_set_mode "${1:-}"
|
||||||
|
;;
|
||||||
|
-m=*|--mode=*)
|
||||||
|
prole_set_mode "${1#*=}"
|
||||||
|
;;
|
||||||
|
-n|--namespace)
|
||||||
|
shift
|
||||||
|
if [[ -z "${1:-}" ]]; then
|
||||||
|
COMMON_CORE_PARSE_ERROR="--namespace requires a value"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
COMMON_CORE_NAMESPACE="$1"
|
||||||
|
;;
|
||||||
|
-n=*|--namespace=*)
|
||||||
|
COMMON_CORE_NAMESPACE="${1#*=}"
|
||||||
|
;;
|
||||||
|
start|stop|status|restart|initialize|update|reload)
|
||||||
|
COMMON_CORE_ACTION="$1"
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
COMMON_CORE_HELP=1
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ -z "$COMMON_CORE_ACTION" && "$1" != -* ]]; then
|
||||||
|
COMMON_CORE_ACTION="$1"
|
||||||
|
else
|
||||||
|
COMMON_CORE_PARSE_ERROR="Unknown argument: $1"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$COMMON_CORE_ACTION" && "$COMMON_CORE_HELP" -eq 0 && -z "$COMMON_CORE_PARSE_ERROR" ]]; then
|
||||||
|
COMMON_CORE_PARSE_ERROR="Action is required (${COMMON_CORE_ACTIONS//|/, })"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
common_core_usage() {
|
||||||
|
local prog="${1:-$0}"
|
||||||
|
echo "Usage: ${prog##*/} [${COMMON_CORE_ACTIONS//|/|}] [-n namespace] [--mode k3d|k3s|k8s] [-c conf/prole.cfg]" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve namespace with precedence: CLI override -> SERVICE_NAMESPACE -> NAMESPACE -> provided default
|
||||||
|
common_core_resolve_namespace() {
|
||||||
|
local default_ns="${1:-default}"
|
||||||
|
local ns="${COMMON_CORE_NAMESPACE:-}"
|
||||||
|
[[ -z "$ns" && -n "${SERVICE_NAMESPACE:-}" ]] && ns="$SERVICE_NAMESPACE"
|
||||||
|
[[ -z "$ns" && -n "${NAMESPACE:-}" ]] && ns="$NAMESPACE"
|
||||||
|
[[ -z "$ns" ]] && ns="$default_ns"
|
||||||
|
echo "$ns"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply a resolved namespace to both NAMESPACE and SERVICE_NAMESPACE for consistency
|
||||||
|
common_core_apply_namespace() {
|
||||||
|
local ns="$1"
|
||||||
|
if [[ -n "$ns" ]]; then
|
||||||
|
NAMESPACE="$ns"
|
||||||
|
SERVICE_NAMESPACE="$ns"
|
||||||
|
export NAMESPACE SERVICE_NAMESPACE
|
||||||
|
fi
|
||||||
|
}
|
||||||
2
mock_val/etc/home.sh
Executable file
2
mock_val/etc/home.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo hometest
|
||||||
2
mock_val/home.sh
Executable file
2
mock_val/home.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo hometest
|
||||||
170
mock_val/init_argocd.sh
Executable file
170
mock_val/init_argocd.sh
Executable file
@ -0,0 +1,170 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# init_argocd.sh
|
||||||
|
# Deploy and manage ArgoCD as part of the common core stack.
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
|
||||||
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
|
set -- "${COMMON_CORE_ARGS[@]}"
|
||||||
|
common_core_parse_args "$@"
|
||||||
|
|
||||||
|
if [[ "${COMMON_CORE_HELP:-0}" == 1 ]]; then
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${COMMON_CORE_PARSE_ERROR:-}" ]]; then
|
||||||
|
echo "ERROR: ${COMMON_CORE_PARSE_ERROR}" >&2
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTION="$COMMON_CORE_ACTION"
|
||||||
|
|
||||||
|
if [[ -d "$SCRIPT_DIR/../k8s/argocd" ]]; then
|
||||||
|
ARGOCD_MANIFEST_DIR="$SCRIPT_DIR/../k8s/argocd"
|
||||||
|
elif [[ -n "${PROLE_HOME:-}" && -d "$PROLE_HOME/k8s/argocd" ]]; then
|
||||||
|
ARGOCD_MANIFEST_DIR="$PROLE_HOME/k8s/argocd"
|
||||||
|
else
|
||||||
|
ARGOCD_MANIFEST_DIR="$SCRIPT_DIR/../k8s/argocd"
|
||||||
|
fi
|
||||||
|
ARGOCD_MANIFEST_FILE=${ARGOCD_MANIFEST_FILE:-"$ARGOCD_MANIFEST_DIR/install.yaml"}
|
||||||
|
|
||||||
|
ARGOCD_NAMESPACE_DEFAULT="${ARGOCD_NAMESPACE:-argocd}"
|
||||||
|
ARGOCD_NAMESPACE="$(common_core_resolve_namespace "$ARGOCD_NAMESPACE_DEFAULT")"
|
||||||
|
common_core_apply_namespace "$ARGOCD_NAMESPACE"
|
||||||
|
export ARGOCD_NAMESPACE
|
||||||
|
|
||||||
|
ARGOCD_LABEL_SELECTOR=${ARGOCD_LABEL_SELECTOR:-"app.kubernetes.io/part-of=argocd"}
|
||||||
|
ARGOCD_SERVER_SERVICE=${ARGOCD_SERVER_SERVICE:-argocd-server}
|
||||||
|
ARGOCD_PORT_FORWARD_LOCAL=${ARGOCD_PORT_FORWARD_LOCAL:-8081}
|
||||||
|
ARGOCD_PORT_FORWARD_REMOTE=${ARGOCD_PORT_FORWARD_REMOTE:-80}
|
||||||
|
ARGOCD_NODE_SELECTOR=${ARGOCD_NODE_SELECTOR:-}
|
||||||
|
|
||||||
|
ensure_tools() {
|
||||||
|
command -v kubectl >/dev/null || { echo "Missing required tool: kubectl" >&2; exit 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_namespace() {
|
||||||
|
if ! kubectl get namespace "$ARGOCD_NAMESPACE" >/dev/null 2>&1; then
|
||||||
|
echo "Creating namespace '$ARGOCD_NAMESPACE' ..."
|
||||||
|
kubectl create namespace "$ARGOCD_NAMESPACE" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
render_manifest() {
|
||||||
|
if [[ ! -f "$ARGOCD_MANIFEST_FILE" ]]; then
|
||||||
|
echo "ERROR: ArgoCD manifest not found: $ARGOCD_MANIFEST_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
prole_render_manifest "$ARGOCD_MANIFEST_FILE" | sed "s|\\${ARGOCD_NAMESPACE}|$ARGOCD_NAMESPACE|g"
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_argocd_node_selector() {
|
||||||
|
if [[ -n "$ARGOCD_NODE_SELECTOR" ]]; then
|
||||||
|
printf '%s' "$ARGOCD_NODE_SELECTOR"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local node=""
|
||||||
|
node=$(kubectl get nodes -l 'node-role.kubernetes.io/control-plane' -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
|
||||||
|
if [[ -n "$node" ]]; then
|
||||||
|
printf 'kubernetes.io/hostname=%s' "$node"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_argocd_node_selector() {
|
||||||
|
local selector
|
||||||
|
selector="$(resolve_argocd_node_selector)"
|
||||||
|
if [[ -z "$selector" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local key="${selector%%=*}"
|
||||||
|
local val="${selector#*=}"
|
||||||
|
if [[ -z "$key" || -z "$val" ]]; then
|
||||||
|
echo "WARN: invalid ARGOCD_NODE_SELECTOR '$selector' (expected key=value); skipping." >&2
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
for target in \
|
||||||
|
deployment/argocd-applicationset-controller \
|
||||||
|
deployment/argocd-dex-server \
|
||||||
|
deployment/argocd-notifications-controller \
|
||||||
|
deployment/argocd-repo-server \
|
||||||
|
deployment/argocd-server \
|
||||||
|
deployment/argocd-redis \
|
||||||
|
statefulset/argocd-application-controller; do
|
||||||
|
kubectl -n "$ARGOCD_NAMESPACE" patch "$target" \
|
||||||
|
--type merge \
|
||||||
|
-p "{\"spec\":{\"template\":{\"spec\":{\"nodeSelector\":{\"$key\":\"$val\"}}}}}" >/dev/null 2>&1 || true
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_argocd() {
|
||||||
|
echo "Applying ArgoCD manifest to namespace '$ARGOCD_NAMESPACE' ..."
|
||||||
|
render_manifest | kubectl apply --server-side --force-conflicts --field-manager=prole-installer --validate=false -n "$ARGOCD_NAMESPACE" -f -
|
||||||
|
apply_argocd_node_selector
|
||||||
|
kubectl rollout status deploy/argocd-server -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true
|
||||||
|
kubectl rollout status deploy/argocd-repo-server -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true
|
||||||
|
kubectl rollout status deploy/argocd-dex-server -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true
|
||||||
|
kubectl rollout status deploy/argocd-applicationset-controller -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true
|
||||||
|
kubectl rollout status deploy/argocd-notifications-controller -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true
|
||||||
|
kubectl rollout status deploy/argocd-redis -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true
|
||||||
|
kubectl rollout status statefulset/argocd-application-controller -n "$ARGOCD_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s} || true
|
||||||
|
}
|
||||||
|
|
||||||
|
rollout_restart_argocd() {
|
||||||
|
kubectl -n "$ARGOCD_NAMESPACE" rollout restart deploy/argocd-server deploy/argocd-repo-server \
|
||||||
|
deploy/argocd-dex-server deploy/argocd-applicationset-controller deploy/argocd-notifications-controller \
|
||||||
|
deploy/argocd-redis >/dev/null 2>&1 || true
|
||||||
|
kubectl -n "$ARGOCD_NAMESPACE" rollout restart statefulset/argocd-application-controller >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_argocd() {
|
||||||
|
echo "Removing ArgoCD resources from namespace '$ARGOCD_NAMESPACE' ..."
|
||||||
|
render_manifest | kubectl delete -n "$ARGOCD_NAMESPACE" -f - --ignore-not-found
|
||||||
|
}
|
||||||
|
|
||||||
|
status_argocd() {
|
||||||
|
kubectl -n "$ARGOCD_NAMESPACE" get deploy,statefulset,svc -l "$ARGOCD_LABEL_SELECTOR" 2>/dev/null || true
|
||||||
|
kubectl -n "$ARGOCD_NAMESPACE" get pods -l "$ARGOCD_LABEL_SELECTOR" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
start|initialize|update|reload)
|
||||||
|
ensure_tools
|
||||||
|
ensure_namespace
|
||||||
|
apply_argocd
|
||||||
|
prole_register_port_forward "argocd" "$ARGOCD_NAMESPACE" "svc/${ARGOCD_SERVER_SERVICE}" \
|
||||||
|
"$ARGOCD_PORT_FORWARD_LOCAL" "$ARGOCD_PORT_FORWARD_REMOTE" "0.0.0.0" "TCP" "ArgoCD"
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
ensure_tools
|
||||||
|
ensure_namespace
|
||||||
|
apply_argocd
|
||||||
|
rollout_restart_argocd
|
||||||
|
prole_register_port_forward "argocd" "$ARGOCD_NAMESPACE" "svc/${ARGOCD_SERVER_SERVICE}" \
|
||||||
|
"$ARGOCD_PORT_FORWARD_LOCAL" "$ARGOCD_PORT_FORWARD_REMOTE" "0.0.0.0" "TCP" "ArgoCD"
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
ensure_tools
|
||||||
|
delete_argocd
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
ensure_tools
|
||||||
|
status_argocd
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
154
mock_val/init_certmgr.sh
Executable file
154
mock_val/init_certmgr.sh
Executable file
@ -0,0 +1,154 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# init_certmgr.sh
|
||||||
|
# Purpose:
|
||||||
|
# - Install or manage cert-manager for ACME certificates
|
||||||
|
# - Provide start/stop/status/restart/update actions
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
|
||||||
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
# Inject default config if not provided
|
||||||
|
_has_config=0
|
||||||
|
for _arg in "$@"; do
|
||||||
|
[[ "$_arg" == "-c" || "$_arg" == "--config" || "$_arg" == -c=* || "$_arg" == --config=* ]] && _has_config=1
|
||||||
|
done
|
||||||
|
if [[ $_has_config -eq 0 && -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then
|
||||||
|
set -- "-c" "$SCRIPT_DIR/../conf/prole.cfg" "$@"
|
||||||
|
fi
|
||||||
|
unset _has_config _arg
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
|
set -- "${COMMON_CORE_ARGS[@]}"
|
||||||
|
common_core_parse_args "$@"
|
||||||
|
|
||||||
|
if [[ -z "${PROLE_MODE:-}" ]]; then
|
||||||
|
export PROLE_MODE="k3s"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${COMMON_CORE_HELP:-0}" == 1 ]]; then
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${COMMON_CORE_PARSE_ERROR:-}" ]]; then
|
||||||
|
echo "ERROR: ${COMMON_CORE_PARSE_ERROR}" >&2
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTION="$COMMON_CORE_ACTION"
|
||||||
|
# cert-manager always runs in its own dedicated namespace; never follows the
|
||||||
|
# common-services namespace override.
|
||||||
|
CERTMGR_NAMESPACE="cert-manager"
|
||||||
|
common_core_apply_namespace "$CERTMGR_NAMESPACE"
|
||||||
|
|
||||||
|
CERTMGR_VERSION="${CERTMGR_VERSION:-v1.14.6}"
|
||||||
|
CERTMGR_RELEASE_URL="https://github.com/cert-manager/cert-manager/releases/download/${CERTMGR_VERSION}/cert-manager.yaml"
|
||||||
|
CERTMGR_PF_ADDR="${CERTMGR_PF_ADDR:-127.0.0.1}"
|
||||||
|
if [[ "${PROLE_MODE:-}" == "k3s" ]]; then
|
||||||
|
CERTMGR_PF_ADDR="${CERTMGR_PF_ADDR:-0.0.0.0}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<USAGE
|
||||||
|
Usage: $0 [--mode MODE] [-n NAMESPACE] [${COMMON_CORE_ACTIONS//|/|}] [-c conf/prole.cfg]
|
||||||
|
|
||||||
|
Actions:
|
||||||
|
start Install cert-manager (CRDs + controller)
|
||||||
|
stop Remove cert-manager resources
|
||||||
|
status Show cert-manager deployment status
|
||||||
|
restart Restart cert-manager deployments
|
||||||
|
update Install or update cert-manager resources
|
||||||
|
USAGE
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_tools() {
|
||||||
|
for t in kubectl curl; do
|
||||||
|
command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; }
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_namespace() {
|
||||||
|
if ! kubectl get namespace "$CERTMGR_NAMESPACE" >/dev/null 2>&1; then
|
||||||
|
echo "Creating namespace '$CERTMGR_NAMESPACE' ..."
|
||||||
|
kubectl create namespace "$CERTMGR_NAMESPACE" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_apply_manifest() {
|
||||||
|
curl -fsSL "$CERTMGR_RELEASE_URL" | kubectl apply -f -
|
||||||
|
}
|
||||||
|
|
||||||
|
_is_already_deployed() {
|
||||||
|
kubectl -n "$CERTMGR_NAMESPACE" get deployment cert-manager cert-manager-cainjector cert-manager-webhook >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
_delete_manifest() {
|
||||||
|
curl -fsSL "$CERTMGR_RELEASE_URL" | kubectl delete -f - --ignore-not-found=true
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_rollout() {
|
||||||
|
kubectl -n "$CERTMGR_NAMESPACE" rollout status deployment/cert-manager --timeout=180s
|
||||||
|
kubectl -n "$CERTMGR_NAMESPACE" rollout status deployment/cert-manager-cainjector --timeout=180s
|
||||||
|
kubectl -n "$CERTMGR_NAMESPACE" rollout status deployment/cert-manager-webhook --timeout=180s
|
||||||
|
}
|
||||||
|
|
||||||
|
status() {
|
||||||
|
echo "=== cert-manager deployments (${CERTMGR_NAMESPACE}) ==="
|
||||||
|
kubectl -n "$CERTMGR_NAMESPACE" get deploy cert-manager cert-manager-cainjector cert-manager-webhook 2>/dev/null || echo "No deployments found"
|
||||||
|
echo ""
|
||||||
|
echo "=== cert-manager pods (${CERTMGR_NAMESPACE}) ==="
|
||||||
|
kubectl -n "$CERTMGR_NAMESPACE" get pods 2>/dev/null || echo "No pods found"
|
||||||
|
}
|
||||||
|
|
||||||
|
restart() {
|
||||||
|
echo "Restarting cert-manager deployments in namespace '$CERTMGR_NAMESPACE' ..."
|
||||||
|
kubectl -n "$CERTMGR_NAMESPACE" rollout restart deployment/cert-manager
|
||||||
|
kubectl -n "$CERTMGR_NAMESPACE" rollout restart deployment/cert-manager-cainjector
|
||||||
|
kubectl -n "$CERTMGR_NAMESPACE" rollout restart deployment/cert-manager-webhook
|
||||||
|
wait_rollout
|
||||||
|
}
|
||||||
|
|
||||||
|
action_update() {
|
||||||
|
ensure_tools
|
||||||
|
ensure_namespace
|
||||||
|
if _is_already_deployed; then
|
||||||
|
echo "cert-manager already deployed in namespace '$CERTMGR_NAMESPACE'; skipping manifest apply."
|
||||||
|
else
|
||||||
|
_apply_manifest
|
||||||
|
wait_rollout
|
||||||
|
fi
|
||||||
|
prole_register_port_forward "certmgr" "$CERTMGR_NAMESPACE" "svc/cert-manager-webhook" "9443" "9443" "${CERTMGR_PF_ADDR}" "TCP" "CertManager Webhook"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
start|initialize|update|reload)
|
||||||
|
action_update
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
ensure_tools
|
||||||
|
_delete_manifest
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
ensure_tools
|
||||||
|
status
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
ensure_tools
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@ -204,8 +204,8 @@ openbao_url() {
|
|||||||
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:8200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
|
elif curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:18200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
@ -577,6 +577,137 @@ apply_prole_manifest_file() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Push image to local registry (via LOCAL_REGISTRY host address) with k3d import fallback.
|
||||||
|
_push_to_k3d_registry() {
|
||||||
|
local image="$1"
|
||||||
|
local cluster_name="$2"
|
||||||
|
local push_host="${LOCAL_REGISTRY:-localhost:5000}"
|
||||||
|
local plain_image="${image##*/}" # strip registry prefix, e.g. prole-db:18-088
|
||||||
|
|
||||||
|
if [[ -n "$push_host" ]]; then
|
||||||
|
local push_ref="${push_host}/${plain_image}"
|
||||||
|
docker tag "$image" "$push_ref" 2>/dev/null || true
|
||||||
|
if docker push "$push_ref" 2>/dev/null; then
|
||||||
|
echo " ✓ Image pushed to registry at '${push_host}'."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo " WARN: push to '${push_host}' failed; falling back to k3d image import ..." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if k3d image import "$image" -c "$cluster_name" 2>/dev/null; then
|
||||||
|
echo " ✓ Image '$image' imported directly into k3d cluster '$cluster_name'."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "ERROR: Failed to push or import image '$image'." >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pre-flight: ensure the prole-db image is available in the k3d cluster before
|
||||||
|
# the CNPG operator ever tries to pull it, avoiding ErrImagePull backoff loops.
|
||||||
|
# Steps: containerd cache → Docker daemon (registry tag) → Docker daemon (plain tag)
|
||||||
|
# → tar import → docker build + push/import.
|
||||||
|
_ensure_prole_db_image() {
|
||||||
|
[[ "${PROLE_MODE:-}" != "k3d" ]] && return 0
|
||||||
|
|
||||||
|
local image_override="${CNPG_IMAGE:-${PROLE_DB_IMAGE:-}}"
|
||||||
|
local image=""
|
||||||
|
if [[ -n "$image_override" ]]; then
|
||||||
|
image="$image_override"
|
||||||
|
else
|
||||||
|
if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then
|
||||||
|
image=$(get_latest_image)
|
||||||
|
else
|
||||||
|
image="prole-db:$VERSION"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
image=$(resolve_cnpg_image "$image")
|
||||||
|
|
||||||
|
# Auto-detect active k3d cluster name
|
||||||
|
local cluster_name="${K3D_CLUSTER_NAME:-}"
|
||||||
|
if [[ -z "$cluster_name" ]]; then
|
||||||
|
cluster_name=$(k3d cluster list --no-headers 2>/dev/null | awk '{print $1}' | head -1)
|
||||||
|
fi
|
||||||
|
cluster_name="${cluster_name:-prole-dev-cluster}"
|
||||||
|
|
||||||
|
local prole_db_dir="${PROLE_HOME:-$SCRIPT_DIR/..}/prole-db"
|
||||||
|
local plain_image="${image##*/}" # e.g. prole-db:18-088
|
||||||
|
|
||||||
|
echo "Pre-flight: verifying image '$image' is available in k3d cluster '$cluster_name' ..."
|
||||||
|
|
||||||
|
# Step 1: check if already present in k3d containerd with matching digest
|
||||||
|
local containerd_digest local_digest containerd_sha
|
||||||
|
containerd_digest=$(docker exec "k3d-${cluster_name}-server-0" \
|
||||||
|
ctr images ls -q 2>/dev/null | grep -F "$image" | head -1 || true)
|
||||||
|
if [[ -n "$containerd_digest" ]]; then
|
||||||
|
local_digest=$(docker inspect --format='{{index .RepoDigests 0}}' "$image" 2>/dev/null \
|
||||||
|
| awk -F@ '{print $2}' || true)
|
||||||
|
containerd_sha=$(docker exec "k3d-${cluster_name}-server-0" \
|
||||||
|
ctr images ls 2>/dev/null | grep -F "$image" | awk '{print $3}' | head -1 || true)
|
||||||
|
if [[ -z "$local_digest" || "$containerd_sha" == "$local_digest" ]]; then
|
||||||
|
echo " ✓ Image '$image' already in k3d containerd (digest match); no import needed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo " Image '$image' in containerd but digest mismatch (local: ${local_digest:-unknown}, containerd: ${containerd_sha:-unknown}); re-importing ..."
|
||||||
|
docker exec "k3d-${cluster_name}-server-0" ctr images rm "$image" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 2a: registry-tagged image in local Docker daemon → push + import
|
||||||
|
if docker image inspect "$image" >/dev/null 2>&1; then
|
||||||
|
echo " Image '$image' found in Docker daemon; pushing to registry ..."
|
||||||
|
_push_to_k3d_registry "$image" "$cluster_name"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 2b: plain-tagged image in local Docker daemon → tag + push + import
|
||||||
|
if docker image inspect "$plain_image" >/dev/null 2>&1; then
|
||||||
|
echo " Plain image '$plain_image' found in Docker daemon; tagging as '$image' and pushing ..."
|
||||||
|
docker tag "$plain_image" "$image"
|
||||||
|
_push_to_k3d_registry "$image" "$cluster_name"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 3: look for a matching tar in the docker-import directory
|
||||||
|
local prole_data="${PROLE_DATA:-$HOME/.prole/data}"
|
||||||
|
local docker_import_dir="${DOCKER_IMPORT_DIR:-${prole_data}/docker-import}"
|
||||||
|
local name_part="${plain_image%%:*}" # e.g. prole-db
|
||||||
|
local tag_part="${plain_image##*:}" # e.g. 18-088
|
||||||
|
local found_tar=""
|
||||||
|
if [[ -d "$docker_import_dir" ]]; then
|
||||||
|
for _t in "$docker_import_dir"/*.tar; do
|
||||||
|
[[ -f "$_t" ]] || continue
|
||||||
|
local _bn
|
||||||
|
_bn=$(basename "$_t" .tar)
|
||||||
|
if [[ "$_bn" == *"$name_part"* && "$_bn" == *"$tag_part"* ]]; then
|
||||||
|
found_tar="$_t"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [[ -n "$found_tar" ]]; then
|
||||||
|
echo " Loading tar '$(basename "$found_tar")' into Docker daemon ..."
|
||||||
|
docker load -i "$found_tar"
|
||||||
|
docker tag "$plain_image" "$image" 2>/dev/null || true
|
||||||
|
_push_to_k3d_registry "$image" "$cluster_name"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 4: image not found anywhere — build from source then push + import
|
||||||
|
if [[ ! -f "$prole_db_dir/Dockerfile" ]]; then
|
||||||
|
echo "ERROR: Dockerfile not found in '$prole_db_dir'; cannot build prole-db image." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo " Image '$image' not found in k3d, Docker daemon, or docker-import dir."
|
||||||
|
echo " Building prole-db image from '$prole_db_dir' ..."
|
||||||
|
if ! docker build -t "$plain_image" "$prole_db_dir"; then
|
||||||
|
echo "ERROR: docker build failed for image '$plain_image'." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
docker tag "$plain_image" "$image"
|
||||||
|
echo " Build complete. Pushing '$image' to registry ..."
|
||||||
|
_push_to_k3d_registry "$image" "$cluster_name"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
ensure_prole_stack_resources() {
|
ensure_prole_stack_resources() {
|
||||||
echo "Applying CloudNative-PG cluster and related resources ..."
|
echo "Applying CloudNative-PG cluster and related resources ..."
|
||||||
local image_override="${CNPG_IMAGE:-${PROLE_DB_IMAGE:-}}"
|
local image_override="${CNPG_IMAGE:-${PROLE_DB_IMAGE:-}}"
|
||||||
@ -726,6 +857,7 @@ wait_for_cnpg_pods() {
|
|||||||
echo " Timeout: ${timeout}s"
|
echo " Timeout: ${timeout}s"
|
||||||
local last_feedback=0
|
local last_feedback=0
|
||||||
local feedback_interval=30 # print detailed status every 30s
|
local feedback_interval=30 # print detailed status every 30s
|
||||||
|
local _image_pull_remediated="" # set after first k3d import attempt to avoid loops
|
||||||
while true; do
|
while true; do
|
||||||
local now elapsed
|
local now elapsed
|
||||||
now=$(date +%s)
|
now=$(date +%s)
|
||||||
@ -735,13 +867,47 @@ wait_for_cnpg_pods() {
|
|||||||
pods=$(kubectl -n "$NAMESPACE" get pods -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" --no-headers 2>/dev/null || true)
|
pods=$(kubectl -n "$NAMESPACE" get pods -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" --no-headers 2>/dev/null || true)
|
||||||
|
|
||||||
if [[ -n "$pods" ]]; then
|
if [[ -n "$pods" ]]; then
|
||||||
# Check for Error or CrashLoopBackOff
|
# Check for Error or CrashLoopBackOff (terminal failures)
|
||||||
if echo "$pods" | grep -E "Error|CrashLoopBackOff" >/dev/null; then
|
if echo "$pods" | grep -E "Error|CrashLoopBackOff" >/dev/null; then
|
||||||
echo "ERROR: Some CNPG pods are in Error or CrashLoopBackOff state:" >&2
|
echo "ERROR: Some CNPG pods are in Error or CrashLoopBackOff state:" >&2
|
||||||
echo "$pods" | grep -E "Error|CrashLoopBackOff" >&2
|
echo "$pods" | grep -E "Error|CrashLoopBackOff" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check for image pull failures — attempt remediation in k3d mode
|
||||||
|
if echo "$pods" | grep -E "ErrImagePull|ImagePullBackOff" >/dev/null; then
|
||||||
|
if [[ "${PROLE_MODE:-}" == "k3d" ]]; then
|
||||||
|
if [[ -z "$_image_pull_remediated" ]]; then
|
||||||
|
_image_pull_remediated=1
|
||||||
|
local fail_pod fail_image
|
||||||
|
fail_pod=$(echo "$pods" | grep -E "ErrImagePull|ImagePullBackOff" | awk '{print $1}' | head -1)
|
||||||
|
# Try containers then initContainers
|
||||||
|
fail_image=$(kubectl -n "$NAMESPACE" get pod "$fail_pod" \
|
||||||
|
-o jsonpath='{.spec.containers[0].image}' 2>/dev/null || true)
|
||||||
|
if [[ -z "$fail_image" ]]; then
|
||||||
|
fail_image=$(kubectl -n "$NAMESPACE" get pod "$fail_pod" \
|
||||||
|
-o jsonpath='{.spec.initContainers[0].image}' 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
echo "WARN: Pod '$fail_pod' cannot pull image '${fail_image:-unknown}' (ErrImagePull/ImagePullBackOff in k3d mode)." >&2
|
||||||
|
import_dir="${DOCKER_IMPORT_DIR:-${PROLE_DATA:+${PROLE_DATA}/docker-import}}"
|
||||||
|
cluster_name="${K3D_CLUSTER_NAME:-}"
|
||||||
|
if [[ -z "$cluster_name" ]]; then
|
||||||
|
cluster_name=$(k3d cluster list --no-headers 2>/dev/null | awk '{print $1}' | head -1)
|
||||||
|
fi
|
||||||
|
cluster_name="${cluster_name:-prole-dev-cluster}"
|
||||||
|
# Re-run full image ensure logic to push/import the failing image
|
||||||
|
echo " Attempting image remediation via _ensure_prole_db_image ..." >&2
|
||||||
|
_ensure_prole_db_image >&2 || true
|
||||||
|
echo " Remediation complete; resuming wait ..." >&2
|
||||||
|
fi
|
||||||
|
# Continue the wait loop — do not return 1
|
||||||
|
else
|
||||||
|
echo "ERROR: Image pull failure in non-k3d mode — cannot auto-recover:" >&2
|
||||||
|
echo "$pods" | grep -E "ErrImagePull|ImagePullBackOff" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Count Running pods by name (exclude initdb)
|
# Count Running pods by name (exclude initdb)
|
||||||
local running_pods
|
local running_pods
|
||||||
running_pods=$(kubectl -n "$NAMESPACE" get pods --no-headers 2>/dev/null \
|
running_pods=$(kubectl -n "$NAMESPACE" get pods --no-headers 2>/dev/null \
|
||||||
@ -1131,8 +1297,8 @@ bao_service_url() {
|
|||||||
echo "http://$OPENBAO_NAME.${OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-default}}.svc.cluster.local:8200"
|
echo "http://$OPENBAO_NAME.${OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-default}}.svc.cluster.local:8200"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
|
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:18200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
@ -1432,6 +1598,11 @@ initialize() {
|
|||||||
fi
|
fi
|
||||||
echo "Pre-flight check passed: all required secrets present."
|
echo "Pre-flight check passed: all required secrets present."
|
||||||
|
|
||||||
|
if ! _ensure_prole_db_image; then
|
||||||
|
echo "ERROR: Pre-flight image check failed; aborting cluster initialization." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
ensure_prole_stack_resources
|
ensure_prole_stack_resources
|
||||||
|
|
||||||
if ! wait_for_cnpg_pods 300; then
|
if ! wait_for_cnpg_pods 300; then
|
||||||
@ -1486,6 +1657,7 @@ deploy_cluster() {
|
|||||||
generate_tls_if_missing
|
generate_tls_if_missing
|
||||||
|
|
||||||
echo "Deploying $image to cluster $CNPG_CLUSTER_NAME in namespace $NAMESPACE..."
|
echo "Deploying $image to cluster $CNPG_CLUSTER_NAME in namespace $NAMESPACE..."
|
||||||
|
_ensure_prole_db_image || true
|
||||||
apply_cnpg_cluster_manifest "$CNPG_MANIFEST"
|
apply_cnpg_cluster_manifest "$CNPG_MANIFEST"
|
||||||
|
|
||||||
local current_image
|
local current_image
|
||||||
@ -1503,6 +1675,20 @@ case "$ACTION" in
|
|||||||
recreate)
|
recreate)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
"$0" delete "$CNPG_CLUSTER_NAME"
|
"$0" delete "$CNPG_CLUSTER_NAME"
|
||||||
|
# Clean up PVCs left behind by the CNPG operator (not in static manifests)
|
||||||
|
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
|
||||||
|
echo "Waiting for pods to terminate in namespace '$NAMESPACE' ..."
|
||||||
|
_wait_term=0
|
||||||
|
while kubectl -n "$NAMESPACE" get pods --no-headers 2>/dev/null | grep -qv '^No resources'; do
|
||||||
|
sleep 3
|
||||||
|
_wait_term=$(( _wait_term + 3 ))
|
||||||
|
if (( _wait_term >= 60 )); then
|
||||||
|
echo "WARN: Pods still present after 60s; proceeding anyway." >&2
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
"$0" create "$CNPG_CLUSTER_NAME"
|
"$0" create "$CNPG_CLUSTER_NAME"
|
||||||
;;
|
;;
|
||||||
create)
|
create)
|
||||||
@ -1513,7 +1699,20 @@ case "$ACTION" in
|
|||||||
delete)
|
delete)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
echo "Deleting all resources for '$CNPG_CLUSTER_NAME' ..."
|
echo "Deleting all resources for '$CNPG_CLUSTER_NAME' ..."
|
||||||
kubectl delete -n "$NAMESPACE" -k "$SCRIPT_DIR/../k8s/prole" --ignore-not-found
|
# Delete each manifest individually, mirroring the apply pattern:
|
||||||
|
# ingress.yaml contains multi-namespace resources and must be deleted without -n.
|
||||||
|
for _del_f in "$SCRIPT_DIR/../k8s/prole"/*.yaml; do
|
||||||
|
_del_base=$(basename "$_del_f")
|
||||||
|
case "$_del_base" in
|
||||||
|
kustomization.yaml|ingress.yaml) continue ;;
|
||||||
|
*) kubectl delete -n "$NAMESPACE" -f "$_del_f" --ignore-not-found 2>&1 \
|
||||||
|
| grep -v "^Error from server (NotFound)" || true ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
if [[ -f "$SCRIPT_DIR/../k8s/prole/ingress.yaml" ]]; then
|
||||||
|
kubectl delete -f "$SCRIPT_DIR/../k8s/prole/ingress.yaml" --ignore-not-found 2>&1 \
|
||||||
|
| grep -v "^Error from server (NotFound)" || true
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
start)
|
start)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
|
|||||||
70
mock_val/init_common_core.sh
Executable file
70
mock_val/init_common_core.sh
Executable file
@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# init_common_core.sh
|
||||||
|
# Launch wrapper that runs all common core services (OpenBao, OpenTofu, Garage, ArgoCD)
|
||||||
|
# with a unified interface.
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
|
set -- "${COMMON_CORE_ARGS[@]}"
|
||||||
|
common_core_parse_args "$@"
|
||||||
|
|
||||||
|
if [[ "${COMMON_CORE_HELP:-0}" == 1 ]]; then
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${COMMON_CORE_PARSE_ERROR:-}" ]]; then
|
||||||
|
echo "ERROR: ${COMMON_CORE_PARSE_ERROR}" >&2
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTION="$COMMON_CORE_ACTION"
|
||||||
|
|
||||||
|
TARGET_NAMESPACE="$(common_core_resolve_namespace "default")"
|
||||||
|
common_core_apply_namespace "$TARGET_NAMESPACE"
|
||||||
|
|
||||||
|
# Forwarded option bundles for child scripts
|
||||||
|
NS_ARGS=("-n" "$TARGET_NAMESPACE")
|
||||||
|
if [[ -n "${COMMON_CORE_NAMESPACE:-}" ]]; then
|
||||||
|
NS_ARGS=("-n" "$COMMON_CORE_NAMESPACE")
|
||||||
|
fi
|
||||||
|
MODE_ARGS=()
|
||||||
|
if [[ -n "${PROLE_MODE:-}" ]]; then
|
||||||
|
MODE_ARGS=("--mode" "$PROLE_MODE")
|
||||||
|
fi
|
||||||
|
CFG_ARGS=()
|
||||||
|
if [[ -n "${COMMON_CORE_CONFIG_PATH:-}" ]]; then
|
||||||
|
CFG_ARGS=("-c" "$COMMON_CORE_CONFIG_PATH")
|
||||||
|
fi
|
||||||
|
|
||||||
|
COMPONENTS=(
|
||||||
|
"openbao"
|
||||||
|
"opentofu"
|
||||||
|
"garage_store"
|
||||||
|
"argocd"
|
||||||
|
)
|
||||||
|
|
||||||
|
for comp in "${COMPONENTS[@]}"; do
|
||||||
|
script="$SCRIPT_DIR/init_${comp}.sh"
|
||||||
|
if [[ ! -x "$script" ]]; then
|
||||||
|
echo "WARN: missing script $script; skipping." >&2
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
echo ">>> init_${comp}.sh ${ACTION} (namespace=${NS_ARGS[1]})"
|
||||||
|
if ! "$script" "$ACTION" "${NS_ARGS[@]}" "${MODE_ARGS[@]}" "${CFG_ARGS[@]}"; then
|
||||||
|
echo "ERROR: init_${comp}.sh ${ACTION} failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
@ -2,14 +2,30 @@
|
|||||||
set -u
|
set -u
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
# Inject default config if not provided
|
||||||
|
_has_config=0
|
||||||
|
for _arg in "$@"; do
|
||||||
|
[[ "$_arg" == "-c" || "$_arg" == "--config" || "$_arg" == -c=* || "$_arg" == --config=* ]] && _has_config=1
|
||||||
|
done
|
||||||
|
if [[ $_has_config -eq 0 && -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then
|
||||||
|
set -- "-c" "$SCRIPT_DIR/../conf/prole.cfg" "$@"
|
||||||
|
fi
|
||||||
|
unset _has_config _arg
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: init_common_services.sh [-n|--namespace NS] [-k|--kerberos] <update|start|status>
|
Usage: init_common_services.sh [-n|--namespace NS] [-k|--kerberos] <update|start|status|verify>
|
||||||
|
|
||||||
Deploys common infrastructure services (ArgoCD, OpenTofu, Garage)
|
Deploys common infrastructure services (ArgoCD, OpenTofu, Garage, OpenBao, Kong, Cert-Manager)
|
||||||
into the given Kubernetes namespace. Use -k to include the Kerberos/KDC service.
|
into the given Kubernetes namespace. Use -k to include the Kerberos/KDC service.
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@ -20,6 +36,10 @@ ENABLE_KERBEROS=0
|
|||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
-c|--config)
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
;;
|
||||||
-m|--mode)
|
-m|--mode)
|
||||||
shift
|
shift
|
||||||
prole_set_mode "${1:-}"
|
prole_set_mode "${1:-}"
|
||||||
@ -42,8 +62,9 @@ while [ $# -gt 0 ]; do
|
|||||||
ENABLE_KERBEROS=1
|
ENABLE_KERBEROS=1
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
update|start|status)
|
update|start|status|verify|repair|reload|restart)
|
||||||
ACTION="$1"
|
ACTION="$1"
|
||||||
|
[[ "$ACTION" == "repair" ]] && ACTION="update"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h|--help)
|
||||||
@ -69,6 +90,41 @@ REGISTRY_NS="${REGISTRY_NAMESPACE:-default}"
|
|||||||
|
|
||||||
prole_ensure_kubeconfig >/dev/null 2>&1 || true
|
prole_ensure_kubeconfig >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Check cluster reachability early to fail fast
|
||||||
|
if [[ "$ACTION" != "status" ]]; then
|
||||||
|
if ! kubectl cluster-info >/dev/null 2>&1; then
|
||||||
|
echo "ERROR: Cluster not reachable. Check KUBECONFIG and cluster status." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
_verify_common_services() {
|
||||||
|
local status_script="$SCRIPT_DIR/status_common_services.sh"
|
||||||
|
if [[ ! -x "$status_script" ]]; then
|
||||||
|
echo "ERROR: status_common_services.sh not found at $status_script" >&2
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
local args=(-n "$NS")
|
||||||
|
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||||
|
args+=(-k)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Verifying common services in namespace '$NS' ..."
|
||||||
|
"$status_script" "${args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Short-circuit verify before any deploy/reload/preload work
|
||||||
|
if [[ -n "${COMMON_SERVICES_INIT_LOG:-}" ]]; then
|
||||||
|
mkdir -p "$(dirname "$COMMON_SERVICES_INIT_LOG")"
|
||||||
|
exec > >(tee -a "$COMMON_SERVICES_INIT_LOG") 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ACTION" == "verify" ]]; then
|
||||||
|
_verify_common_services
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Pre-import container images from $PROLE_DATA/docker-import/*.tar
|
# Pre-import container images from $PROLE_DATA/docker-import/*.tar
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@ -91,6 +147,80 @@ _resolve_prole_data() {
|
|||||||
echo "${d:-}"
|
echo "${d:-}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Standard console execution element.
|
||||||
|
# Runs a command silently (output captured), displays a live elapsed-time footer
|
||||||
|
# on stderr when attached to a TTY, prints elapsed time on completion, and tails
|
||||||
|
# captured output for diagnostics on failure.
|
||||||
|
_exec_with_progress_footer() {
|
||||||
|
local label="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
local start_time
|
||||||
|
start_time=$(date +%s)
|
||||||
|
|
||||||
|
local tmp_out
|
||||||
|
tmp_out=$(mktemp 2>/dev/null || echo "/tmp/_prole_exec_$$.out")
|
||||||
|
|
||||||
|
# Spawn live footer only when stderr is a real TTY
|
||||||
|
local spinner_pid=""
|
||||||
|
if [[ -t 2 ]]; then
|
||||||
|
(
|
||||||
|
while true; do
|
||||||
|
now=$(date +%s)
|
||||||
|
elapsed=$(( now - start_time ))
|
||||||
|
h=$(( elapsed / 3600 ))
|
||||||
|
m=$(( (elapsed % 3600) / 60 ))
|
||||||
|
s=$(( elapsed % 60 ))
|
||||||
|
printf "\r\033[2K [%02d:%02d:%02d elapsed] %s" "$h" "$m" "$s" "$label" >&2
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
spinner_pid=$!
|
||||||
|
disown "$spinner_pid" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$@" >"$tmp_out" 2>&1
|
||||||
|
local rc=$?
|
||||||
|
|
||||||
|
if [[ -n "$spinner_pid" ]]; then
|
||||||
|
kill "$spinner_pid" 2>/dev/null || true
|
||||||
|
wait "$spinner_pid" 2>/dev/null || true
|
||||||
|
printf "\r\033[2K" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
local end_time elapsed_total h m s
|
||||||
|
end_time=$(date +%s)
|
||||||
|
elapsed_total=$(( end_time - start_time ))
|
||||||
|
h=$(( elapsed_total / 3600 ))
|
||||||
|
m=$(( (elapsed_total % 3600) / 60 ))
|
||||||
|
s=$(( elapsed_total % 60 ))
|
||||||
|
printf " [%02dh:%02dm:%02ds elapsed] %s\n" "$h" "$m" "$s" "$label"
|
||||||
|
|
||||||
|
if [[ $rc -ne 0 && -s "$tmp_out" ]]; then
|
||||||
|
echo " [Last output]:" >&2
|
||||||
|
tail -n 10 "$tmp_out" >&2
|
||||||
|
fi
|
||||||
|
rm -f "$tmp_out"
|
||||||
|
return $rc
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract the primary image ref from a Docker tarball's manifest.json.
|
||||||
|
_image_name_from_tar() {
|
||||||
|
local tar_file="$1"
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
python3 - "$tar_file" <<'PYEOF' 2>/dev/null
|
||||||
|
import json, sys, tarfile
|
||||||
|
try:
|
||||||
|
with tarfile.open(sys.argv[1]) as t:
|
||||||
|
tags = json.load(t.extractfile("manifest.json"))[0].get("RepoTags") or []
|
||||||
|
if tags:
|
||||||
|
print(tags[0])
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
PYEOF
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_preload_docker_images() {
|
_preload_docker_images() {
|
||||||
local prole_data
|
local prole_data
|
||||||
prole_data=$(_resolve_prole_data)
|
prole_data=$(_resolve_prole_data)
|
||||||
@ -118,54 +248,186 @@ _preload_docker_images() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
local cluster_name="${K3D_CLUSTER_NAME:-prole-dev-cluster}"
|
local cluster_name="${K3D_CLUSTER_NAME:-prole-dev-cluster}"
|
||||||
local tar_file
|
|
||||||
while IFS= read -r tar_file; do
|
# ── Step 1: build indexed arrays of tar paths + resolved image names ──
|
||||||
echo " Importing $(basename "$tar_file") into k3d cluster '$cluster_name' ..."
|
local -a tar_paths=()
|
||||||
if k3d image import "$tar_file" -c "$cluster_name" >/dev/null 2>&1; then
|
local -a tar_images=()
|
||||||
echo " [OK] $(basename "$tar_file")"
|
local _tf
|
||||||
else
|
while IFS= read -r _tf; do
|
||||||
echo " [WARN] Failed to import $(basename "$tar_file"); image will be pulled at deploy time." >&2
|
[[ -z "$_tf" ]] && continue
|
||||||
fi
|
tar_paths+=("$_tf")
|
||||||
|
tar_images+=("$(_image_name_from_tar "$_tf")")
|
||||||
done <<< "$tar_files"
|
done <<< "$tar_files"
|
||||||
|
local total_tars=${#tar_paths[@]}
|
||||||
|
|
||||||
|
if [[ $total_tars -eq 0 ]]; then
|
||||||
|
echo " [SKIP] No tar files to process."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Step 2: query images already in the k3d cluster ───────────────────
|
||||||
|
echo " Checking k3d cluster '$cluster_name' for existing images ..."
|
||||||
|
local existing_images=""
|
||||||
|
local node_name="k3d-${cluster_name}-server-0"
|
||||||
|
if docker inspect "$node_name" >/dev/null 2>&1; then
|
||||||
|
existing_images=$(docker exec "$node_name" \
|
||||||
|
ctr --address /run/k3s/containerd/containerd.sock \
|
||||||
|
--namespace k8s.io images ls -q 2>/dev/null || true)
|
||||||
|
else
|
||||||
|
echo " [WARN] k3d node '$node_name' not reachable; will import all images."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Step 3: delta — split into already-present vs needs-import ─────────
|
||||||
|
local -a to_import_tars=()
|
||||||
|
local -a to_import_images=()
|
||||||
|
local -a already_present=()
|
||||||
|
local _i
|
||||||
|
for (( _i=0; _i<total_tars; _i++ )); do
|
||||||
|
local _tar="${tar_paths[$_i]}"
|
||||||
|
local _img="${tar_images[$_i]}"
|
||||||
|
local _found=0
|
||||||
|
if [[ -n "$_img" && -n "$existing_images" ]]; then
|
||||||
|
# Strip digest suffix before matching
|
||||||
|
local _img_ref="${_img%%@*}"
|
||||||
|
if echo "$existing_images" | grep -qF "$_img_ref"; then
|
||||||
|
_found=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ $_found -eq 1 ]]; then
|
||||||
|
already_present+=("$(basename "$_tar")${_img:+ → $_img}")
|
||||||
|
else
|
||||||
|
to_import_tars+=("$_tar")
|
||||||
|
to_import_images+=("${_img:-}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Step 4: report delta to user ───────────────────────────────────────
|
||||||
|
if [[ ${#already_present[@]} -gt 0 ]]; then
|
||||||
|
echo " Already in cluster — skipping ${#already_present[@]} image(s):"
|
||||||
|
local _entry
|
||||||
|
for _entry in "${already_present[@]}"; do
|
||||||
|
echo " ✓ $_entry"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
local import_count=${#to_import_tars[@]}
|
||||||
|
if [[ $import_count -eq 0 ]]; then
|
||||||
|
echo " [OK] All images already present in k3d cluster '$cluster_name'; nothing to import."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " Bulk uploading $import_count image(s) into k3d cluster '$cluster_name':"
|
||||||
|
for (( _i=0; _i<import_count; _i++ )); do
|
||||||
|
local _lbl="${to_import_images[$_i]:-$(basename "${to_import_tars[$_i]}")}"
|
||||||
|
echo " → $(basename "${to_import_tars[$_i]}")${_lbl:+ ($_lbl)}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Step 5: bulk import with live progress footer ──────────────────────
|
||||||
|
local overall_start elapsed_bulk
|
||||||
|
overall_start=$(date +%s)
|
||||||
|
|
||||||
|
if _exec_with_progress_footer \
|
||||||
|
"k3d image import — bulk ($import_count images) → $cluster_name" \
|
||||||
|
k3d image import "${to_import_tars[@]}" -c "$cluster_name"; then
|
||||||
|
elapsed_bulk=$(( $(date +%s) - overall_start ))
|
||||||
|
local bm=$(( elapsed_bulk / 60 )) bs=$(( elapsed_bulk % 60 ))
|
||||||
|
echo " [OK] Bulk import of $import_count image(s) complete (${bm}m${bs}s)."
|
||||||
|
else
|
||||||
|
echo " [WARN] Bulk import failed; falling back to sequential import." >&2
|
||||||
|
local _idx
|
||||||
|
for (( _i=0; _i<import_count; _i++ )); do
|
||||||
|
_idx=$(( _i + 1 ))
|
||||||
|
local _tar="${to_import_tars[$_i]}"
|
||||||
|
local _img="${to_import_images[$_i]:-$(basename "$_tar")}"
|
||||||
|
|
||||||
|
# ETA based on average time per completed image
|
||||||
|
local _now _elapsed_so_far _eta_str=""
|
||||||
|
_now=$(date +%s)
|
||||||
|
_elapsed_so_far=$(( _now - overall_start ))
|
||||||
|
if [[ $_i -gt 0 ]]; then
|
||||||
|
local _avg=$(( _elapsed_so_far / _i ))
|
||||||
|
local _remaining=$(( _avg * (import_count - _i) ))
|
||||||
|
local _rm=$(( _remaining / 60 )) _rs=$(( _remaining % 60 ))
|
||||||
|
_eta_str=" | ETA ~${_rm}m${_rs}s"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " [$_idx/$import_count] $(basename "$_tar")${_img:+ ($_img)}${_eta_str}"
|
||||||
|
local _item_start
|
||||||
|
_item_start=$(date +%s)
|
||||||
|
if _exec_with_progress_footer \
|
||||||
|
"k3d image import [$_idx/$import_count] $(basename "$_tar")" \
|
||||||
|
k3d image import "$_tar" -c "$cluster_name"; then
|
||||||
|
local _item_elapsed=$(( $(date +%s) - _item_start ))
|
||||||
|
echo " [OK] $(basename "$_tar") (${_item_elapsed}s)"
|
||||||
|
else
|
||||||
|
echo " [WARN] Failed to import $(basename "$_tar"); will pull at deploy time." >&2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
local _total_elapsed=$(( $(date +%s) - overall_start ))
|
||||||
|
local _te_m=$(( _total_elapsed / 60 )) _te_s=$(( _total_elapsed % 60 ))
|
||||||
|
echo " [DONE] Sequential import complete. Total: ${_te_m}m${_te_s}s"
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
k3s)
|
k3s)
|
||||||
# For k3s running from macOS: load tarballs into Docker, then push to
|
# For k3s: copy tarballs directly to k3s nodes and import via
|
||||||
# the in-cluster registry:2. Falls back to Ansible ctr import if the
|
# `k3s ctr images import`. This avoids the slow docker-push-to-registry
|
||||||
# registry push fails.
|
# flow that can stall for minutes per image layer.
|
||||||
local registry="${LOCAL_REGISTRY:-}"
|
local playbook_dir="$SCRIPT_DIR/../infrastructure/playbooks"
|
||||||
if [[ -z "$registry" ]]; then
|
local playbook="$playbook_dir/k3s_import_images.yml"
|
||||||
# Derive from k3s server URL
|
|
||||||
local _srv="${PROLE_K3S_SERVER:-}"
|
if [[ -f "$playbook" ]]; then
|
||||||
if [[ -n "$_srv" ]]; then
|
# Build JSON array of objects for Ansible extra-vars
|
||||||
_srv="${_srv#https://}"; _srv="${_srv#http://}"; _srv="${_srv%%/*}"; _srv="${_srv%%:*}"
|
local images_json="["
|
||||||
[[ -n "$_srv" ]] && registry="${_srv}:5000"
|
local first=1
|
||||||
fi
|
local tar_file
|
||||||
fi
|
while IFS= read -r tar_file; do
|
||||||
local tar_file
|
[[ -z "$tar_file" ]] && continue
|
||||||
while IFS= read -r tar_file; do
|
|
||||||
local base_name
|
# Extract image name from tarball manifest on host
|
||||||
base_name=$(basename "$tar_file" .tar)
|
local img_name=""
|
||||||
echo " Loading $(basename "$tar_file") into local Docker ..."
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
local loaded_image=""
|
img_name=$(python3 -c "import json, tarfile;
|
||||||
loaded_image=$(docker load -i "$tar_file" 2>/dev/null | sed -n 's/^Loaded image: //p' || true)
|
try:
|
||||||
if [[ -z "$loaded_image" ]]; then
|
with tarfile.open('$tar_file') as tar:
|
||||||
# Try to derive image name from tarball filename (safe_name convention)
|
m = json.load(tar.extractfile('manifest.json'))
|
||||||
loaded_image="${base_name//_//}"
|
print(m[0]['RepoTags'][0])
|
||||||
loaded_image="${loaded_image%/*}:${loaded_image##*/}"
|
except Exception:
|
||||||
fi
|
pass" 2>/dev/null || true)
|
||||||
if [[ -n "$registry" && -n "$loaded_image" ]]; then
|
|
||||||
local remote_tag="${registry}/${loaded_image}"
|
|
||||||
docker tag "$loaded_image" "$remote_tag" 2>/dev/null || true
|
|
||||||
echo " Pushing $remote_tag to registry ..."
|
|
||||||
if docker push "$remote_tag" 2>/dev/null; then
|
|
||||||
echo " [OK] $(basename "$tar_file") → $remote_tag"
|
|
||||||
continue
|
|
||||||
fi
|
fi
|
||||||
echo " [WARN] Registry push failed; image will be pulled at deploy time." >&2
|
|
||||||
else
|
# Fallback to safe_name derivation if python failed or not available
|
||||||
echo " [WARN] No registry configured; image will be pulled at deploy time." >&2
|
if [[ -z "$img_name" ]]; then
|
||||||
|
local base_name=$(basename "$tar_file" .tar)
|
||||||
|
img_name="${base_name//_//}"
|
||||||
|
img_name="${img_name%/*}:${img_name##*/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ $first -eq 0 ]] && images_json+=","
|
||||||
|
images_json+="{\"path\": \"$tar_file\", \"image\": \"$img_name\"}"
|
||||||
|
first=0
|
||||||
|
echo " Queued: $(basename "$tar_file") ${img_name:+(image: $img_name)}"
|
||||||
|
done <<< "$tar_files"
|
||||||
|
images_json+="]"
|
||||||
|
|
||||||
|
echo " Importing images into k3s nodes via Ansible ..."
|
||||||
|
local ansible_cmd=(ansible-playbook)
|
||||||
|
local vault_pass="$SCRIPT_DIR/../.vault_pass"
|
||||||
|
if [[ -f "$vault_pass" ]]; then
|
||||||
|
ansible_cmd+=(--vault-password-file "$vault_pass")
|
||||||
|
elif [[ -n "${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then
|
||||||
|
ansible_cmd+=(--vault-password-file "$ANSIBLE_VAULT_PASSWORD_FILE")
|
||||||
fi
|
fi
|
||||||
done <<< "$tar_files"
|
ansible_cmd+=(-e "{\"k3s_import_images\": $images_json}")
|
||||||
|
ansible_cmd+=("$playbook")
|
||||||
|
|
||||||
|
if "${ansible_cmd[@]}"; then
|
||||||
|
echo " [OK] All images checked/imported into k3s nodes."
|
||||||
|
else
|
||||||
|
echo " [WARN] Ansible import failed; images will be pulled at deploy time." >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " [WARN] Ansible playbook not found ($playbook); images will be pulled at deploy time." >&2
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "WARN: Unknown mode '$mode'; skipping docker-import pre-load." >&2
|
echo "WARN: Unknown mode '$mode'; skipping docker-import pre-load." >&2
|
||||||
@ -176,7 +438,7 @@ _preload_docker_images() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "$ACTION" in
|
case "$ACTION" in
|
||||||
start|update|reload|initialize|restart)
|
start|update|reload|initialize|restart|repair)
|
||||||
_preload_docker_images
|
_preload_docker_images
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@ -190,12 +452,9 @@ if [ -x "$SCRIPT_DIR/init_service_layer.sh" ]; then
|
|||||||
exit $?
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "${COMMON_SERVICES_INIT_LOG:-}" ]]; then
|
|
||||||
mkdir -p "$(dirname "$COMMON_SERVICES_INIT_LOG")"
|
|
||||||
exec > >(tee -a "$COMMON_SERVICES_INIT_LOG") 2>&1
|
|
||||||
fi
|
|
||||||
|
|
||||||
OPENTOFU_NAME=${OPENTOFU_NAME:-opentofu}
|
OPENTOFU_NAME=${OPENTOFU_NAME:-opentofu}
|
||||||
|
OPENBAO_NAME=${OPENBAO_NAME:-openbao}
|
||||||
ARGOCD_SERVER_NAME=${ARGOCD_SERVER_NAME:-argocd-server}
|
ARGOCD_SERVER_NAME=${ARGOCD_SERVER_NAME:-argocd-server}
|
||||||
GARAGE_NAME=${GARAGE_NAME:-garage}
|
GARAGE_NAME=${GARAGE_NAME:-garage}
|
||||||
OPENTOFU_CONFIGMAP=${OPENTOFU_CONFIGMAP:-opentofu-nginx}
|
OPENTOFU_CONFIGMAP=${OPENTOFU_CONFIGMAP:-opentofu-nginx}
|
||||||
@ -256,6 +515,17 @@ migrate_common_services() {
|
|||||||
kubectl delete -n "$old_ns" configmap "$GARAGE_CONFIGMAP" --ignore-not-found >/dev/null 2>&1 || true
|
kubectl delete -n "$old_ns" configmap "$GARAGE_CONFIGMAP" --ignore-not-found >/dev/null 2>&1 || true
|
||||||
kubectl delete -n "$old_ns" secret "$GARAGE_SECRET_NAME" --ignore-not-found >/dev/null 2>&1 || true
|
kubectl delete -n "$old_ns" secret "$GARAGE_SECRET_NAME" --ignore-not-found >/dev/null 2>&1 || true
|
||||||
done
|
done
|
||||||
|
|
||||||
|
for old_ns in $(collect_other_namespaces "$NS" "$OPENBAO_NAME" deployment service statefulset); do
|
||||||
|
echo "Found OpenBao in namespace '$old_ns'; removing before deploy to '$NS' ..."
|
||||||
|
if [ -x "$SCRIPT_DIR/init_openbao.sh" ]; then
|
||||||
|
"$SCRIPT_DIR/init_openbao.sh" -n "$old_ns" stop || true
|
||||||
|
else
|
||||||
|
kubectl delete -n "$old_ns" deploy "$OPENBAO_NAME" --ignore-not-found >/dev/null 2>&1 || true
|
||||||
|
kubectl delete -n "$old_ns" statefulset "$OPENBAO_NAME" --ignore-not-found >/dev/null 2>&1 || true
|
||||||
|
kubectl delete -n "$old_ns" svc "$OPENBAO_NAME" --ignore-not-found >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "Deploying common services (namespace=$NS, action=$ACTION)"
|
echo "Deploying common services (namespace=$NS, action=$ACTION)"
|
||||||
@ -263,7 +533,7 @@ echo "Deploying common services (namespace=$NS, action=$ACTION)"
|
|||||||
rc=0
|
rc=0
|
||||||
|
|
||||||
case "$ACTION" in
|
case "$ACTION" in
|
||||||
start|update|reload|initialize|restart)
|
start|update|reload|initialize|restart|status)
|
||||||
migrate_common_services
|
migrate_common_services
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@ -274,6 +544,13 @@ else
|
|||||||
echo "WARN: init_opentofu.sh not found; skipping OpenTofu."
|
echo "WARN: init_opentofu.sh not found; skipping OpenTofu."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -x "$SCRIPT_DIR/init_openbao.sh" ]; then
|
||||||
|
OPENBAO_NAMESPACE="$NS" SERVICE_NAMESPACE="$NS" \
|
||||||
|
"$SCRIPT_DIR/init_openbao.sh" -n "$NS" "$ACTION" || rc=$?
|
||||||
|
else
|
||||||
|
echo "WARN: init_openbao.sh not found; skipping OpenBao."
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -x "$SCRIPT_DIR/init_registry.sh" ]; then
|
if [ -x "$SCRIPT_DIR/init_registry.sh" ]; then
|
||||||
ARGOCD_NAMESPACE="$ARGOCD_NS" REGISTRY_NAMESPACE="$REGISTRY_NS" \
|
ARGOCD_NAMESPACE="$ARGOCD_NS" REGISTRY_NAMESPACE="$REGISTRY_NS" \
|
||||||
"$SCRIPT_DIR/init_registry.sh" -n "$ARGOCD_NS" --registry-namespace "$REGISTRY_NS" "$ACTION" || rc=$?
|
"$SCRIPT_DIR/init_registry.sh" -n "$ARGOCD_NS" --registry-namespace "$REGISTRY_NS" "$ACTION" || rc=$?
|
||||||
|
|||||||
43
mock_val/init_database_options.sh
Executable file
43
mock_val/init_database_options.sh
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# PROLE_HOME should be set by the caller
|
||||||
|
PROLE_CONF_DIR="${PROLE_HOME:-$HOME/.prole}/conf"
|
||||||
|
mkdir -p "$PROLE_CONF_DIR"
|
||||||
|
VERSION_FILE="$PROLE_CONF_DIR/database_versions.json"
|
||||||
|
|
||||||
|
echo "Fetching latest database versions..."
|
||||||
|
|
||||||
|
# Fetch latest PG version from Docker Hub (best effort)
|
||||||
|
get_latest_pg() {
|
||||||
|
curl -s https://registry.hub.docker.com/v2/repositories/library/postgres/tags?page_size=100 | \
|
||||||
|
grep -o '"name": *"[0-9][0-9]*"' | cut -d'"' -f4 | sort -ur | head -n 1 || echo "17"
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_LATEST=$(get_latest_pg)
|
||||||
|
PG_CURRENT=$((PG_LATEST - 1))
|
||||||
|
PG_STABLE=$((PG_LATEST - 2))
|
||||||
|
|
||||||
|
# For Percona, let's use similar logic or defaults
|
||||||
|
PERCONA_LATEST="$PG_LATEST"
|
||||||
|
PERCONA_CURRENT="$PG_CURRENT"
|
||||||
|
PERCONA_STABLE="$PG_STABLE"
|
||||||
|
PERCONA_18="18"
|
||||||
|
|
||||||
|
cat <<EOF > "$VERSION_FILE"
|
||||||
|
{
|
||||||
|
"postgresql": {
|
||||||
|
"stable": "$PG_STABLE",
|
||||||
|
"current": "$PG_CURRENT",
|
||||||
|
"latest": "$PG_LATEST"
|
||||||
|
},
|
||||||
|
"percona": {
|
||||||
|
"stable": "$PERCONA_STABLE",
|
||||||
|
"current": "$PERCONA_CURRENT",
|
||||||
|
"latest": "$PERCONA_LATEST",
|
||||||
|
"v18": "$PERCONA_18"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Database versions updated in $VERSION_FILE"
|
||||||
@ -10,38 +10,44 @@ set -euo pipefail
|
|||||||
# Initialize SCRIPT_DIR
|
# Initialize SCRIPT_DIR
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
|
||||||
# Load environment and config via prole_cfg.sh
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
if [[ "${1:-}" == "--mode" || "${1:-}" == "-m" ]]; then
|
set -- "${COMMON_CORE_ARGS[@]}"
|
||||||
prole_set_mode "${2:-}"
|
common_core_parse_args "$@"
|
||||||
shift 2
|
|
||||||
elif [[ "${1:-}" == --mode=* || "${1:-}" == -m=* ]]; then
|
if [[ "${COMMON_CORE_HELP:-0}" == 1 ]]; then
|
||||||
prole_set_mode "${1#*=}"
|
common_core_usage "$0"
|
||||||
shift
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ACTION=${1:-}
|
if [[ -n "${COMMON_CORE_PARSE_ERROR:-}" ]]; then
|
||||||
|
echo "ERROR: ${COMMON_CORE_PARSE_ERROR}" >&2
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTION="$COMMON_CORE_ACTION"
|
||||||
|
|
||||||
if [[ -n "${GARAGE_INIT_LOG:-}" ]]; then
|
if [[ -n "${GARAGE_INIT_LOG:-}" ]]; then
|
||||||
mkdir -p "$(dirname "$GARAGE_INIT_LOG")"
|
mkdir -p "$(dirname "$GARAGE_INIT_LOG")"
|
||||||
exec > >(tee -a "$GARAGE_INIT_LOG") 2>&1
|
exec > >(tee -a "$GARAGE_INIT_LOG") 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
RESOLVED_NAMESPACE="$(common_core_resolve_namespace "default")"
|
||||||
|
common_core_apply_namespace "$RESOLVED_NAMESPACE"
|
||||||
|
|
||||||
GARAGE_NAME=${GARAGE_NAME:-garage}
|
GARAGE_NAME=${GARAGE_NAME:-garage}
|
||||||
GARAGE_SECRET_NAME=${GARAGE_SECRET_NAME:-garage-secrets}
|
GARAGE_SECRET_NAME=${GARAGE_SECRET_NAME:-garage-secrets}
|
||||||
GARAGE_NODE_CAPACITY=${GARAGE_NODE_CAPACITY:-10GB}
|
GARAGE_NODE_CAPACITY=${GARAGE_NODE_CAPACITY:-10GB}
|
||||||
GARAGE_ZONE=${GARAGE_ZONE:-local}
|
GARAGE_ZONE=${GARAGE_ZONE:-local}
|
||||||
NAMESPACE=${NAMESPACE:-default}
|
GARAGE_NAMESPACE=${GARAGE_NAMESPACE:-$RESOLVED_NAMESPACE}
|
||||||
SERVICE_NAMESPACE=${SERVICE_NAMESPACE:-}
|
|
||||||
if [[ -z "${GARAGE_NAMESPACE:-}" ]]; then
|
|
||||||
if [[ -n "$SERVICE_NAMESPACE" ]]; then
|
|
||||||
GARAGE_NAMESPACE="$SERVICE_NAMESPACE"
|
|
||||||
else
|
|
||||||
GARAGE_NAMESPACE="$NAMESPACE"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
NAMESPACE="$GARAGE_NAMESPACE"
|
NAMESPACE="$GARAGE_NAMESPACE"
|
||||||
|
|
||||||
# Support both PROLE_HOME/k8s and sibling k8s directory
|
# Support both PROLE_HOME/k8s and sibling k8s directory
|
||||||
@ -72,19 +78,6 @@ GARAGE_APPLY_CHANGED=0
|
|||||||
GARAGE_CONFIG_CHANGED=0
|
GARAGE_CONFIG_CHANGED=0
|
||||||
GARAGE_STATEFULSET_CHANGED=0
|
GARAGE_STATEFULSET_CHANGED=0
|
||||||
|
|
||||||
usage() {
|
|
||||||
cat <<USAGE
|
|
||||||
Usage: $0 [start|stop|restart|status]
|
|
||||||
|
|
||||||
Actions:
|
|
||||||
start Apply Garage manifests and initialize layout
|
|
||||||
stop Delete Garage manifests
|
|
||||||
restart Re-apply Garage manifests and initialize layout
|
|
||||||
status Show Garage resources
|
|
||||||
USAGE
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure_tools() {
|
ensure_tools() {
|
||||||
for t in kubectl openssl; do
|
for t in kubectl openssl; do
|
||||||
command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; }
|
command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; }
|
||||||
@ -384,7 +377,7 @@ status() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "$ACTION" in
|
case "$ACTION" in
|
||||||
start)
|
start|initialize|update|reload|restart)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
ensure_namespace
|
ensure_namespace
|
||||||
ensure_secrets
|
ensure_secrets
|
||||||
@ -403,24 +396,11 @@ case "$ACTION" in
|
|||||||
echo "Deleting Garage manifests from namespace '$NAMESPACE'..."
|
echo "Deleting Garage manifests from namespace '$NAMESPACE'..."
|
||||||
delete_manifests
|
delete_manifests
|
||||||
;;
|
;;
|
||||||
restart)
|
|
||||||
ensure_tools
|
|
||||||
ensure_namespace
|
|
||||||
ensure_secrets
|
|
||||||
k3d_cleanup_pending_pvc
|
|
||||||
echo "Re-applying Garage manifests in namespace '$NAMESPACE'..."
|
|
||||||
apply_manifests
|
|
||||||
ensure_container_command
|
|
||||||
pin_garage_node_k3d
|
|
||||||
restart_statefulset
|
|
||||||
wait_ready
|
|
||||||
init_layout
|
|
||||||
prole_register_port_forward "garage" "${NAMESPACE:-default}" "svc/garage" "3900" "3900" "0.0.0.0" "TCP" "Garage S3 API"
|
|
||||||
;;
|
|
||||||
status)
|
status)
|
||||||
status
|
status
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
common_core_usage "$0"
|
||||||
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
256
mock_val/init_gitea.sh
Executable file
256
mock_val/init_gitea.sh
Executable file
@ -0,0 +1,256 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PROG="init_gitea"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
|
MODE="$(prole_normalize_mode "${PROLE_MODE:-${DEPLOYMENT_MODE:-k3d}}")"
|
||||||
|
NAMESPACE="${GITEA_NAMESPACE:-}"
|
||||||
|
CFG_PATH=""
|
||||||
|
FORCE=0
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage:
|
||||||
|
$PROG [options] [deploy]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--mode <k3d|k3s|k8s|local> Deployment mode (default: ${MODE:-k3d})
|
||||||
|
-n, --namespace <name> Target namespace (default: gitea)
|
||||||
|
-c, --config <prole.cfg> Path to prole.cfg (defaults to detected)
|
||||||
|
--force Delete existing release before deploy
|
||||||
|
--help Show this help
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
- Prefers Helm install/upgrade (gitea-charts/gitea).
|
||||||
|
- Falls back to applying a generated Deployment/Service/Ingress manifest.
|
||||||
|
- If a local registry is enabled, the image is mirrored there first.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
die() { echo "[ERROR] $*" >&2; exit 2; }
|
||||||
|
log() { echo "[INFO] $*"; }
|
||||||
|
warn() { echo "[WARN] $*" >&2; }
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--mode) MODE="$(prole_normalize_mode "${2:-}")"; shift 2 ;;
|
||||||
|
--mode=*) MODE="$(prole_normalize_mode "${1#*=}")"; shift 1 ;;
|
||||||
|
-n|--namespace) NAMESPACE="${2:-}"; shift 2 ;;
|
||||||
|
--namespace=*) NAMESPACE="${1#*=}"; shift 1 ;;
|
||||||
|
-c|--config) CFG_PATH="${2:-}"; shift 2 ;;
|
||||||
|
--config=*) CFG_PATH="${1#*=}"; shift 1 ;;
|
||||||
|
--force) FORCE=1; shift ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Resolve config path and namespace defaults from prole.cfg when present
|
||||||
|
if [[ -z "$CFG_PATH" && -n "${PROLE_CONF:-}" && -f "${PROLE_CONF}/prole.cfg" ]]; then
|
||||||
|
CFG_PATH="${PROLE_CONF}/prole.cfg"
|
||||||
|
elif [[ -z "$CFG_PATH" && -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then
|
||||||
|
CFG_PATH="$SCRIPT_DIR/../conf/prole.cfg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$NAMESPACE" && -n "$CFG_PATH" ]]; then
|
||||||
|
maybe_ns="$(_prole_cfg_extract_key "$CFG_PATH" "GITOPS_NAMESPACE")"
|
||||||
|
[[ -z "$maybe_ns" ]] && maybe_ns="$(_prole_cfg_extract_key "$CFG_PATH" "GITEA_NAMESPACE")"
|
||||||
|
NAMESPACE="$maybe_ns"
|
||||||
|
fi
|
||||||
|
NAMESPACE="${NAMESPACE:-gitea}"
|
||||||
|
export GITEA_NAMESPACE="$NAMESPACE"
|
||||||
|
|
||||||
|
case "$MODE" in
|
||||||
|
k3d|k3s|k8s|local) ;;
|
||||||
|
*) die "Unsupported mode '$MODE' (use k3d, k3s, k8s, or local)" ;;
|
||||||
|
esac
|
||||||
|
export PROLE_MODE="$MODE"
|
||||||
|
|
||||||
|
command -v kubectl >/dev/null || die "kubectl not found"
|
||||||
|
command -v helm >/dev/null || warn "helm not found — will use manifest fallback"
|
||||||
|
|
||||||
|
kubectl get ns "$NAMESPACE" >/dev/null 2>&1 || kubectl create namespace "$NAMESPACE" >/dev/null 2>&1
|
||||||
|
|
||||||
|
CHART_REPO="https://dl.gitea.com/charts"
|
||||||
|
CHART_NAME="gitea-charts/gitea"
|
||||||
|
RELEASE_NAME="gitea"
|
||||||
|
IMAGE_REPO_DEFAULT="gitea/gitea"
|
||||||
|
IMAGE_TAG="${GITEA_IMAGE_TAG:-1.22.3}"
|
||||||
|
IMAGE_REPO="$IMAGE_REPO_DEFAULT"
|
||||||
|
|
||||||
|
# Optional reset
|
||||||
|
if [[ "$FORCE" -eq 1 ]]; then
|
||||||
|
warn "--force specified; removing existing Gitea resources in namespace $NAMESPACE"
|
||||||
|
if command -v helm >/dev/null 2>&1; then
|
||||||
|
helm uninstall "$RELEASE_NAME" -n "$NAMESPACE" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
kubectl -n "$NAMESPACE" delete deploy/gitea svc/gitea-http svc/gitea-ssh ingress/gitea >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Mirror to local registry if enabled
|
||||||
|
if _prole_local_registry_enabled && command -v docker >/dev/null; then
|
||||||
|
HOST_REG="${LOCAL_REGISTRY:-${PROLE_LOCAL_REGISTRY:-}}"
|
||||||
|
INTERNAL_REG="${LOCAL_REGISTRY_INTERNAL:-${PROLE_LOCAL_REGISTRY_INTERNAL:-}}"
|
||||||
|
if [[ -n "$HOST_REG" ]]; then
|
||||||
|
HOST_IMG="${HOST_REG}/${IMAGE_REPO_DEFAULT}"
|
||||||
|
log "Pulling ${IMAGE_REPO_DEFAULT}:${IMAGE_TAG}"
|
||||||
|
docker pull "${IMAGE_REPO_DEFAULT}:${IMAGE_TAG}" >/dev/null
|
||||||
|
log "Tagging ${HOST_IMG}:${IMAGE_TAG}"
|
||||||
|
docker tag "${IMAGE_REPO_DEFAULT}:${IMAGE_TAG}" "${HOST_IMG}:${IMAGE_TAG}"
|
||||||
|
log "Pushing ${HOST_IMG}:${IMAGE_TAG}"
|
||||||
|
docker push "${HOST_IMG}:${IMAGE_TAG}" >/dev/null || warn "Push to local registry failed; continuing with upstream image"
|
||||||
|
if [[ -n "$INTERNAL_REG" ]]; then
|
||||||
|
IMAGE_REPO="${INTERNAL_REG}/gitea/gitea"
|
||||||
|
else
|
||||||
|
IMAGE_REPO="${HOST_REG}/gitea/gitea"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
VALUES_FILE="$(mktemp /tmp/gitea-values.XXXXXX.yaml)"
|
||||||
|
cat >"$VALUES_FILE" <<EOF
|
||||||
|
image:
|
||||||
|
repository: "${IMAGE_REPO}"
|
||||||
|
tag: "${IMAGE_TAG}"
|
||||||
|
service:
|
||||||
|
http:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 3000
|
||||||
|
ssh:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 22
|
||||||
|
gitea:
|
||||||
|
config:
|
||||||
|
server:
|
||||||
|
DOMAIN: ${GITEA_DOMAIN:-git.prole.org}
|
||||||
|
SSH_DOMAIN: ${GITEA_SSH_DOMAIN:-git.prole.org}
|
||||||
|
ROOT_URL: "http://${GITEA_DOMAIN:-git.prole.org}:3000/"
|
||||||
|
ingress:
|
||||||
|
enabled: ${GITEA_INGRESS_ENABLED:-$( [[ "$MODE" == "k3s" || "$MODE" == "k8s" ]] && echo true || echo false )}
|
||||||
|
className: ${GITEA_INGRESS_CLASS:-nginx}
|
||||||
|
hosts:
|
||||||
|
- host: ${GITEA_DOMAIN:-git.prole.org}
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: ${RELEASE_NAME}-http
|
||||||
|
port:
|
||||||
|
number: 3000
|
||||||
|
EOF
|
||||||
|
|
||||||
|
helm_deployed=0
|
||||||
|
if command -v helm >/dev/null; then
|
||||||
|
if ! helm repo list 2>/dev/null | grep -q "gitea-charts"; then
|
||||||
|
helm repo add gitea-charts "$CHART_REPO" >/dev/null 2>&1 || warn "helm repo add failed"
|
||||||
|
fi
|
||||||
|
helm repo update >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
log "Deploying Gitea via Helm to namespace ${NAMESPACE} (mode=${MODE})"
|
||||||
|
set +e
|
||||||
|
helm upgrade --install "$RELEASE_NAME" "$CHART_NAME" \
|
||||||
|
--namespace "$NAMESPACE" \
|
||||||
|
--create-namespace \
|
||||||
|
-f "$VALUES_FILE"
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
if [[ $rc -eq 0 ]]; then
|
||||||
|
helm_deployed=1
|
||||||
|
log "Helm deployment successful."
|
||||||
|
else
|
||||||
|
warn "Helm deployment failed (code $rc); falling back to manifests."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $helm_deployed -eq 0 ]]; then
|
||||||
|
MANIFEST_FILE="$(mktemp /tmp/gitea-manifest.XXXXXX.yaml)"
|
||||||
|
cat >"$MANIFEST_FILE" <<EOF
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: gitea
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: gitea
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: gitea
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: gitea
|
||||||
|
image: ${IMAGE_REPO}:${IMAGE_TAG}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 3000
|
||||||
|
- name: ssh
|
||||||
|
containerPort: 22
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
emptyDir: {}
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: gitea-http
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: gitea
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 3000
|
||||||
|
targetPort: 3000
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: gitea-ssh
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: gitea
|
||||||
|
ports:
|
||||||
|
- name: ssh
|
||||||
|
port: 22
|
||||||
|
targetPort: 22
|
||||||
|
EOF
|
||||||
|
if [[ "$MODE" == "k3s" || "$MODE" == "k8s" ]]; then
|
||||||
|
cat >>"$MANIFEST_FILE" <<EOF
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: gitea
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/proxy-body-size: "64m"
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
rules:
|
||||||
|
- host: git.prole.org
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: gitea-http
|
||||||
|
port:
|
||||||
|
number: 3000
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Applying fallback manifest"
|
||||||
|
kubectl -n "$NAMESPACE" apply -f "$MANIFEST_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure port-forward hints / conflicts handled by caller via _build_required_port_forwards
|
||||||
|
log "Gitea deployment completed in namespace '$NAMESPACE'."
|
||||||
@ -52,7 +52,7 @@ CNPG_WAIT_TIMEOUT=${CNPG_WAIT_TIMEOUT:-300}
|
|||||||
SERVICE_NAMESPACE=${SERVICE_NAMESPACE:-${NAMESPACE:-default}}
|
SERVICE_NAMESPACE=${SERVICE_NAMESPACE:-${NAMESPACE:-default}}
|
||||||
OPENBAO_NAMESPACE=${OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-default}}
|
OPENBAO_NAMESPACE=${OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-default}}
|
||||||
KRB5_AD_NAMESPACE=${KRB5_AD_NAMESPACE:-${SERVICE_NAMESPACE}}
|
KRB5_AD_NAMESPACE=${KRB5_AD_NAMESPACE:-${SERVICE_NAMESPACE}}
|
||||||
OPENBAO_PORT_FORWARD_LOCAL=${OPENBAO_PORT_FORWARD_LOCAL:-18200}
|
OPENBAO_PORT_FORWARD_LOCAL=${OPENBAO_PORT_FORWARD_LOCAL:-8200}
|
||||||
|
|
||||||
log() { printf '%s\n' "$*"; }
|
log() { printf '%s\n' "$*"; }
|
||||||
err() { printf '%s\n' "$*" >&2; }
|
err() { printf '%s\n' "$*" >&2; }
|
||||||
@ -237,8 +237,8 @@ openbao_url() {
|
|||||||
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:8200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
|
elif curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:18200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@ -287,8 +287,8 @@ openbao_url() {
|
|||||||
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:8200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
|
elif curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:18200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@ -1,66 +1,65 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# init_kong.sh
|
# init_kong.sh
|
||||||
# Purpose:
|
# Purpose:
|
||||||
# - Deploy Kong API Gateway (DB-less) into the prole-db namespace
|
# - Deploy Kong API Gateway (DB-less) into the service namespace
|
||||||
# - Replaces the prole nginx deployment as the API endpoint
|
# - Replaces the prole nginx deployment as the API endpoint
|
||||||
# - Routes /backup/* to prole-db-manager
|
# - Routes /backup/* to prole-db-manager
|
||||||
# - Creates the kong declarative config as a ConfigMap
|
# - Creates the kong declarative config as a ConfigMap
|
||||||
# - Applies the kong deployment and service manifests
|
# - Applies the kong deployment and service manifests
|
||||||
# - Provides start/stop/status/restart actions
|
# - Provides start/stop/status/restart actions
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# ./init_kong.sh [--mode MODE] [-n NAMESPACE] <start|stop|status|restart>
|
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
|
||||||
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
# Inject default config if not provided
|
||||||
|
_has_config=0
|
||||||
|
for _arg in "$@"; do
|
||||||
|
[[ "$_arg" == "-c" || "$_arg" == "--config" || "$_arg" == -c=* || "$_arg" == --config=* ]] && _has_config=1
|
||||||
|
done
|
||||||
|
if [[ $_has_config -eq 0 && -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then
|
||||||
|
set -- "-c" "$SCRIPT_DIR/../conf/prole.cfg" "$@"
|
||||||
|
fi
|
||||||
|
unset _has_config _arg
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
if [[ "${1:-}" == "--mode" || "${1:-}" == "-m" ]]; then
|
set -- "${COMMON_CORE_ARGS[@]}"
|
||||||
prole_set_mode "${2:-}"
|
common_core_parse_args "$@"
|
||||||
shift 2
|
|
||||||
elif [[ "${1:-}" == --mode=* || "${1:-}" == -m=* ]]; then
|
if [[ -z "${PROLE_MODE:-}" ]]; then
|
||||||
prole_set_mode "${1#*=}"
|
export PROLE_MODE="k3s"
|
||||||
shift
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
NAMESPACE="${NAMESPACE:-default}"
|
if [[ "${COMMON_CORE_HELP:-0}" == 1 ]]; then
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
if [[ -n "${COMMON_CORE_PARSE_ERROR:-}" ]]; then
|
||||||
case "$1" in
|
echo "ERROR: ${COMMON_CORE_PARSE_ERROR}" >&2
|
||||||
-n|--namespace)
|
common_core_usage "$0"
|
||||||
shift
|
exit 2
|
||||||
if [[ -z "${1:-}" ]]; then
|
fi
|
||||||
echo "ERROR: -n/--namespace requires a value" >&2
|
|
||||||
exit 2
|
ACTION="$COMMON_CORE_ACTION"
|
||||||
fi
|
NAMESPACE="$(common_core_resolve_namespace "default")"
|
||||||
NAMESPACE="$1"
|
common_core_apply_namespace "$NAMESPACE"
|
||||||
;;
|
|
||||||
-n=*|--namespace=*)
|
|
||||||
NAMESPACE="${1#*=}"
|
|
||||||
;;
|
|
||||||
start|stop|status|restart)
|
|
||||||
ACTION="$1"
|
|
||||||
;;
|
|
||||||
-h|--help)
|
|
||||||
usage
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "ERROR: Unknown argument: $1" >&2
|
|
||||||
exit 2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
ACTION="${ACTION:-start}"
|
|
||||||
PROLE_HOME=${PROLE_HOME:-$(cd "$SCRIPT_DIR/.." && pwd)}
|
PROLE_HOME=${PROLE_HOME:-$(cd "$SCRIPT_DIR/.." && pwd)}
|
||||||
KONG_IMAGE="${KONG_IMAGE:-kong:3.9}"
|
KONG_IMAGE="${KONG_IMAGE:-kong:3.9}"
|
||||||
KONG_NAME="${KONG_NAME:-prole-db-kong}"
|
KONG_NAME="${KONG_NAME:-prole-svc-kong}"
|
||||||
KONG_PROXY_PORT="${KONG_PROXY_PORT:-8000}"
|
KONG_PROXY_PORT="${KONG_PROXY_PORT:-8000}"
|
||||||
KONG_ADMIN_PORT="${KONG_ADMIN_PORT:-8001}"
|
KONG_ADMIN_PORT="${KONG_ADMIN_PORT:-8001}"
|
||||||
KONG_CONFIG_NAME="${KONG_CONFIG_NAME:-prole-db-kong-config}"
|
KONG_CONFIG_NAME="${KONG_CONFIG_NAME:-prole-svc-kong-config}"
|
||||||
|
|
||||||
# Upstream service defaults
|
# Upstream service defaults
|
||||||
DB_MANAGER_SERVICE="${DB_MANAGER_SERVICE:-prole-db-manager}"
|
DB_MANAGER_SERVICE="${DB_MANAGER_SERVICE:-prole-db-manager}"
|
||||||
@ -68,13 +67,14 @@ DB_MANAGER_PORT="${DB_MANAGER_PORT:-80}"
|
|||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<USAGE
|
cat <<USAGE
|
||||||
Usage: $0 [--mode MODE] [-n NAMESPACE] <start|stop|status|restart>
|
Usage: $0 [--mode MODE] [-n NAMESPACE] [${COMMON_CORE_ACTIONS//|/|}] [-c conf/prole.cfg]
|
||||||
|
|
||||||
Actions:
|
Actions:
|
||||||
start Create ConfigMap and deploy Kong to the prole-db namespace
|
start Create ConfigMap and deploy Kong
|
||||||
stop Remove Kong deployment, service, and ConfigMap
|
stop Remove Kong deployment, service, and ConfigMap
|
||||||
status Show Kong pod/service status
|
status Show Kong pod/service status
|
||||||
restart Restart Kong pods
|
restart Restart Kong pods
|
||||||
|
update Create or update Kong resources
|
||||||
USAGE
|
USAGE
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ create_kong_config() {
|
|||||||
echo "Creating/updating Kong declarative config '$KONG_CONFIG_NAME' in namespace '$NAMESPACE' ..."
|
echo "Creating/updating Kong declarative config '$KONG_CONFIG_NAME' in namespace '$NAMESPACE' ..."
|
||||||
|
|
||||||
local kong_yml
|
local kong_yml
|
||||||
kong_yml=$(cat <<EOF
|
kong_yml=$(cat <<KONGEOF
|
||||||
_format_version: "3.0"
|
_format_version: "3.0"
|
||||||
_transform: true
|
_transform: true
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ services:
|
|||||||
paths:
|
paths:
|
||||||
- /backup
|
- /backup
|
||||||
strip_path: false
|
strip_path: false
|
||||||
EOF
|
KONGEOF
|
||||||
)
|
)
|
||||||
|
|
||||||
kubectl create configmap "$KONG_CONFIG_NAME" \
|
kubectl create configmap "$KONG_CONFIG_NAME" \
|
||||||
@ -155,12 +155,16 @@ restart() {
|
|||||||
echo "$KONG_NAME restarted."
|
echo "$KONG_NAME restarted."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action_update() {
|
||||||
|
ensure_tools
|
||||||
|
ensure_namespace
|
||||||
|
create_kong_config
|
||||||
|
deploy
|
||||||
|
}
|
||||||
|
|
||||||
case "$ACTION" in
|
case "$ACTION" in
|
||||||
start)
|
start|initialize|update|reload)
|
||||||
ensure_tools
|
action_update
|
||||||
ensure_namespace
|
|
||||||
create_kong_config
|
|
||||||
deploy
|
|
||||||
;;
|
;;
|
||||||
stop)
|
stop)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
|
|||||||
@ -290,8 +290,8 @@ openbao_url() {
|
|||||||
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:8200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
|
elif curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:18200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@ -11,22 +11,65 @@ set -euo pipefail
|
|||||||
# Initialize SCRIPT_DIR
|
# Initialize SCRIPT_DIR
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
|
||||||
# Load environment and config via prole_cfg.sh
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
# Inject default config if not provided
|
||||||
|
_has_config=0
|
||||||
|
for _arg in "$@"; do
|
||||||
|
[[ "$_arg" == "-c" || "$_arg" == "--config" || "$_arg" == -c=* || "$_arg" == --config=* ]] && _has_config=1
|
||||||
|
done
|
||||||
|
if [[ $_has_config -eq 0 && -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then
|
||||||
|
set -- "-c" "$SCRIPT_DIR/../conf/prole.cfg" "$@"
|
||||||
|
fi
|
||||||
|
unset _has_config _arg
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
|
set -- "${COMMON_CORE_ARGS[@]}"
|
||||||
|
common_core_parse_args "$@"
|
||||||
|
|
||||||
|
if [[ -z "${PROLE_MODE:-}" ]]; then
|
||||||
|
export PROLE_MODE="k3s"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${COMMON_CORE_HELP:-0}" == 1 ]]; then
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${COMMON_CORE_PARSE_ERROR:-}" ]]; then
|
||||||
|
echo "ERROR: ${COMMON_CORE_PARSE_ERROR}" >&2
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
PRIMARY_SERVICE_NAMESPACE="${SERVICE_NAMESPACE:-default}"
|
||||||
|
ACTION="$COMMON_CORE_ACTION"
|
||||||
|
NAMESPACE="$(common_core_resolve_namespace "default")"
|
||||||
|
common_core_apply_namespace "$NAMESPACE"
|
||||||
|
|
||||||
if [[ -z "${PROLE_SERVICE:-}" ]]; then
|
if [[ -z "${PROLE_SERVICE:-}" ]]; then
|
||||||
echo "ERROR: PROLE_SERVICE is not defined. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2
|
echo "ERROR: PROLE_SERVICE is not defined. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ACTION=""
|
|
||||||
NAMESPACE_OVERRIDE=""
|
|
||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
OPENBAO_NAME=${OPENBAO_NAME:-openbao}
|
OPENBAO_NAME=${OPENBAO_NAME:-openbao}
|
||||||
OPENBAO_IMAGE=${OPENBAO_IMAGE:-ghcr.io/openbao/openbao:latest}
|
OPENBAO_IMAGE=${OPENBAO_IMAGE:-ghcr.io/openbao/openbao:latest}
|
||||||
OPENBAO_PORT_FORWARD_LOCAL=${OPENBAO_PORT_FORWARD_LOCAL:-18200}
|
OPENBAO_PORT_FORWARD_LOCAL=${OPENBAO_PORT_FORWARD_LOCAL:-8200}
|
||||||
|
OPENBAO_PF_ADDR="${OPENBAO_PF_ADDR:-}"
|
||||||
|
if [[ -z "$OPENBAO_PF_ADDR" ]]; then
|
||||||
|
if [[ "${PROLE_MODE:-}" == "k3s" ]]; then
|
||||||
|
OPENBAO_PF_ADDR="0.0.0.0"
|
||||||
|
else
|
||||||
|
OPENBAO_PF_ADDR="127.0.0.1"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Support both PROLE_HOME/k8s and sibling k8s directory
|
# Support both PROLE_HOME/k8s and sibling k8s directory
|
||||||
if [[ -d "$SCRIPT_DIR/../k8s/openbao" ]]; then
|
if [[ -d "$SCRIPT_DIR/../k8s/openbao" ]]; then
|
||||||
@ -55,55 +98,6 @@ admin_key_priv="$SECRETS_DIR/admin_ed25519.key"
|
|||||||
admin_key_pub="$SECRETS_DIR/admin_ed25519.pub"
|
admin_key_pub="$SECRETS_DIR/admin_ed25519.pub"
|
||||||
root_token_file="$SECRETS_DIR/openbao-root-token"
|
root_token_file="$SECRETS_DIR/openbao-root-token"
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
-m|--mode)
|
|
||||||
shift
|
|
||||||
prole_set_mode "${1:-}"
|
|
||||||
;;
|
|
||||||
-m=*|--mode=*)
|
|
||||||
prole_set_mode "${1#*=}"
|
|
||||||
;;
|
|
||||||
-n|--namespace)
|
|
||||||
shift
|
|
||||||
if [[ -z "${1:-}" ]]; then
|
|
||||||
echo "ERROR: -n/--namespace requires a value" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
NAMESPACE_OVERRIDE="$1"
|
|
||||||
;;
|
|
||||||
-n=*|--namespace=*)
|
|
||||||
NAMESPACE_OVERRIDE="${1#*=}"
|
|
||||||
;;
|
|
||||||
start|stop|status|restart|initialize|update|reload)
|
|
||||||
ACTION="$1"
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
shift
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
if [[ -z "$ACTION" && "$1" != -* ]]; then
|
|
||||||
ACTION="$1"
|
|
||||||
else
|
|
||||||
echo "Unknown argument: $1" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -n "$NAMESPACE_OVERRIDE" ]]; then
|
|
||||||
NAMESPACE="$NAMESPACE_OVERRIDE"
|
|
||||||
export NAMESPACE
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "${NAMESPACE:-}" ]]; then
|
|
||||||
NAMESPACE="default"
|
|
||||||
export NAMESPACE
|
|
||||||
fi
|
|
||||||
|
|
||||||
OPENBAO_NAMESPACE=${OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}
|
OPENBAO_NAMESPACE=${OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}
|
||||||
OPENBAO_PATH_NAMESPACE=${OPENBAO_PATH_NAMESPACE:-${NAMESPACE:-default}}
|
OPENBAO_PATH_NAMESPACE=${OPENBAO_PATH_NAMESPACE:-${NAMESPACE:-default}}
|
||||||
OPENBAO_RESOURCE_NAMESPACE="$OPENBAO_NAMESPACE"
|
OPENBAO_RESOURCE_NAMESPACE="$OPENBAO_NAMESPACE"
|
||||||
@ -227,6 +221,21 @@ bao_service_url() {
|
|||||||
echo "$PROLE_OPENBAO_URL"
|
echo "$PROLE_OPENBAO_URL"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${PROLE_MODE:-}" == "k3s" ]]; then
|
||||||
|
if prole_is_in_cluster; then
|
||||||
|
echo "http://openbao.${OPENBAO_RESOURCE_NAMESPACE}.svc.cluster.local:8200"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
# Use k3s server host if available
|
||||||
|
local k3s_host
|
||||||
|
k3s_host=$(_prole_host_from_url "${PROLE_K3S_SERVER:-}")
|
||||||
|
if [[ -n "$k3s_host" ]]; then
|
||||||
|
echo "http://${k3s_host}:${OPENBAO_PORT_FORWARD_LOCAL:-8200}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo "http://127.0.0.1:${OPENBAO_PORT_FORWARD_LOCAL}"
|
echo "http://127.0.0.1:${OPENBAO_PORT_FORWARD_LOCAL}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +243,44 @@ bao_service_url() {
|
|||||||
_OPENBAO_PF_PID=""
|
_OPENBAO_PF_PID=""
|
||||||
_OPENBAO_PF_STARTED=0
|
_OPENBAO_PF_STARTED=0
|
||||||
|
|
||||||
|
should_skip_deployment() {
|
||||||
|
# If the current namespace is NOT the primary service namespace,
|
||||||
|
# we assume it's a sub-namespace (like prole-db) and we should use the common service.
|
||||||
|
if [[ "$NAMESPACE" != "$PRIMARY_SERVICE_NAMESPACE" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
expose_common_openbao() {
|
||||||
|
local target_ns="$OPENBAO_RESOURCE_NAMESPACE"
|
||||||
|
local source_ns="$PRIMARY_SERVICE_NAMESPACE"
|
||||||
|
|
||||||
|
if [[ "$target_ns" == "$source_ns" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Exposing common OpenBao service from namespace '$source_ns' to '$target_ns' ..."
|
||||||
|
|
||||||
|
# Use ExternalName to point to the common OpenBao service
|
||||||
|
cat <<EOF | kubectl apply -n "$target_ns" -f -
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: $OPENBAO_NAME
|
||||||
|
spec:
|
||||||
|
type: ExternalName
|
||||||
|
externalName: $OPENBAO_NAME.$source_ns.svc.cluster.local
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use PRIMARY_SERVICE_NAMESPACE if skipping deployment (we port-forward to the common svc)
|
||||||
start_openbao_port_forward() {
|
start_openbao_port_forward() {
|
||||||
|
local pf_ns="$OPENBAO_RESOURCE_NAMESPACE"
|
||||||
|
if should_skip_deployment; then
|
||||||
|
pf_ns="$PRIMARY_SERVICE_NAMESPACE"
|
||||||
|
fi
|
||||||
|
|
||||||
local port="${OPENBAO_PORT_FORWARD_LOCAL}"
|
local port="${OPENBAO_PORT_FORWARD_LOCAL}"
|
||||||
local url="http://127.0.0.1:${port}"
|
local url="http://127.0.0.1:${port}"
|
||||||
|
|
||||||
@ -242,7 +288,7 @@ start_openbao_port_forward() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
kubectl -n "$OPENBAO_RESOURCE_NAMESPACE" port-forward "svc/${OPENBAO_NAME}" "${port}:8200" >/dev/null 2>&1 &
|
kubectl -n "$pf_ns" port-forward --address "${OPENBAO_PF_ADDR:-127.0.0.1}" "svc/${OPENBAO_NAME}" "${port}:8200" >/dev/null 2>&1 &
|
||||||
_OPENBAO_PF_PID=$!
|
_OPENBAO_PF_PID=$!
|
||||||
_OPENBAO_PF_STARTED=1
|
_OPENBAO_PF_STARTED=1
|
||||||
|
|
||||||
@ -268,9 +314,11 @@ stop_openbao_port_forward() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wait_for_openbao_http() {
|
wait_for_openbao_http() {
|
||||||
local svc
|
|
||||||
svc="$(bao_service_url)"
|
|
||||||
local max_retries=${1:-20}
|
local max_retries=${1:-20}
|
||||||
|
local svc="${2:-}"
|
||||||
|
if [[ -z "$svc" ]]; then
|
||||||
|
svc="$(bao_service_url)"
|
||||||
|
fi
|
||||||
local count=0
|
local count=0
|
||||||
while ! curl -sS "$svc/v1/sys/health" >/dev/null 2>&1; do
|
while ! curl -sS "$svc/v1/sys/health" >/dev/null 2>&1; do
|
||||||
if [[ $count -ge $max_retries ]]; then
|
if [[ $count -ge $max_retries ]]; then
|
||||||
@ -434,11 +482,16 @@ apply_openbao_host_network_patch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wait_for_openbao() {
|
wait_for_openbao() {
|
||||||
echo "Waiting for OpenBao to become ready ..."
|
local wait_ns="$OPENBAO_RESOURCE_NAMESPACE"
|
||||||
if kubectl get statefulset/$OPENBAO_NAME -n "$OPENBAO_RESOURCE_NAMESPACE" >/dev/null 2>&1; then
|
if should_skip_deployment; then
|
||||||
kubectl rollout status statefulset/$OPENBAO_NAME -n "$OPENBAO_RESOURCE_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s}
|
wait_ns="$PRIMARY_SERVICE_NAMESPACE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Waiting for OpenBao to become ready in namespace '$wait_ns' ..."
|
||||||
|
if kubectl get statefulset/$OPENBAO_NAME -n "$wait_ns" >/dev/null 2>&1; then
|
||||||
|
kubectl rollout status statefulset/$OPENBAO_NAME -n "$wait_ns" --timeout=${ROLLOUT_TIMEOUT:-300s}
|
||||||
else
|
else
|
||||||
kubectl rollout status deploy/$OPENBAO_NAME -n "$OPENBAO_RESOURCE_NAMESPACE" --timeout=${ROLLOUT_TIMEOUT:-300s}
|
kubectl rollout status deploy/$OPENBAO_NAME -n "$wait_ns" --timeout=${ROLLOUT_TIMEOUT:-300s}
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,8 +499,10 @@ init_openbao_kv_and_store_admin_key() {
|
|||||||
local token
|
local token
|
||||||
token=$(cat "$root_token_file")
|
token=$(cat "$root_token_file")
|
||||||
# Enable kv (if not already) and write keys
|
# Enable kv (if not already) and write keys
|
||||||
local svc
|
local svc="${1:-}"
|
||||||
svc="$(bao_service_url)"
|
if [[ -z "$svc" ]]; then
|
||||||
|
svc="$(bao_service_url)"
|
||||||
|
fi
|
||||||
echo "Configuring KV at $svc ..."
|
echo "Configuring KV at $svc ..."
|
||||||
|
|
||||||
# Check if kv/ is already mounted
|
# Check if kv/ is already mounted
|
||||||
@ -608,7 +663,7 @@ init_openbao_kv_and_store_admin_key() {
|
|||||||
|
|
||||||
# status command implementation wrapped in a function to avoid top-level 'local'
|
# status command implementation wrapped in a function to avoid top-level 'local'
|
||||||
cmd_status() {
|
cmd_status() {
|
||||||
echo "--- init_primary_domain status ---"
|
echo "--- openbao status ---"
|
||||||
echo "Namespace: $OPENBAO_RESOURCE_NAMESPACE"
|
echo "Namespace: $OPENBAO_RESOURCE_NAMESPACE"
|
||||||
echo "Secret namespace: $BAO_NAMESPACE"
|
echo "Secret namespace: $BAO_NAMESPACE"
|
||||||
echo "OpenBao name: $OPENBAO_NAME"
|
echo "OpenBao name: $OPENBAO_NAME"
|
||||||
@ -679,6 +734,22 @@ cmd_status() {
|
|||||||
# Prefer explicit override, then localhost port-forward, then cluster DNS
|
# Prefer explicit override, then localhost port-forward, then cluster DNS
|
||||||
local svc_url
|
local svc_url
|
||||||
svc_url="$(bao_service_url)"
|
svc_url="$(bao_service_url)"
|
||||||
|
|
||||||
|
local pf_started=0
|
||||||
|
if ! curl -sS "$svc_url/v1/sys/health" >/dev/null 2>&1; then
|
||||||
|
if start_openbao_port_forward; then
|
||||||
|
pf_started=1
|
||||||
|
# If we started a port-forward and the original URL was not local,
|
||||||
|
# we should use the local URL for status verification.
|
||||||
|
if [[ "$svc_url" != *"127.0.0.1"* && "$svc_url" != *"localhost"* ]]; then
|
||||||
|
local local_url="http://127.0.0.1:${OPENBAO_PORT_FORWARD_LOCAL}"
|
||||||
|
if curl -sS "$local_url/v1/sys/health" >/dev/null 2>&1; then
|
||||||
|
svc_url="$local_url"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if curl -sS "$svc_url/v1/sys/health" >/dev/null 2>&1; then
|
if curl -sS "$svc_url/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "[OK] OpenBao service reachable"
|
echo "[OK] OpenBao service reachable"
|
||||||
if [[ -s "$root_token_file" ]]; then
|
if [[ -s "$root_token_file" ]]; then
|
||||||
@ -696,13 +767,8 @@ cmd_status() {
|
|||||||
ok=1
|
ok=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Service certificate existence (CNPG TLS)
|
if [[ "$pf_started" == "1" ]]; then
|
||||||
local CNPG_CLUSTER_NAME
|
stop_openbao_port_forward
|
||||||
CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db}
|
|
||||||
if kubectl -n "$NAMESPACE" get secret "${CNPG_CLUSTER_NAME}-tls" >/dev/null 2>&1; then
|
|
||||||
echo "[OK] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' exists"
|
|
||||||
else
|
|
||||||
echo "[INFO] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' not found"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "-------------------------------------"
|
echo "-------------------------------------"
|
||||||
@ -720,16 +786,23 @@ case "$ACTION" in
|
|||||||
ensure_tools
|
ensure_tools
|
||||||
ensure_k8s_tools
|
ensure_k8s_tools
|
||||||
ensure_namespace
|
ensure_namespace
|
||||||
generate_openbao_manifests
|
if should_skip_deployment; then
|
||||||
if should_apply_kerberos_configmap; then
|
expose_common_openbao
|
||||||
generate_kerberos_configmap
|
else
|
||||||
|
generate_openbao_manifests
|
||||||
|
if should_apply_kerberos_configmap; then
|
||||||
|
generate_kerberos_configmap
|
||||||
|
fi
|
||||||
|
k3d_cleanup_pending_openbao_pvc
|
||||||
|
apply_k8s
|
||||||
|
apply_openbao_host_network_patch
|
||||||
fi
|
fi
|
||||||
ensure_admin_keypair
|
|
||||||
k3d_cleanup_pending_openbao_pvc
|
|
||||||
apply_k8s
|
|
||||||
apply_openbao_host_network_patch
|
|
||||||
wait_for_openbao
|
wait_for_openbao
|
||||||
prole_register_port_forward "openbao" "${OPENBAO_NAMESPACE:-default}" "svc/openbao" "8200" "8200" "127.0.0.1" "TCP" "OpenBao"
|
pf_ns="${OPENBAO_NAMESPACE:-default}"
|
||||||
|
if should_skip_deployment; then
|
||||||
|
pf_ns="$PRIMARY_SERVICE_NAMESPACE"
|
||||||
|
fi
|
||||||
|
prole_register_port_forward "openbao" "$pf_ns" "svc/openbao" "${OPENBAO_PORT_FORWARD_LOCAL}" "8200" "${OPENBAO_PF_ADDR}" "TCP" "OpenBao"
|
||||||
;;
|
;;
|
||||||
status)
|
status)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
@ -759,13 +832,17 @@ case "$ACTION" in
|
|||||||
ensure_tools
|
ensure_tools
|
||||||
ensure_k8s_tools
|
ensure_k8s_tools
|
||||||
ensure_namespace
|
ensure_namespace
|
||||||
generate_openbao_manifests
|
if should_skip_deployment; then
|
||||||
if should_apply_kerberos_configmap; then
|
expose_common_openbao
|
||||||
generate_kerberos_configmap
|
else
|
||||||
|
generate_openbao_manifests
|
||||||
|
if should_apply_kerberos_configmap; then
|
||||||
|
generate_kerberos_configmap
|
||||||
|
fi
|
||||||
|
k3d_cleanup_pending_openbao_pvc
|
||||||
|
apply_k8s
|
||||||
|
apply_openbao_host_network_patch
|
||||||
fi
|
fi
|
||||||
k3d_cleanup_pending_openbao_pvc
|
|
||||||
apply_k8s
|
|
||||||
apply_openbao_host_network_patch
|
|
||||||
wait_for_openbao
|
wait_for_openbao
|
||||||
# Read password from stdin if provided
|
# Read password from stdin if provided
|
||||||
db_pass=""
|
db_pass=""
|
||||||
@ -779,27 +856,43 @@ case "$ACTION" in
|
|||||||
echo "ERROR: Unable to establish OpenBao port-forward on 127.0.0.1:${OPENBAO_PORT_FORWARD_LOCAL}" >&2
|
echo "ERROR: Unable to establish OpenBao port-forward on 127.0.0.1:${OPENBAO_PORT_FORWARD_LOCAL}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
wait_for_openbao_http 20
|
|
||||||
init_openbao_kv_and_store_admin_key
|
# After port-forward, always use the local URL for initialization so we
|
||||||
prole_register_port_forward "openbao" "${OPENBAO_NAMESPACE:-default}" "svc/openbao" "8200" "8200" "127.0.0.1" "TCP" "OpenBao"
|
# don't depend on OpenBao being externally reachable from this host.
|
||||||
|
local_url="http://127.0.0.1:${OPENBAO_PORT_FORWARD_LOCAL}"
|
||||||
|
wait_for_openbao_http 20 "$local_url"
|
||||||
|
init_openbao_kv_and_store_admin_key "$local_url"
|
||||||
|
pf_ns="${OPENBAO_NAMESPACE:-default}"
|
||||||
|
if should_skip_deployment; then
|
||||||
|
pf_ns="$PRIMARY_SERVICE_NAMESPACE"
|
||||||
|
fi
|
||||||
|
prole_register_port_forward "openbao" "$pf_ns" "svc/openbao" "${OPENBAO_PORT_FORWARD_LOCAL}" "8200" "${OPENBAO_PF_ADDR}" "TCP" "OpenBao"
|
||||||
echo "Initialization complete. OpenBao available at $(bao_service_url)"
|
echo "Initialization complete. OpenBao available at $(bao_service_url)"
|
||||||
;;
|
;;
|
||||||
update|reload)
|
update|reload)
|
||||||
ensure_tools
|
ensure_tools
|
||||||
ensure_k8s_tools
|
ensure_k8s_tools
|
||||||
ensure_namespace
|
ensure_namespace
|
||||||
generate_openbao_manifests
|
if should_skip_deployment; then
|
||||||
if should_apply_kerberos_configmap; then
|
expose_common_openbao
|
||||||
generate_kerberos_configmap
|
else
|
||||||
|
generate_openbao_manifests
|
||||||
|
if should_apply_kerberos_configmap; then
|
||||||
|
generate_kerberos_configmap
|
||||||
|
fi
|
||||||
|
k3d_cleanup_pending_openbao_pvc
|
||||||
|
apply_k8s
|
||||||
|
apply_openbao_host_network_patch
|
||||||
fi
|
fi
|
||||||
k3d_cleanup_pending_openbao_pvc
|
pf_ns="${OPENBAO_NAMESPACE:-default}"
|
||||||
apply_k8s
|
if should_skip_deployment; then
|
||||||
apply_openbao_host_network_patch
|
pf_ns="$PRIMARY_SERVICE_NAMESPACE"
|
||||||
prole_register_port_forward "openbao" "${OPENBAO_NAMESPACE:-default}" "svc/openbao" "8200" "8200" "127.0.0.1" "TCP" "OpenBao"
|
fi
|
||||||
|
prole_register_port_forward "openbao" "$pf_ns" "svc/openbao" "${OPENBAO_PORT_FORWARD_LOCAL}" "8200" "${OPENBAO_PF_ADDR}" "TCP" "OpenBao"
|
||||||
echo "Re-applied manifests."
|
echo "Re-applied manifests."
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: $0 [-n|--namespace NAME] {start|stop|status|restart|initialize|update|reload}" >&2
|
common_core_usage "$0"
|
||||||
exit 2
|
exit 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@ -9,18 +9,38 @@ set -euo pipefail
|
|||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
|
||||||
# Load environment and config via prole_cfg.sh
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
|
set -- "${COMMON_CORE_ARGS[@]}"
|
||||||
|
common_core_parse_args "$@"
|
||||||
|
|
||||||
|
if [[ "${COMMON_CORE_HELP:-0}" == 1 ]]; then
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${COMMON_CORE_PARSE_ERROR:-}" ]]; then
|
||||||
|
echo "ERROR: ${COMMON_CORE_PARSE_ERROR}" >&2
|
||||||
|
common_core_usage "$0"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTION="$COMMON_CORE_ACTION"
|
||||||
|
NAMESPACE="$(common_core_resolve_namespace "default")"
|
||||||
|
common_core_apply_namespace "$NAMESPACE"
|
||||||
|
|
||||||
if [[ -z "${PROLE_SERVICE:-}" ]]; then
|
if [[ -z "${PROLE_SERVICE:-}" ]]; then
|
||||||
echo "ERROR: PROLE_SERVICE is not defined. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2
|
echo "ERROR: PROLE_SERVICE is not defined. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ACTION=""
|
|
||||||
NAMESPACE_OVERRIDE=""
|
|
||||||
|
|
||||||
OPENTOFU_NAME=${OPENTOFU_NAME:-opentofu}
|
OPENTOFU_NAME=${OPENTOFU_NAME:-opentofu}
|
||||||
OPENTOFU_ADMIN_USER=${OPENTOFU_ADMIN_USER:-admin}
|
OPENTOFU_ADMIN_USER=${OPENTOFU_ADMIN_USER:-admin}
|
||||||
|
|
||||||
@ -32,55 +52,6 @@ else
|
|||||||
OPENTOFU_MANIFEST_DIR="$SCRIPT_DIR/../k8s/opentofu"
|
OPENTOFU_MANIFEST_DIR="$SCRIPT_DIR/../k8s/opentofu"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
-m|--mode)
|
|
||||||
shift
|
|
||||||
prole_set_mode "${1:-}"
|
|
||||||
;;
|
|
||||||
-m=*|--mode=*)
|
|
||||||
prole_set_mode "${1#*=}"
|
|
||||||
;;
|
|
||||||
-n|--namespace)
|
|
||||||
shift
|
|
||||||
if [[ -z "${1:-}" ]]; then
|
|
||||||
echo "ERROR: -n/--namespace requires a value" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
NAMESPACE_OVERRIDE="$1"
|
|
||||||
;;
|
|
||||||
-n=*|--namespace=*)
|
|
||||||
NAMESPACE_OVERRIDE="${1#*=}"
|
|
||||||
;;
|
|
||||||
start|stop|status|restart|initialize|update|reload)
|
|
||||||
ACTION="$1"
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
shift
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
if [[ -z "$ACTION" && "$1" != -* ]]; then
|
|
||||||
ACTION="$1"
|
|
||||||
else
|
|
||||||
echo "Unknown argument: $1" >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -n "$NAMESPACE_OVERRIDE" ]]; then
|
|
||||||
NAMESPACE="$NAMESPACE_OVERRIDE"
|
|
||||||
export NAMESPACE
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "${NAMESPACE:-}" ]]; then
|
|
||||||
NAMESPACE="default"
|
|
||||||
export NAMESPACE
|
|
||||||
fi
|
|
||||||
|
|
||||||
OPENTOFU_NAMESPACE=${OPENTOFU_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}
|
OPENTOFU_NAMESPACE=${OPENTOFU_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}
|
||||||
OPENTOFU_SECRET_NAMESPACE=${OPENTOFU_SECRET_NAMESPACE:-${NAMESPACE:-default}}
|
OPENTOFU_SECRET_NAMESPACE=${OPENTOFU_SECRET_NAMESPACE:-${NAMESPACE:-default}}
|
||||||
OPENTOFU_OPENBAO_NAMESPACE=${OPENTOFU_OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-${OPENTOFU_NAMESPACE}}}
|
OPENTOFU_OPENBAO_NAMESPACE=${OPENTOFU_OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-${OPENTOFU_NAMESPACE}}}
|
||||||
@ -110,8 +81,8 @@ openbao_url() {
|
|||||||
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:8200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
|
elif curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
echo "http://127.0.0.1:18200"
|
echo "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
echo ""
|
echo ""
|
||||||
@ -228,7 +199,7 @@ case "${ACTION:-}" in
|
|||||||
status_k8s
|
status_k8s
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: $0 [start|stop|status|restart|initialize|update|reload] [-n namespace] [--mode k3d|k3s|k8s]" >&2
|
common_core_usage "$0"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@ -9,6 +9,22 @@ set -euo pipefail
|
|||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
|
||||||
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
# Inject default config if not provided
|
||||||
|
_has_config=0
|
||||||
|
for _arg in "$@"; do
|
||||||
|
[[ "$_arg" == "-c" || "$_arg" == "--config" || "$_arg" == -c=* || "$_arg" == --config=* ]] && _has_config=1
|
||||||
|
done
|
||||||
|
if [[ $_has_config -eq 0 && -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then
|
||||||
|
set -- "-c" "$SCRIPT_DIR/../conf/prole.cfg" "$@"
|
||||||
|
fi
|
||||||
|
unset _has_config _arg
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
# Load environment and config via prole_cfg.sh
|
# Load environment and config via prole_cfg.sh
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
@ -54,6 +70,15 @@ EOF
|
|||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
-c|--config)
|
||||||
|
shift
|
||||||
|
# Already handled by common_core_preparse_config if we use common_core_lib.sh
|
||||||
|
# but since we don't, we can just skip it here as prole_cfg.sh
|
||||||
|
# will pick it up if we set PROLE_CONF?
|
||||||
|
# Actually prole_cfg.sh uses PROLE_CONF.
|
||||||
|
# Let's just consume it.
|
||||||
|
shift
|
||||||
|
;;
|
||||||
-m|--mode)
|
-m|--mode)
|
||||||
shift
|
shift
|
||||||
prole_set_mode "${1:-}"
|
prole_set_mode "${1:-}"
|
||||||
|
|||||||
@ -4,22 +4,30 @@ set -euo pipefail
|
|||||||
|
|
||||||
# init_service_layer.sh
|
# init_service_layer.sh
|
||||||
# Purpose:
|
# Purpose:
|
||||||
# - Deploy the Prole service layer (ArgoCD, OpenTofu, Garage; Kerberos optional)
|
# - Deploy the Prole service layer (ArgoCD, OpenTofu, Garage, OpenBao; Kerberos optional)
|
||||||
# - Keep service-layer resources grouped in SERVICE_NAMESPACE
|
# - Keep service-layer resources grouped in SERVICE_NAMESPACE
|
||||||
# - Migrate service layer to a new namespace
|
# - Migrate service layer to a new namespace
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
# Inject default config if not provided
|
||||||
|
_has_config=0
|
||||||
|
for _arg in "$@"; do
|
||||||
|
[[ "$_arg" == "-c" || "$_arg" == "--config" || "$_arg" == -c=* || "$_arg" == --config=* ]] && _has_config=1
|
||||||
|
done
|
||||||
|
if [[ $_has_config -eq 0 && -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then
|
||||||
|
set -- "-c" "$SCRIPT_DIR/../conf/prole.cfg" "$@"
|
||||||
|
fi
|
||||||
|
unset _has_config _arg
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
if [[ "${1:-}" == "--mode" || "${1:-}" == "-m" ]]; then
|
|
||||||
prole_set_mode "${2:-}"
|
|
||||||
shift 2
|
|
||||||
elif [[ "${1:-}" == --mode=* || "${1:-}" == -m=* ]]; then
|
|
||||||
prole_set_mode "${1#*=}"
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
|
|
||||||
prole_ensure_kubeconfig >/dev/null 2>&1 || true
|
prole_ensure_kubeconfig >/dev/null 2>&1 || true
|
||||||
|
|
||||||
ACTION=""
|
ACTION=""
|
||||||
@ -36,6 +44,19 @@ EOF
|
|||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
-c|--config)
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-m|--mode)
|
||||||
|
shift
|
||||||
|
prole_set_mode "${1:-}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-m=*|--mode=*)
|
||||||
|
prole_set_mode "${1#*=}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
-n|--namespace)
|
-n|--namespace)
|
||||||
shift
|
shift
|
||||||
SERVICE_NAMESPACE_OVERRIDE="${1:-}"
|
SERVICE_NAMESPACE_OVERRIDE="${1:-}"
|
||||||
@ -67,8 +88,9 @@ while [[ $# -gt 0 ]]; do
|
|||||||
TO_NAMESPACE="${1#*=}"
|
TO_NAMESPACE="${1#*=}"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
start|update|restart|status|stop|migrate|initialize|reload)
|
start|update|restart|status|stop|migrate|initialize|reload|repair)
|
||||||
ACTION="$1"
|
ACTION="$1"
|
||||||
|
[[ "$ACTION" == "repair" ]] && ACTION="update"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
-h|--help)
|
-h|--help)
|
||||||
@ -144,7 +166,7 @@ deploy_service_layer() {
|
|||||||
ensure_namespace "$ns"
|
ensure_namespace "$ns"
|
||||||
label_namespace "$ns"
|
label_namespace "$ns"
|
||||||
|
|
||||||
local argocd_action opentofu_action garage_action kdc_action
|
local argocd_action opentofu_action garage_action kdc_action openbao_action kong_action
|
||||||
case "$action" in
|
case "$action" in
|
||||||
start|initialize|update|reload) argocd_action="update" ;;
|
start|initialize|update|reload) argocd_action="update" ;;
|
||||||
restart) argocd_action="restart" ;;
|
restart) argocd_action="restart" ;;
|
||||||
@ -161,6 +183,14 @@ deploy_service_layer() {
|
|||||||
*) opentofu_action="update" ;;
|
*) opentofu_action="update" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
case "$action" in
|
||||||
|
start|initialize|update|reload) openbao_action="update" ;;
|
||||||
|
restart) openbao_action="restart" ;;
|
||||||
|
stop) openbao_action="stop" ;;
|
||||||
|
status) openbao_action="status" ;;
|
||||||
|
*) openbao_action="update" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
case "$action" in
|
case "$action" in
|
||||||
start|initialize|update|reload) garage_action="start" ;;
|
start|initialize|update|reload) garage_action="start" ;;
|
||||||
restart) garage_action="restart" ;;
|
restart) garage_action="restart" ;;
|
||||||
@ -169,6 +199,14 @@ deploy_service_layer() {
|
|||||||
*) garage_action="start" ;;
|
*) garage_action="start" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
case "$action" in
|
||||||
|
start|initialize|update|reload) kong_action="update" ;;
|
||||||
|
restart) kong_action="restart" ;;
|
||||||
|
stop) kong_action="stop" ;;
|
||||||
|
status) kong_action="status" ;;
|
||||||
|
*) kong_action="update" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
case "$action" in
|
case "$action" in
|
||||||
stop) kdc_action="cleanup" ;;
|
stop) kdc_action="cleanup" ;;
|
||||||
status) kdc_action="status" ;;
|
status) kdc_action="status" ;;
|
||||||
@ -181,9 +219,18 @@ deploy_service_layer() {
|
|||||||
OPENTOFU_NAMESPACE="$ns" OPENTOFU_SECRET_NAMESPACE="${NAMESPACE:-$ns}" OPENTOFU_OPENBAO_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
OPENTOFU_NAMESPACE="$ns" OPENTOFU_SECRET_NAMESPACE="${NAMESPACE:-$ns}" OPENTOFU_OPENBAO_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
||||||
"$SCRIPT_DIR/init_opentofu.sh" -n "$ns" "$opentofu_action" || rc=$?
|
"$SCRIPT_DIR/init_opentofu.sh" -n "$ns" "$opentofu_action" || rc=$?
|
||||||
|
|
||||||
|
OPENBAO_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
||||||
|
"$SCRIPT_DIR/init_openbao.sh" -n "$ns" "$openbao_action" || rc=$?
|
||||||
|
|
||||||
|
# cert-manager is cluster-scoped and managed independently via init_certmgr.sh
|
||||||
|
# in its own dedicated 'cert-manager' namespace; it is not part of the service layer.
|
||||||
|
|
||||||
NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" GARAGE_NAMESPACE="$ns" \
|
NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" GARAGE_NAMESPACE="$ns" \
|
||||||
"$SCRIPT_DIR/init_garage_store.sh" "$garage_action" || rc=$?
|
"$SCRIPT_DIR/init_garage_store.sh" "$garage_action" || rc=$?
|
||||||
|
|
||||||
|
KONG_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
||||||
|
"$SCRIPT_DIR/init_kong.sh" -n "$ns" "$kong_action" || rc=$?
|
||||||
|
|
||||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||||
SERVICE_NAMESPACE="$ns" PROLE_KDC_NAMESPACE="$ns" \
|
SERVICE_NAMESPACE="$ns" PROLE_KDC_NAMESPACE="$ns" \
|
||||||
"$SCRIPT_DIR/init_kdc.sh" "$kdc_action" || rc=$?
|
"$SCRIPT_DIR/init_kdc.sh" "$kdc_action" || rc=$?
|
||||||
@ -199,8 +246,14 @@ cleanup_old_namespace() {
|
|||||||
"$SCRIPT_DIR/init_registry.sh" -n "$ARGOCD_NS" --registry-namespace "$REGISTRY_NS" stop || true
|
"$SCRIPT_DIR/init_registry.sh" -n "$ARGOCD_NS" --registry-namespace "$REGISTRY_NS" stop || true
|
||||||
OPENTOFU_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
OPENTOFU_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
||||||
"$SCRIPT_DIR/init_opentofu.sh" -n "$ns" stop || true
|
"$SCRIPT_DIR/init_opentofu.sh" -n "$ns" stop || true
|
||||||
|
OPENBAO_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
||||||
|
"$SCRIPT_DIR/init_openbao.sh" -n "$ns" stop || true
|
||||||
|
CERTMGR_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
||||||
|
"$SCRIPT_DIR/init_certmgr.sh" -n "$ns" stop || true
|
||||||
NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" GARAGE_NAMESPACE="$ns" \
|
NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" GARAGE_NAMESPACE="$ns" \
|
||||||
"$SCRIPT_DIR/init_garage_store.sh" stop || true
|
"$SCRIPT_DIR/init_garage_store.sh" stop || true
|
||||||
|
KONG_NAMESPACE="$ns" SERVICE_NAMESPACE="$ns" \
|
||||||
|
"$SCRIPT_DIR/init_kong.sh" -n "$ns" stop || true
|
||||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||||
SERVICE_NAMESPACE="$ns" PROLE_KDC_NAMESPACE="$ns" \
|
SERVICE_NAMESPACE="$ns" PROLE_KDC_NAMESPACE="$ns" \
|
||||||
"$SCRIPT_DIR/init_kdc.sh" cleanup || true
|
"$SCRIPT_DIR/init_kdc.sh" cleanup || true
|
||||||
|
|||||||
2
mock_val/local_registry.log
Normal file
2
mock_val/local_registry.log
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
# Registry disabled (mode=unknown) @ 2026-03-01 18:10:50
|
||||||
@ -176,8 +176,8 @@ openbao_url() {
|
|||||||
printf '%s' "http://127.0.0.1:8200"
|
printf '%s' "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
|
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
||||||
printf '%s' "http://127.0.0.1:18200"
|
printf '%s' "http://127.0.0.1:8200"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@ -463,7 +463,7 @@ def resolve_openbao_ref(value: str) -> str:
|
|||||||
|
|
||||||
url = os.environ.get("PROLE_OPENBAO_URL")
|
url = os.environ.get("PROLE_OPENBAO_URL")
|
||||||
if not url:
|
if not url:
|
||||||
for p in ["8200", "18200"]:
|
for p in ["8200", "8200"]:
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(f"http://127.0.0.1:{p}/v1/sys/health", timeout=0.5) as r:
|
with urllib.request.urlopen(f"http://127.0.0.1:{p}/v1/sys/health", timeout=0.5) as r:
|
||||||
if r.getcode() == 200:
|
if r.getcode() == 200:
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
405f3fd4ceea0f00952532333e3035b240b404e9d30e0b7f
|
root
|
||||||
@ -2,6 +2,22 @@
|
|||||||
set -u
|
set -u
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
# Shared option parsing for common core scripts
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SCRIPT_DIR/common_core_lib.sh"
|
||||||
|
|
||||||
|
# Inject default config if not provided
|
||||||
|
_has_config=0
|
||||||
|
for _arg in "$@"; do
|
||||||
|
[[ "$_arg" == "-c" || "$_arg" == "--config" || "$_arg" == -c=* || "$_arg" == --config=* ]] && _has_config=1
|
||||||
|
done
|
||||||
|
if [[ $_has_config -eq 0 && -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then
|
||||||
|
set -- "-c" "$SCRIPT_DIR/../conf/prole.cfg" "$@"
|
||||||
|
fi
|
||||||
|
unset _has_config _arg
|
||||||
|
|
||||||
|
common_core_preparse_config "$@"
|
||||||
|
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||||
|
|
||||||
@ -9,7 +25,7 @@ usage() {
|
|||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: status_common_services.sh [-n|--namespace NS] [-k|--kerberos]
|
Usage: status_common_services.sh [-n|--namespace NS] [-k|--kerberos]
|
||||||
|
|
||||||
Checks common infrastructure services (ArgoCD, OpenTofu, Garage).
|
Checks common infrastructure services (ArgoCD, OpenTofu, Garage, OpenBao).
|
||||||
Use -k to include the Prole KDC (auth) checks.
|
Use -k to include the Prole KDC (auth) checks.
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@ -18,6 +34,19 @@ NS=""
|
|||||||
ENABLE_KERBEROS=0
|
ENABLE_KERBEROS=0
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
-c|--config)
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-m|--mode)
|
||||||
|
shift
|
||||||
|
prole_set_mode "${1:-}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-m=*|--mode=*)
|
||||||
|
prole_set_mode "${1#*=}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
-n|--namespace)
|
-n|--namespace)
|
||||||
shift
|
shift
|
||||||
NS="${1:-}"
|
NS="${1:-}"
|
||||||
@ -50,6 +79,16 @@ if [ -z "$NS" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
ARGOCD_NS="${ARGOCD_NAMESPACE:-argocd}"
|
ARGOCD_NS="${ARGOCD_NAMESPACE:-argocd}"
|
||||||
|
KONG_NS="${KONG_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-}}}"
|
||||||
|
CERTMGR_NS="${CERTMGR_NAMESPACE:-cert-manager}"
|
||||||
|
KONG_NAME="${KONG_NAME:-prole-svc-kong}"
|
||||||
|
|
||||||
|
if [ -z "$KONG_NS" ]; then
|
||||||
|
KONG_NS="default"
|
||||||
|
fi
|
||||||
|
if [ -z "$CERTMGR_NS" ]; then
|
||||||
|
CERTMGR_NS="cert-manager"
|
||||||
|
fi
|
||||||
|
|
||||||
if ! command -v kubectl >/dev/null 2>&1; then
|
if ! command -v kubectl >/dev/null 2>&1; then
|
||||||
echo "ERROR: kubectl not found in PATH"
|
echo "ERROR: kubectl not found in PATH"
|
||||||
@ -69,6 +108,8 @@ if [[ -n "${KUBECONFIG:-}" ]]; then
|
|||||||
fi
|
fi
|
||||||
echo "Namespace: $NS"
|
echo "Namespace: $NS"
|
||||||
echo "ArgoCD Namespace: $ARGOCD_NS"
|
echo "ArgoCD Namespace: $ARGOCD_NS"
|
||||||
|
echo "Kong Namespace: $KONG_NS"
|
||||||
|
echo "Cert-Manager Namespace: $CERTMGR_NS"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
run_cmd() {
|
run_cmd() {
|
||||||
@ -79,29 +120,37 @@ run_cmd() {
|
|||||||
|
|
||||||
echo "== Services =="
|
echo "== Services =="
|
||||||
run_cmd kubectl -n "$ARGOCD_NS" get svc argocd-server
|
run_cmd kubectl -n "$ARGOCD_NS" get svc argocd-server
|
||||||
|
run_cmd kubectl -n "$NS" get svc opentofu garage openbao
|
||||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||||
run_cmd kubectl -n "$NS" get svc opentofu garage auth
|
run_cmd kubectl -n "$NS" get svc auth
|
||||||
else
|
|
||||||
run_cmd kubectl -n "$NS" get svc opentofu garage
|
|
||||||
fi
|
fi
|
||||||
|
run_cmd kubectl -n "$KONG_NS" get svc "$KONG_NAME"
|
||||||
|
run_cmd kubectl -n "$CERTMGR_NS" get svc cert-manager cert-manager-webhook
|
||||||
|
|
||||||
echo "== Workloads =="
|
echo "== Workloads =="
|
||||||
run_cmd kubectl -n "$ARGOCD_NS" get deploy argocd-server argocd-repo-server argocd-dex-server argocd-applicationset-controller argocd-notifications-controller argocd-redis
|
run_cmd kubectl -n "$ARGOCD_NS" get deploy argocd-server argocd-repo-server argocd-dex-server argocd-applicationset-controller argocd-notifications-controller argocd-redis
|
||||||
run_cmd kubectl -n "$ARGOCD_NS" get statefulset argocd-application-controller
|
run_cmd kubectl -n "$ARGOCD_NS" get statefulset argocd-application-controller
|
||||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
run_cmd kubectl -n "$NS" get deploy opentofu
|
||||||
run_cmd kubectl -n "$NS" get deploy opentofu auth
|
if kubectl -n "$NS" get statefulset openbao >/dev/null 2>&1; then
|
||||||
|
run_cmd kubectl -n "$NS" get statefulset openbao
|
||||||
else
|
else
|
||||||
run_cmd kubectl -n "$NS" get deploy opentofu
|
run_cmd kubectl -n "$NS" get deploy openbao
|
||||||
|
fi
|
||||||
|
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||||
|
run_cmd kubectl -n "$NS" get deploy auth
|
||||||
fi
|
fi
|
||||||
run_cmd kubectl -n "$NS" get statefulset garage
|
run_cmd kubectl -n "$NS" get statefulset garage
|
||||||
|
run_cmd kubectl -n "$KONG_NS" get deploy "$KONG_NAME"
|
||||||
|
run_cmd kubectl -n "$CERTMGR_NS" get deploy cert-manager cert-manager-cainjector cert-manager-webhook
|
||||||
|
|
||||||
echo "== Pods =="
|
echo "== Pods =="
|
||||||
run_cmd kubectl -n "$ARGOCD_NS" get pods | grep -Ei "argocd" || true
|
run_cmd kubectl -n "$ARGOCD_NS" get pods | grep -Ei "argocd" || true
|
||||||
|
run_cmd kubectl -n "$NS" get pods | grep -Ei "opentofu|garage|openbao" || true
|
||||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||||
run_cmd kubectl -n "$NS" get pods | grep -Ei "opentofu|garage|auth" || true
|
run_cmd kubectl -n "$NS" get pods | grep -Ei "auth" || true
|
||||||
else
|
|
||||||
run_cmd kubectl -n "$NS" get pods | grep -Ei "opentofu|garage" || true
|
|
||||||
fi
|
fi
|
||||||
|
run_cmd kubectl -n "$KONG_NS" get pods | grep -Ei "kong" || true
|
||||||
|
run_cmd kubectl -n "$CERTMGR_NS" get pods | grep -Ei "cert-manager" || true
|
||||||
|
|
||||||
echo "== Health Check =="
|
echo "== Health Check =="
|
||||||
missing=0
|
missing=0
|
||||||
@ -149,6 +198,9 @@ analyze_pods() {
|
|||||||
|
|
||||||
while IFS='|' read -r comp name ready status healthy; do
|
while IFS='|' read -r comp name ready status healthy; do
|
||||||
[[ -z "$comp" ]] && continue
|
[[ -z "$comp" ]] && continue
|
||||||
|
if [[ "$comp" == cert-manager* ]]; then
|
||||||
|
comp="certmgr"
|
||||||
|
fi
|
||||||
COMP_NAMESPACE["$comp"]="$ns"
|
COMP_NAMESPACE["$comp"]="$ns"
|
||||||
if [[ "$healthy" == "1" ]]; then
|
if [[ "$healthy" == "1" ]]; then
|
||||||
HEALTHY_COUNT["$comp"]=$(( ${HEALTHY_COUNT["$comp"]:-0} + 1 ))
|
HEALTHY_COUNT["$comp"]=$(( ${HEALTHY_COUNT["$comp"]:-0} + 1 ))
|
||||||
@ -175,9 +227,13 @@ check_resource() {
|
|||||||
check_resource svc argocd-server "$ARGOCD_NS"
|
check_resource svc argocd-server "$ARGOCD_NS"
|
||||||
check_resource svc opentofu "$NS"
|
check_resource svc opentofu "$NS"
|
||||||
check_resource svc garage "$NS"
|
check_resource svc garage "$NS"
|
||||||
|
check_resource svc openbao "$NS"
|
||||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||||
check_resource svc auth "$NS"
|
check_resource svc auth "$NS"
|
||||||
fi
|
fi
|
||||||
|
check_resource svc "$KONG_NAME" "$KONG_NS"
|
||||||
|
check_resource svc cert-manager "$CERTMGR_NS"
|
||||||
|
check_resource svc cert-manager-webhook "$CERTMGR_NS"
|
||||||
|
|
||||||
check_resource deploy argocd-server "$ARGOCD_NS"
|
check_resource deploy argocd-server "$ARGOCD_NS"
|
||||||
check_resource deploy argocd-repo-server "$ARGOCD_NS"
|
check_resource deploy argocd-repo-server "$ARGOCD_NS"
|
||||||
@ -186,18 +242,29 @@ check_resource deploy argocd-applicationset-controller "$ARGOCD_NS"
|
|||||||
check_resource deploy argocd-notifications-controller "$ARGOCD_NS"
|
check_resource deploy argocd-notifications-controller "$ARGOCD_NS"
|
||||||
check_resource deploy argocd-redis "$ARGOCD_NS"
|
check_resource deploy argocd-redis "$ARGOCD_NS"
|
||||||
check_resource deploy opentofu "$NS"
|
check_resource deploy opentofu "$NS"
|
||||||
|
if kubectl -n "$NS" get statefulset openbao >/dev/null 2>&1; then
|
||||||
|
check_resource statefulset openbao "$NS"
|
||||||
|
else
|
||||||
|
check_resource deploy openbao "$NS"
|
||||||
|
fi
|
||||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||||
check_resource deploy auth "$NS"
|
check_resource deploy auth "$NS"
|
||||||
fi
|
fi
|
||||||
check_resource statefulset argocd-application-controller "$ARGOCD_NS"
|
check_resource statefulset argocd-application-controller "$ARGOCD_NS"
|
||||||
check_resource statefulset garage "$NS"
|
check_resource statefulset garage "$NS"
|
||||||
|
check_resource deploy "$KONG_NAME" "$KONG_NS"
|
||||||
|
check_resource deploy cert-manager "$CERTMGR_NS"
|
||||||
|
check_resource deploy cert-manager-cainjector "$CERTMGR_NS"
|
||||||
|
check_resource deploy cert-manager-webhook "$CERTMGR_NS"
|
||||||
|
|
||||||
pod_filter="opentofu|garage"
|
pod_filter="opentofu|garage|openbao|kong|cert-manager"
|
||||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||||
pod_filter="opentofu|garage|auth"
|
pod_filter="opentofu|garage|openbao|auth|kong|cert-manager"
|
||||||
fi
|
fi
|
||||||
analyze_pods "$ARGOCD_NS" "argocd"
|
analyze_pods "$ARGOCD_NS" "argocd"
|
||||||
analyze_pods "$NS" "$pod_filter"
|
analyze_pods "$NS" "$pod_filter"
|
||||||
|
analyze_pods "$KONG_NS" "kong"
|
||||||
|
analyze_pods "$CERTMGR_NS" "cert-manager"
|
||||||
|
|
||||||
if [[ -n "$blocked_lines" ]]; then
|
if [[ -n "$blocked_lines" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
@ -208,64 +275,257 @@ fi
|
|||||||
|
|
||||||
if [ $missing -gt 0 ]; then
|
if [ $missing -gt 0 ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "Status: FAILED ($missing resources missing)"
|
# ── Attempt to repair a completely absent cert-manager before failing ──────
|
||||||
exit 1
|
if ! kubectl get namespace "$CERTMGR_NS" >/dev/null 2>&1 \
|
||||||
|
&& [[ -x "$SCRIPT_DIR/init_certmgr.sh" ]]; then
|
||||||
|
echo "== Repair: cert-manager namespace '$CERTMGR_NS' absent — re-initializing =="
|
||||||
|
"$SCRIPT_DIR/init_certmgr.sh" initialize 2>&1 || true
|
||||||
|
echo " Waiting for cert-manager to settle (20s)..."
|
||||||
|
sleep 20
|
||||||
|
echo " Re-checking cert-manager resources..."
|
||||||
|
_check_cm_resource() {
|
||||||
|
local _kind=$1 _name=$2 _ns=$3
|
||||||
|
if ! kubectl -n "$_ns" get "$_kind" "$_name" >/dev/null 2>&1; then
|
||||||
|
echo " [FAIL] $_kind/$_name still missing (ns=$_ns)"
|
||||||
|
else
|
||||||
|
missing=$((missing - 1))
|
||||||
|
echo " [OK] $_kind/$_name present (ns=$_ns)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
_check_cm_resource svc cert-manager "$CERTMGR_NS"
|
||||||
|
_check_cm_resource svc cert-manager-webhook "$CERTMGR_NS"
|
||||||
|
_check_cm_resource deploy cert-manager "$CERTMGR_NS"
|
||||||
|
_check_cm_resource deploy cert-manager-cainjector "$CERTMGR_NS"
|
||||||
|
_check_cm_resource deploy cert-manager-webhook "$CERTMGR_NS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $missing -gt 0 ]; then
|
||||||
|
echo "Status: FAILED ($missing resources missing)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Status: OK (missing resources restored by repair)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Repair helpers ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Emit "kind name ns" lines for every workload owned by a component.
|
||||||
|
_workloads_for_comp() {
|
||||||
|
local _comp="$1"
|
||||||
|
local _ns="${COMP_NAMESPACE[$_comp]:-$NS}"
|
||||||
|
case "$_comp" in
|
||||||
|
argocd)
|
||||||
|
echo "statefulset argocd-application-controller $ARGOCD_NS"
|
||||||
|
for _d in argocd-server argocd-repo-server argocd-dex-server \
|
||||||
|
argocd-applicationset-controller argocd-notifications-controller argocd-redis; do
|
||||||
|
echo "deployment $_d $ARGOCD_NS"
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
openbao)
|
||||||
|
if kubectl -n "$_ns" get statefulset openbao >/dev/null 2>&1; then
|
||||||
|
echo "statefulset openbao $_ns"
|
||||||
|
else
|
||||||
|
echo "deployment openbao $_ns"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
opentofu) echo "deployment opentofu $_ns" ;;
|
||||||
|
garage) echo "statefulset garage $_ns" ;;
|
||||||
|
auth) echo "deployment auth $_ns" ;;
|
||||||
|
kong) echo "deployment $KONG_NAME $KONG_NS" ;;
|
||||||
|
certmgr)
|
||||||
|
for _d in cert-manager cert-manager-cainjector cert-manager-webhook; do
|
||||||
|
echo "deployment $_d $CERTMGR_NS"
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reset all pod-tracking state and re-run analysis.
|
||||||
|
_recheck_pods() {
|
||||||
|
HEALTHY_COUNT=()
|
||||||
|
BLOCKED_COUNT=()
|
||||||
|
BLOCKED_PODS=()
|
||||||
|
COMP_NAMESPACE=()
|
||||||
|
blocked_lines=""
|
||||||
|
blocked=0
|
||||||
|
analyze_pods "$ARGOCD_NS" "argocd"
|
||||||
|
analyze_pods "$NS" "$pod_filter"
|
||||||
|
analyze_pods "$KONG_NS" "kong"
|
||||||
|
analyze_pods "$CERTMGR_NS" "cert-manager"
|
||||||
|
if [[ -n "$blocked_lines" ]]; then
|
||||||
|
blocked=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print the name of each component that currently has blocked pods.
|
||||||
|
_blocked_comps() {
|
||||||
|
for _bc in argocd openbao opentofu garage auth kong certmgr; do
|
||||||
|
[[ "$_bc" == "auth" && "$ENABLE_KERBEROS" != "1" ]] && continue
|
||||||
|
[[ ${BLOCKED_COUNT["$_bc"]:-0} -gt 0 ]] && echo "$_bc"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Escalating repair ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
if [ $blocked -gt 0 ]; then
|
if [ $blocked -gt 0 ]; then
|
||||||
repairable=()
|
|
||||||
fail_all=()
|
# ── Round 1: delete blocked pods ──────────────────────────────────────────
|
||||||
for comp in argocd openbao opentofu garage auth; do
|
echo ""
|
||||||
if [[ "$comp" == "auth" && "$ENABLE_KERBEROS" != "1" ]]; then
|
echo "== Repair Round 1: replacing blocked pods =="
|
||||||
continue
|
mapfile -t _r1_comps < <(_blocked_comps)
|
||||||
|
for _comp in "${_r1_comps[@]}"; do
|
||||||
|
_pods="${BLOCKED_PODS[$_comp]:-}"
|
||||||
|
_ns="${COMP_NAMESPACE[$_comp]:-$NS}"
|
||||||
|
if [[ -n "$_pods" ]]; then
|
||||||
|
echo " Deleting pod(s) for $_comp in $_ns:$_pods"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
kubectl -n "$_ns" delete pod $_pods --wait=false >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
hc=${HEALTHY_COUNT["$comp"]:-0}
|
done
|
||||||
bc=${BLOCKED_COUNT["$comp"]:-0}
|
echo " Waiting for pod replacement (15s)..."
|
||||||
if [[ "$bc" -gt 0 && "$hc" -gt 0 ]]; then
|
sleep 15
|
||||||
repairable+=("$comp")
|
_recheck_pods
|
||||||
elif [[ "$bc" -gt 0 && "$hc" -eq 0 ]]; then
|
|
||||||
fail_all+=("$comp")
|
if [[ $blocked -eq 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "Status: OK (resolved in repair round 1)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Pods still blocked after Round 1:"
|
||||||
|
printf '%b' "$blocked_lines"
|
||||||
|
|
||||||
|
# ── Round 2: rollout restart owning StatefulSet / Deployment ──────────────
|
||||||
|
echo ""
|
||||||
|
echo "== Repair Round 2: restarting workload controllers =="
|
||||||
|
mapfile -t _r2_comps < <(_blocked_comps)
|
||||||
|
for _comp in "${_r2_comps[@]}"; do
|
||||||
|
while IFS=' ' read -r _kind _name _wns; do
|
||||||
|
[[ -z "$_kind" ]] && continue
|
||||||
|
if kubectl -n "$_wns" get "$_kind" "$_name" >/dev/null 2>&1; then
|
||||||
|
echo " kubectl rollout restart $_kind/$_name -n $_wns"
|
||||||
|
kubectl -n "$_wns" rollout restart "$_kind/$_name" >/dev/null 2>&1 || true
|
||||||
|
kubectl -n "$_wns" rollout status "$_kind/$_name" --timeout=60s 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done < <(_workloads_for_comp "$_comp")
|
||||||
|
done
|
||||||
|
echo " Waiting for rollout to settle (20s)..."
|
||||||
|
sleep 20
|
||||||
|
_recheck_pods
|
||||||
|
|
||||||
|
if [[ $blocked -eq 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "Status: OK (resolved in repair round 2)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Pods still blocked after Round 2:"
|
||||||
|
printf '%b' "$blocked_lines"
|
||||||
|
|
||||||
|
# ── Round 3: clear affected namespaces and re-initialize ──────────────────
|
||||||
|
echo ""
|
||||||
|
echo "== Repair Round 3: clearing affected namespaces and re-initializing =="
|
||||||
|
mapfile -t _r3_comps < <(_blocked_comps)
|
||||||
|
declare -A _cleared_ns=()
|
||||||
|
for _comp in "${_r3_comps[@]}"; do
|
||||||
|
_ns="${COMP_NAMESPACE[$_comp]:-$NS}"
|
||||||
|
[[ -n "${_cleared_ns[$_ns]:-}" ]] && continue
|
||||||
|
_cleared_ns["$_ns"]=1
|
||||||
|
if [[ "$_ns" == "default" || "$_ns" == "kube-system" ]]; then
|
||||||
|
echo " [Round 3] Removing workload resources in protected namespace '$_ns' for: $_comp"
|
||||||
|
while IFS=' ' read -r _kind _name _wns; do
|
||||||
|
[[ -z "$_kind" ]] && continue
|
||||||
|
echo " kubectl delete $_kind $_name -n $_wns --ignore-not-found"
|
||||||
|
kubectl -n "$_wns" delete "$_kind" "$_name" --ignore-not-found >/dev/null 2>&1 || true
|
||||||
|
done < <(_workloads_for_comp "$_comp")
|
||||||
|
else
|
||||||
|
echo " [Round 3] Deleting namespace '$_ns' ..."
|
||||||
|
kubectl delete namespace "$_ns" --wait=true --timeout=90s >/dev/null 2>&1 || true
|
||||||
|
echo " [Round 3] Recreating namespace '$_ns' ..."
|
||||||
|
kubectl create namespace "$_ns" >/dev/null 2>&1 || true
|
||||||
|
echo " [OK] Namespace '$_ns' cleared and recreated."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ ${#fail_all[@]} -gt 0 ]; then
|
if [[ -x "$SCRIPT_DIR/init_service_layer.sh" ]]; then
|
||||||
echo ""
|
echo " [Round 3] Re-initializing service layer ..."
|
||||||
echo "Repair skipped: all pods blocked for ${fail_all[*]}."
|
_krb_flag=""
|
||||||
echo "Action: Deploy Missing Services."
|
[[ "$ENABLE_KERBEROS" == "1" ]] && _krb_flag="-k"
|
||||||
echo ""
|
# shellcheck disable=SC2086
|
||||||
echo "Status: NOTICE (blocked pods detected)"
|
"$SCRIPT_DIR/init_service_layer.sh" -n "$NS" $_krb_flag update 2>&1 || true
|
||||||
exit 2
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ${#repairable[@]} -gt 0 ]; then
|
echo " [Round 3] Re-checking status after namespace reset (30s)..."
|
||||||
|
sleep 30
|
||||||
|
_recheck_pods
|
||||||
|
|
||||||
|
if [[ $blocked -eq 0 ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "== Repair =="
|
echo "Status: OK (resolved in repair round 3)"
|
||||||
echo "Repairing blocked pods for: ${repairable[*]}"
|
exit 0
|
||||||
for comp in "${repairable[@]}"; do
|
|
||||||
pods="${BLOCKED_PODS["$comp"]:-}"
|
|
||||||
ns="${COMP_NAMESPACE["$comp"]:-$NS}"
|
|
||||||
if [[ -n "$pods" ]]; then
|
|
||||||
kubectl -n "$ns" delete pod $pods --wait=false >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo "Re-checking pods after repair..."
|
|
||||||
sleep 2
|
|
||||||
HEALTHY_COUNT=()
|
|
||||||
BLOCKED_COUNT=()
|
|
||||||
BLOCKED_PODS=()
|
|
||||||
COMP_NAMESPACE=()
|
|
||||||
blocked_lines=""
|
|
||||||
analyze_pods "$ARGOCD_NS" "argocd"
|
|
||||||
analyze_pods "$NS" "$pod_filter"
|
|
||||||
if [[ -n "$blocked_lines" ]]; then
|
|
||||||
echo ""
|
|
||||||
echo "Blocked pods still detected after repair:"
|
|
||||||
printf '%b' "$blocked_lines"
|
|
||||||
echo ""
|
|
||||||
echo "Status: NOTICE (blocked pods detected)"
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Pods still blocked after Round 3:"
|
||||||
|
printf '%b' "$blocked_lines"
|
||||||
|
|
||||||
|
# ── Round 4: reset k3d cluster, preserve registry ─────────────────────────
|
||||||
|
_prole_mode="${PROLE_MODE:-}"
|
||||||
|
if [[ "$_prole_mode" == "k3d" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "== Repair Round 4: resetting k3d cluster (preserving registry) =="
|
||||||
|
_cluster_name="${K3D_CLUSTER_NAME:-prole-dev-cluster}"
|
||||||
|
|
||||||
|
_registry_args=()
|
||||||
|
if command -v k3d >/dev/null 2>&1; then
|
||||||
|
_reg_name=$(k3d registry list --no-headers 2>/dev/null | awk '{print $1}' | head -1 || true)
|
||||||
|
if [[ -n "$_reg_name" ]]; then
|
||||||
|
echo " Preserving registry: $_reg_name"
|
||||||
|
_registry_args=(--registry-use "$_reg_name")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " Deleting k3d cluster '$_cluster_name' ..."
|
||||||
|
k3d cluster delete "$_cluster_name" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo " Recreating k3d cluster '$_cluster_name' ..."
|
||||||
|
k3d cluster create "$_cluster_name" -a 2 \
|
||||||
|
"${_registry_args[@]}" \
|
||||||
|
--api-port 0.0.0.0:6443 >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
kubectl config use-context "k3d-${_cluster_name}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
if [[ -x "$SCRIPT_DIR/init_service_layer.sh" ]]; then
|
||||||
|
echo " [Round 4] Re-initializing service layer after cluster reset ..."
|
||||||
|
_krb_flag=""
|
||||||
|
[[ "$ENABLE_KERBEROS" == "1" ]] && _krb_flag="-k"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
"$SCRIPT_DIR/init_service_layer.sh" -n "$NS" $_krb_flag update 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " [Round 4] Re-checking status after cluster reset (30s)..."
|
||||||
|
sleep 30
|
||||||
|
_recheck_pods
|
||||||
|
|
||||||
|
if [[ $blocked -eq 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "Status: OK (resolved in repair round 4 — cluster reset)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Pods still blocked after Round 4 (cluster reset):"
|
||||||
|
printf '%b' "$blocked_lines"
|
||||||
|
echo ""
|
||||||
|
echo "Status: FAILED — unable to repair after full cluster reset. Manual intervention required."
|
||||||
|
exit 5
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Status: NOTICE (blocked pods remain; k3d cluster reset not applicable for mode '${_prole_mode:-unknown}')"
|
||||||
|
exit 3
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
81
|
89
|
||||||
@ -80,6 +80,7 @@ RUN set -eux; \
|
|||||||
apt-get install -y --no-install-recommends percona-postgresql-18-repack; \
|
apt-get install -y --no-install-recommends percona-postgresql-18-repack; \
|
||||||
apt-get install -y --no-install-recommends percona-pg-stat-monitor18; \
|
apt-get install -y --no-install-recommends percona-pg-stat-monitor18; \
|
||||||
apt-get install -y --no-install-recommends percona-pgbadger; \
|
apt-get install -y --no-install-recommends percona-pgbadger; \
|
||||||
|
apt-get install -y --no-install-recommends percona-pg-tde18; \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
percona-postgresql-contrib \
|
percona-postgresql-contrib \
|
||||||
freetds-dev \
|
freetds-dev \
|
||||||
|
|||||||
@ -75,6 +75,7 @@ RUN set -eux; \
|
|||||||
RUN set -eux; \
|
RUN set -eux; \
|
||||||
apt-get update; \
|
apt-get update; \
|
||||||
{{EXTENSION_INSTALL_STEPS}}
|
{{EXTENSION_INSTALL_STEPS}}
|
||||||
|
apt-get install -y --no-install-recommends percona-pg-tde{{MAJOR_VERSION}}; \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
percona-postgresql-contrib \
|
percona-postgresql-contrib \
|
||||||
freetds-dev \
|
freetds-dev \
|
||||||
|
|||||||
1
src/db-manager/.gitignore
vendored
1
src/db-manager/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
FROM node:20-alpine
|
|
||||||
|
|
||||||
RUN apk add --no-cache curl \
|
|
||||||
&& curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \
|
|
||||||
&& chmod +x kubectl \
|
|
||||||
&& mv kubectl /usr/local/bin/
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
|
||||||
RUN npm ci --omit=dev
|
|
||||||
|
|
||||||
COPY server.js ./
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
USER node
|
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
|
||||||
830
src/db-manager/package-lock.json
generated
830
src/db-manager/package-lock.json
generated
@ -1,830 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "prole-db-manager",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "prole-db-manager",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"express": "^4.21.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/accepts": {
|
|
||||||
"version": "1.3.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
|
||||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-types": "~2.1.34",
|
|
||||||
"negotiator": "0.6.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/array-flatten": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/body-parser": {
|
|
||||||
"version": "1.20.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
|
|
||||||
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"bytes": "~3.1.2",
|
|
||||||
"content-type": "~1.0.5",
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "2.0.0",
|
|
||||||
"destroy": "~1.2.0",
|
|
||||||
"http-errors": "~2.0.1",
|
|
||||||
"iconv-lite": "~0.4.24",
|
|
||||||
"on-finished": "~2.4.1",
|
|
||||||
"qs": "~6.14.0",
|
|
||||||
"raw-body": "~2.5.3",
|
|
||||||
"type-is": "~1.6.18",
|
|
||||||
"unpipe": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8",
|
|
||||||
"npm": "1.2.8000 || >= 1.4.16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/bytes": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/call-bind-apply-helpers": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"function-bind": "^1.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/call-bound": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
|
||||||
"get-intrinsic": "^1.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/content-disposition": {
|
|
||||||
"version": "0.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
|
||||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "5.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/content-type": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cookie": {
|
|
||||||
"version": "0.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
|
||||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cookie-signature": {
|
|
||||||
"version": "1.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
|
|
||||||
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/debug": {
|
|
||||||
"version": "2.6.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ms": "2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/depd": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/destroy": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8",
|
|
||||||
"npm": "1.2.8000 || >= 1.4.16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dunder-proto": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bind-apply-helpers": "^1.0.1",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"gopd": "^1.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ee-first": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/encodeurl": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/es-define-property": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/es-errors": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/es-object-atoms": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/escape-html": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/etag": {
|
|
||||||
"version": "1.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
|
||||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/express": {
|
|
||||||
"version": "4.22.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
|
||||||
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"accepts": "~1.3.8",
|
|
||||||
"array-flatten": "1.1.1",
|
|
||||||
"body-parser": "~1.20.3",
|
|
||||||
"content-disposition": "~0.5.4",
|
|
||||||
"content-type": "~1.0.4",
|
|
||||||
"cookie": "~0.7.1",
|
|
||||||
"cookie-signature": "~1.0.6",
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "2.0.0",
|
|
||||||
"encodeurl": "~2.0.0",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"etag": "~1.8.1",
|
|
||||||
"finalhandler": "~1.3.1",
|
|
||||||
"fresh": "~0.5.2",
|
|
||||||
"http-errors": "~2.0.0",
|
|
||||||
"merge-descriptors": "1.0.3",
|
|
||||||
"methods": "~1.1.2",
|
|
||||||
"on-finished": "~2.4.1",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"path-to-regexp": "~0.1.12",
|
|
||||||
"proxy-addr": "~2.0.7",
|
|
||||||
"qs": "~6.14.0",
|
|
||||||
"range-parser": "~1.2.1",
|
|
||||||
"safe-buffer": "5.2.1",
|
|
||||||
"send": "~0.19.0",
|
|
||||||
"serve-static": "~1.16.2",
|
|
||||||
"setprototypeof": "1.2.0",
|
|
||||||
"statuses": "~2.0.1",
|
|
||||||
"type-is": "~1.6.18",
|
|
||||||
"utils-merge": "1.0.1",
|
|
||||||
"vary": "~1.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/express"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/finalhandler": {
|
|
||||||
"version": "1.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
|
|
||||||
"integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"encodeurl": "~2.0.0",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"on-finished": "~2.4.1",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"statuses": "~2.0.2",
|
|
||||||
"unpipe": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/forwarded": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fresh": {
|
|
||||||
"version": "0.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
|
||||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/function-bind": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-intrinsic": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
|
||||||
"es-define-property": "^1.0.1",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"es-object-atoms": "^1.1.1",
|
|
||||||
"function-bind": "^1.1.2",
|
|
||||||
"get-proto": "^1.0.1",
|
|
||||||
"gopd": "^1.2.0",
|
|
||||||
"has-symbols": "^1.1.0",
|
|
||||||
"hasown": "^2.0.2",
|
|
||||||
"math-intrinsics": "^1.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-proto": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"dunder-proto": "^1.0.1",
|
|
||||||
"es-object-atoms": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gopd": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/has-symbols": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hasown": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"function-bind": "^1.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/http-errors": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"depd": "~2.0.0",
|
|
||||||
"inherits": "~2.0.4",
|
|
||||||
"setprototypeof": "~1.2.0",
|
|
||||||
"statuses": "~2.0.2",
|
|
||||||
"toidentifier": "~1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/express"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/iconv-lite": {
|
|
||||||
"version": "0.4.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
|
||||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/inherits": {
|
|
||||||
"version": "2.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/ipaddr.js": {
|
|
||||||
"version": "1.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
|
||||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/math-intrinsics": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/media-typer": {
|
|
||||||
"version": "0.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
|
||||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/merge-descriptors": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/methods": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
|
||||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"mime": "cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime-db": {
|
|
||||||
"version": "1.52.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime-types": {
|
|
||||||
"version": "2.1.35",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "1.52.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ms": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/negotiator": {
|
|
||||||
"version": "0.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
|
||||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/object-inspect": {
|
|
||||||
"version": "1.13.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
|
||||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/on-finished": {
|
|
||||||
"version": "2.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
|
||||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ee-first": "1.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/parseurl": {
|
|
||||||
"version": "1.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
|
||||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-to-regexp": {
|
|
||||||
"version": "0.1.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
|
||||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/proxy-addr": {
|
|
||||||
"version": "2.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
|
||||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"forwarded": "0.2.0",
|
|
||||||
"ipaddr.js": "1.9.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/qs": {
|
|
||||||
"version": "6.14.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
|
|
||||||
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"side-channel": "^1.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/range-parser": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/raw-body": {
|
|
||||||
"version": "2.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
|
|
||||||
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"bytes": "~3.1.2",
|
|
||||||
"http-errors": "~2.0.1",
|
|
||||||
"iconv-lite": "~0.4.24",
|
|
||||||
"unpipe": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/safe-buffer": {
|
|
||||||
"version": "5.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
|
||||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "patreon",
|
|
||||||
"url": "https://www.patreon.com/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "consulting",
|
|
||||||
"url": "https://feross.org/support"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/safer-buffer": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/send": {
|
|
||||||
"version": "0.19.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
|
|
||||||
"integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "2.0.0",
|
|
||||||
"destroy": "1.2.0",
|
|
||||||
"encodeurl": "~2.0.0",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"etag": "~1.8.1",
|
|
||||||
"fresh": "~0.5.2",
|
|
||||||
"http-errors": "~2.0.1",
|
|
||||||
"mime": "1.6.0",
|
|
||||||
"ms": "2.1.3",
|
|
||||||
"on-finished": "~2.4.1",
|
|
||||||
"range-parser": "~1.2.1",
|
|
||||||
"statuses": "~2.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/send/node_modules/ms": {
|
|
||||||
"version": "2.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/serve-static": {
|
|
||||||
"version": "1.16.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
|
|
||||||
"integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"encodeurl": "~2.0.0",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"send": "~0.19.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/setprototypeof": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/side-channel": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"object-inspect": "^1.13.3",
|
|
||||||
"side-channel-list": "^1.0.0",
|
|
||||||
"side-channel-map": "^1.0.1",
|
|
||||||
"side-channel-weakmap": "^1.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel-list": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"object-inspect": "^1.13.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel-map": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bound": "^1.0.2",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"get-intrinsic": "^1.2.5",
|
|
||||||
"object-inspect": "^1.13.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel-weakmap": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bound": "^1.0.2",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"get-intrinsic": "^1.2.5",
|
|
||||||
"object-inspect": "^1.13.3",
|
|
||||||
"side-channel-map": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/statuses": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/toidentifier": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/type-is": {
|
|
||||||
"version": "1.6.18",
|
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
|
||||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"media-typer": "0.3.0",
|
|
||||||
"mime-types": "~2.1.24"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unpipe": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/utils-merge": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vary": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "prole-db-manager",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Prole database management REST endpoint for backup operations",
|
|
||||||
"main": "server.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "node server.js",
|
|
||||||
"test": "node --test test/"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"express": "^4.21.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,161 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const express = require('express');
|
|
||||||
const { execFile } = require('child_process');
|
|
||||||
const { promisify } = require('util');
|
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile);
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
const PORT = process.env.PORT || 80;
|
|
||||||
const NAMESPACE = process.env.NAMESPACE || 'default';
|
|
||||||
const BARMAN_PLUGIN_NAME = process.env.BARMAN_PLUGIN_NAME || 'barman-cloud.cloudnative-pg.io';
|
|
||||||
const BARMAN_OBJECT_NAME = process.env.BARMAN_OBJECT_NAME || 'prole-db-barman-objectstore';
|
|
||||||
|
|
||||||
function buildBackupManifest(clusterName, backupName) {
|
|
||||||
return JSON.stringify({
|
|
||||||
apiVersion: 'postgresql.cnpg.io/v1',
|
|
||||||
kind: 'Backup',
|
|
||||||
metadata: {
|
|
||||||
name: backupName,
|
|
||||||
namespace: NAMESPACE
|
|
||||||
},
|
|
||||||
spec: {
|
|
||||||
method: 'plugin',
|
|
||||||
pluginConfiguration: {
|
|
||||||
name: BARMAN_PLUGIN_NAME,
|
|
||||||
parameters: {
|
|
||||||
barmanObjectName: BARMAN_OBJECT_NAME
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cluster: {
|
|
||||||
name: clusterName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateBackupName(clusterName) {
|
|
||||||
const ts = new Date().toISOString().replace(/[-:T]/g, '').replace(/\..+/, '');
|
|
||||||
return `${clusterName}-backup-${ts}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function triggerBackup(clusterName) {
|
|
||||||
const backupName = generateBackupName(clusterName);
|
|
||||||
const manifest = buildBackupManifest(clusterName, backupName);
|
|
||||||
|
|
||||||
const { stdout, stderr } = await execFileAsync('kubectl', [
|
|
||||||
'apply', '-n', NAMESPACE, '-f', '-'
|
|
||||||
], { input: manifest, timeout: 30000 });
|
|
||||||
|
|
||||||
return { backupName, stdout: stdout.trim(), stderr: stderr.trim() };
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getBackupStatus(clusterName) {
|
|
||||||
const { stdout } = await execFileAsync('kubectl', [
|
|
||||||
'get', 'backup', '-n', NAMESPACE,
|
|
||||||
'-l', `cnpg.io/cluster=${clusterName}`,
|
|
||||||
'-o', 'json'
|
|
||||||
], { timeout: 15000 });
|
|
||||||
|
|
||||||
return JSON.parse(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Health check
|
|
||||||
app.get('/health', (_req, res) => {
|
|
||||||
res.json({ status: 'ok', service: 'prole-db-manager' });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Trigger full backup
|
|
||||||
app.post('/backup/:clusterId/full', async (req, res) => {
|
|
||||||
const { clusterId } = req.params;
|
|
||||||
|
|
||||||
if (!clusterId || !/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(clusterId)) {
|
|
||||||
return res.status(400).json({
|
|
||||||
error: 'Invalid cluster ID',
|
|
||||||
message: 'Cluster ID must be a valid Kubernetes resource name'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await triggerBackup(clusterId);
|
|
||||||
res.status(201).json({
|
|
||||||
status: 'backup_triggered',
|
|
||||||
cluster: clusterId,
|
|
||||||
backupName: result.backupName,
|
|
||||||
namespace: NAMESPACE,
|
|
||||||
message: result.stdout
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
const statusCode = err.code === 'ENOENT' ? 503 : 500;
|
|
||||||
res.status(statusCode).json({
|
|
||||||
error: 'Backup trigger failed',
|
|
||||||
cluster: clusterId,
|
|
||||||
message: err.stderr || err.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// GET endpoint for convenience (e.g., curl from browser/grafana)
|
|
||||||
app.get('/backup/:clusterId/full', async (req, res) => {
|
|
||||||
const { clusterId } = req.params;
|
|
||||||
|
|
||||||
if (!clusterId || !/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(clusterId)) {
|
|
||||||
return res.status(400).json({
|
|
||||||
error: 'Invalid cluster ID',
|
|
||||||
message: 'Cluster ID must be a valid Kubernetes resource name'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await triggerBackup(clusterId);
|
|
||||||
res.status(201).json({
|
|
||||||
status: 'backup_triggered',
|
|
||||||
cluster: clusterId,
|
|
||||||
backupName: result.backupName,
|
|
||||||
namespace: NAMESPACE,
|
|
||||||
message: result.stdout
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
const statusCode = err.code === 'ENOENT' ? 503 : 500;
|
|
||||||
res.status(statusCode).json({
|
|
||||||
error: 'Backup trigger failed',
|
|
||||||
cluster: clusterId,
|
|
||||||
message: err.stderr || err.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Backup status
|
|
||||||
app.get('/backup/:clusterId/status', async (req, res) => {
|
|
||||||
const { clusterId } = req.params;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const backups = await getBackupStatus(clusterId);
|
|
||||||
res.json({
|
|
||||||
cluster: clusterId,
|
|
||||||
namespace: NAMESPACE,
|
|
||||||
backups: backups
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
res.status(500).json({
|
|
||||||
error: 'Failed to get backup status',
|
|
||||||
cluster: clusterId,
|
|
||||||
message: err.stderr || err.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Module exports for testing
|
|
||||||
module.exports = { app, buildBackupManifest, generateBackupName, triggerBackup, getBackupStatus };
|
|
||||||
|
|
||||||
// Start server only when run directly
|
|
||||||
if (require.main === module) {
|
|
||||||
app.listen(PORT, () => {
|
|
||||||
console.log(`prole-db-manager listening on port ${PORT}`);
|
|
||||||
console.log(` Namespace: ${NAMESPACE}`);
|
|
||||||
console.log(` Barman plugin: ${BARMAN_PLUGIN_NAME}`);
|
|
||||||
console.log(` Barman object: ${BARMAN_OBJECT_NAME}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,143 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const { describe, it, before, after, mock } = require('node:test');
|
|
||||||
const assert = require('node:assert/strict');
|
|
||||||
const http = require('http');
|
|
||||||
|
|
||||||
// Set test environment before requiring server
|
|
||||||
process.env.NAMESPACE = 'test-ns';
|
|
||||||
process.env.BARMAN_PLUGIN_NAME = 'barman-cloud.cloudnative-pg.io';
|
|
||||||
process.env.BARMAN_OBJECT_NAME = 'prole-db-barman-objectstore';
|
|
||||||
|
|
||||||
const { app, buildBackupManifest, generateBackupName } = require('../server');
|
|
||||||
|
|
||||||
function request(server, method, path) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const url = new URL(path, `http://localhost:${server.address().port}`);
|
|
||||||
const req = http.request(url, { method }, (res) => {
|
|
||||||
let body = '';
|
|
||||||
res.on('data', (chunk) => { body += chunk; });
|
|
||||||
res.on('end', () => {
|
|
||||||
try {
|
|
||||||
resolve({ status: res.statusCode, body: JSON.parse(body) });
|
|
||||||
} catch {
|
|
||||||
resolve({ status: res.statusCode, body });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
req.on('error', reject);
|
|
||||||
req.end();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('buildBackupManifest', () => {
|
|
||||||
it('should generate valid CNPG Backup manifest JSON', () => {
|
|
||||||
const manifest = buildBackupManifest('prole-db-a0001', 'test-backup-001');
|
|
||||||
const parsed = JSON.parse(manifest);
|
|
||||||
|
|
||||||
assert.equal(parsed.apiVersion, 'postgresql.cnpg.io/v1');
|
|
||||||
assert.equal(parsed.kind, 'Backup');
|
|
||||||
assert.equal(parsed.metadata.name, 'test-backup-001');
|
|
||||||
assert.equal(parsed.metadata.namespace, 'test-ns');
|
|
||||||
assert.equal(parsed.spec.method, 'plugin');
|
|
||||||
assert.equal(parsed.spec.pluginConfiguration.name, 'barman-cloud.cloudnative-pg.io');
|
|
||||||
assert.equal(parsed.spec.pluginConfiguration.parameters.barmanObjectName, 'prole-db-barman-objectstore');
|
|
||||||
assert.equal(parsed.spec.cluster.name, 'prole-db-a0001');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use configured namespace from environment', () => {
|
|
||||||
const manifest = buildBackupManifest('my-cluster', 'bk-1');
|
|
||||||
const parsed = JSON.parse(manifest);
|
|
||||||
assert.equal(parsed.metadata.namespace, 'test-ns');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('generateBackupName', () => {
|
|
||||||
it('should include cluster name as prefix', () => {
|
|
||||||
const name = generateBackupName('prole-db-a0001');
|
|
||||||
assert.ok(name.startsWith('prole-db-a0001-backup-'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should include a timestamp suffix', () => {
|
|
||||||
const name = generateBackupName('test-cluster');
|
|
||||||
const suffix = name.replace('test-cluster-backup-', '');
|
|
||||||
assert.ok(/^\d{14}$/.test(suffix), `Expected 14-digit timestamp, got: ${suffix}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate unique names on successive calls', () => {
|
|
||||||
const a = generateBackupName('c');
|
|
||||||
const b = generateBackupName('c');
|
|
||||||
// Within same second they may match, but structure should be valid
|
|
||||||
assert.ok(a.startsWith('c-backup-'));
|
|
||||||
assert.ok(b.startsWith('c-backup-'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('HTTP endpoints', () => {
|
|
||||||
let server;
|
|
||||||
|
|
||||||
before((_, done) => {
|
|
||||||
server = app.listen(0, done);
|
|
||||||
});
|
|
||||||
|
|
||||||
after((_, done) => {
|
|
||||||
server.close(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GET /health', () => {
|
|
||||||
it('should return ok status', async () => {
|
|
||||||
const res = await request(server, 'GET', '/health');
|
|
||||||
assert.equal(res.status, 200);
|
|
||||||
assert.equal(res.body.status, 'ok');
|
|
||||||
assert.equal(res.body.service, 'prole-db-manager');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('POST /backup/:clusterId/full', () => {
|
|
||||||
it('should reject invalid cluster IDs', async () => {
|
|
||||||
const res = await request(server, 'POST', '/backup/INVALID_NAME!/full');
|
|
||||||
assert.equal(res.status, 400);
|
|
||||||
assert.equal(res.body.error, 'Invalid cluster ID');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reject single-char cluster ID that fails regex', async () => {
|
|
||||||
const res = await request(server, 'POST', '/backup/-/full');
|
|
||||||
assert.equal(res.status, 400);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should attempt backup for valid cluster ID (will fail without kubectl)', async () => {
|
|
||||||
const res = await request(server, 'POST', '/backup/prole-db-a0001/full');
|
|
||||||
// Without kubectl available, expect 500 or 503
|
|
||||||
assert.ok([500, 503].includes(res.status));
|
|
||||||
assert.equal(res.body.error, 'Backup trigger failed');
|
|
||||||
assert.equal(res.body.cluster, 'prole-db-a0001');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GET /backup/:clusterId/full', () => {
|
|
||||||
it('should reject invalid cluster IDs', async () => {
|
|
||||||
const res = await request(server, 'GET', '/backup/INVALID!/full');
|
|
||||||
assert.equal(res.status, 400);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should attempt backup for valid cluster ID', async () => {
|
|
||||||
const res = await request(server, 'GET', '/backup/prole-db-a0001/full');
|
|
||||||
assert.ok([500, 503].includes(res.status));
|
|
||||||
assert.equal(res.body.cluster, 'prole-db-a0001');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('GET /backup/:clusterId/status', () => {
|
|
||||||
it('should return a response for status request', async () => {
|
|
||||||
const res = await request(server, 'GET', '/backup/prole-db-a0001/status');
|
|
||||||
assert.ok([200, 500].includes(res.status));
|
|
||||||
if (res.status === 200) {
|
|
||||||
assert.equal(res.body.cluster, 'prole-db-a0001');
|
|
||||||
assert.equal(res.body.namespace, 'test-ns');
|
|
||||||
} else {
|
|
||||||
assert.equal(res.body.error, 'Failed to get backup status');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
37
tests/installer/conftest.py
Normal file
37
tests/installer/conftest.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
"""
|
||||||
|
Shared fixtures for installer unit tests.
|
||||||
|
|
||||||
|
Resets stale tkinter MagicMocks before each test so that tests in
|
||||||
|
test_screen.py don't hit exhausted side-effect iterators left over from
|
||||||
|
test_navigation.py (which replaces sys.modules["tkinter"] at module level).
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import installer.screen as _screen_module
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def reset_tkinter_mock():
|
||||||
|
"""
|
||||||
|
Before each test, replace any stale tkinter MagicMock with a fresh
|
||||||
|
instance both in sys.modules and in installer.screen module namespace,
|
||||||
|
so TerminalConsole widget calls don't hit exhausted side-effect iterators.
|
||||||
|
"""
|
||||||
|
original_screen_tk = getattr(_screen_module, "tk", None)
|
||||||
|
if isinstance(original_screen_tk, MagicMock):
|
||||||
|
fresh = MagicMock()
|
||||||
|
_screen_module.tk = fresh
|
||||||
|
for mod_name in (
|
||||||
|
"tkinter",
|
||||||
|
"tkinter.ttk",
|
||||||
|
"tkinter.scrolledtext",
|
||||||
|
"tkinter.messagebox",
|
||||||
|
):
|
||||||
|
if mod_name in sys.modules and isinstance(sys.modules[mod_name], MagicMock):
|
||||||
|
sys.modules[mod_name] = fresh
|
||||||
|
yield
|
||||||
|
# Restore original reference so other tests in the suite are unaffected
|
||||||
|
if isinstance(original_screen_tk, MagicMock):
|
||||||
|
_screen_module.tk = original_screen_tk
|
||||||
365
tests/installer/test_config_helpers.py
Normal file
365
tests/installer/test_config_helpers.py
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for previously untested installer/config.py helpers.
|
||||||
|
|
||||||
|
Covers:
|
||||||
|
_get_secret_key_file, _get_file_key, normalize_version,
|
||||||
|
_extract_inline_vault_block, _try_read_ansible_vault_value,
|
||||||
|
get_dep_info (check_cmd/version_cmd paths), load_dependencies,
|
||||||
|
get_ui_icon_image_path, get_ui_background_image_path, get_properties
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from installer.config import (
|
||||||
|
_get_secret_key_file,
|
||||||
|
_get_file_key,
|
||||||
|
normalize_version,
|
||||||
|
_extract_yaml_scalar_from_text,
|
||||||
|
_extract_inline_vault_block,
|
||||||
|
_try_read_ansible_vault_value,
|
||||||
|
get_dep_info,
|
||||||
|
load_dependencies,
|
||||||
|
get_ui_icon_image_path,
|
||||||
|
get_ui_background_image_path,
|
||||||
|
get_properties,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _get_secret_key_file
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_get_secret_key_file_returns_path():
|
||||||
|
result = _get_secret_key_file()
|
||||||
|
assert isinstance(result, Path)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _get_file_key
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_get_file_key_generates_new_key(tmp_path):
|
||||||
|
key_path = tmp_path / "subdir" / "key.b64"
|
||||||
|
key = _get_file_key(key_path)
|
||||||
|
assert isinstance(key, bytes)
|
||||||
|
assert len(key) == 32
|
||||||
|
assert key_path.exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_file_key_reads_existing_key(tmp_path):
|
||||||
|
import base64
|
||||||
|
key_path = tmp_path / "key.b64"
|
||||||
|
raw_key = os.urandom(32)
|
||||||
|
key_path.write_text(base64.urlsafe_b64encode(raw_key).decode("utf-8"))
|
||||||
|
key = _get_file_key(key_path)
|
||||||
|
assert key == raw_key
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_file_key_with_wrong_length_content(tmp_path):
|
||||||
|
"""If existing key file has content that decodes to non-32 bytes, still returns bytes."""
|
||||||
|
key_path = tmp_path / "key.b64"
|
||||||
|
# 'dGVzdA==' is valid base64 but decodes to only 4 bytes ('test')
|
||||||
|
key_path.write_text("dGVzdA==")
|
||||||
|
key = _get_file_key(key_path)
|
||||||
|
assert isinstance(key, bytes)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# normalize_version — edge cases
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_normalize_version_empty():
|
||||||
|
assert normalize_version("") == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_version_simple():
|
||||||
|
assert normalize_version("1.2.3") == "01.02.03"
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_version_no_regex_match():
|
||||||
|
"""Text with no digit sequence matching the pattern falls back to findall."""
|
||||||
|
# A string with digits but no contiguous group matching pattern
|
||||||
|
result = normalize_version("v5")
|
||||||
|
assert "05" in result
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_version_no_digits():
|
||||||
|
"""Text with no digits at all returns stripped text."""
|
||||||
|
result = normalize_version("nodigits")
|
||||||
|
assert result == "nodigits"
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_version_prefix():
|
||||||
|
result = normalize_version("v1.2.3-beta")
|
||||||
|
assert result == "01.02.03"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _extract_yaml_scalar_from_text
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_extract_yaml_scalar_simple():
|
||||||
|
text = 'key: somevalue\nother: x\n'
|
||||||
|
assert _extract_yaml_scalar_from_text(text, "key") == "somevalue"
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_yaml_scalar_missing_key():
|
||||||
|
assert _extract_yaml_scalar_from_text("a: b\n", "missing") == ""
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _extract_inline_vault_block
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_extract_inline_vault_block_not_found():
|
||||||
|
text = "key: value\nother: x\n"
|
||||||
|
result = _extract_inline_vault_block(text, "missing")
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_inline_vault_block_plain_value():
|
||||||
|
"""Key found but block doesn't start with $ANSIBLE_VAULT → return ''."""
|
||||||
|
text = "mykey:\n just some plain text\n more text\n"
|
||||||
|
result = _extract_inline_vault_block(text, "mykey")
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_inline_vault_block_ansible_vault():
|
||||||
|
text = (
|
||||||
|
"mykey:\n"
|
||||||
|
" $ANSIBLE_VAULT;1.1;AES256\n"
|
||||||
|
" 6162636465666768\n"
|
||||||
|
)
|
||||||
|
result = _extract_inline_vault_block(text, "mykey")
|
||||||
|
assert "$ANSIBLE_VAULT" in result
|
||||||
|
|
||||||
|
|
||||||
|
def test_extract_inline_vault_block_no_block_lines():
|
||||||
|
"""Key found but indented block is empty → return ''."""
|
||||||
|
text = "mykey:\nother: x\n"
|
||||||
|
result = _extract_inline_vault_block(text, "mykey")
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _try_read_ansible_vault_value
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_try_read_ansible_vault_value_file_not_found(tmp_path):
|
||||||
|
result = _try_read_ansible_vault_value(tmp_path / "nonexistent.cfg", "mykey")
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_try_read_ansible_vault_value_plain_text(tmp_path):
|
||||||
|
vault = tmp_path / "prole.cfg"
|
||||||
|
vault.write_text("db_password: supersecret\n")
|
||||||
|
result = _try_read_ansible_vault_value(vault, "db_password")
|
||||||
|
assert result == "supersecret"
|
||||||
|
|
||||||
|
|
||||||
|
def test_try_read_ansible_vault_value_plain_text_vault_marker_skipped(tmp_path):
|
||||||
|
"""Plain value starting with $ANSIBLE_VAULT is treated as encrypted → skip."""
|
||||||
|
vault = tmp_path / "prole.cfg"
|
||||||
|
vault.write_text("db_password: $ANSIBLE_VAULT;1.1;AES256\n")
|
||||||
|
# No vault password file → returns ""
|
||||||
|
with patch.dict(os.environ, {"ANSIBLE_VAULT_PASSWORD_FILE": ""}, clear=False), \
|
||||||
|
patch("installer.config.shutil.which", return_value=None):
|
||||||
|
result = _try_read_ansible_vault_value(vault, "db_password")
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_try_read_ansible_vault_value_no_password_file(tmp_path):
|
||||||
|
vault = tmp_path / "prole.cfg"
|
||||||
|
vault.write_text("db_password: $ANSIBLE_VAULT;1.1;AES256\n")
|
||||||
|
with patch.dict(os.environ, {"ANSIBLE_VAULT_PASSWORD_FILE": ""}, clear=False), \
|
||||||
|
patch("installer.config.shutil.which", return_value=None):
|
||||||
|
result = _try_read_ansible_vault_value(vault, "db_password")
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_try_read_ansible_vault_value_with_vault_success(tmp_path):
|
||||||
|
vault = tmp_path / "prole.cfg"
|
||||||
|
vault.write_text("db_password: $ANSIBLE_VAULT;1.1;AES256\n")
|
||||||
|
pw_file = tmp_path / ".vault_pass"
|
||||||
|
pw_file.write_text("mypassword\n")
|
||||||
|
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 0
|
||||||
|
mock_result.stdout = "db_password: decryptedvalue\n"
|
||||||
|
|
||||||
|
with patch.dict(os.environ, {"ANSIBLE_VAULT_PASSWORD_FILE": str(pw_file)}, clear=False), \
|
||||||
|
patch("installer.config.shutil.which", return_value="/usr/bin/ansible-vault"), \
|
||||||
|
patch("installer.config.subprocess.run", return_value=mock_result):
|
||||||
|
result = _try_read_ansible_vault_value(vault, "db_password")
|
||||||
|
assert result == "decryptedvalue"
|
||||||
|
|
||||||
|
|
||||||
|
def test_try_read_ansible_vault_value_vault_fails_no_inline(tmp_path):
|
||||||
|
vault = tmp_path / "prole.cfg"
|
||||||
|
vault.write_text("db_password: $ANSIBLE_VAULT;1.1;AES256\n")
|
||||||
|
pw_file = tmp_path / ".vault_pass"
|
||||||
|
pw_file.write_text("mypassword\n")
|
||||||
|
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 1
|
||||||
|
mock_result.stdout = ""
|
||||||
|
|
||||||
|
with patch.dict(os.environ, {"ANSIBLE_VAULT_PASSWORD_FILE": str(pw_file)}, clear=False), \
|
||||||
|
patch("installer.config.shutil.which", return_value="/usr/bin/ansible-vault"), \
|
||||||
|
patch("installer.config.subprocess.run", return_value=mock_result):
|
||||||
|
result = _try_read_ansible_vault_value(vault, "db_password")
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_try_read_ansible_vault_value_uses_project_root_vault_pass(tmp_path):
|
||||||
|
"""Falls back to PROJECT_ROOT/.vault_pass if no env var set."""
|
||||||
|
vault = tmp_path / "prole.cfg"
|
||||||
|
vault.write_text("mykey: plainvalue\n")
|
||||||
|
with patch.dict(os.environ, {"ANSIBLE_VAULT_PASSWORD_FILE": ""}, clear=False):
|
||||||
|
result = _try_read_ansible_vault_value(vault, "mykey")
|
||||||
|
assert result == "plainvalue"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# get_dep_info — check_cmd and version_cmd paths
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _mock_run_results(*return_codes_and_outputs):
|
||||||
|
"""Build a side_effect list for subprocess.run."""
|
||||||
|
results = []
|
||||||
|
for rc, out in return_codes_and_outputs:
|
||||||
|
m = MagicMock()
|
||||||
|
m.returncode = rc
|
||||||
|
m.stdout = out
|
||||||
|
results.append(m)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_dep_info_bin_not_found():
|
||||||
|
dep = {"id": "mytool", "name": "mytool", "bin": "mytool"}
|
||||||
|
with patch("installer.config.subprocess.run",
|
||||||
|
return_value=MagicMock(returncode=1, stdout="")):
|
||||||
|
ok, loc, ver = get_dep_info(dep)
|
||||||
|
assert ok is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_dep_info_bin_found():
|
||||||
|
dep = {"id": "mytool", "name": "mytool", "bin": "mytool"}
|
||||||
|
with patch("installer.config.subprocess.run",
|
||||||
|
return_value=MagicMock(returncode=0, stdout="/usr/bin/mytool")):
|
||||||
|
ok, loc, ver = get_dep_info(dep)
|
||||||
|
assert ok is True
|
||||||
|
assert loc == "/usr/bin/mytool"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_dep_info_check_cmd_success():
|
||||||
|
dep = {"id": "docker", "name": "docker", "bin": "docker",
|
||||||
|
"check_cmd": "docker info"}
|
||||||
|
run_results = [
|
||||||
|
MagicMock(returncode=0, stdout="/usr/bin/docker"), # command -v
|
||||||
|
MagicMock(returncode=0, stdout="Docker version 24.0"), # check_cmd
|
||||||
|
]
|
||||||
|
with patch("installer.config.subprocess.run", side_effect=run_results):
|
||||||
|
ok, loc, ver = get_dep_info(dep)
|
||||||
|
assert ok is True
|
||||||
|
assert "Docker" in (ver or "")
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_dep_info_version_cmd_success():
|
||||||
|
dep = {"id": "kubectl", "name": "kubectl", "bin": "kubectl",
|
||||||
|
"version_cmd": "kubectl version --client --short"}
|
||||||
|
run_results = [
|
||||||
|
MagicMock(returncode=0, stdout="/usr/bin/kubectl"), # command -v
|
||||||
|
MagicMock(returncode=0, stdout="Client Version: v1.28.0"), # version_cmd
|
||||||
|
]
|
||||||
|
with patch("installer.config.subprocess.run", side_effect=run_results):
|
||||||
|
ok, loc, ver = get_dep_info(dep)
|
||||||
|
assert ok is True
|
||||||
|
assert ver == "Client Version: v1.28.0"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_dep_info_exception_returns_false():
|
||||||
|
dep = {"id": "broken", "name": "broken", "bin": "broken"}
|
||||||
|
with patch("installer.config.subprocess.run", side_effect=OSError("broken")):
|
||||||
|
ok, loc, ver = get_dep_info(dep)
|
||||||
|
assert ok is False
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# load_dependencies
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_load_dependencies_no_refresh():
|
||||||
|
deps = load_dependencies(refresh=False)
|
||||||
|
assert isinstance(deps, list)
|
||||||
|
assert all(isinstance(d, dict) for d in deps)
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_dependencies_with_refresh():
|
||||||
|
with patch("installer.config.get_dep_info",
|
||||||
|
return_value=(True, "/usr/bin/test", "1.0")):
|
||||||
|
deps = load_dependencies(refresh=True)
|
||||||
|
assert isinstance(deps, list)
|
||||||
|
for dep in deps:
|
||||||
|
assert dep.get("installed") is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_dependencies_refresh_exception_handled():
|
||||||
|
"""If get_dep_info raises, dep.installed should be False."""
|
||||||
|
with patch("installer.config.get_dep_info", side_effect=RuntimeError("oops")):
|
||||||
|
deps = load_dependencies(refresh=True)
|
||||||
|
for dep in deps:
|
||||||
|
assert dep.get("installed") is False
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# get_ui_icon_image_path / get_ui_background_image_path
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_get_ui_icon_image_path_returns_path():
|
||||||
|
result = get_ui_icon_image_path()
|
||||||
|
assert isinstance(result, Path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_ui_icon_image_path_existing_file(tmp_path):
|
||||||
|
img = tmp_path / "myicon.png"
|
||||||
|
img.write_bytes(b"PNG")
|
||||||
|
with patch("installer.config.get_config_value", return_value="myicon.png"), \
|
||||||
|
patch("installer.config.PROJECT_ROOT", tmp_path):
|
||||||
|
result = get_ui_icon_image_path()
|
||||||
|
assert result == img.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_ui_background_image_path_returns_path():
|
||||||
|
result = get_ui_background_image_path()
|
||||||
|
assert isinstance(result, Path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_ui_background_image_path_existing_file(tmp_path):
|
||||||
|
img = tmp_path / "bg.png"
|
||||||
|
img.write_bytes(b"PNG")
|
||||||
|
with patch("installer.config.get_config_value", return_value="bg.png"), \
|
||||||
|
patch("installer.config.PROJECT_ROOT", tmp_path):
|
||||||
|
result = get_ui_background_image_path()
|
||||||
|
assert result == img.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# get_properties
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_get_properties_returns_dict():
|
||||||
|
result = get_properties()
|
||||||
|
assert isinstance(result, dict)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_properties_consistent(tmp_path):
|
||||||
|
"""Calling get_properties twice returns consistent results."""
|
||||||
|
r1 = get_properties()
|
||||||
|
r2 = get_properties()
|
||||||
|
assert r1 == r2
|
||||||
237
tests/installer/test_controller.py
Normal file
237
tests/installer/test_controller.py
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for installer/core/controller.py
|
||||||
|
|
||||||
|
Covers ProleController: check_docker_running, get_prole_db_version, run_script.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from installer.core.controller import ProleController
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _make_controller(tmp_path: Path, **kwargs) -> ProleController:
|
||||||
|
return ProleController(project_root=tmp_path, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def _scaffold_project(tmp_path: Path) -> None:
|
||||||
|
"""Create minimum project structure expected by run_script."""
|
||||||
|
(tmp_path / "etc").mkdir(parents=True, exist_ok=True)
|
||||||
|
(tmp_path / "conf" / "postgresql").mkdir(parents=True, exist_ok=True)
|
||||||
|
(tmp_path / "prole-db").mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# check_docker_running
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_check_docker_running_success(tmp_path):
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
with patch("installer.core.controller.subprocess.run") as mock_run:
|
||||||
|
mock_run.return_value = MagicMock(returncode=0)
|
||||||
|
assert ctrl.check_docker_running() is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_docker_running_called_process_error(tmp_path):
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
with patch("installer.core.controller.subprocess.run",
|
||||||
|
side_effect=subprocess.CalledProcessError(1, "docker")):
|
||||||
|
assert ctrl.check_docker_running() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_docker_running_file_not_found(tmp_path):
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
with patch("installer.core.controller.subprocess.run",
|
||||||
|
side_effect=FileNotFoundError("docker not found")):
|
||||||
|
assert ctrl.check_docker_running() is False
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# get_prole_db_version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_get_prole_db_version_no_files(tmp_path):
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
version = ctrl.get_prole_db_version()
|
||||||
|
# Falls back to defaults: 17.7 and 43 (zero-padded to 3 digits → 043)
|
||||||
|
assert version == "17.7-043"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_prole_db_version_with_files(tmp_path):
|
||||||
|
(tmp_path / "conf" / "postgresql").mkdir(parents=True)
|
||||||
|
(tmp_path / "conf" / "postgresql" / ".version").write_text("18\n")
|
||||||
|
(tmp_path / "prole-db").mkdir(parents=True)
|
||||||
|
(tmp_path / "prole-db" / ".version").write_text("88\n")
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
version = ctrl.get_prole_db_version()
|
||||||
|
assert version == "18-088"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_prole_db_version_non_numeric_release(tmp_path):
|
||||||
|
(tmp_path / "conf" / "postgresql").mkdir(parents=True)
|
||||||
|
(tmp_path / "conf" / "postgresql" / ".version").write_text("18\n")
|
||||||
|
(tmp_path / "prole-db").mkdir(parents=True)
|
||||||
|
(tmp_path / "prole-db" / ".version").write_text("latest\n")
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
version = ctrl.get_prole_db_version()
|
||||||
|
# Non-numeric release is not zero-padded
|
||||||
|
assert version == "18-latest"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_prole_db_version_only_pg_version(tmp_path):
|
||||||
|
(tmp_path / "conf" / "postgresql").mkdir(parents=True)
|
||||||
|
(tmp_path / "conf" / "postgresql" / ".version").write_text("17\n")
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
version = ctrl.get_prole_db_version()
|
||||||
|
assert version.startswith("17-")
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_prole_db_version_pads_single_digit_release(tmp_path):
|
||||||
|
(tmp_path / "prole-db").mkdir(parents=True)
|
||||||
|
(tmp_path / "prole-db" / ".version").write_text("5\n")
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
version = ctrl.get_prole_db_version()
|
||||||
|
assert version.endswith("-005")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# run_script
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_run_script_basic(tmp_path):
|
||||||
|
"""run_script executes a bash script and captures stdout via on_line."""
|
||||||
|
_scaffold_project(tmp_path)
|
||||||
|
script = tmp_path / "etc" / "hello.sh"
|
||||||
|
script.write_text("#!/bin/bash\necho 'hello from script'\n")
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
lines: list[str] = []
|
||||||
|
rc = ctrl.run_script(
|
||||||
|
"hello.sh",
|
||||||
|
env={"PROLE_HOME": str(tmp_path)},
|
||||||
|
on_line=lambda l: lines.append(l),
|
||||||
|
)
|
||||||
|
assert rc == 0
|
||||||
|
assert any("hello from script" in l for l in lines)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_script_nonzero_exit(tmp_path):
|
||||||
|
_scaffold_project(tmp_path)
|
||||||
|
script = tmp_path / "etc" / "fail.sh"
|
||||||
|
script.write_text("#!/bin/bash\nexit 42\n")
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
rc = ctrl.run_script("fail.sh", env={"PROLE_HOME": str(tmp_path)})
|
||||||
|
assert rc == 42
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_script_with_args(tmp_path):
|
||||||
|
_scaffold_project(tmp_path)
|
||||||
|
script = tmp_path / "etc" / "args.sh"
|
||||||
|
script.write_text("#!/bin/bash\necho \"ARG:$1\"\n")
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
lines: list[str] = []
|
||||||
|
rc = ctrl.run_script(
|
||||||
|
"args.sh",
|
||||||
|
args=["myvalue"],
|
||||||
|
env={"PROLE_HOME": str(tmp_path)},
|
||||||
|
on_line=lambda l: lines.append(l),
|
||||||
|
)
|
||||||
|
assert rc == 0
|
||||||
|
assert any("ARG:myvalue" in l for l in lines)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_script_copies_k8s_and_conf(tmp_path):
|
||||||
|
"""Covers the k8s and conf sync paths when source dirs exist."""
|
||||||
|
_scaffold_project(tmp_path)
|
||||||
|
# Create source k8s and conf dirs
|
||||||
|
(tmp_path / "k8s").mkdir()
|
||||||
|
(tmp_path / "k8s" / "test.yaml").write_text("kind: Pod\n")
|
||||||
|
(tmp_path / "conf" / "prole.cfg").write_text("[General]\n")
|
||||||
|
script = tmp_path / "etc" / "synced.sh"
|
||||||
|
script.write_text("#!/bin/bash\necho ok\n")
|
||||||
|
target = tmp_path / "prole_home_test"
|
||||||
|
target.mkdir()
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
rc = ctrl.run_script("synced.sh", env={"PROLE_HOME": str(target)})
|
||||||
|
assert rc == 0
|
||||||
|
# k8s and conf should have been synced
|
||||||
|
assert (target / "k8s").exists()
|
||||||
|
assert (target / "conf").exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_script_copies_db_version(tmp_path):
|
||||||
|
"""Covers the prole-db/.version copy path."""
|
||||||
|
_scaffold_project(tmp_path)
|
||||||
|
(tmp_path / "prole-db" / ".version").write_text("88\n")
|
||||||
|
script = tmp_path / "etc" / "dbver.sh"
|
||||||
|
script.write_text("#!/bin/bash\necho ok\n")
|
||||||
|
target = tmp_path / "prole_home_db"
|
||||||
|
target.mkdir()
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
rc = ctrl.run_script("dbver.sh", env={"PROLE_HOME": str(target)})
|
||||||
|
assert rc == 0
|
||||||
|
assert (target / "prole-db" / ".version").read_text().strip() == "88"
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_script_default_prole_home(tmp_path):
|
||||||
|
"""Covers the PROLE_HOME fallback to ~/.prole when not in env."""
|
||||||
|
_scaffold_project(tmp_path)
|
||||||
|
script = tmp_path / "etc" / "home.sh"
|
||||||
|
script.write_text("#!/bin/bash\necho hometest\n")
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
# Provide env without PROLE_HOME so it falls back to Path.home() / ".prole"
|
||||||
|
fake_home = tmp_path / "fakehome"
|
||||||
|
fake_home.mkdir()
|
||||||
|
with patch("installer.core.controller.Path.home", return_value=fake_home):
|
||||||
|
rc = ctrl.run_script("home.sh", env={})
|
||||||
|
assert rc == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_script_stderr_to_stdout_false(tmp_path):
|
||||||
|
"""Covers the stderr_to_stdout=False thread path."""
|
||||||
|
_scaffold_project(tmp_path)
|
||||||
|
script = tmp_path / "etc" / "stderr.sh"
|
||||||
|
script.write_text("#!/bin/bash\necho 'out'\n>&2 echo 'err'\n")
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
stderr_lines: list[str] = []
|
||||||
|
rc = ctrl.run_script(
|
||||||
|
"stderr.sh",
|
||||||
|
env={"PROLE_HOME": str(tmp_path)},
|
||||||
|
stderr_to_stdout=False,
|
||||||
|
on_stderr_line=lambda l: stderr_lines.append(l),
|
||||||
|
)
|
||||||
|
assert rc == 0
|
||||||
|
assert any("err" in l for l in stderr_lines)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_script_verbose_sets_env(tmp_path):
|
||||||
|
"""Covers the verbose env var injection path."""
|
||||||
|
_scaffold_project(tmp_path)
|
||||||
|
script = tmp_path / "etc" / "verbose.sh"
|
||||||
|
script.write_text("#!/bin/bash\necho \"VERBOSE:${PROLE_VERBOSE:-unset}\"\n")
|
||||||
|
ctrl = _make_controller(tmp_path, verbose=True)
|
||||||
|
lines: list[str] = []
|
||||||
|
rc = ctrl.run_script(
|
||||||
|
"verbose.sh",
|
||||||
|
env={"PROLE_HOME": str(tmp_path)},
|
||||||
|
on_line=lambda l: lines.append(l),
|
||||||
|
)
|
||||||
|
assert rc == 0
|
||||||
|
assert any("VERBOSE:1" in l for l in lines)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_script_missing_source_script(tmp_path):
|
||||||
|
"""If the source script doesn't exist, target script also won't exist — graceful."""
|
||||||
|
_scaffold_project(tmp_path)
|
||||||
|
# Don't create the script — run_script should still try to run it
|
||||||
|
ctrl = _make_controller(tmp_path)
|
||||||
|
# Should return non-zero (bash can't find the file) but not raise
|
||||||
|
rc = ctrl.run_script("nonexistent.sh", env={"PROLE_HOME": str(tmp_path)})
|
||||||
|
assert rc != 0
|
||||||
132
tests/installer/test_deploy_helpers.py
Normal file
132
tests/installer/test_deploy_helpers.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for installer/deploy.py helpers.
|
||||||
|
|
||||||
|
Covers check_xcode_tools and build_prole_app_core error paths without
|
||||||
|
requiring a real macOS Xcode build environment.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from installer.deploy import check_xcode_tools, build_prole_app_core
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# check_xcode_tools
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_check_xcode_tools_not_darwin():
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Linux"):
|
||||||
|
assert check_xcode_tools() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_xcode_tools_not_darwin_windows():
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Windows"):
|
||||||
|
assert check_xcode_tools() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_xcode_tools_darwin_success():
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 0
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Darwin"), \
|
||||||
|
patch("installer.deploy.subprocess.run", return_value=mock_result):
|
||||||
|
assert check_xcode_tools() is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_xcode_tools_darwin_failure():
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 1
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Darwin"), \
|
||||||
|
patch("installer.deploy.subprocess.run", return_value=mock_result):
|
||||||
|
assert check_xcode_tools() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_xcode_tools_darwin_exception():
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Darwin"), \
|
||||||
|
patch("installer.deploy.subprocess.run", side_effect=FileNotFoundError("xcrun not found")):
|
||||||
|
assert check_xcode_tools() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_xcode_tools_darwin_timeout():
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Darwin"), \
|
||||||
|
patch("installer.deploy.subprocess.run", side_effect=subprocess.TimeoutExpired("xcrun", 10)):
|
||||||
|
assert check_xcode_tools() is False
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# build_prole_app_core — error paths
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_build_prole_app_core_not_darwin(tmp_path):
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Linux"):
|
||||||
|
with pytest.raises(Exception, match="macOS"):
|
||||||
|
build_prole_app_core(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_prole_app_core_no_xcode(tmp_path):
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Darwin"), \
|
||||||
|
patch("installer.deploy.check_xcode_tools", return_value=False):
|
||||||
|
with pytest.raises(Exception, match="[Xx]code"):
|
||||||
|
build_prole_app_core(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_prole_app_core_no_build_script(tmp_path):
|
||||||
|
# xcode ok but prole-app/build.sh missing
|
||||||
|
prole_app = tmp_path / "prole-app"
|
||||||
|
prole_app.mkdir()
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Darwin"), \
|
||||||
|
patch("installer.deploy.check_xcode_tools", return_value=True):
|
||||||
|
with pytest.raises(Exception, match="[Bb]uild script"):
|
||||||
|
build_prole_app_core(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_prole_app_core_build_fails(tmp_path):
|
||||||
|
prole_app = tmp_path / "prole-app"
|
||||||
|
prole_app.mkdir()
|
||||||
|
build_sh = prole_app / "build.sh"
|
||||||
|
build_sh.write_text("#!/bin/bash\nexit 1\n")
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 1
|
||||||
|
mock_result.stderr = "build error"
|
||||||
|
mock_result.stdout = ""
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Darwin"), \
|
||||||
|
patch("installer.deploy.check_xcode_tools", return_value=True), \
|
||||||
|
patch("installer.deploy.subprocess.run", return_value=mock_result):
|
||||||
|
with pytest.raises(Exception, match="[Bb]uild failed"):
|
||||||
|
build_prole_app_core(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_prole_app_core_app_missing_after_build(tmp_path):
|
||||||
|
prole_app = tmp_path / "prole-app"
|
||||||
|
prole_app.mkdir()
|
||||||
|
build_sh = prole_app / "build.sh"
|
||||||
|
build_sh.write_text("#!/bin/bash\nexit 0\n")
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 0
|
||||||
|
mock_result.stderr = ""
|
||||||
|
mock_result.stdout = "Build OK"
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Darwin"), \
|
||||||
|
patch("installer.deploy.check_xcode_tools", return_value=True), \
|
||||||
|
patch("installer.deploy.subprocess.run", return_value=mock_result):
|
||||||
|
# dist/Prole.app is not created → should raise
|
||||||
|
with pytest.raises(Exception, match="[Pp]role.app"):
|
||||||
|
build_prole_app_core(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_prole_app_core_success(tmp_path):
|
||||||
|
prole_app = tmp_path / "prole-app"
|
||||||
|
(prole_app / "dist").mkdir(parents=True)
|
||||||
|
app_path = prole_app / "dist" / "Prole.app"
|
||||||
|
app_path.mkdir()
|
||||||
|
build_sh = prole_app / "build.sh"
|
||||||
|
build_sh.write_text("#!/bin/bash\nexit 0\n")
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 0
|
||||||
|
with patch("installer.deploy.platform.system", return_value="Darwin"), \
|
||||||
|
patch("installer.deploy.check_xcode_tools", return_value=True), \
|
||||||
|
patch("installer.deploy.subprocess.run", return_value=mock_result):
|
||||||
|
result = build_prole_app_core(tmp_path)
|
||||||
|
assert result == app_path
|
||||||
649
tests/installer/test_milestones_extended.py
Normal file
649
tests/installer/test_milestones_extended.py
Normal file
@ -0,0 +1,649 @@
|
|||||||
|
"""
|
||||||
|
Extended unit tests for installer/core/milestones.py
|
||||||
|
|
||||||
|
Covers the previously untested milestone classes:
|
||||||
|
_stream_line, DatabaseCreationMilestone, DockerBuildMilestone,
|
||||||
|
GitOpsMilestone, KerberosMilestone, SupabaseMilestone
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import MagicMock, patch, call
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from installer.core.milestones import (
|
||||||
|
_stream_line,
|
||||||
|
DatabaseCreationMilestone,
|
||||||
|
DockerBuildMilestone,
|
||||||
|
GitOpsMilestone,
|
||||||
|
KerberosMilestone,
|
||||||
|
SupabaseMilestone,
|
||||||
|
DependenciesMilestone,
|
||||||
|
InitializationScriptsMilestone,
|
||||||
|
)
|
||||||
|
from installer.state import InstallerState
|
||||||
|
from installer.core.controller import ProleController
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _make_state(**inputs) -> InstallerState:
|
||||||
|
state = InstallerState()
|
||||||
|
state.inputs.update(inputs)
|
||||||
|
state.controller = MagicMock(spec=ProleController)
|
||||||
|
state.controller.project_root = Path("/tmp/fake-prole")
|
||||||
|
state.controller.get_prole_db_version.return_value = "18-001"
|
||||||
|
state.controller.run_script.return_value = 0
|
||||||
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _stream_line
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_stream_line_writes_to_stdout(capsys):
|
||||||
|
_stream_line("hello world\n")
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
assert "hello world" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_stream_line_empty_string(capsys):
|
||||||
|
_stream_line("")
|
||||||
|
# Should not raise
|
||||||
|
|
||||||
|
|
||||||
|
def test_stream_line_tolerates_broken_stdout():
|
||||||
|
with patch("sys.stdout") as mock_stdout:
|
||||||
|
mock_stdout.write.side_effect = OSError("broken pipe")
|
||||||
|
# Should not raise
|
||||||
|
_stream_line("test")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DatabaseCreationMilestone
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_database_creation_defaults():
|
||||||
|
state = _make_state()
|
||||||
|
milestone = DatabaseCreationMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config._encrypt_cfg_secret", return_value="enc:xxx"), \
|
||||||
|
patch("installer.core.milestones.inst_config._is_prole_secret", return_value=False):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data.get("Database Creation", {}).get("DB_NAME") == "default"
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_creation_with_provided_values():
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_password.db_namespace": "mydb",
|
||||||
|
"init_password.db_username": "alice",
|
||||||
|
"init_password.db_password": "secret123",
|
||||||
|
"init_password.generate_ssh_key": "False",
|
||||||
|
})
|
||||||
|
milestone = DatabaseCreationMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config._encrypt_cfg_secret", return_value="enc:xxx"), \
|
||||||
|
patch("installer.core.milestones.inst_config._is_prole_secret", return_value=False):
|
||||||
|
milestone.execute(state)
|
||||||
|
db = state.config_data.get("Database Creation", {})
|
||||||
|
assert db["DB_USER"] == "alice"
|
||||||
|
assert db["NAMESPACE"] == "mydb"
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_creation_generates_password_when_missing():
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_password.db_username": "bob",
|
||||||
|
"init_password.generate_ssh_key": "False",
|
||||||
|
})
|
||||||
|
milestone = DatabaseCreationMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config._encrypt_cfg_secret", return_value="enc:xxx"), \
|
||||||
|
patch("installer.core.milestones.inst_config._is_prole_secret", return_value=False):
|
||||||
|
milestone.execute(state)
|
||||||
|
# Password should have been generated and stored
|
||||||
|
assert state.inputs.get("init_password.db_password")
|
||||||
|
assert len(state.inputs["init_password.db_password"]) >= 24
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_creation_resolves_secret_ref():
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_password.db_username": "bob",
|
||||||
|
"init_password.db_password": "${openbao:kv/prole/db}",
|
||||||
|
"init_password.generate_ssh_key": "False",
|
||||||
|
})
|
||||||
|
milestone = DatabaseCreationMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config._resolve_secret_value", return_value="${openbao:kv/prole/db}"), \
|
||||||
|
patch("installer.core.milestones.inst_config._encrypt_cfg_secret", return_value="enc:xxx"), \
|
||||||
|
patch("installer.core.milestones.inst_config._is_prole_secret", return_value=False):
|
||||||
|
milestone.execute(state)
|
||||||
|
# Unresolvable ref → new password generated
|
||||||
|
pw = state.inputs.get("init_password.db_password", "")
|
||||||
|
assert not pw.startswith("${")
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_creation_resolves_secret_ref_success():
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_password.db_username": "bob",
|
||||||
|
"init_password.db_password": "${openbao:kv/prole/db}",
|
||||||
|
"init_password.generate_ssh_key": "False",
|
||||||
|
})
|
||||||
|
milestone = DatabaseCreationMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config._resolve_secret_value", return_value="realpassword"), \
|
||||||
|
patch("installer.core.milestones.inst_config._encrypt_cfg_secret", return_value="enc:xxx"), \
|
||||||
|
patch("installer.core.milestones.inst_config._is_prole_secret", return_value=False):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.inputs["init_password.db_password"] == "realpassword"
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_creation_with_progress():
|
||||||
|
state = _make_state(**{"init_password.generate_ssh_key": "False"})
|
||||||
|
milestone = DatabaseCreationMilestone()
|
||||||
|
progress = MagicMock()
|
||||||
|
with patch("installer.core.milestones.inst_config._encrypt_cfg_secret", return_value="enc:xxx"), \
|
||||||
|
patch("installer.core.milestones.inst_config._is_prole_secret", return_value=False):
|
||||||
|
milestone.execute(state, progress=progress)
|
||||||
|
progress.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_database_creation_uses_env_namespace_fallback():
|
||||||
|
state = _make_state(**{
|
||||||
|
"env_setup.NAMESPACE": "staging",
|
||||||
|
"init_password.generate_ssh_key": "False",
|
||||||
|
})
|
||||||
|
milestone = DatabaseCreationMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config._encrypt_cfg_secret", return_value="enc:xxx"), \
|
||||||
|
patch("installer.core.milestones.inst_config._is_prole_secret", return_value=False):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["Database Creation"]["NAMESPACE"] == "staging"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DockerBuildMilestone
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_docker_build_disabled():
|
||||||
|
state = _make_state(**{"init_db_build.run_build": "False"})
|
||||||
|
milestone = DockerBuildMilestone()
|
||||||
|
milestone.execute(state)
|
||||||
|
state.controller.run_script.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_docker_build_runs_script():
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_db_build.run_build": "True",
|
||||||
|
"init_cluster.cluster_env": "dev",
|
||||||
|
})
|
||||||
|
milestone = DockerBuildMilestone()
|
||||||
|
with patch("installer.core.env._normalize_cluster_env", return_value="dev"):
|
||||||
|
milestone.execute(state)
|
||||||
|
state.controller.run_script.assert_called_once()
|
||||||
|
args, kwargs = state.controller.run_script.call_args
|
||||||
|
assert args[0] == "build_db.sh"
|
||||||
|
|
||||||
|
|
||||||
|
def test_docker_build_non_dev_env_adds_registry():
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_db_build.run_build": "True",
|
||||||
|
"init_cluster.cluster_env": "prod",
|
||||||
|
"init_cluster.k3s_server_url": "https://myrddin.prole.org:6443",
|
||||||
|
})
|
||||||
|
milestone = DockerBuildMilestone()
|
||||||
|
with patch("installer.core.env._normalize_cluster_env", return_value="prod"):
|
||||||
|
milestone.execute(state)
|
||||||
|
state.controller.run_script.assert_called_once()
|
||||||
|
_, kwargs = state.controller.run_script.call_args
|
||||||
|
build_args = kwargs.get("args", state.controller.run_script.call_args[1].get("args", []))
|
||||||
|
# Should have --push in args
|
||||||
|
assert "--push" in build_args
|
||||||
|
|
||||||
|
|
||||||
|
def test_docker_build_failed_rc():
|
||||||
|
state = _make_state(**{"init_db_build.run_build": "True"})
|
||||||
|
state.controller.run_script.return_value = 1
|
||||||
|
milestone = DockerBuildMilestone()
|
||||||
|
with patch("installer.core.env._normalize_cluster_env", return_value="dev"):
|
||||||
|
# Should not raise — just logs error
|
||||||
|
milestone.execute(state)
|
||||||
|
|
||||||
|
|
||||||
|
def test_docker_build_with_progress():
|
||||||
|
state = _make_state(**{"init_db_build.run_build": "True"})
|
||||||
|
milestone = DockerBuildMilestone()
|
||||||
|
progress = MagicMock()
|
||||||
|
with patch("installer.core.env._normalize_cluster_env", return_value="dev"):
|
||||||
|
milestone.execute(state, progress=progress)
|
||||||
|
progress.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# GitOpsMilestone
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_gitops_disabled():
|
||||||
|
state = _make_state(**{"init_cluster.gitops_enabled": "False"})
|
||||||
|
milestone = GitOpsMilestone()
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["GitOps"]["STATUS"] == "Skipped"
|
||||||
|
state.controller.run_script.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_gitops_disabled_default():
|
||||||
|
state = _make_state()
|
||||||
|
milestone = GitOpsMilestone()
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["GitOps"]["STATUS"] == "Skipped"
|
||||||
|
|
||||||
|
|
||||||
|
def test_gitops_enabled_success():
|
||||||
|
state = _make_state(**{"init_cluster.gitops_enabled": "True"})
|
||||||
|
milestone = GitOpsMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={"PROLE_MODE": "k3d"}):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["GitOps"]["STATUS"] == "Deployed"
|
||||||
|
|
||||||
|
|
||||||
|
def test_gitops_enabled_with_progress():
|
||||||
|
state = _make_state(**{"init_cluster.gitops_enabled": "True"})
|
||||||
|
milestone = GitOpsMilestone()
|
||||||
|
progress = MagicMock()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={"PROLE_MODE": "k3d"}):
|
||||||
|
milestone.execute(state, progress=progress)
|
||||||
|
progress.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_gitops_enabled_failure_raises():
|
||||||
|
state = _make_state(**{"init_cluster.gitops_enabled": "True"})
|
||||||
|
state.controller.run_script.return_value = 1
|
||||||
|
milestone = GitOpsMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={"PROLE_MODE": "k3d"}):
|
||||||
|
with pytest.raises(Exception, match="[Gg]itea"):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["GitOps"]["STATUS"] == "Attempted"
|
||||||
|
|
||||||
|
|
||||||
|
def test_gitops_uses_cfg_path_when_exists(tmp_path):
|
||||||
|
state = _make_state(**{"init_cluster.gitops_enabled": "True"})
|
||||||
|
state.controller.project_root = tmp_path
|
||||||
|
(tmp_path / "conf").mkdir()
|
||||||
|
(tmp_path / "conf" / "prole.cfg").write_text("[General]\n")
|
||||||
|
milestone = GitOpsMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={"PROLE_MODE": ""}):
|
||||||
|
milestone.execute(state)
|
||||||
|
_, kwargs = state.controller.run_script.call_args
|
||||||
|
assert "-c" in kwargs.get("args", [])
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# KerberosMilestone
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_kerberos_disabled():
|
||||||
|
state = _make_state(**{"kerberos_config.enabled": "False"})
|
||||||
|
milestone = KerberosMilestone()
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["Kerberos Authentication"]["STATUS"] == "Skipped"
|
||||||
|
state.controller.run_script.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_kerberos_disabled_default():
|
||||||
|
state = _make_state()
|
||||||
|
milestone = KerberosMilestone()
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["Kerberos Authentication"]["STATUS"] == "Skipped"
|
||||||
|
|
||||||
|
|
||||||
|
def test_kerberos_disabled_with_progress():
|
||||||
|
state = _make_state(**{"kerberos_config.enabled": "False"})
|
||||||
|
milestone = KerberosMilestone()
|
||||||
|
progress = MagicMock()
|
||||||
|
milestone.execute(state, progress=progress)
|
||||||
|
progress.assert_called_with("Kerberos disabled; skipping", 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_kerberos_enabled_success():
|
||||||
|
state = _make_state(**{"kerberos_config.enabled": "True"})
|
||||||
|
milestone = KerberosMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={"PROLE_MODE": "k3d"}):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["Kerberos Authentication"]["STATUS"] == "Initialized"
|
||||||
|
state.controller.run_script.assert_called_once()
|
||||||
|
_, kwargs = state.controller.run_script.call_args
|
||||||
|
assert "initialize" in kwargs.get("args", [])
|
||||||
|
|
||||||
|
|
||||||
|
def test_kerberos_enabled_with_progress():
|
||||||
|
state = _make_state(**{"kerberos_config.enabled": "True"})
|
||||||
|
milestone = KerberosMilestone()
|
||||||
|
progress = MagicMock()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={"PROLE_MODE": ""}):
|
||||||
|
milestone.execute(state, progress=progress)
|
||||||
|
progress.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_kerberos_enabled_failure_raises():
|
||||||
|
state = _make_state(**{"kerberos_config.enabled": "True"})
|
||||||
|
state.controller.run_script.return_value = 1
|
||||||
|
milestone = KerberosMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={"PROLE_MODE": ""}):
|
||||||
|
with pytest.raises(Exception, match="[Kk]erberos"):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["Kerberos Authentication"]["STATUS"] == "Attempted"
|
||||||
|
|
||||||
|
|
||||||
|
def test_kerberos_enabled_mode_in_args():
|
||||||
|
state = _make_state(**{"kerberos_config.enabled": "True"})
|
||||||
|
milestone = KerberosMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={"PROLE_MODE": "k3d"}):
|
||||||
|
milestone.execute(state)
|
||||||
|
_, kwargs = state.controller.run_script.call_args
|
||||||
|
assert "--mode" in kwargs.get("args", [])
|
||||||
|
assert "k3d" in kwargs.get("args", [])
|
||||||
|
|
||||||
|
|
||||||
|
def test_kerberos_enabled_no_mode():
|
||||||
|
state = _make_state(**{"kerberos_config.enabled": "True"})
|
||||||
|
milestone = KerberosMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={"PROLE_MODE": ""}):
|
||||||
|
milestone.execute(state)
|
||||||
|
_, kwargs = state.controller.run_script.call_args
|
||||||
|
assert "--mode" not in kwargs.get("args", [])
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# SupabaseMilestone
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_supabase_disabled():
|
||||||
|
state = _make_state(**{"init_cluster.supabase_enabled": "False"})
|
||||||
|
milestone = SupabaseMilestone()
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["Supabase"]["STATUS"] == "Skipped"
|
||||||
|
|
||||||
|
|
||||||
|
def test_supabase_disabled_default():
|
||||||
|
state = _make_state()
|
||||||
|
milestone = SupabaseMilestone()
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["Supabase"]["STATUS"] == "Skipped"
|
||||||
|
|
||||||
|
|
||||||
|
def test_supabase_disabled_with_progress():
|
||||||
|
state = _make_state(**{"init_cluster.supabase_enabled": "False"})
|
||||||
|
milestone = SupabaseMilestone()
|
||||||
|
progress = MagicMock()
|
||||||
|
milestone.execute(state, progress=progress)
|
||||||
|
progress.assert_called_with("Supabase disabled; skipping", 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_supabase_enabled_script_missing(tmp_path):
|
||||||
|
state = _make_state(**{"init_cluster.supabase_enabled": "True"})
|
||||||
|
state.controller.project_root = tmp_path
|
||||||
|
milestone = SupabaseMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={}):
|
||||||
|
milestone.execute(state)
|
||||||
|
# deploy.sh doesn't exist → status Attempted, no exception
|
||||||
|
assert state.config_data["Supabase"]["STATUS"] == "Attempted"
|
||||||
|
|
||||||
|
|
||||||
|
def test_supabase_enabled_success(tmp_path):
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_cluster.supabase_enabled": "True",
|
||||||
|
"init_cluster.cluster_env": "dev",
|
||||||
|
})
|
||||||
|
state.controller.project_root = tmp_path
|
||||||
|
supa_dir = tmp_path / "supabase"
|
||||||
|
supa_dir.mkdir()
|
||||||
|
(supa_dir / "deploy.sh").write_text("#!/bin/bash\nexit 0\n")
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 0
|
||||||
|
milestone = SupabaseMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={}), \
|
||||||
|
patch("installer.core.milestones.subprocess.run", return_value=mock_result), \
|
||||||
|
patch("installer.core.env._normalize_cluster_env", return_value="dev"), \
|
||||||
|
patch("installer.core.env._parse_bool", return_value=False):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["Supabase"]["STATUS"] == "Deployed"
|
||||||
|
|
||||||
|
|
||||||
|
def test_supabase_enabled_failure(tmp_path):
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_cluster.supabase_enabled": "True",
|
||||||
|
"init_cluster.cluster_env": "dev",
|
||||||
|
})
|
||||||
|
state.controller.project_root = tmp_path
|
||||||
|
supa_dir = tmp_path / "supabase"
|
||||||
|
supa_dir.mkdir()
|
||||||
|
(supa_dir / "deploy.sh").write_text("#!/bin/bash\nexit 1\n")
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 1
|
||||||
|
milestone = SupabaseMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={}), \
|
||||||
|
patch("installer.core.milestones.subprocess.run", return_value=mock_result), \
|
||||||
|
patch("installer.core.env._normalize_cluster_env", return_value="dev"), \
|
||||||
|
patch("installer.core.env._parse_bool", return_value=False):
|
||||||
|
with pytest.raises(Exception, match="[Ss]upabase"):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data["Supabase"]["STATUS"] == "Attempted"
|
||||||
|
|
||||||
|
|
||||||
|
def test_supabase_enabled_with_progress(tmp_path):
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_cluster.supabase_enabled": "True",
|
||||||
|
"init_cluster.cluster_env": "dev",
|
||||||
|
})
|
||||||
|
state.controller.project_root = tmp_path
|
||||||
|
supa_dir = tmp_path / "supabase"
|
||||||
|
supa_dir.mkdir()
|
||||||
|
(supa_dir / "deploy.sh").write_text("#!/bin/bash\nexit 0\n")
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 0
|
||||||
|
milestone = SupabaseMilestone()
|
||||||
|
progress = MagicMock()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={}), \
|
||||||
|
patch("installer.core.milestones.subprocess.run", return_value=mock_result), \
|
||||||
|
patch("installer.core.env._normalize_cluster_env", return_value="dev"), \
|
||||||
|
patch("installer.core.env._parse_bool", return_value=False):
|
||||||
|
milestone.execute(state, progress=progress)
|
||||||
|
progress.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DependenciesMilestone — error paths not covered by existing tests
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_dependencies_auto_install_disabled():
|
||||||
|
"""When auto_install=False, set status Missing and return without installing."""
|
||||||
|
state = _make_state(**{"dependencies.auto_install_missing": "False"})
|
||||||
|
milestone = DependenciesMilestone()
|
||||||
|
missing_dep = {"id": "brew", "name": "Homebrew", "install_cmd": "brew-install"}
|
||||||
|
with patch("installer.config.get_dep_info", return_value=(False, None, None)):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data.get("Dependencies", {}).get("STATUS") == "Missing"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dependencies_no_install_cmd():
|
||||||
|
"""A missing dep with no install_cmd should log error and be skipped."""
|
||||||
|
state = _make_state(**{"dependencies.auto_install_missing": "True"})
|
||||||
|
milestone = DependenciesMilestone()
|
||||||
|
# First call: dep missing; second call (re-verify): still missing
|
||||||
|
with patch("installer.config.get_dep_info", return_value=(False, None, None)), \
|
||||||
|
patch("installer.config.DEPENDENCIES",
|
||||||
|
[{"id": "mytool", "name": "MyTool"}]):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data.get("Dependencies", {}).get("STATUS") == "Missing"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dependencies_install_skipped_by_config():
|
||||||
|
"""Dep with install disabled in state.inputs should be skipped."""
|
||||||
|
state = _make_state(**{
|
||||||
|
"dependencies.auto_install_missing": "True",
|
||||||
|
"dependencies.brew.install": "False",
|
||||||
|
})
|
||||||
|
milestone = DependenciesMilestone()
|
||||||
|
dep = {"id": "brew", "name": "Homebrew", "install_cmd": "brew-install"}
|
||||||
|
with patch("installer.config.get_dep_info", return_value=(False, None, None)), \
|
||||||
|
patch("installer.config.DEPENDENCIES", [dep]):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data.get("Dependencies", {}).get("STATUS") == "Missing"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dependencies_install_fails():
|
||||||
|
"""When the install command returns non-zero, still mark Missing."""
|
||||||
|
state = _make_state(**{"dependencies.auto_install_missing": "True"})
|
||||||
|
milestone = DependenciesMilestone()
|
||||||
|
dep = {"id": "brew", "name": "Homebrew", "install_cmd": "brew-install"}
|
||||||
|
with patch("installer.config.get_dep_info", return_value=(False, None, None)), \
|
||||||
|
patch("installer.config.DEPENDENCIES", [dep]), \
|
||||||
|
patch.object(milestone, "_run_cmd", return_value=1):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data.get("Dependencies", {}).get("STATUS") == "Missing"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dependencies_install_succeeds_all():
|
||||||
|
"""After install, re-verify passes → status 'All installed'."""
|
||||||
|
state = _make_state(**{"dependencies.auto_install_missing": "True"})
|
||||||
|
milestone = DependenciesMilestone()
|
||||||
|
dep = {"id": "brew", "name": "Homebrew", "install_cmd": "brew-install"}
|
||||||
|
call_count = {"n": 0}
|
||||||
|
|
||||||
|
def _dep_info(d):
|
||||||
|
call_count["n"] += 1
|
||||||
|
# First call (initial check): missing. Re-verify calls: installed.
|
||||||
|
if call_count["n"] <= len([dep]):
|
||||||
|
return (False, None, None)
|
||||||
|
return (True, "/usr/bin/brew", "4.0")
|
||||||
|
|
||||||
|
with patch("installer.config.get_dep_info", side_effect=_dep_info), \
|
||||||
|
patch("installer.config.DEPENDENCIES", [dep]), \
|
||||||
|
patch.object(milestone, "_run_cmd", return_value=0):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert state.config_data.get("Dependencies", {}).get("STATUS") == "All installed"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# InitializationScriptsMilestone._regenerate_port_mapping_cfg
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_regenerate_port_mapping_cfg_k3d_mode(tmp_path):
|
||||||
|
state = _make_state()
|
||||||
|
state.config_data["Port Forwards"] = {
|
||||||
|
"PORT_FORWARD_K3D_MAPPING_argocd": (
|
||||||
|
"id=argocd;namespace=argocd;target=svc/argocd-server;"
|
||||||
|
"hostPort=8080;servicePort=80;address=127.0.0.1"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
milestone = InitializationScriptsMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config.PROJECT_ROOT", tmp_path):
|
||||||
|
(tmp_path / "conf").mkdir(exist_ok=True)
|
||||||
|
milestone._regenerate_port_mapping_cfg(state, "k3d")
|
||||||
|
mapping = (tmp_path / "conf" / "port-mapping.cfg").read_text()
|
||||||
|
assert "argocd: local=8080" in mapping
|
||||||
|
|
||||||
|
|
||||||
|
def test_regenerate_port_mapping_cfg_k3s_mode(tmp_path):
|
||||||
|
state = _make_state()
|
||||||
|
state.config_data["Port Forwards"] = {
|
||||||
|
"PORT_FORWARD_K3S_MAPPING_openbao": (
|
||||||
|
"id=openbao;namespace=default;target=svc/openbao;"
|
||||||
|
"hostPort=8200;servicePort=8200;address=127.0.0.1"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
milestone = InitializationScriptsMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config.PROJECT_ROOT", tmp_path):
|
||||||
|
(tmp_path / "conf").mkdir(exist_ok=True)
|
||||||
|
milestone._regenerate_port_mapping_cfg(state, "k3s")
|
||||||
|
mapping = (tmp_path / "conf" / "port-mapping.cfg").read_text()
|
||||||
|
assert "openbao: local=8200" in mapping
|
||||||
|
|
||||||
|
|
||||||
|
def test_regenerate_port_mapping_cfg_skips_wrong_prefix(tmp_path):
|
||||||
|
state = _make_state()
|
||||||
|
state.config_data["Port Forwards"] = {
|
||||||
|
"PORT_FORWARD_K3S_MAPPING_x": (
|
||||||
|
"id=x;namespace=default;target=svc/x;hostPort=9000;servicePort=9000;address=127.0.0.1"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
milestone = InitializationScriptsMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config.PROJECT_ROOT", tmp_path):
|
||||||
|
(tmp_path / "conf").mkdir(exist_ok=True)
|
||||||
|
# k3d mode should skip k3s-prefixed entries
|
||||||
|
milestone._regenerate_port_mapping_cfg(state, "k3d")
|
||||||
|
mapping = (tmp_path / "conf" / "port-mapping.cfg").read_text()
|
||||||
|
assert "x: local=" not in mapping
|
||||||
|
|
||||||
|
|
||||||
|
def test_regenerate_port_mapping_cfg_resolves_namespace(tmp_path):
|
||||||
|
state = _make_state(**{"init_password.db_namespace": "myns"})
|
||||||
|
state.config_data["Port Forwards"] = {
|
||||||
|
"PORT_FORWARD_K3D_MAPPING_svc": (
|
||||||
|
"id=svc;namespace=${NAMESPACE};target=svc/mysvc;"
|
||||||
|
"hostPort=5000;servicePort=5000;address=127.0.0.1"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
milestone = InitializationScriptsMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config.PROJECT_ROOT", tmp_path):
|
||||||
|
(tmp_path / "conf").mkdir(exist_ok=True)
|
||||||
|
milestone._regenerate_port_mapping_cfg(state, "k3d")
|
||||||
|
mapping = (tmp_path / "conf" / "port-mapping.cfg").read_text()
|
||||||
|
assert "ns=myns" in mapping
|
||||||
|
|
||||||
|
|
||||||
|
def test_regenerate_port_mapping_cfg_skips_unresolvable_port(tmp_path):
|
||||||
|
state = _make_state()
|
||||||
|
state.config_data["Port Forwards"] = {
|
||||||
|
"PORT_FORWARD_K3D_MAPPING_bad": (
|
||||||
|
"id=bad;namespace=default;target=svc/bad;"
|
||||||
|
"hostPort=${UNRESOLVED_PORT};servicePort=9999;address=127.0.0.1"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
milestone = InitializationScriptsMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config.PROJECT_ROOT", tmp_path):
|
||||||
|
(tmp_path / "conf").mkdir(exist_ok=True)
|
||||||
|
milestone._regenerate_port_mapping_cfg(state, "k3d")
|
||||||
|
mapping = (tmp_path / "conf" / "port-mapping.cfg").read_text()
|
||||||
|
assert "bad: local=" not in mapping
|
||||||
|
|
||||||
|
|
||||||
|
def test_regenerate_port_mapping_cfg_empty_section(tmp_path):
|
||||||
|
state = _make_state()
|
||||||
|
milestone = InitializationScriptsMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config.PROJECT_ROOT", tmp_path):
|
||||||
|
(tmp_path / "conf").mkdir(exist_ok=True)
|
||||||
|
milestone._regenerate_port_mapping_cfg(state, "k3d")
|
||||||
|
mapping = (tmp_path / "conf" / "port-mapping.cfg").read_text()
|
||||||
|
assert "# Port mappings" in mapping
|
||||||
|
|
||||||
|
|
||||||
|
def test_regenerate_port_mapping_cfg_write_error(tmp_path):
|
||||||
|
"""Write failure should be caught and logged, not raised."""
|
||||||
|
state = _make_state()
|
||||||
|
milestone = InitializationScriptsMilestone()
|
||||||
|
with patch("installer.core.milestones.inst_config.PROJECT_ROOT", tmp_path), \
|
||||||
|
patch("pathlib.Path.write_text", side_effect=OSError("disk full")):
|
||||||
|
(tmp_path / "conf").mkdir(exist_ok=True)
|
||||||
|
# Should not raise
|
||||||
|
milestone._regenerate_port_mapping_cfg(state, "k3d")
|
||||||
|
|
||||||
|
|
||||||
|
def test_supabase_k8s_mode_for_prod(tmp_path):
|
||||||
|
state = _make_state(**{
|
||||||
|
"init_cluster.supabase_enabled": "True",
|
||||||
|
"init_cluster.cluster_env": "prod",
|
||||||
|
})
|
||||||
|
state.controller.project_root = tmp_path
|
||||||
|
supa_dir = tmp_path / "supabase"
|
||||||
|
supa_dir.mkdir()
|
||||||
|
(supa_dir / "deploy.sh").write_text("#!/bin/bash\nexit 0\n")
|
||||||
|
mock_result = MagicMock()
|
||||||
|
mock_result.returncode = 0
|
||||||
|
captured_cmd = []
|
||||||
|
def capture_run(cmd, **kwargs):
|
||||||
|
captured_cmd.extend(cmd)
|
||||||
|
return mock_result
|
||||||
|
milestone = SupabaseMilestone()
|
||||||
|
with patch.object(milestone, "_get_script_env", return_value={}), \
|
||||||
|
patch("installer.core.milestones.subprocess.run", side_effect=capture_run), \
|
||||||
|
patch("installer.core.env._normalize_cluster_env", return_value="prod"), \
|
||||||
|
patch("installer.core.env._parse_bool", return_value=False):
|
||||||
|
milestone.execute(state)
|
||||||
|
assert "k8s" in captured_cmd
|
||||||
320
tests/installer/test_monitor.py
Normal file
320
tests/installer/test_monitor.py
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
"""
|
||||||
|
Unit tests for installer/core/monitor.py
|
||||||
|
|
||||||
|
Covers the pure-logic helpers:
|
||||||
|
_parse_pf_cfg, write_port_forwards_cfg, check_port_conflicts, _build_kubectl_cmd,
|
||||||
|
_start_port_forwards
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import MagicMock, patch, call
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from installer.core.monitor import (
|
||||||
|
_parse_pf_cfg,
|
||||||
|
write_port_forwards_cfg,
|
||||||
|
check_port_conflicts,
|
||||||
|
_build_kubectl_cmd,
|
||||||
|
_start_port_forwards,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _parse_pf_cfg
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_parse_pf_cfg_missing_file(tmp_path):
|
||||||
|
result = _parse_pf_cfg(tmp_path / "nonexistent.cfg")
|
||||||
|
assert result == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_pf_cfg_empty_file(tmp_path):
|
||||||
|
cfg = tmp_path / "pf.cfg"
|
||||||
|
cfg.write_text("")
|
||||||
|
assert _parse_pf_cfg(cfg) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_pf_cfg_comments_and_blanks(tmp_path):
|
||||||
|
cfg = tmp_path / "pf.cfg"
|
||||||
|
cfg.write_text("# this is a comment\n\n \n")
|
||||||
|
assert _parse_pf_cfg(cfg) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_pf_cfg_no_colon(tmp_path):
|
||||||
|
cfg = tmp_path / "pf.cfg"
|
||||||
|
cfg.write_text("some line without colon\n")
|
||||||
|
assert _parse_pf_cfg(cfg) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_pf_cfg_single_entry(tmp_path):
|
||||||
|
cfg = tmp_path / "pf.cfg"
|
||||||
|
cfg.write_text("argocd: local=8080 remote=80 ns=argocd svc=argocd-server address=127.0.0.1\n")
|
||||||
|
result = _parse_pf_cfg(cfg)
|
||||||
|
assert len(result) == 1
|
||||||
|
m = result[0]
|
||||||
|
assert m["id"] == "argocd"
|
||||||
|
assert m["local"] == "8080"
|
||||||
|
assert m["remote"] == "80"
|
||||||
|
assert m["ns"] == "argocd"
|
||||||
|
assert m["svc"] == "argocd-server"
|
||||||
|
assert m["address"] == "127.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_pf_cfg_multiple_entries(tmp_path):
|
||||||
|
cfg = tmp_path / "pf.cfg"
|
||||||
|
cfg.write_text(
|
||||||
|
"# Port forwards\n"
|
||||||
|
"argocd: local=8080 remote=80 ns=argocd svc=argocd-server address=127.0.0.1\n"
|
||||||
|
"openbao: local=8200 remote=8200 ns=default svc=openbao address=127.0.0.1\n"
|
||||||
|
)
|
||||||
|
result = _parse_pf_cfg(cfg)
|
||||||
|
assert len(result) == 2
|
||||||
|
assert result[0]["id"] == "argocd"
|
||||||
|
assert result[1]["id"] == "openbao"
|
||||||
|
assert result[1]["local"] == "8200"
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_pf_cfg_token_without_equals(tmp_path):
|
||||||
|
"""Tokens without '=' should be silently ignored."""
|
||||||
|
cfg = tmp_path / "pf.cfg"
|
||||||
|
cfg.write_text("myid: local=9000 badtoken remote=80\n")
|
||||||
|
result = _parse_pf_cfg(cfg)
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0]["local"] == "9000"
|
||||||
|
assert result[0]["remote"] == "80"
|
||||||
|
# 'badtoken' has no '=' so it's not added
|
||||||
|
assert "badtoken" not in result[0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_pf_cfg_id_with_extra_whitespace(tmp_path):
|
||||||
|
cfg = tmp_path / "pf.cfg"
|
||||||
|
cfg.write_text(" myservice : local=1234 remote=5678\n")
|
||||||
|
result = _parse_pf_cfg(cfg)
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0]["id"] == "myservice"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# write_port_forwards_cfg
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_write_port_forwards_cfg_empty(tmp_path):
|
||||||
|
out = tmp_path / "out.cfg"
|
||||||
|
write_port_forwards_cfg(out, [])
|
||||||
|
content = out.read_text()
|
||||||
|
assert "# Port forward configuration" in content
|
||||||
|
assert "# Format:" in content
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_port_forwards_cfg_skips_no_id(tmp_path):
|
||||||
|
out = tmp_path / "out.cfg"
|
||||||
|
# Mapping string with no id= token
|
||||||
|
write_port_forwards_cfg(out, ["hostPort=8080;servicePort=80"])
|
||||||
|
content = out.read_text()
|
||||||
|
# No entry line should be written (id is empty)
|
||||||
|
lines = [l for l in content.splitlines() if l and not l.startswith("#")]
|
||||||
|
assert lines == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_port_forwards_cfg_single_mapping(tmp_path):
|
||||||
|
out = tmp_path / "out.cfg"
|
||||||
|
mapping = "id=argocd;namespace=argocd;target=svc/argocd-server;hostPort=8080;servicePort=80;address=127.0.0.1"
|
||||||
|
write_port_forwards_cfg(out, [mapping])
|
||||||
|
content = out.read_text()
|
||||||
|
assert "argocd: local=8080 remote=80 ns=argocd svc=argocd-server address=127.0.0.1" in content
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_port_forwards_cfg_target_without_svc_prefix(tmp_path):
|
||||||
|
out = tmp_path / "out.cfg"
|
||||||
|
mapping = "id=myapp;namespace=default;target=myapp-service;hostPort=9000;servicePort=9000;address=0.0.0.0"
|
||||||
|
write_port_forwards_cfg(out, [mapping])
|
||||||
|
content = out.read_text()
|
||||||
|
# target doesn't start with svc/, so svc = target as-is
|
||||||
|
assert "svc=myapp-service" in content
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_port_forwards_cfg_roundtrip(tmp_path):
|
||||||
|
"""Written file can be parsed back by _parse_pf_cfg."""
|
||||||
|
out = tmp_path / "pf.cfg"
|
||||||
|
mappings = [
|
||||||
|
"id=argocd;namespace=argocd;target=svc/argocd-server;hostPort=8080;servicePort=80;address=127.0.0.1",
|
||||||
|
"id=openbao;namespace=default;target=svc/openbao;hostPort=8200;servicePort=8200;address=127.0.0.1",
|
||||||
|
]
|
||||||
|
write_port_forwards_cfg(out, mappings)
|
||||||
|
parsed = _parse_pf_cfg(out)
|
||||||
|
assert len(parsed) == 2
|
||||||
|
assert parsed[0]["id"] == "argocd"
|
||||||
|
assert parsed[1]["id"] == "openbao"
|
||||||
|
assert parsed[0]["local"] == "8080"
|
||||||
|
assert parsed[1]["remote"] == "8200"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# check_port_conflicts
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_check_port_conflicts_empty():
|
||||||
|
assert check_port_conflicts([]) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_port_conflicts_no_conflicts():
|
||||||
|
mappings = [
|
||||||
|
{"id": "a", "local": "8080"},
|
||||||
|
{"id": "b", "local": "8200"},
|
||||||
|
{"id": "c", "local": "9000"},
|
||||||
|
]
|
||||||
|
assert check_port_conflicts(mappings) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_port_conflicts_single_conflict():
|
||||||
|
mappings = [
|
||||||
|
{"id": "a", "local": "8080"},
|
||||||
|
{"id": "b", "local": "8080"},
|
||||||
|
]
|
||||||
|
result = check_port_conflicts(mappings)
|
||||||
|
assert len(result) == 1
|
||||||
|
assert "8080" in result[0]
|
||||||
|
assert "a" in result[0]
|
||||||
|
assert "b" in result[0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_port_conflicts_multiple_conflicts():
|
||||||
|
mappings = [
|
||||||
|
{"id": "a", "local": "8080"},
|
||||||
|
{"id": "b", "local": "8080"},
|
||||||
|
{"id": "c", "local": "9000"},
|
||||||
|
{"id": "d", "local": "9000"},
|
||||||
|
]
|
||||||
|
result = check_port_conflicts(mappings)
|
||||||
|
assert len(result) == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_port_conflicts_missing_local_key():
|
||||||
|
"""Mappings without 'local' key use '' as port and should not crash."""
|
||||||
|
mappings = [
|
||||||
|
{"id": "a"},
|
||||||
|
{"id": "b"},
|
||||||
|
]
|
||||||
|
result = check_port_conflicts(mappings)
|
||||||
|
# Both have empty port '' — should detect conflict
|
||||||
|
assert len(result) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_port_conflicts_missing_id_key():
|
||||||
|
"""Mappings without 'id' key should not crash."""
|
||||||
|
mappings = [
|
||||||
|
{"local": "8080"},
|
||||||
|
{"local": "8080"},
|
||||||
|
]
|
||||||
|
result = check_port_conflicts(mappings)
|
||||||
|
assert len(result) == 1
|
||||||
|
assert "?" in result[0]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _build_kubectl_cmd
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_build_kubectl_cmd_basic():
|
||||||
|
m = {"ns": "argocd", "svc": "argocd-server", "local": "8080", "remote": "80", "address": "127.0.0.1"}
|
||||||
|
cmd = _build_kubectl_cmd(m)
|
||||||
|
assert cmd == ["kubectl", "port-forward", "-n", "argocd", "svc/argocd-server", "8080:80"]
|
||||||
|
# address is 127.0.0.1 so --address is NOT added
|
||||||
|
assert "--address" not in cmd
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_kubectl_cmd_non_localhost_address():
|
||||||
|
m = {"ns": "default", "svc": "myapp", "local": "9000", "remote": "9000", "address": "0.0.0.0"}
|
||||||
|
cmd = _build_kubectl_cmd(m)
|
||||||
|
assert "--address" in cmd
|
||||||
|
assert "0.0.0.0" in cmd
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_kubectl_cmd_svc_prefix_not_doubled():
|
||||||
|
"""svc/ prefix should only appear once."""
|
||||||
|
m = {"ns": "default", "svc": "svc/myapp", "local": "9000", "remote": "9000", "address": "127.0.0.1"}
|
||||||
|
cmd = _build_kubectl_cmd(m)
|
||||||
|
svc_arg = cmd[4]
|
||||||
|
assert svc_arg == "svc/myapp"
|
||||||
|
assert not svc_arg.startswith("svc/svc/")
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_kubectl_cmd_adds_svc_prefix():
|
||||||
|
"""Service name without svc/ prefix should get one."""
|
||||||
|
m = {"ns": "default", "svc": "myapp", "local": "9000", "remote": "9000", "address": "127.0.0.1"}
|
||||||
|
cmd = _build_kubectl_cmd(m)
|
||||||
|
assert "svc/myapp" in cmd
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_kubectl_cmd_defaults():
|
||||||
|
"""Missing keys should fall back to defaults."""
|
||||||
|
cmd = _build_kubectl_cmd({})
|
||||||
|
assert cmd[0] == "kubectl"
|
||||||
|
assert "-n" in cmd
|
||||||
|
assert "default" in cmd # default namespace
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_kubectl_cmd_empty_address():
|
||||||
|
"""Empty address string should not add --address flag."""
|
||||||
|
m = {"ns": "default", "svc": "myapp", "local": "9000", "remote": "9000", "address": ""}
|
||||||
|
cmd = _build_kubectl_cmd(m)
|
||||||
|
assert "--address" not in cmd
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _start_port_forwards
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_start_port_forwards_empty():
|
||||||
|
result = _start_port_forwards([])
|
||||||
|
assert result == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_port_forwards_success(capsys):
|
||||||
|
mapping = {"id": "argocd", "ns": "argocd", "svc": "argocd-server", "local": "8080", "remote": "80", "address": "127.0.0.1"}
|
||||||
|
mock_proc = MagicMock(spec=subprocess.Popen)
|
||||||
|
with patch("installer.core.monitor.subprocess.Popen", return_value=mock_proc) as mock_popen:
|
||||||
|
result = _start_port_forwards([mapping])
|
||||||
|
assert len(result) == 1
|
||||||
|
m_out, proc_out = result[0]
|
||||||
|
assert m_out is mapping
|
||||||
|
assert proc_out is mock_proc
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
assert "argocd" in out
|
||||||
|
assert "✓" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_port_forwards_failure(capsys):
|
||||||
|
mapping = {"id": "broken", "ns": "default", "svc": "nosvc", "local": "9999", "remote": "9999", "address": "127.0.0.1"}
|
||||||
|
with patch("installer.core.monitor.subprocess.Popen", side_effect=OSError("not found")):
|
||||||
|
result = _start_port_forwards([mapping])
|
||||||
|
assert len(result) == 1
|
||||||
|
_, proc = result[0]
|
||||||
|
assert proc is None
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
assert "✗" in out
|
||||||
|
assert "broken" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_port_forwards_verbose(capsys):
|
||||||
|
mapping = {"id": "test", "ns": "default", "svc": "myapp", "local": "8000", "remote": "8000", "address": "127.0.0.1"}
|
||||||
|
mock_proc = MagicMock(spec=subprocess.Popen)
|
||||||
|
with patch("installer.core.monitor.subprocess.Popen", return_value=mock_proc):
|
||||||
|
_start_port_forwards([mapping], verbose=True)
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
assert "[PORT-FWD]" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_port_forwards_multiple(capsys):
|
||||||
|
mappings = [
|
||||||
|
{"id": "a", "ns": "default", "svc": "svc-a", "local": "8001", "remote": "80", "address": "127.0.0.1"},
|
||||||
|
{"id": "b", "ns": "default", "svc": "svc-b", "local": "8002", "remote": "80", "address": "127.0.0.1"},
|
||||||
|
]
|
||||||
|
mock_proc = MagicMock(spec=subprocess.Popen)
|
||||||
|
with patch("installer.core.monitor.subprocess.Popen", return_value=mock_proc):
|
||||||
|
result = _start_port_forwards(mappings)
|
||||||
|
assert len(result) == 2
|
||||||
|
assert result[0][0]["id"] == "a"
|
||||||
|
assert result[1][0]["id"] == "b"
|
||||||
@ -39,6 +39,13 @@ def test_render_title_no_canvas():
|
|||||||
|
|
||||||
@patch("platform.system", return_value="Darwin")
|
@patch("platform.system", return_value="Darwin")
|
||||||
def test_terminal_console(mock_platform):
|
def test_terminal_console(mock_platform):
|
||||||
|
import sys
|
||||||
|
from unittest.mock import MagicMock as _MM
|
||||||
|
|
||||||
|
# TerminalConsole requires a real tkinter root; skip when tkinter is mocked.
|
||||||
|
if isinstance(sys.modules.get("tkinter"), _MM):
|
||||||
|
pytest.skip("tkinter is mocked in this test run; TerminalConsole needs real tkinter")
|
||||||
|
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
try:
|
try:
|
||||||
console = TerminalConsole(root)
|
console = TerminalConsole(root)
|
||||||
@ -51,3 +58,5 @@ def test_terminal_console(mock_platform):
|
|||||||
assert console.text.cget("state") == "disabled"
|
assert console.text.cget("state") == "disabled"
|
||||||
finally:
|
finally:
|
||||||
root.destroy()
|
root.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -37,6 +37,40 @@ def test_run_streaming_cmd_streams_stdout_incrementally():
|
|||||||
assert started_at < first_chunk_at[0]
|
assert started_at < first_chunk_at[0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_streaming_cmd_nonzero_exit():
|
||||||
|
rc = run_streaming_cmd([sys.executable, "-c", "import sys; sys.exit(42)"])
|
||||||
|
assert rc == 42
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_streaming_cmd_string_form():
|
||||||
|
"""A plain string command is wrapped in bash -lc."""
|
||||||
|
chunks: list[str] = []
|
||||||
|
rc = run_streaming_cmd(
|
||||||
|
f"{sys.executable} -c \"print('strtest')\"",
|
||||||
|
on_stdout=lambda t: chunks.append(t),
|
||||||
|
)
|
||||||
|
assert rc == 0
|
||||||
|
assert "strtest" in "".join(chunks)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_streaming_cmd_stdin_text():
|
||||||
|
"""stdin_text is written to the subprocess stdin."""
|
||||||
|
chunks: list[str] = []
|
||||||
|
rc = run_streaming_cmd(
|
||||||
|
[sys.executable, "-c", "import sys; print(sys.stdin.read().strip())"],
|
||||||
|
stdin_text="hello_from_stdin",
|
||||||
|
on_stdout=lambda t: chunks.append(t),
|
||||||
|
)
|
||||||
|
assert rc == 0
|
||||||
|
assert "hello_from_stdin" in "".join(chunks)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_streaming_cmd_no_callbacks():
|
||||||
|
"""Running without callbacks should not crash."""
|
||||||
|
rc = run_streaming_cmd([sys.executable, "-c", "print('silent')"])
|
||||||
|
assert rc == 0
|
||||||
|
|
||||||
|
|
||||||
def test_run_streaming_cmd_keeps_stderr_and_ansi_sequences():
|
def test_run_streaming_cmd_keeps_stderr_and_ansi_sequences():
|
||||||
stdout_chunks: list[str] = []
|
stdout_chunks: list[str] = []
|
||||||
stderr_chunks: list[str] = []
|
stderr_chunks: list[str] = []
|
||||||
|
|||||||
@ -105,6 +105,9 @@ def test_navigation_flow_standard(mock_installer):
|
|||||||
with patch.object(
|
with patch.object(
|
||||||
mock_installer, "_generate_prole_db_dockerfile", return_value=True
|
mock_installer, "_generate_prole_db_dockerfile", return_value=True
|
||||||
):
|
):
|
||||||
|
mock_installer.on_next()
|
||||||
|
assert mock_installer.pages[mock_installer.page_index][0] == "init_db_build"
|
||||||
|
|
||||||
mock_installer.on_next()
|
mock_installer.on_next()
|
||||||
assert mock_installer.pages[mock_installer.page_index][0] == "init_password"
|
assert mock_installer.pages[mock_installer.page_index][0] == "init_password"
|
||||||
|
|
||||||
@ -137,8 +140,11 @@ def test_navigation_flow_missing_deps(mock_installer):
|
|||||||
|
|
||||||
|
|
||||||
def test_on_prev(mock_installer):
|
def test_on_prev(mock_installer):
|
||||||
# init_password -> database_options
|
# init_password -> init_db_build -> database_options
|
||||||
mock_installer.show_page("init_password")
|
mock_installer.show_page("init_password")
|
||||||
|
mock_installer.on_prev()
|
||||||
|
assert mock_installer.pages[mock_installer.page_index][0] == "init_db_build"
|
||||||
|
|
||||||
mock_installer.on_prev()
|
mock_installer.on_prev()
|
||||||
assert mock_installer.pages[mock_installer.page_index][0] == "database_options"
|
assert mock_installer.pages[mock_installer.page_index][0] == "database_options"
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user