mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
165 lines
4.7 KiB
Python
165 lines
4.7 KiB
Python
"""Multi-cluster management for Hanzo K8s CLI.
|
|
|
|
Stores known clusters in ~/.hanzo/k8s/clusters.json and manages
|
|
the current active cluster context for kubectl operations.
|
|
|
|
Usage:
|
|
hanzo k8s clusters # List all clusters
|
|
hanzo k8s use <name> # Switch to a cluster
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import click
|
|
from rich.console import Console
|
|
from rich.table import Table
|
|
|
|
console = Console()
|
|
|
|
CLUSTERS_FILE = Path.home() / ".hanzo" / "k8s" / "clusters.json"
|
|
|
|
DEFAULT_CLUSTERS: dict = {
|
|
"clusters": {
|
|
"hanzo": {
|
|
"context": "do-sfo3-hanzo-k8s",
|
|
"namespace": "hanzo",
|
|
"ip": "209.38.69.69",
|
|
},
|
|
"adnexus": {
|
|
"context": "do-sfo3-adnexus-k8s",
|
|
"namespace": "adnexus",
|
|
"ip": "134.199.141.68",
|
|
},
|
|
"lux": {
|
|
"context": "do-sfo3-lux-k8s",
|
|
"namespace": "lux",
|
|
},
|
|
"pars": {
|
|
"context": "do-sfo3-pars-k8s",
|
|
"namespace": "pars",
|
|
},
|
|
"zoo": {
|
|
"context": "do-sfo3-zoo-k8s",
|
|
"namespace": "zoo",
|
|
},
|
|
"bootnode": {
|
|
"context": "do-sfo3-bootnode-k8s",
|
|
"namespace": "bootnode",
|
|
},
|
|
},
|
|
"current": "hanzo",
|
|
}
|
|
|
|
|
|
def _load_clusters() -> dict:
|
|
"""Load clusters config, auto-creating with defaults on first use."""
|
|
if CLUSTERS_FILE.exists():
|
|
try:
|
|
return json.loads(CLUSTERS_FILE.read_text())
|
|
except (json.JSONDecodeError, OSError):
|
|
pass
|
|
_save_clusters(DEFAULT_CLUSTERS)
|
|
return DEFAULT_CLUSTERS
|
|
|
|
|
|
def _save_clusters(data: dict) -> None:
|
|
"""Persist clusters config to disk."""
|
|
CLUSTERS_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
CLUSTERS_FILE.write_text(json.dumps(data, indent=2))
|
|
|
|
|
|
def get_current_cluster() -> tuple[str, dict]:
|
|
"""Return (name, cluster_info) for the current active cluster."""
|
|
config = _load_clusters()
|
|
name = config.get("current", "hanzo")
|
|
cluster = config.get("clusters", {}).get(name, {})
|
|
return name, cluster
|
|
|
|
|
|
@click.command("clusters")
|
|
def clusters() -> None:
|
|
"""List all known Kubernetes clusters."""
|
|
config = _load_clusters()
|
|
current = config.get("current", "")
|
|
cluster_map = config.get("clusters", {})
|
|
|
|
if not cluster_map:
|
|
console.print("[yellow]No clusters configured.[/yellow]")
|
|
return
|
|
|
|
table = Table(title="Kubernetes Clusters")
|
|
table.add_column("", width=2)
|
|
table.add_column("Name", style="cyan")
|
|
table.add_column("Context", style="white")
|
|
table.add_column("Namespace", style="green")
|
|
table.add_column("IP", style="dim")
|
|
|
|
for name, info in sorted(cluster_map.items()):
|
|
marker = "[bold green]*[/bold green]" if name == current else ""
|
|
table.add_row(
|
|
marker,
|
|
name,
|
|
info.get("context", ""),
|
|
info.get("namespace", ""),
|
|
info.get("ip", ""),
|
|
)
|
|
|
|
console.print(table)
|
|
console.print(f"\nCurrent: [bold cyan]{current}[/bold cyan]")
|
|
|
|
|
|
@click.command("use")
|
|
@click.argument("name")
|
|
def use(name: str) -> None:
|
|
"""Set the current Kubernetes cluster.
|
|
|
|
Switches kubectl context and updates the default namespace for
|
|
subsequent hanzo k8s commands.
|
|
"""
|
|
config = _load_clusters()
|
|
cluster_map = config.get("clusters", {})
|
|
|
|
if name not in cluster_map:
|
|
available = ", ".join(sorted(cluster_map.keys()))
|
|
console.print(f"[red]Unknown cluster:[/red] {name}")
|
|
console.print(f"Available: {available}")
|
|
raise SystemExit(1)
|
|
|
|
info = cluster_map[name]
|
|
context = info.get("context", "")
|
|
|
|
# Switch kubectl context
|
|
if context:
|
|
kubectl = shutil.which("kubectl")
|
|
if kubectl:
|
|
result = subprocess.run(
|
|
[kubectl, "config", "use-context", context],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if result.returncode != 0:
|
|
console.print(
|
|
f"[yellow]Warning:[/yellow] kubectl context switch failed: "
|
|
f"{result.stderr.strip()}"
|
|
)
|
|
console.print(
|
|
"You may need to add this context first with "
|
|
"'doctl kubernetes cluster kubeconfig save'"
|
|
)
|
|
else:
|
|
console.print(f"Switched kubectl context to [cyan]{context}[/cyan]")
|
|
else:
|
|
console.print("[yellow]kubectl not found, skipping context switch.[/yellow]")
|
|
|
|
# Update current cluster in config
|
|
config["current"] = name
|
|
_save_clusters(config)
|
|
|
|
ns = info.get("namespace", name)
|
|
console.print(f"Active cluster: [bold green]{name}[/bold green] (namespace: {ns})")
|