optimized events
This commit is contained in:
@@ -17,6 +17,12 @@ WATERMAPS_RUNTIME_DIR=/srv/watermaps-runtime
|
||||
# Verhindert Speicherabbrüche beim vollständigen DE/NL-Indexaufbau auf 4-GB-Servern.
|
||||
WATERMAPS_SWAP_SIZE_GB=4
|
||||
|
||||
# Persistente PostGIS-Datenbank für Häfen, Schleusen, Brücken und Kontaktdaten.
|
||||
# Vor dem ersten Deployment beispielsweise mit `openssl rand -hex 32`
|
||||
# erzeugen. Das Secret muss mindestens 24 Zeichen lang sein.
|
||||
WATERMAPS_POSTGRES_PASSWORD=REPLACE_WITH_RANDOM_32_BYTE_HEX_SECRET
|
||||
WATERMAPS_POSTGRES_IMAGE=postgis/postgis:16-3.4
|
||||
|
||||
# Gitea Container Registry. Die commitgenauen App-Referenzen werden beim
|
||||
# Deployment separat erzeugt und niemals hier von Hand auf `latest` gesetzt.
|
||||
WATERMAPS_REGISTRY=gitea.incoso.eu
|
||||
@@ -29,6 +35,10 @@ WATERMAPS_CERTBOT_IMAGE=certbot/certbot:v5.7.0
|
||||
# Niederlande erneut geprüft und der Routingindex neu gebaut werden sollen.
|
||||
WATERMAPS_REBUILD_ROUTE_DATA=false
|
||||
|
||||
# Erzwingt unabhängig vom Bereitschaftsmarker einen vollständigen Neuimport
|
||||
# der Marine-Features aus den vorhandenen Deutschland-/Niederlande-PBFs.
|
||||
WATERMAPS_REBUILD_MARINE_DATA=false
|
||||
|
||||
# Für einen Test gegen Let's Encrypt Staging auf true setzen.
|
||||
# Das damit ausgestellte Zertifikat ist im Browser nicht vertrauenswürdig.
|
||||
WATERMAPS_CERTBOT_STAGING=false
|
||||
|
||||
+64
-21
@@ -1,9 +1,10 @@
|
||||
# Watermaps-Produktion
|
||||
|
||||
Dieser Stack hostet die Watermaps-App und die lokalen Fahrrouten für
|
||||
Deutschland und die Niederlande. Die sichtbaren Kartenkacheln bleiben externe
|
||||
Dienste. Öffentlich gebunden werden ausschließlich TCP 80 und 443; die
|
||||
Watermaps-App ist nur im internen Docker-Netz erreichbar.
|
||||
Dieser Stack hostet die Watermaps-App, die lokalen Fahrrouten sowie eine
|
||||
persistente PostGIS-Datenbank für Häfen, Schleusen, Brücken und Kontaktdaten in
|
||||
Deutschland und den Niederlanden. Die sichtbaren Kartenkacheln bleiben externe
|
||||
Dienste. Öffentlich gebunden werden ausschließlich TCP 80 und 443; App und
|
||||
PostGIS sind nur im internen Docker-Netz erreichbar.
|
||||
|
||||
## Konfiguration
|
||||
|
||||
@@ -12,11 +13,19 @@ cp deploy/.env.production.example deploy/.env.production
|
||||
editor deploy/.env.production
|
||||
```
|
||||
|
||||
Mindestens `WATERMAPS_ACME_EMAIL` muss angepasst werden. Außerdem müssen
|
||||
`WATERMAPS_REGISTRY` und `WATERMAPS_REGISTRY_OWNER` auf die Gitea Container
|
||||
Registry zeigen. Der Hetzner-API-Token gehört **nicht** in diese Datei. Er
|
||||
bleibt lokal in der ignorierten Datei `infra/opentofu/terraform.tfvars`
|
||||
(alternativ kann der Provider `TF_VAR_hcloud_token` lesen).
|
||||
Mindestens `WATERMAPS_ACME_EMAIL` und `WATERMAPS_POSTGRES_PASSWORD` müssen
|
||||
angepasst werden. Für das Datenbankpasswort eignet sich ein URL-unabhängiges
|
||||
Hex-Secret:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
Außerdem müssen `WATERMAPS_REGISTRY` und `WATERMAPS_REGISTRY_OWNER` auf die
|
||||
Gitea Container Registry zeigen. Der Hetzner-API-Token gehört **nicht** in
|
||||
diese Datei. Er bleibt lokal in der ignorierten Datei
|
||||
`infra/opentofu/terraform.tfvars` (alternativ kann der Provider
|
||||
`TF_VAR_hcloud_token` lesen).
|
||||
|
||||
Die private SSH-Keydatei wird ebenfalls nicht gespeichert. Sie kann beim
|
||||
Deployment mit `--identity` oder über `WATERMAPS_SSH_KEY` angegeben werden.
|
||||
@@ -53,8 +62,8 @@ werden nur Tests ausgeführt; Registry-Secrets werden dabei nicht verwendet.
|
||||
Nach einem erfolgreichen Image-Build liest das Skript standardmäßig den
|
||||
OpenTofu-Output `server_ipv4`. Es erlaubt ausschließlich einen sauberen,
|
||||
vollständig committeten Git-Stand und überträgt nur den kleinen Ordner
|
||||
`deploy/`, nicht den Anwendungsquellcode. Die Image-Tags entsprechen exakt
|
||||
`git rev-parse HEAD`.
|
||||
`deploy/` sowie das kanonische `database/schema.sql`, nicht den
|
||||
Anwendungsquellcode. Die Image-Tags entsprechen exakt `git rev-parse HEAD`.
|
||||
|
||||
Ist die Registry privat, werden einmalig beziehungsweise nach Tokenwechsel
|
||||
lokale Pull-Zugangsdaten mitgegeben:
|
||||
@@ -78,20 +87,36 @@ Anmeldung gültig ist:
|
||||
--identity ~/.ssh/watermaps_hetzner_ed25519
|
||||
```
|
||||
|
||||
Das Serverskript lädt App, Routingdaten-Builder, Nginx und Certbot mit
|
||||
`docker compose pull`. Die beiden commitgenauen Gitea-Tags werden anschließend
|
||||
in ihre unveränderlichen Registry-Digests aufgelöst. Erst danach werden die
|
||||
Container mit `--no-build` gestartet. Der Produktionsserver benötigt deshalb
|
||||
weder Git noch Node/npm oder den Quellcode.
|
||||
Das Serverskript lädt App, den kombinierten Routing-/Feature-Daten-Builder,
|
||||
PostGIS, Nginx und Certbot mit `docker compose pull`. Die beiden commitgenauen
|
||||
Gitea-Tags werden anschließend in ihre unveränderlichen Registry-Digests
|
||||
aufgelöst. Erst danach werden die Container mit `--no-build` gestartet. Der
|
||||
Produktionsserver benötigt deshalb weder Git noch Node/npm oder den Quellcode.
|
||||
|
||||
Der erste Datenaufbau lädt die Geofabrik-Extrakte für Deutschland und die
|
||||
Niederlande herunter und kann entsprechend der Serverleistung längere Zeit
|
||||
dauern. Der produktive Index liegt auf dem Server unter:
|
||||
Niederlande, baut den Fahrroutenindex und importiert daraus die
|
||||
`marine_features`. Je nach Serverleistung kann dies längere Zeit dauern.
|
||||
App und Nginx werden beim ersten Release erst gestartet, wenn sowohl der
|
||||
Routingindex als auch ein nicht leerer, zur PBF-Prüfsumme passender Bestand an
|
||||
Häfen, Schleusen und Brücken geprüft wurde. Ein HTTP-Healthcheck allein kann
|
||||
damit keine leere Ereignisdatenbank freigeben.
|
||||
|
||||
Die produktiven Daten liegen auf dem persistenten Servervolume:
|
||||
|
||||
```text
|
||||
/srv/watermaps-data/local/germany-netherlands-fairways.json
|
||||
/srv/watermaps-data/local/.marine-features.ready
|
||||
/srv/watermaps-data/postgres/
|
||||
/srv/watermaps-data/tmp/
|
||||
```
|
||||
|
||||
PostGIS veröffentlicht keinen Host-Port. Die App verbindet sich im privaten
|
||||
Compose-Netz; das Passwort wird separat über `PGPASSWORD` übergeben und muss
|
||||
nicht URL-kodiert in `DATABASE_URL` dupliziert werden. Auch die temporären,
|
||||
potenziell mehrere Gigabyte großen Filter- und GeoJSON-Dateien des Imports
|
||||
liegen unter `tmp/` auf diesem Volume und nicht im Docker-Overlay der
|
||||
Rootdisk; nach jedem Importlauf werden sie entfernt.
|
||||
|
||||
Der Upload wartet zuerst auf SSH und den Abschluss von Cloud-init. Anschließend
|
||||
startet und prüft er `watermaps-volume-setup.service`. Ohne tatsächlich unter
|
||||
`/srv/watermaps-data` eingehängtes Volume wird kein Download gestartet, damit
|
||||
@@ -108,7 +133,10 @@ HTTP-Anfragen erhalten 404.
|
||||
Jedes erfolgreiche Release wird mit Commit-SHA und den aufgelösten
|
||||
Image-Digests unter `/srv/watermaps-runtime/deployments/` gespeichert. Scheitert
|
||||
ein Deployment nach dem Containerwechsel, startet `deploy.sh` automatisch das
|
||||
vorherige Release und führt die Health- und Routentests erneut aus.
|
||||
vorherige Release und führt die Health-, Routen- und Featuretests erneut aus.
|
||||
Ein Rollback wechselt nur die unveränderlichen App-/Builder-Images. Die
|
||||
persistente PostGIS-Datenbank bleibt erhalten; das Schema und der Import sind
|
||||
aufwärtskompatibel und idempotent.
|
||||
|
||||
Das unmittelbar vorherige Release lässt sich auch manuell aktivieren:
|
||||
|
||||
@@ -153,11 +181,26 @@ HTTPS um.
|
||||
`bootstrap-server.sh` installiert zwei systemd-Timer:
|
||||
|
||||
- `watermaps-route-update.timer`: täglich neue Deutschland- und
|
||||
Niederlande-Daten; bei Build- oder Routentestfehler bleibt der vorherige
|
||||
Index aktiv.
|
||||
Niederlande-Daten. Der Fahrroutenindex wird atomar aktiviert. Der
|
||||
Feature-Importer aktualisiert beide Länder per Upsert und entfernt
|
||||
verschwundene Datensätze erst, nachdem beide Importe vollständig waren;
|
||||
dabei werden ausschließlich OSM-Zeilen bereinigt. EuRIS- und
|
||||
Website-Anreicherungen bleiben erhalten.
|
||||
- `watermaps-certbot-renew.timer`: zweimal täglich Certbot-Prüfung mit
|
||||
anschließendem Nginx-Reload.
|
||||
|
||||
Ein vollständiger manueller Feature-Neuimport lässt sich über die
|
||||
Option `--force-marine` erzwingen:
|
||||
|
||||
```bash
|
||||
/opt/watermaps/deploy/scripts/update-route-data.sh --force-marine
|
||||
```
|
||||
|
||||
`WATERMAPS_REBUILD_MARINE_DATA=true` in `.env.production` erzwingt den
|
||||
Neuimport stattdessen beim nächsten regulären Deployment. Der systemd-Dienst
|
||||
hat bewusst kein Startzeitlimit, da Download, Indexbau und Import auf kleineren
|
||||
Servern deutlich länger als 90 Sekunden dauern können.
|
||||
|
||||
Status und Logs:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -7,7 +7,8 @@ services:
|
||||
NODE_ENV: production
|
||||
HOST: 0.0.0.0
|
||||
PORT: 5174
|
||||
DATABASE_URL: ""
|
||||
DATABASE_URL: postgresql://seacompass@postgres:5432/seacompass
|
||||
PGPASSWORD: ${WATERMAPS_POSTGRES_PASSWORD:?WATERMAPS_POSTGRES_PASSWORD fehlt}
|
||||
REDIS_URL: ""
|
||||
WATERMAPS_WEB_DIST_PATH: /app/apps/web/dist
|
||||
WATERMAPS_LOCAL_FAIRWAYS_PATH: /data/germany-netherlands-fairways.json
|
||||
@@ -20,6 +21,9 @@ services:
|
||||
source: ${WATERMAPS_DATA_DIR:-/srv/watermaps-data}/local
|
||||
target: /data
|
||||
read_only: true
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
security_opt:
|
||||
@@ -32,7 +36,7 @@ services:
|
||||
- node
|
||||
- -e
|
||||
- >-
|
||||
fetch('http://127.0.0.1:5174/health')
|
||||
fetch('http://127.0.0.1:5174/ready')
|
||||
.then(response => { if (!response.ok) process.exit(1) })
|
||||
.catch(() => process.exit(1))
|
||||
interval: 15s
|
||||
@@ -40,6 +44,38 @@ services:
|
||||
start_period: 15s
|
||||
retries: 5
|
||||
|
||||
postgres:
|
||||
image: ${WATERMAPS_POSTGRES_IMAGE:-postgis/postgis:16-3.4}
|
||||
environment:
|
||||
POSTGRES_DB: seacompass
|
||||
POSTGRES_USER: seacompass
|
||||
POSTGRES_PASSWORD: ${WATERMAPS_POSTGRES_PASSWORD:?WATERMAPS_POSTGRES_PASSWORD fehlt}
|
||||
expose:
|
||||
- "5432"
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ${WATERMAPS_DATA_DIR:-/srv/watermaps-data}/postgres
|
||||
target: /var/lib/postgresql/data
|
||||
bind:
|
||||
create_host_path: false
|
||||
- type: bind
|
||||
source: ./database/schema.sql
|
||||
target: /docker-entrypoint-initdb.d/01-schema.sql
|
||||
read_only: true
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- pg_isready --username "$$POSTGRES_USER" --dbname "$$POSTGRES_DB"
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
start_period: 30s
|
||||
retries: 10
|
||||
stop_grace_period: 60s
|
||||
|
||||
nginx:
|
||||
image: ${WATERMAPS_NGINX_IMAGE:-nginx:1.30.4-alpine}
|
||||
depends_on:
|
||||
@@ -92,3 +128,22 @@ services:
|
||||
source: ${WATERMAPS_DATA_DIR:-/srv/watermaps-data}
|
||||
target: /workspace/data
|
||||
restart: "no"
|
||||
|
||||
marine-data:
|
||||
image: ${WATERMAPS_ROUTE_DATA_IMAGE:?WATERMAPS_ROUTE_DATA_IMAGE fehlt}
|
||||
profiles: ["maintenance"]
|
||||
entrypoint: ["/workspace/deploy/scripts/prepare-marine-features.sh"]
|
||||
environment:
|
||||
DATABASE_URL: postgresql://seacompass@postgres:5432/seacompass
|
||||
PGPASSWORD: ${WATERMAPS_POSTGRES_PASSWORD:?WATERMAPS_POSTGRES_PASSWORD fehlt}
|
||||
TMPDIR: /workspace/data/tmp
|
||||
WATERMAPS_GEOFABRIK_DIR: /workspace/data/geofabrik
|
||||
WATERMAPS_MARINE_MARKER_PATH: /workspace/data/local/.marine-features.ready
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ${WATERMAPS_DATA_DIR:-/srv/watermaps-data}
|
||||
target: /workspace/data
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
restart: "no"
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
FROM python:3.12-slim
|
||||
FROM node:22-bookworm-slim
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --yes --no-install-recommends ca-certificates curl coreutils libexpat1 \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& python3 -m pip install --no-cache-dir "osmium==4.3.1"
|
||||
&& apt-get install --yes --no-install-recommends \
|
||||
ca-certificates \
|
||||
coreutils \
|
||||
curl \
|
||||
libexpat1 \
|
||||
osmium-tool \
|
||||
postgresql-client \
|
||||
python3 \
|
||||
python3-pip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN python3 -m pip install \
|
||||
--break-system-packages \
|
||||
--disable-pip-version-check \
|
||||
--no-cache-dir \
|
||||
--target /workspace/.tools/python \
|
||||
"osmium==4.3.1" \
|
||||
&& npm install --no-package-lock --no-save "pg@8.16.3"
|
||||
|
||||
COPY scripts ./scripts
|
||||
COPY database ./database
|
||||
COPY deploy/scripts/prepare-route-data.sh ./deploy/scripts/prepare-route-data.sh
|
||||
COPY deploy/scripts/prepare-marine-features.sh ./deploy/scripts/prepare-marine-features.sh
|
||||
|
||||
ENTRYPOINT ["/workspace/deploy/scripts/prepare-route-data.sh"]
|
||||
|
||||
@@ -87,6 +87,8 @@ install -d -m 0755 \
|
||||
"$WATERMAPS_DATA_DIR" \
|
||||
"$WATERMAPS_DATA_DIR/geofabrik" \
|
||||
"$WATERMAPS_DATA_DIR/local" \
|
||||
"$WATERMAPS_DATA_DIR/postgres" \
|
||||
"$WATERMAPS_DATA_DIR/tmp" \
|
||||
"$WATERMAPS_DATA_DIR/certbot" \
|
||||
"$WATERMAPS_DATA_DIR/certbot/www" \
|
||||
"$WATERMAPS_DATA_DIR/certbot/letsencrypt" \
|
||||
@@ -95,6 +97,12 @@ install -d -m 0755 \
|
||||
"$WATERMAPS_RUNTIME_DIR/nginx/conf.d" \
|
||||
"$WATERMAPS_RUNTIME_DIR/locks"
|
||||
|
||||
# postgis/postgis uses the Debian postgres UID/GID 999. Keeping PGDATA on the
|
||||
# mounted Hetzner volume makes event data survive image deployments and host
|
||||
# reboots; the official entrypoint can still repair ownership inside PGDATA.
|
||||
chown 999:999 "$WATERMAPS_DATA_DIR/postgres"
|
||||
chmod 0700 "$WATERMAPS_DATA_DIR/postgres"
|
||||
|
||||
if [[ ! -f "$WATERMAPS_RUNTIME_DIR/nginx/conf.d/default.conf" ]]; then
|
||||
install -m 0644 \
|
||||
"$WM_DEPLOY_DIR/nginx/bootstrap.conf" \
|
||||
|
||||
@@ -33,6 +33,16 @@ wm_load_env() {
|
||||
|
||||
wm_require_safe_absolute_dir "$WATERMAPS_DATA_DIR"
|
||||
wm_require_safe_absolute_dir "$WATERMAPS_RUNTIME_DIR"
|
||||
wm_validate_postgres_password
|
||||
}
|
||||
|
||||
wm_validate_postgres_password() {
|
||||
local password="${WATERMAPS_POSTGRES_PASSWORD:-}"
|
||||
|
||||
[[ "${#password}" -ge 24 &&
|
||||
"$password" != *[[:space:]]* &&
|
||||
"$password" != *REPLACE* ]] ||
|
||||
wm_die "WATERMAPS_POSTGRES_PASSWORD muss ein gesetztes Secret mit mindestens 24 Zeichen ohne Leerzeichen sein."
|
||||
}
|
||||
|
||||
wm_require_safe_absolute_dir() {
|
||||
@@ -162,6 +172,10 @@ wm_route_marker() {
|
||||
printf '%s/local/.germany-netherlands-fairways.ready\n' "$WATERMAPS_DATA_DIR"
|
||||
}
|
||||
|
||||
wm_marine_marker() {
|
||||
printf '%s/local/.marine-features.ready\n' "$WATERMAPS_DATA_DIR"
|
||||
}
|
||||
|
||||
wm_acquire_route_lock() {
|
||||
install -d -m 0755 "$WATERMAPS_RUNTIME_DIR/locks"
|
||||
exec 9>"$WATERMAPS_RUNTIME_DIR/locks/route-update.lock"
|
||||
@@ -288,6 +302,107 @@ wm_route_data_files_ready() {
|
||||
return 0
|
||||
}
|
||||
|
||||
wm_geofabrik_checksum() {
|
||||
local region="$1"
|
||||
local checksum_file="$WATERMAPS_DATA_DIR/geofabrik/${region}-latest.osm.pbf.md5"
|
||||
local checksum
|
||||
|
||||
[[ -s "$checksum_file" ]] || return 1
|
||||
checksum="$(awk 'NR == 1 { print tolower($1) }' "$checksum_file")"
|
||||
[[ "$checksum" =~ ^[0-9a-f]{32}$ ]] || return 1
|
||||
printf '%s\n' "$checksum"
|
||||
}
|
||||
|
||||
wm_postgres_query() {
|
||||
local query="$1"
|
||||
|
||||
wm_compose exec --no-TTY postgres \
|
||||
psql \
|
||||
--no-psqlrc \
|
||||
--username seacompass \
|
||||
--dbname seacompass \
|
||||
--tuples-only \
|
||||
--no-align \
|
||||
--field-separator '|' \
|
||||
--set ON_ERROR_STOP=1 \
|
||||
--command "$query"
|
||||
}
|
||||
|
||||
wm_marine_features_ready() {
|
||||
local marker germany_checksum netherlands_checksum database_counts
|
||||
local total_count harbour_count lock_count bridge_count
|
||||
local marker_format marker_source marker_germany marker_netherlands
|
||||
local marker_total marker_harbours marker_locks marker_bridges
|
||||
|
||||
marker="$(wm_marine_marker)"
|
||||
if [[ ! -s "$marker" ]]; then
|
||||
wm_log "Bereitschaftsmarker für Marine-Features fehlt: $marker"
|
||||
return 1
|
||||
fi
|
||||
|
||||
germany_checksum="$(wm_geofabrik_checksum germany 2>/dev/null || true)"
|
||||
netherlands_checksum="$(wm_geofabrik_checksum netherlands 2>/dev/null || true)"
|
||||
if [[ -z "$germany_checksum" || -z "$netherlands_checksum" ]]; then
|
||||
wm_log "Geofabrik-Prüfsummen für den Marine-Feature-Stand fehlen oder sind ungültig."
|
||||
return 1
|
||||
fi
|
||||
|
||||
marker_format="$(wm_marker_value "$marker" format_version 2>/dev/null || true)"
|
||||
marker_source="$(wm_marker_value "$marker" source 2>/dev/null || true)"
|
||||
marker_germany="$(wm_marker_value "$marker" germany_md5 2>/dev/null || true)"
|
||||
marker_netherlands="$(wm_marker_value "$marker" netherlands_md5 2>/dev/null || true)"
|
||||
marker_total="$(wm_marker_value "$marker" total_osm_features 2>/dev/null || true)"
|
||||
marker_harbours="$(wm_marker_value "$marker" harbour_count 2>/dev/null || true)"
|
||||
marker_locks="$(wm_marker_value "$marker" lock_count 2>/dev/null || true)"
|
||||
marker_bridges="$(wm_marker_value "$marker" bridge_count 2>/dev/null || true)"
|
||||
|
||||
if [[ "$marker_format" != "1" ||
|
||||
"$marker_source" != "germany+netherlands" ||
|
||||
"$marker_germany" != "$germany_checksum" ||
|
||||
"$marker_netherlands" != "$netherlands_checksum" ]]; then
|
||||
wm_log "Marine-Feature-Marker passt nicht zu den aktuellen Deutschland-/Niederlande-Snapshots."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! database_counts="$(
|
||||
wm_postgres_query "
|
||||
SELECT
|
||||
count(*)::bigint,
|
||||
count(*) FILTER (WHERE layer = 'harbours')::bigint,
|
||||
count(*) FILTER (WHERE layer = 'locks')::bigint,
|
||||
count(*) FILTER (WHERE layer = 'bridges')::bigint
|
||||
FROM marine_features
|
||||
WHERE source = 'osm';
|
||||
"
|
||||
)"; then
|
||||
wm_log "Marine-Feature-Tabellen sind in PostGIS nicht abfragbar."
|
||||
return 1
|
||||
fi
|
||||
database_counts="${database_counts//[[:space:]]/}"
|
||||
IFS='|' read -r total_count harbour_count lock_count bridge_count <<<"$database_counts"
|
||||
|
||||
for count in "$total_count" "$harbour_count" "$lock_count" "$bridge_count"; do
|
||||
if [[ ! "$count" =~ ^[1-9][0-9]*$ ]]; then
|
||||
wm_log "PostGIS enthält keine vollständigen OSM-Hafen-/Schleusen-/Brückendaten."
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$marker_total" != "$total_count" ||
|
||||
"$marker_harbours" != "$harbour_count" ||
|
||||
"$marker_locks" != "$lock_count" ||
|
||||
"$marker_bridges" != "$bridge_count" ]]; then
|
||||
wm_log "Marine-Feature-Marker stimmt nicht mit den OSM-Zeilen in PostGIS überein."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
wm_assert_marine_features() {
|
||||
wm_marine_features_ready ||
|
||||
wm_die "Marine-Features sind nicht vollständig in PostGIS bereit."
|
||||
}
|
||||
|
||||
wm_wait_for_health() {
|
||||
local service="$1"
|
||||
local timeout_seconds="${2:-180}"
|
||||
@@ -479,3 +594,44 @@ wm_smoke_test_route() {
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
wm_smoke_test_features() {
|
||||
wm_compose exec --no-TTY watermaps node --input-type=module --eval '
|
||||
const params = new URLSearchParams({
|
||||
bbox: "7.118483884871649,53.302596677614666,7.543960668999219,53.50669706666667",
|
||||
layers: "harbours,locks,bridges"
|
||||
});
|
||||
const response = await fetch(`http://127.0.0.1:5174/api/features?${params}`);
|
||||
if (!response.ok) {
|
||||
console.error("Feature-Smoke-Test", response.status, await response.text());
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const collection = await response.json();
|
||||
const features = Array.isArray(collection.features) ? collection.features : [];
|
||||
const layers = new Set(features.map((feature) => feature?.properties?.layer));
|
||||
const contactFeature = features.find((feature) =>
|
||||
["harbours", "locks"].includes(feature?.properties?.layer)
|
||||
&& typeof feature?.properties?.phone === "string"
|
||||
&& feature.properties.phone.trim().length > 0
|
||||
);
|
||||
if (
|
||||
collection?.metadata?.source !== "postgis"
|
||||
|| !layers.has("harbours")
|
||||
|| !layers.has("locks")
|
||||
|| !layers.has("bridges")
|
||||
|| !contactFeature
|
||||
) {
|
||||
console.error(
|
||||
"PostGIS-Feature-Prüfung für die Emden–Aurich-Teststrecke fehlgeschlagen.",
|
||||
JSON.stringify({
|
||||
source: collection?.metadata?.source,
|
||||
featureCount: features.length,
|
||||
layers: [...layers],
|
||||
hasCallableHarbourOrLock: Boolean(contactFeature)
|
||||
})
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
+28
-12
@@ -69,12 +69,16 @@ wm_finish_deployment() {
|
||||
if [[ -n "$rollback_images_file" && -f "$rollback_images_file" ]]; then
|
||||
wm_log "Deployment fehlgeschlagen; vorheriges Container-Release wird wiederhergestellt."
|
||||
wm_use_images_env "$rollback_images_file"
|
||||
wm_compose up --detach --no-build --remove-orphans watermaps nginx
|
||||
wm_compose up --detach --no-build --remove-orphans postgres watermaps nginx
|
||||
wm_wait_for_health postgres 240
|
||||
wm_wait_for_health watermaps 240
|
||||
wm_wait_for_health nginx 120
|
||||
if wm_route_data_ready; then
|
||||
wm_smoke_test_route
|
||||
fi
|
||||
if wm_marine_features_ready; then
|
||||
wm_smoke_test_features
|
||||
fi
|
||||
else
|
||||
wm_log "Deployment fehlgeschlagen; für das erste Release existiert noch kein Rollback."
|
||||
fi
|
||||
@@ -99,7 +103,7 @@ requested_app_image="$(wm_env_value "$images_env_file" WATERMAPS_APP_IMAGE)"
|
||||
requested_route_data_image="$(wm_env_value "$images_env_file" WATERMAPS_ROUTE_DATA_IMAGE)"
|
||||
|
||||
wm_log "Container-Images für Commit $revision werden aus den Registries geladen."
|
||||
wm_compose --profile maintenance pull watermaps nginx certbot route-data
|
||||
wm_compose --profile maintenance pull watermaps postgres nginx certbot route-data
|
||||
|
||||
resolved_app_image="$(wm_resolve_image_digest "$requested_app_image")"
|
||||
resolved_route_data_image="$(wm_resolve_image_digest "$requested_route_data_image")"
|
||||
@@ -114,30 +118,42 @@ resolved_images_file="$(
|
||||
chmod 0644 "$resolved_images_file"
|
||||
wm_use_images_env "$resolved_images_file"
|
||||
|
||||
wm_log "Commit $revision wird mit unveränderlichen Image-Digests gestartet."
|
||||
wm_compose up --detach --no-build --remove-orphans watermaps nginx
|
||||
wm_log "Persistentes PostGIS wird vor App und Datenimport gestartet."
|
||||
wm_compose up --detach --no-build postgres
|
||||
wm_wait_for_health postgres 240
|
||||
|
||||
wm_wait_for_health watermaps 240
|
||||
wm_wait_for_health nginx 120
|
||||
|
||||
route_rebuild_required=false
|
||||
data_update_required=false
|
||||
update_args=()
|
||||
if [[ "${WATERMAPS_REBUILD_ROUTE_DATA:-false}" == "true" ]]; then
|
||||
route_rebuild_required=true
|
||||
data_update_required=true
|
||||
update_args+=(--force)
|
||||
elif ! wm_route_data_ready; then
|
||||
route_rebuild_required=true
|
||||
data_update_required=true
|
||||
fi
|
||||
if [[ "${WATERMAPS_REBUILD_MARINE_DATA:-false}" == "true" ]]; then
|
||||
data_update_required=true
|
||||
update_args+=(--force-marine)
|
||||
elif ! wm_marine_features_ready; then
|
||||
data_update_required=true
|
||||
fi
|
||||
|
||||
if [[ "$route_rebuild_required" == "true" ]]; then
|
||||
wm_log "Deutschland- und Niederlande-Routingdaten werden sicher vorbereitet."
|
||||
if [[ "$data_update_required" == "true" ]]; then
|
||||
wm_log "Deutschland-/Niederlande-Routingdaten und Marine-Features werden sicher vorbereitet."
|
||||
WATERMAPS_ROUTE_LOCK_HELD=true \
|
||||
"$WM_DEPLOY_DIR/scripts/update-route-data.sh" "${update_args[@]}"
|
||||
fi
|
||||
|
||||
wm_assert_route_data
|
||||
wm_assert_marine_features
|
||||
|
||||
wm_log "Commit $revision wird erst mit validierten Routing- und Ereignisdaten gestartet."
|
||||
wm_compose up --detach --no-build --remove-orphans postgres watermaps nginx
|
||||
|
||||
wm_wait_for_health postgres 240
|
||||
wm_wait_for_health watermaps 240
|
||||
wm_wait_for_health nginx 120
|
||||
wm_smoke_test_route
|
||||
wm_smoke_test_features
|
||||
|
||||
release_images_file="$WATERMAPS_RUNTIME_DIR/deployments/$revision.env"
|
||||
install -m 0644 "$resolved_images_file" "$release_images_file"
|
||||
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
cd /workspace
|
||||
|
||||
database_url="${DATABASE_URL:-}"
|
||||
geofabrik_dir="${WATERMAPS_GEOFABRIK_DIR:-/workspace/data/geofabrik}"
|
||||
marker_file="${WATERMAPS_MARINE_MARKER_PATH:-/workspace/data/local/.marine-features.ready}"
|
||||
temporary_root="${TMPDIR:-/tmp}"
|
||||
pbf_paths=(
|
||||
"$geofabrik_dir/germany-latest.osm.pbf"
|
||||
"$geofabrik_dir/netherlands-latest.osm.pbf"
|
||||
)
|
||||
|
||||
if [[ -z "$database_url" ]]; then
|
||||
printf 'Fehler: DATABASE_URL ist für den Marine-Feature-Import erforderlich.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -x /workspace/scripts/import-geofabrik.sh ]]; then
|
||||
printf 'Fehler: scripts/import-geofabrik.sh fehlt oder ist nicht ausführbar.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$marker_file")" "$temporary_root"
|
||||
export TMPDIR="$temporary_root"
|
||||
|
||||
declare -A source_checksums
|
||||
for pbf_path in "${pbf_paths[@]}"; do
|
||||
if [[ ! -s "$pbf_path" ]]; then
|
||||
printf 'Fehler: Geofabrik-Snapshot fehlt oder ist leer: %s\n' "$pbf_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
checksum_file="${pbf_path}.md5"
|
||||
if [[ ! -s "$checksum_file" ]]; then
|
||||
printf 'Fehler: Geofabrik-Prüfsumme fehlt: %s\n' "$checksum_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expected_checksum="$(awk 'NR == 1 { print tolower($1) }' "$checksum_file")"
|
||||
if [[ ! "$expected_checksum" =~ ^[0-9a-f]{32}$ ]]; then
|
||||
printf 'Fehler: Ungültige Geofabrik-Prüfsumme in %s\n' "$checksum_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
actual_checksum="$(md5sum "$pbf_path" | awk '{ print $1 }')"
|
||||
if [[ "$actual_checksum" != "$expected_checksum" ]]; then
|
||||
printf 'Fehler: Geofabrik-Snapshot stimmt nicht mit seiner Prüfsumme überein: %s\n' "$pbf_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
source_checksums["$(basename "$pbf_path" -latest.osm.pbf)"]="$actual_checksum"
|
||||
done
|
||||
|
||||
# Every imported OSM row receives updated_at=now(). The shared database
|
||||
# timestamp lets us remove disappeared OSM objects only after both regional
|
||||
# imports completed and passed the sanity checks. Other sources such as EuRIS
|
||||
# and facility-website enrichments are deliberately preserved.
|
||||
import_started_epoch="$(
|
||||
psql "$database_url" \
|
||||
--no-psqlrc \
|
||||
--tuples-only \
|
||||
--no-align \
|
||||
--set ON_ERROR_STOP=1 \
|
||||
--command "SELECT extract(epoch FROM clock_timestamp());"
|
||||
)"
|
||||
import_started_epoch="${import_started_epoch//[[:space:]]/}"
|
||||
if [[ ! "$import_started_epoch" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
|
||||
printf 'Fehler: Datenbank lieferte keinen gültigen Importzeitpunkt.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for pbf_path in "${pbf_paths[@]}"; do
|
||||
printf 'Importiere Marine-Features aus %s\n' "$(basename "$pbf_path")"
|
||||
DATABASE_URL="$database_url" /workspace/scripts/import-geofabrik.sh "$pbf_path"
|
||||
done
|
||||
|
||||
fresh_counts="$(
|
||||
psql "$database_url" \
|
||||
--no-psqlrc \
|
||||
--tuples-only \
|
||||
--no-align \
|
||||
--field-separator '|' \
|
||||
--set ON_ERROR_STOP=1 \
|
||||
--command "
|
||||
SELECT
|
||||
count(*)::bigint,
|
||||
count(*) FILTER (WHERE layer = 'harbours')::bigint,
|
||||
count(*) FILTER (WHERE layer = 'locks')::bigint,
|
||||
count(*) FILTER (WHERE layer = 'bridges')::bigint
|
||||
FROM marine_features
|
||||
WHERE source = 'osm'
|
||||
AND updated_at >= to_timestamp($import_started_epoch);
|
||||
"
|
||||
)"
|
||||
fresh_counts="${fresh_counts//[[:space:]]/}"
|
||||
IFS='|' read -r fresh_total fresh_harbours fresh_locks fresh_bridges <<<"$fresh_counts"
|
||||
for count in "$fresh_total" "$fresh_harbours" "$fresh_locks" "$fresh_bridges"; do
|
||||
if [[ ! "$count" =~ ^[1-9][0-9]*$ ]]; then
|
||||
printf 'Fehler: Der neue OSM-Import enthält keine vollständigen Hafen-/Schleusen-/Brückendaten.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
psql "$database_url" \
|
||||
--no-psqlrc \
|
||||
--set ON_ERROR_STOP=1 \
|
||||
--command "
|
||||
BEGIN;
|
||||
DELETE FROM marine_features
|
||||
WHERE source = 'osm'
|
||||
AND updated_at < to_timestamp($import_started_epoch);
|
||||
DELETE FROM marine_fairway_edges
|
||||
WHERE source = 'osm'
|
||||
AND updated_at < to_timestamp($import_started_epoch);
|
||||
COMMIT;
|
||||
ANALYZE marine_features;
|
||||
ANALYZE marine_fairway_edges;
|
||||
"
|
||||
|
||||
final_counts="$(
|
||||
psql "$database_url" \
|
||||
--no-psqlrc \
|
||||
--tuples-only \
|
||||
--no-align \
|
||||
--field-separator '|' \
|
||||
--set ON_ERROR_STOP=1 \
|
||||
--command "
|
||||
SELECT
|
||||
count(*)::bigint,
|
||||
count(*) FILTER (WHERE layer = 'harbours')::bigint,
|
||||
count(*) FILTER (WHERE layer = 'locks')::bigint,
|
||||
count(*) FILTER (WHERE layer = 'bridges')::bigint
|
||||
FROM marine_features
|
||||
WHERE source = 'osm';
|
||||
"
|
||||
)"
|
||||
final_counts="${final_counts//[[:space:]]/}"
|
||||
IFS='|' read -r total_osm_features harbour_count lock_count bridge_count <<<"$final_counts"
|
||||
|
||||
temporary_marker="$(mktemp "$(dirname "$marker_file")/.marine-features-ready.XXXXXX")"
|
||||
{
|
||||
printf 'format_version=1\n'
|
||||
printf 'generated_at=%s\n' "$(date --utc +%Y-%m-%dT%H:%M:%SZ)"
|
||||
printf 'source=germany+netherlands\n'
|
||||
printf 'germany_md5=%s\n' "${source_checksums[germany]}"
|
||||
printf 'netherlands_md5=%s\n' "${source_checksums[netherlands]}"
|
||||
printf 'total_osm_features=%s\n' "$total_osm_features"
|
||||
printf 'harbour_count=%s\n' "$harbour_count"
|
||||
printf 'lock_count=%s\n' "$lock_count"
|
||||
printf 'bridge_count=%s\n' "$bridge_count"
|
||||
} >"$temporary_marker"
|
||||
chmod 0644 "$temporary_marker"
|
||||
mv -f "$temporary_marker" "$marker_file"
|
||||
|
||||
printf 'Marine-Features sind bereit: %s OSM-Objekte (%s Häfen, %s Schleusen, %s Brücken)\n' \
|
||||
"$total_osm_features" "$harbour_count" "$lock_count" "$bridge_count"
|
||||
@@ -71,4 +71,56 @@ if wm_route_data_ready; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'Routingdaten- und Markerprüfungen: OK\n'
|
||||
mkdir -p "$WATERMAPS_DATA_DIR/geofabrik"
|
||||
printf '%s germany-latest.osm.pbf\n' \
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' \
|
||||
>"$WATERMAPS_DATA_DIR/geofabrik/germany-latest.osm.pbf.md5"
|
||||
printf '%s netherlands-latest.osm.pbf\n' \
|
||||
'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' \
|
||||
>"$WATERMAPS_DATA_DIR/geofabrik/netherlands-latest.osm.pbf.md5"
|
||||
|
||||
cat >"$(wm_marine_marker)" <<'MARKER'
|
||||
format_version=1
|
||||
generated_at=2026-07-28T00:00:00Z
|
||||
source=germany+netherlands
|
||||
germany_md5=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
netherlands_md5=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
total_osm_features=100
|
||||
harbour_count=10
|
||||
lock_count=5
|
||||
bridge_count=20
|
||||
MARKER
|
||||
|
||||
wm_postgres_query() {
|
||||
printf '100|10|5|20\n'
|
||||
}
|
||||
wm_marine_features_ready
|
||||
|
||||
wm_postgres_query() {
|
||||
printf '100|10|0|20\n'
|
||||
}
|
||||
if wm_marine_features_ready; then
|
||||
printf 'Marine-Feature-Marker wurde trotz fehlender Schleusen akzeptiert.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
wm_postgres_query() {
|
||||
printf '99|10|5|20\n'
|
||||
}
|
||||
if wm_marine_features_ready; then
|
||||
printf 'Marine-Feature-Marker wurde trotz abweichendem Datenbankbestand akzeptiert.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -Eq 'DATABASE_URL:[[:space:]]*""' \
|
||||
"$ROOT_DIR/docker-compose.yml" \
|
||||
"$ROOT_DIR/deploy/compose.production.yml"; then
|
||||
printf 'Compose trennt die App weiterhin explizit von PostGIS.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -Fq 'condition: service_healthy' "$ROOT_DIR/deploy/compose.production.yml"
|
||||
grep -Fq '/ready' "$ROOT_DIR/deploy/compose.production.yml"
|
||||
grep -Fq 'source: ${WATERMAPS_DATA_DIR:-/srv/watermaps-data}/postgres' \
|
||||
"$ROOT_DIR/deploy/compose.production.yml"
|
||||
|
||||
printf 'Routingdaten-, Marine-Feature- und Compose-Prüfungen: OK\n'
|
||||
|
||||
@@ -16,6 +16,7 @@ mkdir -p "$FAKE_BIN" "$DATA_DIR/local" "$RUNTIME_DIR"
|
||||
cat >"$ENV_FILE" <<EOF
|
||||
WATERMAPS_DATA_DIR=$DATA_DIR
|
||||
WATERMAPS_RUNTIME_DIR=$RUNTIME_DIR
|
||||
WATERMAPS_POSTGRES_PASSWORD=0123456789abcdef0123456789abcdef
|
||||
EOF
|
||||
|
||||
cat >"$IMAGES_ENV_FILE" <<'EOF'
|
||||
@@ -48,6 +49,11 @@ if [[ "${1:-}" == "inspect" ]]; then
|
||||
fi
|
||||
|
||||
arguments=" $* "
|
||||
if [[ "$arguments" == *" ps --quiet postgres "* ]]; then
|
||||
printf 'postgres-test-container\n'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$arguments" == *" ps --quiet watermaps "* ]]; then
|
||||
printf 'watermaps-test-container\n'
|
||||
exit 0
|
||||
@@ -55,6 +61,13 @@ fi
|
||||
|
||||
if [[ "$arguments" == *" run "*" route-data "* ||
|
||||
"$arguments" == *" run "*" route-data" ]]; then
|
||||
mkdir -p "$WATERMAPS_DATA_DIR/geofabrik"
|
||||
printf '%s germany-latest.osm.pbf\n' \
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' \
|
||||
>"$WATERMAPS_DATA_DIR/geofabrik/germany-latest.osm.pbf.md5"
|
||||
printf '%s netherlands-latest.osm.pbf\n' \
|
||||
'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' \
|
||||
>"$WATERMAPS_DATA_DIR/geofabrik/netherlands-latest.osm.pbf.md5"
|
||||
route_file="$WATERMAPS_DATA_DIR/local/.germany-netherlands-fairways.candidate.json"
|
||||
marker_file="$WATERMAPS_DATA_DIR/local/.germany-netherlands-fairways.candidate.ready"
|
||||
cat >"$route_file" <<JSON
|
||||
@@ -86,6 +99,27 @@ JSON
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$arguments" == *" run "*" marine-data "* ||
|
||||
"$arguments" == *" run "*" marine-data" ]]; then
|
||||
cat >"$WATERMAPS_DATA_DIR/local/.marine-features.ready" <<'MARKER'
|
||||
format_version=1
|
||||
generated_at=2026-07-28T00:00:00Z
|
||||
source=germany+netherlands
|
||||
germany_md5=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
netherlands_md5=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
total_osm_features=100
|
||||
harbour_count=10
|
||||
lock_count=5
|
||||
bridge_count=20
|
||||
MARKER
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$arguments" == *" exec --no-TTY postgres "* ]]; then
|
||||
printf '100|10|5|20\n'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$arguments" == *" exec --no-TTY watermaps "* ]]; then
|
||||
active_file="$WATERMAPS_DATA_DIR/local/germany-netherlands-fairways.json"
|
||||
active_revision="$(jq --raw-output '.revision // 0' "$active_file" 2>/dev/null || true)"
|
||||
|
||||
@@ -13,11 +13,22 @@ if [[ "$(id -u)" -ne 0 ]]; then
|
||||
fi
|
||||
|
||||
force_rebuild=false
|
||||
if [[ "${1:-}" == "--force" ]]; then
|
||||
force_rebuild=true
|
||||
shift
|
||||
fi
|
||||
[[ "$#" -eq 0 ]] || wm_die "Unbekannte Argumente für update-route-data.sh."
|
||||
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
|
||||
@@ -26,6 +37,9 @@ if [[ "${WATERMAPS_ROUTE_LOCK_HELD:-false}" != "true" ]]; then
|
||||
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")"
|
||||
@@ -58,6 +72,16 @@ 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" ]] &&
|
||||
@@ -78,8 +102,8 @@ wm_restore_previous_route() {
|
||||
|
||||
wm_restart_after_restore() {
|
||||
if ! wm_watermaps_is_running; then
|
||||
wm_log "Watermaps läuft nicht; wiederhergestellte Routingdaten können noch nicht getestet werden."
|
||||
return 1
|
||||
wm_log "Watermaps läuft noch nicht; wiederhergestellte Routingdaten werden beim App-Start getestet."
|
||||
return 0
|
||||
fi
|
||||
|
||||
wm_compose restart watermaps
|
||||
@@ -156,8 +180,10 @@ 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 und Routingindex sind unverändert."
|
||||
wm_log "Geofabrik-Snapshots, Routingindex und Marine-Features sind aktuell."
|
||||
rm -f "$candidate_file" "$candidate_marker"
|
||||
exit 0
|
||||
fi
|
||||
@@ -186,8 +212,18 @@ wm_write_route_marker "$route_file" "$route_marker"
|
||||
wm_route_data_ready ||
|
||||
wm_die "Der aktivierte Routingindex besitzt keinen gültigen Bereitschaftsmarker."
|
||||
|
||||
wm_compose restart watermaps
|
||||
if wm_wait_for_health watermaps 240 && wm_smoke_test_route; then
|
||||
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"
|
||||
|
||||
@@ -124,9 +124,9 @@ printf '[watermaps] Warte auf Cloud-init und das persistente Hetzner-Volume.\n'
|
||||
ssh "${WM_SSH_OPTIONS[@]}" "$WM_SSH_TARGET" \
|
||||
"cloud-init status --wait && ${remote_prefix}systemctl start watermaps-volume-setup.service && mountpoint --quiet /srv/watermaps-data"
|
||||
|
||||
printf '[watermaps] Übertrage Deployment-Dateien nach %s:/opt/watermaps/deploy\n' "$WM_SSH_TARGET"
|
||||
printf '[watermaps] Übertrage Deployment-Dateien und Datenbankschema nach %s:/opt/watermaps\n' "$WM_SSH_TARGET"
|
||||
ssh "${WM_SSH_OPTIONS[@]}" "$WM_SSH_TARGET" \
|
||||
"${remote_prefix}install -d -m 0755 /opt/watermaps /opt/watermaps/deploy"
|
||||
"${remote_prefix}install -d -m 0755 /opt/watermaps /opt/watermaps/deploy /opt/watermaps/database"
|
||||
|
||||
rsync \
|
||||
--archive \
|
||||
@@ -140,6 +140,14 @@ rsync \
|
||||
"$WM_DEPLOY_DIR/" \
|
||||
"$WM_SSH_TARGET:/opt/watermaps/deploy/"
|
||||
|
||||
rsync \
|
||||
--archive \
|
||||
--chmod=F644 \
|
||||
--rsync-path="$rsync_path" \
|
||||
-e "ssh ${WM_SSH_OPTIONS[*]@Q}" \
|
||||
"$WM_LOCAL_ROOT_DIR/database/schema.sql" \
|
||||
"$WM_SSH_TARGET:/opt/watermaps/database/schema.sql"
|
||||
|
||||
rsync \
|
||||
--archive \
|
||||
--chmod=F600 \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=Watermaps Deutschland-/Niederlande-Routingdaten aktualisieren
|
||||
Description=Watermaps Deutschland-/Niederlande-Routing- und Ereignisdaten aktualisieren
|
||||
Wants=network-online.target
|
||||
After=network-online.target docker.service watermaps-volume-setup.service
|
||||
Requires=docker.service
|
||||
@@ -9,6 +9,7 @@ RequiresMountsFor=/srv/watermaps-data
|
||||
Type=oneshot
|
||||
WorkingDirectory=/opt/watermaps
|
||||
ExecStart=/opt/watermaps/deploy/scripts/update-route-data.sh
|
||||
TimeoutStartSec=0
|
||||
Nice=10
|
||||
IOSchedulingClass=best-effort
|
||||
IOSchedulingPriority=7
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=Täglich Watermaps-Routingdaten aktualisieren
|
||||
Description=Täglich Watermaps-Routing- und Ereignisdaten aktualisieren
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 06:15:00
|
||||
|
||||
Reference in New Issue
Block a user