fix(station): stat-Reihenfolge, Phase nur im Migrationsmodus, eingerueckte Konfig-Werte, kein /tmp-Zugriff im Test
Fixrunde 2 zu Task 4:
- N1 (wichtig): file_mode() in test_cli.sh/test_setup_pi.sh probierte
"stat -f '%Lp'" vor "stat -c '%a'" - auf GNU-coreutils (dem Pi) deutet
"-f" dort "Dateisystemstatus" und liefert Muell statt der Rechte-Zahl,
faellt aber trotzdem in den "||"-Zweig durch, der zusaetzlich "644"
anhaengt. Reihenfolge umgedreht und mit einem per gstat erzwungenen
GNU-Testlauf zusaetzlich abgesichert.
- N2 (wichtig): status zeigte nach einem Wechsel zurueck in den
Rettungsmodus weiterhin die letzte Migrationsphase aus einer liegen
gebliebenen control.json (moeglich seit RuntimeDirectoryPreserve=
restart aus Fixrunde 1). Phase wird jetzt nur noch angezeigt, wenn
Modus tatsaechlich "migration" ist.
- N3 (klein): die Schluesselerkennung in setup_pi.sh war rein
zeilenverankert ("^KEY=") und haette einen eingerueckten Bestandswert
als fehlend gewertet - beim naechsten Sourcen der Konfiguration haette
die zusaetzlich angehaengte Vorlagenzeile das eigene Passwort
ueberschrieben. Muster auf fuehrenden Leerraum/export gehaertet.
- N4 (klein): test_setup_pi.sh liess setup_pi.sh ueber den scp-Stub echte
Dateien im /tmp des Entwicklungsrechners anlegen und wieder loeschen.
Neue Variable NINLIGHT_STAGE_DIR biegt das auf eine Sandbox um.
Alle vier Punkte mit Regressionstests, N2 und N3 zusaetzlich per
Mutationsprobe (Fix zurueckgenommen, Test wird rot) verifiziert.
This commit is contained in:
@@ -373,7 +373,22 @@ PY
|
|||||||
# wer die Phase dort ablesen wuerde, saehe nach einer erfolgreichen Freigabe
|
# wer die Phase dort ablesen wuerde, saehe nach einer erfolgreichen Freigabe
|
||||||
# weiterhin die Phase von vor dem letzten Dienststart und koennte "arm" fuer
|
# weiterhin die Phase von vor dem letzten Dienststart und koennte "arm" fuer
|
||||||
# gescheitert halten oder faelschlich glauben, nichts sei mehr scharf.
|
# gescheitert halten oder faelschlich glauben, nichts sei mehr scharf.
|
||||||
if [ -r "$_control_file" ]; then
|
#
|
||||||
|
# ABER: Phase ist ein Konzept, das nur im Migrationsmodus existiert - control.json
|
||||||
|
# kann von einem frueheren "arm"/"migrate on" liegen bleiben (RuntimeDirectoryPreserve=
|
||||||
|
# restart aus Fixrunde 1, C1 laesst es Neustarts jetzt ausdruecklich ueberleben),
|
||||||
|
# auch nachdem laengst wieder in den Rettungsmodus zurueckgeschaltet wurde -
|
||||||
|
# "recovery on" raeumt die Datei bewusst nicht auf, weil der Dienst sie im
|
||||||
|
# Rettungsmodus ohnehin ignoriert (server.py liest sie nur im Migrationsmodus).
|
||||||
|
# Ohne diese Bedingung wuerde status nach "migrate on --listen; arm X; on" ein
|
||||||
|
# irrefuehrendes "Phase: armed" im Rettungsmodus zeigen (Fixrunde 2, N2) - genau
|
||||||
|
# in der sicherheitsrelevanten Dimension "scharf/nicht scharf", die I1 eigentlich
|
||||||
|
# klarstellen sollte. Deshalb wird control.json nur gelesen, wenn Modus wirklich
|
||||||
|
# "migration" ist; sonst ist die Phase per Definition "-", unabhaengig davon, was
|
||||||
|
# noch in control.json steht.
|
||||||
|
if [ "$_modus" != "migration" ]; then
|
||||||
|
_phase="-"
|
||||||
|
elif [ -r "$_control_file" ]; then
|
||||||
_phase=$(python3 - "$_control_file" <<'PY'
|
_phase=$(python3 - "$_control_file" <<'PY'
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ HERE=$(cd "$(dirname "$0")" && pwd)
|
|||||||
# Stubs statt einen echten Pi laufen kann.
|
# Stubs statt einen echten Pi laufen kann.
|
||||||
SSH_BIN=${NINLIGHT_SSH_BIN:-/usr/bin/ssh}
|
SSH_BIN=${NINLIGHT_SSH_BIN:-/usr/bin/ssh}
|
||||||
SCP_BIN=${NINLIGHT_SCP_BIN:-/usr/bin/scp}
|
SCP_BIN=${NINLIGHT_SCP_BIN:-/usr/bin/scp}
|
||||||
|
# Wo die per scp uebertragenen Dateien auf dem Zielsystem zwischenlanden, bevor
|
||||||
|
# sie an ihren endgueltigen Platz installiert werden. Ueberschreibbar wie die
|
||||||
|
# anderen NINLIGHT_*-Pfade: auf dem Pi bleibt es bei /tmp, ein Test kann hier in
|
||||||
|
# eine Sandbox statt in das echte /tmp des Entwicklungsrechners umleiten
|
||||||
|
# (Fixrunde 2, N4) - sowohl fuer die lokale scp-Zielangabe unten als auch fuer
|
||||||
|
# die REMOTE-Bloecke, die dieselbe Variable (mit demselben Default) referenzieren.
|
||||||
|
STAGE_DIR=${NINLIGHT_STAGE_DIR:-/tmp}
|
||||||
|
|
||||||
echo "== Voraussetzungen auf $HOST pruefen =="
|
echo "== Voraussetzungen auf $HOST pruefen =="
|
||||||
"$SSH_BIN" "$HOST" bash -s <<'REMOTE'
|
"$SSH_BIN" "$HOST" bash -s <<'REMOTE'
|
||||||
@@ -24,14 +31,14 @@ echo " ok"
|
|||||||
REMOTE
|
REMOTE
|
||||||
|
|
||||||
echo "== Konfiguration =="
|
echo "== Konfiguration =="
|
||||||
"$SCP_BIN" -q "$HERE/station.conf.example" "$HOST:/tmp/ninlight-station.conf.example"
|
"$SCP_BIN" -q "$HERE/station.conf.example" "$HOST:$STAGE_DIR/ninlight-station.conf.example"
|
||||||
# CONF ueberschreibbar wie ROOT_DIR/BIN_PATH/UNIT_PATH unten: auf dem Pi bleibt es
|
# CONF ueberschreibbar wie ROOT_DIR/BIN_PATH/UNIT_PATH unten: auf dem Pi bleibt es
|
||||||
# beim Default, ein Test kann hier gegen eine Sandbox-Datei statt gegen echte
|
# beim Default, ein Test kann hier gegen eine Sandbox-Datei statt gegen echte
|
||||||
# Systempfade laufen.
|
# Systempfade laufen.
|
||||||
"$SSH_BIN" "$HOST" bash -s <<'REMOTE'
|
"$SSH_BIN" "$HOST" bash -s <<'REMOTE'
|
||||||
set -e
|
set -e
|
||||||
CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf}
|
CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf}
|
||||||
TEMPLATE=/tmp/ninlight-station.conf.example
|
TEMPLATE=${NINLIGHT_STAGE_DIR:-/tmp}/ninlight-station.conf.example
|
||||||
if [ -f "$CONF" ]; then
|
if [ -f "$CONF" ]; then
|
||||||
# Eine bestehende Konfiguration bleibt bei ihren Werten - nur Schluessel, die
|
# Eine bestehende Konfiguration bleibt bei ihren Werten - nur Schluessel, die
|
||||||
# eine neuere Version des CLI zusaetzlich verlangt (z.B. die Migrations- und
|
# eine neuere Version des CLI zusaetzlich verlangt (z.B. die Migrations- und
|
||||||
@@ -43,7 +50,14 @@ if [ -f "$CONF" ]; then
|
|||||||
ADDED=""
|
ADDED=""
|
||||||
for KEY in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION \
|
for KEY in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION \
|
||||||
MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do
|
MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do
|
||||||
if ! sudo grep -Eq "^${KEY}=" "$CONF"; then
|
# Nicht nur "^KEY=": eine vorhandene Zuweisung mit fuehrendem Leerraum oder
|
||||||
|
# einem "export" davor (beides gueltiges ". datei"-Shell) wuerde sonst als
|
||||||
|
# fehlend gelten, die Vorlagenzeile wird angehaengt, und da das CLI die
|
||||||
|
# Datei per ". $CONF" sourct, gewinnt die LETZTE Zuweisung - ein eigenes
|
||||||
|
# Passwort wuerde so still durch den oeffentlichen Vorlagenwert ersetzt
|
||||||
|
# (Fixrunde 2, N3: genau das "vorhandene Werte nicht ueberschreiben" aus I4,
|
||||||
|
# nur eine Formatierungsvariante weiter).
|
||||||
|
if ! sudo grep -Eq "^[[:space:]]*(export[[:space:]]+)?${KEY}=" "$CONF"; then
|
||||||
LINE=$(grep -E "^${KEY}=" "$TEMPLATE" || true)
|
LINE=$(grep -E "^${KEY}=" "$TEMPLATE" || true)
|
||||||
if [ -n "$LINE" ]; then
|
if [ -n "$LINE" ]; then
|
||||||
printf '%s\n' "$LINE" | sudo tee -a "$CONF" >/dev/null
|
printf '%s\n' "$LINE" | sudo tee -a "$CONF" >/dev/null
|
||||||
@@ -64,21 +78,22 @@ rm -f "$TEMPLATE"
|
|||||||
REMOTE
|
REMOTE
|
||||||
|
|
||||||
echo "== Server, CLI und Unit installieren =="
|
echo "== Server, CLI und Unit installieren =="
|
||||||
"$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/"
|
"$SCP_BIN" -q "$HERE/server.py" "$HERE/products.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:$STAGE_DIR/"
|
||||||
# Pfade als Variablen mit Default (wie in deploy.sh): so kann ein Test die
|
# Pfade als Variablen mit Default (wie in deploy.sh): so kann ein Test die
|
||||||
# Installation gegen ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen
|
# Installation gegen ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen
|
||||||
# lassen.
|
# lassen.
|
||||||
"$SSH_BIN" "$HOST" bash -s <<'REMOTE'
|
"$SSH_BIN" "$HOST" bash -s <<'REMOTE'
|
||||||
set -e
|
set -e
|
||||||
|
STAGE_DIR=${NINLIGHT_STAGE_DIR:-/tmp}
|
||||||
ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station}
|
ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station}
|
||||||
BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station}
|
BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station}
|
||||||
UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service}
|
UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service}
|
||||||
sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images"
|
sudo install -d -m 0755 "$ROOT_DIR" "$ROOT_DIR/images"
|
||||||
sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py"
|
sudo install -m 0755 "$STAGE_DIR/server.py" "$ROOT_DIR/server.py"
|
||||||
sudo install -m 0644 /tmp/products.py "$ROOT_DIR/products.py"
|
sudo install -m 0644 "$STAGE_DIR/products.py" "$ROOT_DIR/products.py"
|
||||||
sudo install -m 0755 /tmp/ninlight-station "$BIN_PATH"
|
sudo install -m 0755 "$STAGE_DIR/ninlight-station" "$BIN_PATH"
|
||||||
sudo install -m 0644 /tmp/ninlight-station.service "$UNIT_PATH"
|
sudo install -m 0644 "$STAGE_DIR/ninlight-station.service" "$UNIT_PATH"
|
||||||
rm -f /tmp/server.py /tmp/products.py /tmp/ninlight-station /tmp/ninlight-station.service
|
rm -f "$STAGE_DIR/server.py" "$STAGE_DIR/products.py" "$STAGE_DIR/ninlight-station" "$STAGE_DIR/ninlight-station.service"
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
echo " installiert"
|
echo " installiert"
|
||||||
REMOTE
|
REMOTE
|
||||||
|
|||||||
@@ -30,10 +30,37 @@ fail() { echo "FEHLER - $1" >&2; FAILED=1; }
|
|||||||
|
|
||||||
file_mode() {
|
file_mode() {
|
||||||
# $1 = Pfad. BSD-stat (macOS) und GNU-stat (Linux/Pi) haben unterschiedliche
|
# $1 = Pfad. BSD-stat (macOS) und GNU-stat (Linux/Pi) haben unterschiedliche
|
||||||
# Flags - hier reicht "irgendeine funktioniert".
|
# Flags - hier reicht "irgendeine funktioniert". Reihenfolge ist wichtig
|
||||||
stat -f '%Lp' "$1" 2>/dev/null || stat -c '%a' "$1" 2>/dev/null
|
# (Fixrunde 2, N1): "stat -f '%Lp' datei" auf GNU-coreutils deutet "-f" als
|
||||||
|
# "Dateisystemstatus" und '%Lp' als ZWEITES Dateiargument - der Aufruf schreibt
|
||||||
|
# mehrere Zeilen Dateisystem-Infos auf stdout und scheitert (kein passendes
|
||||||
|
# zweites File), aber der "||"-Zweig haengt DANACH trotzdem noch "644" an, und
|
||||||
|
# der Vergleich "= 644" schlaegt fehl, obwohl die Rechte stimmen - auf dem Pi
|
||||||
|
# (echtes GNU-coreutils) also immer rot. BSD-"stat -c" dagegen bricht sofort
|
||||||
|
# mit einer Fehlermeldung auf stderr ab, ganz ohne Ausgabe auf stdout - der
|
||||||
|
# GNU-Aufruf muss deshalb ZUERST stehen.
|
||||||
|
stat -c '%a' "$1" 2>/dev/null || stat -f '%Lp' "$1" 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Erzwingt den GNU-Zweig von file_mode() (der auf dem Pi tatsaechlich laeuft),
|
||||||
|
# indem "stat" im PATH auf GNU-coreutils' "gstat" umgebogen wird, falls
|
||||||
|
# vorhanden (macOS mit "brew install coreutils"). Ohne diese Probe wuerde ein
|
||||||
|
# falsch herum sortiertes file_mode() auf dieser Entwicklungsmaschine (BSD-stat
|
||||||
|
# zuerst probiert und erfolgreich) unbemerkt bleiben, obwohl es auf dem Pi
|
||||||
|
# (nur GNU-stat) rot waere - genau der Fehler aus Fixrunde 2, N1.
|
||||||
|
if command -v gstat >/dev/null 2>&1; then
|
||||||
|
GSTAT_CHECK_DIR="$TMPROOT/gstat-check"
|
||||||
|
mkdir -p "$GSTAT_CHECK_DIR/bin"
|
||||||
|
ln -sf "$(command -v gstat)" "$GSTAT_CHECK_DIR/bin/stat"
|
||||||
|
GSTAT_CHECK_FILE="$TMPROOT/gstat-check/probe"
|
||||||
|
: >"$GSTAT_CHECK_FILE"
|
||||||
|
chmod 0644 "$GSTAT_CHECK_FILE"
|
||||||
|
GSTAT_MODE=$(PATH="$GSTAT_CHECK_DIR/bin:$PATH" file_mode "$GSTAT_CHECK_FILE")
|
||||||
|
[ "$GSTAT_MODE" = "644" ] && pass "file_mode() liefert unter erzwungenem GNU-stat korrekt '644'" || fail "file_mode() liefert unter GNU-stat '$GSTAT_MODE' statt '644' - siehe Fixrunde 2, N1"
|
||||||
|
else
|
||||||
|
echo "Hinweis: gstat nicht gefunden - GNU-Zweig von file_mode() wird nur auf dem Pi selbst geprueft" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
# --- Stub-Kommandos fuer nmcli/systemctl/sudo im PATH ---
|
# --- Stub-Kommandos fuer nmcli/systemctl/sudo im PATH ---
|
||||||
# $1 = bindir, $2 = logdatei, $3 = nmcli-Zustandsverzeichnis, $4 = RUN_DIR,
|
# $1 = bindir, $2 = logdatei, $3 = nmcli-Zustandsverzeichnis, $4 = RUN_DIR,
|
||||||
# $5 = Pfad zur systemd-Unit, deren RuntimeDirectoryPreserve-Zeile ueber das
|
# $5 = Pfad zur systemd-Unit, deren RuntimeDirectoryPreserve-Zeile ueber das
|
||||||
@@ -489,6 +516,39 @@ echo "$ERR" | grep -q "unerwartet" && pass "Fehlermeldung nennt den Mismatch" ||
|
|||||||
ACTIVE_AFTER=$(cat "$SBX/nmstate/active")
|
ACTIVE_AFTER=$(cat "$SBX/nmstate/active")
|
||||||
[ -z "$ACTIVE_AFTER" ] && pass "beide Stationsprofile wurden nach dem erkannten Mismatch heruntergefahren" || fail "mindestens ein Profil blieb aktiv: $ACTIVE_AFTER"
|
[ -z "$ACTIVE_AFTER" ] && pass "beide Stationsprofile wurden nach dem erkannten Mismatch heruntergefahren" || fail "mindestens ein Profil blieb aktiv: $ACTIVE_AFTER"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Zusatzfall N2 (Fixrunde 2): 'status' zeigt keine veraltete Migrationsphase mehr, nachdem in den Rettungsmodus zurueckgeschaltet wurde ==="
|
||||||
|
SBX="$TMPROOT/tN2"
|
||||||
|
setup_sandbox "$SBX"
|
||||||
|
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||||
|
run_cli "$SBX" migrate on --listen >/dev/null 2>&1
|
||||||
|
run_cli "$SBX" arm gu10_rgb >/dev/null 2>&1 # control.json bleibt mit phase=armed liegen
|
||||||
|
run_cli "$SBX" on >/dev/null 2>&1 # zurueck in den Rettungsmodus - control.json wird bewusst NICHT aufgeraeumt
|
||||||
|
[ "$(control_phase "$SBX/run/control.json")" = "armed" ] && pass "Vorbedingung: control.json ist tatsaechlich noch mit phase=armed liegen geblieben" || fail "Vorbedingung nicht erfuellt (control.json wurde schon vorher veraendert)"
|
||||||
|
# status.json so, wie sie der Server nach dem Neustart im Rettungsmodus schreiben
|
||||||
|
# wuerde (mode=recovery, keine last_seen/phase-Felder - siehe server.py write_state()).
|
||||||
|
cat >"$SBX/run/status.json" <<'JSON'
|
||||||
|
{"mode": "recovery"}
|
||||||
|
JSON
|
||||||
|
OUT=$(run_cli "$SBX" status 2>&1)
|
||||||
|
echo "$OUT" | grep -qE "Modus[[:space:]]*: recovery" && pass "Modus zeigt korrekt 'recovery'" || fail "Modus falsch: $OUT"
|
||||||
|
echo "$OUT" | grep -qi "armed" && fail "status zeigt weiterhin die veraltete Migrationsphase 'armed' im Rettungsmodus: $OUT" || pass "keine veraltete Migrationsphase 'armed' im Rettungsmodus"
|
||||||
|
echo "$OUT" | grep -qE "Phase[[:space:]]*: -" && pass "Phase zeigt den neutralen Platzhalter '-' ausserhalb des Migrationsmodus" || fail "Phase zeigt keinen neutralen Platzhalter: $OUT"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Zusatzfall N2b (Fixrunde 2): dieselbe veraltete Freigabe, aber ohne aktualisierte status.json (Modus '-') - genau das woertliche Repro aus dem Review ==="
|
||||||
|
SBX="$TMPROOT/tN2b"
|
||||||
|
setup_sandbox "$SBX"
|
||||||
|
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||||
|
run_cli "$SBX" migrate on --listen >/dev/null 2>&1
|
||||||
|
run_cli "$SBX" arm gu10_rgb >/dev/null 2>&1
|
||||||
|
run_cli "$SBX" on >/dev/null 2>&1
|
||||||
|
rm -f "$SBX/run/status.json" # kein Serverprozess in diesem Test aktualisiert sie
|
||||||
|
OUT=$(run_cli "$SBX" status 2>&1)
|
||||||
|
echo "$OUT" | grep -qE "Modus[[:space:]]*: -" && pass "Modus zeigt '-' (keine status.json vorhanden)" || fail "Modus unerwartet: $OUT"
|
||||||
|
echo "$OUT" | grep -qi "armed" && fail "status zeigt 'armed', obwohl der Modus gar nicht bekannt ist: $OUT" || pass "keine 'armed'-Phase bei unbekanntem Modus"
|
||||||
|
echo "$OUT" | grep -qE "Phase[[:space:]]*: -" && pass "Phase zeigt '-' statt einer Vermutung" || fail "Phase zeigt keinen neutralen Platzhalter: $OUT"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
if [ "$FAILED" -eq 0 ]; then
|
if [ "$FAILED" -eq 0 ]; then
|
||||||
echo "Alle Tests bestanden."
|
echo "Alle Tests bestanden."
|
||||||
|
|||||||
@@ -26,6 +26,18 @@ FAILED=0
|
|||||||
pass() { echo "ok - $1"; }
|
pass() { echo "ok - $1"; }
|
||||||
fail() { echo "FEHLER - $1" >&2; FAILED=1; }
|
fail() { echo "FEHLER - $1" >&2; FAILED=1; }
|
||||||
|
|
||||||
|
file_mode() {
|
||||||
|
# $1 = Pfad. GNU-stat (Linux/Pi) muss zuerst probiert werden, nicht BSD-stat
|
||||||
|
# (macOS): "stat -f '%Lp' datei" deutet auf GNU-coreutils "-f" als
|
||||||
|
# "Dateisystemstatus" und '%Lp' als zweites Dateiargument, scheitert dabei
|
||||||
|
# zwar (rc != 0), schreibt aber vorher schon mehrere Zeilen Dateisystem-Infos
|
||||||
|
# auf stdout - der "||"-Zweig haengt danach zusaetzlich "600"/"644" an, und
|
||||||
|
# der Aufrufer sieht Muell statt der reinen Zahl (Fixrunde 2, N1). BSD-"stat
|
||||||
|
# -c" bricht dagegen sofort mit einem Fehler auf stderr ab, ganz ohne
|
||||||
|
# Ausgabe auf stdout.
|
||||||
|
stat -c '%a' "$1" 2>/dev/null || stat -f '%Lp' "$1" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
setup_stubbin() {
|
setup_stubbin() {
|
||||||
local bindir=$1 log=$2
|
local bindir=$1 log=$2
|
||||||
mkdir -p "$bindir"
|
mkdir -p "$bindir"
|
||||||
@@ -100,16 +112,23 @@ setup_state() {
|
|||||||
local state=$1
|
local state=$1
|
||||||
rm -rf "$state"
|
rm -rf "$state"
|
||||||
mkdir -p "$state/etc" "$state/opt" "$state/usr/local/bin" "$state/etc/systemd/system" \
|
mkdir -p "$state/etc" "$state/opt" "$state/usr/local/bin" "$state/etc/systemd/system" \
|
||||||
"$state/etc/NetworkManager/system-connections"
|
"$state/etc/NetworkManager/system-connections" "$state/tmp"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Fuehrt setup_pi.sh mit gestubbtem PATH und auf die Sandbox umgebogenen
|
# Fuehrt setup_pi.sh mit gestubbtem PATH und auf die Sandbox umgebogenen
|
||||||
# Systempfaden aus. $1 = state-Verzeichnis, $2 = stub-bin-Verzeichnis.
|
# Systempfaden aus. $1 = state-Verzeichnis, $2 = stub-bin-Verzeichnis.
|
||||||
|
#
|
||||||
|
# NINLIGHT_STAGE_DIR biegt auch die per scp "uebertragenen" Zwischendateien auf
|
||||||
|
# die Sandbox um (Fixrunde 2, N4): ohne das wuerden scp/ssh-Stub sie unter dem
|
||||||
|
# echten /tmp des Entwicklungsrechners ablegen und am Ende per "rm -f" wieder
|
||||||
|
# loeschen - mit denselben, leicht kollidierenden Namen wie
|
||||||
|
# "ninlight-station.conf.example" oder "server.py".
|
||||||
run_setup() {
|
run_setup() {
|
||||||
local state=$1 stubbin=$2
|
local state=$1 stubbin=$2
|
||||||
PATH="$stubbin:$PATH" \
|
PATH="$stubbin:$PATH" \
|
||||||
NINLIGHT_SSH_BIN="$stubbin/ssh" \
|
NINLIGHT_SSH_BIN="$stubbin/ssh" \
|
||||||
NINLIGHT_SCP_BIN="$stubbin/scp" \
|
NINLIGHT_SCP_BIN="$stubbin/scp" \
|
||||||
|
NINLIGHT_STAGE_DIR="$state/tmp" \
|
||||||
NINLIGHT_STATION_CONF="$state/etc/ninlight-station.conf" \
|
NINLIGHT_STATION_CONF="$state/etc/ninlight-station.conf" \
|
||||||
NINLIGHT_STATION_ROOT="$state/opt/ninlight-station" \
|
NINLIGHT_STATION_ROOT="$state/opt/ninlight-station" \
|
||||||
NINLIGHT_STATION_BIN="$state/usr/local/bin/ninlight-station" \
|
NINLIGHT_STATION_BIN="$state/usr/local/bin/ninlight-station" \
|
||||||
@@ -172,7 +191,7 @@ for KEY in MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do
|
|||||||
fail "Schluessel $KEY fehlt weiterhin in der Konfiguration"
|
fail "Schluessel $KEY fehlt weiterhin in der Konfiguration"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
MODE=$(stat -f '%Lp' "$CONF_OUT" 2>/dev/null || stat -c '%a' "$CONF_OUT" 2>/dev/null)
|
MODE=$(file_mode "$CONF_OUT")
|
||||||
[ "$MODE" = "600" ] && pass "Konfigurationsdatei bleibt 0600" || fail "Konfigurationsdatei hat falsche Rechte: $MODE"
|
[ "$MODE" = "600" ] && pass "Konfigurationsdatei bleibt 0600" || fail "Konfigurationsdatei hat falsche Rechte: $MODE"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
@@ -187,6 +206,47 @@ echo "$OUT" | grep -q "alle Schluessel vorhanden" && pass "meldet 'alle Schluess
|
|||||||
DUP=$(grep -c '^EPD_REVISION=' "$CONF_OUT")
|
DUP=$(grep -c '^EPD_REVISION=' "$CONF_OUT")
|
||||||
[ "$DUP" -eq 1 ] && pass "EPD_REVISION steht nur einmal in der Datei (kein doppeltes Anhaengen)" || fail "EPD_REVISION steht $DUP mal in der Datei"
|
[ "$DUP" -eq 1 ] && pass "EPD_REVISION steht nur einmal in der Datei (kein doppeltes Anhaengen)" || fail "EPD_REVISION steht $DUP mal in der Datei"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Szenario 4 (Fixrunde 2, N3): ein eingerueckter Bestandswert gilt nicht als fehlend ==="
|
||||||
|
STATE="$TMPROOT/s4-state"; STUBBIN="$TMPROOT/bin4"; LOG="$TMPROOT/s4.log"
|
||||||
|
setup_state "$STATE"
|
||||||
|
setup_stubbin "$STUBBIN" "$LOG"
|
||||||
|
# Fuehrendes Leerzeichen vor PSK= - gueltiges ". datei"-Shell, aber eine rein
|
||||||
|
# zeilenverankerte Erkennung ("^PSK=") wuerde das als "fehlt" werten und die
|
||||||
|
# Vorlagenzeile anhaengen. Da das CLI die Datei per ". $CONF" sourct, gewinnt
|
||||||
|
# dann die LETZTE Zuweisung - das eigene Passwort waere durch den oeffentlichen
|
||||||
|
# Default aus der Vorlage ersetzt, ohne dass die Datei das irgendwo anzeigt.
|
||||||
|
cat >"$STATE/etc/ninlight-station.conf" <<'CONF'
|
||||||
|
SSID='eigene-ssid'
|
||||||
|
PSK='eigenes-eingeruecktes-passwort'
|
||||||
|
IMAGE_DIR='/opt/ninlight-station/images'
|
||||||
|
HOME_CONNECTION='preconfigured'
|
||||||
|
AP_CONNECTION='ninlight-recovery'
|
||||||
|
CONF
|
||||||
|
chmod 0600 "$STATE/etc/ninlight-station.conf"
|
||||||
|
: >"$LOG"
|
||||||
|
set +e
|
||||||
|
OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1)
|
||||||
|
RC=$?
|
||||||
|
set -e
|
||||||
|
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT"
|
||||||
|
CONF_OUT4="$STATE/etc/ninlight-station.conf"
|
||||||
|
PSK_LINES=$(grep -cE '^[[:space:]]*PSK=' "$CONF_OUT4")
|
||||||
|
[ "$PSK_LINES" -eq 1 ] && pass "PSK steht weiterhin nur einmal in der Datei (nicht dupliziert)" || fail "PSK steht $PSK_LINES mal in der Datei: $(cat "$CONF_OUT4")"
|
||||||
|
if grep -Fq "eigenes-eingeruecktes-passwort" "$CONF_OUT4"; then
|
||||||
|
pass "der eingerueckte Bestandswert von PSK blieb erhalten"
|
||||||
|
else
|
||||||
|
fail "der eingerueckte Bestandswert von PSK wurde ersetzt: $(cat "$CONF_OUT4")"
|
||||||
|
fi
|
||||||
|
if grep -Fqx "PSK='ninlight-recovery'" "$CONF_OUT4"; then
|
||||||
|
fail "der oeffentliche Vorlagen-Default fuer PSK wurde zusaetzlich angehaengt - eigenes Passwort waere beim Sourcen ueberschrieben worden"
|
||||||
|
else
|
||||||
|
pass "der oeffentliche Vorlagen-Default fuer PSK wurde NICHT angehaengt"
|
||||||
|
fi
|
||||||
|
for KEY in MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do
|
||||||
|
grep -Eq "^${KEY}=" "$CONF_OUT4" && pass "fehlender Schluessel $KEY wurde trotzdem ergaenzt" || fail "Schluessel $KEY fehlt weiterhin"
|
||||||
|
done
|
||||||
|
|
||||||
echo
|
echo
|
||||||
if [ "$FAILED" -eq 0 ]; then
|
if [ "$FAILED" -eq 0 ]; then
|
||||||
echo "Alle Tests bestanden."
|
echo "Alle Tests bestanden."
|
||||||
|
|||||||
Reference in New Issue
Block a user