Commit e0c1aae9 by Bambang Adrian

fix(osrm): release v4.3 fix granular control dependency flow (itemize)

parent a9add541
......@@ -2,11 +2,12 @@
#
# install-osrm-ngpm.sh
# OSRM + NGPM installer & manager (Rocky Linux 10)
# Final self-contained script (v3.8 - Fundamental Filename Logic Fix)
# Final self-contained script (v4.3 - Final Execution Logic Fix)
#
set -euo pipefail
IFS=$'\n\t'
# ... (Seluruh bagian DEFAULTS hingga Core Action Functions sama persis seperti v4.2) ...
# -------------------------
# DEFAULTS
# -------------------------
......@@ -20,10 +21,6 @@ DEFAULT_NETWORK="ngpm-network"
DEFAULT_PROFILES="car"
DEFAULT_BACKEND_PORT=5000
DEFAULT_FRONTEND_PORT=9966
DEFAULT_BACKEND_DOMAIN="localhost"
DEFAULT_FRONTEND_DOMAIN="localhost"
DEFAULT_BACKEND_PATH="/osrm"
DEFAULT_API_VERSION="v1"
DEFAULT_MAP_CENTER="-6.2,106.8" # Jakarta
DEFAULT_MAP_ZOOM="13"
......@@ -37,10 +34,6 @@ profiles_csv="${DEFAULT_PROFILES}"
network_name="${DEFAULT_NETWORK}"
backend_port="${DEFAULT_BACKEND_PORT}"
frontend_port="${DEFAULT_FRONTEND_PORT}"
backend_domain="${DEFAULT_BACKEND_DOMAIN}"
frontend_domain="${DEFAULT_FRONTEND_DOMAIN}"
backend_path="${DEFAULT_BACKEND_PATH}"
api_version="${DEFAULT_API_VERSION}"
mapbox_token=""
map_center="${DEFAULT_MAP_CENTER}"
map_zoom="${DEFAULT_MAP_ZOOM}"
......@@ -70,7 +63,7 @@ err() { _log "ERROR" "$*" >&2; }
# -------------------------
usage() {
cat <<EOF
OSRM Installer & Manager v3.8
OSRM Installer & Manager v4.3
Usage:
$0 <command> --map-name <name> [options]
Commands:
......@@ -85,8 +78,8 @@ Options:
--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.
--frontend-port <port> Host port for the frontend UI.
--work-dir <dir> Root directory for all OSRM data.
--network <name> Docker network name to use.
--no-frontend Do NOT include the frontend service.
--purge Also delete all data directories on remove.
--dry-run Simulate steps without executing.
......@@ -100,338 +93,281 @@ EOF
# Argument Parsing & Setup
# -------------------------
parse_args() {
if [ $# -eq 0 ]; then usage; fi
command_name="$1"; shift || true
sub_command=""
if [ $# -eq 0 ]; then usage; fi; command_name="$1"; shift || true; sub_command=""
if [[ "${command_name}" == "update" ]]; then
if [ $# -gt 0 ] && ! [[ "$1" =~ ^-- ]]; then sub_command="$1"; shift; else err "Update command requires a component."; usage; fi
fi
while (( "$#" )); do
case "$1" in
--map-name) map_name="$2"; shift 2 ;;
--map-source) map_source="$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 ;;
--frontend-port) frontend_port="$2"; shift 2 ;;
--no-frontend) no_frontend=true; shift ;;
--purge) do_purge=true; shift ;;
--dry-run) dry_run=true; shift ;;
--yes) yes_all=true; shift ;;
-h|--help) usage ;;
*) err "Unknown option: $1"; usage ;;
--map-name) map_name="$2"; shift 2 ;; --map-source) map_source="$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 ;; --frontend-port) frontend_port="$2"; shift 2 ;;
--work-dir) work_dir="${2/#\~/$HOME}"; shift 2 ;; --network) network_name="$2"; shift 2 ;;
--no-frontend) no_frontend=true; shift ;; --purge) do_purge=true; shift ;;
--dry-run) dry_run=true; shift ;; --yes) yes_all=true; shift ;;
-h|--help) usage ;; *) err "Unknown option: $1"; usage ;;
esac
done
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}"
LOGFILE="${log_dir}/osrm-manager-$(date '+%Y%m%d_%H%M%S').log"; : > "${LOGFILE}"
LOGFILE="${log_dir}/osrm-manager-$(date '+%Ym%d_%H%M%S').log"; : > "${LOGFILE}"
COMPOSE_FILE="${work_dir}/docker-compose.${map_name}.yml"
IFS=',' read -r -a PROFILE_ARR <<< "$(echo "${profiles_csv}" | tr -d '[:space:]')"
if [ "${#PROFILE_ARR[@]}" -eq 0 ]; then PROFILE_ARR=(car); fi
}
# -------------------------
# 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."; }
prereqs_check() { info "Running prerequisite checks..."; local ok=0; if ! command_exists docker; then err "Docker is not installed."; ok=1; fi; if ! command_exists yq; then err "Dependency 'yq' is missing. Please install it."; 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 Action Functions
# -------------------------
download_map() {
action_prepare_map() {
# ... (same as v4.2)
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
info "Downloading map with curl..."; if ! curl -L --fail -o "${MAP_PATH}.tmp" "${map_source}"; then rm -f "${MAP_PATH}.tmp"; err "Download failed."; return 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;
else err "Map source '${map_source}' is not a valid URL or an existing file."; return 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
action_create_profile_file() {
# ... (same as v4.2)
local profile="$1"
local profiles_dir="${work_dir}/${map_name}/profiles"
mkdir -p "${profiles_dir}"
local pf="${profiles_dir}/${profile}.lua"
info "Creating file '${profile}.lua'..."
case "${profile}" in
car|bicycle|bike|foot)
cat > "${pf}" <<LUA
return dofile('/opt/${profile}.lua')
LUA
;;
motorcycle)
info "Generating custom 'motorcycle' profile based on 'car'"; cat > "${pf}" <<'LUA'
;;
motorcycle)
cat > "${pf}" <<'LUA'
local car_module = dofile('/opt/car.lua')
local profile = car_module.setup()
profile.properties.max_speed = 90
profile.properties.u_turn_penalty = 60
profile.restrictions = profile.restrictions or {}
profile.restrictions["toll"] = "prohibited"
profile.speed_profile["motorway"] = 90
profile.speed_profile["motorway_link"] = 45
car_module.setup = function() return profile end
return car_module
LUA
;;
*) warn "No generator for profile '${profile}'. Please create it manually."; continue;;
esac
fi
cp "${pf}" "${patched_dir}/${profile}.lua"
done
success "Profile generation complete."
;;
*) warn "Generator for profile '${profile}' not found. An empty file will be created."; touch "${pf}";;
esac
}
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}"
action_run_preprocessing() {
# ... (same as v4.2)
local profile="$1"
local map_instance_dir="${work_dir}/${map_name}"
local pdata_dir="${map_instance_dir}/${profile}-data"
info "Preparing map 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}' succeeded."
else
err "Preprocessing for '${profile}' FAILED."
docker compose -f "${COMPOSE_FILE}" rm -fsv "${service_name}" >/dev/null 2>&1 || true
return 1
fi
docker compose -f "${COMPOSE_FILE}" rm -fsv "${service_name}" >/dev/null 2>&1 || true
}
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}"
action_update_compose_file() {
# ... (same as v4.2)
info "Writing new configuration to ${COMPOSE_FILE}..."
echo "$1" > "$2"
success "File ${COMPOSE_FILE} updated."
}
generate_docker_compose() {
info "Generating Docker Compose file: ${COMPOSE_FILE}"
local map_instance_dir="${work_dir}/${map_name}"
local selabel="${SELINUX_LABEL:-}"
# Start writing the file
cat > "${COMPOSE_FILE}" <<EOF
services:
EOF
# Append backend services
action_recreate_container() {
# ... (same as v4.2)
local service_name="$1"
info "Applying changes to container '${service_name}'..."
if ! docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans "${service_name}"; then
err "Failed to create/recreate container '${service_name}'."
return 1
fi
success "Container '${service_name}' is up to date."
}
generate_docker_compose_content() {
# ... (same as v4.2 - already correct)
local map_instance_dir="${work_dir}/${map_name}"; local selabel="${SELINUX_LABEL:-}"; local patched_dir="${map_instance_dir}/patched-profiles"
local compose_content="services:\n"
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"
# --- 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}"
cat >> "${COMPOSE_FILE}" <<EOF
osrm-preprocess-${map_name}-${profile}:
image: ${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}
container_name: osrm-preprocess-${map_name}-${profile}
idx=$((idx + 1)); local host_port=$((backend_port + idx - 1)); local pdata="${map_instance_dir}/${profile}-data"
local base_name="${map_name}-${profile}"; mkdir -p "${pdata}"
services_yaml=$(printf '
osrm-preprocess-%s-%s:
image: %s:%s
container_name: osrm-preprocess-%s-%s
command: >
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'
sh -c ''ln -s map.osm.pbf /data/%s.osm.pbf && osrm-extract -p /opt/custom_profiles/%s.lua /data/%s.osm.pbf && osrm-partition /data/%s.osrm && osrm-customize /data/%s.osrm''
volumes:
- ${map_instance_dir}/patched-profiles:/opt/custom_profiles:ro${selabel}
- ${pdata}:/data:rw${selabel}
- %s:/opt/custom_profiles:ro%s
- %s:/data:rw%s
networks:
- ${network_name}
osrm-routed-${map_name}-${profile}:
image: ${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}
container_name: osrm-routed-${map_name}-${profile}
- %s
osrm-routed-%s-%s:
image: %s:%s
container_name: osrm-routed-%s-%s
restart: unless-stopped
command: osrm-routed --algorithm mld /data/${base_name}.osrm
command: osrm-routed --algorithm mld /data/%s.osrm
volumes:
- ${pdata}:/data:ro${selabel}
- %s:/data:ro%s
ports:
- "${host_port}:5000"
- "%s:5000"
networks:
- ${network_name}
EOF
- %s
' "${map_name}" "${profile}" "${DEFAULT_BACKEND_REGISTRY}" "${DEFAULT_BACKEND_VERSION}" "${map_name}" "${profile}" "${base_name}" "${profile}" "${base_name}" "${base_name}" "${base_name}" "${patched_dir}" "${selabel}" "${pdata}" "${selabel}" "${network_name}" "${map_name}" "${profile}" "${DEFAULT_BACKEND_REGISTRY}" "${DEFAULT_BACKEND_VERSION}" "${map_name}" "${profile}" "${base_name}" "${pdata}" "${selabel}" "${host_port}" "${network_name}")
compose_content+="${services_yaml}"
done
# Append frontend service
if [ "${no_frontend}" != true ]; then
if [ -z "${mapbox_token}" ]; then warn "Mapbox token not provided. The frontend map may not render."; fi
cat >> "${COMPOSE_FILE}" <<EOF
osrm-frontend-${map_name}:
build:
context: ${map_instance_dir}
dockerfile: Dockerfile.frontend
container_name: osrm-frontend-${map_name}
if [ -z "${mapbox_token}" ]; then warn "Mapbox token not provided."; fi
local backend_urls=""; local first=true
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}"
if [[ "$first" == "true" ]]; then backend_urls+="${label}|http://${backend_service_name}:5000/route/v1"; first=false; else backend_urls+=",${label}|http://${backend_service_name}:5000/route/v1"; fi
done
local depends_on_yaml=""; for profile in "${PROFILE_ARR[@]}"; do depends_on_yaml+=$(printf '\n - osrm-routed-%s-%s' "${map_name}" "${profile}"); done
frontend_yaml=$(printf '
osrm-frontend-%s:
image: %s:%s
container_name: osrm-frontend-%s
restart: unless-stopped
environment:
- OSRM_MAPBOX_TOKEN=${mapbox_token}
- OSRM_BACKEND=%s
- OSRM_MAPBOX_TOKEN=%s
- OSRM_CENTER=%s
- OSRM_ZOOM=%s
ports:
- "${frontend_port}:9966"
- "%s:9966"
networks:
- ${network_name}
depends_on:
EOF
for profile in "${PROFILE_ARR[@]}"; do
cat >> "${COMPOSE_FILE}" <<EOF
- osrm-routed-${map_name}-${profile}
EOF
done
- %s
depends_on:%s
' "${map_name}" "${DEFAULT_FRONTEND_REGISTRY}" "${DEFAULT_FRONTEND_VERSION}" "${map_name}" "${backend_urls}" "${mapbox_token}" "${map_center}" "${map_zoom}" "${frontend_port}" "${network_name}" "${depends_on_yaml}")
compose_content+="${frontend_yaml}"
fi
# Append networks block
cat >> "${COMPOSE_FILE}" <<EOF
networks:
${network_name}:
external: true
name: ${network_name}
EOF
success "Docker Compose file written successfully."
compose_content+=$(printf '\nnetworks:\n %s:\n external: true\n name: %s\n' "${network_name}" "${network_name}")
echo -e "${compose_content}" # Use -e to interpret newlines correctly
}
# -------------------------
# Command Implementations
# Main Execution Flow
# -------------------------
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/rebuild containers. Continue?"; then info "Installation aborted."; exit 0; fi
download_map; generate_profiles
if [[ "${no_frontend}" != "true" ]]; then
generate_leaflet_options; generate_frontend_dockerfile
main() {
parse_args "$@"
prereqs_check; setup_selinux_flag
declare -A decisions
info "--- Phase 1: Analysis ---"
local map_instance_dir="${work_dir}/${map_name}"
local dataset_dir="${map_instance_dir}/dataset"
local profiles_dir="${map_instance_dir}/profiles"
local patched_dir="${map_instance_dir}/patched-profiles"
mkdir -p "${dataset_dir}" "${profiles_dir}" "${patched_dir}"
local plan_map_action="none"; local map_changed=false
local existing_map_path; existing_map_path=$(find "${dataset_dir}" -name "*.osm.pbf" -print -quit 2>/dev/null || true)
if [[ "${command_name}" == "update" && "${sub_command}" == "map" ]] && [[ "${map_source}" != "${existing_map_path}" ]]; then
plan_map_action="update"; map_changed=true
elif [ ! -f "${existing_map_path}" ]; then
plan_map_action="create"
else
MAP_PATH="${existing_map_path}"
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 plan_profiles_to_create=(); for profile in "${PROFILE_ARR[@]}"; do if [ ! -f "${profiles_dir}/${profile}.lua" ]; then plan_profiles_to_create+=("${profile}"); fi; done
local plan_profiles_to_preprocess=()
if [[ "${map_changed}" == "true" || ("${command_name}" == "update" && "${sub_command}" == "profile") ]]; then
plan_profiles_to_preprocess=("${PROFILE_ARR[@]}")
else
for profile in "${PROFILE_ARR[@]}"; do if [ ! -f "${map_instance_dir}/${profile}-data/${map_name}-${profile}.osrm.properties" ]; then plan_profiles_to_preprocess+=("${profile}"); fi; done
fi
local plan_compose_update=false
local new_compose_content; new_compose_content=$(generate_docker_compose_content)
if [ ! -f "${COMPOSE_FILE}" ] || ! cmp -s <(echo -e "${new_compose_content}") "${COMPOSE_FILE}"; then plan_compose_update=true; fi
local needs_preprocessing=false
for profile in "${PROFILE_ARR[@]}"; do
local pdata_dir="${work_dir}/${map_name}/${profile}-data"
# --- FIX IS HERE (v3.8) ---
# 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
break
info "--- Phase 2: Itemized Confirmation ---"
if [[ "${plan_map_action}" != "none" ]]; then if [[ "${plan_map_action}" == "create" ]]; then info "[PLAN] Peta sumber belum ada; akan disiapkan dari '${map_source}'."; else info "[PLAN] Peta akan diubah dari '${existing_map_path:-<none>}' menjadi '${map_source}'."; fi; warn "[DEPENDENCY] Aksi ini krusial untuk preprocessing."; if ask_confirm "Lanjutkan dengan persiapan peta?"; then decisions[map]=true; else decisions[map]=false; fi; else decisions[map]=true; fi
if [[ "${decisions[map]}" == "false" ]]; then info "Persiapan peta dilewati, preprocessing tidak dapat dilanjutkan."; map_changed=false; fi
for profile in "${plan_profiles_to_create[@]}"; do info "[PLAN] File profil '${profile}.lua' akan dibuat."; warn "[DEPENDENCY] Jika dilewati, preprocessing untuk '${profile}' tidak dapat dilanjutkan."; if ask_confirm "Lanjutkan?"; then decisions["profile_${profile}"]=true; else decisions["profile_${profile}"]=false; fi; done
for profile in "${plan_profiles_to_preprocess[@]}"; do local profile_lua_exists=true; if [[ " ${plan_profiles_to_create[*]} " =~ " ${profile} " ]] && [[ "${decisions[profile_${profile}]:-false}" == "false" ]]; then profile_lua_exists=false; fi; if [[ "${profile_lua_exists}" == "false" ]]; then warn "[SKIP] Preprocessing untuk '${profile}' dilewati karena pembuatan file .lua tidak disetujui."; decisions["preprocess_${profile}"]=false; continue; fi; info "[PLAN] Preprocessing akan dijalankan untuk profil '${profile}'."; warn "[DEPENDENCY] Jika dilewati, container backend untuk '${profile}' tidak akan dapat dimulai."; if ask_confirm "Lanjutkan? (Memakan waktu)"; then decisions["preprocess_${profile}"]=true; else decisions["preprocess_${profile}"]=false; fi; done
if [[ "${plan_compose_update}" == "true" ]]; then info "[PLAN] File 'docker-compose.${map_name}.yml' akan diperbarui."; warn "[DEPENDENCY] Aksi ini krusial untuk semua perubahan container."; if ask_confirm "Lanjutkan?"; then decisions[compose]=true; else decisions[compose]=false; fi; else decisions[compose]=true; fi
local all_services=(); if [ -n "${new_compose_content}" ]; then all_services=($(echo -e "${new_compose_content}" | yq '.services | keys | .[]' 2>/dev/null || true)); fi
if [[ "${decisions[compose]}" == "true" ]]; then
for service in "${all_services[@]}"; do
if [[ "$service" == *"osrm-preprocess"* ]]; then continue; fi # Skip asking for preprocess containers
local plan_action="none"; if ! docker ps -a --format '{{.Names}}' | grep -q "^${service}$"; then plan_action="create"; elif [[ "${plan_compose_update}" == "true" ]]; then plan_action="recreate"; fi
if [[ "${plan_action}" != "none" ]]; then info "[PLAN] Container '${service}' akan di-${plan_action}."; if ask_confirm "Lanjutkan?"; then decisions["container_${service}"]=true; else decisions["container_${service}"]=false; fi; fi
done
fi
info "--- Phase 3: Execution ---"
if [[ "${dry_run}" == "true" ]]; then warn "DRY RUN: Eksekusi dilewati."; exit 0; fi
if [[ "${decisions[map]:-false}" == "true" ]]; then action_prepare_map; else info "Persiapan peta dilewati."; fi
MAP_PATH=$(find "${dataset_dir}" -name "*.osm.pbf" -print -quit 2>/dev/null || true); if [ -z "${MAP_PATH}" ]; then err "Peta tidak ditemukan, eksekusi dihentikan."; exit 1; fi
for profile in "${plan_profiles_to_create[@]}"; do if [[ "${decisions[profile_${profile}]:-false}" == "true" ]]; then action_create_profile_file "${profile}"; else info "Pembuatan file profil '${profile}.lua' dilewati."; fi; done
for profile in "${PROFILE_ARR[@]}"; do if [ -f "${profiles_dir}/${profile}.lua" ]; then cp "${profiles_dir}/${profile}.lua" "${patched_dir}/${profile}.lua"; fi; done
if [[ "${decisions[compose]:-false}" == "true" ]]; then action_update_compose_file "${new_compose_content}" "${COMPOSE_FILE}"; fi
for profile in "${plan_profiles_to_preprocess[@]}"; do if [[ "${decisions[preprocess_${profile}]:-false}" == "true" ]]; then action_run_preprocessing "${profile}" || warn "Preprocessing untuk ${profile} gagal."; else info "Preprocessing untuk '${profile}' dilewati."; fi; done
# --- FIX IS HERE (v4.3) ---
# This block now correctly executes the container actions based on decisions.
info "Menjalankan: Sinkronisasi Container..."
local services_to_run=()
for service in "${all_services[@]}"; do
if [[ "${decisions[container_${service}]:-false}" == "true" ]]; then
services_to_run+=("${service}")
fi
done
if [[ "${needs_preprocessing}" == "true" ]]; then
info "Starting data preprocessing for missing profiles..."
for profile in "${PROFILE_ARR[@]}"; do
local pdata_dir="${work_dir}/${map_name}/${profile}-data"
local base_name="${map_name}-${profile}"
if [ -f "${pdata_dir}/${base_name}.osrm.properties" ]; then
info "--- Skipping profile '${profile}', data already exists. ---"
continue
if [ ${#services_to_run[@]} -gt 0 ]; then
info "Menarik base image terbaru (jika perlu)..."
docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"; if [[ "${no_frontend}" != "true" ]]; then docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"; fi
for service in "${services_to_run[@]}"; do
local can_run=true
if [[ "$service" == *"osrm-routed"* ]]; then
local profile_name; profile_name=$(echo "$service" | sed -E "s/osrm-routed-${map_name}-//")
if [ ! -f "${map_instance_dir}/${profile_name}-data/${map_name}-${profile_name}.osrm.properties" ]; then
warn "Melewati '${service}' karena data preprocessing yang dibutuhkan tidak ada/tidak disetujui."
can_run=false
fi
fi
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."; if ! ask_confirm "Continue?"; then err "Aborting."; exit 1; fi
if [[ "${can_run}" == "true" ]]; then
action_recreate_container "${service}"
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."
info "Tidak ada perubahan container yang disetujui untuk dieksekusi."
fi
info "Starting all services for instance '${map_name}'..."; docker compose -f "${COMPOSE_FILE}" up --build -d --remove-orphans
success "Installation for '${map_name}' completed!"; print_summary
}
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
local map_instance_dir="${work_dir}/${map_name}/dataset"
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
case "${sub_command}" in
backend|frontend)
info "Pulling latest base image for ${sub_command}...";
if [[ "${sub_command}" == "backend" ]]; then docker pull "${DEFAULT_BACKEND_REGISTRY}:${DEFAULT_BACKEND_VERSION}"; else docker pull "${DEFAULT_FRONTEND_REGISTRY}:${DEFAULT_FRONTEND_VERSION}"; fi
info "Rebuilding and recreating services..."; docker compose -f "${COMPOSE_FILE}" up --build -d --remove-orphans
;;
map) download_map; ;&
profile)
generate_profiles
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
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 all services..."; docker compose -f "${COMPOSE_FILE}" up --build -d --remove-orphans
;;
*) err "Invalid update component. Use: backend, frontend, map, profile."; 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."; fi
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}"; rm -f "${COMPOSE_FILE}"; success "Instance data for '${map_name}' has been purged."
fi
fi
else info "Removal aborted by user."; fi
success "Proses selesai!"; print_summary
}
print_summary() {
echo "--- Instance '${map_name}' Summary ---"; echo "Profiles: ${profiles_csv}"
echo "ACCESS POINTS:"; 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}"; 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
# ... (sisa fungsi tidak berubah)
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."; fi; 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}"; rm -f "${COMPOSE_FILE}"; 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 "Profiles: ${profiles_csv}"; echo "ACCESS POINTS:"; 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}"; 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 "--------------------------"; }
if ! command_exists yq; then err "Dependency 'yq' is missing. Please install it (e.g., 'sudo dnf install yq')."; exit 1; fi
if [[ "${1:-}" == "remove" ]]; then
parse_args "$@"
run_remove
else
main "$@"
fi
\ No newline at end of file
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