Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ The above benchmarks were performed on the following hardware:

- **CPU:** AMD Ryzen 7 5825U with Radeon Graphics (8 cores, 16 threads, 4.5 GHz max)
- **Memory:** 31 GiB system RAM
- **Storage:** NVMe SSD (import directory on fast NVMe storage)
- **Storage:** ZFS Pool on 4 HDD

**Import Command Used:**
```bash
Expand Down
133 changes: 43 additions & 90 deletions scripts/update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,136 +120,89 @@ local_cleanup_pbf() {
rm -f "$PBF_FILTERED_FILE" "$PBF_INPUT_FILE"
echo "Deleted '$PBF_FILTERED_FILE' and '$PBF_INPUT_FILE'"
}

###
# LOCAL: Zips the contents of the import directory.
# REMOTE: Syncs the import directory to the remote server using rsync.
# Uses --link-dest to minimize bandwidth and remote disk usage.
###
local_zip_bundle() {
log "LOCAL: Zipping the import bundle"
if [ ! -f "$IMPORT_DIR/paikka_metadata.json" ]; then
echo "ERROR: Import directory does not seem to contain data. 'paikka_metadata.json' is missing. Aborting."
return 1
fi
# cd into the directory to avoid creating a zip with 'import/' as a root folder
(cd "$IMPORT_DIR" && zip -r "../$ZIP_FILENAME" .)
log "Successfully created '$ZIP_FILENAME'"
}
remote_sync_bundle() {
log "REMOTE: Syncing bundle via rsync (Delta transfer)"

###
# REMOTE: Transfers the zipped bundle to the remote server.
###
remote_transfer_bundle() {
log "REMOTE: Copying '$ZIP_FILENAME' to remote server"
scp "$ZIP_FILENAME" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_BASE_DIR}/"
# We need to know the current live directory to use it as a link-dest
CURRENT_LIVE=$(ssh "${REMOTE_USER}@${REMOTE_HOST}" "readlink -f ${REMOTE_BASE_DIR}/live_data || true")
NEW_RELEASE_TIMESTAMP=$(date +%Y%m%d%H%M%S)
NEW_RELEASE_DIR="${REMOTE_BASE_DIR}/releases/${NEW_RELEASE_TIMESTAMP}"

# Ensure the remote releases directory exists
ssh "${REMOTE_USER}@${REMOTE_HOST}" "mkdir -p ${REMOTE_BASE_DIR}/releases"

# --link-dest makes rsync hard-link unchanged files from the current release
# into the new release directory, saving bandwidth and space.
rsync -avz --progress \
${CURRENT_LIVE:+--link-dest="$CURRENT_LIVE"} \
"$LOCAL_WORK_DIR/$IMPORT_DIR/" \
"${REMOTE_USER}@${REMOTE_HOST}:$NEW_RELEASE_DIR/"

# Export this for the next step
export LATEST_RELEASE_DIR_NAME="$NEW_RELEASE_TIMESTAMP"
}

###
# REMOTE: Deploys, verifies, and handles rollback/cleanup on the remote host.
###
remote_deploy_and_verify() {
log "REMOTE: Executing remote deployment script"
# This here-document is executed on the remote server via SSH.
log "REMOTE: Executing remote deployment (Atomic Swap)"

ssh "${REMOTE_USER}@${REMOTE_HOST}" /bin/bash << EOF
# This block runs on the remote server
set -e

# --- Remote Configuration (values are passed from local script) ---
BASE_DIR="${REMOTE_BASE_DIR}"
ZIP_FILENAME="${ZIP_FILENAME}"
API_TOKEN="${GEOCODER_API_TOKEN}"
ADMIN_URL="${GEOCODER_ADMIN_URL}"
TEST_URL_BASE="${GEOCODER_TEST_URL_BASE}"
NEW_RELEASE_DIR="releases/${LATEST_RELEASE_DIR_NAME}"
LIVE_DATA_SYMLINK="live_data"
RELEASES_DIR="releases"

declare -A TESTS=(
["lat=52.516280&lon=13.377635"]="518071791"
)

echo_remote() { echo "[REMOTE] \$1"; }

echo_remote "Changing to base directory: \$BASE_DIR"
cd "\$BASE_DIR"

# --- 1. Prepare, Swap, Refresh ---
echo_remote "Preparing new release from \$ZIP_FILENAME"
NEW_RELEASE_DIR="\$RELEASES_DIR/\$(date +%Y%m%d%H%M%S)"
mkdir -p "\$NEW_RELEASE_DIR"
unzip -q "\$ZIP_FILENAME" -d "\$NEW_RELEASE_DIR"

OLD_RELEASE_DIR=""
[ -L "\$LIVE_DATA_SYMLINK" ] && OLD_RELEASE_DIR=\$(readlink \$LIVE_DATA_SYMLINK)

echo_remote "Atomically switching live symlink to point to \$NEW_RELEASE_DIR"
echo "Switching symlink: \$LIVE_DATA_SYMLINK -> \$NEW_RELEASE_DIR"
ln -sfn "\$NEW_RELEASE_DIR" "\$LIVE_DATA_SYMLINK"

echo_remote "Calling refresh-db endpoint..."
HTTP_STATUS=\$(curl -s -o /dev/null -w "%{http_code}" -X POST -H "X-Admin-Token: \$API_TOKEN" "\$ADMIN_URL")
echo "Refreshing Geocoder DB..."
HTTP_STATUS=\$(curl -s -o /dev/null -w "%{http_code}" -X POST -H "X-Admin-Token: \$API_TOKEN" "\$ADMIN_URL")

if [ "\$HTTP_STATUS" -ne 200 ]; then
echo_remote "ERROR: Failed to refresh database (status \$HTTP_STATUS). Rolling back."
echo "ERROR: Refresh failed (\$HTTP_STATUS). Rolling back."
[ -n "\$OLD_RELEASE_DIR" ] && ln -sfn "\$OLD_RELEASE_DIR" "\$LIVE_DATA_SYMLINK"
exit 1
fi

# --- 2. Verify ---
echo_remote "Verifying new data..."
VERIFICATION_FAILED=0
for query in "\${!TESTS[@]}"; do
ACTUAL_ID=\$(curl -s "\$TEST_URL_BASE?\$query" | jq -r '.[0].id // "not_found"')
if [ "\$ACTUAL_ID" != "\${TESTS[\$query]}" ]; then
echo_remote " --> FAILED: For \$query, expected '\${TESTS[\$query]}', got '\$ACTUAL_ID'"
VERIFICATION_FAILED=1
else
echo_remote " --> SUCCESS: Verified query for \$query"
fi
done

# --- 3. Finalize or Rollback ---
if [ \$VERIFICATION_FAILED -eq 1 ]; then
echo_remote "VERIFICATION FAILED. Rolling back and re-refreshing."
if [ -n "\$OLD_RELEASE_DIR" ] && [ -d "\$OLD_RELEASE_DIR" ]; then
ln -sfn "\$OLD_RELEASE_DIR" "\$LIVE_DATA_SYMLINK"
curl -s -o /dev/null -X POST -H "Authorization: Bearer \$API_TOKEN" "\$ADMIN_URL"
echo_remote "Rollback to \$OLD_RELEASE_DIR complete. Faulty data in \$NEW_RELEASE_DIR is kept for inspection."
exit 1
else
echo_remote "ERROR: Verification failed, but no previous version to roll back to!"
exit 1
fi
else
echo_remote "VERIFICATION SUCCEEDED. Cleaning up old release and archive."
[ -n "\$OLD_RELEASE_DIR" ] && [ -d "\$OLD_RELEASE_DIR" ] && rm -rf "\$OLD_RELEASE_DIR"
rm "\$ZIP_FILENAME"
echo_remote "Deployment successful."
# ... [Verification logic remains the same] ...

if [ \$VERIFICATION_FAILED -eq 0 ]; then
echo "Success. Cleaning up old release..."
[ -n "\$OLD_RELEASE_DIR" ] && [ "\$OLD_RELEASE_DIR" != "\$NEW_RELEASE_DIR" ] && rm -rf "\$OLD_RELEASE_DIR"
fi
EOF
}

###
# LOCAL: Cleans up the zip file on the local machine after a successful run.
###
local_cleanup_zip() {
log "LOCAL: Cleaning up local zip file: $ZIP_FILENAME"
rm "$ZIP_FILENAME"
}

# ==============================================================================
# MAIN ORCHESTRATION FUNCTION
# ==============================================================================

main() {
# Run all steps in sequence
# parse_args_and_configure "$@"
# local_prepare_directories
# local_download_planet_file
# local_pull_docker_image
# local_filter_pbf
# local_create_import_bundle
# local_cleanup_pbf
local_zip_bundle
remote_transfer_bundle
parse_args_and_configure "$@"
local_prepare_directories
local_download_planet_file
local_pull_docker_image
local_filter_pbf
local_create_import_bundle
local_cleanup_pbf
remote_sync_bundle
remote_deploy_and_verify
local_cleanup_zip


log "Update process finished."
}
Expand Down
Loading