#!/usr/bin/env bash set -Eeuo pipefail WM_DEPLOY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # shellcheck source=common.sh source "$WM_DEPLOY_DIR/scripts/common.sh" wm_load_env wm_assert_data_mount if [[ "$(id -u)" -ne 0 ]]; then wm_die "Dieses Skript muss als root ausgeführt werden." fi force_rebuild=false force_marine_import=false while [[ "$#" -gt 0 ]]; do case "$1" in --force) force_rebuild=true shift ;; --force-marine) force_marine_import=true shift ;; *) wm_die "Unbekanntes Argument für update-route-data.sh: $1" ;; esac done install -d -m 0755 "$WATERMAPS_RUNTIME_DIR/locks" "$WATERMAPS_DATA_DIR/local" if [[ "${WATERMAPS_ROUTE_LOCK_HELD:-false}" != "true" ]]; then if ! wm_acquire_route_lock; then exit 0 fi fi wm_compose up --detach --no-build postgres wm_wait_for_health postgres 240 route_file="$(wm_route_file)" route_marker="$(wm_route_marker)" route_dir="$(dirname "$route_file")" candidate_file="$route_dir/.germany-netherlands-fairways.candidate.json" candidate_marker="$route_dir/.germany-netherlands-fairways.candidate.ready" backup_file="$route_dir/.germany-netherlands-fairways.previous.json" backup_marker="$route_dir/.germany-netherlands-fairways.previous.ready" transaction_file="$WATERMAPS_RUNTIME_DIR/locks/route-update.transaction" container_candidate="/workspace/data/local/$(basename "$candidate_file")" container_candidate_marker="/workspace/data/local/$(basename "$candidate_marker")" transaction_active=false rollback_restored_previous=false wm_copy_route_atomically() { local source_file="$1" local destination_file="$2" local temporary_file temporary_file="$(mktemp "$(dirname "$destination_file")/.route-copy.XXXXXX")" if ! cp --reflink=auto "$source_file" "$temporary_file"; then rm -f "$temporary_file" return 1 fi chmod 0644 "$temporary_file" mv -f "$temporary_file" "$destination_file" } wm_watermaps_is_running() { [[ -n "$(wm_compose ps --quiet watermaps)" ]] } wm_update_marine_features() { if [[ "$force_marine_import" == "true" ]] || ! wm_marine_features_ready; then wm_log "Häfen, Schleusen und Brücken werden aus den verifizierten Geofabrik-Snapshots importiert." wm_compose --profile maintenance run --rm marine-data else wm_log "Marine-Features in PostGIS sind bereits aktuell." fi wm_assert_marine_features } wm_restore_previous_route() { rollback_restored_previous=false if [[ -s "$backup_file" && -s "$backup_marker" ]] && wm_route_data_files_ready "$backup_file" "$backup_marker"; then if ! wm_copy_route_atomically "$backup_file" "$route_file" || ! wm_write_route_marker "$route_file" "$route_marker" || ! wm_route_data_ready; then rm -f "$route_file" "$route_marker" return 1 fi rollback_restored_previous=true return 0 fi rm -f "$route_file" "$route_marker" return 1 } wm_restart_after_restore() { if ! wm_watermaps_is_running; then wm_log "Watermaps läuft noch nicht; wiederhergestellte Routingdaten werden beim App-Start getestet." return 0 fi wm_compose restart watermaps wm_wait_for_health watermaps 240 || return 1 if [[ "$rollback_restored_previous" == "true" ]]; then wm_smoke_test_route fi } wm_rollback_transaction() { local restore_status=0 if ! wm_restore_previous_route; then restore_status=1 fi rm -f "$transaction_file" transaction_active=false if ! wm_restart_after_restore; then return 1 fi return "$restore_status" } wm_cleanup_on_exit() { local status=$? trap - EXIT HUP INT TERM set +e if [[ "$transaction_active" == "true" ]]; then wm_log "Unvollständige Routingaktivierung wird zurückgerollt." wm_rollback_transaction fi rm -f "$candidate_file" "$candidate_marker" exit "$status" } trap wm_cleanup_on_exit EXIT trap 'exit 130' HUP INT TERM # A hard interruption cannot run shell traps. The durable transaction marker # makes the next invocation restore the last validated index before proceeding. if [[ -e "$transaction_file" ]]; then wm_log "Unterbrochene Routingaktualisierung erkannt; letzter gültiger Stand wird wiederhergestellt." if wm_restore_previous_route; then wm_restart_after_restore || wm_die "Der wiederhergestellte Routingindex bestand den Routentest nicht." else wm_restart_after_restore || wm_log "Es existierte noch kein vorheriger Routingindex; aktiver Marker wurde entfernt." fi rm -f "$transaction_file" fi rm -f "$candidate_file" "$candidate_marker" previous_ready=false if wm_route_data_ready; then previous_ready=true if [[ "$force_rebuild" != "true" ]]; then cp --reflink=auto "$route_file" "$candidate_file" chmod 0644 "$candidate_file" fi fi wm_log "Deutschland-/Niederlande-Routingdaten werden in einer Stagingdatei gebaut." if ! wm_compose --profile maintenance run --rm \ --env "WATERMAPS_LOCAL_FAIRWAYS_PATH=$container_candidate" \ --env "WATERMAPS_ROUTE_MARKER_PATH=$container_candidate_marker" \ route-data; then wm_die "Der Datenaufbau ist fehlgeschlagen; der aktive Index wurde nicht verändert." fi if ! wm_route_data_files_ready "$candidate_file" "$candidate_marker"; then wm_die "Der neu gebaute Routingindex oder sein Bereitschaftsmarker ist ungültig." fi wm_update_marine_features if [[ "$previous_ready" == "true" ]] && cmp --silent "$candidate_file" "$route_file"; then wm_log "Geofabrik-Snapshots, Routingindex und Marine-Features sind aktuell." rm -f "$candidate_file" "$candidate_marker" exit 0 fi if [[ "$previous_ready" == "true" ]]; then wm_copy_route_atomically "$route_file" "$backup_file" wm_write_route_marker "$backup_file" "$backup_marker" wm_route_data_files_ready "$backup_file" "$backup_marker" || wm_die "Der bisherige Routingindex konnte nicht sicher gesichert werden." else rm -f "$backup_file" "$backup_marker" fi temporary_transaction="$(mktemp "$WATERMAPS_RUNTIME_DIR/locks/.route-update-transaction.XXXXXX")" { printf 'started_at=%s\n' "$(date --utc +%Y-%m-%dT%H:%M:%SZ)" printf 'had_previous=%s\n' "$previous_ready" } >"$temporary_transaction" chmod 0644 "$temporary_transaction" mv -f "$temporary_transaction" "$transaction_file" transaction_active=true mv -f "$candidate_file" "$route_file" rm -f "$candidate_marker" wm_write_route_marker "$route_file" "$route_marker" wm_route_data_ready || wm_die "Der aktivierte Routingindex besitzt keinen gültigen Bereitschaftsmarker." activation_valid=false if wm_watermaps_is_running; then wm_compose restart watermaps if wm_wait_for_health watermaps 240 && wm_smoke_test_route; then activation_valid=true fi else wm_log "Watermaps läuft noch nicht; Routing- und Feature-API werden beim App-Start getestet." activation_valid=true fi if [[ "$activation_valid" == "true" ]]; then rm -f "$transaction_file" transaction_active=false rm -f "$backup_file" "$backup_marker" wm_log "Routingdaten-Update wurde erfolgreich aktiviert." exit 0 fi wm_log "Der neue Routingindex bestand die Routentests nicht; Rollback wird ausgeführt." if wm_rollback_transaction; then rm -f "$backup_file" "$backup_marker" wm_die "Neuer Routingindex verworfen; der vorherige, erneut getestete Index ist aktiv." fi if [[ "$rollback_restored_previous" == "true" ]]; then wm_die "Rollback-Dateien wurden wiederhergestellt, bestanden aber den Routentest nicht." fi wm_die "Neuer Routingindex verworfen; es existierte noch kein vorheriger gültiger Index."