diff --git a/CHANGELOG.md b/CHANGELOG.md index a1a343b..04b0595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# v0.8.20-beta — UniFi herstelt zijn MongoDB-gebruiker + +Een eerder geïnitialiseerd Mongo-volume voerde het init-script niet opnieuw uit. +Daardoor bleef UniFi herstarten met `UserNotFound: unifi@unifi` en kwam de +webinterface nooit online. + +- **Idempotente databasereparatie.** Een korte init-container maakt de + UniFi-gebruiker aan of werkt hem bij, ook bij een bestaand Mongo-volume. +- **Oude installaties worden ondersteund.** Zowel de officiële authsource + `admin` als de eerder opgeslagen authsource `unifi` wordt gerepareerd. +- **UniFi wacht op de reparatie.** De applicatie start pas wanneer Mongo gezond + is en de gebruikerscontrole succesvol is afgerond. +- **Geen misleidende poort 8843.** De appkaart toont alleen werkelijk op de host + gepubliceerde poorten; UniFi's ongepubliceerde gastportaalpoort verdwijnt. + # v0.8.19-beta — Mobiele appacties reageren weer In het mobiele actiemenu kregen normale acties zonder expliciete diff --git a/VERSION b/VERSION index 641695e..3c2e9ad 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.19-beta +0.8.20-beta diff --git a/apps/unifi-network/files/compose.yaml b/apps/unifi-network/files/compose.yaml index 29bdb2b..a1d8b98 100644 --- a/apps/unifi-network/files/compose.yaml +++ b/apps/unifi-network/files/compose.yaml @@ -22,6 +22,8 @@ services: depends_on: << service_name >>-mongo: condition: service_healthy + << service_name >>-mongo-init: + condition: service_completed_successfully restart: unless-stopped << service_name >>-mongo: @@ -47,5 +49,25 @@ services: start_period: 30s restart: unless-stopped + << service_name >>-mongo-init: + image: mongo:4.4.29 + container_name: << service_name >>-mongo-init + environment: + - MONGO_INITDB_ROOT_USERNAME=root + - MONGO_INITDB_ROOT_PASSWORD=<< mongo_password >> + - MONGO_USER=unifi + - MONGO_PASS=<< mongo_password >> + - MONGO_HOST=<< service_name >>-mongo + - MONGO_PORT=27017 + - MONGO_DBNAME=unifi + - MONGO_AUTHSOURCE=admin + volumes: + - ./init-mongo.sh:/init-mongo.sh:ro + entrypoint: ["/bin/bash", "/init-mongo.sh"] + depends_on: + << service_name >>-mongo: + condition: service_healthy + restart: "no" + volumes: << service_name >>_mongodata: diff --git a/apps/unifi-network/files/init-mongo.sh b/apps/unifi-network/files/init-mongo.sh index 3785a7d..1debf7a 100755 --- a/apps/unifi-network/files/init-mongo.sh +++ b/apps/unifi-network/files/init-mongo.sh @@ -1,24 +1,49 @@ <% raw %> #!/bin/bash +set -e + if which mongosh > /dev/null 2>&1; then mongo_init_bin="mongosh" else mongo_init_bin="mongo" fi -"${mongo_init_bin}" < diff --git a/server-up/core/docker.py b/server-up/core/docker.py index 3a82081..5133cf7 100644 --- a/server-up/core/docker.py +++ b/server-up/core/docker.py @@ -229,6 +229,26 @@ def has_compose(d: Path) -> bool: return find_compose(d) is not None +def _published_ports(container: dict) -> str: + """Toon alleen echte hostmappings, niet alle EXPOSE-poorten van het image.""" + if "Publishers" not in container: + # Oudere Compose-versies leveren alleen de reeds geformatteerde tekst. + return str(container.get("Ports") or "") + uit = [] + for mapping in container.get("Publishers") or []: + if not isinstance(mapping, dict): + continue + host = mapping.get("PublishedPort") + doel = mapping.get("TargetPort") + if not host or not doel: + continue + protocol = str(mapping.get("Protocol") or "tcp") + tekst = f"{host}->{doel}/{protocol}" + if tekst not in uit: + uit.append(tekst) + return ", ".join(uit) + + def compose_ps(d: Path, name=None) -> list[dict]: f = find_compose(d) if not f: @@ -249,7 +269,7 @@ def compose_ps(d: Path, name=None) -> list[dict]: "service": c.get("Service", ""), "state": c.get("State", ""), "status": c.get("Status", ""), - "ports": c.get("Ports", ""), + "ports": _published_ports(c), "running": c.get("State", "").lower() in ("running", "up"), }) except Exception: diff --git a/tests/test_apps.py b/tests/test_apps.py index c05c1b5..c4da160 100644 --- a/tests/test_apps.py +++ b/tests/test_apps.py @@ -3,6 +3,7 @@ Deze suite draait over de echte templates, niet over voorbeelden. Zo valt een kapot template op vóórdat iemand het probeert te installeren. """ +import subprocess import sys import tempfile from pathlib import Path @@ -384,18 +385,32 @@ def test_unifi_heeft_pi_geschikte_en_correct_geinitialiseerde_mongodb(): script = (tmp / "init-mongo.sh").read_text(encoding="utf-8") mongo = doc["services"]["unifi-mongo"] + reparatie = doc["services"]["unifi-mongo-init"] unifi = doc["services"]["unifi"] assert mongo["image"] == "mongo:4.4.29" assert "./init-mongo.sh:/docker-entrypoint-initdb.d/init-mongo.sh:ro" in mongo["volumes"] assert any(v == "MONGO_AUTHSOURCE=admin" for v in unifi["environment"]) assert any(v == "MONGO_AUTHSOURCE=admin" for v in mongo["environment"]) assert unifi["depends_on"]["unifi-mongo"]["condition"] == "service_healthy" + assert unifi["depends_on"]["unifi-mongo-init"]["condition"] == \ + "service_completed_successfully" + assert reparatie["image"] == "mongo:4.4.29" + assert reparatie["depends_on"]["unifi-mongo"]["condition"] == "service_healthy" + assert reparatie["restart"] == "no" + assert "./init-mongo.sh:/init-mongo.sh:ro" in reparatie["volumes"] + assert any(v == "MONGO_HOST=unifi-mongo" for v in reparatie["environment"]) assert mongo["healthcheck"]["test"][0] == "CMD-SHELL" assert isinstance(mongo["healthcheck"]["test"][1], str) assert "$${MONGO_INITDB_ROOT_PASSWORD}" in mongo["healthcheck"]["test"][1] - assert "db.createUser" in script + assert "createUser" in script assert "clusterMonitor" in script assert 'role: "dbOwner"' in script + assert "getUser" in script and "updateUser" in script + assert 'ensureUser("${MONGO_AUTHSOURCE}")' in script + assert 'ensureUser("${MONGO_DBNAME}")' in script + syntaxis = subprocess.run(["bash", "-n"], input=script, text=True, + capture_output=True) + assert syntaxis.returncode == 0, syntaxis.stderr assert bp.metadata(app)["architectures"] == ["amd64", "arm64"] assert bp.metadata(app)["web"] == {"scheme": "https", "port": 8443} diff --git a/tests/test_containers.py b/tests/test_containers.py index 38b1de7..8885177 100644 --- a/tests/test_containers.py +++ b/tests/test_containers.py @@ -240,3 +240,20 @@ def test_server_up_verwijdert_zichzelf_niet(client, env, monkeypatch): r = client.post(f"/api/containers/{app_mod.CONTAINER_NAME}/remove", headers={"X-CSRF-Token": csrf}) assert r.status_code == 400 + + +def test_compose_ps_toont_alleen_werkelijk_gepubliceerde_poorten(host): + container = { + "Ports": "8443/tcp, 8843/tcp", + "Publishers": [ + {"TargetPort": 8443, "PublishedPort": 8443, "Protocol": "tcp"}, + {"TargetPort": 8443, "PublishedPort": 8443, "Protocol": "tcp"}, + {"TargetPort": 8843, "PublishedPort": 0, "Protocol": "tcp"}, + ], + } + assert host._published_ports(container) == "8443->8443/tcp" + + +def test_compose_ps_valt_bij_oude_compose_terug_op_ports(host): + assert host._published_ports( + {"Ports": "0.0.0.0:8080->80/tcp"}) == "0.0.0.0:8080->80/tcp"