Commit a9add541 by Bambang Adrian

fix(osrm): release v3.8 fundamental filename logic fix

parent c9b26f67
Ketika Anda menggunakan Docker, file-file yang ada di dalam
image Docker tidak dapat diubah secara langsung setelah image dibuat. Untuk memodifikasi leaflet_options.js, Anda harus membuat image Docker kustom yang menggantikan file asli dengan versi yang sudah Anda ubah. Anda dapat melakukan ini dengan bantuan Dockerfile dan mengelola semuanya dengan docker-compose.yml.
Berikut adalah panduan langkah demi langkah untuk menerapkan perubahan ini menggunakan Docker Compose.
Langkah 1: Siapkan semua file
Pertama, Anda perlu mengumpulkan semua file yang diperlukan di satu direktori proyek Anda.
Struktur direktori:
my-osrm-project/
├── docker-compose.yml
├── leaflet_options.js
├── Dockerfile.frontend
└── (direktori data, misalnya ./data untuk .osrm files)
Penjelasan:
docker-compose.yml: File utama untuk mendefinisikan dan menjalankan layanan multi-kontainer Anda.
leaflet_options.js: File ini adalah versi kustom dari src/leaflet_options.js dari repositori osrm-frontend. Di sinilah Anda akan mendefinisikan profil rute yang berbeda.
Dockerfile.frontend: File ini digunakan untuk membuat image Docker kustom untuk frontend Anda.
data: Direktori lokal tempat Anda menyimpan semua file .osm.pbf dan .osrm yang sudah diproses.
Langkah 2: Buat file leaflet_options.js kustom
Buat file baru bernama leaflet_options.js di direktori proyek Anda, dan isi dengan konfigurasi untuk setiap profil yang Anda inginkan.
Isi file leaflet_options.js:
javascript
// leaflet_options.js
module.exports = {
services: [
{
label: 'Mobil (Tercepat)',
path: 'http://backend-car:5000/route/v1' // Ganti dengan nama service backend mobil
},
{
label: 'Sepeda (Tercepat)',
path: 'http://backend-bike:5000/route/v1' // Ganti dengan nama service backend sepeda
},
{
label: 'Jalan Kaki (Tercepat)',
path: 'http://backend-foot:5000/route/v1' // Ganti dengan nama service backend jalan kaki
}
],
center: [ -6.2, 106.8 ], // Koordinat pusat peta (contoh untuk Jakarta)
zoom: 13,
// Tambahkan opsi konfigurasi lain di sini jika diperlukan
};
Gunakan kode dengan hati-hati.
Penting: Perhatikan bahwa path mengarah ke nama layanan Docker (misalnya, backend-car) yang akan didefinisikan di docker-compose.yml, bukan localhost.
Langkah 3: Buat Dockerfile kustom untuk frontend
Buat file baru bernama Dockerfile.frontend untuk membuat image Docker frontend kustom Anda.
Isi file Dockerfile.frontend:
dockerfile
# Gunakan image osrm-frontend resmi sebagai basis
FROM osrm/osrm-frontend:latest
# Salin file leaflet_options.js kustom Anda ke dalam direktori src
COPY leaflet_options.js src/leaflet_options.js
# Kompilasi ulang aset frontend dengan konfigurasi baru
RUN npm run compile
Gunakan kode dengan hati-hati.
COPY akan menimpa file asli di dalam image dengan versi kustom Anda. Perintah RUN npm run compile memastikan bahwa semua aset JavaScript diperbarui dengan perubahan konfigurasi Anda.
Langkah 4: Tulis file docker-compose.yml
Buat docker-compose.yml yang akan menyatukan semua layanan (backend dan frontend) yang berbeda.
Isi file docker-compose.yml:
yaml
version: '3.7'
services:
# OSRM Backend untuk profil Mobil
backend-car:
image: osrm/osrm-backend:latest
container_name: osrm_backend_car
volumes:
- ./data:/data
command: osrm-routed --algorithm mld /data/indonesia-latest-car.osrm
ports:
- "5000:5000" # Tetap menggunakan port 5000 di dalam container
restart: always
# OSRM Backend untuk profil Sepeda
backend-bike:
image: osrm/osrm-backend:latest
container_name: osrm_backend_bike
volumes:
- ./data:/data
command: osrm-routed --algorithm mld /data/indonesia-latest-bike.osrm
ports:
- "5001:5000" # Mapping ke port 5001 di host
restart: always
# OSRM Backend untuk profil Jalan Kaki
backend-foot:
image: osrm/osrm-backend:latest
container_name: osrm_backend_foot
volumes:
- ./data:/data
command: osrm-routed --algorithm mld /data/indonesia-latest-foot.osrm
ports:
- "5002:5000" # Mapping ke port 5002 di host
restart: always
# Frontend menggunakan image kustom
frontend:
build:
context: .
dockerfile: Dockerfile.frontend
container_name: osrm_frontend
ports:
- "9966:9966"
depends_on:
- backend-car
- backend-bike
- backend-foot
restart: always
Gunakan kode dengan hati-hati.
Penjelasan penting:
build: Menunjuk ke direktori saat ini (.) dan Dockerfile.frontend untuk membuat image kustom.
volumes: Pastikan semua backend mengacu pada direktori data yang sama.
ports: Backend berjalan pada port 5000 di dalam setiap kontainer, tetapi dipetakan ke port yang berbeda di host untuk menghindari konflik. Frontend tidak perlu terhubung ke port host ini karena ia berkomunikasi dengan nama layanan backend (misalnya backend-car) di jaringan Docker.
depends_on: Memastikan bahwa semua backend sudah berjalan sebelum frontend mencoba untuk memulai.
Langkah 5: Jalankan Docker Compose
Dari direktori proyek Anda, jalankan perintah berikut di terminal:
bash
# Lakukan proses data terlebih dahulu (extract, partition, customize)
# Perintah ini akan memakan waktu.
docker compose run --rm backend-car osrm-extract -p /opt/car.lua /data/indonesia-latest.osm.pbf
docker compose run --rm backend-car osrm-partition /data/indonesia-latest.osrm
docker compose run --rm backend-car osrm-customize /data/indonesia-latest.osrm
# Lakukan hal yang sama untuk profil sepeda dan jalan kaki
docker compose run --rm backend-bike osrm-extract -p /opt/bicycle.lua /data/indonesia-latest.osm.pbf
# ... dan seterusnya ...
# Jalankan semua layanan
docker compose up --build -d
Gunakan kode dengan hati-hati.
Setelah semua kontainer berjalan, Anda bisa mengakses frontend Anda di http://localhost:9966. Anda akan melihat dropdown dengan profil rute yang telah Anda definisikan.
\ No newline at end of file
diff --git a/scripts/install-osrm-ngpm.sh b/scripts/install-osrm-ngpm.sh
index bde75a6..c02ed76 100755
--- a/scripts/install-osrm-ngpm.sh
+++ b/scripts/install-osrm-ngpm.sh
@@ -2,7 +2,7 @@
#
# install-osrm-ngpm.sh
# OSRM + NGPM installer & manager (Rocky Linux 10)
-# Final self-contained script (v2.6 - Final Cleanup & Readability)
+# Final self-contained script (v2.7 - Correct Skip-Preprocessing Check)
#
set -euo pipefail
IFS=$'\n\t'
@@ -52,10 +52,17 @@ LOGFILE=""
PROFILE_ARR=()
# -------------------------
-# Logging helpers (unchanged)
+# Logging helpers
# -------------------------
timestamp() { date '+%Y-%m-%d %H:%M:%S'; }
-_log() { local lvl="$1"; shift; local ts; ts="$(timestamp)"; if [[ -n "${LOGFILE:-}" ]]; then printf "%s [%s] %s\n" "$ts" "$lvl" "$*" >> "${LOGFILE}"; fi; printf "%s [%s] %s\n" "$ts" "$lvl" "$*"; }
+_log() {
+ local lvl="$1"; shift
+ local ts; ts="$(timestamp)"
+ if [[ -n "${LOGFILE:-}" ]]; then
+ printf "%s [%s] %s\n" "$ts" "$lvl" "$*" >> "${LOGFILE}"
+ fi
+ printf "%s [%s] %s\n" "$ts" "$lvl" "$*"
+}
info() { _log "INFO" "$*"; }
success() { _log "SUCCESS" "$*"; }
warn() { _log "WARN" "$*"; }
@@ -133,21 +140,89 @@ parse_args() {
}
# -------------------------
-# Helper Functions (unchanged)
+# Helper Functions
# -------------------------
command_exists() { command -v "$1" >/dev/null 2>&1; }
-prereqs_check() { info "Running prerequisite checks..."; local ok=0; if ! command_exists docker; then err "Docker is not installed."; ok=1; fi; if ! docker compose version >/dev/null 2>&1; then err "Docker Compose V2 plugin is not installed."; ok=1; fi; if ! command_exists curl; then err "curl is not installed."; ok=1; fi; if [ ${ok} -ne 0 ]; then err "Prerequisite checks failed."; exit 1; fi; success "All prerequisites are met."; }
-SELINUX_LABEL=""; setup_selinux_flag() { if command_exists sestatus && sestatus 2>/dev/null | grep -qi "SELinux status:.*enabled"; then SELINUX_LABEL=",z"; info "SELinux is enabled. Appending ',z' to volume mounts."; fi; }
-ask_confirm() { local prompt="$1"; if [ "${yes_all}" = true ]; then info "[auto-yes] ${prompt} -> yes"; return 0; fi; while true; do printf "%s [y/N]: " "${prompt}"; read -r yn; case "${yn}" in [Yy]*) return 0 ;; [Nn]*|"") return 1 ;; *) echo "Please answer y or n." ;; esac; done; }
+prereqs_check() {
+ info "Running prerequisite checks..."
+ local ok=0
+ if ! command_exists docker; then err "Docker is not installed."; ok=1; fi
+ if ! docker compose version >/dev/null 2>&1; then err "Docker Compose V2 plugin is not installed."; ok=1; fi
+ if ! command_exists curl; then err "curl is not installed."; ok=1; fi
+ if [ ${ok} -ne 0 ]; then err "Prerequisite checks failed."; exit 1; fi
+ success "All prerequisites are met."
+}
+SELINUX_LABEL="";
+setup_selinux_flag() {
+ if command_exists sestatus && sestatus 2>/dev/null | grep -qi "SELinux status:.*enabled"; then
+ SELINUX_LABEL=",z"
+ info "SELinux is enabled. Appending ',z' to volume mounts."
+ fi
+}
+ask_confirm() {
+ local prompt="$1"
+ if [ "${yes_all}" = true ]; then info "[auto-yes] ${prompt} -> yes"; return 0; fi
+ while true; do
+ printf "%s [y/N]: " "${prompt}"
+ read -r yn
+ case "${yn}" in
+ [Yy]*) return 0 ;;
+ [Nn]*|"") return 1 ;;
+ *) echo "Please answer y or n." ;;
+ esac
+ done
+}
# -------------------------
# Core Logic
# -------------------------
-download_map() { info "Preparing map from source: ${map_source}"; local map_instance_dir="${work_dir}/${map_name}/dataset"; mkdir -p "${map_instance_dir}"; MAP_PATH="${map_instance_dir}/$(basename "${map_source}")"; if [[ -f "${MAP_PATH}" ]]; then info "Map file already exists. Skipping download."; return 0; fi; if [[ "${map_source}" =~ ^https?:// ]]; then if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would download map to ${MAP_PATH}"; return 0; fi; info "Downloading map with curl..."; if ! curl -L --fail -o "${MAP_PATH}.tmp" "${map_source}"; then rm -f "${MAP_PATH}.tmp"; err "Download failed."; exit 1; fi; mv "${MAP_PATH}.tmp" "${MAP_PATH}"; success "Map downloaded successfully."; elif [[ -f "${map_source}" ]]; then if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would copy local map to ${MAP_PATH}"; return 0; fi; info "Copying local map file..."; cp -v "${map_source}" "${MAP_PATH}"; success "Local map copied."; else err "Map source '${map_source}' is not a valid URL or an existing file."; exit 1; fi; }
-generate_profiles() { info "Generating profile files for map instance '${map_name}'"; local profile_dir="${work_dir}/${map_name}/profiles"; local patched_dir="${work_dir}/${map_name}/patched-profiles"; mkdir -p "${profile_dir}" "${patched_dir}"; for profile in "${PROFILE_ARR[@]}"; do local pf="${profile_dir}/${profile}.lua"; if [ ! -f "${pf}" ]; then case "${profile}" in car|bicycle|bike|foot) info "Generating wrapper for built-in profile: ${profile}"; cat > "${pf}" <<LUA
+download_map() {
+ info "Preparing map from source: ${map_source}"
+ local map_instance_dir="${work_dir}/${map_name}/dataset"
+ mkdir -p "${map_instance_dir}"
+ MAP_PATH="${map_instance_dir}/$(basename "${map_source}")"
+ if [[ -f "${MAP_PATH}" ]]; then
+ info "Map file already exists. Skipping download."
+ return 0
+ fi
+ if [[ "${map_source}" =~ ^https?:// ]]; then
+ if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would download map to ${MAP_PATH}"; return 0; fi
+ info "Downloading map with curl..."
+ if ! curl -L --fail -o "${MAP_PATH}.tmp" "${map_source}"; then
+ rm -f "${MAP_PATH}.tmp"
+ err "Download failed."
+ exit 1
+ fi
+ mv "${MAP_PATH}.tmp" "${MAP_PATH}"
+ success "Map downloaded successfully."
+ elif [[ -f "${map_source}" ]]; then
+ if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would copy local map to ${MAP_PATH}"; return 0; fi
+ info "Copying local map file..."
+ cp -v "${map_source}" "${MAP_PATH}"
+ success "Local map copied."
+ else
+ err "Map source '${map_source}' is not a valid URL or an existing file."
+ exit 1
+ fi
+}
+generate_profiles() {
+ info "Generating profile files for map instance '${map_name}'"
+ local profile_dir="${work_dir}/${map_name}/profiles"
+ local patched_dir="${work_dir}/${map_name}/patched-profiles"
+ mkdir -p "${profile_dir}" "${patched_dir}"
+ for profile in "${PROFILE_ARR[@]}"; do
+ local pf="${profile_dir}/${profile}.lua"
+ if [ ! -f "${pf}" ]; then
+ case "${profile}" in
+ car|bicycle|bike|foot)
+ info "Generating wrapper for built-in profile: ${profile}"
+ cat > "${pf}" <<LUA
return dofile('/opt/${profile}.lua')
LUA
- ;; motorcycle) info "Generating custom 'motorcycle' profile based on 'car'"; cat > "${pf}" <<'LUA'
+ ;;
+ motorcycle)
+ info "Generating custom 'motorcycle' profile based on 'car'"
+ cat > "${pf}" <<'LUA'
local car_module = dofile('/opt/car.lua')
local profile = car_module.setup()
profile.properties.max_speed = 100
@@ -157,12 +232,28 @@ profile.restrictions["toll"] = "prohibited"
car_module.setup = function() return profile end
return car_module
LUA
- ;; *) warn "No generator for profile '${profile}'."; continue;; esac; fi; cp "${pf}" "${patched_dir}/${profile}.lua"; done; success "Profile generation complete."; }
+ ;;
+ *)
+ warn "No generator for profile '${profile}'."
+ continue
+ ;;
+ esac
+ fi
+ cp "${pf}" "${patched_dir}/${profile}.lua"
+ done
+ success "Profile generation complete."
+}
generate_docker_compose() {
info "Generating Docker Compose file: ${COMPOSE_FILE}"
- local map_instance_dir="${work_dir}/${map_name}"; local selabel="${SELINUX_LABEL:-}"; local services=""; local idx=0
+ local map_instance_dir="${work_dir}/${map_name}"
+ local selabel="${SELINUX_LABEL:-}"
+ local services=""
+ local idx=0
for profile in "${PROFILE_ARR[@]}"; do
- idx=$((idx+1)); local host_port=$((backend_port + idx - 1)); local pdata="${map_instance_dir}/${profile}-data"; mkdir -p "${pdata}"
+ idx=$((idx + 1))
+ local host_port=$((backend_port + idx - 1))
+ local pdata="${map_instance_dir}/${profile}-data"
+ mkdir -p "${pdata}"
services+="
osrm-preprocess-${map_name}-${profile}:
image: ${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}
@@ -188,12 +279,19 @@ generate_docker_compose() {
- ${network_name}
"
done
- local frontend_block=""; if [ "${no_frontend}" != true ]; then
+ local frontend_block=""
+ if [ "${no_frontend}" != true ]; then
if [ -z "${mapbox_token}" ]; then
- warn "Mapbox token not provided. The frontend map may not render. Use --mapbox-token <your_token>."
+ warn "Mapbox token not provided. The frontend map may not render."
fi
- local backend_urls=""; local idx2=0
- for profile in "${PROFILE_ARR[@]}"; do idx2=$((idx2+1)); local host_port=$((backend_port + idx2 - 1)); local url="http://${backend_domain}:${host_port}${backend_path}/${api_version}/${profile}"; backend_urls+="${url},"; done
+ local backend_urls=""
+ local idx2=0
+ for profile in "${PROFILE_ARR[@]}"; do
+ idx2=$((idx2 + 1))
+ local host_port=$((backend_port + idx2 - 1))
+ local url="http://${backend_domain}:${host_port}${backend_path}/${api_version}/${profile}"
+ backend_urls+="${url},"
+ done
backend_urls=${backend_urls%,}
frontend_block+="
osrm-frontend-${map_name}:
@@ -224,15 +322,29 @@ EOF
# -------------------------
run_install() {
info "Starting OSRM installation for map instance: '${map_name}'"
- if [ -f "${COMPOSE_FILE}" ] && ! ask_confirm "Instance '${map_name}' may exist. Re-running will update config and recreate containers. Continue?"; then info "Installation aborted."; exit 0; fi
- download_map; generate_profiles; generate_docker_compose
- if [ "${dry_run}" = true ]; then warn "[DRY RUN] Skipping network and container startup."; return; fi
- info "Ensuring Docker network '${network_name}' exists..."; docker network create "${network_name}" >/dev/null 2>&1 || info "Network already exists."
- info "Pulling latest Docker images..."; docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"; if [[ "${no_frontend}" != "true" ]]; then docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"; fi
+ if [ -f "${COMPOSE_FILE}" ] && ! ask_confirm "Instance '${map_name}' may exist. Re-running will update config and recreate containers. Continue?"; then
+ info "Installation aborted."
+ exit 0
+ fi
+ download_map
+ generate_profiles
+ generate_docker_compose
+ if [ "${dry_run}" = true ]; then
+ warn "[DRY RUN] Skipping network and container startup."
+ return
+ fi
+ info "Ensuring Docker network '${network_name}' exists..."
+ docker network create "${network_name}" >/dev/null 2>&1 || info "Network already exists."
+ info "Pulling latest Docker images..."
+ docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"
+ if [[ "${no_frontend}" != "true" ]]; then
+ docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"
+ fi
local needs_preprocessing=false
for profile in "${PROFILE_ARR[@]}"; do
local pdata_dir="${work_dir}/${map_name}/${profile}-data"
- if [ ! -f "${pdata_dir}/map.osrm" ]; then
+ # FIX (v2.7): Check for the .properties file, which is a reliable indicator of completion for MLD.
+ if [ ! -f "${pdata_dir}/map.osrm.properties" ]; then
needs_preprocessing=true
break
fi
@@ -241,25 +353,33 @@ run_install() {
info "Starting data preprocessing for missing profiles..."
for profile in "${PROFILE_ARR[@]}"; do
local pdata_dir="${work_dir}/${map_name}/${profile}-data"
- if [ -f "${pdata_dir}/map.osrm" ]; then
+ # FIX (v2.7): Same check here.
+ if [ -f "${pdata_dir}/map.osrm.properties" ]; then
info "--- Skipping profile '${profile}', data already exists. ---"
continue
fi
- info "Preparing map file for profile '${profile}'..."; cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
+ info "Preparing map file for profile '${profile}'..."
+ cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
local service_name="osrm-preprocess-${map_name}-${profile}"
info "--- Processing profile: ${profile} ---"
if docker compose -f "${COMPOSE_FILE}" up --build --exit-code-from "${service_name}" "${service_name}"; then
success "Preprocessing for '${profile}' completed."
else
- err "Preprocessing for '${profile}' FAILED. Check logs for details."; if ! ask_confirm "Continue with other profiles?"; then err "Aborting installation."; exit 1; fi
+ err "Preprocessing for '${profile}' FAILED. Check logs for details."
+ if ! ask_confirm "Continue with other profiles?"; then
+ err "Aborting installation."
+ exit 1
+ fi
fi
docker compose -f "${COMPOSE_FILE}" rm -fsv "${service_name}" >/dev/null 2>&1 || true
done
else
info "All profile data already exists, skipping preprocessing."
fi
- info "Starting all services for instance '${map_name}'..."; docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
- success "Installation for '${map_name}' completed!"; print_summary
+ info "Starting all services for instance '${map_name}'..."
+ docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
+ success "Installation for '${map_name}' completed!"
+ print_summary
}
run_update() {
info "Starting update for instance '${map_name}', component '${sub_command}'"
@@ -268,36 +388,36 @@ run_update() {
exit 1
fi
case "${sub_command}" in
- backend|frontend|all)
- info "Pulling latest images..."
- docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"
- if [[ "${sub_command}" != "backend" ]]; then
- docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"
- fi
- info "Recreating services..."
- docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
- ;;
- map|profile)
- if [[ "${sub_command}" == "map" ]]; then
- download_map
- fi
- generate_profiles
- info "Re-running preprocessing..."
- for profile in "${PROFILE_ARR[@]}"; do
- local pdata_dir="${work_dir}/${map_name}/${profile}-data"
- info "Preparing map file for profile '${profile}'..."
- cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
- local service_name="osrm-preprocess-${map_name}-${profile}"
- docker compose -f "${COMPOSE_FILE}" up --build --exit-code-from "${service_name}" "${service_name}" || err "Preprocessing failed for ${profile}"
- docker compose -f "${COMPOSE_FILE}" rm -fsv "${service_name}" >/dev/null 2>&1 || true
- done
- info "Restarting services..."
- docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
- ;;
- *)
- err "Invalid update component."
- exit 1
- ;;
+ backend | frontend | all)
+ info "Pulling latest images..."
+ docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"
+ if [[ "${sub_command}" != "backend" ]]; then
+ docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"
+ fi
+ info "Recreating services..."
+ docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
+ ;;
+ map | profile)
+ if [[ "${sub_command}" == "map" ]]; then
+ download_map
+ fi
+ generate_profiles
+ info "Re-running preprocessing..."
+ for profile in "${PROFILE_ARR[@]}"; do
+ local pdata_dir="${work_dir}/${map_name}/${profile}-data"
+ info "Preparing map file for profile '${profile}'..."
+ cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
+ local service_name="osrm-preprocess-${map_name}-${profile}"
+ docker compose -f "${COMPOSE_FILE}" up --build --exit-code-from "${service_name}" "${service_name}" || err "Preprocessing failed for ${profile}"
+ docker compose -f "${COMPOSE_FILE}" rm -fsv "${service_name}" >/dev/null 2>&1 || true
+ done
+ info "Restarting services..."
+ docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
+ ;;
+ *)
+ err "Invalid update component."
+ exit 1
+ ;;
esac
success "Update for '${map_name}' completed."
}
@@ -350,10 +470,10 @@ main() {
info "Starting script execution."
info "Command: ${command_name}, Map Instance: ${map_name}"
case "${command_name}" in
- install) run_install ;;
- update) run_update ;;
- remove) run_remove ;;
- *) err "Unknown command: ${command_name}"; usage ;;
+ install) run_install ;;
+ update) run_update ;;
+ remove) run_remove ;;
+ *) err "Unknown command: ${command_name}"; usage ;;
esac
info "Script execution finished."
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
# install-osrm-ngpm.sh # install-osrm-ngpm.sh
# OSRM + NGPM installer & manager (Rocky Linux 10) # OSRM + NGPM installer & manager (Rocky Linux 10)
# Final self-contained script (v2.7 - Correct Skip-Preprocessing Check) # Final self-contained script (v3.8 - Fundamental Filename Logic Fix)
# #
set -euo pipefail set -euo pipefail
IFS=$'\n\t' IFS=$'\n\t'
...@@ -24,6 +24,8 @@ DEFAULT_BACKEND_DOMAIN="localhost" ...@@ -24,6 +24,8 @@ DEFAULT_BACKEND_DOMAIN="localhost"
DEFAULT_FRONTEND_DOMAIN="localhost" DEFAULT_FRONTEND_DOMAIN="localhost"
DEFAULT_BACKEND_PATH="/osrm" DEFAULT_BACKEND_PATH="/osrm"
DEFAULT_API_VERSION="v1" DEFAULT_API_VERSION="v1"
DEFAULT_MAP_CENTER="-6.2,106.8" # Jakarta
DEFAULT_MAP_ZOOM="13"
# ------------------------- # -------------------------
# RUN-TIME VARIABLES # RUN-TIME VARIABLES
...@@ -40,6 +42,8 @@ frontend_domain="${DEFAULT_FRONTEND_DOMAIN}" ...@@ -40,6 +42,8 @@ frontend_domain="${DEFAULT_FRONTEND_DOMAIN}"
backend_path="${DEFAULT_BACKEND_PATH}" backend_path="${DEFAULT_BACKEND_PATH}"
api_version="${DEFAULT_API_VERSION}" api_version="${DEFAULT_API_VERSION}"
mapbox_token="" mapbox_token=""
map_center="${DEFAULT_MAP_CENTER}"
map_zoom="${DEFAULT_MAP_ZOOM}"
no_frontend=false no_frontend=false
dry_run=false dry_run=false
yes_all=false yes_all=false
...@@ -55,14 +59,7 @@ PROFILE_ARR=() ...@@ -55,14 +59,7 @@ PROFILE_ARR=()
# Logging helpers # Logging helpers
# ------------------------- # -------------------------
timestamp() { date '+%Y-%m-%d %H:%M:%S'; } timestamp() { date '+%Y-%m-%d %H:%M:%S'; }
_log() { _log() { local lvl="$1"; shift; local ts; ts="$(timestamp)"; if [[ -n "${LOGFILE:-}" ]]; then printf "%s [%s] %s\n" "$ts" "$lvl" "$*" >> "${LOGFILE}"; fi; printf "%s [%s] %s\n" "$ts" "$lvl" "$*"; }
local lvl="$1"; shift
local ts; ts="$(timestamp)"
if [[ -n "${LOGFILE:-}" ]]; then
printf "%s [%s] %s\n" "$ts" "$lvl" "$*" >> "${LOGFILE}"
fi
printf "%s [%s] %s\n" "$ts" "$lvl" "$*"
}
info() { _log "INFO" "$*"; } info() { _log "INFO" "$*"; }
success() { _log "SUCCESS" "$*"; } success() { _log "SUCCESS" "$*"; }
warn() { _log "WARN" "$*"; } warn() { _log "WARN" "$*"; }
...@@ -73,26 +70,24 @@ err() { _log "ERROR" "$*" >&2; } ...@@ -73,26 +70,24 @@ err() { _log "ERROR" "$*" >&2; }
# ------------------------- # -------------------------
usage() { usage() {
cat <<EOF cat <<EOF
OSRM Installer & Manager v2.7 (Multi-Map Support) OSRM Installer & Manager v3.8
Usage: Usage:
$0 <command> --map-name <name> [options] $0 <command> --map-name <name> [options]
Commands: Commands:
install Install a new OSRM instance. install Install or update a full OSRM instance.
update <component> Update a component (backend, frontend, map, profile, all). update <component> Update a specific component (map, profile, backend, frontend).
remove Remove an OSRM instance. remove Remove an OSRM instance.
Required Argument: Required Argument:
--map-name <name> A unique name for this map instance (e.g., 'indonesia'). --map-name <name> A unique name for this map instance (e.g., 'indonesia').
Options: Options:
--map-source <url|path> Map URL or local file path. --map-source <url|path> Map URL or local file path.
--profiles <csv> Profiles to enable (car,motorcycle,bike,foot). --profiles <csv> Profiles to enable (car,motorcycle,bike,foot).
--mapbox-token <token> (Required for Frontend) Your Mapbox public access token.
--map-center <lat,lon> Initial map center coordinate (default: Jakarta).
--map-zoom <level> Initial map zoom level (default: 13).
--backend-port <port> Host start port for backend services. --backend-port <port> Host start port for backend services.
--frontend-port <port> Host port for the frontend UI. --frontend-port <port> Host port for the frontend UI.
--mapbox-token <token> (Required for Frontend) Your Mapbox public access token. --no-frontend Do NOT include the frontend service.
--no-frontend Do NOT install the frontend UI.
--purge Also delete all data directories on remove. --purge Also delete all data directories on remove.
--dry-run Simulate steps without executing. --dry-run Simulate steps without executing.
--yes Answer 'yes' to all prompts. --yes Answer 'yes' to all prompts.
...@@ -114,24 +109,22 @@ parse_args() { ...@@ -114,24 +109,22 @@ parse_args() {
while (( "$#" )); do while (( "$#" )); do
case "$1" in case "$1" in
--map-name) map_name="$2"; shift 2 ;; --map-name) map_name="$2"; shift 2 ;;
--work-dir) work_dir="${2/#\~/$HOME}"; shift 2 ;;
--map-source) map_source="$2"; shift 2 ;; --map-source) map_source="$2"; shift 2 ;;
--profiles) profiles_csv="$2"; shift 2 ;; --profiles) profiles_csv="$2"; shift 2 ;;
--mapbox-token) mapbox_token="$2"; shift 2 ;;
--map-center) map_center="$2"; shift 2 ;;
--map-zoom) map_zoom="$2"; shift 2 ;;
--backend-port) backend_port="$2"; shift 2 ;; --backend-port) backend_port="$2"; shift 2 ;;
--frontend-port) frontend_port="$2"; shift 2 ;; --frontend-port) frontend_port="$2"; shift 2 ;;
--network) network_name="$2"; shift 2 ;;
--backend-domain) backend_domain="$2"; shift 2 ;;
--frontend-domain) frontend_domain="$2"; shift 2 ;;
--mapbox-token) mapbox_token="$2"; shift 2 ;;
--no-frontend) no_frontend=true; shift ;; --no-frontend) no_frontend=true; shift ;;
--dry-run) dry_run=true; shift ;;
--purge) do_purge=true; shift ;; --purge) do_purge=true; shift ;;
--dry-run) dry_run=true; shift ;;
--yes) yes_all=true; shift ;; --yes) yes_all=true; shift ;;
-h|--help) usage ;; -h|--help) usage ;;
*) err "Unknown option: $1"; usage ;; *) err "Unknown option: $1"; usage ;;
esac esac
done done
if [[ "${map_name}" == "default" && "${command_name}" != "install" ]]; then err "The --map-name argument is required."; usage; fi if [[ "${map_name}" == "default" ]]; then err "The --map-name argument is required."; usage; fi
work_dir="${work_dir/#\~/$HOME}"; local log_dir="${work_dir}/logs"; mkdir -p "${work_dir}" "${log_dir}" work_dir="${work_dir/#\~/$HOME}"; local log_dir="${work_dir}/logs"; mkdir -p "${work_dir}" "${log_dir}"
LOGFILE="${log_dir}/osrm-manager-$(date '+%Y%m%d_%H%M%S').log"; : > "${LOGFILE}" LOGFILE="${log_dir}/osrm-manager-$(date '+%Y%m%d_%H%M%S').log"; : > "${LOGFILE}"
COMPOSE_FILE="${work_dir}/docker-compose.${map_name}.yml" COMPOSE_FILE="${work_dir}/docker-compose.${map_name}.yml"
...@@ -143,35 +136,9 @@ parse_args() { ...@@ -143,35 +136,9 @@ parse_args() {
# Helper Functions # Helper Functions
# ------------------------- # -------------------------
command_exists() { command -v "$1" >/dev/null 2>&1; } command_exists() { command -v "$1" >/dev/null 2>&1; }
prereqs_check() { prereqs_check() { info "Running prerequisite checks..."; local ok=0; if ! command_exists docker; then err "Docker is not installed."; ok=1; fi; if ! docker compose version >/dev/null 2>&1; then err "Docker Compose V2 plugin is not installed."; ok=1; fi; if ! command_exists curl; then err "curl is not installed."; ok=1; fi; if [ ${ok} -ne 0 ]; then err "Prerequisite checks failed."; exit 1; fi; success "All prerequisites are met."; }
info "Running prerequisite checks..." SELINUX_LABEL=""; setup_selinux_flag() { if command_exists sestatus && sestatus 2>/dev/null | grep -qi "SELinux status:.*enabled"; then SELINUX_LABEL=",z"; info "SELinux is enabled. Appending ',z' to volume mounts."; fi; }
local ok=0 ask_confirm() { local prompt="$1"; if [ "${yes_all}" = true ]; then info "[auto-yes] ${prompt} -> yes"; return 0; fi; while true; do printf "%s [y/N]: " "${prompt}"; read -r yn; case "${yn}" in [Yy]*) return 0 ;; [Nn]*|"") return 1 ;; *) echo "Please answer y or n." ;; esac; done; }
if ! command_exists docker; then err "Docker is not installed."; ok=1; fi
if ! docker compose version >/dev/null 2>&1; then err "Docker Compose V2 plugin is not installed."; ok=1; fi
if ! command_exists curl; then err "curl is not installed."; ok=1; fi
if [ ${ok} -ne 0 ]; then err "Prerequisite checks failed."; exit 1; fi
success "All prerequisites are met."
}
SELINUX_LABEL="";
setup_selinux_flag() {
if command_exists sestatus && sestatus 2>/dev/null | grep -qi "SELinux status:.*enabled"; then
SELINUX_LABEL=",z"
info "SELinux is enabled. Appending ',z' to volume mounts."
fi
}
ask_confirm() {
local prompt="$1"
if [ "${yes_all}" = true ]; then info "[auto-yes] ${prompt} -> yes"; return 0; fi
while true; do
printf "%s [y/N]: " "${prompt}"
read -r yn
case "${yn}" in
[Yy]*) return 0 ;;
[Nn]*|"") return 1 ;;
*) echo "Please answer y or n." ;;
esac
done
}
# ------------------------- # -------------------------
# Core Logic # Core Logic
...@@ -181,85 +148,113 @@ download_map() { ...@@ -181,85 +148,113 @@ download_map() {
local map_instance_dir="${work_dir}/${map_name}/dataset" local map_instance_dir="${work_dir}/${map_name}/dataset"
mkdir -p "${map_instance_dir}" mkdir -p "${map_instance_dir}"
MAP_PATH="${map_instance_dir}/$(basename "${map_source}")" MAP_PATH="${map_instance_dir}/$(basename "${map_source}")"
if [[ -f "${MAP_PATH}" ]]; then if [[ -f "${MAP_PATH}" ]]; then info "Map file already exists. Skipping download."; return 0; fi
info "Map file already exists. Skipping download."
return 0
fi
if [[ "${map_source}" =~ ^https?:// ]]; then if [[ "${map_source}" =~ ^https?:// ]]; then
if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would download map to ${MAP_PATH}"; return 0; fi if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would download map to ${MAP_PATH}"; return 0; fi
info "Downloading map with curl..." info "Downloading map with curl..."; if ! curl -L --fail -o "${MAP_PATH}.tmp" "${map_source}"; then rm -f "${MAP_PATH}.tmp"; err "Download failed."; exit 1; fi
if ! curl -L --fail -o "${MAP_PATH}.tmp" "${map_source}"; then mv "${MAP_PATH}.tmp" "${MAP_PATH}"; success "Map downloaded successfully."
rm -f "${MAP_PATH}.tmp"
err "Download failed."
exit 1
fi
mv "${MAP_PATH}.tmp" "${MAP_PATH}"
success "Map downloaded successfully."
elif [[ -f "${map_source}" ]]; then elif [[ -f "${map_source}" ]]; then
if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would copy local map to ${MAP_PATH}"; return 0; fi if [ "${dry_run}" = true ]; then warn "[DRY RUN] Would copy local map to ${MAP_PATH}"; return 0; fi
info "Copying local map file..." info "Copying local map file..."; cp -v "${map_source}" "${MAP_PATH}"; success "Local map copied."
cp -v "${map_source}" "${MAP_PATH}" else err "Map source '${map_source}' is not a valid URL or an existing file."; exit 1;
success "Local map copied."
else
err "Map source '${map_source}' is not a valid URL or an existing file."
exit 1
fi fi
} }
generate_profiles() { generate_profiles() {
info "Generating profile files for map instance '${map_name}'" info "Generating profile files for map instance '${map_name}'"
local profile_dir="${work_dir}/${map_name}/profiles" local profile_dir="${work_dir}/${map_name}/profiles"; local patched_dir="${work_dir}/${map_name}/patched-profiles"
local patched_dir="${work_dir}/${map_name}/patched-profiles"
mkdir -p "${profile_dir}" "${patched_dir}" mkdir -p "${profile_dir}" "${patched_dir}"
for profile in "${PROFILE_ARR[@]}"; do for profile in "${PROFILE_ARR[@]}"; do
local pf="${profile_dir}/${profile}.lua" local pf="${profile_dir}/${profile}.lua"
if [ ! -f "${pf}" ]; then if [ ! -f "${pf}" ]; then
case "${profile}" in case "${profile}" in
car|bicycle|bike|foot) car|bicycle|bike|foot)
info "Generating wrapper for built-in profile: ${profile}" info "Generating wrapper for built-in profile: ${profile}"; cat > "${pf}" <<LUA
cat > "${pf}" <<LUA
return dofile('/opt/${profile}.lua') return dofile('/opt/${profile}.lua')
LUA LUA
;; ;;
motorcycle) motorcycle)
info "Generating custom 'motorcycle' profile based on 'car'" info "Generating custom 'motorcycle' profile based on 'car'"; cat > "${pf}" <<'LUA'
cat > "${pf}" <<'LUA'
local car_module = dofile('/opt/car.lua') local car_module = dofile('/opt/car.lua')
local profile = car_module.setup() local profile = car_module.setup()
profile.properties.max_speed = 100 profile.properties.max_speed = 90
profile.properties.u_turn_penalty = 60 profile.properties.u_turn_penalty = 60
profile.restrictions = profile.restrictions or {} profile.restrictions = profile.restrictions or {}
profile.restrictions["toll"] = "prohibited" profile.restrictions["toll"] = "prohibited"
profile.speed_profile["motorway"] = 90
profile.speed_profile["motorway_link"] = 45
car_module.setup = function() return profile end car_module.setup = function() return profile end
return car_module return car_module
LUA LUA
;; ;;
*) *) warn "No generator for profile '${profile}'. Please create it manually."; continue;;
warn "No generator for profile '${profile}'."
continue
;;
esac esac
fi fi
cp "${pf}" "${patched_dir}/${profile}.lua" cp "${pf}" "${patched_dir}/${profile}.lua"
done done
success "Profile generation complete." success "Profile generation complete."
} }
generate_leaflet_options() {
local map_instance_dir="${work_dir}/${map_name}"; local leaflet_options_path="${map_instance_dir}/leaflet_options.js"
info "Generating custom leaflet_options.js for '${map_name}'"
local services_js="services: [\n"; for profile in "${PROFILE_ARR[@]}"; do
local label; label="$(tr '[:lower:]' '[:upper:]' <<< ${profile:0:1})${profile:1}"
local backend_service_name="osrm-routed-${map_name}-${profile}"
services_js+=" {\n label: '${label}',\n path: 'http://${backend_service_name}:5000/route/v1'\n },\n"
done; services_js+=" ],"
cat > "${leaflet_options_path}" <<EOF
module.exports = {
$services_js
center: [ ${map_center} ],
zoom: ${map_zoom}
};
EOF
success "leaflet_options.js generated at ${leaflet_options_path}"
}
generate_frontend_dockerfile() {
local map_instance_dir="${work_dir}/${map_name}"; local dockerfile_path="${map_instance_dir}/Dockerfile.frontend"
info "Generating custom Dockerfile.frontend for '${map_name}'"
cat > "${dockerfile_path}" <<EOF
FROM ${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}
COPY leaflet_options.js src/leaflet_options.js
RUN npm run compile
EOF
success "Dockerfile.frontend generated at ${dockerfile_path}"
}
generate_docker_compose() { generate_docker_compose() {
info "Generating Docker Compose file: ${COMPOSE_FILE}" info "Generating Docker Compose file: ${COMPOSE_FILE}"
local map_instance_dir="${work_dir}/${map_name}" local map_instance_dir="${work_dir}/${map_name}"
local selabel="${SELINUX_LABEL:-}" local selabel="${SELINUX_LABEL:-}"
local services=""
# Start writing the file
cat > "${COMPOSE_FILE}" <<EOF
services:
EOF
# Append backend services
local idx=0 local idx=0
for profile in "${PROFILE_ARR[@]}"; do for profile in "${PROFILE_ARR[@]}"; do
idx=$((idx + 1)) idx=$((idx + 1))
local host_port=$((backend_port + idx - 1)) local host_port=$((backend_port + idx - 1))
local pdata="${map_instance_dir}/${profile}-data" local pdata="${map_instance_dir}/${profile}-data"
# --- FIX IS HERE (v3.8) ---
# 1. Use a clean base name for all operations.
local base_name="${map_name}-${profile}"
# 2. Use this base name for the symlink and all osrm commands.
# 3. This ensures output files are named `indonesia-car.osrm.*` correctly.
mkdir -p "${pdata}" mkdir -p "${pdata}"
services+="
cat >> "${COMPOSE_FILE}" <<EOF
osrm-preprocess-${map_name}-${profile}: osrm-preprocess-${map_name}-${profile}:
image: ${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION} image: ${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}
container_name: osrm-preprocess-${map_name}-${profile} container_name: osrm-preprocess-${map_name}-${profile}
command: > command: >
sh -c \"osrm-extract -p /opt/custom_profiles/${profile}.lua /data/map.osm.pbf && osrm-partition /data/map.osrm && osrm-customize /data/map.osrm\" sh -c 'ln -s map.osm.pbf /data/${base_name}.osm.pbf &&
osrm-extract -p /opt/custom_profiles/${profile}.lua /data/${base_name}.osm.pbf &&
osrm-partition /data/${base_name}.osrm &&
osrm-customize /data/${base_name}.osrm'
volumes: volumes:
- ${map_instance_dir}/patched-profiles:/opt/custom_profiles:ro${selabel} - ${map_instance_dir}/patched-profiles:/opt/custom_profiles:ro${selabel}
- ${pdata}:/data:rw${selabel} - ${pdata}:/data:rw${selabel}
...@@ -270,45 +265,46 @@ generate_docker_compose() { ...@@ -270,45 +265,46 @@ generate_docker_compose() {
image: ${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION} image: ${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}
container_name: osrm-routed-${map_name}-${profile} container_name: osrm-routed-${map_name}-${profile}
restart: unless-stopped restart: unless-stopped
command: osrm-routed --algorithm mld /data/map.osrm command: osrm-routed --algorithm mld /data/${base_name}.osrm
volumes: volumes:
- ${pdata}:/data:ro${selabel} - ${pdata}:/data:ro${selabel}
ports: ports:
- \"${host_port}:5000\" - "${host_port}:5000"
networks: networks:
- ${network_name} - ${network_name}
" EOF
done done
local frontend_block=""
# Append frontend service
if [ "${no_frontend}" != true ]; then if [ "${no_frontend}" != true ]; then
if [ -z "${mapbox_token}" ]; then if [ -z "${mapbox_token}" ]; then warn "Mapbox token not provided. The frontend map may not render."; fi
warn "Mapbox token not provided. The frontend map may not render."
fi cat >> "${COMPOSE_FILE}" <<EOF
local backend_urls=""
local idx2=0
for profile in "${PROFILE_ARR[@]}"; do
idx2=$((idx2 + 1))
local host_port=$((backend_port + idx2 - 1))
local url="http://${backend_domain}:${host_port}${backend_path}/${api_version}/${profile}"
backend_urls+="${url},"
done
backend_urls=${backend_urls%,}
frontend_block+="
osrm-frontend-${map_name}: osrm-frontend-${map_name}:
image: ${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION} build:
context: ${map_instance_dir}
dockerfile: Dockerfile.frontend
container_name: osrm-frontend-${map_name} container_name: osrm-frontend-${map_name}
restart: unless-stopped restart: unless-stopped
environment: environment:
- VITE_DEFAULT_BACKEND_URLS=${backend_urls} - OSRM_MAPBOX_TOKEN=${mapbox_token}
- VITE_MAPBOX_ACCESS_TOKEN=${mapbox_token}
ports: ports:
- \"${frontend_port}:9966\" - "${frontend_port}:9966"
networks: networks:
- ${network_name} - ${network_name}
" depends_on:
EOF
for profile in "${PROFILE_ARR[@]}"; do
cat >> "${COMPOSE_FILE}" <<EOF
- osrm-routed-${map_name}-${profile}
EOF
done
fi fi
cat > "${COMPOSE_FILE}" <<EOF
services:${services}${frontend_block} # Append networks block
cat >> "${COMPOSE_FILE}" <<EOF
networks: networks:
${network_name}: ${network_name}:
external: true external: true
...@@ -322,29 +318,23 @@ EOF ...@@ -322,29 +318,23 @@ EOF
# ------------------------- # -------------------------
run_install() { run_install() {
info "Starting OSRM installation for map instance: '${map_name}'" info "Starting OSRM installation for map instance: '${map_name}'"
if [ -f "${COMPOSE_FILE}" ] && ! ask_confirm "Instance '${map_name}' may exist. Re-running will update config and recreate containers. Continue?"; then if [ -f "${COMPOSE_FILE}" ] && ! ask_confirm "Instance '${map_name}' may exist. Re-running will update config and recreate/rebuild containers. Continue?"; then info "Installation aborted."; exit 0; fi
info "Installation aborted." download_map; generate_profiles
exit 0
fi
download_map
generate_profiles
generate_docker_compose
if [ "${dry_run}" = true ]; then
warn "[DRY RUN] Skipping network and container startup."
return
fi
info "Ensuring Docker network '${network_name}' exists..."
docker network create "${network_name}" >/dev/null 2>&1 || info "Network already exists."
info "Pulling latest Docker images..."
docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"
if [[ "${no_frontend}" != "true" ]]; then if [[ "${no_frontend}" != "true" ]]; then
docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}" generate_leaflet_options; generate_frontend_dockerfile
fi fi
generate_docker_compose
if [ "${dry_run}" = true ]; then warn "[DRY RUN] Skipping network and container startup."; return; fi
info "Ensuring Docker network '${network_name}' exists..."; docker network create "${network_name}" >/dev/null 2>&1 || info "Network already exists."
info "Pulling latest base Docker images..."; docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"; if [[ "${no_frontend}" != "true" ]]; then docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"; fi
local needs_preprocessing=false local needs_preprocessing=false
for profile in "${PROFILE_ARR[@]}"; do for profile in "${PROFILE_ARR[@]}"; do
local pdata_dir="${work_dir}/${map_name}/${profile}-data" local pdata_dir="${work_dir}/${map_name}/${profile}-data"
# FIX (v2.7): Check for the .properties file, which is a reliable indicator of completion for MLD. # --- FIX IS HERE (v3.8) ---
if [ ! -f "${pdata_dir}/map.osrm.properties" ]; then # Use the correct base name to check for the properties file.
local base_name="${map_name}-${profile}"
if [ ! -f "${pdata_dir}/${base_name}.osrm.properties" ]; then
needs_preprocessing=true needs_preprocessing=true
break break
fi fi
...@@ -353,122 +343,89 @@ run_install() { ...@@ -353,122 +343,89 @@ run_install() {
info "Starting data preprocessing for missing profiles..." info "Starting data preprocessing for missing profiles..."
for profile in "${PROFILE_ARR[@]}"; do for profile in "${PROFILE_ARR[@]}"; do
local pdata_dir="${work_dir}/${map_name}/${profile}-data" local pdata_dir="${work_dir}/${map_name}/${profile}-data"
# FIX (v2.7): Same check here. local base_name="${map_name}-${profile}"
if [ -f "${pdata_dir}/map.osrm.properties" ]; then if [ -f "${pdata_dir}/${base_name}.osrm.properties" ]; then
info "--- Skipping profile '${profile}', data already exists. ---" info "--- Skipping profile '${profile}', data already exists. ---"
continue continue
fi fi
info "Preparing map file for profile '${profile}'..." info "Preparing map file for profile '${profile}'..."; cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
local service_name="osrm-preprocess-${map_name}-${profile}" local service_name="osrm-preprocess-${map_name}-${profile}"
info "--- Processing profile: ${profile} ---" info "--- Processing profile: ${profile} ---"
if docker compose -f "${COMPOSE_FILE}" up --build --exit-code-from "${service_name}" "${service_name}"; then if docker compose -f "${COMPOSE_FILE}" up --build --exit-code-from "${service_name}" "${service_name}"; then
success "Preprocessing for '${profile}' completed." success "Preprocessing for '${profile}' completed."
else else
err "Preprocessing for '${profile}' FAILED. Check logs for details." err "Preprocessing for '${profile}' FAILED."; if ! ask_confirm "Continue?"; then err "Aborting."; exit 1; fi
if ! ask_confirm "Continue with other profiles?"; then
err "Aborting installation."
exit 1
fi
fi fi
docker compose -f "${COMPOSE_FILE}" rm -fsv "${service_name}" >/dev/null 2>&1 || true docker compose -f "${COMPOSE_FILE}" rm -fsv "${service_name}" >/dev/null 2>&1 || true
done done
else else
info "All profile data already exists, skipping preprocessing." info "All profile data already exists, skipping preprocessing."
fi fi
info "Starting all services for instance '${map_name}'..." info "Starting all services for instance '${map_name}'..."; docker compose -f "${COMPOSE_FILE}" up --build -d --remove-orphans
docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans success "Installation for '${map_name}' completed!"; print_summary
success "Installation for '${map_name}' completed!"
print_summary
} }
run_update() { run_update() {
info "Starting update for instance '${map_name}', component '${sub_command}'" info "Starting update for instance '${map_name}', component '${sub_command}'"
if [ ! -f "${COMPOSE_FILE}" ]; then if [ ! -f "${COMPOSE_FILE}" ]; then err "Instance '${map_name}' not found."; exit 1; fi
err "Instance '${map_name}' not found." local map_instance_dir="${work_dir}/${map_name}/dataset"
exit 1 MAP_PATH=$(find "${map_instance_dir}" -name "*.osm.pbf" -print -quit)
if [[ "${sub_command}" != "backend" && "${sub_command}" != "frontend" && -z "${MAP_PATH}" ]]; then
err "Could not find existing map file for instance '${map_name}'."; exit 1;
fi fi
case "${sub_command}" in case "${sub_command}" in
backend | frontend | all) backend|frontend)
info "Pulling latest images..." info "Pulling latest base image for ${sub_command}...";
docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}" if [[ "${sub_command}" == "backend" ]]; then docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"; else docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"; fi
if [[ "${sub_command}" != "backend" ]]; then info "Rebuilding and recreating services..."; docker compose -f "${COMPOSE_FILE}" up --build -d --remove-orphans
docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"
fi
info "Recreating services..."
docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
;; ;;
map | profile) map) download_map; ;&
if [[ "${sub_command}" == "map" ]]; then profile)
download_map
fi
generate_profiles generate_profiles
info "Re-running preprocessing..." if [[ "${no_frontend}" != "true" ]]; then generate_leaflet_options; generate_frontend_dockerfile; fi
generate_docker_compose
info "Re-running preprocessing for all specified profiles..."
for profile in "${PROFILE_ARR[@]}"; do for profile in "${PROFILE_ARR[@]}"; do
local pdata_dir="${work_dir}/${map_name}/${profile}-data" local pdata_dir="${work_dir}/${map_name}/${profile}-data"
info "Preparing map file for profile '${profile}'..." info "Preparing map file for profile '${profile}'..."; cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
cp -vf "${MAP_PATH}" "${pdata_dir}/map.osm.pbf"
local service_name="osrm-preprocess-${map_name}-${profile}" local service_name="osrm-preprocess-${map_name}-${profile}"
docker compose -f "${COMPOSE_FILE}" up --build --exit-code-from "${service_name}" "${service_name}" || err "Preprocessing failed for ${profile}" docker compose -f "${COMPOSE_FILE}" up --build --exit-code-from "${service_name}" "${service_name}" || err "Preprocessing failed for ${profile}"
docker compose -f "${COMPOSE_FILE}" rm -fsv "${service_name}" >/dev/null 2>&1 || true docker compose -f "${COMPOSE_FILE}" rm -fsv "${service_name}" >/dev/null 2>&1 || true
done done
info "Restarting services..." info "Restarting all services..."; docker compose -f "${COMPOSE_FILE}" up --build -d --remove-orphans
docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
;;
*)
err "Invalid update component."
exit 1
;; ;;
*) err "Invalid update component. Use: backend, frontend, map, profile."; exit 1;;
esac esac
success "Update for '${map_name}' completed." success "Update for '${map_name}' completed."
} }
run_remove() { run_remove() {
info "Starting removal of OSRM instance: '${map_name}'" info "Starting removal of OSRM instance: '${map_name}'"
if [ ! -f "${COMPOSE_FILE}" ]; then if [ ! -f "${COMPOSE_FILE}" ]; then warn "Instance '${map_name}' not found."; fi
warn "Instance '${map_name}' not found."
fi
if ask_confirm "This will STOP and REMOVE all containers for instance '${map_name}'. Are you sure?"; then if ask_confirm "This will STOP and REMOVE all containers for instance '${map_name}'. Are you sure?"; then
if [ -f "${COMPOSE_FILE}" ]; then if [ -f "${COMPOSE_FILE}" ]; then docker compose -f "${COMPOSE_FILE}" down -v || warn "Could not stop containers."; fi
docker compose -f "${COMPOSE_FILE}" down -v || warn "Could not stop containers."
rm -f "${COMPOSE_FILE}"
fi
success "All containers for '${map_name}' have been removed."
if [[ "${do_purge}" == "true" ]]; then if [[ "${do_purge}" == "true" ]]; then
local instance_dir="${work_dir}/${map_name}" local instance_dir="${work_dir}/${map_name}"
if ask_confirm "PURGE ENABLED. This will DELETE '${instance_dir}'. IRREVERSIBLE. Proceed?"; then if ask_confirm "PURGE ENABLED. This will DELETE '${instance_dir}'. IRREVERSIBLE. Proceed?"; then
rm -rf "${instance_dir}" rm -rf "${instance_dir}"; rm -f "${COMPOSE_FILE}"; success "Instance data for '${map_name}' has been purged."
success "Instance data for '${map_name}' has been purged."
fi
fi fi
else
info "Removal aborted by user."
fi fi
else info "Removal aborted by user."; fi
} }
print_summary() { print_summary() {
echo "--- Instance '${map_name}' Summary ---" echo "--- Instance '${map_name}' Summary ---"; echo "Profiles: ${profiles_csv}"
echo "Compose File: ${COMPOSE_FILE}" echo "ACCESS POINTS:"; local idx=0
echo "Map Source: ${MAP_PATH}"
echo "Profiles: ${profiles_csv}"
echo
echo "ACCESS POINTS (Example):"
local idx=0
for profile in "${PROFILE_ARR[@]}"; do for profile in "${PROFILE_ARR[@]}"; do
idx=$((idx + 1)) idx=$((idx + 1)); local host_port=$((backend_port + idx - 1))
local host_port=$((backend_port + idx - 1)) echo " - Backend (${profile}): http://localhost:${host_port}"; echo " (NGPM Target: osrm-routed-${map_name}-${profile}:5000)"
echo " - Backend (${profile}): http://localhost:${host_port}${backend_path}/${api_version}/${profile}"
echo " (NGPM Target: osrm-routed-${map_name}-${profile}:5000)"
done done
if [ "${no_frontend}" != true ]; then if [ "${no_frontend}" != true ]; then
echo " - Frontend UI: http://localhost:${frontend_port}" echo " - Frontend UI: http://localhost:${frontend_port}"; echo " (NGPM Target: osrm-frontend-${map_name}:9966)"
echo " (NGPM Target: osrm-frontend-${map_name}:9966)"
fi fi
echo "--------------------------" echo "--------------------------"
} }
main() { main() {
parse_args "$@" parse_args "$@"; prereqs_check; setup_selinux_flag
prereqs_check info "Starting script execution."; info "Command: ${command_name}, Map Instance: ${map_name}"
setup_selinux_flag
info "Starting script execution."
info "Command: ${command_name}, Map Instance: ${map_name}"
case "${command_name}" in case "${command_name}" in
install) run_install ;; install) run_install ;;
update) run_update ;; update) run_update ;;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment