from pathlib import Path import sys import pytest PROJECT_ROOT = Path(__file__).resolve().parents[2] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from subprocess import CompletedProcess from knoe.core.ops import gke_clusters def test_build_kubectl_env_for_cluster_sets_explicit_role_and_context(): env = gke_clusters.build_kubectl_env_for_cluster( base_env={"EXISTING": "1"}, kubecontext="ctx-app", cluster_name="knoe-dev-0", cluster_role="app", ) assert env["EXISTING"] == "1" assert env["KUBECTL_CONTEXT"] == "ctx-app" assert env["CLUSTER_NAME"] == "knoe-dev-0" assert env["KNOE_CLUSTER_ROLE"] == "app" assert env["KNOE_APP_CLUSTER_NAME"] == "knoe-dev-0" def test_build_kubectl_env_for_cluster_requires_explicit_context_when_missing(): with pytest.raises(ValueError, match="Missing required kube context"): gke_clusters.build_kubectl_env_for_cluster( base_env={}, kubecontext="", cluster_name="knoe-dev-0", cluster_role="app", ) def test_build_kubectl_env_for_cluster_rejects_mismatched_required_role_context(): with pytest.raises(ValueError, match="expected APP_CLUSTER_KUBECONTEXT"): gke_clusters.build_kubectl_env_for_cluster( base_env={"APP_CLUSTER_KUBECONTEXT": "ctx-app"}, kubecontext="ctx-other", cluster_name="knoe-dev-0", cluster_role="app", ) def test_build_kubectl_env_for_cluster_rejects_cross_role_context_usage(): with pytest.raises(ValueError, match="matches DB_CLUSTER_KUBECONTEXT"): gke_clusters.build_kubectl_env_for_cluster( base_env={ "APP_CLUSTER_KUBECONTEXT": "ctx-app", "DB_CLUSTER_KUBECONTEXT": "ctx-db", }, kubecontext="ctx-db", cluster_name="knoe-dev-0", cluster_role="app", ) def test_get_cluster_credentials_builds_expected_gcloud_command(monkeypatch): called = {} def fake_run(cmd, capture_output, text): called["cmd"] = cmd return CompletedProcess(cmd, 0, stdout="", stderr="") monkeypatch.setattr(gke_clusters.subprocess, "run", fake_run) context = gke_clusters.get_cluster_credentials( project_id="proj-1", cluster_name="knoe-cnpg-0", location="us-central1", ) assert context == "gke_proj-1_us-central1_knoe-cnpg-0" assert called["cmd"] == [ "gcloud", "container", "clusters", "get-credentials", "knoe-cnpg-0", "--project", "proj-1", "--region", "us-central1", "--quiet", ] def test_ensure_db_cluster_creates_standard_cluster_with_db_node_labels(monkeypatch): commands = [] def fake_run(cmd, log=None): commands.append(cmd) if cmd[:4] == ["gcloud", "container", "clusters", "describe"]: return CompletedProcess(cmd, 1, stdout="", stderr="not found") return CompletedProcess(cmd, 0, stdout="created", stderr="") monkeypatch.setattr(gke_clusters, "_run", fake_run) spec = gke_clusters.GkeClusterSpec( name="knoe-cnpg-0", mode="standard", location="us-central1", machine_type="e2-small", node_count=1, node_pool_name="cnpg-db-pool", ) gke_clusters.ensure_db_cluster(project_id="proj-1", spec=spec) create_cmd = commands[-1] assert create_cmd[:5] == ["gcloud", "container", "clusters", "create", "knoe-cnpg-0"] assert "--num-nodes" in create_cmd assert create_cmd[create_cmd.index("--num-nodes") + 1] == "1" assert "--node-labels" in create_cmd assert create_cmd[create_cmd.index("--node-labels") + 1] == "workload=db" def test_ensure_db_cluster_existing_cluster_adds_labeled_db_pool(monkeypatch): commands = [] def fake_run(cmd, log=None): commands.append(cmd) if cmd[:4] == ["gcloud", "container", "clusters", "describe"]: return CompletedProcess(cmd, 0, stdout="knoe-cnpg-0", stderr="") if cmd[:4] == ["gcloud", "container", "node-pools", "describe"]: return CompletedProcess(cmd, 1, stdout="", stderr="not found") return CompletedProcess(cmd, 0, stdout="ok", stderr="") monkeypatch.setattr(gke_clusters, "_run", fake_run) spec = gke_clusters.GkeClusterSpec( name="knoe-cnpg-0", mode="standard", location="us-central1-a", machine_type="e2-standard-2", node_count=3, node_pool_name="cnpg-db-pool", ) gke_clusters.ensure_db_cluster(project_id="proj-1", spec=spec) create_pool_cmd = commands[-1] assert create_pool_cmd[:4] == ["gcloud", "container", "node-pools", "create"] assert create_pool_cmd[4] == "cnpg-db-pool" assert "--machine-type" in create_pool_cmd assert create_pool_cmd[create_pool_cmd.index("--machine-type") + 1] == "e2-standard-2" assert "--num-nodes" in create_pool_cmd assert create_pool_cmd[create_pool_cmd.index("--num-nodes") + 1] == "3" assert "--node-labels" in create_pool_cmd assert create_pool_cmd[create_pool_cmd.index("--node-labels") + 1] == "workload=db"