mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 19:04:31 +00:00
Itemized changes:
1. knoe-auth: New cluster-internal KDC and SSO gateway service
- Created etc/init_knoe_auth.sh based on init_kdc.sh with knoe-auth naming
- Namespace defaults to SERVICE_NAMESPACE (knoe-system)
- ConfigMap: knoe-auth-kdc-config, Secret: knoe-auth-secrets
- Legacy cleanup removes old auth/dog/authority deployments
2. Orchestration: knoe-auth initializes before CloudNativePG
- Updated prole.sh to insert init_knoe_auth.sh as step 2 (before CNPG)
- Renumbered all subsequent initialization steps
3. Kong routing: Updated init_kong.sh to route to knoe-auth in SERVICE_NAMESPACE
4. Comment/reference updates for knoe-auth
- Updated init_common_services.sh, init_service_layer.sh, init_kerberos.sh
5. prole-db renamed to knoe-db across the entire codebase
- Renamed prole-db/ directory to knoe-db/
- Renamed all prole-db Kubernetes manifests (deploy/opentofu, k8s/)
- Renamed scripts: docker-root-knoe-db.sh, docker-run-knoe-db.sh, test-cnpg-knoe-db.sh
- Renamed etc/init_prole-db-reset.sh to etc/init_knoe-db-reset.sh
- Renamed etc/prole-db-passwwd.sh to etc/knoe-db-passwwd.sh
- Renamed mock_val counterparts accordingly
- Renamed tests/etc/test_init_prole-db-reset.sh to test_init_knoe-db-reset.sh
- Renamed docs/prole-db-documentation-mcp-architecture.md to knoe-db variant
- Renamed modes/k3d/prole-db/ to modes/k3d/knoe-db/
- Renamed prole-db.iml to knoe-db.iml
6. Configuration updates
- Updated conf/dev, conf/prod, conf/test, conf/service prole.cfg files
- Updated conf/port-mapping.cfg
- Updated etc/prole_cfg.sh and mock_val/prole_cfg.sh
- Updated service/prole.cfg
7. Kubernetes manifests and deploy configuration
- Updated deploy/opentofu/k3s ArgoCD application YAMLs
- Updated kong-configmap.yaml and kustomization.yaml
- Updated k3s/kong-config.yml and prole-resources.yaml
- Updated prole-mssql-db deployment YAMLs
- Updated supabase helm render and deploy scripts
8. Infrastructure and GCP Terraform
- Updated deploy/gcp/terraform: folders, groups, IAM, service-projects
9. Python/installer code updates
- Updated knoe/core: actions, build_context, controller, env, milestones
- Updated knoe/milestone.py
- Updated knoe/ui/screens: cfg, database, database_options, deploy, docker,
navigation, security, services, validate
- Updated knoe.spec, status.py
10. Shell script updates
- Updated etc/: build_db, init_cloudnative_pg, init_cnpg_backup,
init_db_manager, init_forgejo, init_gitlab, init_monitoring, init_openbao,
init_port_forwards, init_postgrest, init_supabase_ports, status
- Updated mock_val/ counterparts for all above scripts
- Updated prole-net/init-prole-dns.sh
- Updated bin/prole-kpf.sh, gitea/deploy.sh, supabase/deploy.sh
11. Test updates
- Updated tests/etc/: test_init_cloudnative_pg*, test_init_cnpg_backup*,
test_init_kdc*, test_init_kerberos*, test_init_kong*, test_prole_cfg*
- Updated tests/installer/: test_actions_helpers, test_cfg_save_kubecontext,
test_controller, test_core_classes, test_milestones, test_milestones_extended,
test_namespace_propagation
- Updated tests/: test_database_options, test_navigation,
test_render_supabase_hostname, test_docker_build_fix,
test_all_prole_home_fixes, silent_install_test, final_test
12. Documentation updates
- Updated docs/: DOCKER-BUILD-FIX, PROLE-CFG-SECRETS, PROLE-HOME-DIRECTORY,
build-system, patent
- Updated scan/network_description.txt
- Updated pom.xml
13. Miscellaneous script updates
- Updated root-level: _adopt_replica_pvcs, _fix_replica_merlin, _import_pi,
_patch_cluster, _prebind_pvcs, _rebind_d002, _rebind_d002b, test_resolve
- Updated scripts/generate_spec.py
Co-authored-by: Junie <junie@jetbrains.com>
304 lines
8.6 KiB
Bash
Executable File
304 lines
8.6 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
|
# Logging setup
|
|
LOG_DIR="${PROLE_LOGS:-$ROOT_DIR/logs}"
|
|
LOG_DIR="${LOG_DIR%/}"
|
|
mkdir -p "$LOG_DIR"
|
|
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
|
|
LOG_FILE="${LOG_DIR}/silent_install_${TIMESTAMP}.log"
|
|
|
|
# Function to handle output redirection
|
|
if [[ "${SILENT_INSTALL_LOG:-}" == "true" ]]; then
|
|
echo "Logging output to: $LOG_FILE"
|
|
# Redirect stdout and stderr to both terminal and log file
|
|
exec > >(tee -a "$LOG_FILE") 2>&1
|
|
fi
|
|
|
|
CFG_PATH="${1:-}"
|
|
if [[ -z "$CFG_PATH" ]]; then
|
|
if [[ -n "${PROLE_TEST_CFG:-}" ]]; then
|
|
if [[ "$PROLE_TEST_CFG" == "true" || "$PROLE_TEST_CFG" == "1" ]]; then
|
|
CFG_PATH="$ROOT_DIR/tests/fixtures/test-prole.cfg"
|
|
else
|
|
CFG_PATH="$PROLE_TEST_CFG"
|
|
fi
|
|
elif [[ -n "${PROLE_CONF:-}" && -f "$PROLE_CONF/prole.cfg" ]]; then
|
|
CFG_PATH="$PROLE_CONF/prole.cfg"
|
|
elif [[ -f "$ROOT_DIR/conf/prole.cfg" ]]; then
|
|
CFG_PATH="$ROOT_DIR/conf/prole.cfg"
|
|
else
|
|
echo "ERROR: prole.cfg not found. Pass a path or set PROLE_CONF." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
CFG_DIR="$(cd "$(dirname "$CFG_PATH")" && pwd)"
|
|
if [[ -z "${PROLE_CONF:-}" ]]; then
|
|
PROLE_CONF="$CFG_DIR"
|
|
export PROLE_CONF
|
|
fi
|
|
|
|
TMP_CFG=""
|
|
if [[ "${PROLE_CFG_INPLACE:-}" != "true" ]]; then
|
|
TMP_CFG="$(mktemp -t prole.cfg.XXXXXX)"
|
|
cp "$CFG_PATH" "$TMP_CFG"
|
|
CFG_PATH="$TMP_CFG"
|
|
trap '[[ -n "$TMP_CFG" ]] && rm -f "$TMP_CFG"' EXIT
|
|
fi
|
|
|
|
echo "=============================================="
|
|
echo " SILENT INSTALL TEST"
|
|
echo " prole.cfg: $CFG_PATH"
|
|
echo "=============================================="
|
|
echo ""
|
|
|
|
echo "1. Validating prole.cfg..."
|
|
python3 - "$CFG_PATH" <<'PY'
|
|
import configparser
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
cfg_path = Path(sys.argv[1]).expanduser()
|
|
if not cfg_path.exists():
|
|
print(f"ERROR: prole.cfg not found at {cfg_path}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
cfg = configparser.ConfigParser(interpolation=None)
|
|
cfg.optionxform = str
|
|
cfg.read(cfg_path)
|
|
|
|
required_sections = ["Inputs", "Global"]
|
|
missing_sections = [s for s in required_sections if not cfg.has_section(s)]
|
|
if missing_sections:
|
|
print(f"ERROR: Missing sections: {', '.join(missing_sections)}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
required_inputs = [
|
|
"build.deploy_env",
|
|
"build.run_build",
|
|
"dependencies.auto_install_missing",
|
|
"dependencies.brew.install",
|
|
"dependencies.docker.install",
|
|
"dependencies.k3d.install",
|
|
"dependencies.verify_all",
|
|
"disk_selection.disk_type",
|
|
"disk_selection.removable_mount",
|
|
"disk_selection.local_path",
|
|
"env_setup.NAMESPACE",
|
|
"env_setup.PROLE_CONF",
|
|
"env_setup.PROLE_DATA",
|
|
"env_setup.PROLE_HOME",
|
|
"env_setup.PROLE_LOGS",
|
|
"env_setup.PROLE_SERVICE",
|
|
"init_cluster.at_rest_encryption_enabled",
|
|
"init_cluster.cluster_env",
|
|
"init_cluster.kerberos_enabled",
|
|
"init_cluster.start_cluster",
|
|
"init_cluster.supabase_enabled",
|
|
"init_cnpg_deploy.force_rollout",
|
|
"init_cnpg_deploy.run_deploy",
|
|
"init_db_build.run_build",
|
|
"init_password.db_host_port",
|
|
"init_password.db_namespace",
|
|
"init_password.db_password",
|
|
"init_password.db_password_confirm",
|
|
"init_password.db_username",
|
|
"init_password.generate_ssh_key",
|
|
"init_scripts.run_scripts",
|
|
"kerberos_config.enabled",
|
|
"kerberos_config.init_authority",
|
|
"kerberos_config.kdc",
|
|
"kerberos_config.password",
|
|
"kerberos_config.realm",
|
|
"kerberos_config.test_connection",
|
|
"kerberos_config.user",
|
|
"network_scan.run",
|
|
]
|
|
|
|
missing_inputs = [k for k in required_inputs if not cfg.has_option("Inputs", k)]
|
|
if missing_inputs:
|
|
print("ERROR: Missing Inputs keys:", file=sys.stderr)
|
|
for key in missing_inputs:
|
|
print(f" - {key}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
required_non_empty = {
|
|
"Inputs": [
|
|
"env_setup.PROLE_HOME",
|
|
"env_setup.PROLE_CONF",
|
|
"env_setup.PROLE_DATA",
|
|
"env_setup.PROLE_LOGS",
|
|
"env_setup.PROLE_SERVICE",
|
|
"env_setup.NAMESPACE",
|
|
"init_password.db_namespace",
|
|
"init_password.db_username",
|
|
"init_password.db_host_port",
|
|
],
|
|
"Global": [
|
|
"PROLE_HOME",
|
|
"KNOE_DB_USER",
|
|
"CLUSTER_ENV",
|
|
"NAMESPACE",
|
|
],
|
|
}
|
|
|
|
def is_anchor(val: str) -> bool:
|
|
val = val.strip()
|
|
return val.startswith("${OPENBAO:") or val.startswith("${PROLE_SECRET:")
|
|
|
|
errors = []
|
|
warnings = []
|
|
|
|
for section, keys in required_non_empty.items():
|
|
for key in keys:
|
|
if not cfg.has_option(section, key):
|
|
errors.append(f"{section}.{key} missing")
|
|
continue
|
|
value = cfg.get(section, key, fallback="").strip()
|
|
if not value:
|
|
errors.append(f"{section}.{key} is empty")
|
|
|
|
secret_keys = {
|
|
("Inputs", "init_password.db_password"),
|
|
("Inputs", "init_password.db_password_confirm"),
|
|
("Global", "DB_PASSWORD"),
|
|
}
|
|
|
|
for section, key in secret_keys:
|
|
if not cfg.has_option(section, key):
|
|
errors.append(f"{section}.{key} missing")
|
|
continue
|
|
value = cfg.get(section, key, fallback="").strip()
|
|
if not value:
|
|
errors.append(f"{section}.{key} is empty")
|
|
elif is_anchor(value):
|
|
warnings.append(f"{section}.{key} is anchored ({value})")
|
|
|
|
pw = cfg.get("Inputs", "init_password.db_password", fallback="")
|
|
pw_confirm = cfg.get("Inputs", "init_password.db_password_confirm", fallback="")
|
|
if pw and pw_confirm and pw != pw_confirm:
|
|
errors.append("Inputs.init_password.db_password does not match init_password.db_password_confirm")
|
|
|
|
if errors:
|
|
print("ERROR: prole.cfg validation failed:", file=sys.stderr)
|
|
for err in errors:
|
|
print(f" - {err}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if warnings:
|
|
print("WARN: prole.cfg contains anchored secrets:")
|
|
for warn in warnings:
|
|
print(f" - {warn}")
|
|
|
|
print("OK: prole.cfg validation")
|
|
PY
|
|
|
|
echo ""
|
|
echo "2. Running silent install..."
|
|
if python3 "$ROOT_DIR/install.py" -S -c "$CFG_PATH"; then
|
|
echo "OK: Silent install completed"
|
|
else
|
|
echo "ERROR: Silent install failed" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "3. Blessing sanitized gold config..."
|
|
python3 - "$CFG_PATH" "$PROLE_CONF/knoe-db/prole.cfg" <<'PY'
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
import configparser
|
|
|
|
source = Path(sys.argv[1]).expanduser()
|
|
target = Path(sys.argv[2]).expanduser()
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
text = source.read_text()
|
|
|
|
cfg = configparser.ConfigParser(interpolation=None)
|
|
cfg.optionxform = str
|
|
cfg.read(source)
|
|
|
|
var_pattern = re.compile(r"\$(\w+)|\$\{(\w+)\}")
|
|
|
|
def expand_cfg_value(val: str, cfg_vars: dict) -> str:
|
|
if val is None:
|
|
return ""
|
|
raw = str(val)
|
|
def repl(match):
|
|
var = match.group(1) or match.group(2)
|
|
return str(cfg_vars.get(var, match.group(0)))
|
|
out = raw
|
|
for _ in range(5):
|
|
new = var_pattern.sub(repl, out)
|
|
if new == out:
|
|
break
|
|
out = new
|
|
return out
|
|
|
|
cfg_vars = {}
|
|
for section in cfg.sections():
|
|
for k, v in cfg.items(section):
|
|
if k in cfg_vars:
|
|
continue
|
|
cfg_vars[k] = expand_cfg_value(v, cfg_vars)
|
|
|
|
namespace = (
|
|
expand_cfg_value(cfg.get("Global", "NAMESPACE", fallback=""), cfg_vars)
|
|
or expand_cfg_value(cfg.get("Inputs", "env_setup.NAMESPACE", fallback=""), cfg_vars)
|
|
or "default"
|
|
).strip() or "default"
|
|
|
|
def bao(leaf: str, key: str) -> str:
|
|
return f"${{OPENBAO:kv/prole/{namespace}/{leaf}#{key}}}"
|
|
|
|
secret_map = {
|
|
"init_password.db_password": bao("db", "password"),
|
|
"init_password.db_password_confirm": bao("db", "password"),
|
|
"kerberos_config.password": bao("kerberos", "password"),
|
|
"DB_PASSWORD": bao("db", "password"),
|
|
"PASSWORD": bao("kerberos", "password"),
|
|
"GRAFANA_ADMIN_PASSWORD": bao("monitoring", "grafana_admin_password"),
|
|
}
|
|
|
|
pattern = re.compile(r"^(?P<lead>\s*)(?P<key>[^=]+?)(?P<pre>\s*)=(?P<post>\s*).*$")
|
|
out_lines = []
|
|
|
|
for line in text.splitlines():
|
|
stripped = line.strip()
|
|
if not stripped or stripped.startswith((';', '#')) or '=' not in line:
|
|
out_lines.append(line)
|
|
continue
|
|
m = pattern.match(line)
|
|
if not m:
|
|
out_lines.append(line)
|
|
continue
|
|
key = m.group("key").strip()
|
|
lead = m.group("lead")
|
|
pre = m.group("pre")
|
|
post = m.group("post")
|
|
if key in secret_map:
|
|
value = secret_map[key]
|
|
out_lines.append(f"{lead}{key}{pre}={post}{value}")
|
|
continue
|
|
if re.search(r"(?i)(?:^|[_.])(password|token|secret)$", key):
|
|
out_lines.append(f"{lead}{key}{pre}={post}${{OPENBAO:REDACTED}}")
|
|
continue
|
|
out_lines.append(line)
|
|
|
|
target.write_text("\n".join(out_lines) + "\n")
|
|
print(f"OK: Wrote sanitized gold config: {target}")
|
|
PY
|
|
|
|
echo ""
|
|
echo "=============================================="
|
|
echo " OK: Silent Install Test Completed"
|
|
echo "=============================================="
|
|
echo ""
|