mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 10:13:58 +00:00
Checkpoint: rename installer to knoe + harden db build context
- Add build-context helper to copy Docker context safely (ignore runtime data, keep symlinks) - Update UI and core actions to use ~/.prole/build and shared copy helper - Add/adjust tests and scripts; introduce knoe ops helpers and update manifests Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
parent
2244d6acd6
commit
4ee2b259c9
6
Makefile
6
Makefile
@ -63,8 +63,8 @@ init:
|
||||
@command -v tofu >/dev/null 2>&1 || (echo "Error: OpenTofu (tofu) not found in PATH." && exit 1)
|
||||
@echo "Syncing OpenTofu pipeline from $(PROLE_MODE) runtime into $(PIPELINE_DIR)..."
|
||||
@PROLE_MODE=$(PROLE_MODE) PROLE_CONF=$(PROLE_CONF) PROLE_GIT_REPO=$(DEPLOYMENT_REPO_URL) PYTHONPATH=$(CURDIR) $(PYTHON) - <<'PY'
|
||||
from installer.core.controller import ProleController
|
||||
from installer.core.env import PROJECT_ROOT
|
||||
from knoe.core.controller import ProleController
|
||||
from knoe.core.env import PROJECT_ROOT
|
||||
from prole.deployment import ProleDeployment
|
||||
import sys, os
|
||||
from pathlib import Path
|
||||
@ -102,5 +102,5 @@ pyconv:
|
||||
clean:
|
||||
@echo "Cleaning build artifacts..."
|
||||
rm -rf $(BUILD_DIR) $(DIST_DIR) *.spec
|
||||
rm -rf __pycache__ prole/__pycache__ installer/__pycache__
|
||||
rm -rf __pycache__ prole/__pycache__ knoe/__pycache__
|
||||
@echo "✓ Clean complete"
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
; Prole Master Configuration File
|
||||
; Generated by install.py on 2026-03-17 00:58:58
|
||||
; Generated by install.py on 2026-03-22 01:44:24
|
||||
; This file is used as input for Ansible deployment and k8s cluster creation.
|
||||
|
||||
[User]
|
||||
; User-editable values; derived values below reference these by default.
|
||||
; No configuration values captured yet for this section.
|
||||
; No user values captured yet for this section.
|
||||
|
||||
[Inputs]
|
||||
; Screen-scoped inputs used for unattended replays (-S)
|
||||
@ -37,51 +37,58 @@ dependencies.python.install = true
|
||||
dependencies.verify_all = false
|
||||
disk_selection.disk_type = local
|
||||
disk_selection.local_path = /Users/chrisfu/dev/prole/prole-tools-app/dist
|
||||
disk_selection.removable_mount =
|
||||
disk_selection.removable_mount =
|
||||
env_setup.NAMESPACE = ${NAMESPACE}
|
||||
env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf
|
||||
env_setup.PROLE_DATA = /Users/chrisfu/dev/prole/data
|
||||
env_setup.PROLE_HOME = /Users/chrisfu/dev/prole
|
||||
env_setup.PROLE_LOGS = /Users/chrisfu/dev/prole/logs
|
||||
env_setup.PROLE_SERVICE = /Users/chrisfu/dev/prole/etc
|
||||
init_cluster.argocd_enabled = false
|
||||
init_cluster.at_rest_encryption_enabled = true
|
||||
init_cluster.cluster_env = service
|
||||
init_cluster.deployment_target = prole-service-cluster
|
||||
init_cluster.cluster_env = dev
|
||||
init_cluster.deployment_target = prole-dev-cluster
|
||||
init_cluster.gitops_enabled = false
|
||||
init_cluster.k3s_server_url = https://myrddin.prole.org:6443
|
||||
init_cluster.k3s_token = ${PROLE_SECRET:v1:tOW53iZqoGRwqxUz:n0jktIyKBQ5cAggXfdR2oZ7OYyt859dRFHn5f4MzCy2zL7D_Ur9C-4e89RrTNPVLlGmd2GbHnP9uSLfnxRKe4zmypr5CfQnj3WDpwTr977EZBckkGFaSUQvUq-nShA-mRNDj14bXc2s5Oba9IQ9tiYRj4HT0W32MQ04HzQ==}
|
||||
init_cluster.k3s_token = ${PROLE_SECRET:v1:oJzjBIw61NsNXh7f:DJ9niYssxFCc6-gubQmWNlmQkhUhOtxLaXIIOTGpSxkfYK1uxcLw_BWb8n6kkl7uDkM1jEc4isjwjbycG5hRdd2at0oILHcyyFwrICH1roH4o0MHdd-kyxKkWo23WgSnTUGdRXfch5UWyPYZKniCxSbBhWud4ZFIZsZ4pw==}
|
||||
init_cluster.kerberos_enabled = true
|
||||
init_cluster.mode = k3s
|
||||
init_cluster.mode = k3d
|
||||
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_password =
|
||||
init_password.db_password_confirm =
|
||||
init_password.db_host_port = 5432
|
||||
init_password.db_namespace = ${NAMESPACE}
|
||||
init_password.db_password = ${PROLE_SECRET:v1:8fVi1VEwmCDDXQ8X:lnf6j8OiIAe5hVp0gGKPc43S98G_GwM_}
|
||||
init_password.db_password_confirm = ${PROLE_SECRET:v1:8fVi1VEwmCDDXQ8X:lnf6j8OiIAe5hVp0gGKPc43S98G_GwM_}
|
||||
init_password.db_username = root
|
||||
init_password.generate_ssh_key = true
|
||||
init_scripts.run_scripts = true
|
||||
kerberos_config.enabled = true
|
||||
kerberos_config.kdc = 10.0.0.3
|
||||
; kerberos_config.password omitted (defaults to prompt/secret resolution)
|
||||
kerberos_config.password = ${PROLE_SECRET:v1:MYcGYgRYF_0uzmX4:tF8WMMCx7DOkEyeI2gdOBzZPuqrWF90C3VInrJsoPWGzxCys}
|
||||
kerberos_config.realm = PROLE.ORG
|
||||
kerberos_config.test_connection = false
|
||||
kerberos_config.user = administrator
|
||||
network_scan.run = true
|
||||
ollama_config.model =
|
||||
ollama_config.server_host =
|
||||
ollama_config.server_host = fairyland.prole.org
|
||||
ollama_config.server_port = 11434
|
||||
|
||||
[Global]
|
||||
; Variables used by name in more than one place or assumed global scope
|
||||
CLUSTER_ENV = service
|
||||
CLUSTER_ENV = dev
|
||||
DB_HOST_PORT = 5432
|
||||
DB_PASSWORD =
|
||||
DEPLOYMENT_MODE = k3s
|
||||
DEPLOYMENT_TARGET = prole-service-cluster
|
||||
DB_PASSWORD = ${PROLE_SECRET:v1:dsPAIoTIWP8_8O35:RfNqyfh0j_eu8-tt9LZBQ_06wLa8D8aW}
|
||||
DEPLOYMENT_MODE = k3d
|
||||
DEPLOYMENT_TARGET = prole-dev-cluster
|
||||
DOCKER_PRELOAD = false
|
||||
KUBECONTEXT = prole-k3s
|
||||
NAMESPACE = knoe-db
|
||||
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2
|
||||
PROLE_DB_USER = root
|
||||
PROLE_HOME = /Users/chrisfu/dev/prole
|
||||
PROLE_K3S_SERVER = https://myrddin.prole.org:6443
|
||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:tOW53iZqoGRwqxUz:n0jktIyKBQ5cAggXfdR2oZ7OYyt859dRFHn5f4MzCy2zL7D_Ur9C-4e89RrTNPVLlGmd2GbHnP9uSLfnxRKe4zmypr5CfQnj3WDpwTr977EZBckkGFaSUQvUq-nShA-mRNDj14bXc2s5Oba9IQ9tiYRj4HT0W32MQ04HzQ==}
|
||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:oJzjBIw61NsNXh7f:DJ9niYssxFCc6-gubQmWNlmQkhUhOtxLaXIIOTGpSxkfYK1uxcLw_BWb8n6kkl7uDkM1jEc4isjwjbycG5hRdd2at0oILHcyyFwrICH1roH4o0MHdd-kyxKkWo23WgSnTUGdRXfch5UWyPYZKniCxSbBhWud4ZFIZsZ4pw==}
|
||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
||||
SERVICE_NAMESPACE = knoe-system
|
||||
|
||||
@ -120,7 +127,10 @@ PROLE_SERVICE = /Users/chrisfu/dev/prole/etc
|
||||
; No configuration values captured yet for this section.
|
||||
|
||||
[Ollama]
|
||||
; No configuration values captured yet for this section.
|
||||
OLLAMA_HOST = http://fairyland.prole.org:11434
|
||||
OLLAMA_SERVERS = fairyland.prole.org:11434,k3d.localhost:11434,morgoth.prole.org:11434
|
||||
OLLAMA_SERVER_HOST = fairyland.prole.org
|
||||
OLLAMA_SERVER_PORT = 11434
|
||||
|
||||
[Optional Features]
|
||||
AT_REST_ENCRYPTION_ENABLED = True
|
||||
@ -135,21 +145,21 @@ SUPABASE_ENABLED = False
|
||||
; No configuration values captured yet for this section.
|
||||
|
||||
[Initialize Cluster]
|
||||
ENVIRONMENT = service
|
||||
ENVIRONMENT = dev
|
||||
K3S_SERVER_URL = https://myrddin.prole.org:6443
|
||||
K3S_TOKEN = ${PROLE_SECRET:v1:tOW53iZqoGRwqxUz:n0jktIyKBQ5cAggXfdR2oZ7OYyt859dRFHn5f4MzCy2zL7D_Ur9C-4e89RrTNPVLlGmd2GbHnP9uSLfnxRKe4zmypr5CfQnj3WDpwTr977EZBckkGFaSUQvUq-nShA-mRNDj14bXc2s5Oba9IQ9tiYRj4HT0W32MQ04HzQ==}
|
||||
K3S_TOKEN = ${PROLE_SECRET:v1:oJzjBIw61NsNXh7f:DJ9niYssxFCc6-gubQmWNlmQkhUhOtxLaXIIOTGpSxkfYK1uxcLw_BWb8n6kkl7uDkM1jEc4isjwjbycG5hRdd2at0oILHcyyFwrICH1roH4o0MHdd-kyxKkWo23WgSnTUGdRXfch5UWyPYZKniCxSbBhWud4ZFIZsZ4pw==}
|
||||
|
||||
[Dev Cluster (k3d)]
|
||||
CLUSTER_ENV = dev
|
||||
DISPLAY_NAME = knoe-dev-cluster
|
||||
KUBECTL_CONTEXT = prole-k3s
|
||||
KUBECTL_CONTEXT =
|
||||
MODE = k3d
|
||||
|
||||
[Service Cluster (k3s)]
|
||||
CLUSTER_ENV = prole-service-cluster
|
||||
DISPLAY_NAME = prole-service-cluster
|
||||
K3S_SERVER_URL = https://myrddin.prole.org:6443
|
||||
K3S_TOKEN = ${PROLE_SECRET:v1:tOW53iZqoGRwqxUz:n0jktIyKBQ5cAggXfdR2oZ7OYyt859dRFHn5f4MzCy2zL7D_Ur9C-4e89RrTNPVLlGmd2GbHnP9uSLfnxRKe4zmypr5CfQnj3WDpwTr977EZBckkGFaSUQvUq-nShA-mRNDj14bXc2s5Oba9IQ9tiYRj4HT0W32MQ04HzQ==}
|
||||
K3S_TOKEN = ${PROLE_SECRET:v1:oJzjBIw61NsNXh7f:DJ9niYssxFCc6-gubQmWNlmQkhUhOtxLaXIIOTGpSxkfYK1uxcLw_BWb8n6kkl7uDkM1jEc4isjwjbycG5hRdd2at0oILHcyyFwrICH1roH4o0MHdd-kyxKkWo23WgSnTUGdRXfch5UWyPYZKniCxSbBhWud4ZFIZsZ4pw==}
|
||||
MODE = k3s
|
||||
PIPELINE_URL = http://127.0.0.1:8080
|
||||
|
||||
@ -161,14 +171,15 @@ MODE = k8s
|
||||
PIPELINE_URL = http://127.0.0.1:8080
|
||||
|
||||
[Docker Build]
|
||||
; No configuration values captured yet for this section.
|
||||
LOCAL_REGISTRY = localhost:5000
|
||||
LOCAL_REGISTRY_INTERNAL = k3d-prole-registry.localhost:5000
|
||||
|
||||
[Initialization Scripts]
|
||||
; No configuration values captured yet for this section.
|
||||
|
||||
[Deployment]
|
||||
MODE = k3s
|
||||
TARGET = prole-service-cluster
|
||||
MODE = k3d
|
||||
TARGET = prole-dev-cluster
|
||||
|
||||
[Install]
|
||||
; No configuration values captured yet for this section.
|
||||
|
||||
@ -1,3 +1,11 @@
|
||||
# Port mappings for Prole Tools (generated).
|
||||
# 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=knoe-system svc=garage address=0.0.0.0
|
||||
openbao: local=8200 remote=8200 ns=knoe-system svc=openbao address=127.0.0.1
|
||||
opentofu: local=8080 remote=8080 ns=knoe-system svc=opentofu address=0.0.0.0
|
||||
dashboard: local=8443 remote=443 ns=kubernetes-dashboard svc=kubernetes-dashboard-kong-proxy address=127.0.0.1
|
||||
postgres: local=5432 remote=5432 ns=knoe-db 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 +1 @@
|
||||
18
|
||||
17
|
||||
@ -1,5 +1,5 @@
|
||||
; Prole Master Configuration File
|
||||
; Generated by install.py on 2026-03-20 16:59:08
|
||||
; Generated by install.py on 2026-03-21 23:40:10
|
||||
; This file is used as input for Ansible deployment and k8s cluster creation.
|
||||
|
||||
[User]
|
||||
@ -50,7 +50,7 @@ init_cluster.cluster_env = prod
|
||||
init_cluster.deployment_target = prole-prod-cluster
|
||||
init_cluster.gitops_enabled = false
|
||||
init_cluster.k3s_server_url = https://myrddin.prole.org:6443
|
||||
init_cluster.k3s_token = ${PROLE_SECRET:v1:byngYfgT7BPkndc5:TukgM2QvPylhorfIm0t-tPysEPq-Lvc8lqbv594nzDsJxc6Kj6J9walkiVQS6o18ZJYNU7xpuYsRWV1yyZW8RBxvPi-glfs7C-sdj6Q3mwia2A9jLcxyJMck3_8z4L-mc7utmo8PIxw0MnjeF8P_Ixzoyi4cM3WoKtZiyA==}
|
||||
init_cluster.k3s_token = ${PROLE_SECRET:v1:iTRfvIPRo7v-aK5j:H6zki1L-02mHJQLXIjZyFnVWByy5TfoCASeGEi08tcfZCqqybxeCatiXK_NyADR1oElbaZnvjw83fuhircDNdRRY4sQo_hEuKU0ll9s9o6QZBKZZzWpgcU4WMX7YAS4aOgc8d5pBFUH0mC-ZcJ9lSWT3JxhgBXRdbVopQQ==}
|
||||
init_cluster.kerberos_enabled = true
|
||||
init_cluster.mode = k8s
|
||||
init_cluster.start_cluster = true
|
||||
@ -67,7 +67,7 @@ init_password.generate_ssh_key = true
|
||||
init_scripts.run_scripts = true
|
||||
kerberos_config.enabled = true
|
||||
kerberos_config.kdc = 10.0.0.3
|
||||
kerberos_config.password = ${PROLE_SECRET:v1:A6qvhIB8BlPEcQjO:0tFeTyOFqqkn13rROLkgcJ9NgYmVYRD1ZjJzCAeFwfrgSxQx}
|
||||
kerberos_config.password = ${PROLE_SECRET:v1:rEiGEX6WGmmQ2vyE:EjwlefHMKjZwZRVIrh3x4VjqxnEddJ-EpRSaoqd3PjaZGTh5}
|
||||
kerberos_config.realm = PROLE.ORG
|
||||
kerberos_config.test_connection = false
|
||||
kerberos_config.user = administrator
|
||||
@ -89,7 +89,7 @@ OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2
|
||||
PROLE_DB_USER = root
|
||||
PROLE_HOME = $HOME/dev/prole
|
||||
PROLE_K3S_SERVER = https://myrddin.prole.org:6443
|
||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:dQZZFvDfN_I8bFPY:yr9laHz55aM5lL_QTnBim1m0xBpX5SgfOPOVp-2HwLdii-2TNnT0gSAuWqSjgwI9JSoMT3QfD2lavVF2qMoWh9SFN8idZ3-VIYRsiZ7SM2BOiS_sbOG3_QDucvciuIxy5MXNyFEmxOGTtXwyzC1cnQjlsiEnKy0XbFF7dA==}
|
||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:cWc4c9mYxi20rxbR:wTCIMGahxg8rxxGotDtMhobeDIzf1k_9thQti2je2znzRVZVqV4TibePL21oghQvLLBbkjzd5hwIlUGGqq7sOYkpIetik4swjmaQnJ4DHGT82RWX3PZuuCrPWLGb5NOs4Hqv61TKYru5C-ImJzrPyEq7vJDgBG3aita0HQ==}
|
||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
||||
SERVICE_NAMESPACE = knoe-system
|
||||
|
||||
@ -155,7 +155,7 @@ MODE = k3d
|
||||
CLUSTER_ENV = prole-service-cluster
|
||||
DISPLAY_NAME = prole-service-cluster
|
||||
K3S_SERVER_URL = https://myrddin.prole.org:6443
|
||||
K3S_TOKEN = ${PROLE_SECRET:v1:_iFv8sZdDHQxTxd4:a3RRUkKtO9ctIm_393LrEg9Lq9tN1Sfl7B3TZLnSwEXEKgpNnp9VAeicvFy8kV4unSX99yXZPhTf3RbTQ7qaTt4Oj8vU9aFK905qa5BVPOzmAZo_10mTcfXYTOeNXALl4sprStWambtJ4CEYol3XsIXFywzGq1jkldPyLw==}
|
||||
K3S_TOKEN = ${PROLE_SECRET:v1:3DHF_algPRkZXK_o:hMRZr17b7GihxuRs-6NPhlcS9h-aIctsZih2SS6dPCqwZabpKbG2ziZBgLiwOAhWih-ofvR8qbaOWdmbXurTYvWAWoj5F0MzOa35klQ6ujO2A2B2Y_uHrnzHXwkwEVufl9g8mucOkSMZ6UiVkNK4jYowVZHNTXe7uhghCA==}
|
||||
MODE = k3s
|
||||
PIPELINE_URL = http://127.0.0.1:8080
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
; Prole Master Configuration File
|
||||
; Generated by install.py on 2026-03-20 17:05:22
|
||||
; Generated by install.py on 2026-03-21 13:37:28
|
||||
; This file is used as input for Ansible deployment and k8s cluster creation.
|
||||
|
||||
[User]
|
||||
@ -50,7 +50,7 @@ init_cluster.cluster_env = service
|
||||
init_cluster.deployment_target = prole-service-cluster
|
||||
init_cluster.gitops_enabled = false
|
||||
init_cluster.k3s_server_url = https://myrddin.prole.org:6443
|
||||
init_cluster.k3s_token = ${PROLE_SECRET:v1:s4MdUcnQRJBPdAss:irg5n342LjxLeLV8kGtoAAO_Od3QcmWtTXLQbPw2o2mUJrRnNEBOhN-z4Q6J3UZylEKp6ZhmD_TdRGqdpfpC79j1kFtaTPWCvl_cdHHiG9580AQ87V4XmtAwEh-_ICm4GS35szQHELlXkJH4j7CvfAngW8RW_1nvtLHQyQ==}
|
||||
init_cluster.k3s_token = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==}
|
||||
init_cluster.kerberos_enabled = true
|
||||
init_cluster.mode = k3s
|
||||
init_cluster.start_cluster = true
|
||||
@ -67,7 +67,7 @@ init_password.generate_ssh_key = true
|
||||
init_scripts.run_scripts = true
|
||||
kerberos_config.enabled = true
|
||||
kerberos_config.kdc = 10.0.0.3
|
||||
kerberos_config.password = ${PROLE_SECRET:v1:1zuEATgIOtkCPTIi:w2eKBUzce6mq6BJAlefU9cl6K05A9EDT81AQ1uSVzgI1NbUY}
|
||||
kerberos_config.password = ${PROLE_SECRET:v1:71ROfYyYysQDJDpX:0K4cB3p9nnMb1yi2OImqUm-pa9Zg21JYTmYHzYB_7czTKMQJ}
|
||||
kerberos_config.realm = PROLE.ORG
|
||||
kerberos_config.test_connection = false
|
||||
kerberos_config.user = administrator
|
||||
@ -84,13 +84,12 @@ DB_PASSWORD =
|
||||
DEPLOYMENT_MODE = k3s
|
||||
DEPLOYMENT_TARGET = prole-service-cluster
|
||||
DOCKER_PRELOAD = false
|
||||
KUBECONTEXT = prole-k3s
|
||||
NAMESPACE = prole-db
|
||||
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2
|
||||
PROLE_DB_USER = root
|
||||
PROLE_HOME = $HOME/dev/prole
|
||||
PROLE_K3S_SERVER = https://myrddin.prole.org:6443
|
||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:SNcp3N71DMccQw5G:UXmhfoiN8PKqw3jaarQxhhv5rtPjjUQTRSVo4pS6FkmHqLDt1xghL4RfgBdcaR5HA9lnlDc5jFpLruIrC1ivxk5HssTn1prde0lKvhioO7SkIOSl6HsA4XXosf6KveNASpxCdeZ6RkZlg2i7jpHxvxX3zbIrSKNISN5gEQ==}
|
||||
PROLE_K3S_TOKEN = ${PROLE_SECRET:v1:_r5tIPt5VZcr0SrV:e2_sHY49gG2i-vhQZr-42M1kekln4D4ZHq6tJrDRCn8f4KOVSn59rndLt4PuHmsebPSPP4tGHZV0MaVtdBMm4RJmk9IXbqV7BjSNruK2SXi62NOXIIUWZGNcVIOFjmv7EovfOifo3nye8142zKDspTeyo5Y2KY6qGLtS2g==}
|
||||
PROLE_OPENTOFU_URL = http://127.0.0.1:8080
|
||||
SERVICE_NAMESPACE = knoe-system
|
||||
|
||||
@ -135,10 +134,7 @@ OLLAMA_SERVER_HOST = fairyland.prole.org
|
||||
OLLAMA_SERVER_PORT = 11434
|
||||
|
||||
[Optional Features]
|
||||
AT_REST_ENCRYPTION_ENABLED = True
|
||||
GITOPS_ENABLED = False
|
||||
KERBEROS_ENABLED = True
|
||||
SUPABASE_ENABLED = False
|
||||
|
||||
[GitOps]
|
||||
; No configuration values captured yet for this section.
|
||||
@ -147,14 +143,12 @@ SUPABASE_ENABLED = False
|
||||
; No configuration values captured yet for this section.
|
||||
|
||||
[Initialize Cluster]
|
||||
ENVIRONMENT = service
|
||||
K3S_SERVER_URL = https://myrddin.prole.org:6443
|
||||
K3S_TOKEN = ${PROLE_SECRET:v1:570CIZjbSCH131v_:pDQwbH9nKYsCl5z0f-iAb-cilZlo1KLpI16c6Hgj0Ic81Jk1I7CSjEFo5UJ1ezV_pdat-7kxhaWE_TxCXG6ZnuOA8SBh3xCCi1LsgYFluPwm47b4IaS5ftvMOe5TVWbQfnzsXuU4SdO_3O9mgagKHdDIzu8kufYfpeDyqA==}
|
||||
; No configuration values captured yet for this section.
|
||||
|
||||
[Dev Cluster (k3d)]
|
||||
CLUSTER_ENV = dev
|
||||
DISPLAY_NAME = knoe-dev-cluster
|
||||
KUBECTL_CONTEXT = prole-k3s
|
||||
KUBECTL_CONTEXT =
|
||||
MODE = k3d
|
||||
|
||||
[Service Cluster (k3s)]
|
||||
|
||||
@ -14,6 +14,7 @@ resources:
|
||||
- prole-auth-deployment.yaml
|
||||
- prole-auth-service.yaml
|
||||
- prole-auth-kerberos-configmap.yaml
|
||||
- prole-kdc-configmap.yaml
|
||||
- grafana-proxy-configmap.yaml
|
||||
- grafana-proxy-deployment.yaml
|
||||
- grafana-proxy-service.yaml
|
||||
|
||||
@ -14,6 +14,119 @@ spec:
|
||||
labels:
|
||||
app: prole-auth
|
||||
spec:
|
||||
initContainers:
|
||||
- name: keytab-bootstrap
|
||||
image: k3d-prole-registry:5000/prole-authority:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/bash
|
||||
- -lc
|
||||
- |
|
||||
set -euo pipefail
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
realm="${PROLE_KDC_REALM:-PROLE.ORG}"
|
||||
admin_principal="${PROLE_KDC_ADMIN_PRINCIPAL:-admin/admin}"
|
||||
if [[ "${admin_principal}" != *"@"* ]]; then
|
||||
admin_principal="${admin_principal}@${realm}"
|
||||
fi
|
||||
|
||||
svc_principal="${PROLE_KERBEROS_SERVICE_PRINCIPAL:?Missing PROLE_KERBEROS_SERVICE_PRINCIPAL}"
|
||||
if [[ "${svc_principal}" != *"@"* ]]; then
|
||||
svc_principal="${svc_principal}@${realm}"
|
||||
fi
|
||||
|
||||
keytab_out="/etc/prole/keytabs/http.keytab"
|
||||
mkdir -p "$(dirname "${keytab_out}")"
|
||||
|
||||
if [[ -f /mnt/keytab-secret/http.keytab ]]; then
|
||||
cp /mnt/keytab-secret/http.keytab "${keytab_out}"
|
||||
chmod 0400 "${keytab_out}" || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v kadmin.local >/dev/null 2>&1; then
|
||||
echo "Installing Kerberos packages..."
|
||||
echo "krb5-config krb5-config/default_realm string ${realm}" | debconf-set-selections || true
|
||||
echo "krb5-config krb5-config/kerberos_servers string 127.0.0.1" | debconf-set-selections || true
|
||||
echo "krb5-config krb5-config/admin_server string 127.0.0.1" | debconf-set-selections || true
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends krb5-kdc krb5-admin-server krb5-user dnsutils ca-certificates
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
|
||||
mkdir -p /etc/krb5kdc /var/lib/krb5kdc
|
||||
if [[ -f /opt/prole-kdc/krb5.conf ]]; then
|
||||
cp /opt/prole-kdc/krb5.conf /etc/krb5.conf
|
||||
fi
|
||||
if [[ -f /opt/prole-kdc/kdc.conf ]]; then
|
||||
cp /opt/prole-kdc/kdc.conf /etc/krb5kdc/kdc.conf
|
||||
fi
|
||||
if [[ -f /opt/prole-kdc/kadm5.acl ]]; then
|
||||
cp /opt/prole-kdc/kadm5.acl /etc/krb5kdc/kadm5.acl
|
||||
fi
|
||||
|
||||
if [[ -z "${PROLE_KDC_MASTER_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env PROLE_KDC_MASTER_PASSWORD (secret 'prole-kdc-secrets/master_password')." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${PROLE_KDC_ADMIN_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env PROLE_KDC_ADMIN_PASSWORD (secret 'prole-kdc-secrets/admin_password')." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f /var/lib/krb5kdc/principal ]]; then
|
||||
echo "Initializing realm database for ${realm}..."
|
||||
kdb5_util create -s -r "${realm}" -P "${PROLE_KDC_MASTER_PASSWORD}"
|
||||
fi
|
||||
|
||||
if ! kadmin.local -q "get_principal ${admin_principal}" >/dev/null 2>&1; then
|
||||
echo "Creating admin principal ${admin_principal}..."
|
||||
kadmin.local -q "addprinc -pw ${PROLE_KDC_ADMIN_PASSWORD} ${admin_principal}"
|
||||
fi
|
||||
|
||||
if ! kadmin.local -q "get_principal ${svc_principal}" >/dev/null 2>&1; then
|
||||
echo "Creating service principal ${svc_principal}..."
|
||||
kadmin.local -q "addprinc -randkey ${svc_principal}"
|
||||
fi
|
||||
|
||||
kadmin.local -q "ktadd -k ${keytab_out} -norandkey ${svc_principal}"
|
||||
chmod 0400 "${keytab_out}" || true
|
||||
env:
|
||||
- name: PROLE_KDC_REALM
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: prole-auth-kerberos
|
||||
key: realm
|
||||
- name: PROLE_KDC_ADMIN_PRINCIPAL
|
||||
value: "admin/admin"
|
||||
- name: PROLE_KDC_MASTER_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: prole-kdc-secrets
|
||||
key: master_password
|
||||
- name: PROLE_KDC_ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: prole-kdc-secrets
|
||||
key: admin_password
|
||||
- name: PROLE_KERBEROS_SERVICE_PRINCIPAL
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: prole-auth-kerberos
|
||||
key: servicePrincipal
|
||||
volumeMounts:
|
||||
- name: keytab
|
||||
mountPath: /etc/prole/keytabs
|
||||
- name: keytab-secret
|
||||
mountPath: /mnt/keytab-secret
|
||||
readOnly: true
|
||||
- name: prole-kdc-config
|
||||
mountPath: /opt/prole-kdc
|
||||
- name: prole-kdc-data
|
||||
mountPath: /var/lib/krb5kdc
|
||||
- name: prole-kdc-data
|
||||
mountPath: /etc/krb5kdc
|
||||
containers:
|
||||
- name: prole-auth
|
||||
image: prole-auth:latest
|
||||
@ -37,11 +150,74 @@ spec:
|
||||
key: servicePrincipal
|
||||
- name: PROLE_KERBEROS_KEYTAB_PATH
|
||||
value: "/etc/prole/keytabs/http.keytab"
|
||||
- name: PROLE_KERBEROS_REALM
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: prole-auth-kerberos
|
||||
key: realm
|
||||
volumeMounts:
|
||||
- name: keytab
|
||||
mountPath: /etc/prole/keytabs
|
||||
readOnly: true
|
||||
- name: prole-kdc-config
|
||||
mountPath: /etc/krb5.conf
|
||||
subPath: krb5.conf
|
||||
readOnly: true
|
||||
- name: kdc
|
||||
image: k3d-prole-registry:5000/prole-authority:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/bash", "/opt/prole-kdc/entrypoint.sh"]
|
||||
env:
|
||||
- name: PROLE_KDC_REALM
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: prole-auth-kerberos
|
||||
key: realm
|
||||
- name: PROLE_KDC_ADMIN_PRINCIPAL
|
||||
value: "admin/admin"
|
||||
- name: PROLE_KDC_MASTER_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: prole-kdc-secrets
|
||||
key: master_password
|
||||
- name: PROLE_KDC_ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: prole-kdc-secrets
|
||||
key: admin_password
|
||||
ports:
|
||||
- name: krb5-udp
|
||||
containerPort: 88
|
||||
protocol: UDP
|
||||
- name: krb5-tcp
|
||||
containerPort: 88
|
||||
protocol: TCP
|
||||
- name: kpasswd-udp
|
||||
containerPort: 464
|
||||
protocol: UDP
|
||||
- name: kpasswd-tcp
|
||||
containerPort: 464
|
||||
protocol: TCP
|
||||
- name: kadmin
|
||||
containerPort: 749
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- name: prole-kdc-config
|
||||
mountPath: /opt/prole-kdc
|
||||
- name: prole-kdc-data
|
||||
mountPath: /var/lib/krb5kdc
|
||||
- name: prole-kdc-data
|
||||
mountPath: /etc/krb5kdc
|
||||
volumes:
|
||||
- name: keytab
|
||||
emptyDir: {}
|
||||
- name: keytab-secret
|
||||
secret:
|
||||
secretName: prole-auth-keytab
|
||||
optional: true
|
||||
- name: prole-kdc-config
|
||||
configMap:
|
||||
name: prole-kdc-config
|
||||
defaultMode: 0755
|
||||
- name: prole-kdc-data
|
||||
emptyDir: {}
|
||||
|
||||
@ -5,3 +5,4 @@ metadata:
|
||||
data:
|
||||
# Kerberos HTTP service principal for SPNEGO (must match keytab)
|
||||
servicePrincipal: "HTTP/api.prole.org@PROLE.ORG"
|
||||
realm: "PROLE.ORG"
|
||||
|
||||
@ -11,4 +11,24 @@ spec:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: http
|
||||
- name: krb5-udp
|
||||
port: 88
|
||||
targetPort: 88
|
||||
protocol: UDP
|
||||
- name: krb5-tcp
|
||||
port: 88
|
||||
targetPort: 88
|
||||
protocol: TCP
|
||||
- name: kpasswd-udp
|
||||
port: 464
|
||||
targetPort: 464
|
||||
protocol: UDP
|
||||
- name: kpasswd-tcp
|
||||
port: 464
|
||||
targetPort: 464
|
||||
protocol: TCP
|
||||
- name: kadmin
|
||||
port: 749
|
||||
targetPort: 749
|
||||
protocol: TCP
|
||||
type: ClusterIP
|
||||
|
||||
@ -4,7 +4,7 @@ metadata:
|
||||
name: prole-db
|
||||
spec:
|
||||
instances: 3
|
||||
imageName: myrddin.prole.org:5000/prole-db:0.0.0
|
||||
imageName: k3d-prole-registry:5000/knoe-db:17-137
|
||||
postgresUID: 100
|
||||
postgresGID: 101
|
||||
maxSyncReplicas: 1
|
||||
|
||||
91
deploy/opentofu/k3s/manifests/prole/prole-kdc-configmap.yaml
Normal file
91
deploy/opentofu/k3s/manifests/prole/prole-kdc-configmap.yaml
Normal file
@ -0,0 +1,91 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: prole-kdc-config
|
||||
data:
|
||||
krb5.conf: |
|
||||
[libdefaults]
|
||||
default_realm = PROLE.ORG
|
||||
dns_lookup_realm = false
|
||||
dns_lookup_kdc = false
|
||||
|
||||
[realms]
|
||||
PROLE.ORG = {
|
||||
kdc = 127.0.0.1
|
||||
admin_server = 127.0.0.1
|
||||
}
|
||||
kdc.conf: |
|
||||
[kdcdefaults]
|
||||
kdc_ports = 88
|
||||
kdc_tcp_ports = 88
|
||||
[realms]
|
||||
PROLE.ORG = {
|
||||
database_name = /var/lib/krb5kdc/principal
|
||||
admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
|
||||
acl_file = /etc/krb5kdc/kadm5.acl
|
||||
key_stash_file = /etc/krb5kdc/stash
|
||||
max_life = 10h 0m 0s
|
||||
max_renewable_life = 7d 0h 0m 0s
|
||||
default_principal_flags = +preauth
|
||||
}
|
||||
kadm5.acl: |
|
||||
admin/admin@PROLE.ORG *
|
||||
entrypoint.sh: |
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
realm="${PROLE_KDC_REALM:-PROLE.ORG}"
|
||||
admin_principal="${PROLE_KDC_ADMIN_PRINCIPAL:-admin/admin}"
|
||||
if [[ "${admin_principal}" != *"@"* ]]; then
|
||||
admin_principal="${admin_principal}@${realm}"
|
||||
fi
|
||||
|
||||
if ! command -v krb5kdc >/dev/null 2>&1; then
|
||||
echo "Installing Kerberos packages..."
|
||||
echo "krb5-config krb5-config/default_realm string ${realm}" | debconf-set-selections || true
|
||||
echo "krb5-config krb5-config/kerberos_servers string 127.0.0.1" | debconf-set-selections || true
|
||||
echo "krb5-config krb5-config/admin_server string 127.0.0.1" | debconf-set-selections || true
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends krb5-kdc krb5-admin-server krb5-user dnsutils ca-certificates
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
|
||||
mkdir -p /etc/krb5kdc /var/lib/krb5kdc
|
||||
if [[ -f /opt/prole-kdc/krb5.conf ]]; then
|
||||
cp /opt/prole-kdc/krb5.conf /etc/krb5.conf
|
||||
fi
|
||||
if [[ -f /opt/prole-kdc/kdc.conf ]]; then
|
||||
cp /opt/prole-kdc/kdc.conf /etc/krb5kdc/kdc.conf
|
||||
fi
|
||||
if [[ -f /opt/prole-kdc/kadm5.acl ]]; then
|
||||
cp /opt/prole-kdc/kadm5.acl /etc/krb5kdc/kadm5.acl
|
||||
fi
|
||||
|
||||
if [[ -z "${PROLE_KDC_MASTER_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env PROLE_KDC_MASTER_PASSWORD (secret 'prole-kdc-secrets/master_password')." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "${PROLE_KDC_ADMIN_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env PROLE_KDC_ADMIN_PASSWORD (secret 'prole-kdc-secrets/admin_password')." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f /var/lib/krb5kdc/principal ]]; then
|
||||
echo "Initializing realm database for ${realm}..."
|
||||
kdb5_util create -s -r "${realm}" -P "${PROLE_KDC_MASTER_PASSWORD}"
|
||||
fi
|
||||
|
||||
if ! kadmin.local -q "get_principal ${admin_principal}" >/dev/null 2>&1; then
|
||||
echo "Creating admin principal ${admin_principal}..."
|
||||
kadmin.local -q "addprinc -pw ${PROLE_KDC_ADMIN_PASSWORD} ${admin_principal}"
|
||||
fi
|
||||
|
||||
echo "Starting krb5kdc and kadmind ..."
|
||||
krb5kdc -n &
|
||||
sleep 0.5
|
||||
if ! pgrep -x krb5kdc >/dev/null 2>&1; then
|
||||
echo "ERROR: krb5kdc failed to start. Check /var/log/ (syslog) for details." >&2
|
||||
exit 1
|
||||
fi
|
||||
exec kadmind -nofork
|
||||
@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: prole-kdc-secrets
|
||||
type: Opaque
|
||||
stringData:
|
||||
# Strong random values (do not commit real secrets)
|
||||
master_password: "CHANGE_ME"
|
||||
admin_password: "CHANGE_ME"
|
||||
@ -36,12 +36,14 @@ This repo’s first-pass Grafana SSO is implemented as:
|
||||
Manifests are under `deploy/opentofu/k3s/manifests/prole/`:
|
||||
|
||||
- `prole-auth-deployment.yaml` / `prole-auth-service.yaml`
|
||||
- `prole-kdc-configmap.yaml` (embedded KDC sidecar configuration)
|
||||
- `grafana-proxy-configmap.yaml` / `grafana-proxy-deployment.yaml` / `grafana-proxy-service.yaml`
|
||||
|
||||
Required (provided externally):
|
||||
|
||||
- Secret `prole-auth-secrets` with key `sessionSecret`
|
||||
- Secret `prole-auth-keytab` containing the HTTP service keytab at `http.keytab`
|
||||
- Secret `prole-kdc-secrets` with keys `master_password` and `admin_password` (for the embedded KDC)
|
||||
|
||||
#### Logout / session invalidation
|
||||
|
||||
|
||||
@ -82,14 +82,14 @@ The ncurses implementation shares the same business logic (`ProleController`) as
|
||||
|
||||
**Key Components:**
|
||||
|
||||
1. **installer/ncurses_ui.py** - UI primitives
|
||||
1. **knoe/ncurses_ui.py** - UI primitives
|
||||
- `CursesWindow` - Basic window wrapper with rendering helpers
|
||||
- `TerminalConsole` - Scrollable console output
|
||||
- `NavFooter` - Navigation button bar
|
||||
- `InputField` - Text input widget
|
||||
- `Checkbox` - Checkbox widget
|
||||
|
||||
2. **installer/ncurses_installer.py** - Main installer class
|
||||
2. **knoe/ncurses_installer.py** - Main installer class
|
||||
- `ProleNcursesInstaller` - Screen management and rendering
|
||||
- `run_ncurses_installer()` - Entry point for ncurses mode
|
||||
|
||||
@ -145,7 +145,7 @@ Test the ncurses interface:
|
||||
|
||||
```bash
|
||||
# Verify modules load correctly
|
||||
python3 -c "from installer.ncurses_installer import run_ncurses_installer; print('OK')"
|
||||
python3 -c "from knoe.ncurses_installer import run_ncurses_installer; print('OK')"
|
||||
|
||||
# Run the installer
|
||||
./install.py --no-gui
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
# etc/build_db.sh
|
||||
# Purpose: Build prole-db image, supporting multi-platform (amd64, arm64)
|
||||
# Purpose: Build knoe-db image, supporting multi-platform (amd64, arm64)
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
@ -40,7 +40,7 @@ done
|
||||
cd "$PROJECT_ROOT/prole-db"
|
||||
|
||||
if docker buildx version >/dev/null 2>&1; then
|
||||
log "Building multi-platform image: prole-db:$TAG for $PLATFORMS"
|
||||
log "Building multi-platform image: knoe-db:$TAG for $PLATFORMS"
|
||||
|
||||
# Check if a builder exists that supports multi-platform
|
||||
if ! docker buildx inspect prole-builder >/dev/null 2>&1; then
|
||||
@ -54,15 +54,15 @@ if docker buildx version >/dev/null 2>&1; then
|
||||
if [[ "$TAG" == */* ]]; then
|
||||
BUILD_ARGS+=(-t "$TAG")
|
||||
else
|
||||
BUILD_ARGS+=(-t "prole-db:$TAG")
|
||||
BUILD_ARGS+=(-t "knoe-db:$TAG")
|
||||
fi
|
||||
|
||||
if [[ "$PUSH" == "true" ]]; then
|
||||
log "Pushing to registry..."
|
||||
BUILD_ARGS+=(--push)
|
||||
# Also tag as prole-db:latest for local convenience if it's a remote tag
|
||||
# Also tag as knoe-db:latest for local convenience if it's a remote tag
|
||||
if [[ "$TAG" == */* ]]; then
|
||||
BUILD_ARGS+=(-t "prole-db:latest")
|
||||
BUILD_ARGS+=(-t "knoe-db:latest")
|
||||
fi
|
||||
else
|
||||
# If not pushing, we build only for local platform to allow --load
|
||||
@ -74,7 +74,7 @@ if docker buildx version >/dev/null 2>&1; then
|
||||
if [[ "$TAG" == */* ]]; then
|
||||
BUILD_ARGS+=(-t "$TAG")
|
||||
else
|
||||
BUILD_ARGS+=(-t "prole-db:$TAG")
|
||||
BUILD_ARGS+=(-t "knoe-db:$TAG")
|
||||
fi
|
||||
BUILD_ARGS+=(--load)
|
||||
fi
|
||||
@ -82,5 +82,5 @@ if docker buildx version >/dev/null 2>&1; then
|
||||
docker buildx build --progress=plain "${BUILD_ARGS[@]}" .
|
||||
else
|
||||
log "docker buildx not found; building for local platform only"
|
||||
docker build --progress=plain -t "prole-db:$TAG" .
|
||||
docker build --progress=plain -t "knoe-db:$TAG" .
|
||||
fi
|
||||
|
||||
@ -506,7 +506,7 @@ get_latest_image() {
|
||||
release=$(printf "%03d" "$release")
|
||||
fi
|
||||
|
||||
echo "prole-db:${pg_version}-${release}"
|
||||
echo "knoe-db:${pg_version}-${release}"
|
||||
}
|
||||
|
||||
resolve_cnpg_image() {
|
||||
@ -585,7 +585,7 @@ resolve_cnpg_image() {
|
||||
# If the image is unqualified (no registry), prefix it with the chosen registry
|
||||
if [[ "$image" != */* ]]; then
|
||||
image="${registry}/${image}"
|
||||
# If the first path segment has no dot/colon, it's still unqualified (e.g., prole-db:TAG)
|
||||
# If the first path segment has no dot/colon, it's still unqualified (e.g., knoe-db:TAG)
|
||||
elif [[ "$first" != *"."* && "$first" != *":"* ]]; then
|
||||
image="${registry}/${image}"
|
||||
fi
|
||||
@ -1252,7 +1252,7 @@ _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
|
||||
local plain_image="${image##*/}" # strip registry prefix, e.g. knoe-db:18-088
|
||||
|
||||
if [[ -n "$push_host" ]]; then
|
||||
local push_ref="${push_host}/${plain_image}"
|
||||
@ -1272,7 +1272,7 @@ _push_to_k3d_registry() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# Pre-flight: ensure the prole-db image is available in the k3d cluster before
|
||||
# Pre-flight: ensure the knoe-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.
|
||||
@ -1289,12 +1289,12 @@ _ensure_prole_db_image() {
|
||||
if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then
|
||||
image=$(get_latest_image)
|
||||
else
|
||||
image="prole-db:$VERSION"
|
||||
image="knoe-db:$VERSION"
|
||||
fi
|
||||
fi
|
||||
image=$(resolve_cnpg_image "$image")
|
||||
local prole_db_dir="${PROLE_HOME:-$SCRIPT_DIR/..}/prole-db"
|
||||
local plain_image="${image##*/}" # e.g. prole-db:18-088
|
||||
local plain_image="${image##*/}" # e.g. knoe-db:18-088
|
||||
|
||||
if [[ "${PROLE_MODE:-}" == "k3s" ]]; then
|
||||
echo "Pre-flight: ensuring image '$image' is available in k3s registry/import path ..."
|
||||
@ -1380,7 +1380,7 @@ _ensure_prole_db_image() {
|
||||
|
||||
# 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
|
||||
echo "ERROR: Dockerfile not found in '$prole_db_dir'; cannot build knoe-db image." >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "${PROLE_MODE:-}" == "k3s" ]]; then
|
||||
@ -1388,7 +1388,7 @@ _ensure_prole_db_image() {
|
||||
else
|
||||
echo " Image '$image' not found in k3d, Docker daemon, or docker-import dir."
|
||||
fi
|
||||
echo " Building prole-db image from '$prole_db_dir' ..."
|
||||
echo " Building knoe-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
|
||||
@ -1427,7 +1427,7 @@ ensure_prole_stack_resources() {
|
||||
if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then
|
||||
image=$(get_latest_image)
|
||||
else
|
||||
image="prole-db:$VERSION"
|
||||
image="knoe-db:$VERSION"
|
||||
fi
|
||||
fi
|
||||
image=$(resolve_cnpg_image "$image")
|
||||
@ -2361,7 +2361,7 @@ restore_cnpg_cluster() {
|
||||
if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then
|
||||
image=$(get_latest_image)
|
||||
else
|
||||
image="prole-db:$VERSION"
|
||||
image="knoe-db:$VERSION"
|
||||
fi
|
||||
image=$(resolve_cnpg_image "$image")
|
||||
|
||||
@ -2458,57 +2458,43 @@ bao_service_url() {
|
||||
}
|
||||
|
||||
fetch_admin_keys_and_db_pass_from_bao_or_local() {
|
||||
local token url
|
||||
if [[ -f "$OPENBAO_TOKEN_FILE" ]]; then
|
||||
token=$(cat "$OPENBAO_TOKEN_FILE")
|
||||
else
|
||||
token=""
|
||||
fi
|
||||
url=$(bao_service_url)
|
||||
if [[ -n "$token" && -n "$url" ]]; then
|
||||
local priv_b64 pub_b64
|
||||
priv_b64=$(fetch_openbao_secret "$BAO_PATH_ADMIN" "admin_private_key_b64")
|
||||
pub_b64=$(fetch_openbao_secret "$BAO_PATH_ADMIN" "admin_public_key_b64")
|
||||
if [[ -n "${priv_b64:-}" && "${priv_b64:-}" != "null" && -n "${pub_b64:-}" && "${pub_b64:-}" != "null" ]]; then
|
||||
echo "Attempting to read admin key pair from OpenBao kv/$BAO_PATH_ADMIN ..."
|
||||
if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/$BAO_PATH_ADMIN" | jq -e '.data.data' >/dev/null 2>&1; then
|
||||
local priv_b64 pub_b64
|
||||
priv_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/$BAO_PATH_ADMIN" | jq -r '.data.data.admin_private_key_b64')
|
||||
pub_b64=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/$BAO_PATH_ADMIN" | jq -r '.data.data.admin_public_key_b64')
|
||||
# Use a temporary file to determine where to save based on existing legacy or generic preference
|
||||
local target_priv="$ADMIN_PRIV_GENERIC"
|
||||
local target_pub="$ADMIN_PUB_GENERIC"
|
||||
|
||||
# If legacy keys exist, we might want to overwrite them too for compatibility
|
||||
printf "%s" "$priv_b64" | base64 -d >"$target_priv"
|
||||
printf "%s" "$pub_b64" | base64 -d >"$target_pub"
|
||||
chmod 0600 "$target_priv"
|
||||
|
||||
# Mirror to legacy path if it was expected by other scripts
|
||||
cp "$target_priv" "$ADMIN_PRIV_ED25519" 2>/dev/null || true
|
||||
cp "$target_pub" "$ADMIN_PUB_ED25519" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "Attempting to read database password from OpenBao kv/$BAO_PATH_DB ..."
|
||||
if curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/$BAO_PATH_DB" | jq -e '.data.data' >/dev/null 2>&1; then
|
||||
local db_pass
|
||||
db_pass=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/$BAO_PATH_DB" | jq -r '.data.data.password')
|
||||
if [[ -n "$db_pass" ]]; then
|
||||
echo "Updating database user secret 'prole-db-user' from OpenBao ..."
|
||||
kubectl create secret generic prole-db-user -n "$NAMESPACE" \
|
||||
--from-literal=username=prole \
|
||||
--from-literal=password="$db_pass" \
|
||||
--dry-run=client -o yaml | kubectl_apply_retry "$NAMESPACE"
|
||||
# Prefer the generic filenames.
|
||||
printf "%s" "$priv_b64" | base64 -d >"$ADMIN_PRIV_GENERIC"
|
||||
printf "%s" "$pub_b64" | base64 -d >"$ADMIN_PUB_GENERIC"
|
||||
chmod 0600 "$ADMIN_PRIV_GENERIC"
|
||||
|
||||
echo "Updating database superuser secret 'prole-db-superuser' from OpenBao ..."
|
||||
kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \
|
||||
--from-literal=username=postgres \
|
||||
--from-literal=password="$db_pass" \
|
||||
--dry-run=client -o yaml | kubectl_apply_retry "$NAMESPACE"
|
||||
fi
|
||||
fi
|
||||
# Mirror to legacy path for compatibility.
|
||||
cp "$ADMIN_PRIV_GENERIC" "$ADMIN_PRIV_ED25519" 2>/dev/null || true
|
||||
cp "$ADMIN_PUB_GENERIC" "$ADMIN_PUB_ED25519" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
local db_pass db_user
|
||||
db_pass=$(resolve_db_password)
|
||||
db_user="${PROLE_DB_USER:-prole}"
|
||||
if [[ -n "${db_pass:-}" && "${db_pass:-}" != "null" ]]; then
|
||||
echo "Ensuring database user secret 'prole-db-user' ..."
|
||||
kubectl create secret generic prole-db-user -n "$NAMESPACE" \
|
||||
--from-literal=username="$db_user" \
|
||||
--from-literal=password="$db_pass" \
|
||||
--dry-run=client -o yaml | kubectl_apply_retry "$NAMESPACE"
|
||||
|
||||
echo "Ensuring database superuser secret 'prole-db-superuser' ..."
|
||||
kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \
|
||||
--from-literal=username=postgres \
|
||||
--from-literal=password="$db_pass" \
|
||||
--dry-run=client -o yaml | kubectl_apply_retry "$NAMESPACE"
|
||||
fi
|
||||
|
||||
# No env fallback: if OpenBao is unreachable and secrets are missing, fail clearly
|
||||
|
||||
if ! kubectl -n "$NAMESPACE" get secret prole-db-user >/dev/null 2>&1; then
|
||||
echo "ERROR: 'prole-db-user' secret is missing in namespace '$NAMESPACE' and could not be resolved from OpenBao." >&2
|
||||
echo "ERROR: 'prole-db-user' secret is missing in namespace '$NAMESPACE' and could not be resolved from OpenBao or local DB_PASSWORD." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
@ -2783,7 +2769,7 @@ deploy_cluster() {
|
||||
if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then
|
||||
image=$(get_latest_image)
|
||||
else
|
||||
image="prole-db:$VERSION"
|
||||
image="knoe-db:$VERSION"
|
||||
fi
|
||||
image=$(resolve_cnpg_image "$image")
|
||||
sync_manifest_image "$image"
|
||||
|
||||
@ -628,17 +628,23 @@ else
|
||||
fi
|
||||
|
||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||
if [ -x "$SCRIPT_DIR/init_kdc.sh" ]; then
|
||||
kdc_action="$ACTION"
|
||||
case "$kdc_action" in
|
||||
stop) kdc_action="cleanup" ;;
|
||||
status) kdc_action="status" ;;
|
||||
*) kdc_action="update" ;;
|
||||
esac
|
||||
SERVICE_NAMESPACE="$NS" PROLE_KDC_NAMESPACE="$NS" \
|
||||
"$SCRIPT_DIR/init_kdc.sh" "$kdc_action" || rc=$?
|
||||
# KDC is now embedded in the `prole-auth` pod (multi-container) by default.
|
||||
# Only deploy a standalone KDC when explicitly requested.
|
||||
if [[ "${PROLE_KDC_STANDALONE:-0}" == "1" ]]; then
|
||||
if [ -x "$SCRIPT_DIR/init_kdc.sh" ]; then
|
||||
kdc_action="$ACTION"
|
||||
case "$kdc_action" in
|
||||
stop) kdc_action="cleanup" ;;
|
||||
status) kdc_action="status" ;;
|
||||
*) kdc_action="update" ;;
|
||||
esac
|
||||
SERVICE_NAMESPACE="$NS" PROLE_KDC_NAMESPACE="$NS" \
|
||||
"$SCRIPT_DIR/init_kdc.sh" "$kdc_action" || rc=$?
|
||||
else
|
||||
echo "WARN: init_kdc.sh not found; standalone KDC deploy skipped."
|
||||
fi
|
||||
else
|
||||
echo "WARN: init_kdc.sh not found; kerberos deploy skipped."
|
||||
echo "[INFO] Kerberos enabled: skipping standalone KDC deploy (KDC runs as `kdc` sidecar in `prole-auth`)."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@ -26,6 +26,16 @@ K3S_REGISTRY_HOST=${K3S_REGISTRY_HOST:-$(registry_host_from_url "${PROLE_K3S_SER
|
||||
K3S_REGISTRY_PORT=${K3S_REGISTRY_PORT:-5000}
|
||||
K3S_REGISTRY_NAMESPACE=${K3S_REGISTRY_NAMESPACE:-${REGISTRY_NAMESPACE:-${SERVICE_NAMESPACE:-${PROLE_NAMESPACE:-default}}}}
|
||||
K3S_REGISTRY_FILE=${K3S_REGISTRY_FILE:-/etc/rancher/k3s/registries.yaml}
|
||||
K3S_REGISTRY_SCHEME=${K3S_REGISTRY_SCHEME:-}
|
||||
|
||||
if [[ -z "${K3S_REGISTRY_SCHEME}" ]]; then
|
||||
# k3d's local registry is plain HTTP by default.
|
||||
if [[ "${K3S_REGISTRY_HOST}" == k3d-* ]]; then
|
||||
K3S_REGISTRY_SCHEME="http"
|
||||
else
|
||||
K3S_REGISTRY_SCHEME="https"
|
||||
fi
|
||||
fi
|
||||
|
||||
ensure_root() {
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
@ -38,14 +48,28 @@ render_registries_yaml() {
|
||||
local host="$1"
|
||||
local port="$2"
|
||||
local ns="$3"
|
||||
local scheme="$4"
|
||||
|
||||
if [[ "$scheme" == "http" ]]; then
|
||||
cat <<EOF
|
||||
mirrors:
|
||||
"${host}:${port}":
|
||||
endpoint:
|
||||
- "http://${host}:${port}"
|
||||
"registry.${ns}.svc.cluster.local:${port}":
|
||||
endpoint:
|
||||
- "http://${host}:${port}"
|
||||
EOF
|
||||
return 0
|
||||
fi
|
||||
cat <<EOF
|
||||
mirrors:
|
||||
"${host}:${port}":
|
||||
endpoint:
|
||||
- "https://${host}:${port}"
|
||||
- "${scheme}://${host}:${port}"
|
||||
"registry.${ns}.svc.cluster.local:${port}":
|
||||
endpoint:
|
||||
- "https://${host}:${port}"
|
||||
- "${scheme}://${host}:${port}"
|
||||
configs:
|
||||
"${host}:${port}":
|
||||
tls:
|
||||
@ -64,8 +88,8 @@ case "$ACTION" in
|
||||
fi
|
||||
ensure_root
|
||||
mkdir -p "$(dirname "$K3S_REGISTRY_FILE")"
|
||||
render_registries_yaml "$K3S_REGISTRY_HOST" "$K3S_REGISTRY_PORT" "$K3S_REGISTRY_NAMESPACE" >"$K3S_REGISTRY_FILE"
|
||||
echo "Wrote $K3S_REGISTRY_FILE for ${K3S_REGISTRY_HOST}:${K3S_REGISTRY_PORT}"
|
||||
render_registries_yaml "$K3S_REGISTRY_HOST" "$K3S_REGISTRY_PORT" "$K3S_REGISTRY_NAMESPACE" "$K3S_REGISTRY_SCHEME" >"$K3S_REGISTRY_FILE"
|
||||
echo "Wrote $K3S_REGISTRY_FILE for ${K3S_REGISTRY_SCHEME}://${K3S_REGISTRY_HOST}:${K3S_REGISTRY_PORT}"
|
||||
echo "Restart k3s to apply: sudo systemctl restart k3s"
|
||||
;;
|
||||
status)
|
||||
|
||||
113
etc/init_kdc.sh
113
etc/init_kdc.sh
@ -31,6 +31,9 @@ prole_ensure_kube_context || exit 1
|
||||
ACTION=${1:-initialize}
|
||||
|
||||
KDC_NAMESPACE=${PROLE_KDC_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}
|
||||
# Namespace where prole-auth runs and expects the `prole-kdc-config` ConfigMap.
|
||||
# Defaults to PROLE_NAMESPACE (from prole.cfg) when present.
|
||||
PROLE_AUTH_NAMESPACE=${PROLE_AUTH_NAMESPACE:-${PROLE_NAMESPACE:-}}
|
||||
PROLE_KDC_ENABLED=${PROLE_KDC_ENABLED:-1}
|
||||
PROLE_KDC_NAME=${PROLE_KDC_NAME:-auth}
|
||||
PROLE_KDC_SERVICE=${PROLE_KDC_SERVICE:-auth}
|
||||
@ -810,6 +813,112 @@ EOF
|
||||
|
||||
apply_kdc_manifest
|
||||
|
||||
# prole-auth runs in PROLE_AUTH_NAMESPACE (default: PROLE_NAMESPACE) and mounts
|
||||
# ConfigMap `prole-kdc-config` for its embedded KDC sidecar. When the KDC itself
|
||||
# is deployed into a different namespace (default: SERVICE_NAMESPACE), ensure
|
||||
# the configmap also exists in the prole-auth namespace to prevent FailedMount.
|
||||
if [[ -n "${PROLE_AUTH_NAMESPACE:-}" && "${PROLE_AUTH_NAMESPACE}" != "${KDC_NAMESPACE}" ]]; then
|
||||
if ! kubectl get namespace "$PROLE_AUTH_NAMESPACE" >/dev/null 2>&1; then
|
||||
log "Creating namespace '$PROLE_AUTH_NAMESPACE' ..."
|
||||
kubectl create namespace "$PROLE_AUTH_NAMESPACE" >/dev/null 2>&1 || true
|
||||
fi
|
||||
log "Ensuring ConfigMap 'prole-kdc-config' exists in namespace '${PROLE_AUTH_NAMESPACE}' for prole-auth ..."
|
||||
cat <<EOF | kubectl apply -n "$PROLE_AUTH_NAMESPACE" -f -
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: prole-kdc-config
|
||||
namespace: ${PROLE_AUTH_NAMESPACE}
|
||||
data:
|
||||
krb5.conf: |
|
||||
[libdefaults]
|
||||
default_realm = ${PROLE_KDC_REALM}
|
||||
dns_lookup_realm = false
|
||||
dns_lookup_kdc = false
|
||||
|
||||
[realms]
|
||||
${PROLE_KDC_REALM} = {
|
||||
kdc = 127.0.0.1
|
||||
admin_server = 127.0.0.1
|
||||
}${trust_block}
|
||||
kdc.conf: |
|
||||
[kdcdefaults]
|
||||
kdc_ports = 88
|
||||
kdc_tcp_ports = 88
|
||||
[realms]
|
||||
${PROLE_KDC_REALM} = {
|
||||
database_name = /var/lib/krb5kdc/principal
|
||||
admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
|
||||
acl_file = /etc/krb5kdc/kadm5.acl
|
||||
key_stash_file = /etc/krb5kdc/stash
|
||||
max_life = 10h 0m 0s
|
||||
max_renewable_life = 7d 0h 0m 0s
|
||||
default_principal_flags = +preauth
|
||||
}
|
||||
kadm5.acl: |
|
||||
${admin_acl_principal} *
|
||||
entrypoint.sh: |
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
realm="\${PROLE_KDC_REALM:-PROLE.ORG}"
|
||||
admin_principal="\${PROLE_KDC_ADMIN_PRINCIPAL:-admin/admin}"
|
||||
if [[ "\${admin_principal}" != *"@"* ]]; then
|
||||
admin_principal="\${admin_principal}@\${realm}"
|
||||
fi
|
||||
|
||||
if ! command -v krb5kdc >/dev/null 2>&1; then
|
||||
echo "Installing Kerberos packages..."
|
||||
echo "krb5-config krb5-config/default_realm string \${PROLE_KDC_REALM}" | debconf-set-selections || true
|
||||
echo "krb5-config krb5-config/kerberos_servers string 127.0.0.1" | debconf-set-selections || true
|
||||
echo "krb5-config krb5-config/admin_server string 127.0.0.1" | debconf-set-selections || true
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends krb5-kdc krb5-admin-server krb5-user dnsutils ca-certificates
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
|
||||
mkdir -p /etc/krb5kdc /var/lib/krb5kdc
|
||||
if [[ -f /opt/prole-kdc/krb5.conf ]]; then
|
||||
cp /opt/prole-kdc/krb5.conf /etc/krb5.conf
|
||||
fi
|
||||
if [[ -f /opt/prole-kdc/kdc.conf ]]; then
|
||||
cp /opt/prole-kdc/kdc.conf /etc/krb5kdc/kdc.conf
|
||||
fi
|
||||
if [[ -f /opt/prole-kdc/kadm5.acl ]]; then
|
||||
cp /opt/prole-kdc/kadm5.acl /etc/krb5kdc/kadm5.acl
|
||||
fi
|
||||
|
||||
if [[ -z "\${PROLE_KDC_MASTER_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env PROLE_KDC_MASTER_PASSWORD (secret 'prole-kdc-secrets/master_password')." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "\${PROLE_KDC_ADMIN_PASSWORD:-}" ]]; then
|
||||
echo "ERROR: Missing required env PROLE_KDC_ADMIN_PASSWORD (secret 'prole-kdc-secrets/admin_password')." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f /var/lib/krb5kdc/principal ]]; then
|
||||
echo "Initializing realm database for \${realm}..."
|
||||
kdb5_util create -s -r "\${realm}" -P "\${PROLE_KDC_MASTER_PASSWORD}"
|
||||
fi
|
||||
|
||||
if ! kadmin.local -q "get_principal \${admin_principal}" >/dev/null 2>&1; then
|
||||
echo "Creating admin principal \${admin_principal}..."
|
||||
kadmin.local -q "addprinc -pw \${PROLE_KDC_ADMIN_PASSWORD} \${admin_principal}"
|
||||
fi
|
||||
|
||||
echo "Starting krb5kdc and kadmind ..."
|
||||
krb5kdc -n &
|
||||
sleep 0.5
|
||||
if ! pgrep -x krb5kdc >/dev/null 2>&1; then
|
||||
echo "ERROR: krb5kdc failed to start. Check /var/log/ (syslog) for details." >&2
|
||||
exit 1
|
||||
fi
|
||||
exec kadmind -nofork
|
||||
EOF
|
||||
fi
|
||||
|
||||
local rollout_timeout="$PROLE_KDC_ROLLOUT_TIMEOUT"
|
||||
if [[ "$deployment_present" -eq 0 ]]; then
|
||||
rollout_timeout="$PROLE_KDC_DEPLOY_TIMEOUT"
|
||||
@ -839,6 +948,10 @@ cleanup_prole_kdc() {
|
||||
kubectl -n "$KDC_NAMESPACE" delete service "$PROLE_KDC_SERVICE" --ignore-not-found
|
||||
kubectl -n "$KDC_NAMESPACE" delete deployment "$PROLE_KDC_NAME" --ignore-not-found
|
||||
kubectl -n "$KDC_NAMESPACE" delete configmap prole-kdc-config --ignore-not-found
|
||||
|
||||
if [[ -n "${PROLE_AUTH_NAMESPACE:-}" && "${PROLE_AUTH_NAMESPACE}" != "${KDC_NAMESPACE}" ]]; then
|
||||
kubectl -n "$PROLE_AUTH_NAMESPACE" delete configmap prole-kdc-config --ignore-not-found
|
||||
fi
|
||||
}
|
||||
|
||||
status() {
|
||||
|
||||
@ -481,6 +481,11 @@ default_port_forward_if_local() {
|
||||
}
|
||||
|
||||
sync_prole_kdc_trust() {
|
||||
if [[ "${PROLE_KDC_STANDALONE:-0}" != "1" ]]; then
|
||||
# Default deployment embeds the KDC as a sidecar in `prole-auth`; do not
|
||||
# attempt to manage a standalone KDC unless explicitly requested.
|
||||
return 0
|
||||
fi
|
||||
if [[ ! -x "$SCRIPT_DIR/init_kdc.sh" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
@ -197,7 +197,7 @@ export_backup_bucket() {
|
||||
|
||||
local image pod_name export_subdir
|
||||
image=$(kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" -o jsonpath='{.spec.imageName}' 2>/dev/null || true)
|
||||
image=${image:-${PROLE_DB_IMAGE:-prole-db:latest}}
|
||||
image=${image:-${PROLE_DB_IMAGE:-knoe-db:latest}}
|
||||
|
||||
pod_name="prole-barman-export-$(date +%s)"
|
||||
export_subdir="${BACKUP_DIR}/garage-export-$(date +%Y%m%d%H%M%S)"
|
||||
|
||||
@ -241,8 +241,13 @@ deploy_service_layer() {
|
||||
"$SCRIPT_DIR/init_kong.sh" -n "$ns" "$kong_action" || rc=$?
|
||||
|
||||
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
|
||||
SERVICE_NAMESPACE="$ns" PROLE_KDC_NAMESPACE="$ns" \
|
||||
"$SCRIPT_DIR/init_kdc.sh" "$kdc_action" || rc=$?
|
||||
# KDC is embedded in `prole-auth` by default. Only deploy standalone KDC when requested.
|
||||
if [[ "${PROLE_KDC_STANDALONE:-0}" == "1" ]]; then
|
||||
SERVICE_NAMESPACE="$ns" PROLE_KDC_NAMESPACE="$ns" \
|
||||
"$SCRIPT_DIR/init_kdc.sh" "$kdc_action" || rc=$?
|
||||
else
|
||||
log "[INFO] Kerberos enabled: skipping standalone KDC deploy (KDC runs as `kdc` sidecar in `prole-auth`)."
|
||||
fi
|
||||
fi
|
||||
|
||||
return "$rc"
|
||||
@ -265,7 +270,7 @@ cleanup_old_namespace() {
|
||||
"$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" && "${PROLE_KDC_STANDALONE:-0}" == "1" ]]; then
|
||||
SERVICE_NAMESPACE="$ns" PROLE_KDC_NAMESPACE="$ns" \
|
||||
"$SCRIPT_DIR/init_kdc.sh" cleanup || true
|
||||
fi
|
||||
|
||||
@ -721,7 +721,7 @@ root = sys.argv[1]
|
||||
pw = sys.argv[2]
|
||||
|
||||
sys.path.insert(0, root)
|
||||
from installer import config as inst_config
|
||||
from knoe import config as inst_config
|
||||
|
||||
print(inst_config._encrypt_prole_secret(pw))
|
||||
PY
|
||||
|
||||
@ -13,6 +13,11 @@ _prole_cfg_script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
_prole_cfg_home_guess=$(cd "$_prole_cfg_script_dir/.." && pwd)
|
||||
|
||||
# 1. Try to load env.sh to establish base PROLE_HOME/PROLE_CONF
|
||||
# If PROLE_HOME/PROLE_CONF/etc are already set by the caller (e.g. tests),
|
||||
# do not allow env.sh to clobber them.
|
||||
_prole_cfg_preserve_home="${PROLE_HOME:-}"
|
||||
_prole_cfg_preserve_conf="${PROLE_CONF:-}"
|
||||
_prole_cfg_preserve_service="${PROLE_SERVICE:-}"
|
||||
if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$PROLE_HOME/env.sh"
|
||||
@ -24,6 +29,20 @@ elif [[ -f "$_prole_cfg_home_guess/env.sh" ]]; then
|
||||
source "$_prole_cfg_home_guess/env.sh"
|
||||
fi
|
||||
|
||||
if [[ -n "$_prole_cfg_preserve_home" ]]; then
|
||||
PROLE_HOME="$_prole_cfg_preserve_home"
|
||||
export PROLE_HOME
|
||||
fi
|
||||
if [[ -n "$_prole_cfg_preserve_conf" ]]; then
|
||||
PROLE_CONF="$_prole_cfg_preserve_conf"
|
||||
export PROLE_CONF
|
||||
fi
|
||||
if [[ -n "$_prole_cfg_preserve_service" ]]; then
|
||||
PROLE_SERVICE="$_prole_cfg_preserve_service"
|
||||
export PROLE_SERVICE
|
||||
fi
|
||||
unset _prole_cfg_preserve_home _prole_cfg_preserve_conf _prole_cfg_preserve_service
|
||||
|
||||
_prole_trim() {
|
||||
local s="$1"
|
||||
s="${s#"${s%%[![:space:]]*}"}"
|
||||
@ -441,6 +460,17 @@ if [[ -n "$_prole_cfg_file" ]]; then
|
||||
if [[ -n "$_cfg_sns" ]]; then
|
||||
export SERVICE_NAMESPACE="$_cfg_sns"
|
||||
fi
|
||||
|
||||
# Always prefer deployment/mode hint from prole.cfg (single source of truth).
|
||||
# This prevents leaked environment values (e.g. PROLE_MODE=k3d) from forcing the wrong mode.
|
||||
_cfg_dm=$(_prole_cfg_extract_key_in_files "DEPLOYMENT_MODE" "${_prole_cfg_files[@]}")
|
||||
if [[ -z "$_cfg_dm" ]]; then
|
||||
_cfg_dm=$(_prole_cfg_extract_key_in_files "prole.mode" "${_prole_cfg_files[@]}")
|
||||
fi
|
||||
if [[ -n "$_cfg_dm" ]]; then
|
||||
DEPLOYMENT_MODE="$_cfg_dm"
|
||||
export DEPLOYMENT_MODE
|
||||
fi
|
||||
_cfg_sh=$(_prole_cfg_extract_key_in_files "SERVICE_HOSTNAME" "${_prole_cfg_files[@]}")
|
||||
if [[ -z "$_cfg_sh" ]]; then
|
||||
_cfg_sh=$(_prole_cfg_extract_key_in_files "service_hostname" "${_prole_cfg_files[@]}")
|
||||
@ -462,7 +492,7 @@ if [[ -n "$_prole_cfg_file" ]]; then
|
||||
if [[ -n "$_cfg_ctx" ]]; then
|
||||
export KUBECONTEXT="$_cfg_ctx"
|
||||
fi
|
||||
unset _cfg_ns _cfg_sns _cfg_sh _cfg_sbh _cfg_ctx _prole_cfg_files _prole_cfg_f
|
||||
unset _cfg_ns _cfg_sns _cfg_dm _cfg_sh _cfg_sbh _cfg_ctx _prole_cfg_files _prole_cfg_f
|
||||
fi
|
||||
|
||||
if [[ -z "${PROLE_HOME:-}" && -d "$_prole_cfg_home_guess" ]]; then
|
||||
|
||||
@ -6,8 +6,8 @@ set -euo pipefail
|
||||
# Purpose:
|
||||
# - Thin wrapper around the Python-native cluster repair logic.
|
||||
#
|
||||
# The real orchestration now lives in `installer/core/actions.py` and is
|
||||
# invoked through `installer/core/repair_pipeline_cli.py`.
|
||||
# The real orchestration now lives in `knoe/core/actions.py` and is
|
||||
# invoked through `knoe/core/repair_pipeline_cli.py`.
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
ROOT_DIR=$(cd "$SCRIPT_DIR/.." && pwd)
|
||||
@ -88,4 +88,4 @@ if [[ -n "$MODE" ]]; then
|
||||
fi
|
||||
args+=("--config" "$PROLE_CONF")
|
||||
|
||||
exec python3 -m installer.core.repair_pipeline_cli "${args[@]}"
|
||||
exec python3 -m knoe.core.repair_pipeline_cli "${args[@]}"
|
||||
|
||||
@ -6,7 +6,7 @@ from pathlib import Path
|
||||
ROOT_DIR = Path(__file__).resolve().parents[1]
|
||||
sys.path.append(str(ROOT_DIR))
|
||||
|
||||
from installer import config as inst_config
|
||||
from knoe import config as inst_config
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@ -12,7 +12,7 @@ import tkinter as tk # noqa: F401
|
||||
from tkinter import ttk, scrolledtext, messagebox, filedialog # noqa: F401
|
||||
from pathlib import Path # noqa: F401
|
||||
|
||||
from installer.ui.screens import (
|
||||
from knoe.ui.screens import (
|
||||
ProleInstaller,
|
||||
ProleController,
|
||||
get_resource_path,
|
||||
|
||||
7
k3s/registries.yaml
Normal file
7
k3s/registries.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
mirrors:
|
||||
"k3d-prole-registry:5000":
|
||||
endpoint:
|
||||
- "http://k3d-prole-registry:5000"
|
||||
"k3d-prole-data-registry:5000":
|
||||
endpoint:
|
||||
- "http://k3d-prole-data-registry:5000"
|
||||
@ -5,7 +5,7 @@ metadata:
|
||||
name: prole-db
|
||||
spec:
|
||||
instances: 3
|
||||
imageName: myrddin.prole.org:5000/prole-db:0.0.0
|
||||
imageName: k3d-prole-registry:5000/knoe-db:17-137
|
||||
postgresUID: 100
|
||||
postgresGID: 101
|
||||
maxSyncReplicas: 1
|
||||
|
||||
@ -4,7 +4,7 @@ metadata:
|
||||
name: prole-db
|
||||
spec:
|
||||
instances: 3
|
||||
imageName: myrddin.prole.org:5000/prole-db:18-125
|
||||
imageName: myrddin.prole.org:5000/knoe-db:18-125
|
||||
postgresUID: 100
|
||||
postgresGID: 101
|
||||
maxSyncReplicas: 1
|
||||
|
||||
96
knoe.spec
Normal file
96
knoe.spec
Normal file
@ -0,0 +1,96 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
block_cipher = None
|
||||
|
||||
# Get project root
|
||||
project_root = Path('.').absolute()
|
||||
|
||||
# Data files to include
|
||||
datas = [
|
||||
('installer', 'installer'),
|
||||
('conf', 'conf'),
|
||||
('etc', 'etc'),
|
||||
('img', 'img'),
|
||||
('docs', 'docs'),
|
||||
('k8s', 'k8s'),
|
||||
('prole-db', 'prole-db'),
|
||||
('prole-app/dist/Prole Tools.app', 'prole-app/dist/Prole Tools.app'),
|
||||
]
|
||||
|
||||
# Binaries to include (with execute permissions)
|
||||
binaries = [
|
||||
('prole-net/prole-agent', 'prole-net'),
|
||||
]
|
||||
|
||||
# Hidden imports
|
||||
hiddenimports = [
|
||||
'installer',
|
||||
'knoe.config',
|
||||
'knoe.build',
|
||||
'knoe.deploy',
|
||||
'knoe.screen',
|
||||
'knoe.main',
|
||||
'knoe.ncurses_ui',
|
||||
'knoe.ncurses_installer',
|
||||
'curses',
|
||||
'_curses',
|
||||
]
|
||||
|
||||
a = Analysis(
|
||||
['install.py'],
|
||||
pathex=[],
|
||||
binaries=binaries,
|
||||
datas=datas,
|
||||
hiddenimports=hiddenimports,
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='prole-installer',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True, # Enable console for --no-gui mode
|
||||
disable_windowed_traceback=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon='build/prole.icns',
|
||||
)
|
||||
|
||||
# Create macOS app bundle
|
||||
app = BUNDLE(
|
||||
exe,
|
||||
name='Prole Installer.app',
|
||||
icon='build/prole.icns',
|
||||
bundle_identifier='com.prole.installer',
|
||||
info_plist={
|
||||
'CFBundleName': 'Prole Installer',
|
||||
'CFBundleDisplayName': 'Prole Database Installer',
|
||||
'CFBundleVersion': '1.0.0',
|
||||
'CFBundleShortVersionString': '1.0.0',
|
||||
'NSHighResolutionCapable': 'True',
|
||||
'LSMinimumSystemVersion': '10.13.0',
|
||||
},
|
||||
)
|
||||
@ -61,7 +61,7 @@ class BuildMilestone(Milestone):
|
||||
|
||||
# ---------------- Screen (UI) helpers ----------------
|
||||
def create_build_page(app):
|
||||
"""Create the Build page UI and register it via installer.main."""
|
||||
"""Create the Build page UI and register it via knoe.main."""
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, scrolledtext
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
"""
|
||||
Shared configuration and utility functions for the Prole installer (root-level).
|
||||
|
||||
This mirrors `prole.installer.config` but is located under the root `installer/`
|
||||
package per the refactor request. UI code should import from `installer.config`.
|
||||
This mirrors `prole.knoe.config` but is located under the root `installer/`
|
||||
package per the refactor request. UI code should import from `knoe.config`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@ -30,7 +30,7 @@ OPENBAO_PREFIX = "${OPENBAO:"
|
||||
OPENBAO_SUFFIX = "}"
|
||||
PROLE_SECRET_VERSION = "v1"
|
||||
PROLE_SECRET_SERVICE = "prole-installer"
|
||||
PROLE_SECRET_KEY_FILE = Path.home() / ".prole" / "secrets" / "installer.key"
|
||||
PROLE_SECRET_KEY_FILE = Path.home() / ".prole" / "secrets" / "knoe.key"
|
||||
|
||||
# Map config keys to OpenBao paths (namespace injected at runtime)
|
||||
SECRET_KEY_SPECS = {
|
||||
@ -452,6 +452,83 @@ def _collect_cfg_vars(cfg: any) -> dict:
|
||||
return variables
|
||||
|
||||
|
||||
def _collect_cfg_vars_from_data(cfg_data: dict | None) -> dict[str, str]:
|
||||
"""Collect variable names from a `prole_cfg_data`-style dict.
|
||||
|
||||
This mirrors `_collect_cfg_vars()` but operates on the UI/controller's in-memory
|
||||
dict instead of a `configparser.ConfigParser`.
|
||||
"""
|
||||
variables: dict[str, str] = {}
|
||||
if not cfg_data or not isinstance(cfg_data, dict):
|
||||
return variables
|
||||
|
||||
def _is_placeholder(v: str) -> bool:
|
||||
vv = (v or "").strip()
|
||||
return vv.startswith("${") and vv.endswith("}")
|
||||
|
||||
for section in ("Global", "System Environment", "User"):
|
||||
sec = cfg_data.get(section, {}) or {}
|
||||
if not isinstance(sec, dict):
|
||||
continue
|
||||
for k, v in sec.items():
|
||||
s = str(v or "").strip()
|
||||
if not s or _is_placeholder(s):
|
||||
continue
|
||||
variables[str(k)] = s
|
||||
return variables
|
||||
|
||||
|
||||
def _expand_cfg_vars_shellstyle(val: str, variables: dict[str, str]) -> str:
|
||||
"""Expand `$VAR` and `${VAR}` using `variables`.
|
||||
|
||||
Notes:
|
||||
- Only expands simple shell-style identifiers (letters/digits/underscore).
|
||||
This intentionally avoids treating `${PROLE_SECRET:...}` or similar
|
||||
colon-delimited references as variables.
|
||||
- Unknown variables are left intact.
|
||||
"""
|
||||
if not val or not isinstance(val, str) or not variables:
|
||||
return val
|
||||
|
||||
# ${VAR}
|
||||
def repl_braced(m: re.Match) -> str:
|
||||
name = m.group(1)
|
||||
return variables.get(name, m.group(0))
|
||||
|
||||
out = re.sub(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}", repl_braced, val)
|
||||
|
||||
# $VAR (avoid $ followed by { ... } which is already handled above)
|
||||
def repl_plain(m: re.Match) -> str:
|
||||
name = m.group(1)
|
||||
return variables.get(name, m.group(0))
|
||||
|
||||
out = re.sub(r"\$([A-Za-z_][A-Za-z0-9_]*)", repl_plain, out)
|
||||
return out
|
||||
|
||||
|
||||
def _expand_path_expr(val: str | None, variables: dict[str, str] | None = None) -> str:
|
||||
"""Expand a path-like expression with support for config variables.
|
||||
|
||||
Expansion order (fixed-point loop):
|
||||
1) `~` expansion
|
||||
2) config variables (`$VAR` and `${VAR}`)
|
||||
3) OS environment variables (via `os.path.expandvars`)
|
||||
"""
|
||||
if not val:
|
||||
return ""
|
||||
|
||||
out = os.path.expanduser(str(val))
|
||||
for _ in range(10):
|
||||
new = out
|
||||
if variables:
|
||||
new = _expand_cfg_vars_shellstyle(new, variables)
|
||||
new = os.path.expandvars(new)
|
||||
if new == out:
|
||||
break
|
||||
out = new
|
||||
return out
|
||||
|
||||
|
||||
def _expand_cfg_value(val: str, variables: dict) -> str:
|
||||
if not val or not isinstance(val, str):
|
||||
return val
|
||||
@ -583,7 +660,7 @@ def _update_prole_cfg_value(section: str, key: str, value: str):
|
||||
# Prefer `$PROLE_CONF/prole.cfg` (single entrypoint) and follow symlink so we
|
||||
# update the active environment base file without mutating other environments.
|
||||
try:
|
||||
from installer import prole_conf
|
||||
from knoe import prole_conf
|
||||
|
||||
conf_dir = prole_conf.resolve_prole_conf_dir(PROJECT_ROOT)
|
||||
cfg_path = prole_conf.entrypoint_path(conf_dir)
|
||||
@ -15,7 +15,7 @@ import threading
|
||||
import uuid
|
||||
from typing import Callable, Sequence
|
||||
|
||||
from installer.config import (
|
||||
from knoe.config import (
|
||||
_expand_path,
|
||||
_collect_cfg_vars,
|
||||
_expand_cfg_value,
|
||||
@ -28,10 +28,10 @@ from installer.config import (
|
||||
_encrypt_cfg_secret,
|
||||
_merge_kubeconfig,
|
||||
)
|
||||
from installer import prole_conf as prole_conf_mgr
|
||||
from installer.core.controller import ProleController
|
||||
from installer.core.env import * # noqa: F401,F403
|
||||
from installer.core.env import (
|
||||
from knoe import prole_conf as prole_conf_mgr
|
||||
from knoe.core.controller import ProleController
|
||||
from knoe.core.env import * # noqa: F401,F403
|
||||
from knoe.core.env import (
|
||||
_parse_ollama_host,
|
||||
_bool_str,
|
||||
_deployment_mode_from_env,
|
||||
@ -63,7 +63,7 @@ from installer.core.env import (
|
||||
_kubectl_base_cmd_for_k3s as _kubectl_base_cmd_for_k3s_fn,
|
||||
_verify_k3s_services_status,
|
||||
)
|
||||
from installer.core.milestones import (
|
||||
from knoe.core.milestones import (
|
||||
DependenciesMilestone,
|
||||
NetworkScanMilestone,
|
||||
EnvSetupMilestone,
|
||||
@ -78,8 +78,9 @@ from installer.core.milestones import (
|
||||
SupabaseImagePreloadMilestone,
|
||||
SupabaseMilestone,
|
||||
)
|
||||
from installer.core.stream_exec import run_streaming_cmd
|
||||
from installer.core.policy import (
|
||||
from knoe.core.stream_exec import run_streaming_cmd
|
||||
from knoe.core.build_context import copy_build_context_dir
|
||||
from knoe.core.policy import (
|
||||
POLICY_CFG_KEY,
|
||||
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES,
|
||||
evaluate_optional_workloads_allowed,
|
||||
@ -773,13 +774,13 @@ class ProleInstaller:
|
||||
try:
|
||||
return os.getlogin()
|
||||
except Exception:
|
||||
return "prole-db"
|
||||
return "knoe"
|
||||
|
||||
def _sanitize_namespace(self, name: str) -> str:
|
||||
cleaned = re.sub(r"[^a-z0-9-]+", "-", (name or "").lower())
|
||||
cleaned = re.sub(r"-{2,}", "-", cleaned).strip("-")
|
||||
if not cleaned:
|
||||
cleaned = "prole-db"
|
||||
cleaned = "knoe-db"
|
||||
if len(cleaned) > 63:
|
||||
cleaned = cleaned[:63].rstrip("-")
|
||||
return cleaned
|
||||
@ -787,13 +788,13 @@ class ProleInstaller:
|
||||
def _generate_namespace_name(self) -> str:
|
||||
owner = self._sanitize_namespace(self._get_local_owner())
|
||||
suffix = uuid.uuid4().hex[:6]
|
||||
base = f"prole-db-{owner}-{suffix}"
|
||||
base = f"knoe-db-{owner}-{suffix}"
|
||||
return self._sanitize_namespace(base)
|
||||
|
||||
def _ensure_namespace_prefix(self, name: str) -> str:
|
||||
cleaned = (name or "").strip()
|
||||
if not cleaned:
|
||||
return "prole-db"
|
||||
return "knoe-db"
|
||||
return cleaned
|
||||
|
||||
def _read_existing_cfg_namespace(self) -> str | None:
|
||||
@ -1073,12 +1074,29 @@ class ProleInstaller:
|
||||
cluster_env = self._get_input("init_cluster.cluster_env", "dev")
|
||||
env_key = _normalize_cluster_env(cluster_env)
|
||||
|
||||
home = Path(values["PROLE_HOME"]).expanduser()
|
||||
env_map = dict(os.environ)
|
||||
try:
|
||||
env_map.update({str(k): str(v) for k, v in (values or {}).items() if v is not None})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
expanded_home = self._expand_shell_path(values.get("PROLE_HOME", ""), env=env_map)
|
||||
expanded_home = os.path.expanduser(expanded_home) if expanded_home else ""
|
||||
if expanded_home:
|
||||
env_map["PROLE_HOME"] = expanded_home
|
||||
home = Path(expanded_home)
|
||||
else:
|
||||
home = Path.home() / ".prole"
|
||||
if env_key == "dev":
|
||||
home.mkdir(parents=True, exist_ok=True)
|
||||
for key in ("PROLE_CONF", "PROLE_DATA", "PROLE_LOGS", "PROLE_SERVICE"):
|
||||
try:
|
||||
Path(values[key]).expanduser().mkdir(parents=True, exist_ok=True)
|
||||
raw = values.get(key, "")
|
||||
expanded = self._expand_shell_path(raw, env=env_map)
|
||||
expanded = os.path.expanduser(expanded) if expanded else ""
|
||||
if expanded:
|
||||
env_map[key] = expanded
|
||||
Path(expanded).mkdir(parents=True, exist_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@ -1149,8 +1167,21 @@ class ProleInstaller:
|
||||
def _deploy_env_resources(self, values: dict):
|
||||
"""Copy init scripts and etc directory into PROLE_HOME / PROLE_SERVICE."""
|
||||
try:
|
||||
prole_home = Path(values["PROLE_HOME"]).expanduser()
|
||||
prole_service = Path(values["PROLE_SERVICE"]).expanduser()
|
||||
env_map = dict(os.environ)
|
||||
try:
|
||||
env_map.update({str(k): str(v) for k, v in (values or {}).items() if v is not None})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
expanded_home = self._expand_shell_path(values.get("PROLE_HOME", ""), env=env_map)
|
||||
expanded_home = os.path.expanduser(expanded_home) if expanded_home else ""
|
||||
if expanded_home:
|
||||
env_map["PROLE_HOME"] = expanded_home
|
||||
expanded_service = self._expand_shell_path(values.get("PROLE_SERVICE", ""), env=env_map)
|
||||
expanded_service = os.path.expanduser(expanded_service) if expanded_service else ""
|
||||
|
||||
prole_home = Path(expanded_home or (Path.home() / ".prole"))
|
||||
prole_service = Path(expanded_service or (prole_home / "etc"))
|
||||
|
||||
init_pf_src_candidates = [
|
||||
self.project_root / "src" / "prole" / "etc" / "init-port-forward.sh",
|
||||
@ -1184,9 +1215,10 @@ class ProleInstaller:
|
||||
pass
|
||||
|
||||
def reload_env_from_shell(self) -> None:
|
||||
home = Path(
|
||||
self._get_input("env_setup.PROLE_HOME", str(Path.home() / ".prole"))
|
||||
)
|
||||
raw_home = self._get_input("env_setup.PROLE_HOME", str(Path.home() / ".prole"))
|
||||
expanded_home = self._expand_shell_path(raw_home, env=os.environ)
|
||||
expanded_home = os.path.expanduser(expanded_home) if expanded_home else ""
|
||||
home = Path(expanded_home or str(Path.home() / ".prole"))
|
||||
env_file = home / "env.sh"
|
||||
cmd = (
|
||||
f"export PROLE_HOME={shlex.quote(str(home))}; "
|
||||
@ -4271,7 +4303,7 @@ class ProleConsoleInstaller(ProleInstaller):
|
||||
return
|
||||
|
||||
tag = self.controller.get_prole_db_version()
|
||||
image_name = f"prole-db:{tag}"
|
||||
image_name = f"knoe-db:{tag}"
|
||||
env_key = _normalize_cluster_env(
|
||||
self._get_input("init_cluster.cluster_env", "dev")
|
||||
)
|
||||
@ -4309,7 +4341,7 @@ class ProleConsoleInstaller(ProleInstaller):
|
||||
if not registry or registry == "localhost:5000":
|
||||
push_ok = False
|
||||
self.err(
|
||||
"[ERROR] k3s mode but registry host could not be resolved; cannot push prole-db image."
|
||||
"[ERROR] k3s mode but registry host could not be resolved; cannot push knoe-db image."
|
||||
)
|
||||
else:
|
||||
remote_tag = f"{registry}/{image_name}"
|
||||
@ -4337,16 +4369,12 @@ class ProleConsoleInstaller(ProleInstaller):
|
||||
)
|
||||
return
|
||||
|
||||
self.log("==> Build prole-db image")
|
||||
self.log("==> Build knoe-db image")
|
||||
prole_home = Path.home() / ".prole"
|
||||
build_dir = prole_home / "build" / "prole-db"
|
||||
build_dir.mkdir(parents=True, exist_ok=True)
|
||||
source_dir = get_resource_path("prole-db")
|
||||
if source_dir.exists():
|
||||
if source_dir.resolve() != build_dir.resolve():
|
||||
if build_dir.exists():
|
||||
shutil.rmtree(build_dir)
|
||||
shutil.copytree(source_dir, build_dir)
|
||||
copy_build_context_dir(source_dir, build_dir)
|
||||
|
||||
pub_key_path = Path.home() / ".ssh" / "id_prole_ed25519.pub"
|
||||
pub_key = pub_key_path.read_text().strip() if pub_key_path.exists() else ""
|
||||
@ -5117,7 +5145,14 @@ class ProleConsoleInstaller(ProleInstaller):
|
||||
env_key = _normalize_cluster_env(
|
||||
self._get_input("init_cluster.cluster_env", "")
|
||||
)
|
||||
if env_key != "service":
|
||||
mode = _deployment_mode_from_env(self._get_input("init_cluster.mode", ""))
|
||||
if not mode:
|
||||
mode = _deployment_mode_from_env(os.environ.get("PROLE_MODE", ""))
|
||||
|
||||
# OpenTofu pipeline is k3s-scoped, but we still want it available for k3d
|
||||
# installs when the user provides k3s connection settings (e.g., staging
|
||||
# manifests for a service cluster while running a local dev cluster).
|
||||
if env_key != "service" and mode not in ("k3d", "k3s"):
|
||||
return
|
||||
namespace = (
|
||||
self._get_input("init_password.db_namespace", "") or ""
|
||||
@ -5611,63 +5646,63 @@ def _prepare_k3s_pipeline(
|
||||
_log("==> Loading installer config...\n")
|
||||
installer = ProleConsoleInstaller(controller, str(cfg_path))
|
||||
try:
|
||||
existing_inputs = installer._load_inputs_from_cfg()
|
||||
existing_inputs = knoe._load_inputs_from_cfg()
|
||||
except Exception:
|
||||
existing_inputs = {}
|
||||
installer.inputs = {**installer._default_inputs(), **existing_inputs}
|
||||
knoe.inputs = {**knoe._default_inputs(), **existing_inputs}
|
||||
|
||||
if k3s_server and not installer.inputs.get("init_cluster.k3s_server_url"):
|
||||
installer.inputs["init_cluster.k3s_server_url"] = k3s_server
|
||||
if k3s_token and not installer.inputs.get("init_cluster.k3s_token"):
|
||||
installer.inputs["init_cluster.k3s_token"] = k3s_token
|
||||
if k3s_server and not knoe.inputs.get("init_cluster.k3s_server_url"):
|
||||
knoe.inputs["init_cluster.k3s_server_url"] = k3s_server
|
||||
if k3s_token and not knoe.inputs.get("init_cluster.k3s_token"):
|
||||
knoe.inputs["init_cluster.k3s_token"] = k3s_token
|
||||
|
||||
namespace = (installer._get_input("init_password.db_namespace", "") or "").strip()
|
||||
namespace = (knoe._get_input("init_password.db_namespace", "") or "").strip()
|
||||
if not namespace:
|
||||
namespace = (
|
||||
installer._get_input("env_setup.NAMESPACE", "") or ""
|
||||
knoe._get_input("env_setup.NAMESPACE", "") or ""
|
||||
).strip() or "default"
|
||||
_log(f" Target namespace: {namespace}\n")
|
||||
_log(" [OK] Config loaded\n\n")
|
||||
|
||||
if not skip_validation:
|
||||
# Standalone mode: write a pipeline-specific cfg and validate
|
||||
installer.inputs["init_cluster.cluster_env"] = "prole-service-cluster"
|
||||
installer.inputs["init_cluster.supabase_enabled"] = _bool_str(False)
|
||||
installer.inputs["init_cluster.kerberos_enabled"] = _bool_str(False)
|
||||
installer.inputs["init_cluster.at_rest_encryption_enabled"] = _bool_str(True)
|
||||
installer.inputs["kerberos_config.enabled"] = _bool_str(False)
|
||||
installer.inputs["kerberos_config.test_connection"] = _bool_str(False)
|
||||
knoe.inputs["init_cluster.cluster_env"] = "prole-service-cluster"
|
||||
knoe.inputs["init_cluster.supabase_enabled"] = _bool_str(False)
|
||||
knoe.inputs["init_cluster.kerberos_enabled"] = _bool_str(False)
|
||||
knoe.inputs["init_cluster.at_rest_encryption_enabled"] = _bool_str(True)
|
||||
knoe.inputs["kerberos_config.enabled"] = _bool_str(False)
|
||||
knoe.inputs["kerberos_config.test_connection"] = _bool_str(False)
|
||||
|
||||
installer.inputs["env_setup.PROLE_HOME"] = str(project_root)
|
||||
installer.inputs["env_setup.PROLE_CONF"] = str(project_root / "conf")
|
||||
installer.inputs["env_setup.PROLE_DATA"] = installer._resolve_env_value(
|
||||
knoe.inputs["env_setup.PROLE_HOME"] = str(project_root)
|
||||
knoe.inputs["env_setup.PROLE_CONF"] = str(project_root / "conf")
|
||||
knoe.inputs["env_setup.PROLE_DATA"] = knoe._resolve_env_value(
|
||||
"PROLE_DATA", str(Path.home() / ".prole" / "data")
|
||||
) or str(Path.home() / ".prole" / "data")
|
||||
installer.inputs["env_setup.PROLE_LOGS"] = installer._resolve_env_value(
|
||||
knoe.inputs["env_setup.PROLE_LOGS"] = knoe._resolve_env_value(
|
||||
"PROLE_LOGS", str(Path.home() / ".prole" / "logs")
|
||||
) or str(Path.home() / ".prole" / "logs")
|
||||
installer.inputs["env_setup.PROLE_SERVICE"] = str(project_root / "etc")
|
||||
knoe.inputs["env_setup.PROLE_SERVICE"] = str(project_root / "etc")
|
||||
|
||||
# Ensure required keys that the silent install test validates
|
||||
if not installer.inputs.get("env_setup.NAMESPACE"):
|
||||
installer.inputs["env_setup.NAMESPACE"] = (
|
||||
installer.inputs.get("init_password.db_namespace", "") or "default"
|
||||
if not knoe.inputs.get("env_setup.NAMESPACE"):
|
||||
knoe.inputs["env_setup.NAMESPACE"] = (
|
||||
knoe.inputs.get("init_password.db_namespace", "") or "default"
|
||||
)
|
||||
if not installer.inputs.get("init_password.db_namespace"):
|
||||
installer.inputs["init_password.db_namespace"] = (
|
||||
installer.inputs.get("env_setup.NAMESPACE", "") or "default"
|
||||
if not knoe.inputs.get("init_password.db_namespace"):
|
||||
knoe.inputs["init_password.db_namespace"] = (
|
||||
knoe.inputs.get("env_setup.NAMESPACE", "") or "default"
|
||||
)
|
||||
if not installer.inputs.get("kerberos_config.init_authority"):
|
||||
installer.inputs["kerberos_config.init_authority"] = _bool_str(False)
|
||||
if not installer.inputs.get("init_password.db_host_port"):
|
||||
installer.inputs["init_password.db_host_port"] = "5432"
|
||||
if not knoe.inputs.get("kerberos_config.init_authority"):
|
||||
knoe.inputs["kerberos_config.init_authority"] = _bool_str(False)
|
||||
if not knoe.inputs.get("init_password.db_host_port"):
|
||||
knoe.inputs["init_password.db_host_port"] = "5432"
|
||||
_log(" [OK] Config overrides applied for k3s pipeline\n")
|
||||
|
||||
_log("==> Writing prole.cfg...\n")
|
||||
installer._write_cfg()
|
||||
knoe._write_cfg()
|
||||
_log(f" [OK] {cfg_path}\n\n")
|
||||
|
||||
db_password = installer._get_input("init_password.db_password", "").strip()
|
||||
db_password = knoe._get_input("init_password.db_password", "").strip()
|
||||
_log(f"==> Target namespace: {namespace}\n\n")
|
||||
|
||||
attempt = 1
|
||||
@ -5685,8 +5720,8 @@ def _prepare_k3s_pipeline(
|
||||
break
|
||||
|
||||
_log(f" [WARN] Silent install test failed (code {rc})\n")
|
||||
cur_server = installer.inputs.get("init_cluster.k3s_server_url", k3s_server)
|
||||
cur_token = installer.inputs.get("init_cluster.k3s_token", k3s_token)
|
||||
cur_server = knoe.inputs.get("init_cluster.k3s_server_url", k3s_server)
|
||||
cur_token = knoe.inputs.get("init_cluster.k3s_token", k3s_token)
|
||||
|
||||
if attempt == 1:
|
||||
_log(" Attempting k3s repair...\n")
|
||||
@ -5706,8 +5741,8 @@ def _prepare_k3s_pipeline(
|
||||
else:
|
||||
_log("==> Skipping validation (silent install already succeeded)\n\n")
|
||||
|
||||
cur_server = installer.inputs.get("init_cluster.k3s_server_url", k3s_server)
|
||||
cur_token = installer.inputs.get("init_cluster.k3s_token", k3s_token)
|
||||
cur_server = knoe.inputs.get("init_cluster.k3s_server_url", k3s_server)
|
||||
cur_token = knoe.inputs.get("init_cluster.k3s_token", k3s_token)
|
||||
_sync_opentofu_pipeline(project_root, namespace, cur_server, cur_token, log_fn=_log)
|
||||
|
||||
_log("\n==> k3s pipeline preparation complete.\n")
|
||||
66
knoe/core/build_context.py
Normal file
66
knoe/core/build_context.py
Normal file
@ -0,0 +1,66 @@
|
||||
"""Helpers for preparing Docker build contexts.
|
||||
|
||||
These functions exist to make Docker builds robust when running from:
|
||||
- a source checkout (may contain runtime artifacts like `prole-db/data/`)
|
||||
- a packaged distribution (resources extracted to a temp dir)
|
||||
|
||||
The Docker build context should only contain source-controlled inputs needed
|
||||
for the image build. Runtime data directories can contain broken symlinks,
|
||||
concurrently-mutating files, or large volumes that should never be copied.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
DEFAULT_IGNORED_NAMES: tuple[str, ...] = (
|
||||
# Runtime/stateful artifacts (can be huge and may contain dangling paths)
|
||||
"data",
|
||||
# Common VCS/cache noise
|
||||
".git",
|
||||
"__pycache__",
|
||||
".pytest_cache",
|
||||
)
|
||||
|
||||
|
||||
def copy_build_context_dir(
|
||||
source_dir: Path,
|
||||
build_dir: Path,
|
||||
*,
|
||||
ignored_names: Iterable[str] = DEFAULT_IGNORED_NAMES,
|
||||
) -> None:
|
||||
"""Copy a directory tree into `build_dir` for use as a Docker build context.
|
||||
|
||||
- Removes any existing `build_dir` first.
|
||||
- Preserves symlinks (does not follow them).
|
||||
- Ignores selected directory/file names anywhere in the tree.
|
||||
"""
|
||||
|
||||
source_dir = Path(source_dir)
|
||||
build_dir = Path(build_dir)
|
||||
|
||||
if source_dir.exists() is False:
|
||||
return
|
||||
|
||||
if source_dir.resolve() == build_dir.resolve():
|
||||
# Nothing to do; copying onto itself is both pointless and dangerous.
|
||||
return
|
||||
|
||||
ignored_set = set(ignored_names)
|
||||
|
||||
def _ignore(_dir: str, names: list[str]) -> set[str]:
|
||||
return {name for name in names if name in ignored_set}
|
||||
|
||||
if build_dir.exists():
|
||||
shutil.rmtree(build_dir)
|
||||
|
||||
shutil.copytree(
|
||||
source_dir,
|
||||
build_dir,
|
||||
symlinks=True,
|
||||
ignore=_ignore,
|
||||
ignore_dangling_symlinks=True,
|
||||
)
|
||||
@ -7,10 +7,11 @@ import logging
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Callable, Sequence
|
||||
|
||||
from installer.state import InstallerState
|
||||
from knoe.state import InstallerState
|
||||
from knoe.core.env import _expand_cfg_value
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from installer.milestone import Milestone
|
||||
from knoe.milestone import Milestone
|
||||
|
||||
|
||||
class ProleController:
|
||||
@ -61,15 +62,26 @@ class ProleController:
|
||||
if args is None:
|
||||
args = []
|
||||
|
||||
base_env = os.environ.copy()
|
||||
if env:
|
||||
try:
|
||||
base_env.update(env)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Get paths
|
||||
source_script = self.project_root / "etc" / script_name
|
||||
|
||||
# Determine target etc directory
|
||||
prole_home_val = (env or os.environ).get("PROLE_HOME")
|
||||
prole_home_val = base_env.get("PROLE_HOME")
|
||||
if not prole_home_val:
|
||||
prole_home = Path.home() / ".prole"
|
||||
else:
|
||||
prole_home = Path(prole_home_val).expanduser()
|
||||
expanded_home = _expand_cfg_value(
|
||||
str(prole_home_val), env=base_env, max_depth=10
|
||||
)
|
||||
expanded_home = os.path.expanduser(expanded_home)
|
||||
prole_home = Path(expanded_home)
|
||||
|
||||
target_etc = prole_home / "etc"
|
||||
target_etc.mkdir(parents=True, exist_ok=True)
|
||||
@ -178,7 +190,24 @@ class ProleController:
|
||||
self.logger.debug(f"Starting subprocess: {' '.join(cmd)}")
|
||||
|
||||
# Ensure PYTHONUNBUFFERED=1 for any python scripts called within the bash script
|
||||
run_env = (env or os.environ).copy()
|
||||
run_env = base_env.copy()
|
||||
# Expand PROLE_* paths so subprocesses never see literal `$HOME`/`$PROLE_HOME`.
|
||||
try:
|
||||
run_env["PROLE_HOME"] = str(prole_home)
|
||||
for k in (
|
||||
"PROLE_CONF",
|
||||
"PROLE_DATA",
|
||||
"PROLE_LOGS",
|
||||
"PROLE_SERVICE",
|
||||
"KUBECONFIG",
|
||||
):
|
||||
raw = (run_env.get(k) or "").strip()
|
||||
if not raw or "$" not in raw:
|
||||
continue
|
||||
expanded = _expand_cfg_value(raw, env=run_env, max_depth=10)
|
||||
run_env[k] = os.path.expanduser(expanded)
|
||||
except Exception:
|
||||
pass
|
||||
self.logger.debug(
|
||||
f" cwd={os.getcwd()} KUBECONFIG={run_env.get('KUBECONFIG','<unset>')} NAMESPACE={run_env.get('NAMESPACE','<unset>')} PROLE_MODE={run_env.get('PROLE_MODE','<unset>')}"
|
||||
)
|
||||
@ -23,9 +23,9 @@ from pathlib import Path
|
||||
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
|
||||
from installer import config as inst_config
|
||||
from installer import prole_conf
|
||||
from installer.core.policy import OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES, POLICY_CFG_KEY
|
||||
from knoe import config as inst_config
|
||||
from knoe import prole_conf
|
||||
from knoe.core.policy import OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES, POLICY_CFG_KEY
|
||||
|
||||
from typing import Any
|
||||
|
||||
@ -84,7 +84,7 @@ OPENBAO_PREFIX = "${OPENBAO:"
|
||||
OPENBAO_SUFFIX = "}"
|
||||
PROLE_SECRET_VERSION = "v1"
|
||||
PROLE_SECRET_SERVICE = "prole-installer"
|
||||
PROLE_SECRET_KEY_FILE = Path.home() / ".prole" / "secrets" / "installer.key"
|
||||
PROLE_SECRET_KEY_FILE = Path.home() / ".prole" / "secrets" / "knoe.key"
|
||||
|
||||
# Map config keys to OpenBao paths (namespace injected at runtime)
|
||||
SECRET_KEY_SPECS = {
|
||||
@ -7,19 +7,19 @@ import time
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from installer.milestone import Milestone
|
||||
from installer import config as inst_config
|
||||
from installer import prole_conf
|
||||
from installer.core.stream_exec import run_streaming_cmd
|
||||
from installer.core.policy import (
|
||||
from knoe.milestone import Milestone
|
||||
from knoe import config as inst_config
|
||||
from knoe import prole_conf
|
||||
from knoe.core.stream_exec import run_streaming_cmd
|
||||
from knoe.core.policy import (
|
||||
POLICY_CFG_KEY,
|
||||
OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES,
|
||||
evaluate_optional_workloads_allowed,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from installer.state import InstallerState
|
||||
from installer.milestone import ProgressCallback
|
||||
from knoe.state import InstallerState
|
||||
from knoe.milestone import ProgressCallback
|
||||
|
||||
|
||||
def _stream_line(line: str) -> None:
|
||||
@ -376,10 +376,10 @@ class DockerBuildMilestone(Milestone):
|
||||
return
|
||||
|
||||
# In k3s/service installs, `init_cloudnative_pg.sh` already has a robust
|
||||
# pre-flight that ensures the `prole-db` image is present/pushed (and can
|
||||
# pre-flight that ensures the `knoe-db` image is present/pushed (and can
|
||||
# build it if missing). Building a multi-platform image here is expensive
|
||||
# and redundant when initialization scripts are enabled.
|
||||
from installer.core.env import _normalize_cluster_env
|
||||
from knoe.core.env import _normalize_cluster_env
|
||||
|
||||
env_key = (
|
||||
_normalize_cluster_env(state.inputs.get("init_cluster.cluster_env", "dev"))
|
||||
@ -409,7 +409,7 @@ class DockerBuildMilestone(Milestone):
|
||||
server = server.split("://")[1]
|
||||
if ":" in server:
|
||||
server = server.split(":")[0]
|
||||
args = ["--tag", f"{server}:5000/prole-db:{tag}", "--push"]
|
||||
args = ["--tag", f"{server}:5000/knoe-db:{tag}", "--push"]
|
||||
|
||||
rc = state.controller.run_script("build_db.sh", args=args, on_line=_stream_line)
|
||||
if rc != 0:
|
||||
@ -427,7 +427,7 @@ class ClusterLifecycleMilestone(Milestone):
|
||||
def execute(
|
||||
self, state: InstallerState, progress: ProgressCallback | None = None
|
||||
) -> None:
|
||||
from installer.core.env import _normalize_cluster_env
|
||||
from knoe.core.env import _normalize_cluster_env
|
||||
|
||||
cluster_env = state.inputs.get("init_cluster.cluster_env", "dev")
|
||||
env_key = _normalize_cluster_env(cluster_env) or cluster_env
|
||||
@ -465,7 +465,7 @@ class ClusterLifecycleMilestone(Milestone):
|
||||
)
|
||||
res_stdout = res.stdout or ""
|
||||
if cluster_name not in res_stdout:
|
||||
from installer.core.env import _k3d_prole_data_volume_args
|
||||
from knoe.core.env import _k3d_prole_data_volume_args
|
||||
|
||||
prole_data = state.inputs.get(
|
||||
"env_setup.PROLE_DATA",
|
||||
@ -495,9 +495,9 @@ class ClusterLifecycleMilestone(Milestone):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Ensure prole-db image is available in the cluster (avoid registry pull issues).
|
||||
# Ensure knoe-db image is available in the cluster (avoid registry pull issues).
|
||||
tag = state.controller.get_prole_db_version()
|
||||
local_image = f"prole-db:{tag}"
|
||||
local_image = f"knoe-db:{tag}"
|
||||
if (
|
||||
subprocess.run(
|
||||
["docker", "image", "inspect", local_image], capture_output=True
|
||||
@ -512,7 +512,7 @@ class ClusterLifecycleMilestone(Milestone):
|
||||
registry = registry.replace(".localhost", "")
|
||||
if not registry:
|
||||
registry = "k3d-prole-registry:5000"
|
||||
remote_tag = f"{registry}/prole-db:{tag}"
|
||||
remote_tag = f"{registry}/knoe-db:{tag}"
|
||||
subprocess.run(
|
||||
["docker", "tag", local_image, remote_tag], capture_output=True
|
||||
)
|
||||
@ -932,7 +932,7 @@ class KerberosMilestone(Milestone):
|
||||
|
||||
|
||||
def _resolve_supabase_deploy_mode(state: InstallerState) -> str:
|
||||
from installer.core.env import _normalize_cluster_env
|
||||
from knoe.core.env import _normalize_cluster_env
|
||||
|
||||
deploy_mode = (
|
||||
os.environ.get("SUPABASE_DEPLOY_MODE") or os.environ.get("SUPABASE_MODE") or ""
|
||||
@ -980,7 +980,7 @@ class SupabaseImagePreloadMilestone(Milestone):
|
||||
progress(f"Supabase image preload skipped by policy: {opt_reason}", 1.0)
|
||||
return
|
||||
|
||||
from installer.core.env import _parse_bool
|
||||
from knoe.core.env import _parse_bool
|
||||
|
||||
env = self._get_script_env(state)
|
||||
script_path = state.controller.project_root / "supabase" / "deploy.sh"
|
||||
@ -1061,7 +1061,7 @@ class SupabaseMilestone(Milestone):
|
||||
progress(f"Supabase skipped by policy: {opt_reason}", 1.0)
|
||||
return
|
||||
|
||||
from installer.core.env import _parse_bool
|
||||
from knoe.core.env import _parse_bool
|
||||
|
||||
env = self._get_script_env(state)
|
||||
script_path = state.controller.project_root / "supabase" / "deploy.sh"
|
||||
@ -9,9 +9,9 @@ import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from installer.config import _expand_path, _collect_cfg_vars, _expand_cfg_value
|
||||
from installer import prole_conf
|
||||
from installer.core.env import (
|
||||
from knoe.config import _expand_path, _collect_cfg_vars, _expand_cfg_value
|
||||
from knoe import prole_conf
|
||||
from knoe.core.env import (
|
||||
_build_required_port_forwards,
|
||||
_pf_extract_id,
|
||||
_pf_mapping_str,
|
||||
@ -15,7 +15,7 @@ import threading
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
from installer.core.env import DEFAULT_OLLAMA_PORT
|
||||
from knoe.core.env import DEFAULT_OLLAMA_PORT
|
||||
|
||||
|
||||
def _is_ip(name: str) -> bool:
|
||||
@ -5,9 +5,9 @@ import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from installer import prole_conf
|
||||
from installer.core.controller import ProleController
|
||||
from installer.core.actions import ProleConsoleInstaller
|
||||
from knoe import prole_conf
|
||||
from knoe.core.controller import ProleController
|
||||
from knoe.core.actions import ProleConsoleInstaller
|
||||
|
||||
|
||||
def _project_root() -> Path:
|
||||
@ -84,42 +84,42 @@ def main(argv: list[str] | None = None) -> int:
|
||||
installer = ProleConsoleInstaller(controller, cfg_path=cfg)
|
||||
|
||||
try:
|
||||
existing_inputs = installer._load_inputs_from_cfg()
|
||||
existing_inputs = knoe._load_inputs_from_cfg()
|
||||
except Exception:
|
||||
existing_inputs = {}
|
||||
installer.inputs = {**installer._default_inputs(), **existing_inputs}
|
||||
knoe.inputs = {**knoe._default_inputs(), **existing_inputs}
|
||||
|
||||
# Ensure mode influences the env we build (KUBECONFIG resolution, etc.).
|
||||
if mode == "k3s":
|
||||
installer.inputs["init_cluster.cluster_env"] = "service"
|
||||
knoe.inputs["init_cluster.cluster_env"] = "service"
|
||||
elif mode == "k3d":
|
||||
installer.inputs["init_cluster.cluster_env"] = "dev"
|
||||
knoe.inputs["init_cluster.cluster_env"] = "dev"
|
||||
elif mode == "k8s":
|
||||
installer.inputs["init_cluster.cluster_env"] = "prod"
|
||||
knoe.inputs["init_cluster.cluster_env"] = "prod"
|
||||
|
||||
service_ns = (args.service_namespace or "").strip() or installer._service_namespace()
|
||||
service_ns = (args.service_namespace or "").strip() or knoe._service_namespace()
|
||||
db_ns = (
|
||||
(args.db_namespace or "").strip()
|
||||
or (installer._get_input("init_password.db_namespace", "") or "").strip()
|
||||
or (knoe._get_input("init_password.db_namespace", "") or "").strip()
|
||||
or service_ns
|
||||
or "default"
|
||||
)
|
||||
if args.db_namespace:
|
||||
installer.inputs["init_password.db_namespace"] = db_ns
|
||||
knoe.inputs["init_password.db_namespace"] = db_ns
|
||||
|
||||
env = installer._script_env_for_namespace(service_ns)
|
||||
env = knoe._script_env_for_namespace(service_ns)
|
||||
env["SERVICE_NAMESPACE"] = service_ns
|
||||
env["DB_NAMESPACE"] = db_ns
|
||||
env["NAMESPACE"] = service_ns
|
||||
|
||||
anomalies: list[str] = []
|
||||
if installer._dashboard_kong_missing(env):
|
||||
if knoe._dashboard_kong_missing(env):
|
||||
anomalies.append("dashboard")
|
||||
if installer._authority_context_missing():
|
||||
if knoe._authority_context_missing():
|
||||
anomalies.append("authority")
|
||||
|
||||
installer._repair_stale_released_pvs(env)
|
||||
installer._run_cluster_repair_pipeline(env=env, anomalies=anomalies)
|
||||
knoe._repair_stale_released_pvs(env)
|
||||
knoe._run_cluster_repair_pipeline(env=env, anomalies=anomalies)
|
||||
return 0
|
||||
|
||||
|
||||
@ -180,7 +180,7 @@ def _create_deploy_row(app, parent, step: dict):
|
||||
|
||||
|
||||
def create_deploy_page(app):
|
||||
"""Create the Deploy page UI and register it via installer.main.
|
||||
"""Create the Deploy page UI and register it via knoe.main.
|
||||
|
||||
Final milestone flow for Deploy page:
|
||||
- Show completed steps from earlier phases
|
||||
@ -29,12 +29,12 @@ def clear_canvas_page(app):
|
||||
|
||||
|
||||
def create_page_build(app):
|
||||
# Delegates build page UI creation to installer.build
|
||||
# Delegates build page UI creation to knoe.build
|
||||
return build_mod.create_build_page(app)
|
||||
|
||||
|
||||
def create_page_deploy(app):
|
||||
# Delegates deploy page UI creation to installer.deploy
|
||||
# Delegates deploy page UI creation to knoe.deploy
|
||||
return deploy_mod.create_deploy_page(app)
|
||||
|
||||
|
||||
@ -110,7 +110,7 @@ class Milestone(ABC):
|
||||
service_ns = env["NAMESPACE"]
|
||||
env["SERVICE_NAMESPACE"] = service_ns
|
||||
|
||||
from installer.core.env import _deployment_mode_from_env
|
||||
from knoe.core.env import _deployment_mode_from_env
|
||||
|
||||
cluster_env = state.inputs.get("init_cluster.cluster_env", "dev")
|
||||
mode = _deployment_mode_from_env(cluster_env) or "k3d"
|
||||
@ -970,7 +970,7 @@ def run_ncurses_installer(controller):
|
||||
|
||||
def _main(stdscr):
|
||||
installer = ProleNcursesInstaller(stdscr, controller)
|
||||
installer.run()
|
||||
knoe.run()
|
||||
|
||||
try:
|
||||
curses.wrapper(_main)
|
||||
7
knoe/ops/__init__.py
Normal file
7
knoe/ops/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .context import KnoeContext
|
||||
|
||||
__all__ = [
|
||||
"KnoeContext",
|
||||
]
|
||||
52
knoe/ops/cli.py
Normal file
52
knoe/ops/cli.py
Normal file
@ -0,0 +1,52 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from .context import KnoeContext
|
||||
|
||||
|
||||
def _project_root() -> Path:
|
||||
# <root>/knoe/ops/cli.py -> parents: ops, knoe, root
|
||||
return Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
p = argparse.ArgumentParser(prog="knoe-ops", add_help=True)
|
||||
p.add_argument("component", help="component name (e.g. common_core, openbao)")
|
||||
p.add_argument(
|
||||
"action",
|
||||
help="action to run (initialize, start, update, restart, stop, status)",
|
||||
)
|
||||
p.add_argument("--mode", default=os.environ.get("PROLE_MODE", "dev"))
|
||||
p.add_argument("--namespace", default=os.environ.get("PROLE_NAMESPACE", "default"))
|
||||
p.add_argument(
|
||||
"--service-namespace",
|
||||
default=os.environ.get("PROLE_SERVICE_NAMESPACE", "default"),
|
||||
)
|
||||
p.add_argument("--config", dest="config", default=os.environ.get("PROLE_CFG"))
|
||||
args = p.parse_args(argv)
|
||||
|
||||
cfg_path = Path(args.config).expanduser().resolve() if args.config else None
|
||||
ctx = KnoeContext(
|
||||
project_root=_project_root(),
|
||||
cfg_path=cfg_path,
|
||||
mode=args.mode,
|
||||
namespace=args.namespace,
|
||||
service_namespace=args.service_namespace,
|
||||
env=dict(os.environ),
|
||||
logger=lambda m: print(m),
|
||||
err_logger=lambda m: print(m, file=sys.stderr),
|
||||
)
|
||||
|
||||
mod = importlib.import_module(f"knoe.ops.components.{args.component}")
|
||||
fn = getattr(mod, args.action)
|
||||
rc = fn(ctx)
|
||||
return 0 if rc is None else int(rc)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
raise SystemExit(main())
|
||||
10
knoe/ops/components/__init__.py
Normal file
10
knoe/ops/components/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = [
|
||||
"argocd",
|
||||
"common_core",
|
||||
"garage_store",
|
||||
"openbao",
|
||||
"opentofu",
|
||||
"registry",
|
||||
]
|
||||
31
knoe/ops/components/argocd.py
Normal file
31
knoe/ops/components/argocd.py
Normal file
@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ..context import KnoeContext
|
||||
from ..legacy_shell import run_script
|
||||
|
||||
|
||||
_SCRIPT = "init_argocd.sh"
|
||||
|
||||
|
||||
def initialize(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "initialize")
|
||||
|
||||
|
||||
def start(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "start")
|
||||
|
||||
|
||||
def update(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "update")
|
||||
|
||||
|
||||
def restart(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "restart")
|
||||
|
||||
|
||||
def stop(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "stop")
|
||||
|
||||
|
||||
def status(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "status")
|
||||
31
knoe/ops/components/common_core.py
Normal file
31
knoe/ops/components/common_core.py
Normal file
@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ..context import KnoeContext
|
||||
from ..legacy_shell import run_script
|
||||
|
||||
|
||||
_SCRIPT = "init_common_core.sh"
|
||||
|
||||
|
||||
def initialize(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "initialize")
|
||||
|
||||
|
||||
def start(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "start")
|
||||
|
||||
|
||||
def update(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "update")
|
||||
|
||||
|
||||
def restart(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "restart")
|
||||
|
||||
|
||||
def stop(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "stop")
|
||||
|
||||
|
||||
def status(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "status")
|
||||
31
knoe/ops/components/garage_store.py
Normal file
31
knoe/ops/components/garage_store.py
Normal file
@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ..context import KnoeContext
|
||||
from ..legacy_shell import run_script
|
||||
|
||||
|
||||
_SCRIPT = "init_garage_store.sh"
|
||||
|
||||
|
||||
def initialize(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "initialize")
|
||||
|
||||
|
||||
def start(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "start")
|
||||
|
||||
|
||||
def update(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "update")
|
||||
|
||||
|
||||
def restart(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "restart")
|
||||
|
||||
|
||||
def stop(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "stop")
|
||||
|
||||
|
||||
def status(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "status")
|
||||
31
knoe/ops/components/openbao.py
Normal file
31
knoe/ops/components/openbao.py
Normal file
@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ..context import KnoeContext
|
||||
from ..legacy_shell import run_script
|
||||
|
||||
|
||||
_SCRIPT = "init_openbao.sh"
|
||||
|
||||
|
||||
def initialize(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "initialize")
|
||||
|
||||
|
||||
def start(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "start")
|
||||
|
||||
|
||||
def update(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "update")
|
||||
|
||||
|
||||
def restart(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "restart")
|
||||
|
||||
|
||||
def stop(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "stop")
|
||||
|
||||
|
||||
def status(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "status")
|
||||
31
knoe/ops/components/opentofu.py
Normal file
31
knoe/ops/components/opentofu.py
Normal file
@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ..context import KnoeContext
|
||||
from ..legacy_shell import run_script
|
||||
|
||||
|
||||
_SCRIPT = "init_opentofu.sh"
|
||||
|
||||
|
||||
def initialize(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "initialize")
|
||||
|
||||
|
||||
def start(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "start")
|
||||
|
||||
|
||||
def update(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "update")
|
||||
|
||||
|
||||
def restart(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "restart")
|
||||
|
||||
|
||||
def stop(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "stop")
|
||||
|
||||
|
||||
def status(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "status")
|
||||
31
knoe/ops/components/registry.py
Normal file
31
knoe/ops/components/registry.py
Normal file
@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ..context import KnoeContext
|
||||
from ..legacy_shell import run_script
|
||||
|
||||
|
||||
_SCRIPT = "init_registry.sh"
|
||||
|
||||
|
||||
def initialize(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "initialize")
|
||||
|
||||
|
||||
def start(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "start")
|
||||
|
||||
|
||||
def update(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "update")
|
||||
|
||||
|
||||
def restart(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "restart")
|
||||
|
||||
|
||||
def stop(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "stop")
|
||||
|
||||
|
||||
def status(ctx: KnoeContext):
|
||||
return run_script(ctx, _SCRIPT, "status")
|
||||
29
knoe/ops/context.py
Normal file
29
knoe/ops/context.py
Normal file
@ -0,0 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
|
||||
def _default_logger(msg: str) -> None:
|
||||
print(msg)
|
||||
|
||||
|
||||
def _default_err_logger(msg: str) -> None:
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class KnoeContext:
|
||||
"""Small shared context object for Stage 1 ops modules."""
|
||||
|
||||
project_root: Path
|
||||
cfg_path: Path | None = None
|
||||
mode: str = "dev"
|
||||
namespace: str = "default"
|
||||
service_namespace: str = "default"
|
||||
env: dict[str, str] = field(default_factory=lambda: dict(os.environ))
|
||||
logger: Callable[[str], None] = _default_logger
|
||||
err_logger: Callable[[str], None] = _default_err_logger
|
||||
49
knoe/ops/files.py
Normal file
49
knoe/ops/files.py
Normal file
@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def ensure_dir(path: Path) -> None:
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def write_if_changed(path: Path, text: str, *, encoding: str = "utf-8") -> bool:
|
||||
existing = None
|
||||
try:
|
||||
existing = path.read_text(encoding=encoding)
|
||||
except Exception:
|
||||
existing = None
|
||||
|
||||
if existing == text:
|
||||
return False
|
||||
|
||||
ensure_dir(path.parent)
|
||||
path.write_text(text, encoding=encoding)
|
||||
return True
|
||||
|
||||
|
||||
def copy_if_changed(src: Path, dst: Path) -> bool:
|
||||
try:
|
||||
if dst.exists() and src.read_bytes() == dst.read_bytes():
|
||||
return False
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
ensure_dir(dst.parent)
|
||||
shutil.copy2(src, dst)
|
||||
return True
|
||||
|
||||
|
||||
def chmod_if_needed(path: Path, mode: int) -> bool:
|
||||
try:
|
||||
current = path.stat().st_mode & 0o777
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
if current == mode:
|
||||
return False
|
||||
|
||||
os.chmod(path, mode)
|
||||
return True
|
||||
49
knoe/ops/helm.py
Normal file
49
knoe/ops/helm.py
Normal file
@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Mapping
|
||||
|
||||
from .context import KnoeContext
|
||||
from .process import check
|
||||
|
||||
|
||||
def _env(ctx: KnoeContext, extra_env: Mapping[str, str] | None = None) -> dict[str, str]:
|
||||
e = dict(os.environ)
|
||||
e.update(ctx.env)
|
||||
if extra_env:
|
||||
e.update(dict(extra_env))
|
||||
return e
|
||||
|
||||
|
||||
def repo_add(ctx: KnoeContext, name: str, url: str) -> None:
|
||||
check(["helm", "repo", "add", name, url], env=_env(ctx))
|
||||
|
||||
|
||||
def repo_update(ctx: KnoeContext) -> None:
|
||||
check(["helm", "repo", "update"], env=_env(ctx))
|
||||
|
||||
|
||||
def upgrade_install(
|
||||
ctx: KnoeContext,
|
||||
release: str,
|
||||
chart: str,
|
||||
*,
|
||||
namespace: str | None = None,
|
||||
values_file: str | None = None,
|
||||
extra_args: list[str] | None = None,
|
||||
) -> None:
|
||||
cmd: list[str] = ["helm", "upgrade", "--install", release, chart]
|
||||
if namespace:
|
||||
cmd += ["--namespace", namespace]
|
||||
if values_file:
|
||||
cmd += ["-f", values_file]
|
||||
if extra_args:
|
||||
cmd += list(extra_args)
|
||||
check(cmd, env=_env(ctx))
|
||||
|
||||
|
||||
def uninstall(ctx: KnoeContext, release: str, *, namespace: str | None = None) -> None:
|
||||
cmd: list[str] = ["helm", "uninstall", release]
|
||||
if namespace:
|
||||
cmd += ["--namespace", namespace]
|
||||
check(cmd, env=_env(ctx))
|
||||
95
knoe/ops/k8s.py
Normal file
95
knoe/ops/k8s.py
Normal file
@ -0,0 +1,95 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Any, Mapping, Sequence
|
||||
|
||||
from .context import KnoeContext
|
||||
from .process import CommandFailed, check
|
||||
|
||||
|
||||
def _env(ctx: KnoeContext, extra_env: Mapping[str, str] | None = None) -> dict[str, str]:
|
||||
e = dict(os.environ)
|
||||
e.update(ctx.env)
|
||||
if extra_env:
|
||||
e.update(dict(extra_env))
|
||||
return e
|
||||
|
||||
|
||||
def kubectl(
|
||||
ctx: KnoeContext,
|
||||
*args: str,
|
||||
namespace: str | None = None,
|
||||
input_text: str | None = None,
|
||||
extra_env: Mapping[str, str] | None = None,
|
||||
) -> str:
|
||||
cmd: list[str] = ["kubectl", *args]
|
||||
if namespace and ("-n" not in args and "--namespace" not in args):
|
||||
cmd += ["-n", namespace]
|
||||
proc = check(cmd, env=_env(ctx, extra_env), input_text=input_text)
|
||||
return proc.stdout
|
||||
|
||||
|
||||
def kubectl_json(ctx: KnoeContext, *args: str, namespace: str | None = None) -> Any:
|
||||
out = kubectl(ctx, *args, "-o", "json", namespace=namespace)
|
||||
return json.loads(out or "{}")
|
||||
|
||||
|
||||
def namespace_exists(ctx: KnoeContext, namespace: str) -> bool:
|
||||
try:
|
||||
kubectl(ctx, "get", "namespace", namespace)
|
||||
return True
|
||||
except CommandFailed:
|
||||
return False
|
||||
|
||||
|
||||
def ensure_namespace(ctx: KnoeContext, namespace: str) -> None:
|
||||
if namespace_exists(ctx, namespace):
|
||||
return
|
||||
kubectl(ctx, "create", "namespace", namespace)
|
||||
|
||||
|
||||
def apply_yaml(ctx: KnoeContext, yaml_text: str, namespace: str | None = None) -> None:
|
||||
kubectl(
|
||||
ctx,
|
||||
"apply",
|
||||
"-f",
|
||||
"-",
|
||||
namespace=namespace,
|
||||
input_text=yaml_text,
|
||||
)
|
||||
|
||||
|
||||
def get_resource_json(
|
||||
ctx: KnoeContext,
|
||||
resource: str,
|
||||
name: str | None = None,
|
||||
*,
|
||||
namespace: str | None = None,
|
||||
) -> Any:
|
||||
args: list[str] = ["get", resource]
|
||||
if name:
|
||||
args.append(name)
|
||||
return kubectl_json(ctx, *args, namespace=namespace)
|
||||
|
||||
|
||||
def patch_resource(
|
||||
ctx: KnoeContext,
|
||||
resource: str,
|
||||
name: str,
|
||||
patch_json: str,
|
||||
*,
|
||||
namespace: str | None = None,
|
||||
patch_type: str = "merge",
|
||||
) -> None:
|
||||
kubectl(
|
||||
ctx,
|
||||
"patch",
|
||||
resource,
|
||||
name,
|
||||
"--type",
|
||||
patch_type,
|
||||
"-p",
|
||||
patch_json,
|
||||
namespace=namespace,
|
||||
)
|
||||
51
knoe/ops/legacy_shell.py
Normal file
51
knoe/ops/legacy_shell.py
Normal file
@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Callable, Mapping, Sequence
|
||||
|
||||
from .context import KnoeContext
|
||||
from .process import check, run_streaming
|
||||
|
||||
|
||||
def _env(ctx: KnoeContext, extra_env: Mapping[str, str] | None = None) -> dict[str, str]:
|
||||
e = dict(os.environ)
|
||||
e.update(ctx.env)
|
||||
|
||||
# Bridge common knobs for legacy scripts.
|
||||
e.setdefault("PROLE_MODE", ctx.mode)
|
||||
e.setdefault("PROLE_NAMESPACE", ctx.namespace)
|
||||
e.setdefault("PROLE_SERVICE_NAMESPACE", ctx.service_namespace)
|
||||
if ctx.cfg_path is not None:
|
||||
e.setdefault("PROLE_CFG", str(ctx.cfg_path))
|
||||
|
||||
if extra_env:
|
||||
e.update(dict(extra_env))
|
||||
return e
|
||||
|
||||
|
||||
def run_script(
|
||||
ctx: KnoeContext,
|
||||
script_name: str,
|
||||
action: str,
|
||||
*,
|
||||
extra_args: Sequence[str] | None = None,
|
||||
stdin_text: str | None = None,
|
||||
on_line: Callable[[str], None] | None = None,
|
||||
extra_env: Mapping[str, str] | None = None,
|
||||
streaming: bool = True,
|
||||
) -> int:
|
||||
if not script_name.endswith(".sh"):
|
||||
script_name = f"{script_name}.sh"
|
||||
|
||||
script_path = (ctx.project_root / "etc" / script_name).resolve()
|
||||
cmd: list[str] = ["bash", str(script_path), action]
|
||||
if extra_args:
|
||||
cmd += list(extra_args)
|
||||
|
||||
env = _env(ctx, extra_env)
|
||||
if streaming:
|
||||
return run_streaming(cmd, cwd=ctx.project_root, env=env, stdin_text=stdin_text, on_line=on_line)
|
||||
|
||||
check(cmd, cwd=ctx.project_root, env=env, input_text=stdin_text)
|
||||
return 0
|
||||
93
knoe/ops/process.py
Normal file
93
knoe/ops/process.py
Normal file
@ -0,0 +1,93 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Callable, Mapping, Sequence
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class CommandFailed(RuntimeError):
|
||||
cmd: Sequence[str]
|
||||
returncode: int
|
||||
stdout: str
|
||||
stderr: str
|
||||
|
||||
def __str__(self) -> str: # pragma: no cover
|
||||
cmd_str = " ".join(self.cmd)
|
||||
return f"Command failed ({self.returncode}): {cmd_str}"
|
||||
|
||||
|
||||
def run(
|
||||
cmd: Sequence[str],
|
||||
*,
|
||||
cwd: Path | None = None,
|
||||
env: Mapping[str, str] | None = None,
|
||||
input_text: str | None = None,
|
||||
timeout: float | None = None,
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
list(cmd),
|
||||
cwd=str(cwd) if cwd is not None else None,
|
||||
env=dict(env) if env is not None else None,
|
||||
input=input_text,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
|
||||
def check(
|
||||
cmd: Sequence[str],
|
||||
*,
|
||||
cwd: Path | None = None,
|
||||
env: Mapping[str, str] | None = None,
|
||||
input_text: str | None = None,
|
||||
timeout: float | None = None,
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
proc = run(cmd, cwd=cwd, env=env, input_text=input_text, timeout=timeout)
|
||||
if proc.returncode != 0:
|
||||
raise CommandFailed(cmd=cmd, returncode=proc.returncode, stdout=proc.stdout, stderr=proc.stderr)
|
||||
return proc
|
||||
|
||||
|
||||
def run_streaming(
|
||||
cmd: Sequence[str],
|
||||
*,
|
||||
cwd: Path | None = None,
|
||||
env: Mapping[str, str] | None = None,
|
||||
stdin_text: str | None = None,
|
||||
on_line: Callable[[str], None] | None = None,
|
||||
) -> int:
|
||||
"""Run a subprocess and stream stdout/stderr lines to `on_line`.
|
||||
|
||||
Returns the process return code.
|
||||
"""
|
||||
|
||||
p = subprocess.Popen(
|
||||
list(cmd),
|
||||
cwd=str(cwd) if cwd is not None else None,
|
||||
env=dict(env) if env is not None else None,
|
||||
stdin=subprocess.PIPE if stdin_text is not None else None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
)
|
||||
|
||||
try:
|
||||
if stdin_text is not None and p.stdin is not None:
|
||||
p.stdin.write(stdin_text)
|
||||
p.stdin.close()
|
||||
|
||||
if p.stdout is not None:
|
||||
for line in p.stdout:
|
||||
if on_line is not None:
|
||||
on_line(line.rstrip("\n"))
|
||||
return p.wait()
|
||||
finally:
|
||||
try:
|
||||
if p.stdout is not None:
|
||||
p.stdout.close()
|
||||
except Exception:
|
||||
pass
|
||||
52
knoe/ops/secrets.py
Normal file
52
knoe/ops/secrets.py
Normal file
@ -0,0 +1,52 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from knoe import config as inst_config
|
||||
|
||||
from .context import KnoeContext
|
||||
from .k8s import apply_yaml
|
||||
|
||||
|
||||
def resolve_secret_value(value: str | None) -> str:
|
||||
if not value:
|
||||
return ""
|
||||
return inst_config._resolve_secret_value(value)
|
||||
|
||||
|
||||
def ensure_db_k8s_secrets(
|
||||
ctx: KnoeContext,
|
||||
*,
|
||||
namespace: str,
|
||||
name: str = "knoe-db",
|
||||
username: str | None = None,
|
||||
password: str | None = None,
|
||||
extra: dict[str, str] | None = None,
|
||||
) -> None:
|
||||
"""Ensure a Secret exists containing DB credentials.
|
||||
|
||||
Stage 1 implementation is intentionally minimal and uses `kubectl apply`.
|
||||
"""
|
||||
|
||||
data: dict[str, Any] = {}
|
||||
if username is not None:
|
||||
data["username"] = resolve_secret_value(username)
|
||||
if password is not None:
|
||||
data["password"] = resolve_secret_value(password)
|
||||
if extra:
|
||||
for k, v in extra.items():
|
||||
data[k] = resolve_secret_value(v)
|
||||
|
||||
# We keep this as stringData for readability; kubectl will base64-encode.
|
||||
yaml_lines = [
|
||||
"apiVersion: v1",
|
||||
"kind: Secret",
|
||||
"metadata:",
|
||||
f" name: {name}",
|
||||
f" namespace: {namespace}",
|
||||
"type: Opaque",
|
||||
"stringData:",
|
||||
]
|
||||
for k, v in data.items():
|
||||
yaml_lines.append(f" {k}: {v}")
|
||||
apply_yaml(ctx, "\n".join(yaml_lines) + "\n", namespace=namespace)
|
||||
15
knoe/ops/status.py
Normal file
15
knoe/ops/status.py
Normal file
@ -0,0 +1,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from typing import Callable
|
||||
|
||||
|
||||
def wait_for(predicate: Callable[[], bool], *, timeout_s: float = 60.0, poll_s: float = 1.0) -> bool:
|
||||
"""Minimal shared readiness helper."""
|
||||
|
||||
end = time.time() + timeout_s
|
||||
while time.time() < end:
|
||||
if predicate():
|
||||
return True
|
||||
time.sleep(poll_s)
|
||||
return False
|
||||
@ -193,16 +193,16 @@ def _default_base_cfg_text(env: str) -> str:
|
||||
env = "dev"
|
||||
|
||||
# Keep defaults conservative and compatible with prior single-file defaults:
|
||||
# - non-test uses `prole-db` to avoid surprising namespace drift
|
||||
# - non-test uses a stable DB namespace default
|
||||
# - test is isolated
|
||||
if env == "test":
|
||||
ns = "prole-test"
|
||||
sns = "prole-test"
|
||||
elif env == "dev":
|
||||
ns = "prole-db"
|
||||
ns = "knoe-db"
|
||||
sns = "default"
|
||||
else:
|
||||
ns = "prole-db"
|
||||
ns = "knoe-db"
|
||||
sns = "knoe-system"
|
||||
|
||||
return (
|
||||
@ -1,6 +1,6 @@
|
||||
"""Backward-compatibility shim for legacy_tk imports."""
|
||||
|
||||
from installer.ui import screens as _screens
|
||||
from knoe.ui import screens as _screens
|
||||
|
||||
globals().update({k: v for k, v in _screens.__dict__.items() if not k.startswith("__")})
|
||||
__all__ = [name for name in globals() if not name.startswith("__")]
|
||||
@ -18,11 +18,11 @@ import tkinter as tk
|
||||
import uuid
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
|
||||
from installer import deploy as inst_deploy
|
||||
from installer import prole_conf
|
||||
from installer import screen as ui
|
||||
from installer.build import get_build_command as inst_get_build_command
|
||||
from installer.config import (
|
||||
from knoe import deploy as inst_deploy
|
||||
from knoe import prole_conf
|
||||
from knoe import screen as ui
|
||||
from knoe.build import get_build_command as inst_get_build_command
|
||||
from knoe.config import (
|
||||
_expand_cfg_value,
|
||||
_collect_cfg_vars,
|
||||
_is_prole_secret,
|
||||
@ -32,8 +32,8 @@ from installer.config import (
|
||||
_encrypt_cfg_secret,
|
||||
_encrypt_prole_secret,
|
||||
)
|
||||
from installer.core.controller import ProleController
|
||||
from installer.core.env import (
|
||||
from knoe.core.controller import ProleController
|
||||
from knoe.core.env import (
|
||||
_detect_ansible_topology,
|
||||
_format_ansible_topology_summary,
|
||||
_normalize_k3s_token,
|
||||
@ -68,8 +68,8 @@ if platform.system() == "Darwin":
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from installer.core.env import * # noqa: F401,F403
|
||||
from installer.core.actions import (
|
||||
from knoe.core.env import * # noqa: F401,F403
|
||||
from knoe.core.actions import (
|
||||
ProleInstaller,
|
||||
ProleConsoleInstaller,
|
||||
_prepare_k3s_pipeline,
|
||||
@ -82,27 +82,27 @@ GLOBAL_SCAN_FRAMES: list = []
|
||||
# ---------------------------------------------------------------------------
|
||||
# Mixin imports — one per screen / concern
|
||||
# ---------------------------------------------------------------------------
|
||||
from installer.ui.screens.base import ScreenBaseMixin
|
||||
from installer.ui.screens.navigation import NavigationMixin
|
||||
from installer.ui.screens.welcome import WelcomeScreenMixin
|
||||
from installer.ui.screens.dependencies import DependenciesScreenMixin
|
||||
from installer.ui.screens.network import NetworkScreenMixin
|
||||
from installer.ui.screens.environment import EnvironmentScreenMixin
|
||||
from installer.ui.screens.database import DatabaseScreenMixin
|
||||
from installer.ui.screens.database_options import DatabaseOptionsScreenMixin
|
||||
from installer.ui.screens.cluster import ClusterScreenMixin
|
||||
from installer.ui.screens.cluster_nodes import ClusterNodesScreenMixin
|
||||
from installer.ui.screens.services import ServicesScreenMixin
|
||||
from installer.ui.screens.security import SecurityScreenMixin
|
||||
from installer.ui.screens.supabase import SupabaseScreenMixin
|
||||
from installer.ui.screens.gitops import GitOpsScreenMixin
|
||||
from installer.ui.screens.argocd import ArgoCDScreenMixin
|
||||
from installer.ui.screens.docker import DockerScreenMixin
|
||||
from installer.ui.screens.build import BuildScreenMixin
|
||||
from installer.ui.screens.packaging import PackagingScreenMixin
|
||||
from installer.ui.screens.deploy import DeployScreenMixin
|
||||
from installer.ui.screens.validate import ValidateScreenMixin
|
||||
from installer.ui.screens.cfg import ConfigMixin
|
||||
from knoe.ui.screens.base import ScreenBaseMixin
|
||||
from knoe.ui.screens.navigation import NavigationMixin
|
||||
from knoe.ui.screens.welcome import WelcomeScreenMixin
|
||||
from knoe.ui.screens.dependencies import DependenciesScreenMixin
|
||||
from knoe.ui.screens.network import NetworkScreenMixin
|
||||
from knoe.ui.screens.environment import EnvironmentScreenMixin
|
||||
from knoe.ui.screens.database import DatabaseScreenMixin
|
||||
from knoe.ui.screens.database_options import DatabaseOptionsScreenMixin
|
||||
from knoe.ui.screens.cluster import ClusterScreenMixin
|
||||
from knoe.ui.screens.cluster_nodes import ClusterNodesScreenMixin
|
||||
from knoe.ui.screens.services import ServicesScreenMixin
|
||||
from knoe.ui.screens.security import SecurityScreenMixin
|
||||
from knoe.ui.screens.supabase import SupabaseScreenMixin
|
||||
from knoe.ui.screens.gitops import GitOpsScreenMixin
|
||||
from knoe.ui.screens.argocd import ArgoCDScreenMixin
|
||||
from knoe.ui.screens.docker import DockerScreenMixin
|
||||
from knoe.ui.screens.build import BuildScreenMixin
|
||||
from knoe.ui.screens.packaging import PackagingScreenMixin
|
||||
from knoe.ui.screens.deploy import DeployScreenMixin
|
||||
from knoe.ui.screens.validate import ValidateScreenMixin
|
||||
from knoe.ui.screens.cfg import ConfigMixin
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@ -651,7 +651,7 @@ class ProleInstaller(
|
||||
try:
|
||||
self.db_username = tk.StringVar(value=os.getlogin())
|
||||
except Exception:
|
||||
self.db_username = tk.StringVar(value="prole-db")
|
||||
self.db_username = tk.StringVar(value="knoe")
|
||||
self.db_password = tk.StringVar()
|
||||
self.db_password_confirm = tk.StringVar()
|
||||
self.namespace_owner = self._get_local_owner()
|
||||
@ -856,7 +856,7 @@ def main():
|
||||
else:
|
||||
log_file_path = args.log
|
||||
|
||||
from installer.config import setup_logging
|
||||
from knoe.config import setup_logging
|
||||
|
||||
setup_logging(verbose=args.verbose, debug=args.debug)
|
||||
|
||||
@ -867,7 +867,7 @@ def main():
|
||||
)
|
||||
|
||||
if args.command == "monitor":
|
||||
from installer.core.monitor import run_monitor
|
||||
from knoe.core.monitor import run_monitor
|
||||
|
||||
pf_cfg = args.command_args[0] if args.command_args else None
|
||||
sys.exit(run_monitor(controller, args.config, pf_cfg, verbose=args.verbose))
|
||||
@ -921,12 +921,12 @@ def main():
|
||||
print(f"Failed to start GUI: {e}", file=sys.stderr)
|
||||
print("Falling back to ncurses interface...", file=sys.stderr)
|
||||
time.sleep(1)
|
||||
from installer.ncurses_installer import run_ncurses_installer
|
||||
from knoe.ncurses_installer import run_ncurses_installer
|
||||
|
||||
run_ncurses_installer(controller)
|
||||
else:
|
||||
_ensure_ansible_vault_credentials(prompt_ui=False)
|
||||
from installer.ncurses_installer import run_ncurses_installer
|
||||
from knoe.ncurses_installer import run_ncurses_installer
|
||||
|
||||
run_ncurses_installer(controller)
|
||||
|
||||
@ -11,8 +11,8 @@ import subprocess
|
||||
import threading
|
||||
import tkinter as tk
|
||||
|
||||
from installer import screen as ui
|
||||
from installer.core.env import PROJECT_ROOT, _normalize_cluster_env
|
||||
from knoe import screen as ui
|
||||
from knoe.core.env import PROJECT_ROOT, _normalize_cluster_env
|
||||
|
||||
|
||||
class ArgoCDScreenMixin:
|
||||
@ -207,7 +207,7 @@ class ArgoCDScreenMixin:
|
||||
if candidate.exists():
|
||||
cfg_path = candidate
|
||||
if not cfg_path:
|
||||
from installer import prole_conf
|
||||
from knoe import prole_conf
|
||||
|
||||
candidate = prole_conf.entrypoint_path(
|
||||
prole_conf.resolve_prole_conf_dir(PROJECT_ROOT)
|
||||
@ -5,8 +5,8 @@ import subprocess
|
||||
from datetime import datetime
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
from installer.screen import TerminalConsole
|
||||
from installer import screen as ui
|
||||
from knoe.screen import TerminalConsole
|
||||
from knoe import screen as ui
|
||||
|
||||
|
||||
class ScreenBaseMixin:
|
||||
@ -11,11 +11,11 @@ import time
|
||||
import webbrowser
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
from installer.screen import TerminalConsole
|
||||
from installer import deploy as inst_deploy
|
||||
from installer.build import get_build_command as inst_get_build_command
|
||||
from installer import screen as ui
|
||||
from installer.core.env import PROJECT_ROOT
|
||||
from knoe.screen import TerminalConsole
|
||||
from knoe import deploy as inst_deploy
|
||||
from knoe.build import get_build_command as inst_get_build_command
|
||||
from knoe import screen as ui
|
||||
from knoe.core.env import PROJECT_ROOT
|
||||
|
||||
|
||||
class BuildScreenMixin:
|
||||
@ -793,7 +793,7 @@ echo "-------------------------------------------------------------------";
|
||||
pass
|
||||
|
||||
def get_build_command(self, env: str) -> str:
|
||||
"""Delegate to installer.build.get_build_command."""
|
||||
"""Delegate to knoe.build.get_build_command."""
|
||||
return inst_get_build_command(PROJECT_ROOT, env)
|
||||
|
||||
# ---------------- Build Summary page ----------------
|
||||
@ -9,8 +9,8 @@ import subprocess
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
from installer import prole_conf
|
||||
from installer.core.env import (
|
||||
from knoe import prole_conf
|
||||
from knoe.core.env import (
|
||||
DEFAULT_ACTION_FLAGS,
|
||||
DEFAULT_OLLAMA_PORT,
|
||||
PROJECT_ROOT,
|
||||
@ -28,7 +28,7 @@ from installer.core.env import (
|
||||
_read_k3s_cfg,
|
||||
_render_prole_cfg,
|
||||
)
|
||||
from installer.config import _collect_cfg_vars, _encrypt_cfg_secret, _expand_cfg_value
|
||||
from knoe.config import _collect_cfg_vars, _encrypt_cfg_secret, _expand_cfg_value
|
||||
|
||||
|
||||
class ConfigMixin:
|
||||
@ -12,10 +12,10 @@ import urllib.request
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
from installer.screen import TerminalConsole
|
||||
from installer import screen as ui
|
||||
from installer import prole_conf
|
||||
from installer.core.env import (
|
||||
from knoe.screen import TerminalConsole
|
||||
from knoe import screen as ui
|
||||
from knoe import prole_conf
|
||||
from knoe.core.env import (
|
||||
PROJECT_ROOT,
|
||||
_default_opentofu_pipeline_url,
|
||||
_deployment_mode_from_env,
|
||||
@ -29,12 +29,14 @@ from installer.core.env import (
|
||||
_resolve_k3s_connection as _resolve_k3s_connection_fn,
|
||||
_safe_str,
|
||||
)
|
||||
from installer.config import (
|
||||
from knoe.config import (
|
||||
_collect_cfg_vars_from_data,
|
||||
_encrypt_cfg_secret,
|
||||
_expand_path_expr,
|
||||
_merge_kubeconfig,
|
||||
_write_k3s_kubeconfig,
|
||||
)
|
||||
from installer.core.actions import _reset_k3s_namespace
|
||||
from knoe.core.actions import _reset_k3s_namespace
|
||||
|
||||
|
||||
class ClusterScreenMixin:
|
||||
@ -1386,13 +1388,15 @@ class ClusterScreenMixin:
|
||||
"Validation", "Please specify an artifact staging directory."
|
||||
)
|
||||
return False
|
||||
p = Path(path).expanduser()
|
||||
cfg_vars = _collect_cfg_vars_from_data(getattr(self, "prole_cfg_data", None))
|
||||
expanded_path = _expand_path_expr(path, cfg_vars)
|
||||
p = Path(expanded_path).expanduser()
|
||||
if not p.exists():
|
||||
try:
|
||||
p.mkdir(parents=True, exist_ok=True)
|
||||
except Exception as e:
|
||||
messagebox.showerror(
|
||||
"Error", f"Failed to create directory {path}: {e}"
|
||||
"Error", f"Failed to create directory {expanded_path or path}: {e}"
|
||||
)
|
||||
return False
|
||||
cluster_val = self.cluster_env.get()
|
||||
@ -6,7 +6,7 @@ import json
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
|
||||
from installer import screen as ui
|
||||
from knoe import screen as ui
|
||||
|
||||
|
||||
class ClusterNodesScreenMixin:
|
||||
@ -3,15 +3,14 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog, simpledialog
|
||||
from installer import screen as ui
|
||||
from installer.config import get_docker_build_platform_args
|
||||
from installer.core.env import (
|
||||
from tkinter import ttk, messagebox, simpledialog
|
||||
from knoe import screen as ui
|
||||
from knoe.config import get_docker_build_platform_args
|
||||
from knoe.core.env import (
|
||||
PROJECT_ROOT,
|
||||
_bool_str,
|
||||
_deployment_mode_from_env,
|
||||
@ -20,6 +19,7 @@ from installer.core.env import (
|
||||
_push_docker_image,
|
||||
get_resource_path,
|
||||
)
|
||||
from knoe.core.build_context import copy_build_context_dir
|
||||
|
||||
|
||||
class DatabaseScreenMixin:
|
||||
@ -396,7 +396,7 @@ class DatabaseScreenMixin:
|
||||
if vals:
|
||||
ns = vals[1]
|
||||
if ns == "supabase":
|
||||
# Do not allow selecting 'supabase' as the primary prole-db namespace
|
||||
# Do not allow selecting 'supabase' as the primary knoe-db namespace
|
||||
return
|
||||
if ns == self.db_namespace.get():
|
||||
# Already selected, don't trigger re-refresh/save
|
||||
@ -562,14 +562,14 @@ class DatabaseScreenMixin:
|
||||
if ns == "supabase":
|
||||
port = "5432"
|
||||
else:
|
||||
# For prole-db namespaces
|
||||
# For knoe-db namespaces
|
||||
if ns == current_ns:
|
||||
if supabase_enabled:
|
||||
port = "15432"
|
||||
else:
|
||||
port = db_host_port or "5432"
|
||||
else:
|
||||
# If it's another prole-db namespace, we might not know its port easily
|
||||
# If it's another knoe-db namespace, we might not know its port easily
|
||||
# but if Supabase is enabled globally, we assume standard alternate port
|
||||
if supabase_enabled:
|
||||
port = "15432"
|
||||
@ -940,7 +940,7 @@ class DatabaseScreenMixin:
|
||||
|
||||
self._render_title("Build Database Image", y=150)
|
||||
self._render_paragraph(
|
||||
"Building the prole-db Postgres image. This may take a few minutes.", y=200
|
||||
"Building the knoe-db Postgres image. This may take a few minutes.", y=200
|
||||
)
|
||||
|
||||
# Registry status (checked async)
|
||||
@ -1414,7 +1414,7 @@ class DatabaseScreenMixin:
|
||||
)
|
||||
|
||||
tag = self.get_prole_db_version()
|
||||
image_name = f"prole-db:{tag}"
|
||||
image_name = f"knoe-db:{tag}"
|
||||
|
||||
# Use $HOME/.prole/build for Docker build context
|
||||
# This avoids issues with PyInstaller's temporary _MEIPASS directory
|
||||
@ -1424,14 +1424,7 @@ class DatabaseScreenMixin:
|
||||
|
||||
# Copy prole-db directory to writable location
|
||||
source_dir = get_resource_path("prole-db")
|
||||
if source_dir.exists():
|
||||
import shutil
|
||||
|
||||
# Remove old build dir and copy fresh
|
||||
if source_dir.resolve() != build_dir.resolve():
|
||||
if build_dir.exists():
|
||||
shutil.rmtree(build_dir)
|
||||
shutil.copytree(source_dir, build_dir)
|
||||
copy_build_context_dir(source_dir, build_dir)
|
||||
|
||||
cwd = build_dir
|
||||
|
||||
@ -1498,7 +1491,7 @@ class DatabaseScreenMixin:
|
||||
import_tag = image_name
|
||||
if registry_info:
|
||||
host_registry, cluster_registry = registry_info
|
||||
remote_tag = f"{host_registry}/prole-db:{tag}"
|
||||
remote_tag = f"{host_registry}/knoe-db:{tag}"
|
||||
self._db_build_console.write(
|
||||
f"Tagging image for registry: {remote_tag}\n"
|
||||
)
|
||||
@ -1578,13 +1571,13 @@ class DatabaseScreenMixin:
|
||||
self._db_build_console.write(f"Push complete: {remote_tag}\n")
|
||||
|
||||
if cluster_registry:
|
||||
import_tag = f"{cluster_registry}/prole-db:{tag}"
|
||||
import_tag = f"{cluster_registry}/knoe-db:{tag}"
|
||||
# k3d registry is often reachable internally without the .localhost suffix
|
||||
if cluster_registry.endswith(".localhost:5000"):
|
||||
alt_registry = cluster_registry.replace(
|
||||
".localhost", ""
|
||||
)
|
||||
alt_tag = f"{alt_registry}/prole-db:{tag}"
|
||||
alt_tag = f"{alt_registry}/knoe-db:{tag}"
|
||||
if alt_tag != import_tag:
|
||||
self._db_build_console.write(
|
||||
f"Tagging image for k3d import: {alt_tag}\n"
|
||||
@ -1647,7 +1640,7 @@ class DatabaseScreenMixin:
|
||||
)
|
||||
return
|
||||
|
||||
remote_tag = f"{registry_url}/prole-db:{tag}"
|
||||
remote_tag = f"{registry_url}/knoe-db:{tag}"
|
||||
self._db_build_console.write(
|
||||
f"Tagging image for registry: {remote_tag}\n"
|
||||
)
|
||||
@ -7,8 +7,8 @@ import threading
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox
|
||||
from installer import screen as ui
|
||||
from installer.core.env import PROJECT_ROOT, _safe_str
|
||||
from knoe import screen as ui
|
||||
from knoe.core.env import PROJECT_ROOT, _safe_str
|
||||
|
||||
class DatabaseOptionsScreenMixin:
|
||||
"""Mixin for the Database Options screen."""
|
||||
@ -5,8 +5,8 @@ import threading
|
||||
import time
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
from installer import config as inst_config
|
||||
from installer import screen as ui
|
||||
from knoe import config as inst_config
|
||||
from knoe import screen as ui
|
||||
|
||||
|
||||
class DependenciesScreenMixin:
|
||||
@ -627,9 +627,9 @@ class DependenciesScreenMixin:
|
||||
return True
|
||||
|
||||
def get_dep_info(self, dep):
|
||||
"""Delegate dependency probing to installer.config.get_dep_info."""
|
||||
"""Delegate dependency probing to knoe.config.get_dep_info."""
|
||||
return inst_config.get_dep_info(dep)
|
||||
|
||||
def normalize_version(self, text: str) -> str:
|
||||
"""Normalize versions via installer.config.normalize_version."""
|
||||
"""Normalize versions via knoe.config.normalize_version."""
|
||||
return inst_config.normalize_version(text)
|
||||
@ -8,8 +8,8 @@ import threading
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
from installer import screen as ui
|
||||
from installer.core.env import PROJECT_ROOT
|
||||
from knoe import screen as ui
|
||||
from knoe.core.env import PROJECT_ROOT
|
||||
|
||||
|
||||
class DeployScreenMixin:
|
||||
@ -9,8 +9,8 @@ import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from installer.config import get_docker_build_platform_args
|
||||
from installer.core.env import (
|
||||
from knoe.config import get_docker_build_platform_args
|
||||
from knoe.core.env import (
|
||||
PROJECT_ROOT,
|
||||
_collect_images_from_files,
|
||||
_deployment_mode_from_env,
|
||||
@ -424,10 +424,10 @@ class DockerScreenMixin:
|
||||
return None
|
||||
|
||||
def build_docker_image(self):
|
||||
"""Build prole-db Docker image"""
|
||||
"""Build knoe-db Docker image"""
|
||||
# If Kerberos is enabled, update pg_hba.conf in conf/postgresql before copying
|
||||
if self.kerberos_enabled.get():
|
||||
realm = self.kerberos_realm.get().strip() or "EXAMPLE.COM"
|
||||
realm = self.kerberos_realm.get().strip() or "KNOEY.COM"
|
||||
hba_src = PROJECT_ROOT / "conf" / "postgresql" / "pg_hba.conf"
|
||||
if hba_src.exists():
|
||||
content = hba_src.read_text()
|
||||
@ -436,14 +436,14 @@ class DockerScreenMixin:
|
||||
content += f"\nhost all all all gss include_realm=1 krb_realm={realm}\n"
|
||||
else:
|
||||
content = content.replace(
|
||||
"krb_realm=EXAMPLE.COM", f"krb_realm={realm}"
|
||||
"krb_realm=KNOEY.COM", f"krb_realm={realm}"
|
||||
)
|
||||
hba_src.write_text(content)
|
||||
print(f"Updated {hba_src} with realm {realm}")
|
||||
|
||||
# Base local image tag (before pushing to registry)
|
||||
version = self.get_prole_db_version()
|
||||
image_tag = f"prole-db:{version}"
|
||||
image_tag = f"knoe-db:{version}"
|
||||
|
||||
# Prepare build context: copy conf/postgresql to prole-db/postgresql
|
||||
conf_src = PROJECT_ROOT / "conf" / "postgresql"
|
||||
@ -514,9 +514,9 @@ class DockerScreenMixin:
|
||||
return
|
||||
version = self.get_prole_db_version()
|
||||
registry = getattr(self, "registry_url", "localhost:5000")
|
||||
image_name = f"prole-db:{version}"
|
||||
image_name = f"knoe-db:{version}"
|
||||
image = getattr(self, "local_image_tag", image_name)
|
||||
self.remote_image_tag = f"{registry}/prole-db:{version}"
|
||||
self.remote_image_tag = f"{registry}/knoe-db:{version}"
|
||||
subprocess.run(
|
||||
["docker", "tag", image, self.remote_image_tag],
|
||||
check=True,
|
||||
@ -537,7 +537,7 @@ class DockerScreenMixin:
|
||||
def import_k3d_image(self, cluster_name="knoe-dev-cluster"):
|
||||
"""Import image to k3d cluster (only for Dev)."""
|
||||
version = self.get_prole_db_version()
|
||||
image_name = f"prole-db:{version}"
|
||||
image_name = f"knoe-db:{version}"
|
||||
subprocess.run(
|
||||
["k3d", "image", "import", image_name, "-c", cluster_name],
|
||||
check=True,
|
||||
@ -556,12 +556,12 @@ class DockerScreenMixin:
|
||||
if not manifest_path.exists():
|
||||
continue
|
||||
content = manifest_path.read_text()
|
||||
# Update imageName: prole-db:17.7-043
|
||||
# Update imageName: knoe-db:17.7-043
|
||||
new_content = re.sub(r"imageName:\s*.*", f"imageName: {image_tag}", content)
|
||||
|
||||
# Update Kerberos realm in manifest if enabled
|
||||
if self.kerberos_enabled.get():
|
||||
realm = self.kerberos_realm.get().strip() or "EXAMPLE.COM"
|
||||
realm = self.kerberos_realm.get().strip() or "KNOEY.COM"
|
||||
if "gss" not in new_content:
|
||||
# Insert GSS rule after existing scram rules for service accounts
|
||||
new_content = new_content.replace(
|
||||
@ -571,7 +571,7 @@ class DockerScreenMixin:
|
||||
)
|
||||
else:
|
||||
new_content = new_content.replace(
|
||||
"krb_realm=EXAMPLE.COM", f"krb_realm={realm}"
|
||||
"krb_realm=KNOEY.COM", f"krb_realm={realm}"
|
||||
)
|
||||
|
||||
if new_content != content:
|
||||
@ -7,8 +7,8 @@ import subprocess
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
from installer import screen as ui
|
||||
from installer.core.env import PROJECT_ROOT, _expand_cfg_value
|
||||
from knoe import screen as ui
|
||||
from knoe.core.env import PROJECT_ROOT, _expand_cfg_value
|
||||
|
||||
|
||||
class EnvironmentScreenMixin:
|
||||
@ -7,9 +7,9 @@ import subprocess
|
||||
import threading
|
||||
import tkinter as tk
|
||||
|
||||
from installer import prole_conf
|
||||
from installer import screen as ui
|
||||
from installer.core.env import PROJECT_ROOT, _normalize_cluster_env, _parse_bool
|
||||
from knoe import prole_conf
|
||||
from knoe import screen as ui
|
||||
from knoe.core.env import PROJECT_ROOT, _normalize_cluster_env, _parse_bool
|
||||
|
||||
|
||||
class GitOpsScreenMixin:
|
||||
@ -5,8 +5,8 @@ import platform
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
from installer import config as inst_config
|
||||
from installer.core.env import get_resource_path, _deployment_mode_from_env
|
||||
from knoe import config as inst_config
|
||||
from knoe.core.env import get_resource_path, _deployment_mode_from_env
|
||||
|
||||
|
||||
class NavigationMixin:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user