v0.10.10-beta - drie fouten in het installatiescript
Some checks failed
Deploy server-up (dev) / deploy (push) Failing after 3m28s

- vragen werden bij `curl | sh` niet gesteld en zelf met "ja" beantwoord:
  vraag() keek naar stdin (de pipe) in plaats van naar /dev/tty. Docker werd
  daardoor van get.docker.com gehaald zonder dat het gevraagd was en
  --uninstall brak af zonder bevestiging. Eén terminal_beschikbaar() voor alle
  vijf de plekken
- met --bind <eigen ip> mislukten de healthcheck en het aanmaken van het
  beheerdersaccount, omdat het script altijd 127.0.0.1 vroeg terwijl compose op
  $BIND publiceert; het controleadres volgt nu $BIND, met blokhaken om IPv6
- een mislukte update meldde "Bijgewerkt." met exitcode 0; hij stopt nu met een
  foutmelding, en een verse installatie zegt in het slot eerlijk dat er niet
  geantwoord is
- curl kreeg tijdslimieten bij het pollen, zodat het aantal pogingen weer iets
  betekent; SU_WACHT_POGINGEN maakt het instelbaar
- het slot noemde nog "beheert Docker als root" en toont nu het account

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy
This commit is contained in:
Ramon 2026-08-02 23:28:45 +02:00
parent a7b00fc82b
commit afda654983
5 changed files with 283 additions and 20 deletions

View file

@ -1,3 +1,47 @@
# v0.10.10-beta — Het installatiescript loog op drie plekken
Een controle van het hele installatiepad leverde drie fouten op die alledrie op
`curl … | sh` liggen — de regel die de README noemt.
**Vragen werden niet gesteld en beantwoordden zichzelf met "ja".** `vraag()`
keek naar stdin, en bij `curl … | sh` is dat de pipe. Alle andere prompts in het
script gebruiken `/dev/tty`, dat gewoon bereikbaar is. Gevolg: "Nu installeren
via het officiële script van docker.com?" werd overgeslagen en met ja
beantwoord, `--uninstall` brak je installatie af zonder bevestiging, en de vraag
of je een beheerdersaccount wilde bleef ongesteld terwijl de vervolgprompt
("Gebruikersnaam:") wél verscheen. Er is nu één `terminal_beschikbaar()` die
alle vijf de plekken gebruiken.
**Met een eigen bind-adres mislukte de healthcheck én het beheerdersaccount.**
Compose publiceert op `$BIND`, maar het script vroeg altijd `127.0.0.1`. Met
`--bind 10.0.20.5` luistert daar niets, dus meldde het "reageerde niet binnen
anderhalve minuut" en daarna "Server Up was niet bereikbaar; account niet
aangemaakt" — terwijl alles draaide. Je hield een installatie over zonder
account, precies het venster waar het script voor waarschuwt. Het controleadres
volgt nu `$BIND`, met blokhaken om IPv6.
**Een mislukte update meldde zich als geslaagd.** `wacht_op_gereed || true`
gevolgd door `goed "Bijgewerkt."` maakte een kapotte update niet te
onderscheiden van een goede, ook niet aan de exitcode. Nu stopt hij met een
foutmelding en de weg terug. Bij een verse installatie gaat hij wél door — die
aanwijzingen zijn juist dán nuttig — maar het slot zegt eerlijk dat er niet
geantwoord is en de exitcode is 1.
Verder:
- `curl` kreeg tijdslimieten bij het pollen. Zonder die limieten bleef hij
hangen op een adres waar niets naartoe routeert en betekende het aantal
pogingen niets. Het aantal is nu ook instelbaar met `SU_WACHT_POGINGEN`, voor
trage machines.
- Het slot zei nog "Server Up beheert Docker als root" en noemt nu onder welk
account het draait.
Onderweg gevonden en meteen verholpen: de eerste versie van
`terminal_beschikbaar()` gebruikte `{ : </dev/tty; }`. `:` is een *special
builtin*, en een mislukte redirect daarop beëindigt volgens POSIX de hele shell
— exit 2, zonder melding, nog vóór de eerste stap. Dat brak elke installatie
zonder terminal. De openpoging staat nu in een subshell.
# v0.10.00-beta — Server Up draait niet meer als root # v0.10.00-beta — Server Up draait niet meer als root
Er stond `user: "0:0"` in `docker-compose.yml` en nergens waarom. Dat is nu een Er stond `user: "0:0"` in `docker-compose.yml` en nergens waarom. Dat is nu een

