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.
163 lines
4.3 KiB
Bash
Executable File
163 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
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"
|
|
|
|
readonly RUNTIME_DIR="/run/tuxflotte/server"
|
|
readonly ACTIVATION_FILE="${RUNTIME_DIR}/activation.json"
|
|
readonly AUTHORIZATION_FILE="/run/tuxflotte/enrollment/authorization.json"
|
|
readonly RESPONSE_FILE="${RUNTIME_DIR}/response.json"
|
|
|
|
log() {
|
|
printf '[%s] %s\n' "${SCRIPT_NAME}" "$*" >&2
|
|
}
|
|
|
|
fatal() {
|
|
printf '[%s] FEHLER: %s\n' "${SCRIPT_NAME}" "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_root() {
|
|
if [[ "${EUID}" -ne 0 ]]; then
|
|
fatal "Das Server-Handshake-Modul muss als root ausgeführt werden."
|
|
fi
|
|
}
|
|
|
|
prepare_runtime_directory() {
|
|
install -d \
|
|
--mode=0700 \
|
|
--owner=root \
|
|
--group=root \
|
|
"${RUNTIME_DIR}"
|
|
|
|
rm -f -- "${ACTIVATION_FILE}" "${RESPONSE_FILE}"
|
|
}
|
|
|
|
validate_inputs() {
|
|
[[ -r "${NETWORK_STATE}" ]] ||
|
|
fatal "Netzwerkstatus nicht gefunden: ${NETWORK_STATE}"
|
|
|
|
[[ -r "${HARDWARE_FILE}" ]] ||
|
|
fatal "Hardwareinformationen nicht gefunden: ${HARDWARE_FILE}"
|
|
|
|
jq --exit-status . "${HARDWARE_FILE}" >/dev/null ||
|
|
fatal "Hardwaredatei enthält kein gültiges JSON."
|
|
|
|
[[ -r "${AUTHORIZATION_FILE}" ]] ||
|
|
fatal "Enrollment-Autorisierung nicht gefunden: ${AUTHORIZATION_FILE}"
|
|
|
|
jq --exit-status '
|
|
.schema_version == 1
|
|
and .authorization_type == "bootstrap_activation_code"
|
|
and (.activation_code | type == "string")
|
|
and (.activation_code | length > 0)
|
|
' "${AUTHORIZATION_FILE}" >/dev/null ||
|
|
fatal "Enrollment-Autorisierung ist ungültig."
|
|
}
|
|
|
|
build_activation_request() {
|
|
local activation_code="$1"
|
|
local hostname
|
|
local machine_id
|
|
|
|
hostname="$(hostname)"
|
|
machine_id="$(
|
|
jq --raw-output \
|
|
'.identity.machine_id // empty' \
|
|
"${HARDWARE_FILE}"
|
|
)"
|
|
|
|
jq \
|
|
--null-input \
|
|
--arg activation_code "${activation_code}" \
|
|
--arg hostname "${hostname}" \
|
|
--arg machine_id "${machine_id}" \
|
|
--arg client_version "0.1.0" \
|
|
--slurpfile hardware "${HARDWARE_FILE}" \
|
|
'{
|
|
activation_code: $activation_code,
|
|
device_fingerprint: $hardware[0].identity.device_fingerprint,
|
|
hostname: $hostname,
|
|
machine_id: (
|
|
if $machine_id == ""
|
|
then null
|
|
else $machine_id
|
|
end
|
|
),
|
|
client_version: $client_version,
|
|
hardware: $hardware[0]
|
|
}'
|
|
}
|
|
|
|
send_activation_request() {
|
|
local server_url
|
|
|
|
# shellcheck disable=SC1090
|
|
source "${NETWORK_STATE}"
|
|
|
|
server_url="${TUXFLOTTE_SERVER_URL%/health}"
|
|
|
|
curl \
|
|
--silent \
|
|
--show-error \
|
|
--fail \
|
|
--location \
|
|
--header 'Content-Type: application/json' \
|
|
--data-binary "@${ACTIVATION_FILE}" \
|
|
--output "${RESPONSE_FILE}" \
|
|
"${server_url}/api/v1/activate" ||
|
|
fatal "Provisioning-Server konnte nicht erfolgreich kontaktiert werden."
|
|
|
|
chmod 0600 "${RESPONSE_FILE}"
|
|
|
|
jq --exit-status . "${RESPONSE_FILE}" >/dev/null ||
|
|
fatal "Serverantwort enthält kein gültiges JSON."
|
|
|
|
jq --exit-status '.success == true' "${RESPONSE_FILE}" >/dev/null ||
|
|
fatal "Provisioning-Server hat die Aktivierung abgelehnt."
|
|
|
|
log "Provisioning-Handshake erfolgreich abgeschlossen."
|
|
tuxflotte_report_installation_event "activation_confirmed"
|
|
}
|
|
|
|
main() {
|
|
require_root
|
|
prepare_runtime_directory
|
|
validate_inputs
|
|
|
|
log "Eingabedaten für den Provisioning-Handshake sind gültig."
|
|
|
|
local activation_code
|
|
|
|
local activation_code
|
|
|
|
activation_code="$(
|
|
jq --raw-output \
|
|
'.activation_code' \
|
|
"${AUTHORIZATION_FILE}"
|
|
)"
|
|
|
|
build_activation_request "${activation_code}" >"${ACTIVATION_FILE}"
|
|
|
|
chmod 0600 "${ACTIVATION_FILE}"
|
|
|
|
jq --exit-status . "${ACTIVATION_FILE}" >/dev/null ||
|
|
fatal "Aktivierungsrequest enthält kein gültiges JSON."
|
|
|
|
log "Aktivierungsrequest wurde unter ${ACTIVATION_FILE} gespeichert."
|
|
|
|
send_activation_request
|
|
|
|
}
|
|
|
|
main "$@"
|