122 lines
5.2 KiB
Bash
122 lines
5.2 KiB
Bash
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
project_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
|
|
candidate="$project_root/deploy/server/sync-backup-offsite.sh"
|
|
bash -n "$candidate"
|
|
test_root=$(mktemp -d /var/tmp/offsite-backup-test-XXXXXX)
|
|
case "$test_root" in /var/tmp/offsite-backup-test-*) ;; *) exit 90;; esac
|
|
metadata_pid=
|
|
cleanup() {
|
|
if [[ -n "$metadata_pid" ]]; then kill "$metadata_pid" 2>/dev/null || true; fi
|
|
rm -rf -- "$test_root"
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
mkdir -p "$test_root/backups" "$test_root/state" "$test_root/mock-oss"
|
|
|
|
role_mode_file="$test_root/role-mode"
|
|
port_file="$test_root/metadata-port"
|
|
printf 'missing\n' >"$role_mode_file"
|
|
python3 - "$port_file" "$role_mode_file" <<'PY' &
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
import pathlib,sys
|
|
port_file=pathlib.Path(sys.argv[1])
|
|
role_mode=pathlib.Path(sys.argv[2])
|
|
class Handler(BaseHTTPRequestHandler):
|
|
def log_message(self,*args): pass
|
|
def do_PUT(self):
|
|
if self.path!='/latest/api/token': self.send_error(404); return
|
|
self.send_response(200); self.end_headers(); self.wfile.write(b'test-imdsv2-token')
|
|
def do_GET(self):
|
|
if self.path!='/latest/meta-data/ram/security-credentials/': self.send_error(404); return
|
|
if role_mode.read_text(encoding='utf-8').strip()!='attached': self.send_error(404); return
|
|
if self.headers.get('X-aliyun-ecs-metadata-token')!='test-imdsv2-token': self.send_error(401); return
|
|
self.send_response(200); self.end_headers(); self.wfile.write(b'DataTerminalCloudOssRole')
|
|
server=ThreadingHTTPServer(('127.0.0.1',0),Handler)
|
|
port_file.write_text(str(server.server_port),encoding='utf-8')
|
|
server.serve_forever()
|
|
PY
|
|
metadata_pid=$!
|
|
for _ in $(seq 1 50); do [[ -s "$port_file" ]] && break; sleep 0.1; done
|
|
[[ -s "$port_file" ]]
|
|
metadata_base="http://127.0.0.1:$(<"$port_file")/latest"
|
|
|
|
cat >"$test_root/enabled-role.env" <<'EOF'
|
|
OFFSITE_BACKUP_ENABLED=true
|
|
OFFSITE_BACKUP_REQUIRED=true
|
|
OFFSITE_OSS_BUCKET=valid-private-backup-bucket
|
|
OFFSITE_OSS_PREFIX=data-terminal-cloud/test
|
|
OFFSITE_OSS_AUTH_MODE=ecs_ram_role
|
|
OFFSITE_OSS_REGION=cn-hangzhou
|
|
OFFSITE_OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
|
|
EOF
|
|
set +e
|
|
ECS_METADATA_BASE_URL="$metadata_base" OFFSITE_CONFIG_FILE="$test_root/enabled-role.env" OFFSITE_STATE_DIR="$test_root/state" BACKUP_ROOT="$test_root/backups" \
|
|
"$candidate" >/dev/null 2>/dev/null
|
|
result=$?
|
|
set -e
|
|
test "$result" = 4
|
|
python3 - "$test_root/state/status.json" <<'PY'
|
|
import json,sys
|
|
d=json.load(open(sys.argv[1],encoding='utf-8'))
|
|
assert d['status']=='failed' and d['reason']=='ecs_ram_role_missing',d
|
|
PY
|
|
|
|
cat >"$test_root/disabled.env" <<'EOF'
|
|
OFFSITE_BACKUP_ENABLED=false
|
|
EOF
|
|
rm -f "$test_root/state/status.json"
|
|
OFFSITE_CONFIG_FILE="$test_root/disabled.env" OFFSITE_STATE_DIR="$test_root/state" BACKUP_ROOT="$test_root/backups" "$candidate" >/dev/null
|
|
|
|
printf 'encrypted-backup-fixture\n' >"$test_root/backups/platform-20260826T140000Z.tar.gz.enc"
|
|
(cd "$test_root/backups" && sha256sum platform-20260826T140000Z.tar.gz.enc >platform-20260826T140000Z.tar.gz.enc.sha256)
|
|
cat >"$test_root/mock-ossutil" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
printf '%s\n' "$*" >>"$MOCK_OSS_LOG"
|
|
test "$1" = cp
|
|
source_path=$2
|
|
target_path=$3
|
|
map_path() {
|
|
if [[ "$1" == oss://* ]]; then printf '%s/%s' "$MOCK_OSS_ROOT" "${1#oss://}"; else printf '%s' "$1"; fi
|
|
}
|
|
source_local=$(map_path "$source_path")
|
|
target_local=$(map_path "$target_path")
|
|
mkdir -p "$(dirname "$target_local")"
|
|
cp "$source_local" "$target_local"
|
|
EOF
|
|
chmod 0700 "$test_root/mock-ossutil"
|
|
printf '[default]\nregion=cn-hangzhou\n' >"$test_root/ossutilconfig"
|
|
|
|
cat >"$test_root/config-mode.env" <<EOF
|
|
OFFSITE_BACKUP_ENABLED=true
|
|
OFFSITE_BACKUP_REQUIRED=true
|
|
OFFSITE_OSS_BUCKET=valid-private-backup-bucket
|
|
OFFSITE_OSS_PREFIX=data-terminal-cloud/config-test
|
|
OFFSITE_OSS_AUTH_MODE=config
|
|
OFFSITE_OSS_CONFIG_FILE=$test_root/ossutilconfig
|
|
EOF
|
|
rm -f "$test_root/state/status.json" "$test_root/ossutil.log"
|
|
MOCK_OSS_ROOT="$test_root/mock-oss" MOCK_OSS_LOG="$test_root/ossutil.log" OSSUTIL_BIN="$test_root/mock-ossutil" \
|
|
OFFSITE_CONFIG_FILE="$test_root/config-mode.env" OFFSITE_STATE_DIR="$test_root/state" BACKUP_ROOT="$test_root/backups" "$candidate" >/dev/null
|
|
python3 - "$test_root/state/status.json" <<'PY'
|
|
import json,sys
|
|
d=json.load(open(sys.argv[1],encoding='utf-8'))
|
|
assert d['status']=='verified' and d['reason']=='upload_and_download_verified',d
|
|
PY
|
|
|
|
printf 'attached\n' >"$role_mode_file"
|
|
rm -f "$test_root/state/status.json" "$test_root/ossutil.log"
|
|
MOCK_OSS_ROOT="$test_root/mock-oss" MOCK_OSS_LOG="$test_root/ossutil.log" OSSUTIL_BIN="$test_root/mock-ossutil" \
|
|
ECS_METADATA_BASE_URL="$metadata_base" OFFSITE_CONFIG_FILE="$test_root/enabled-role.env" OFFSITE_STATE_DIR="$test_root/state" \
|
|
BACKUP_ROOT="$test_root/backups" "$candidate" >/dev/null
|
|
python3 - "$test_root/state/status.json" <<'PY'
|
|
import json,sys
|
|
d=json.load(open(sys.argv[1],encoding='utf-8'))
|
|
assert d['status']=='verified' and d['reason']=='upload_and_download_verified',d
|
|
PY
|
|
grep -q -- '--mode EcsRamRole' "$test_root/ossutil.log"
|
|
grep -q -- '--region cn-hangzhou' "$test_root/ossutil.log"
|
|
grep -q -- '--endpoint oss-cn-hangzhou.aliyuncs.com' "$test_root/ossutil.log"
|
|
echo 'offsite_backup_smoke=ok disabled=ok missing_role=ok config_round_trip=ok ecs_role_round_trip=ok'
|