mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 12:03:59 +00:00
chore: update prole.cfg defaults for dev cluster, refine config overrides
- Updated default paths, namespaces, and cluster names for k3d/dev configuration. - Added `skip_names` set to exclude `prod.cfg` and `gcp.cfg` from override processing. - Refined init parameters for Supabase, Kubernetes, and database deployments to align with dev-specific settings. - Enhanced test coverage for excluded config files (`prod.cfg`, `gcp.cfg`) within `test_prole_conf.py`.
This commit is contained in:
parent
65369811f7
commit
138a01c181
@ -1,18 +1,3 @@
|
||||
# Port mappings for Prole Tools (generated).
|
||||
# Format: key: local=... remote=... ns=... svc=... address=...
|
||||
|
||||
argocd: local=8081 remote=80 ns=argocd svc=argocd-server address=0.0.0.0
|
||||
supabase-studio: local=18080 remote=3000 ns=supabase svc=studio address=0.0.0.0
|
||||
supabase-auth: local=9999 remote=9999 ns=supabase svc=auth address=127.0.0.1
|
||||
supabase-rest: local=3001 remote=3000 ns=supabase svc=rest address=0.0.0.0
|
||||
supabase-realtime: local=4000 remote=4000 ns=supabase svc=realtime address=0.0.0.0
|
||||
gitea-http: local=13000 remote=3000 ns=gitea svc=gitea-http address=0.0.0.0
|
||||
gitea-ssh: local=22 remote=22 ns=gitea svc=gitea-ssh address=0.0.0.0
|
||||
garage: local=3900 remote=3900 ns=knoe-system svc=garage address=0.0.0.0
|
||||
openbao: local=8200 remote=8200 ns=knoe-system svc=openbao address=0.0.0.0
|
||||
opentofu: local=8080 remote=8080 ns=knoe-system svc=opentofu address=0.0.0.0
|
||||
dashboard: local=8443 remote=443 ns=kubernetes-dashboard svc=kubernetes-dashboard-kong-proxy address=127.0.0.1
|
||||
postgres: local=5432 remote=5432 ns=knoe-db svc=knoe-db-rw address=0.0.0.0
|
||||
prometheus: local=9090 remote=9090 ns=monitoring svc=kps-kube-prometheus-stack-prometheus address=127.0.0.1
|
||||
grafana: local=3000 remote=80 ns=monitoring svc=kps-grafana address=0.0.0.0
|
||||
supabase-kong: local=8000 remote=8000 ns=supabase svc=kong address=0.0.0.0
|
||||
|
||||
@ -1049,6 +1049,16 @@ DEPENDENCIES = [
|
||||
"version_cmd": "brew list --versions kubectx 2>/dev/null || brew info kubectx 2>/dev/null | head -n1",
|
||||
"bin": "kubectx",
|
||||
},
|
||||
{
|
||||
"id": "gcloud",
|
||||
"name": "gcloud",
|
||||
"parent": "brew",
|
||||
"description": "Google Cloud SDK CLI for GKE authentication and context setup",
|
||||
"url": "https://cloud.google.com/sdk/docs/install",
|
||||
"install_cmd": "brew install --cask google-cloud-sdk || brew install google-cloud-sdk",
|
||||
"check_cmd": "gcloud --version",
|
||||
"bin": "gcloud",
|
||||
},
|
||||
{
|
||||
"id": "docker",
|
||||
"name": "Docker",
|
||||
@ -1125,6 +1135,13 @@ def _augment_env_for_brew(env: dict | None = None) -> dict:
|
||||
prepend: list[str] = []
|
||||
if bin_dir and bin_dir not in parts:
|
||||
prepend.append(bin_dir)
|
||||
try:
|
||||
sdk_bin_dir = str(prefix / "share" / "google-cloud-sdk" / "bin")
|
||||
if os.path.isdir(sdk_bin_dir) and sdk_bin_dir not in parts:
|
||||
prepend.append(sdk_bin_dir)
|
||||
except Exception:
|
||||
# SDK path is optional
|
||||
pass
|
||||
try:
|
||||
if os.path.isdir(sbin_dir) and sbin_dir not in parts:
|
||||
prepend.append(sbin_dir)
|
||||
|
||||
@ -4699,6 +4699,9 @@ class KnoeConsoleInstaller(KnoeInstaller):
|
||||
self.log(f"[MISSING] {dep['name']}")
|
||||
|
||||
if not missing:
|
||||
if not self._ensure_gcloud_auth_for_k8s():
|
||||
self.prole_cfg_data["Dependencies"]["STATUS"] = "Missing"
|
||||
return False
|
||||
self.prole_cfg_data["Dependencies"]["STATUS"] = "All installed"
|
||||
return True
|
||||
|
||||
@ -4736,9 +4739,59 @@ class KnoeConsoleInstaller(KnoeInstaller):
|
||||
self.err(f"[ERROR] Still missing: {', '.join(still_missing)}")
|
||||
self.prole_cfg_data["Dependencies"]["STATUS"] = "Missing"
|
||||
return False
|
||||
if not self._ensure_gcloud_auth_for_k8s():
|
||||
self.prole_cfg_data["Dependencies"]["STATUS"] = "Missing"
|
||||
return False
|
||||
self.prole_cfg_data["Dependencies"]["STATUS"] = "All installed"
|
||||
return True
|
||||
|
||||
def _ensure_gcloud_auth_for_k8s(self) -> bool:
|
||||
if self._deployment_mode() != "k8s":
|
||||
return True
|
||||
|
||||
cmd_base, gcloud_env = inst_config._gcloud_plain(os.environ.copy())
|
||||
token_cmd = list(cmd_base) + ["auth", "print-access-token", "--quiet"]
|
||||
try:
|
||||
token_res = subprocess.run(
|
||||
token_cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=gcloud_env,
|
||||
timeout=20,
|
||||
)
|
||||
if token_res.returncode == 0 and (token_res.stdout or "").strip():
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
interactive = bool(getattr(sys.stdin, "isatty", lambda: False)())
|
||||
if interactive:
|
||||
login_cmd = list(cmd_base) + ["auth", "login", "--no-launch-browser"]
|
||||
self.log(
|
||||
"[ACTION] No active gcloud session found. Starting login flow: "
|
||||
+ " ".join(shlex.quote(part) for part in login_cmd)
|
||||
)
|
||||
try:
|
||||
login_rc = self._run_cmd(login_cmd)
|
||||
if login_rc == 0:
|
||||
token_res = subprocess.run(
|
||||
token_cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=gcloud_env,
|
||||
timeout=20,
|
||||
)
|
||||
if token_res.returncode == 0 and (token_res.stdout or "").strip():
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.err(
|
||||
"[ERROR] gcloud is installed but no active auth session is available for GKE. "
|
||||
"Run `gcloud auth login --no-launch-browser`, complete the URL/code flow on a browser-capable machine, then retry deploy."
|
||||
)
|
||||
return False
|
||||
|
||||
def _step_network_scan(self) -> None:
|
||||
if not self._get_input_bool(
|
||||
"network_scan.run", DEFAULT_ACTION_FLAGS.get("network_scan.run", True)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
@ -68,6 +69,9 @@ class DependenciesMilestone(Milestone):
|
||||
)
|
||||
|
||||
if not missing:
|
||||
if not self._ensure_gcloud_auth_for_k8s(state):
|
||||
self._set_status(state, "Missing")
|
||||
return
|
||||
self._set_status(state, "All installed")
|
||||
if progress:
|
||||
progress("All dependencies installed", 1.0)
|
||||
@ -114,10 +118,61 @@ class DependenciesMilestone(Milestone):
|
||||
self.logger.error(f"Still missing: {', '.join(still_missing)}")
|
||||
self._set_status(state, "Missing")
|
||||
else:
|
||||
if not self._ensure_gcloud_auth_for_k8s(state):
|
||||
self._set_status(state, "Missing")
|
||||
return
|
||||
self._set_status(state, "All installed")
|
||||
if progress:
|
||||
progress("All dependencies installed", 1.0)
|
||||
|
||||
def _ensure_gcloud_auth_for_k8s(self, state: InstallerState) -> bool:
|
||||
cluster_env = str(state.inputs.get("init_cluster.cluster_env", "")).strip().lower()
|
||||
if cluster_env not in {"prod", "production", "k8s"}:
|
||||
return True
|
||||
|
||||
cmd_base, gcloud_env = inst_config._gcloud_plain(os.environ.copy())
|
||||
token_cmd = list(cmd_base) + ["auth", "print-access-token", "--quiet"]
|
||||
try:
|
||||
token_res = subprocess.run(
|
||||
token_cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=gcloud_env,
|
||||
timeout=20,
|
||||
)
|
||||
if token_res.returncode == 0 and (token_res.stdout or "").strip():
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
interactive = bool(getattr(sys.stdin, "isatty", lambda: False)())
|
||||
if interactive:
|
||||
login_cmd = list(cmd_base) + ["auth", "login", "--no-launch-browser"]
|
||||
self.logger.info(
|
||||
"[ACTION] No active gcloud session found. Starting login flow: %s",
|
||||
" ".join(shlex.quote(part) for part in login_cmd),
|
||||
)
|
||||
try:
|
||||
login_rc = self._run_cmd(login_cmd)
|
||||
if login_rc == 0:
|
||||
token_res = subprocess.run(
|
||||
token_cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=gcloud_env,
|
||||
timeout=20,
|
||||
)
|
||||
if token_res.returncode == 0 and (token_res.stdout or "").strip():
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.logger.error(
|
||||
"gcloud is installed but no active auth session is available for GKE. "
|
||||
"Run `gcloud auth login --no-launch-browser` in a terminal, complete the URL/code flow on a browser-capable machine, then retry deploy."
|
||||
)
|
||||
return False
|
||||
|
||||
def _set_status(self, state: InstallerState, status: str):
|
||||
if "Dependencies" not in state.config_data:
|
||||
state.config_data["Dependencies"] = {}
|
||||
|
||||
@ -1013,3 +1013,79 @@ class TestPrepareOpenTofuPipeline:
|
||||
assert inst.prole_cfg_data["Deployment"]["OPENTOFU_PIPELINE_DIR"] == str(
|
||||
expected_dir
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _step_dependencies / gcloud auth preflight
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestStepDependencies:
|
||||
def test_step_dependencies_marks_missing_when_k8s_auth_missing(self, monkeypatch):
|
||||
inst = _TestableSilentInstaller(inputs={"init_cluster.cluster_env": "prod"})
|
||||
inst.dependencies = [{"id": "gcloud", "name": "gcloud"}]
|
||||
|
||||
monkeypatch.setattr(
|
||||
"knoe.core.actions.inst_config.get_dep_info",
|
||||
lambda _dep: (True, "/usr/bin/gcloud", "1.0"),
|
||||
)
|
||||
monkeypatch.setattr(inst, "_ensure_gcloud_auth_for_k8s", lambda: False)
|
||||
|
||||
ok = inst._step_dependencies()
|
||||
|
||||
assert ok is False
|
||||
assert inst.prole_cfg_data["Dependencies"]["STATUS"] == "Missing"
|
||||
|
||||
def test_ensure_gcloud_auth_for_k8s_non_interactive_failure(self, monkeypatch):
|
||||
import knoe.core.actions as actions_mod
|
||||
|
||||
inst = _TestableSilentInstaller(inputs={"init_cluster.cluster_env": "prod"})
|
||||
|
||||
monkeypatch.setattr(
|
||||
actions_mod.inst_config,
|
||||
"_gcloud_plain",
|
||||
lambda _env: (["gcloud"], {"PATH": "x"}),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
actions_mod.subprocess,
|
||||
"run",
|
||||
lambda *_a, **_k: subprocess.CompletedProcess([], 1, stdout="", stderr="no auth"),
|
||||
)
|
||||
monkeypatch.setattr(actions_mod.sys.stdin, "isatty", lambda: False, raising=False)
|
||||
|
||||
errors = []
|
||||
monkeypatch.setattr(inst, "err", lambda msg: errors.append(msg))
|
||||
|
||||
ok = inst._ensure_gcloud_auth_for_k8s()
|
||||
|
||||
assert ok is False
|
||||
assert any("gcloud auth login --no-launch-browser" in msg for msg in errors)
|
||||
|
||||
def test_ensure_gcloud_auth_for_k8s_interactive_login_success(self, monkeypatch):
|
||||
import knoe.core.actions as actions_mod
|
||||
|
||||
inst = _TestableSilentInstaller(inputs={"init_cluster.cluster_env": "prod"})
|
||||
|
||||
monkeypatch.setattr(
|
||||
actions_mod.inst_config,
|
||||
"_gcloud_plain",
|
||||
lambda _env: (["gcloud"], {"PATH": "x"}),
|
||||
)
|
||||
responses = [
|
||||
subprocess.CompletedProcess([], 1, stdout="", stderr="no auth"),
|
||||
subprocess.CompletedProcess([], 0, stdout="tok123\n", stderr=""),
|
||||
]
|
||||
|
||||
def _fake_run(*_a, **_k):
|
||||
return responses.pop(0)
|
||||
|
||||
monkeypatch.setattr(actions_mod.subprocess, "run", _fake_run)
|
||||
monkeypatch.setattr(actions_mod.sys.stdin, "isatty", lambda: True, raising=False)
|
||||
|
||||
login_calls = []
|
||||
monkeypatch.setattr(inst, "_run_cmd", lambda cmd, **_kw: login_calls.append(cmd) or 0)
|
||||
|
||||
ok = inst._ensure_gcloud_auth_for_k8s()
|
||||
|
||||
assert ok is True
|
||||
assert login_calls
|
||||
|
||||
@ -648,6 +648,53 @@ def test_dependencies_install_succeeds_all():
|
||||
assert state.config_data.get("Dependencies", {}).get("STATUS") == "All installed"
|
||||
|
||||
|
||||
def test_dependencies_k8s_fails_without_gcloud_auth_session():
|
||||
state = _make_state(
|
||||
**{
|
||||
"dependencies.auto_install_missing": "True",
|
||||
"init_cluster.cluster_env": "prod",
|
||||
}
|
||||
)
|
||||
milestone = DependenciesMilestone()
|
||||
dep = {"id": "gcloud", "name": "gcloud", "install_cmd": "brew install gcloud"}
|
||||
|
||||
no_token = MagicMock(returncode=1, stdout="", stderr="not logged in")
|
||||
|
||||
with patch("knoe.config.get_dep_info", return_value=(True, "/usr/bin/gcloud", "1.0")), \
|
||||
patch("knoe.config.DEPENDENCIES", [dep]), \
|
||||
patch("knoe.config._gcloud_plain", return_value=(["gcloud"], {"PATH": "x"})), \
|
||||
patch("knoe.core.milestones.subprocess.run", return_value=no_token), \
|
||||
patch("knoe.core.milestones.sys.stdin.isatty", return_value=False):
|
||||
milestone.execute(state)
|
||||
|
||||
assert state.config_data.get("Dependencies", {}).get("STATUS") == "Missing"
|
||||
|
||||
|
||||
def test_dependencies_k8s_interactive_login_recovers_auth_session():
|
||||
state = _make_state(
|
||||
**{
|
||||
"dependencies.auto_install_missing": "True",
|
||||
"init_cluster.cluster_env": "prod",
|
||||
}
|
||||
)
|
||||
milestone = DependenciesMilestone()
|
||||
dep = {"id": "gcloud", "name": "gcloud", "install_cmd": "brew install gcloud"}
|
||||
|
||||
no_token = MagicMock(returncode=1, stdout="", stderr="not logged in")
|
||||
with_token = MagicMock(returncode=0, stdout="tok123\n", stderr="")
|
||||
|
||||
with patch("knoe.config.get_dep_info", return_value=(True, "/usr/bin/gcloud", "1.0")), \
|
||||
patch("knoe.config.DEPENDENCIES", [dep]), \
|
||||
patch("knoe.config._gcloud_plain", return_value=(["gcloud"], {"PATH": "x"})), \
|
||||
patch("knoe.core.milestones.subprocess.run", side_effect=[no_token, with_token]), \
|
||||
patch("knoe.core.milestones.sys.stdin.isatty", return_value=True), \
|
||||
patch.object(milestone, "_run_cmd", return_value=0) as run_cmd_mock:
|
||||
milestone.execute(state)
|
||||
|
||||
assert state.config_data.get("Dependencies", {}).get("STATUS") == "All installed"
|
||||
run_cmd_mock.assert_called_once()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# InitializationScriptsMilestone._regenerate_port_mapping_cfg
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Loading…
Reference in New Issue
Block a user