From d496adad09ac31df813be563c7b82e378d6f0f5a Mon Sep 17 00:00:00 2001 From: Thomas Stallinger Date: Tue, 1 Sep 2026 16:52:15 +0200 Subject: [PATCH] installer: Installations-Meilensteine/Fehlschlaege an anode melden Neue scripts/lib/reporting.sh (tuxflotte_report_installation_event()) meldet best-effort an POST /api/v1/devices/{device_id}/installation-events (device_fingerprint-authentifiziert, siehe provisioning-server-Commit 51fbf0f) - ein Fehlschlag beim Melden selbst darf die eigentliche Installation nie aufhalten. Vier Meldepunkte: - "activation_confirmed" direkt nach erfolgreichem Provisioning-Handshake (15_server_handshake.sh) - "installation_started" direkt nach bestaetigtem Commit Point (installer.sh) - "installation_completed" ganz am Ende von backend_postinstall(), vor dem finalen Reboot (backend.sh) - "installation_failed" zentral in boot-autostart.sh (einziger Ort, an dem der echte Exit-Code von installer.sh bekannt ist, ohne jedes Modul einzeln instrumentieren zu muessen) - Detail = letzte 40 Logzeilen, enthalten bereits die eigentliche [FEHLER]-Meldung des gescheiterten Moduls. Nur bei STATUS != 0, nicht bei einem kontrollierten Abbruch an einem Commit-Gate (STATUS = 0). Nutzer-Wunsch (01.09.2026, nach dem ersten echten Hardware-Test): Fehlschlaege waehrend der Installation sichtbar machen, ohne am Bildschirm mitschreiben zu muessen ("das ist kein Flow... fuer die Weiterentwicklung eines irgendwann fehlerfreien Betriebs sind fuer uns diese Meldungen Gold wert"). Live in QEMU end-to-end verifiziert: alle drei Erfolgs-Meilensteine korrekt in device_installation_events angekommen (activation_confirmed, installation_started, installation_completed), vollstaendiger Durchlauf bis zum echten Mint-Cinnamon-Login-Bildschirm nach Reboot bestaetigt. --- backends/mint-image/backend.sh | 8 +++ .../tuxflotte/backends/mint-image/backend.sh | 8 +++ .../opt/tuxflotte/scripts/boot-autostart.sh | 13 ++++ .../opt/tuxflotte/scripts/installer.sh | 2 + .../opt/tuxflotte/scripts/lib/reporting.sh | 61 +++++++++++++++++++ .../scripts/modules/15_server_handshake.sh | 6 ++ scripts/boot-autostart.sh | 13 ++++ scripts/installer.sh | 2 + scripts/lib/reporting.sh | 61 +++++++++++++++++++ scripts/modules/15_server_handshake.sh | 6 ++ 10 files changed, 180 insertions(+) create mode 100644 boot-medium/config/includes.chroot/opt/tuxflotte/scripts/lib/reporting.sh create mode 100644 scripts/lib/reporting.sh diff --git a/backends/mint-image/backend.sh b/backends/mint-image/backend.sh index 64d339b..1060c5d 100644 --- a/backends/mint-image/backend.sh +++ b/backends/mint-image/backend.sh @@ -22,6 +22,7 @@ REPO_DIR="$(cd "${BACKEND_DIR}/../.." && pwd)" POSTINSTALL_SCRIPT="${BACKEND_DIR}/postinstall.sh" IMAGE_DEPLOY_LIB="${REPO_DIR}/scripts/lib/image_deploy.sh" BOOT_MEDIUM_LIB="${REPO_DIR}/scripts/lib/boot_medium.sh" +REPORTING_LIB="${REPO_DIR}/scripts/lib/reporting.sh" # Vom Netzwerkmodul geschuetzt abgelegtes WLAN-/Ethernet-Verbindungsprofil # (siehe export_connection_profile() in 05_network.sh) - existiert nur, @@ -108,6 +109,11 @@ backend_init() { # shellcheck source=../../scripts/lib/boot_medium.sh source "${BOOT_MEDIUM_LIB}" + [[ -r "${REPORTING_LIB}" ]] || + { backend_fatal "Reporting-Bibliothek nicht gefunden: ${REPORTING_LIB}"; return 1; } + # shellcheck source=../../scripts/lib/reporting.sh + source "${REPORTING_LIB}" + [[ -r "${POSTINSTALL_SCRIPT}" ]] || { backend_fatal "Postinstall-Skript nicht gefunden: ${POSTINSTALL_SCRIPT}"; return 1; } @@ -334,6 +340,8 @@ backend_postinstall() { umount --recursive "${TARGET_DIR}" || { backend_fatal "${TARGET_DIR} konnte nicht ausgehängt werden."; return 1; } + tuxflotte_report_installation_event "installation_completed" + # Nutzer-Feedback (01.09.2026, echter Hardware-Test): akustischer Hinweis # + klare Ansage, dass der USB-Stick jetzt gefahrlos gezogen werden kann # (ab hier wird nur noch von der Zielplatte gebootet) - Bestaetigung mit diff --git a/boot-medium/config/includes.chroot/opt/tuxflotte/backends/mint-image/backend.sh b/boot-medium/config/includes.chroot/opt/tuxflotte/backends/mint-image/backend.sh index 64d339b..1060c5d 100644 --- a/boot-medium/config/includes.chroot/opt/tuxflotte/backends/mint-image/backend.sh +++ b/boot-medium/config/includes.chroot/opt/tuxflotte/backends/mint-image/backend.sh @@ -22,6 +22,7 @@ REPO_DIR="$(cd "${BACKEND_DIR}/../.." && pwd)" POSTINSTALL_SCRIPT="${BACKEND_DIR}/postinstall.sh" IMAGE_DEPLOY_LIB="${REPO_DIR}/scripts/lib/image_deploy.sh" BOOT_MEDIUM_LIB="${REPO_DIR}/scripts/lib/boot_medium.sh" +REPORTING_LIB="${REPO_DIR}/scripts/lib/reporting.sh" # Vom Netzwerkmodul geschuetzt abgelegtes WLAN-/Ethernet-Verbindungsprofil # (siehe export_connection_profile() in 05_network.sh) - existiert nur, @@ -108,6 +109,11 @@ backend_init() { # shellcheck source=../../scripts/lib/boot_medium.sh source "${BOOT_MEDIUM_LIB}" + [[ -r "${REPORTING_LIB}" ]] || + { backend_fatal "Reporting-Bibliothek nicht gefunden: ${REPORTING_LIB}"; return 1; } + # shellcheck source=../../scripts/lib/reporting.sh + source "${REPORTING_LIB}" + [[ -r "${POSTINSTALL_SCRIPT}" ]] || { backend_fatal "Postinstall-Skript nicht gefunden: ${POSTINSTALL_SCRIPT}"; return 1; } @@ -334,6 +340,8 @@ backend_postinstall() { umount --recursive "${TARGET_DIR}" || { backend_fatal "${TARGET_DIR} konnte nicht ausgehängt werden."; return 1; } + tuxflotte_report_installation_event "installation_completed" + # Nutzer-Feedback (01.09.2026, echter Hardware-Test): akustischer Hinweis # + klare Ansage, dass der USB-Stick jetzt gefahrlos gezogen werden kann # (ab hier wird nur noch von der Zielplatte gebootet) - Bestaetigung mit diff --git a/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/boot-autostart.sh b/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/boot-autostart.sh index 3870e9f..78e942c 100755 --- a/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/boot-autostart.sh +++ b/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/boot-autostart.sh @@ -44,6 +44,19 @@ export TUXFLOTTE_LOG_FILE="${LOG_FILE}" /opt/tuxflotte/scripts/installer.sh 2>&1 | tee -a "${LOG_FILE}" STATUS="${PIPESTATUS[0]}" +# Nutzer-Wunsch (01.09.2026): Fehlschlaege sollen sich melden, ohne dass am +# Bildschirm mitgeschrieben werden muss ("Gold wert" fuer die eigene +# Fehlersuche). Nur bei echtem Fehlschlag (STATUS != 0) - ein kontrollierter +# Abbruch an einem Commit-Gate (Nutzer sagt "N") beendet installer.sh +# ebenfalls, aber mit "exit 0", das ist kein Fehler. Detail = die letzten +# Logzeilen (enthalten bereits die eigentliche [FEHLER]-Meldung des +# gescheiterten Moduls) - best effort, siehe reporting.sh. +if [[ "${STATUS}" -ne 0 ]]; then + # shellcheck source=lib/reporting.sh + source /opt/tuxflotte/scripts/lib/reporting.sh + tuxflotte_report_installation_event "installation_failed" "$(tail -n 40 "${LOG_FILE}" 2>/dev/null)" +fi + # Live gefunden (31.08.2026): ein ExecStopPost, der beim Dienstende # getty@tty1.service neu startet, ist unzuverlaessig (Race mit dem eigenen # TTY-Teardown dieses Dienstes - der Job wurde angestossen, blieb aber diff --git a/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/installer.sh b/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/installer.sh index e4ed7d7..1b6d590 100755 --- a/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/installer.sh +++ b/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/installer.sh @@ -8,6 +8,7 @@ source "$SCRIPT_DIR/lib/logging.sh" source "$SCRIPT_DIR/lib/errors.sh" source "$SCRIPT_DIR/lib/utils.sh" source "$SCRIPT_DIR/lib/checks.sh" +source "$SCRIPT_DIR/lib/reporting.sh" DRY_RUN=false @@ -65,6 +66,7 @@ source "$INSTALLATION_STATE_FILE" case "${TUXFLOTTE_INSTALLATION_CONFIRMED:-}" in true) log_info "Commit Point bestätigt. Installationsphase wird fortgesetzt." + tuxflotte_report_installation_event "installation_started" ;; false) log_info "Provisionierung wurde durch den Benutzer kontrolliert beendet." diff --git a/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/lib/reporting.sh b/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/lib/reporting.sh new file mode 100644 index 0000000..7b7e700 --- /dev/null +++ b/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/lib/reporting.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +# Meldet Installations-Meilensteine/Fehlschlaege an anode (device_installation_events, +# siehe Migration 0025 in provisioning-server) - ausschliesslich fuer die eigene +# Fehlersuche/Betriebsentwicklung gedacht ("Gold wert" fuer den Support, Nutzer-Wunsch +# 01.09.2026 nach dem ersten echten Hardware-Test), noch NICHT zwangslaeufig auf der +# Kunden-Oberflaeche sichtbar - das ist eine separate, noch offene Entscheidung. +# +# Rein melde-basiert und best effort: das Geraet hat waehrend der Installation noch +# kein agent_secret (das entsteht erst ganz am Ende in backend_postinstall()), deshalb +# device_fingerprint als Nachweis statt eines Service-Tokens (siehe +# InstallationEventRequest/device_fingerprint_matches() serverseitig - gleiches +# Vertrauensniveau wie /api/v1/activate selbst). Ein Fehlschlag beim Melden selbst +# (Netzwerk gerade weg, Server nicht erreichbar, Geraet noch nicht aktiviert) darf die +# eigentliche Installation NIE aufhalten - deshalb "|| true" ueberall, kein "set -e"- +# Abbruch moeglich, keine Rueckgabewert-Pruefung durch die Aufrufer noetig. +tuxflotte_report_installation_event() { + local event_type="$1" + local detail="${2:-}" + + local response_file="/run/tuxflotte/server/response.json" + local hardware_file="/run/tuxflotte/hardware/hardware.json" + local network_state="/run/tuxflotte/network/state.env" + + [[ -r "${response_file}" && -r "${hardware_file}" && -r "${network_state}" ]] || return 0 + + local device_id device_fingerprint server_url payload + device_id="$(jq --raw-output '.device.id // empty' "${response_file}" 2>/dev/null)" + device_fingerprint="$(jq --raw-output '.identity.device_fingerprint // empty' "${hardware_file}" 2>/dev/null)" + + [[ -n "${device_id}" && -n "${device_fingerprint}" ]] || return 0 + + # shellcheck disable=SC1090 + source "${network_state}" 2>/dev/null || return 0 + server_url="${TUXFLOTTE_SERVER_URL%/health}" + [[ -n "${server_url}" ]] || return 0 + + payload="$( + jq --null-input \ + --arg device_fingerprint "${device_fingerprint}" \ + --arg event_type "${event_type}" \ + --arg detail "${detail}" \ + '{ + device_fingerprint: $device_fingerprint, + event_type: $event_type, + detail: (if $detail == "" then null else $detail end) + }' 2>/dev/null + )" + [[ -n "${payload}" ]] || return 0 + + curl \ + --silent \ + --show-error \ + --fail \ + --location \ + --max-time 5 \ + --header 'Content-Type: application/json' \ + --data-binary "${payload}" \ + "${server_url}/api/v1/devices/${device_id}/installation-events" \ + >/dev/null 2>&1 || true +} diff --git a/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/modules/15_server_handshake.sh b/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/modules/15_server_handshake.sh index ed8bbd0..00a54fe 100755 --- a/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/modules/15_server_handshake.sh +++ b/boot-medium/config/includes.chroot/opt/tuxflotte/scripts/modules/15_server_handshake.sh @@ -4,6 +4,11 @@ set -Eeuo pipefail readonly SCRIPT_NAME="${0##*/}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_DIR +# shellcheck source=../lib/reporting.sh +source "${SCRIPT_DIR}/../lib/reporting.sh" + readonly NETWORK_STATE="/run/tuxflotte/network/state.env" readonly HARDWARE_FILE="/run/tuxflotte/hardware/hardware.json" @@ -121,6 +126,7 @@ send_activation_request() { fatal "Provisioning-Server hat die Aktivierung abgelehnt." log "Provisioning-Handshake erfolgreich abgeschlossen." + tuxflotte_report_installation_event "activation_confirmed" } main() { diff --git a/scripts/boot-autostart.sh b/scripts/boot-autostart.sh index 3870e9f..78e942c 100755 --- a/scripts/boot-autostart.sh +++ b/scripts/boot-autostart.sh @@ -44,6 +44,19 @@ export TUXFLOTTE_LOG_FILE="${LOG_FILE}" /opt/tuxflotte/scripts/installer.sh 2>&1 | tee -a "${LOG_FILE}" STATUS="${PIPESTATUS[0]}" +# Nutzer-Wunsch (01.09.2026): Fehlschlaege sollen sich melden, ohne dass am +# Bildschirm mitgeschrieben werden muss ("Gold wert" fuer die eigene +# Fehlersuche). Nur bei echtem Fehlschlag (STATUS != 0) - ein kontrollierter +# Abbruch an einem Commit-Gate (Nutzer sagt "N") beendet installer.sh +# ebenfalls, aber mit "exit 0", das ist kein Fehler. Detail = die letzten +# Logzeilen (enthalten bereits die eigentliche [FEHLER]-Meldung des +# gescheiterten Moduls) - best effort, siehe reporting.sh. +if [[ "${STATUS}" -ne 0 ]]; then + # shellcheck source=lib/reporting.sh + source /opt/tuxflotte/scripts/lib/reporting.sh + tuxflotte_report_installation_event "installation_failed" "$(tail -n 40 "${LOG_FILE}" 2>/dev/null)" +fi + # Live gefunden (31.08.2026): ein ExecStopPost, der beim Dienstende # getty@tty1.service neu startet, ist unzuverlaessig (Race mit dem eigenen # TTY-Teardown dieses Dienstes - der Job wurde angestossen, blieb aber diff --git a/scripts/installer.sh b/scripts/installer.sh index e4ed7d7..1b6d590 100755 --- a/scripts/installer.sh +++ b/scripts/installer.sh @@ -8,6 +8,7 @@ source "$SCRIPT_DIR/lib/logging.sh" source "$SCRIPT_DIR/lib/errors.sh" source "$SCRIPT_DIR/lib/utils.sh" source "$SCRIPT_DIR/lib/checks.sh" +source "$SCRIPT_DIR/lib/reporting.sh" DRY_RUN=false @@ -65,6 +66,7 @@ source "$INSTALLATION_STATE_FILE" case "${TUXFLOTTE_INSTALLATION_CONFIRMED:-}" in true) log_info "Commit Point bestätigt. Installationsphase wird fortgesetzt." + tuxflotte_report_installation_event "installation_started" ;; false) log_info "Provisionierung wurde durch den Benutzer kontrolliert beendet." diff --git a/scripts/lib/reporting.sh b/scripts/lib/reporting.sh new file mode 100644 index 0000000..7b7e700 --- /dev/null +++ b/scripts/lib/reporting.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +# Meldet Installations-Meilensteine/Fehlschlaege an anode (device_installation_events, +# siehe Migration 0025 in provisioning-server) - ausschliesslich fuer die eigene +# Fehlersuche/Betriebsentwicklung gedacht ("Gold wert" fuer den Support, Nutzer-Wunsch +# 01.09.2026 nach dem ersten echten Hardware-Test), noch NICHT zwangslaeufig auf der +# Kunden-Oberflaeche sichtbar - das ist eine separate, noch offene Entscheidung. +# +# Rein melde-basiert und best effort: das Geraet hat waehrend der Installation noch +# kein agent_secret (das entsteht erst ganz am Ende in backend_postinstall()), deshalb +# device_fingerprint als Nachweis statt eines Service-Tokens (siehe +# InstallationEventRequest/device_fingerprint_matches() serverseitig - gleiches +# Vertrauensniveau wie /api/v1/activate selbst). Ein Fehlschlag beim Melden selbst +# (Netzwerk gerade weg, Server nicht erreichbar, Geraet noch nicht aktiviert) darf die +# eigentliche Installation NIE aufhalten - deshalb "|| true" ueberall, kein "set -e"- +# Abbruch moeglich, keine Rueckgabewert-Pruefung durch die Aufrufer noetig. +tuxflotte_report_installation_event() { + local event_type="$1" + local detail="${2:-}" + + local response_file="/run/tuxflotte/server/response.json" + local hardware_file="/run/tuxflotte/hardware/hardware.json" + local network_state="/run/tuxflotte/network/state.env" + + [[ -r "${response_file}" && -r "${hardware_file}" && -r "${network_state}" ]] || return 0 + + local device_id device_fingerprint server_url payload + device_id="$(jq --raw-output '.device.id // empty' "${response_file}" 2>/dev/null)" + device_fingerprint="$(jq --raw-output '.identity.device_fingerprint // empty' "${hardware_file}" 2>/dev/null)" + + [[ -n "${device_id}" && -n "${device_fingerprint}" ]] || return 0 + + # shellcheck disable=SC1090 + source "${network_state}" 2>/dev/null || return 0 + server_url="${TUXFLOTTE_SERVER_URL%/health}" + [[ -n "${server_url}" ]] || return 0 + + payload="$( + jq --null-input \ + --arg device_fingerprint "${device_fingerprint}" \ + --arg event_type "${event_type}" \ + --arg detail "${detail}" \ + '{ + device_fingerprint: $device_fingerprint, + event_type: $event_type, + detail: (if $detail == "" then null else $detail end) + }' 2>/dev/null + )" + [[ -n "${payload}" ]] || return 0 + + curl \ + --silent \ + --show-error \ + --fail \ + --location \ + --max-time 5 \ + --header 'Content-Type: application/json' \ + --data-binary "${payload}" \ + "${server_url}/api/v1/devices/${device_id}/installation-events" \ + >/dev/null 2>&1 || true +} diff --git a/scripts/modules/15_server_handshake.sh b/scripts/modules/15_server_handshake.sh index ed8bbd0..00a54fe 100755 --- a/scripts/modules/15_server_handshake.sh +++ b/scripts/modules/15_server_handshake.sh @@ -4,6 +4,11 @@ set -Eeuo pipefail readonly SCRIPT_NAME="${0##*/}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_DIR +# shellcheck source=../lib/reporting.sh +source "${SCRIPT_DIR}/../lib/reporting.sh" + readonly NETWORK_STATE="/run/tuxflotte/network/state.env" readonly HARDWARE_FILE="/run/tuxflotte/hardware/hardware.json" @@ -121,6 +126,7 @@ send_activation_request() { fatal "Provisioning-Server hat die Aktivierung abgelehnt." log "Provisioning-Handshake erfolgreich abgeschlossen." + tuxflotte_report_installation_event "activation_confirmed" } main() {