mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 11:03:59 +00:00
- make cluster-storage milestone opt-in and remove installer cluster-storage step from UI navigation\n- add cluster storage browser and GKE cluster ops helpers with CLI coverage\n- update k8s/CNPG config and install flow files for corrected cluster setup\n- add/refresh tests for storage browser, GKE ops, prod config, and service-layer navigation Co-authored-by: Junie <junie@jetbrains.com>
116 lines
4.5 KiB
Python
116 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import importlib
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from knoe.core.cluster_storage_browser import (
|
|
ClusterStorageRequest,
|
|
GcloudRegionQuotaProvider,
|
|
PingLatencyProvider,
|
|
build_cluster_storage_browser_result,
|
|
)
|
|
|
|
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:
|
|
argv = list(argv) if argv is not None else list(sys.argv[1:])
|
|
|
|
if argv and argv[0] == "storage-browser":
|
|
storage = argparse.ArgumentParser(prog="knoe-ops storage-browser", add_help=True)
|
|
storage.add_argument("--project", required=True, help="GCP project ID")
|
|
storage.add_argument("--timezone", default="", help="Timezone group: americas|emea|apac")
|
|
storage.add_argument("--pgdata-gb", type=float, default=100.0)
|
|
storage.add_argument("--wal-gb", type=float, default=25.0)
|
|
storage.add_argument(
|
|
"--regions",
|
|
default="",
|
|
help="Comma-separated region list. If omitted, fetch via gcloud compute regions list.",
|
|
)
|
|
storage.add_argument("--output", choices=["table", "json"], default="table")
|
|
args = storage.parse_args(argv[1:])
|
|
|
|
regions = [r.strip() for r in (args.regions or "").split(",") if r.strip()]
|
|
if not regions:
|
|
import subprocess
|
|
|
|
cmd = ["gcloud", "compute", "regions", "list", "--project", args.project, "--format=value(name)"]
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=20)
|
|
if result.returncode == 0:
|
|
regions = [r.strip() for r in result.stdout.splitlines() if r.strip()]
|
|
except Exception:
|
|
regions = []
|
|
|
|
result = build_cluster_storage_browser_result(
|
|
project_id=args.project,
|
|
requested=ClusterStorageRequest(pgdata_gb=args.pgdata_gb, wal_gb=args.wal_gb),
|
|
available_regions=regions,
|
|
timezone_group=(args.timezone or "").strip().lower() or None,
|
|
quota_provider=GcloudRegionQuotaProvider(),
|
|
latency_provider=PingLatencyProvider(),
|
|
)
|
|
if args.output == "json":
|
|
print(result.to_json())
|
|
return 0
|
|
|
|
print(f"Project: {result.project_id} Timezone: {result.timezone_group}")
|
|
print(f"Quota docs: {result.quota_link}")
|
|
print(f"Cloud Hub: {result.quota_console_link}")
|
|
print("region\tfeasible\tdefault(pg/wal)\tpremium_headroom\tstandard_headroom\tlatency_ms")
|
|
for c in result.candidates:
|
|
f = c.feasibility
|
|
feasible = "yes" if (f.can_premium or f.can_standard) else "no"
|
|
premium = "-" if f.premium_headroom_gb is None else f"{f.premium_headroom_gb:.1f}"
|
|
standard = "-" if f.standard_headroom_gb is None else f"{f.standard_headroom_gb:.1f}"
|
|
latency = "-" if c.latency_ms is None else f"{c.latency_ms:.2f}"
|
|
print(
|
|
f"{f.region}\t{feasible}\t{f.default_pgdata_class}/{f.default_wal_class}\t"
|
|
f"{premium}\t{standard}\t{latency}"
|
|
)
|
|
return 0
|
|
|
|
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())
|