server-up/tests/test_stack_api.py
Ramon 8b29553a71
Some checks failed
Deploy server-up (dev) / deploy (push) Failing after 3s
v0.5.00-beta - containerbeheer, eigen IP-adressen, veiliger installeren
- Beheer per container: de stackkaart klapt uit naar de losse containers, met
  start/stop/herstart, logs en live CPU-/geheugengebruik per container. De
  containernaam wordt getoetst aan compose_ps van díé stack, zodat de route
  geen willekeurige container op de host kan raken.
- Stacks met een eigen IP-adres (macvlan/ipvlan): netwerkbeheer onder
  Instellingen → Netwerken, netwerkkeuze in de installatiemodal met voorstel
  voor het eerstvolgende vrije adres, en automatische omzetting van het
  gerenderde compose-bestand (poortmappings eruit, ipv4_address erin). De
  templates in apps/ blijven ongewijzigd.
- Toegekende IP's worden vastgehouden in .serverup.json en getoond op de
  stackkaart, zodat een volgende installatie ze niet opnieuw uitdeelt.
- Vrije poort voorstellen bij installeren: next_free_port() bestond al maar
  werd nergens gebruikt. Bezette poorten worden in de UI gemeld.
- Genereerknop voor velden die op een geheim wijzen (token/password/secret),
  lokaal gegenereerd via crypto.getRandomValues.
- Compose valideren met `docker compose config` vóór het wegschrijven, zowel
  bij de editor als na het renderen bij installatie. Ontbreekt de compose-CLI,
  dan blokkeert dat een installatie niet.
- Uitloggen in de zijbalk; gebruikersbeheer en wachtwoord wijzigen onder
  Instellingen → Beveiliging.
- docs/netwerken.md (incl. de shim-interface die de host nodig heeft om zijn
  eigen macvlan-containers te bereiken) en docs/beveiliging.md toegevoegd.
- CHANGELOG bijgewerkt; testsuite uitgebreid naar 112 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
2026-07-26 14:51:32 +02:00

82 lines
3.1 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")