All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 15m31s
- Een backup bevatte alleen de stackmap: compose, .env en metadata. LIBRARY_DIR en de appdata-map staan naast elkaar, geen genestelde mappen, dus je maakte een backup, kreeg een groen vinkje en hield bij terugzetten een lege app over. Een archief heeft nu drie takken: de stackmap, de appdata van die stack, en een dump per database - De dump is geen dubbelop: een tar van een draaiende PostgreSQL is een momentopname van bestanden die tijdens het inpakken veranderden. Bij terugzetten komt de dump erin nadat de container draait, met een wachtlus. Stond de container tijdens de backup uit, dan is dat een waarschuwing in het log en geen stille overslag - Nieuwe instelling BACKUP_APPDATA (standaard aan), uit te zetten bij een mediabibliotheek waar dit in de honderden gigabytes loopt - apps/newt en apps/pangolin. Server Up had al een Pangolin-integratie met een publiceerknop per stack, maar je moest Pangolin zelf elders vandaan halen - Nieuwe stap in de setup-wizard na git: koppelen aan een bestaande Pangolin, hier installeren, of overslaan. De wizard hing zijn afhandeling aan stapnummers, dus met een stap ertussen ging de git-stap mis; dat gaat nu op de naam van de stap - depends_on waarschuwt nu ook op de stackpagina. Haal je Mosquitto later weg, dan viel Zigbee2MQTT stil zonder dat iets zei waarom Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
119 lines
4.7 KiB
Python
119 lines
4.7 KiB
Python
"""Routes rond stacks: containerbeheer en de netwerkkeuze bij installeren."""
|
|
from conftest import login
|
|
|
|
|
|
def test_containers_van_onbekende_stack(client):
|
|
login(client)
|
|
r = client.get("/api/stacks/bestaatniet/containers")
|
|
assert r.status_code == 404
|
|
|
|
|
|
def test_containeractie_alleen_binnen_eigen_stack(client):
|
|
"""De containernaam wordt getoetst aan compose_ps van díé stack, zodat deze
|
|
route nooit een willekeurige container op de host kan raken."""
|
|
csrf = login(client)
|
|
r = client.post("/api/stacks/mijnstack/containers/traefik/stop",
|
|
headers={"X-CSRF-Token": csrf})
|
|
assert r.status_code == 404
|
|
assert "hoort niet bij deze stack" in r.get_json()["msg"]
|
|
|
|
|
|
def test_onbekende_containeractie(client):
|
|
csrf = login(client)
|
|
r = client.post("/api/stacks/mijnstack/containers/web/verwijder",
|
|
headers={"X-CSRF-Token": csrf})
|
|
assert r.status_code == 400
|
|
|
|
|
|
def test_containerlogs_alleen_binnen_eigen_stack(client):
|
|
login(client)
|
|
r = client.get("/api/stacks/mijnstack/containers/vreemde/logs")
|
|
assert r.status_code == 404
|
|
|
|
|
|
def test_install_weigert_onbekend_netwerk(client):
|
|
csrf = login(client)
|
|
r = client.post("/api/store/install",
|
|
json={"stack": "vaultwarden", "repo_id": "server-up",
|
|
"instance": "vw", "network": "bestaatniet"},
|
|
headers={"X-CSRF-Token": csrf})
|
|
assert r.status_code == 400
|
|
assert "niet geconfigureerd" in r.get_json()["msg"]
|
|
|
|
|
|
def test_install_weigert_ip_buiten_subnet(client, env):
|
|
csrf = login(client)
|
|
env["core"].patch({"NETWORKS": [{
|
|
"name": "lan", "driver": "macvlan", "parent": "eth0",
|
|
"subnet": "192.168.1.0/24", "gateway": "192.168.1.1"}]})
|
|
r = client.post("/api/store/install",
|
|
json={"stack": "vaultwarden", "repo_id": "server-up",
|
|
"instance": "vw", "network": "lan", "ip": "10.9.9.9"},
|
|
headers={"X-CSRF-Token": csrf})
|
|
assert r.status_code == 400
|
|
assert "buiten het subnet" in r.get_json()["msg"]
|
|
|
|
|
|
def test_netwerk_toevoegen_valideert(client):
|
|
csrf = login(client)
|
|
r = client.post("/api/networks",
|
|
json={"name": "lan", "driver": "macvlan",
|
|
"parent": "eth0; id", "subnet": "192.168.1.0/24"},
|
|
headers={"X-CSRF-Token": csrf})
|
|
assert r.status_code == 400
|
|
|
|
|
|
def test_netwerk_verwijderen_onbekend(client):
|
|
csrf = login(client)
|
|
r = client.delete("/api/networks/bestaatniet", headers={"X-CSRF-Token": csrf})
|
|
assert r.status_code == 404
|
|
|
|
|
|
def test_gereserveerde_ips_worden_gelezen_uit_stacks(client, env):
|
|
"""Een geïnstalleerde stack houdt zijn adres, ook als hij gestopt is."""
|
|
import json
|
|
login(client)
|
|
stack = env["lib"] / "vaultwarden"
|
|
stack.mkdir()
|
|
(stack / ".serverup.json").write_text(json.dumps({"ip": "192.168.1.241"}))
|
|
import app as app_module
|
|
with app_module.app.test_request_context():
|
|
assert "192.168.1.241" in app_module._reserved_ips()
|
|
assert "192.168.1.241" not in app_module._reserved_ips(exclude_stack="vaultwarden")
|
|
|
|
|
|
def test_stack_meldt_wat_er_niet_meer_draait(client, env, monkeypatch):
|
|
"""De melding bij het installeren was eenmalig. Haal je Mosquitto later weg,
|
|
dan valt Zigbee2MQTT stil zonder dat iets zegt waarom."""
|
|
import json as _json
|
|
from core import docker, git
|
|
login(client)
|
|
|
|
bron = git.cache_dir("server-up") / "apps" / "z2m"
|
|
(bron / "files").mkdir(parents=True)
|
|
(bron / "template.json").write_text(_json.dumps({
|
|
"kind": "compose",
|
|
"metadata": {"name": "Zigbee2MQTT", "description": "d", "tags": ["mqtt"],
|
|
"depends_on": [{"app": "mosquitto", "reason": "broker nodig"}]},
|
|
"variables": [],
|
|
}), encoding="utf-8")
|
|
(bron / "files" / "compose.yaml").write_text("services: {}\n", encoding="utf-8")
|
|
|
|
d = env["lib"] / "z2m"
|
|
d.mkdir(parents=True)
|
|
(d / "docker-compose.yml").write_text("services:\n a:\n image: x\n")
|
|
(d / ".serverup.json").write_text(_json.dumps(
|
|
{"source": "z2m", "repo_id": "server-up"}))
|
|
monkeypatch.setattr(docker, "compose_ps", lambda *a, **k: [])
|
|
|
|
st = {s["name"]: s for s in client.get("/api/stacks").get_json()}
|
|
assert st["z2m"]["depends_missing"] == [
|
|
{"app": "mosquitto", "reason": "broker nodig"}]
|
|
|
|
# Zodra de broker er staat, is de melding weg.
|
|
mq = env["lib"] / "mqtt"
|
|
mq.mkdir()
|
|
(mq / "docker-compose.yml").write_text("services:\n a:\n image: x\n")
|
|
(mq / ".serverup.json").write_text(_json.dumps({"source": "mosquitto"}))
|
|
st = {s["name"]: s for s in client.get("/api/stacks").get_json()}
|
|
assert st["z2m"]["depends_missing"] == []
|