View file

@ -1 +1 @@
0.10.00-beta 0.10.10-beta

View file

@ -62,6 +62,17 @@ schrijven én `docker` aanroepen, dan blijft alles onder je eigen account.
--dry-run Alleen tonen wat er zou gebeuren --dry-run Alleen tonen wat er zou gebeuren
``` ```
Het script vraagt ook wanneer je het met `curl … | sh` draait: het praat met je
terminal, niet met stdin. Wil je écht niets gevraagd krijgen, gebruik dan
`--yes`.
Duurt de eerste start langer dan anderhalve minuut — een trage schijf, een
zwakke machine — zet dan het aantal pogingen hoger:
```bash
SU_WACHT_POGINGEN=120 sh install.sh
```
### Voorbeelden ### Voorbeelden
```bash ```bash

View file

@ -13,7 +13,7 @@
set -eu set -eu
SCRIPT_VERSIE="1.1" SCRIPT_VERSIE="1.2"
GIT_HOST="git.ramonbesselink.nl" GIT_HOST="git.ramonbesselink.nl"
GIT_PAD="bes-r/server-up" GIT_PAD="bes-r/server-up"
BRANCH="main" BRANCH="main"
@ -38,6 +38,9 @@ SU_UID=""
SU_GID="" SU_GID=""
SU_NAAM="" SU_NAAM=""
BASE_DIR="${BASE_DIR:-}" BASE_DIR="${BASE_DIR:-}"
# Heeft de server na het starten daadwerkelijk geantwoord? Bepaalt het slot en
# de exitcode; bij --dry-run is er niets om op te wachten.
GEREED=1
# ── Uitvoer ────────────────────────────────────────────────────────────────── # ── Uitvoer ──────────────────────────────────────────────────────────────────
# Kleuren alleen als we naar een terminal schrijven; in een pipe of logbestand # Kleuren alleen als we naar een terminal schrijven; in een pipe of logbestand
@ -71,10 +74,28 @@ doe() {
fi fi
} }
# Is er een terminal om iets aan te vragen? Bewust /dev/tty en niet stdin: bij
# `curl … | sh` — de manier die de README noemt — is stdin de pipe, terwijl de
# terminal gewoon bereikbaar is. Op stdin testen betekende dat élke vraag
# ongesteld bleef en zichzelf met "ja" beantwoordde: Docker werd van
# get.docker.com gehaald zonder dat het gevraagd was, en --uninstall brak je
# installatie af zonder bevestiging.
#
# Bestaan is niet genoeg: in een container zonder terminal bestaat /dev/tty wel
# maar levert openen "No such device or address".
# De openpoging staat in een subshell, niet tussen accolades: ':' is een
# special builtin, en een mislukte redirect daarop beëindigt volgens POSIX de
# hele shell. In een subshell sneuvelt alleen die subshell.
terminal_beschikbaar() {
[ -e /dev/tty ] || return 1
( : </dev/tty ) 2>/dev/null || return 1
return 0
}
vraag() { vraag() {
# vraag "Vraag?" -> 0 bij ja. Met --yes of zonder terminal: altijd ja. # vraag "Vraag?" -> 0 bij ja. Met --yes of zonder terminal: altijd ja.
[ "$JA" -eq 1 ] && return 0 [ "$JA" -eq 1 ] && return 0
[ -t 0 ] || return 0 terminal_beschikbaar || return 0
printf ' %s [J/n] ' "$1" printf ' %s [J/n] ' "$1"
read -r antwoord </dev/tty || return 0 read -r antwoord </dev/tty || return 0
case "$antwoord" in [nN]*) return 1 ;; *) return 0 ;; esac case "$antwoord" in [nN]*) return 1 ;; *) return 0 ;; esac
@ -161,6 +182,20 @@ geldig_bind() {
} }
geldig_bind "$BIND" || fout "Ongeldig bind-adres: $BIND (verwacht een IP-adres, bijvoorbeeld 127.0.0.1 of 0.0.0.0)" geldig_bind "$BIND" || fout "Ongeldig bind-adres: $BIND (verwacht een IP-adres, bijvoorbeeld 127.0.0.1 of 0.0.0.0)"
# Waar bereiken wíj de server na het starten? Compose publiceert op $BIND, dus
# met een specifiek adres luistert er niets op 127.0.0.1. Daar toch naartoe
# vragen leverde "reageerde niet binnen anderhalve minuut" en een overgeslagen
# beheerdersaccount op, terwijl alles gewoon draaide — precies de installatie
# die daarna zonder account open staat.
probe_adres() {
case "$BIND" in
''|0.0.0.0) printf '127.0.0.1' ;;
::|'[::]') printf '[::1]' ;;
*:*) printf '[%s]' "$BIND" ;; # IPv6 hoort tussen blokhaken
*) printf '%s' "$BIND" ;;
esac
}
# ── Rechten ────────────────────────────────────────────────────────────────── # ── Rechten ──────────────────────────────────────────────────────────────────
# Alleen sudo gebruiken waar het echt nodig is: kan de gebruiker zelf al in de # Alleen sudo gebruiken waar het echt nodig is: kan de gebruiker zelf al in de
# doelmap schrijven en docker aanroepen, dan blijft alles onder zijn eigen account. # doelmap schrijven en docker aanroepen, dan blijft alles onder zijn eigen account.
@ -286,7 +321,7 @@ kies_bind() {
[ "$BIND_EXPLICIET" -eq 1 ] && return 0 [ "$BIND_EXPLICIET" -eq 1 ] && return 0
[ "$JA" -eq 1 ] && return 0 [ "$JA" -eq 1 ] && return 0
[ "$DROOG" -eq 1 ] && return 0 [ "$DROOG" -eq 1 ] && return 0
[ -e /dev/tty ] || return 0 terminal_beschikbaar || return 0
# Installeer je opnieuw over een bestaande map, dan is je huidige keuze het # Installeer je opnieuw over een bestaande map, dan is je huidige keuze het
# uitgangspunt — anders zet enter je server ongemerkt terug op loopback. # uitgangspunt — anders zet enter je server ongemerkt terug op loopback.
@ -420,7 +455,7 @@ kies_gebruiker() {
# zonder dit zou een bestaande installatie elke keer opnieuw vragen. # zonder dit zou een bestaande installatie elke keer opnieuw vragen.
lees_env_terug "$DOEL/.env" lees_env_terug "$DOEL/.env"
[ -n "$SU_UID" ] && return 0 [ -n "$SU_UID" ] && return 0
if [ "$JA" -eq 1 ] || [ ! -e /dev/tty ]; then if [ "$JA" -eq 1 ] || ! terminal_beschikbaar; then
zet_gebruiker serverup zet_gebruiker serverup
return 0 return 0
fi fi
@ -600,14 +635,21 @@ start() {
wacht_op_gereed() { wacht_op_gereed() {
[ "$DROOG" -eq 1 ] && return 0 [ "$DROOG" -eq 1 ] && return 0
# Aantal pogingen is instelbaar: op een trage machine duurt de eerste start
# langer dan anderhalve minuut, en de tests willen niet zo lang wachten.
pogingen="${SU_WACHT_POGINGEN:-45}"
basis="http://$(probe_adres):${POORT}"
stap "Wachten tot Server Up reageert" stap "Wachten tot Server Up reageert"
printf ' ' printf ' '
i=0 i=0
while [ "$i" -lt 45 ]; do # Zonder tijdslimiet blijft curl hangen op een adres waar niets naartoe
if curl -fsS "http://127.0.0.1:${POORT}/healthz" >/dev/null 2>&1; then # routeert, en betekent het aantal pogingen niets meer.
tijden="--connect-timeout 2 --max-time 5"
while [ "$i" -lt "$pogingen" ]; do
if curl -fsS $tijden "${basis}/healthz" >/dev/null 2>&1; then
# Spatie na de dubbele punt toestaan: of json compact is, # Spatie na de dubbele punt toestaan: of json compact is,
# hangt af van de Flask-versie. # hangt af van de Flask-versie.
versie="$(curl -fsS "http://127.0.0.1:${POORT}/healthz" 2>/dev/null \ versie="$(curl -fsS $tijden "${basis}/healthz" 2>/dev/null \
| sed -n 's/.*"version" *: *"\([^"]*\)".*/\1/p')" | sed -n 's/.*"version" *: *"\([^"]*\)".*/\1/p')"
printf '\n' printf '\n'
goed "Server Up${versie:+ }${versie:-} draait" goed "Server Up${versie:+ }${versie:-} draait"
@ -619,7 +661,7 @@ wacht_op_gereed() {
sleep 2 sleep 2
done done
printf '\n' printf '\n'
waarschuw "Server Up reageerde niet binnen anderhalve minuut." waarschuw "Server Up reageerde niet op ${basis}."
log "Bekijk de logs met: $SUDO docker compose -f $DOEL/docker-compose.yml logs --tail=50" log "Bekijk de logs met: $SUDO docker compose -f $DOEL/docker-compose.yml logs --tail=50"
return 1 return 1
} }
@ -681,7 +723,7 @@ bepaal_wachtwoord() {
BEHEERDER_WW="$SU_ADMIN_PASSWORD" BEHEERDER_WW="$SU_ADMIN_PASSWORD"
return 0 return 0
fi fi
if [ "$JA" -eq 0 ] && [ -e /dev/tty ]; then if [ "$JA" -eq 0 ] && terminal_beschikbaar; then
vraag_wachtwoord && return 0 vraag_wachtwoord && return 0
fi fi
BEHEERDER_WW="$(genereer_wachtwoord)" BEHEERDER_WW="$(genereer_wachtwoord)"
@ -696,7 +738,7 @@ vraag_beheerder() {
[ -n "$BEHEERDER" ] && return 0 [ -n "$BEHEERDER" ] && return 0
[ "$JA" -eq 1 ] && return 0 [ "$JA" -eq 1 ] && return 0
[ "$DROOG" -eq 1 ] && return 0 [ "$DROOG" -eq 1 ] && return 0
[ -e /dev/tty ] || return 0 terminal_beschikbaar || return 0
stap "Beheerdersaccount" stap "Beheerdersaccount"
if ! vraag "Nu meteen een beheerdersaccount aanmaken?"; then if ! vraag "Nu meteen een beheerdersaccount aanmaken?"; then
@ -740,9 +782,10 @@ maak_beheerder() {
code="$(printf '{"username":"%s","password":"%s"}' \ code="$(printf '{"username":"%s","password":"%s"}' \
"$(json_tekst "$BEHEERDER")" "$(json_tekst "$BEHEERDER_WW")" \ "$(json_tekst "$BEHEERDER")" "$(json_tekst "$BEHEERDER_WW")" \
| curl -sS -o "$antwoord" -w '%{http_code}' \ | curl -sS -o "$antwoord" -w '%{http_code}' \
--connect-timeout 5 --max-time 20 \
-X POST -H 'Content-Type: application/json' \ -X POST -H 'Content-Type: application/json' \
--data-binary @- \ --data-binary @- \
"http://127.0.0.1:${POORT}/api/auth/setup" 2>/dev/null || true)" "http://$(probe_adres):${POORT}/api/auth/setup" 2>/dev/null || true)"
# Bij een mislukte verbinding schrijft curl zelf al "000"; kan curl helemaal # Bij een mislukte verbinding schrijft curl zelf al "000"; kan curl helemaal
# niet draaien, dan blijft het leeg. Beide gevallen zijn hetzelfde verhaal. # niet draaien, dan blijft het leeg. Beide gevallen zijn hetzelfde verhaal.
case "$code" in ''|*[!0-9]*) code="000" ;; esac case "$code" in ''|*[!0-9]*) code="000" ;; esac
@ -775,9 +818,19 @@ afsluiting() {
fi fi
printf '\n%s────────────────────────────────────────────────────────%s\n' "$C_KOP" "$C_UIT" printf '\n%s────────────────────────────────────────────────────────%s\n' "$C_KOP" "$C_UIT"
printf '%s Server Up draait op %s%s\n' "$C_OK" "$adres" "$C_UIT" if [ "$GEREED" -eq 1 ]; then
printf '%s Server Up draait op %s%s\n' "$C_OK" "$adres" "$C_UIT"
else
printf '%s Server Up antwoordde niet op %s%s\n' "$C_WAARSCHUW" "$adres" "$C_UIT"
fi
printf '%s────────────────────────────────────────────────────────%s\n\n' "$C_KOP" "$C_UIT" printf '%s────────────────────────────────────────────────────────%s\n\n' "$C_KOP" "$C_UIT"
if [ "$GEREED" -eq 0 ]; then
printf ' De container is aangemaakt, maar reageerde nog niet. Kijk wat\n'
printf ' er aan de hand is met:\n'
printf ' %s docker compose -f %s/docker-compose.yml logs --tail=50\n\n' "${SUDO:-}" "$DOEL"
fi
if [ -n "$BEHEERDER" ]; then if [ -n "$BEHEERDER" ]; then
printf ' %s✔%s Inloggen als: %s%s%s\n' "$C_OK" "$C_UIT" "$C_KOP" "$BEHEERDER" "$C_UIT" printf ' %s✔%s Inloggen als: %s%s%s\n' "$C_OK" "$C_UIT" "$C_KOP" "$BEHEERDER" "$C_UIT"
if [ "$WW_GEGENEREERD" -eq 1 ]; then if [ "$WW_GEGENEREERD" -eq 1 ]; then
@ -797,10 +850,12 @@ afsluiting() {
printf ' 2. sed -i "s/^BIND=.*/BIND=0.0.0.0/" %s/.env\n' "$DOEL" printf ' 2. sed -i "s/^BIND=.*/BIND=0.0.0.0/" %s/.env\n' "$DOEL"
printf ' 3. %s docker compose -f %s/docker-compose.yml up -d\n\n' "${SUDO:-}" "$DOEL" printf ' 3. %s docker compose -f %s/docker-compose.yml up -d\n\n' "${SUDO:-}" "$DOEL"
else else
printf ' %sDe interface staat open op je netwerk. Server Up beheert%s\n' "$C_WAARSCHUW" "$C_UIT" printf ' %sDe interface staat open op je netwerk.%s Server Up beheert de\n' "$C_WAARSCHUW" "$C_UIT"
printf ' Docker als root, dus zet er een reverse proxy met TLS voor.\n\n' printf ' Docker-daemon, dus wie erbij kan, kan alles op deze server.\n'
printf ' Zet er een reverse proxy met TLS voor.\n\n'
fi fi
printf ' Draait als: %s\n' "${SU_NAAM:-root}"
printf ' Bijwerken: sh %s/install.sh --update\n' "$DOEL" printf ' Bijwerken: sh %s/install.sh --update\n' "$DOEL"
printf ' Logs: %s docker compose -f %s/docker-compose.yml logs -f\n' "${SUDO:-}" "$DOEL" printf ' Logs: %s docker compose -f %s/docker-compose.yml logs -f\n' "${SUDO:-}" "$DOEL"
printf ' Documentatie: %s/README.md\n\n' "$DOEL" printf ' Documentatie: %s/README.md\n\n' "$DOEL"
@ -854,9 +909,19 @@ case "$ACTIE" in
schrijf_env schrijf_env
zet_mappen_klaar zet_mappen_klaar
start start
wacht_op_gereed || true # Niet wegkijken als hij niet opkomt: "Bijgewerkt." bij een kapotte
printf '\n' # update zag er precies zo uit als een geslaagde, en een script dat dit
goed "Bijgewerkt." # aanroept zag exitcode 0.
if wacht_op_gereed; then
printf '\n'
goed "Bijgewerkt."
else
printf '\n'
log "Terug naar de vorige versie kan met:"
log " cd $DOEL && git log --oneline -5 # kies een eerdere versie"
log " $SUDO docker compose -f $DOEL/docker-compose.yml up -d --build"
fout "Bijwerken mislukt: de nieuwe versie kwam niet omhoog."
fi
;; ;;
create-admin) create-admin)
# Losse actie voor een installatie die al draait: handig als het # Losse actie voor een installatie die al draait: handig als het
@ -865,7 +930,7 @@ case "$ACTIE" in
# en één verzoek naar de draaiende server — dus geen bepaal_rechten. # en één verzoek naar de draaiende server — dus geen bepaal_rechten.
lees_env_terug "$DOEL/.env" lees_env_terug "$DOEL/.env"
if [ -z "$BEHEERDER" ]; then if [ -z "$BEHEERDER" ]; then
if [ -e /dev/tty ] && [ "$JA" -eq 0 ]; then if terminal_beschikbaar && [ "$JA" -eq 0 ]; then
printf '\n Gebruikersnaam: ' printf '\n Gebruikersnaam: '
read -r naam </dev/tty || naam="" read -r naam </dev/tty || naam=""
BEHEERDER="$(printf '%s' "$naam" | tr -d '[:space:]')" BEHEERDER="$(printf '%s' "$naam" | tr -d '[:space:]')"
@ -889,8 +954,11 @@ case "$ACTIE" in
schrijf_env schrijf_env
zet_mappen_klaar zet_mappen_klaar
start start
wacht_op_gereed || true # Hier wél doorgaan: de aanwijzingen aan het eind zijn juist dán nuttig.
# Maar het slot mag niet doen alsof alles goed ging.
if wacht_op_gereed; then GEREED=1; else GEREED=0; fi
maak_beheerder maak_beheerder
afsluiting afsluiting
[ "$GEREED" -eq 1 ] || exit 1
;; ;;
esac esac

View file

@ -614,3 +614,143 @@ def test_appdata_wordt_niet_van_eigenaar_veranderd():
continue continue
if "chown" in kaal: if "chown" in kaal:
assert "appdata" not in kaal, f"appdata wordt gechownd: {kaal}" assert "appdata" not in kaal, f"appdata wordt gechownd: {kaal}"
# ── Waar controleert het script of de server draait? ─────────────────────────
# Compose publiceert op $BIND. Het script vroeg altijd 127.0.0.1, dus met een
# eigen bind-adres meldde het "reageerde niet" en sloeg het het
# beheerdersaccount over — terwijl alles gewoon draaide. Dat laat je installatie
# zonder account open staan, precies waar het script voor waarschuwt.
@pytest.mark.parametrize("bind,verwacht", [
("127.0.0.1", "127.0.0.1"),
("0.0.0.0", "127.0.0.1"), # alle interfaces: loopback voldoet
("10.0.20.5", "10.0.20.5"), # specifiek adres: dáár moet je zijn
("::", "[::1]"),
("2001:db8::1", "[2001:db8::1]"), # IPv6 hoort tussen blokhaken in een URL
])
def test_het_controleadres_volgt_het_bind_adres(bind, verwacht, tmp_path):
uit = _sh(f'BIND="{bind}"\nprobe_adres\nprintf "\\n"\n', tmp_path)
assert uit.strip().splitlines()[-1] == verwacht, uit
def _sh(fragment, tmp_path):
"""Laadt install.sh en draait een fragment; geen terminal in het spel."""
script = tmp_path / "frag.sh"
script.write_text(f"set --\nSU_INSTALL_ALLEEN_FUNCTIES=1 . {SCRIPT}\n{fragment}\n")
r = subprocess.run(["sh", str(script)], capture_output=True, text=True,
timeout=60, stdin=subprocess.DEVNULL)
return r.stdout + r.stderr
def _server(poort_uit, pad="/healthz", inhoud=b'{"version":"9.9.9"}'):
"""Een minimale HTTP-server op 127.0.0.1 die één pad kent."""
import http.server
import threading
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == pad:
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(inhoud)))
self.end_headers()
self.wfile.write(inhoud)
else:
self.send_error(404)
def log_message(self, *a):
pass
srv = http.server.HTTPServer(("127.0.0.1", 0), H)
poort_uit.append(srv.server_address[1])
threading.Thread(target=srv.serve_forever, daemon=True).start()
return srv
def test_wachten_slaagt_zodra_de_server_antwoordt(tmp_path):
poort = []
srv = _server(poort)
try:
uit = _sh(f'DROOG=0; BIND="127.0.0.1"; POORT={poort[0]}; DOEL="{tmp_path}"\n'
'SU_WACHT_POGINGEN=3 wacht_op_gereed && echo "UITKOMST=gereed"\n',
tmp_path)
finally:
srv.shutdown()
assert "UITKOMST=gereed" in uit, uit
assert "9.9.9" in uit, "de versie uit /healthz wordt niet getoond"
def test_wachten_kijkt_naar_het_ingestelde_adres_niet_naar_loopback(tmp_path):
"""De server luistert op 127.0.0.1, maar we doen alsof BIND iets anders is.
Vroeg het script tóch 127.0.0.1, dan zou dit ten onrechte slagen."""
poort = []
srv = _server(poort)
try:
uit = _sh(f'DROOG=0; BIND="10.255.255.1"; POORT={poort[0]}; DOEL="{tmp_path}"\n'
'SU_WACHT_POGINGEN=1 wacht_op_gereed || echo "UITKOMST=niet-gereed"\n',
tmp_path)
finally:
srv.shutdown()
assert "UITKOMST=niet-gereed" in uit, uit
assert "10.255.255.1" in uit, "de melding noemt het gevraagde adres niet"
def test_wachten_faalt_als_er_niets_luistert(tmp_path):
uit = _sh(f'DROOG=0; BIND="127.0.0.1"; POORT=1; DOEL="{tmp_path}"\n'
'SU_WACHT_POGINGEN=1 wacht_op_gereed || echo "UITKOMST=niet-gereed"\n',
tmp_path)
assert "UITKOMST=niet-gereed" in uit, uit
assert "logs --tail" in uit, "geen aanwijzing hoe je verder kijkt"
def test_een_mislukte_update_meldt_zich_als_mislukt():
"""`wacht_op_gereed || true` gevolgd door "Bijgewerkt." maakte een kapotte
update niet te onderscheiden van een geslaagde, ook niet aan de exitcode."""
regels = SCRIPT.read_text(encoding="utf-8").splitlines()
for i, regel in enumerate(regels):
if "wacht_op_gereed" not in regel or regel.strip().startswith("#"):
continue
assert "|| true" not in regel, \
f"regel {i+1} negeert of de server opkwam: {regel.strip()}"
# ── Vragen bij `curl | sh` ───────────────────────────────────────────────────
# De README noemt `curl … | sh` als de manier om te installeren. Daarbij is
# stdin de pipe, niet de terminal. `vraag()` testte op stdin en beantwoordde
# zichzelf dus met "ja": Docker werd van get.docker.com gehaald zonder dat het
# gevraagd was, en --uninstall brak je installatie af zonder bevestiging.
# `</dev/null` bootst de pipe na; /dev/tty is in deze harnas de pseudo-terminal.
PIPE_FRAGMENT = (
'JA=0\n'
'if vraag "Doorgaan?" </dev/null; then echo "UITKOMST=ja"; else echo "UITKOMST=nee"; fi\n'
)
def test_de_vraag_wordt_ook_gesteld_als_stdin_geen_terminal_is(tmp_path):
uit = _in_terminal(PIPE_FRAGMENT, "n\n", tmp_path)
assert "Doorgaan? [J/n]" in uit, f"de vraag is niet eens gesteld: {uit}"
assert "UITKOMST=nee" in uit, uit
def test_ja_blijft_ja(tmp_path):
uit = _in_terminal(PIPE_FRAGMENT, "\n", tmp_path)
assert "UITKOMST=ja" in uit, uit
def test_zonder_enige_terminal_gaat_hij_door(tmp_path):
"""Geen terminal én geen stdin: dan blijft "ja" het antwoord, want anders
kan een onbeheerde installatie nergens meer verder."""
uit = _sh(PIPE_FRAGMENT, tmp_path)
assert "UITKOMST=ja" in uit, uit
def test_zonder_terminal_valt_het_script_niet_om(tmp_path):
"""`{ : </dev/tty; }` lijkt onschuldig, maar ':' is een special builtin en
een mislukte redirect daarop beëindigt volgens POSIX de hele shell exit 2,
zonder melding, nog vóór de eerste stap."""
uit = _sh('if terminal_beschikbaar; then echo "UITKOMST=tty"; '
'else echo "UITKOMST=geen-tty"; fi\necho "EINDE"\n', tmp_path)
assert "UITKOMST=geen-tty" in uit, uit
assert "EINDE" in uit, "het script stopte halverwege"