forked from baron/baron-sso
186 lines
6.0 KiB
Bash
Executable File
186 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd "$script_dir/../.." && pwd)"
|
|
source "$repo_root/scripts/backup/lib/common.sh"
|
|
|
|
dotenv_value() {
|
|
local key="$1"
|
|
local env_file="${WORKS_DOCKER_IMAGE_ENV_FILE:-.env}"
|
|
|
|
[[ -f "$env_file" ]] || return 0
|
|
sed -n "s/^${key}=//p" "$env_file" | tail -n 1
|
|
}
|
|
|
|
urlencode_path() {
|
|
jq -nr --arg value "$1" '$value|@uri'
|
|
}
|
|
|
|
split_curl_response() {
|
|
local response="$1"
|
|
local __body_var="$2"
|
|
local __status_var="$3"
|
|
local status
|
|
local body
|
|
|
|
status="$(tail -n 1 <<<"$response")"
|
|
if [[ "$status" =~ ^[0-9][0-9][0-9]$ ]]; then
|
|
body="$(sed '$d' <<<"$response")"
|
|
else
|
|
status="200"
|
|
body="$response"
|
|
fi
|
|
|
|
printf -v "$__body_var" '%s' "$body"
|
|
printf -v "$__status_var" '%s' "$status"
|
|
}
|
|
|
|
redact_for_log() {
|
|
sed -E 's/("(access_token|refresh_token|assertion|client_secret|Authorization)"[[:space:]]*:[[:space:]]*)"[^"]*"/\1"REDACTED"/Ig'
|
|
}
|
|
|
|
resolve_files_endpoint() {
|
|
local parent_file_id="${1:-}"
|
|
local encoded_drive_id
|
|
|
|
encoded_drive_id="$(urlencode_path "$drive_id")"
|
|
if [[ -n "$parent_file_id" ]]; then
|
|
printf '%s/v1.0/sharedrives/%s/files/%s\n' "$api_base_url" "$encoded_drive_id" "$(urlencode_path "$parent_file_id")"
|
|
else
|
|
printf '%s/v1.0/sharedrives/%s/files\n' "$api_base_url" "$encoded_drive_id"
|
|
fi
|
|
}
|
|
|
|
list_children() {
|
|
local parent_file_id="${1:-}"
|
|
local endpoint
|
|
local response
|
|
local response_body
|
|
local http_status
|
|
|
|
endpoint="$(resolve_files_endpoint "$parent_file_id")/children"
|
|
response="$("$curl_bin" -sS -w $'\n%{http_code}' \
|
|
-H "Authorization: Bearer $access_token" \
|
|
"$endpoint")"
|
|
split_curl_response "$response" response_body http_status
|
|
|
|
if [[ "$http_status" -lt 200 || "$http_status" -ge 300 ]]; then
|
|
backup_die "WORKS folder list request failed (HTTP $http_status): $(printf '%s' "$response_body" | redact_for_log)"
|
|
fi
|
|
|
|
printf '%s\n' "$response_body"
|
|
}
|
|
|
|
find_child_id() {
|
|
local parent_file_id="$1"
|
|
local child_name="$2"
|
|
local expected_type="${3:-}"
|
|
local children_json
|
|
|
|
children_json="$(list_children "$parent_file_id")"
|
|
jq -er --arg name "$child_name" --arg expectedType "$expected_type" '
|
|
[
|
|
(.files // .children // .items // [])[]
|
|
| select((.fileName // .name) == $name)
|
|
| select(
|
|
$expectedType == ""
|
|
or (((.fileType // .type // "") | ascii_downcase) == ($expectedType | ascii_downcase))
|
|
)
|
|
| .fileId // .id
|
|
][0] // empty
|
|
' <<<"$children_json" 2>/dev/null || true
|
|
}
|
|
|
|
resolve_folder_path() {
|
|
local path="$1"
|
|
local parent_file_id="${WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID:-}"
|
|
local component
|
|
local folder_id
|
|
|
|
IFS='/' read -r -a components <<<"$path"
|
|
for component in "${components[@]}"; do
|
|
[[ -n "$component" ]] || continue
|
|
folder_id="$(find_child_id "$parent_file_id" "$component" "folder")"
|
|
[[ -n "$folder_id" ]] || backup_die "WORKS Drive folder not found: ${path}"
|
|
parent_file_id="$folder_id"
|
|
done
|
|
|
|
printf '%s\n' "$parent_file_id"
|
|
}
|
|
|
|
download_file() {
|
|
local file_id="$1"
|
|
local output_file="$2"
|
|
local endpoint
|
|
|
|
endpoint="$(resolve_files_endpoint "$file_id")/download"
|
|
"$curl_bin" -fL -sS \
|
|
--location-trusted \
|
|
-H "Authorization: Bearer $access_token" \
|
|
-o "$output_file" \
|
|
"$endpoint"
|
|
}
|
|
|
|
load_image_archive() {
|
|
local archive_file="$1"
|
|
|
|
backup_log "Loading Docker image archive: $(basename "$archive_file")"
|
|
zstd -dc "$archive_file" | docker load >/dev/null
|
|
}
|
|
|
|
image_tag="${IMAGE_TAG:-$(dotenv_value IMAGE_TAG)}"
|
|
drive_id="${WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID:-${WORKS_DRIVE_SHARED_DRIVE_ID:-}}"
|
|
access_token="${WORKS_DRIVE_ACCESS_TOKEN:-}"
|
|
api_base_url="${WORKS_ADMIN_API_BASE_URL:-https://www.worksapis.com}"
|
|
curl_bin="${WORKS_DRIVE_CURL_BIN:-curl}"
|
|
image_root_dir="${WORKS_DRIVE_DOCKER_IMAGE_DIR:-${WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR:-baron-sso}}"
|
|
download_root="${WORKS_DOCKER_IMAGE_DOWNLOAD_DIR:-/tmp/baron-sso-docker-image-download}"
|
|
image_list="${WORKS_DOCKER_IMAGE_NAMES:-backend userfront adminfront devfront orgfront}"
|
|
|
|
[[ -n "$image_tag" ]] || backup_die "IMAGE_TAG is required."
|
|
[[ -n "$drive_id" ]] || backup_die "WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID is required."
|
|
[[ -n "$access_token" ]] || backup_die "WORKS_DRIVE_ACCESS_TOKEN is required."
|
|
|
|
backup_require_command jq
|
|
backup_require_command sha256sum
|
|
backup_require_command zstd
|
|
backup_require_command docker
|
|
backup_require_command "$curl_bin"
|
|
|
|
# Normalized remote path: baron-sso/${IMAGE_TAG}/${image}.${IMAGE_TAG}.tar.zst
|
|
remote_path="${image_root_dir}/${image_tag}"
|
|
target_folder_id="$(resolve_folder_path "$remote_path")"
|
|
artifact_dir="${download_root}/${remote_path}"
|
|
mkdir -p "$artifact_dir"
|
|
|
|
for image in $image_list; do
|
|
archive_name="${image}.${image_tag}.tar.zst"
|
|
checksum_name="${image}.${image_tag}.sha256"
|
|
manifest_name="manifest.${image_tag}.json"
|
|
|
|
archive_id="$(find_child_id "$target_folder_id" "$archive_name")"
|
|
checksum_id="$(find_child_id "$target_folder_id" "$checksum_name")"
|
|
manifest_id="$(find_child_id "$target_folder_id" "$manifest_name")"
|
|
|
|
[[ -n "$archive_id" ]] || backup_die "WORKS Drive image archive not found: ${remote_path}/${archive_name}"
|
|
[[ -n "$checksum_id" ]] || backup_die "WORKS Drive image checksum not found: ${remote_path}/${checksum_name}"
|
|
[[ -n "$manifest_id" ]] || backup_die "WORKS Drive image manifest not found: ${remote_path}/${manifest_name}"
|
|
|
|
download_file "$archive_id" "$artifact_dir/$archive_name"
|
|
download_file "$checksum_id" "$artifact_dir/$checksum_name"
|
|
download_file "$manifest_id" "$artifact_dir/$manifest_name"
|
|
|
|
(
|
|
cd "$artifact_dir"
|
|
sha256sum -c "$checksum_name" >/dev/null
|
|
)
|
|
manifest_sha256="$(jq -er --arg image "$image" '.images[$image].archive.sha256 // .archive.sha256' "$artifact_dir/$manifest_name")"
|
|
actual_sha256="$(sha256sum "$artifact_dir/$archive_name" | awk '{print $1}')"
|
|
[[ "$manifest_sha256" == "$actual_sha256" ]] || backup_die "manifest sha256 mismatch for $archive_name"
|
|
|
|
load_image_archive "$artifact_dir/$archive_name"
|
|
done
|
|
|
|
backup_log "Loaded WORKS Drive Docker image archives from ${remote_path}"
|