fix(unifi): herstel MongoDB-gebruiker
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 5m44s
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 5m44s
This commit is contained in:
parent
da9981378b
commit
6f549357b1
7 changed files with 131 additions and 17 deletions
15
CHANGELOG.md
15
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
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.8.19-beta
|
||||
0.8.20-beta
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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}" <<EOF
|
||||
use ${MONGO_AUTHSOURCE}
|
||||
db.auth("${MONGO_INITDB_ROOT_USERNAME}", "${MONGO_INITDB_ROOT_PASSWORD}")
|
||||
db.createUser({
|
||||
user: "${MONGO_USER}",
|
||||
pwd: "${MONGO_PASS}",
|
||||
roles: [
|
||||
"clusterMonitor",
|
||||
{ db: "${MONGO_DBNAME}", role: "dbOwner" },
|
||||
{ db: "${MONGO_DBNAME}_stat", role: "dbOwner" },
|
||||
{ db: "${MONGO_DBNAME}_audit", role: "dbOwner" },
|
||||
{ db: "${MONGO_DBNAME}_restore", role: "dbOwner" }
|
||||
]
|
||||
})
|
||||
|
||||
mongo_args=()
|
||||
if [[ -n "${MONGO_HOST:-}" ]]; then
|
||||
mongo_args=(
|
||||
--host "${MONGO_HOST}"
|
||||
--port "${MONGO_PORT:-27017}"
|
||||
)
|
||||
fi
|
||||
|
||||
"${mongo_init_bin}" "${mongo_args[@]}" <<EOF
|
||||
const authDb = db.getSiblingDB("${MONGO_AUTHSOURCE}");
|
||||
if (!authDb.auth("${MONGO_INITDB_ROOT_USERNAME}", "${MONGO_INITDB_ROOT_PASSWORD}")) {
|
||||
quit(18);
|
||||
}
|
||||
|
||||
const roles = [
|
||||
{ db: "admin", role: "clusterMonitor" },
|
||||
{ db: "${MONGO_DBNAME}", role: "dbOwner" },
|
||||
{ db: "${MONGO_DBNAME}_stat", role: "dbOwner" },
|
||||
{ db: "${MONGO_DBNAME}_audit", role: "dbOwner" },
|
||||
{ db: "${MONGO_DBNAME}_restore", role: "dbOwner" }
|
||||
];
|
||||
|
||||
function ensureUser(database) {
|
||||
const userDb = db.getSiblingDB(database);
|
||||
const existing = userDb.getUser("${MONGO_USER}");
|
||||
if (existing) {
|
||||
userDb.updateUser("${MONGO_USER}", { pwd: "${MONGO_PASS}", roles: roles });
|
||||
} else {
|
||||
userDb.createUser({ user: "${MONGO_USER}", pwd: "${MONGO_PASS}", roles: roles });
|
||||
}
|
||||
}
|
||||
|
||||
ensureUser("${MONGO_AUTHSOURCE}");
|
||||
if ("${MONGO_AUTHSOURCE}" !== "${MONGO_DBNAME}") {
|
||||
ensureUser("${MONGO_DBNAME}");
|
||||
}
|
||||
EOF
|
||||
<% endraw %>
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in a new issue