fix(milestone): SERVICE_NAMESPACE must not fall back to DB namespace

When SERVICE_NAMESPACE was absent from the config file and OS env,
_get_script_env() fell back to env["NAMESPACE"] — the database namespace
(e.g. knoe-db). InitializationScriptsMilestone then called init_kong.sh
with SERVICE_NAMESPACE=knoe-db, causing Kong to deploy into the DB
namespace and collide with the existing knoe-system/svc-knoe-ingress.

Fix: default to "knoe-system", matching _service_namespace() in actions.py.
Configs that set SERVICE_NAMESPACE explicitly are unaffected.

Add tests/test_deployment_mode_isolation.py to guard against k3d/k3s/gke
mode-specific config bleeding into each other: SERVICE_NAMESPACE fallback,
monitoring storage class separation, Kong gitea host gating, and
_service_namespace() across all three deployment modes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chrisfu 2026-05-03 13:22:12 -07:00
parent 8dcec846cd
commit 4bcd8f846e
2 changed files with 103 additions and 1 deletions

View File

@ -129,7 +129,7 @@ class Milestone(ABC):
if not service_ns:
service_ns = (os.environ.get("SERVICE_NAMESPACE") or "").strip()
if not service_ns:
service_ns = env["NAMESPACE"]
service_ns = "knoe-system"
env["SERVICE_NAMESPACE"] = service_ns
from knoe.core.env import _deployment_mode_from_env

View File

@ -0,0 +1,102 @@
"""Regression tests: k3d / k3s / gke mode-specific changes must not bleed into each other."""
from __future__ import annotations
import pathlib
import tempfile
import pytest
# ---------------------------------------------------------------------------
# 7a — milestone._get_script_env SERVICE_NAMESPACE must not fall back to DB ns
# ---------------------------------------------------------------------------
def test_get_script_env_service_namespace_does_not_fall_back_to_db_ns(monkeypatch):
"""Regression: SERVICE_NAMESPACE must not default to the DB namespace.
milestone._get_script_env formerly fell back to env["NAMESPACE"] (the DB
namespace) when SERVICE_NAMESPACE was absent from config and OS env.
This caused Kong to deploy into the DB namespace and collide with the
already-deployed knoe-system/svc-knoe-ingress.
"""
monkeypatch.delenv("SERVICE_NAMESPACE", raising=False)
class _FakeState:
inputs = {"init_password.db_namespace": "knoe-db", "init_cluster.cluster_env": "dev"}
config_data = {"Global": {}} # no SERVICE_NAMESPACE
from knoe.milestone import Milestone
class _M(Milestone):
def execute(self, state, progress=None):
pass
m = _M.__new__(_M)
env = m._get_script_env(_FakeState())
assert env["SERVICE_NAMESPACE"] != "knoe-db", (
"SERVICE_NAMESPACE must not fall back to the DB namespace"
)
assert env["SERVICE_NAMESPACE"] == "knoe-system"
# ---------------------------------------------------------------------------
# 7b — monitoring values YAML is mode-specific
# ---------------------------------------------------------------------------
def test_monitoring_k3d_values_use_local_path_storage():
from knoe.core.ops.monitoring import _values_yaml_k3d
yaml_str = _values_yaml_k3d("", {})
assert "local-path" in yaml_str
assert "merlin-local-iscsi" not in yaml_str
assert "pi.prole.org" not in yaml_str # no k3s-specific node affinity
def test_monitoring_k3s_values_do_not_use_local_path():
from knoe.core.ops.monitoring import _values_yaml_k3s
yaml_str = _values_yaml_k3s("", {})
assert "merlin-local-iscsi" in yaml_str
assert "local-path" not in yaml_str
# ---------------------------------------------------------------------------
# 7c — init_kong.sh gitea host disabled for k3d and k8s, enabled for k3s
# ---------------------------------------------------------------------------
def test_kong_gitea_host_excluded_for_k3d_and_k8s():
text = pathlib.Path("etc/init_kong.sh").read_text(encoding="utf-8")
# k3d and k8s branches set include_gitea_host=0
assert "include_gitea_host=0" in text
# k3s starts with include_gitea_host=1 (default)
assert "include_gitea_host=1" in text
# The mode guard must mention both k8s and k3d explicitly
assert '"k8s"' in text or "k8s" in text
assert '"k3d"' in text or "k3d" in text
# ---------------------------------------------------------------------------
# 7d — _service_namespace() always returns knoe-system regardless of mode
# ---------------------------------------------------------------------------
@pytest.mark.parametrize("cluster_env,mode", [
("dev", "k3d"),
("service", "k3s"),
("prod", "k8s"),
])
def test_service_namespace_defaults_to_knoe_system_for_all_modes(
cluster_env, mode, monkeypatch
):
monkeypatch.delenv("SERVICE_NAMESPACE", raising=False)
from knoe.core.actions import KnoeConsoleInstaller
from knoe.core.controller import KnoeController
with tempfile.TemporaryDirectory() as td:
c = KnoeController(pathlib.Path(td))
inst = KnoeConsoleInstaller(c)
inst.knoe_cfg_data = {"Global": {}}
inst.inputs["init_cluster.cluster_env"] = cluster_env
assert inst._service_namespace() == "knoe-system", (
f"_service_namespace() returned wrong value for mode={mode}"
)