mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 18:34:31 +00:00
UI screens - database.py: fix mode detection to use env_key priority (prod→k8s, service→k3s) so stale DEPLOYMENT_MODE never overrides the user's chosen environment - database.py: Registry status reads ARTIFACT_REGISTRY_AVAILABLE persisted by cluster screen; uses SERVICE_NAMESPACE for Artifact Registry repo name - cluster.py: add Artifact Registry traffic light (amber→green/red) to prod section; _check_artifact_registry_async persists ARTIFACT_REGISTRY_AVAILABLE into Global cfg - cluster.py: re-trigger Artifact Registry check after GKE cluster selection so the light re-evaluates once region is available from KUBECONTEXT - cluster_nodes.py: fix TclError on Python 3.14 — pady=(2,0) tuple → pady=2 scalar - __init__.py: seed knoe-system namespace when saved value is "default", not only when empty - services.py: replace hardcoded "Prole DB" log string with dynamic cnpg_cluster name Core ops - cloudnative_pg.py: replace one-shot Barman plugin retry with 6-attempt loop; first cert-manager/x509 failure triggers rollout restart + 30 s CA propagation wait; subsequent failures back off up to 60 s per attempt - cloudnative_pg.py: TLS CA CN now uses cluster_name instead of hardcoded "Prole CNPG CA" - registry.py, garage_store.py: refactored into per-mode modules (k3d/k3s/k8s registry and garage store, shared _garage_common) Deploy / config - deploy/gcp/gke/knoe-db.yaml: GKE-specific CNPG cluster manifest (rw/ro/r on separate nodes with premium-rwo storage) - etc/init_common_services.sh, modes/k8s/knoe-db/.version: updated for current deploy - kong-deployment.yaml: updated manifest Tests - test_cluster_nodes_render_smoke.py: add pack/grid, winfo_children, winfo_reqheight, update_idletasks, grid_slaves to dummy widgets; monkeypatch tk.Label so CNPG placement render completes without a real Tkinter root Co-authored-by: Junie <junie@jetbrains.com>
149 lines
4.3 KiB
Python
149 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
import knoe.ui.screens.cluster_nodes as cluster_nodes
|
|
from knoe.ui.screens.cluster_nodes import ClusterNodesScreenMixin
|
|
|
|
|
|
def test_render_cluster_nodes_page_topology_messages_are_scrollable(monkeypatch):
|
|
class _DummyWidget:
|
|
def __init__(self, *_args, **_kwargs):
|
|
pass
|
|
|
|
def destroy(self):
|
|
return None
|
|
|
|
def place_forget(self):
|
|
return None
|
|
|
|
def configure(self, **_kwargs):
|
|
return None
|
|
|
|
def pack(self, *_args, **_kwargs):
|
|
return None
|
|
|
|
def grid(self, *_args, **_kwargs):
|
|
return None
|
|
|
|
class _DummyFrame(_DummyWidget):
|
|
def winfo_children(self):
|
|
return [_DummyWidget()]
|
|
|
|
def winfo_reqheight(self):
|
|
return 0
|
|
|
|
def update_idletasks(self):
|
|
return None
|
|
|
|
def grid_slaves(self, *_args, **_kwargs):
|
|
return [_DummyWidget()]
|
|
|
|
class _DummyScrollbar(_DummyWidget):
|
|
instances: list["_DummyScrollbar"] = []
|
|
|
|
def __init__(self, *_args, **kwargs):
|
|
super().__init__()
|
|
self.command = kwargs.get("command")
|
|
_DummyScrollbar.instances.append(self)
|
|
|
|
def pack(self, *_args, **_kwargs):
|
|
return None
|
|
|
|
def set(self, *_args, **_kwargs):
|
|
return None
|
|
|
|
def configure(self, **kwargs):
|
|
if "command" in kwargs:
|
|
self.command = kwargs["command"]
|
|
|
|
class _DummyText(_DummyWidget):
|
|
instances: list["_DummyText"] = []
|
|
|
|
def __init__(self, *_args, **kwargs):
|
|
super().__init__()
|
|
self.yscrollcommand = kwargs.get("yscrollcommand")
|
|
self.contents = ""
|
|
self.state = "normal"
|
|
_DummyText.instances.append(self)
|
|
|
|
def pack(self, *_args, **_kwargs):
|
|
return None
|
|
|
|
def insert(self, _idx: str, text: str):
|
|
self.contents += text
|
|
|
|
def see(self, *_args, **_kwargs):
|
|
return None
|
|
|
|
def configure(self, **kwargs):
|
|
if "state" in kwargs:
|
|
self.state = kwargs["state"]
|
|
|
|
def yview(self, *_args, **_kwargs):
|
|
return None
|
|
|
|
monkeypatch.setattr(cluster_nodes.tk, "Frame", _DummyFrame)
|
|
monkeypatch.setattr(cluster_nodes.tk, "Scrollbar", _DummyScrollbar)
|
|
monkeypatch.setattr(cluster_nodes.tk, "Text", _DummyText)
|
|
monkeypatch.setattr(cluster_nodes.tk, "Button", _DummyWidget)
|
|
monkeypatch.setattr(cluster_nodes.tk, "Label", _DummyWidget)
|
|
|
|
class _DummyCanvas:
|
|
def __init__(self):
|
|
self._next_id = 1
|
|
|
|
def create_text(self, *_args, **_kwargs):
|
|
item = self._next_id
|
|
self._next_id += 1
|
|
return item
|
|
|
|
def create_window(self, *_args, **_kwargs):
|
|
item = self._next_id
|
|
self._next_id += 1
|
|
return item
|
|
|
|
class _DummyApp(ClusterNodesScreenMixin):
|
|
def __init__(self):
|
|
self.bg_canvas = _DummyCanvas()
|
|
self._canvas_items: list[int] = []
|
|
self._overlay_widgets: list[object] = []
|
|
self._cluster_nodes_topology_state = {
|
|
"phase": "complete",
|
|
"messages": [f"line-{i}" for i in range(1, 7)],
|
|
"topology": None,
|
|
"topology_path": "",
|
|
"selected": set(),
|
|
"busy": True,
|
|
"expanded": None,
|
|
}
|
|
|
|
def _clear_canvas_page(self):
|
|
return None
|
|
|
|
def _should_show_cluster_nodes_screen(self):
|
|
return True
|
|
|
|
def _render_title(self, *_args, **_kwargs):
|
|
return None
|
|
|
|
def _render_paragraph(self, *_args, **_kwargs):
|
|
return None
|
|
|
|
def _cluster_nodes_start_discovery(self, force: bool = False):
|
|
return None
|
|
|
|
def _cluster_nodes_render_summary_cards(self, _topology, _x, y):
|
|
return y
|
|
|
|
def _cluster_nodes_render_table(self, _topology, _x, y):
|
|
return y
|
|
|
|
app = _DummyApp()
|
|
app._render_cluster_nodes_page()
|
|
|
|
assert _DummyScrollbar.instances
|
|
assert _DummyText.instances
|
|
assert _DummyText.instances[0].state == "disabled"
|
|
assert "line-1" in _DummyText.instances[0].contents
|
|
assert "line-6" in _DummyText.instances[0].contents
|
|
assert _DummyScrollbar.instances[0].command is not None
|