Commit c9b26f67 by Bambang Adrian

fix(osrm): self containded script v2.7 correct skip-mode in preprocessing check

parent 59775b48
diff --git a/scripts/install-osrm-ngpm.sh b/scripts/install-osrm-ngpm.sh
index 1c4dfef..f2bb274 100755
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.5 - Frontend & Domain Fix)
+# Final self-contained script (v2.7 - Final Cleanup & Readability)
-# 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'
@@ -20,7 +20,6 @@ DEFAULT_NETWORK="ngpm-network"
DEFAULT_PROFILES="car"
DEFAULT_BACKEND_PORT=5000
DEFAULT_FRONTEND_PORT=9966
-# FIX: Default domains changed to 'localhost' for out-of-the-box functionality
DEFAULT_BACKEND_DOMAIN="localhost"
DEFAULT_FRONTEND_DOMAIN="localhost"
DEFAULT_BACKEND_PATH="/osrm"
@@ -40,7 +39,7 @@ backend_domain="${DEFAULT_BACKEND_DOMAIN}"
frontend_domain="${DEFAULT_FRONTEND_DOMAIN}"
backend_path="${DEFAULT_BACKEND_PATH}"
api_version="${DEFAULT_API_VERSION}"
-mapbox_token="pk.eyJ1IjoiaXRsbHMiLCJhIjoiY21nc2QxdDBjMzcxNjJtcTJodzV0aHRzcSJ9.EcVZI7KfUuHDSSdMNRV3CA" # <-- NEW: Variable for Mapbox token
+mapbox_token=""
no_frontend=false
dry_run=false
yes_all=false
@@ -67,7 +66,7 @@ err() { _log "ERROR" "$*" >&2; }
@@ -52,10 +52,17 @@ LOGFILE=""
PROFILE_ARR=()
# -------------------------
-# Logging helpers (unchanged)
+# Logging helpers
# -------------------------
usage() {
cat <<EOF
-OSRM Installer & Manager v2.5 (Multi-Map Support)
+OSRM Installer & Manager v2.7 (Multi-Map Support)
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() {
}
Usage:
$0 <command> --map-name <name> [options]
@@ -116,7 +115,7 @@ parse_args() {
--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 ;; # <-- NEW: Parse Mapbox token
+ --mapbox-token) mapbox_token="$2"; shift 2 ;;
--no-frontend) no_frontend=true; shift ;;
--dry-run) dry_run=true; shift ;;
--purge) do_purge=true; shift ;;
@@ -203,7 +202,7 @@ generate_docker_compose() {
restart: unless-stopped
environment:
- VITE_DEFAULT_BACKEND_URLS=${backend_urls}
- - VITE_MAPBOX_ACCESS_TOKEN=${mapbox_token} # <-- NEW: Inject Mapbox token
+ - VITE_MAPBOX_ACCESS_TOKEN=${mapbox_token}
ports:
- \"${frontend_port}:9966\"
networks:
@@ -230,17 +229,14 @@ run_install() {
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
-
# -------------------------
-# 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"
- # Check if pre-processed data exists. If not, we need to run pre-processing.
if [ ! -f "${pdata_dir}/map.osrm" ]; then
- 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
done
-
if [[ "${needs_preprocessing}" == "true" ]]; then
@@ -241,25 +353,33 @@ run_install() {
info "Starting data preprocessing for missing profiles..."
for profile in "${PROFILE_ARR[@]}"; do
@@ -262,13 +258,103 @@ run_install() {
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
+ 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
}
-# ... (rest of the script is unchanged: update, remove, summary, main)
-run_update() { info "Starting update for instance '${map_name}', component '${sub_command}'"; if [ ! -f "${COMPOSE_FILE}" ]; then err "Instance '${map_name}' not found."; 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;; esac; success "Update for '${map_name}' completed."; }
-run_remove() { info "Starting removal of OSRM instance: '${map_name}'"; if [ ! -f "${COMPOSE_FILE}" ]; then 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 [ -f "${COMPOSE_FILE}" ]; then 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 local instance_dir="${work_dir}/${map_name}"; if ask_confirm "PURGE ENABLED. This will DELETE '${instance_dir}'. IRREVERSIBLE. Proceed?"; then rm -rf "${instance_dir}"; success "Instance data for '${map_name}' has been purged."; fi; fi; else info "Removal aborted by user."; fi; }
-print_summary() { echo "--- Instance '${map_name}' Summary ---"; echo "Compose File: ${COMPOSE_FILE}"; echo "Map Source: ${MAP_PATH}"; echo "Profiles: ${profiles_csv}"; echo; echo "ACCESS POINTS (Example):"; local idx=0; for profile in "${PROFILE_ARR[@]}"; do idx=$((idx + 1)); local host_port=$((backend_port + idx - 1)); echo " - Backend (${profile}): http://localhost:${host_port}${backend_path}/${api_version}/${profile}"; echo " (NGPM Target: osrm-routed-${map_name}-${profile}:5000)"; done; if [ "${no_frontend}" != true ]; then echo " - Frontend UI: http://localhost:${frontend_port}"; echo " (NGPM Target: osrm-frontend-${map_name}:9966)"; fi; echo "--------------------------"; }
-main() { parse_args "$@"; prereqs_check; setup_selinux_flag; 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 ;; esac; info "Script execution finished."; }
+run_update() {
+ info "Starting update for instance '${map_name}', component '${sub_command}'"
+ if [ ! -f "${COMPOSE_FILE}" ]; then
+ err "Instance '${map_name}' not found."
+ 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
+ ;;
+ esac
+ success "Update for '${map_name}' completed."
+}
+run_remove() {
+ info "Starting removal of OSRM instance: '${map_name}'"
+ if [ ! -f "${COMPOSE_FILE}" ]; then
+ 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 [ -f "${COMPOSE_FILE}" ]; then
+ docker compose -f "${COMPOSE_FILE}" down -v || warn "Could not stop containers."
+ rm -f "${COMPOSE_FILE}"
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
+ success "All containers for '${map_name}' have been removed."
+ if [[ "${do_purge}" == "true" ]]; then
+ local instance_dir="${work_dir}/${map_name}"
+ if ask_confirm "PURGE ENABLED. This will DELETE '${instance_dir}'. IRREVERSIBLE. Proceed?"; then
+ rm -rf "${instance_dir}"
+ success "Instance data for '${map_name}' has been purged."
+ fi
+ info "Recreating services..."
+ docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
+ ;;
+ map | profile)
+ if [[ "${sub_command}" == "map" ]]; then
+ download_map
+ fi
+ else
+ info "Removal aborted by user."
+ fi
+}
+print_summary() {
+ echo "--- Instance '${map_name}' Summary ---"
+ echo "Compose File: ${COMPOSE_FILE}"
+ echo "Map Source: ${MAP_PATH}"
+ echo "Profiles: ${profiles_csv}"
+ echo
+ echo "ACCESS POINTS (Example):"
+ local idx=0
+ for profile in "${PROFILE_ARR[@]}"; do
+ idx=$((idx + 1))
+ local host_port=$((backend_port + idx - 1))
+ echo " - Backend (${profile}): http://localhost:${host_port}${backend_path}/${api_version}/${profile}"
+ echo " (NGPM Target: osrm-routed-${map_name}-${profile}:5000)"
+ done
+ if [ "${no_frontend}" != true ]; then
+ echo " - Frontend UI: http://localhost:${frontend_port}"
+ echo " (NGPM Target: osrm-frontend-${map_name}:9966)"
+ fi
+ echo "--------------------------"
+}
+main() {
+ parse_args "$@"
+ prereqs_check
+ setup_selinux_flag
+ 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 ;;
+ esac
+ info "Script execution finished."
+}
main "$@"
\ No newline at end of file
+ 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 @@
#
# 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."
}
......
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