mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 12:03:59 +00:00
UI screens - database.py: fix mode detection to use env_key priority (prod→k8s, service→k3s) so stale DEPLOYMENT_MODE never overrides the user's chosen environment - database.py: Registry status reads ARTIFACT_REGISTRY_AVAILABLE persisted by cluster screen; uses SERVICE_NAMESPACE for Artifact Registry repo name - cluster.py: add Artifact Registry traffic light (amber→green/red) to prod section; _check_artifact_registry_async persists ARTIFACT_REGISTRY_AVAILABLE into Global cfg - cluster.py: re-trigger Artifact Registry check after GKE cluster selection so the light re-evaluates once region is available from KUBECONTEXT - cluster_nodes.py: fix TclError on Python 3.14 — pady=(2,0) tuple → pady=2 scalar - __init__.py: seed knoe-system namespace when saved value is "default", not only when empty - services.py: replace hardcoded "Prole DB" log string with dynamic cnpg_cluster name Core ops - cloudnative_pg.py: replace one-shot Barman plugin retry with 6-attempt loop; first cert-manager/x509 failure triggers rollout restart + 30 s CA propagation wait; subsequent failures back off up to 60 s per attempt - cloudnative_pg.py: TLS CA CN now uses cluster_name instead of hardcoded "Prole CNPG CA" - registry.py, garage_store.py: refactored into per-mode modules (k3d/k3s/k8s registry and garage store, shared _garage_common) Deploy / config - deploy/gcp/gke/knoe-db.yaml: GKE-specific CNPG cluster manifest (rw/ro/r on separate nodes with premium-rwo storage) - etc/init_common_services.sh, modes/k8s/knoe-db/.version: updated for current deploy - kong-deployment.yaml: updated manifest Tests - test_cluster_nodes_render_smoke.py: add pack/grid, winfo_children, winfo_reqheight, update_idletasks, grid_slaves to dummy widgets; monkeypatch tk.Label so CNPG placement render completes without a real Tkinter root Co-authored-by: Junie <junie@jetbrains.com>
203 lines
5.6 KiB
Python
203 lines
5.6 KiB
Python
"""Registry ops — k3s mode (bare-metal / Synology cluster).
|
|
|
|
Deploys an in-cluster Docker registry as a Kubernetes Deployment + Service.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from ._services_common import (
|
|
_LogFn,
|
|
_ensure_namespace,
|
|
_exists,
|
|
_kubectl,
|
|
_log,
|
|
_manifest_path,
|
|
_prune_named_workload_other_namespaces,
|
|
_reconcile_deployment_replicasets,
|
|
_registry_namespace,
|
|
_to_bool,
|
|
_wait_rollout,
|
|
)
|
|
|
|
|
|
def _manifest(project_root: str | Path) -> Path:
|
|
return _manifest_path(project_root, "k8s", "registry", "deployment.yaml")
|
|
|
|
|
|
def _deployment_is_available(
|
|
*,
|
|
deployment: str,
|
|
namespace: str,
|
|
env: dict | None = None,
|
|
) -> bool:
|
|
res = _kubectl(
|
|
["-n", namespace, "get", "deployment", deployment, "-o", "json"],
|
|
env=env,
|
|
timeout=20,
|
|
)
|
|
if res.returncode != 0 or not res.stdout.strip():
|
|
return False
|
|
try:
|
|
payload = json.loads(res.stdout)
|
|
except Exception:
|
|
return False
|
|
spec = payload.get("spec") or {}
|
|
status = payload.get("status") or {}
|
|
desired = int(spec.get("replicas") or 1)
|
|
available = int(status.get("availableReplicas") or 0)
|
|
return desired > 0 and available >= desired
|
|
|
|
|
|
def initialize(
|
|
*,
|
|
namespace: str | None = None,
|
|
env: dict | None = None,
|
|
project_root: str | Path = ".",
|
|
log: _LogFn | None = None,
|
|
mode: str | None = None,
|
|
) -> None:
|
|
update(namespace=namespace, env=env, project_root=project_root, log=log, mode=mode)
|
|
|
|
|
|
def start(
|
|
*,
|
|
namespace: str | None = None,
|
|
env: dict | None = None,
|
|
project_root: str | Path = ".",
|
|
log: _LogFn | None = None,
|
|
mode: str | None = None,
|
|
) -> None:
|
|
update(namespace=namespace, env=env, project_root=project_root, log=log, mode=mode)
|
|
|
|
|
|
def update(
|
|
*,
|
|
namespace: str | None = None,
|
|
env: dict | None = None,
|
|
project_root: str | Path = ".",
|
|
log: _LogFn | None = None,
|
|
mode: str | None = None,
|
|
) -> None:
|
|
target_ns = _registry_namespace(namespace, env)
|
|
_ensure_namespace(target_ns, env)
|
|
_prune_named_workload_other_namespaces(
|
|
kind="deployment",
|
|
name="registry",
|
|
target_namespace=target_ns,
|
|
label_selector="app=registry",
|
|
env=env,
|
|
log=log,
|
|
)
|
|
_prune_named_workload_other_namespaces(
|
|
kind="service",
|
|
name="registry",
|
|
target_namespace=target_ns,
|
|
label_selector="app=registry",
|
|
env=env,
|
|
log=log,
|
|
)
|
|
|
|
force_apply = _to_bool((env or {}).get("PROLE_REGISTRY_FORCE_APPLY"), default=False)
|
|
if not force_apply and _exists("deployment", "registry", target_ns, env=env) and _exists(
|
|
"service", "registry", target_ns, env=env
|
|
):
|
|
_reconcile_deployment_replicasets(
|
|
deployment="registry",
|
|
namespace=target_ns,
|
|
label_selector="app=registry",
|
|
env=env,
|
|
log=log,
|
|
)
|
|
if _deployment_is_available(deployment="registry", namespace=target_ns, env=env):
|
|
_log(
|
|
log,
|
|
f"[REGISTRY] Healthy deployment already present in namespace {target_ns}; skipping re-apply",
|
|
)
|
|
return
|
|
_log(
|
|
log,
|
|
f"[REGISTRY] Existing deployment in namespace {target_ns} is not ready; applying manifest for recovery",
|
|
)
|
|
|
|
if force_apply:
|
|
_log(
|
|
log,
|
|
f"[REGISTRY] Force apply enabled; reconciling manifest in namespace {target_ns}",
|
|
)
|
|
|
|
manifest = _manifest(project_root)
|
|
if not manifest.exists():
|
|
raise RuntimeError(f"Registry manifest not found: {manifest}")
|
|
_log(log, f"[REGISTRY] Applying {manifest}")
|
|
_kubectl(
|
|
["-n", target_ns, "apply", "-f", str(manifest)],
|
|
env=env,
|
|
timeout=180,
|
|
check=True,
|
|
)
|
|
_reconcile_deployment_replicasets(
|
|
deployment="registry",
|
|
namespace=target_ns,
|
|
label_selector="app=registry",
|
|
env=env,
|
|
log=log,
|
|
)
|
|
_wait_rollout("deployment", "registry", target_ns, env=env)
|
|
|
|
|
|
def stop(
|
|
*,
|
|
namespace: str | None = None,
|
|
env: dict | None = None,
|
|
mode: str | None = None,
|
|
log: _LogFn | None = None,
|
|
) -> None:
|
|
target_ns = _registry_namespace(namespace, env)
|
|
if _exists("deployment", "registry", target_ns, env=env):
|
|
_kubectl(
|
|
["-n", target_ns, "scale", "deployment/registry", "--replicas=0"],
|
|
env=env,
|
|
timeout=90,
|
|
check=True,
|
|
)
|
|
|
|
|
|
def restart(
|
|
*,
|
|
namespace: str | None = None,
|
|
env: dict | None = None,
|
|
mode: str | None = None,
|
|
log: _LogFn | None = None,
|
|
) -> None:
|
|
target_ns = _registry_namespace(namespace, env)
|
|
if not _exists("deployment", "registry", target_ns, env=env):
|
|
update(namespace=namespace, env=env, mode=mode, log=log)
|
|
return
|
|
_kubectl(
|
|
["-n", target_ns, "rollout", "restart", "deployment/registry"],
|
|
env=env,
|
|
timeout=120,
|
|
check=True,
|
|
)
|
|
_wait_rollout("deployment", "registry", target_ns, env=env)
|
|
|
|
|
|
def status(
|
|
*,
|
|
namespace: str | None = None,
|
|
env: dict | None = None,
|
|
mode: str | None = None,
|
|
) -> bool:
|
|
target_ns = _registry_namespace(namespace, env)
|
|
dep = _kubectl(
|
|
["-n", target_ns, "get", "deployment", "registry", "-o", "jsonpath={.status.readyReplicas}"],
|
|
env=env,
|
|
timeout=20,
|
|
)
|
|
svc = _kubectl(["-n", target_ns, "get", "svc", "registry"], env=env, timeout=20)
|
|
return _to_bool(
|
|
dep.returncode == 0 and (dep.stdout or "0").strip() not in {"", "0"} and svc.returncode == 0
|
|
)
|