-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."; }
+ if command_exists sestatus && sestatus 2>/dev/null | grep -qi "SELinux status:.*enabled"; then
@@ -230,17 +229,14 @@ run_install() {
+ SELINUX_LABEL=",z"
if [ "${dry_run}" = true ]; then warn "[DRY RUN] Skipping network and container startup."; return; fi
+ info "SELinux is enabled. Appending ',z' to volume mounts."
info "Ensuring Docker network '${network_name}' exists..."; docker network create "${network_name}" >/dev/null 2>&1 || info "Network already exists."
+ fi
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
+}
-
+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"
- 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
- 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..."
local pdata_dir="${work_dir}/${map_name}/${profile}-data"
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
needs_preprocessing=true
break
break
fi
fi
done
@@ -241,25 +353,33 @@ run_install() {
-
if [[ "${needs_preprocessing}" == "true" ]]; then
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
@@ -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
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}'..."; docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans
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
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() {
-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."; }
info "Starting update for instance '${map_name}', component '${sub_command}'"
-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; }
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.";}
prereqs_check(){
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;}
info "Running prerequisite checks..."
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;}
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
local frontend_block="";if["${no_frontend}"!=true];then
local frontend_block=""
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. Use --mapbox-token <your_token>."
warn "Mapbox token not provided. The frontend map may not render."
fi
fi
local backend_urls="";local idx2=0
local backend_urls=""
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 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%,}
backend_urls=${backend_urls%,}
frontend_block+="
frontend_block+="
osrm-frontend-${map_name}:
osrm-frontend-${map_name}:
...
@@ -224,15 +322,29 @@ EOF
...
@@ -224,15 +322,29 @@ 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 info "Installation aborted.";exit 0;fi
if[-f"${COMPOSE_FILE}"]&&! ask_confirm "Instance '${map_name}' may exist. Re-running will update config and recreate containers. Continue?";then
local pdata_dir="${work_dir}/${map_name}/${profile}-data"
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
needs_preprocessing=true
break
break
fi
fi
...
@@ -241,25 +353,33 @@ run_install() {
...
@@ -241,25 +353,33 @@ 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"
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. ---"
info "--- Skipping profile '${profile}', data already exists. ---"
continue
continue
fi
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}"
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.";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