forked from baron/baron-sso
production 푸시 초안
This commit is contained in:
@@ -79,14 +79,27 @@ for target in dump-verify restore-verify dump-list restore-plan; do
|
||||
assert_dry_run_contains "$target_dry_run" "scripts/backup/"
|
||||
done
|
||||
|
||||
upload_default_dry_run="$(
|
||||
make --dry-run --always-make -C "$repo_root" upload-cloud BACKUP="backups/example" 2>&1
|
||||
)"
|
||||
|
||||
assert_dry_run_contains "$upload_default_dry_run" "scripts/backup/upload_cloud.sh"
|
||||
if grep -Fq 'WORKS_DRIVE_AUTH_MODE=""' <<<"$upload_default_dry_run"; then
|
||||
fail "make upload-cloud must not pass an empty WORKS_DRIVE_AUTH_MODE that masks .env."
|
||||
fi
|
||||
if grep -Fq 'WORKS_DRIVE_DRY_RUN=""' <<<"$upload_default_dry_run"; then
|
||||
fail "make upload-cloud must not pass an empty WORKS_DRIVE_DRY_RUN that masks .env."
|
||||
fi
|
||||
|
||||
upload_dry_run="$(
|
||||
make --dry-run --always-make -C "$repo_root" upload-cloud BACKUP="backups/example" WORKS_DRIVE_DRY_RUN=true 2>&1
|
||||
make --dry-run --always-make -C "$repo_root" upload-cloud BACKUP="backups/example" WORKS_DRIVE_DRY_RUN=true WORKS_DRIVE_AUTH_MODE=refresh-token 2>&1
|
||||
)"
|
||||
|
||||
assert_dry_run_contains "$upload_dry_run" "docker run"
|
||||
assert_dry_run_contains "$upload_dry_run" "scripts/backup/upload_cloud.sh"
|
||||
assert_dry_run_contains "$upload_dry_run" "BACKUP=\"backups/example\""
|
||||
assert_dry_run_contains "$upload_dry_run" "WORKS_DRIVE_DRY_RUN=\"true\""
|
||||
assert_dry_run_contains "$upload_dry_run" "WORKS_DRIVE_AUTH_MODE=\"refresh-token\""
|
||||
|
||||
if make -C "$repo_root" BACKUP_USE_DOCKER=false restore >/tmp/baron-sso-restore-missing.out 2>&1; then
|
||||
fail "make restore must fail when BACKUP and CONFIRM_RESTORE are not provided."
|
||||
|
||||
@@ -37,6 +37,17 @@ printf '%s\n' "$*" >>"${FAKE_CURL_LOG}"
|
||||
|
||||
last_arg="${!#}"
|
||||
case "$last_arg" in
|
||||
https://auth.example.test/token)
|
||||
if [[ "$*" == *"grant_type=refresh_token"* ]]; then
|
||||
if [[ "${ALLOW_REFRESH_TOKEN_GRANT:-false}" == "true" ]]; then
|
||||
printf '{"access_token":"refresh-token-access-token"}'
|
||||
exit 0
|
||||
fi
|
||||
echo "refresh-token grant must not be used when service-account credentials are configured" >&2
|
||||
exit 2
|
||||
fi
|
||||
printf '{"access_token":"service-account-token"}'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/folder-1/children)
|
||||
printf '{"files":[]}'
|
||||
;;
|
||||
@@ -67,12 +78,31 @@ cat >"$fake_bin/zstd" <<'EOF'
|
||||
cat
|
||||
EOF
|
||||
chmod +x "$fake_bin/zstd"
|
||||
cat >"$fake_bin/openssl" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
case "${1:-}" in
|
||||
base64)
|
||||
base64 | tr -d '\n'
|
||||
;;
|
||||
dgst)
|
||||
cat >/dev/null
|
||||
printf 'signed-fixture'
|
||||
;;
|
||||
*)
|
||||
echo "unexpected openssl command: $*" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$fake_bin/openssl"
|
||||
|
||||
WORKS_DRIVE_ACCESS_TOKEN="test-access-token" \
|
||||
WORKS_DRIVE_TARGET="sharedrive" \
|
||||
WORKS_DRIVE_SHARED_DRIVE_ID="shared-drive-1" \
|
||||
WORKS_DRIVE_PARENT_FILE_ID="folder-1" \
|
||||
WORKS_DRIVE_CURL_BIN="$fake_curl" \
|
||||
WORKS_DRIVE_ARCHIVE_DIR="$tmp_dir/archive" \
|
||||
FAKE_CURL_LOG="$curl_log" \
|
||||
PATH="$fake_bin:$PATH" \
|
||||
BACKUP="$backup_dir" \
|
||||
@@ -95,14 +125,108 @@ report_file="$backup_dir/reports/cloud-upload.json"
|
||||
[[ -f "$report_file" ]] || fail "upload must write reports/cloud-upload.json."
|
||||
jq -e '.target == "sharedrive" and .files[0].status == "uploaded" and .report_files[0].status == "uploaded" and (.report_files[0].file_name | test("^backup-report-[0-9]{8}-[0-9]{6}Z[.]md$"))' "$report_file" >/dev/null || fail "upload report must include timestamped markdown report file status."
|
||||
|
||||
service_account_curl_log="$tmp_dir/service-account-curl.log"
|
||||
WORKS_DRIVE_AUTH_MODE="auto" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN="" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN_FILE="" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN_CMD="" \
|
||||
WORKS_DRIVE_OAUTH_REFRESH_TOKEN="stale-refresh-token" \
|
||||
WORKS_DRIVE_OAUTH_CLIENT_ID="client-id-1" \
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SECRET="client-secret-1" \
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SERVICE_ACCOUNT="service-account-1" \
|
||||
WORKS_DRIVE_OAUTH_CLIENT_PRIVATE_KEY="private-key-fixture" \
|
||||
WORKS_ADMIN_OAUTH_TOKEN_URL="https://auth.example.test/token" \
|
||||
WORKS_DRIVE_TARGET="sharedrive" \
|
||||
WORKS_DRIVE_SHARED_DRIVE_ID="shared-drive-1" \
|
||||
WORKS_DRIVE_PARENT_FILE_ID="folder-1" \
|
||||
WORKS_DRIVE_CURL_BIN="$fake_curl" \
|
||||
WORKS_DRIVE_ARCHIVE_DIR="$tmp_dir/service-account-archive" \
|
||||
FAKE_CURL_LOG="$service_account_curl_log" \
|
||||
PATH="$fake_bin:$PATH" \
|
||||
BACKUP="$backup_dir" \
|
||||
"$repo_root/scripts/backup/upload_cloud.sh" >"$tmp_dir/service-account-upload.out"
|
||||
|
||||
grep -Fq "Upload complete" "$tmp_dir/service-account-upload.out" || fail "service-account upload must complete with fake curl."
|
||||
grep -Fq "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" "$service_account_curl_log" || fail "service-account credentials must use jwt-bearer grant."
|
||||
grep -Fq "Authorization: Bearer service-account-token" "$service_account_curl_log" || fail "service-account token must be used for WORKS API calls."
|
||||
if grep -Fq "grant_type=refresh_token" "$service_account_curl_log"; then
|
||||
fail "refresh-token grant must not be used when service-account credentials are configured."
|
||||
fi
|
||||
|
||||
empty_override_env_dir="$tmp_dir/empty-override-repo"
|
||||
mkdir -p "$empty_override_env_dir"
|
||||
cat >"$empty_override_env_dir/.env" <<'EOF'
|
||||
WORKS_DRIVE_AUTH_MODE=refresh-token
|
||||
WORKS_DRIVE_OAUTH_REFRESH_TOKEN=fresh-refresh-token
|
||||
WORKS_DRIVE_OAUTH_CLIENT_ID=client-id-1
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SECRET=client-secret-1
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SERVICE_ACCOUNT=service-account-1
|
||||
WORKS_DRIVE_OAUTH_CLIENT_PRIVATE_KEY=private-key-fixture
|
||||
WORKS_ADMIN_OAUTH_TOKEN_URL=https://auth.example.test/token
|
||||
WORKS_DRIVE_TARGET=sharedrive
|
||||
WORKS_DRIVE_SHARED_DRIVE_ID=shared-drive-1
|
||||
WORKS_DRIVE_PARENT_FILE_ID=folder-1
|
||||
WORKS_DRIVE_ARCHIVE_DIR=/tmp/unused-by-test
|
||||
EOF
|
||||
|
||||
empty_override_curl_log="$tmp_dir/empty-override-curl.log"
|
||||
BACKUP_REPO_ROOT="$empty_override_env_dir" \
|
||||
WORKS_DRIVE_AUTH_MODE="" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN="" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN_FILE="" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN_CMD="" \
|
||||
WORKS_DRIVE_CURL_BIN="$fake_curl" \
|
||||
WORKS_DRIVE_ARCHIVE_DIR="$tmp_dir/empty-override-archive" \
|
||||
ALLOW_REFRESH_TOKEN_GRANT="true" \
|
||||
FAKE_CURL_LOG="$empty_override_curl_log" \
|
||||
PATH="$fake_bin:$PATH" \
|
||||
BACKUP="$backup_dir" \
|
||||
"$repo_root/scripts/backup/upload_cloud.sh" >"$tmp_dir/empty-override-upload.out"
|
||||
|
||||
grep -Fq "Upload complete" "$tmp_dir/empty-override-upload.out" || fail "empty WORKS_DRIVE_AUTH_MODE override must still complete with .env value."
|
||||
grep -Fq "grant_type=refresh_token" "$empty_override_curl_log" || fail "empty WORKS_DRIVE_AUTH_MODE override must not mask .env refresh-token mode."
|
||||
if grep -Fq "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" "$empty_override_curl_log"; then
|
||||
fail "empty WORKS_DRIVE_AUTH_MODE override must not fall back to jwt-bearer when .env requests refresh-token."
|
||||
fi
|
||||
|
||||
forced_refresh_curl_log="$tmp_dir/forced-refresh-curl.log"
|
||||
WORKS_DRIVE_AUTH_MODE="refresh-token" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN="" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN_FILE="" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN_CMD="" \
|
||||
WORKS_DRIVE_OAUTH_REFRESH_TOKEN="fresh-refresh-token" \
|
||||
WORKS_DRIVE_OAUTH_CLIENT_ID="client-id-1" \
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SECRET="client-secret-1" \
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SERVICE_ACCOUNT="service-account-1" \
|
||||
WORKS_DRIVE_OAUTH_CLIENT_PRIVATE_KEY="private-key-fixture" \
|
||||
WORKS_ADMIN_OAUTH_TOKEN_URL="https://auth.example.test/token" \
|
||||
WORKS_DRIVE_TARGET="sharedrive" \
|
||||
WORKS_DRIVE_SHARED_DRIVE_ID="shared-drive-1" \
|
||||
WORKS_DRIVE_PARENT_FILE_ID="folder-1" \
|
||||
WORKS_DRIVE_CURL_BIN="$fake_curl" \
|
||||
WORKS_DRIVE_ARCHIVE_DIR="$tmp_dir/forced-refresh-archive" \
|
||||
ALLOW_REFRESH_TOKEN_GRANT="true" \
|
||||
FAKE_CURL_LOG="$forced_refresh_curl_log" \
|
||||
PATH="$fake_bin:$PATH" \
|
||||
BACKUP="$backup_dir" \
|
||||
"$repo_root/scripts/backup/upload_cloud.sh" >"$tmp_dir/forced-refresh-upload.out"
|
||||
|
||||
grep -Fq "Upload complete" "$tmp_dir/forced-refresh-upload.out" || fail "forced refresh-token upload must complete with fake curl."
|
||||
grep -Fq "grant_type=refresh_token" "$forced_refresh_curl_log" || fail "WORKS_DRIVE_AUTH_MODE=refresh-token must use refresh-token grant."
|
||||
grep -Fq "Authorization: Bearer refresh-token-access-token" "$forced_refresh_curl_log" || fail "forced refresh-token access token must be used for WORKS API calls."
|
||||
if grep -Fq "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" "$forced_refresh_curl_log"; then
|
||||
fail "WORKS_DRIVE_AUTH_MODE=refresh-token must not use jwt-bearer grant."
|
||||
fi
|
||||
|
||||
WORKS_DRIVE_DRY_RUN=true \
|
||||
WORKS_DRIVE_TARGET="sharedrive" \
|
||||
WORKS_DRIVE_SHARED_DRIVE_ID="shared-drive-1" \
|
||||
WORKS_DRIVE_PARENT_FILE_ID="folder-1" \
|
||||
WORKS_DRIVE_ARCHIVE_DIR="$tmp_dir/archive" \
|
||||
PATH="$fake_bin:$PATH" \
|
||||
BACKUP="$backup_dir" \
|
||||
"$repo_root/scripts/backup/upload_cloud.sh" >"$tmp_dir/dry-run.out"
|
||||
|
||||
grep -Fq "Dry run" "$tmp_dir/dry-run.out" || fail "dry-run must not require a token or call curl."
|
||||
|
||||
echo "OK: upload_cloud uploads current backup artifacts to WORKS Drive"
|
||||
echo "OK: upload_cloud mock upload flow packages backup artifacts for WORKS Drive"
|
||||
|
||||
75
test/devfront_port_policy_test.sh
Normal file
75
test/devfront_port_policy_test.sh
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
fail() {
|
||||
echo "ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local file="$1"
|
||||
local pattern="$2"
|
||||
grep -Fq -- "$pattern" "$file" || fail "$file must contain: $pattern"
|
||||
}
|
||||
|
||||
assert_not_contains() {
|
||||
local file="$1"
|
||||
local pattern="$2"
|
||||
if grep -Fq -- "$pattern" "$file"; then
|
||||
fail "$file must not contain stale devfront port pattern: $pattern"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_root_service_contains() {
|
||||
local service="$1"
|
||||
local pattern="$2"
|
||||
awk -v service=" $service:" '
|
||||
$0 == service { in_service = 1; next }
|
||||
in_service && /^ [[:alnum:]_-]+:/ { in_service = 0 }
|
||||
in_service { print }
|
||||
' "$ROOT_DIR/docker-compose.yaml" | grep -Fq -- "$pattern" || fail "docker-compose.yaml $service service must contain: $pattern"
|
||||
}
|
||||
|
||||
DEVFRONT_DOCKERFILE="$ROOT_DIR/devfront/Dockerfile"
|
||||
DEVFRONT_VITE_CONFIG="$ROOT_DIR/devfront/vite.config.ts"
|
||||
DEVFRONT_RUNTIME="$ROOT_DIR/devfront/scripts/runtime-mode.sh"
|
||||
DEVFRONT_DEPLOY_VITE_CONFIG="$ROOT_DIR/deploy/templates/devfront/vite.config.ts"
|
||||
|
||||
assert_contains "$DEVFRONT_DOCKERFILE" "EXPOSE 5174"
|
||||
assert_contains "$DEVFRONT_DOCKERFILE" 'CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5174"]'
|
||||
assert_contains "$DEVFRONT_DOCKERFILE" "ENV PORT=5174"
|
||||
assert_not_contains "$DEVFRONT_DOCKERFILE" "EXPOSE 5173"
|
||||
assert_not_contains "$DEVFRONT_DOCKERFILE" '"--port", "5173"'
|
||||
assert_not_contains "$DEVFRONT_DOCKERFILE" "ENV PORT=5173"
|
||||
|
||||
assert_contains "$DEVFRONT_VITE_CONFIG" "port: 5174"
|
||||
assert_contains "$DEVFRONT_RUNTIME" "npm run preview -- --host 0.0.0.0 --port 5174"
|
||||
assert_contains "$DEVFRONT_RUNTIME" "npm run dev -- --host 0.0.0.0 --port 5174"
|
||||
assert_contains "$DEVFRONT_DEPLOY_VITE_CONFIG" "port: 5174"
|
||||
|
||||
assert_root_service_contains "devfront" 'command: ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5174"]'
|
||||
assert_root_service_contains "devfront" '- "${DEVFRONT_PORT:-5174}:5174"'
|
||||
assert_root_service_contains "orgfront" '- "${ORGFRONT_PORT:-5175}:5175"'
|
||||
|
||||
for file in \
|
||||
"$ROOT_DIR/docker/docker-compose.template.yaml" \
|
||||
"$ROOT_DIR/docker/docker-compose.staging.template.yaml" \
|
||||
"$ROOT_DIR/docker/staging_pull_compose.template.yaml" \
|
||||
"$ROOT_DIR/deploy/templates/docker-compose.yaml"
|
||||
do
|
||||
assert_contains "$file" "devfront:"
|
||||
assert_not_contains "$file" '${DEVFRONT_PORT:-5174}:5173'
|
||||
assert_not_contains "$file" '${DEVFRONT_PORT}:5173'
|
||||
done
|
||||
|
||||
assert_contains "$ROOT_DIR/docker/docker-compose.template.yaml" '${DEVFRONT_PORT:-5174}:5174'
|
||||
assert_contains "$ROOT_DIR/docker/docker-compose.staging.template.yaml" '${DEVFRONT_PORT:-5174}:5174'
|
||||
assert_contains "$ROOT_DIR/docker/staging_pull_compose.template.yaml" '${DEVFRONT_PORT:-5174}:5174'
|
||||
assert_contains "$ROOT_DIR/deploy/templates/docker-compose.yaml" '${DEVFRONT_PORT}:5174'
|
||||
|
||||
assert_contains "$ROOT_DIR/docker/staging_pull_compose.template.yaml" "fetch('http://127.0.0.1:5174/')"
|
||||
assert_contains "$ROOT_DIR/deploy/templates/docker-compose.yaml" "traefik.http.services.\${COMPOSE_PROJECT_NAME}-devfront.loadbalancer.server.port=5174"
|
||||
|
||||
echo "OK: DevFront Docker internal port is aligned to 5174"
|
||||
70
test/prod_deploy_traefik_policy_test.sh
Normal file
70
test/prod_deploy_traefik_policy_test.sh
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
compose_template="$repo_root/deploy/templates/docker-compose.yaml"
|
||||
env_template="$repo_root/deploy/templates/.env.template"
|
||||
create_script="$repo_root/deploy/create-instance.sh"
|
||||
|
||||
fail() {
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ -f "$compose_template" ]] || fail "deploy/templates/docker-compose.yaml must exist."
|
||||
[[ -f "$env_template" ]] || fail "deploy/templates/.env.template must exist."
|
||||
[[ -f "$create_script" ]] || fail "deploy/create-instance.sh must exist."
|
||||
|
||||
grep -Fq "TRAEFIK_PUBLIC_NETWORK=traefik-public" "$env_template" \
|
||||
|| fail ".env template must define TRAEFIK_PUBLIC_NETWORK=traefik-public."
|
||||
grep -Fq "TRAEFIK_ENTRYPOINT=websecure" "$env_template" \
|
||||
|| fail ".env template must define TRAEFIK_ENTRYPOINT=websecure."
|
||||
grep -Fq "TRAEFIK_CERT_RESOLVER=myresolver" "$env_template" \
|
||||
|| fail ".env template must define TRAEFIK_CERT_RESOLVER=myresolver."
|
||||
grep -Fq "SOURCE_ROOT=../.." "$env_template" \
|
||||
|| fail ".env template must define SOURCE_ROOT for compose build context."
|
||||
|
||||
grep -Fq "traefik.enable=true" "$compose_template" \
|
||||
|| fail "gateway must enable Traefik docker provider labels."
|
||||
grep -Fq 'traefik.http.routers.${COMPOSE_PROJECT_NAME}-gateway.rule=Host(`${PUBLIC_HOST}`)' "$compose_template" \
|
||||
|| fail "gateway must route the public host through Traefik."
|
||||
grep -Fq 'traefik.http.routers.${COMPOSE_PROJECT_NAME}-gateway.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure}' "$compose_template" \
|
||||
|| fail "gateway router must use the configured Traefik entrypoint."
|
||||
grep -Fq 'traefik.http.routers.${COMPOSE_PROJECT_NAME}-gateway.tls.certresolver=${TRAEFIK_CERT_RESOLVER:-myresolver}' "$compose_template" \
|
||||
|| fail "gateway router must configure the TLS cert resolver."
|
||||
grep -Fq 'traefik.http.services.${COMPOSE_PROJECT_NAME}-gateway.loadbalancer.server.port=80' "$compose_template" \
|
||||
|| fail "gateway service must expose internal port 80 to Traefik."
|
||||
grep -Fq 'traefik.docker.network=${TRAEFIK_PUBLIC_NETWORK:-traefik-public}' "$compose_template" \
|
||||
|| fail "gateway must pin Traefik to the public network."
|
||||
grep -Fq 'context: ${SOURCE_ROOT:-../..}' "$compose_template" \
|
||||
|| fail "public front builds must use SOURCE_ROOT so TARGET_DIR can live outside the repo."
|
||||
grep -Fq '${SOURCE_ROOT:-../..}/adminfront/seed-tenant.csv:/app/seed-tenant.csv:ro' "$compose_template" \
|
||||
|| fail "backend seed volume must use SOURCE_ROOT so TARGET_DIR can live outside the repo."
|
||||
|
||||
grep -Eq 'gateway:[[:space:]]*$' "$compose_template" \
|
||||
|| fail "gateway service must exist."
|
||||
grep -Fq -- "- traefik_public" "$compose_template" \
|
||||
|| fail "public-facing services must join the external Traefik public network."
|
||||
grep -Fq 'external: true' "$compose_template" \
|
||||
|| fail "Traefik public network must be declared as external."
|
||||
grep -Fq 'name: ${TRAEFIK_PUBLIC_NETWORK:-traefik-public}' "$compose_template" \
|
||||
|| fail "Traefik public network name must be configurable."
|
||||
|
||||
for service in adminfront devfront orgfront; do
|
||||
grep -Fq "traefik.http.routers.\${COMPOSE_PROJECT_NAME}-${service}.rule=Host(" "$compose_template" \
|
||||
|| fail "$service must expose a Traefik host router."
|
||||
grep -Fq "traefik.http.services.\${COMPOSE_PROJECT_NAME}-${service}.loadbalancer.server.port=" "$compose_template" \
|
||||
|| fail "$service must declare its internal Traefik service port."
|
||||
done
|
||||
|
||||
grep -Fq 'TRAEFIK_PUBLIC_NETWORK' "$create_script" \
|
||||
|| fail "create-instance must know about the Traefik public network."
|
||||
grep -Fq 'docker network create "$TRAEFIK_PUBLIC_NETWORK"' "$create_script" \
|
||||
|| fail "create-instance must create the Traefik public network when missing."
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
cp "$compose_template" "$tmp_dir/docker-compose.yaml"
|
||||
cp "$env_template" "$tmp_dir/.env"
|
||||
sed -i 's/{{INSTANCE_NAME}}/policy/g; s/{{PORT_PREFIX}}/26/g' "$tmp_dir/.env"
|
||||
docker compose --env-file "$tmp_dir/.env" -f "$tmp_dir/docker-compose.yaml" config >/dev/null
|
||||
126
test/production_image_workflows_policy_test.sh
Normal file
126
test/production_image_workflows_policy_test.sh
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
publish_workflow="$repo_root/.gitea/workflows/production_image_publish.yml"
|
||||
staging_deploy_workflow="$repo_root/.gitea/workflows/staging_image_deploy.yml"
|
||||
deploy_workflow="$repo_root/.gitea/workflows/production_image_deploy.yml"
|
||||
image_compose="$repo_root/deploy/templates/docker-compose.images.yaml"
|
||||
bundle_script="$repo_root/scripts/deploy/build_image_deploy_bundle.sh"
|
||||
remote_deploy_script="$repo_root/scripts/deploy/upload_and_run_image_deploy.sh"
|
||||
|
||||
fail() {
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ -f "$publish_workflow" ]] || fail "production image publish workflow must exist."
|
||||
[[ -f "$staging_deploy_workflow" ]] || fail "staging image deploy workflow must exist."
|
||||
[[ -f "$deploy_workflow" ]] || fail "production image deploy workflow must exist."
|
||||
[[ -f "$image_compose" ]] || fail "image-based production compose template must exist."
|
||||
[[ -f "$bundle_script" ]] || fail "shared image deployment bundle script must exist."
|
||||
[[ -f "$remote_deploy_script" ]] || fail "shared image remote deploy script must exist."
|
||||
|
||||
grep -Fq "name: Publish Baron SSO Production Images" "$publish_workflow" \
|
||||
|| fail "publish workflow must have the expected name."
|
||||
grep -Fq "workflow_dispatch:" "$publish_workflow" \
|
||||
|| fail "publish workflow must be manually dispatchable."
|
||||
if grep -Fq "source_ref:" "$publish_workflow"; then
|
||||
fail "publish workflow must not accept an arbitrary source_ref; production images must be built from dev."
|
||||
fi
|
||||
grep -Fq "ref: dev" "$publish_workflow" \
|
||||
|| fail "publish workflow must checkout the dev branch."
|
||||
grep -Fq "version_prefix:" "$publish_workflow" \
|
||||
|| fail "publish workflow must accept a version prefix."
|
||||
grep -Fq 'git rev-parse --short=4 HEAD' "$publish_workflow" \
|
||||
|| fail "publish workflow must derive the final image tag from the checked-out commit hash."
|
||||
grep -Fq 'image_tag="${VERSION_PREFIX}.${short_sha}"' "$publish_workflow" \
|
||||
|| fail "publish workflow must append the 4-character commit hash as the last version segment."
|
||||
grep -Fq "steps.version.outputs.image_tag" "$publish_workflow" \
|
||||
|| fail "publish workflow must use the computed image tag for pushed images."
|
||||
grep -Fq "Upload pushed images to WORKS Drive archive" "$publish_workflow" \
|
||||
|| fail "publish workflow must archive the exact pushed images to WORKS Drive."
|
||||
grep -Fq "docker pull" "$publish_workflow" \
|
||||
|| fail "publish workflow must pull the pushed image before WORKS archive upload."
|
||||
grep -Fq "scripts/docker-image/upload_works_drive.sh" "$publish_workflow" \
|
||||
|| fail "publish workflow must use the shared WORKS Drive image archive script."
|
||||
grep -Fq "docker/login-action@v3" "$publish_workflow" \
|
||||
|| fail "publish workflow must login to the shared registry."
|
||||
grep -Fq "docker/build-push-action@v5" "$publish_workflow" \
|
||||
|| fail "publish workflow must build and push images."
|
||||
for image in backend userfront adminfront devfront orgfront; do
|
||||
grep -Fq "/baron_sso/${image}:" "$publish_workflow" \
|
||||
|| fail "publish workflow must push ${image} image."
|
||||
done
|
||||
|
||||
grep -Fq "name: Deploy Baron SSO Staging Images" "$staging_deploy_workflow" \
|
||||
|| fail "staging deploy workflow must have the expected name."
|
||||
grep -Fq "image_tag:" "$staging_deploy_workflow" \
|
||||
|| fail "staging deploy workflow must accept the same immutable image tag as production."
|
||||
grep -Fq "IMAGE_TAG must look like vX.YYMM.ab12" "$bundle_script" \
|
||||
|| fail "shared bundle script must validate the commit-hash image tag format."
|
||||
grep -Fq "IMAGE_DEPLOY_ENV: stage" "$staging_deploy_workflow" \
|
||||
|| fail "staging deploy workflow must select the stage deployment environment."
|
||||
grep -Fq "deploy/templates/docker-compose.images.yaml" "$staging_deploy_workflow" \
|
||||
|| fail "staging deploy workflow must pass the image-based compose template through the shared bundle script."
|
||||
grep -Fq "scripts/deploy/build_image_deploy_bundle.sh" "$staging_deploy_workflow" \
|
||||
|| fail "staging deploy workflow must use the shared bundle script."
|
||||
grep -Fq "scripts/deploy/upload_and_run_image_deploy.sh" "$staging_deploy_workflow" \
|
||||
|| fail "staging deploy workflow must use the shared remote deploy script."
|
||||
|
||||
grep -Fq "name: Deploy Baron SSO Production Images" "$deploy_workflow" \
|
||||
|| fail "deploy workflow must have the expected name."
|
||||
grep -Fq "image_tag:" "$deploy_workflow" \
|
||||
|| fail "deploy workflow must accept an image tag."
|
||||
grep -Fq "v1.2606.ab12" "$deploy_workflow" \
|
||||
|| fail "deploy workflow must document the commit-hash image tag format with an example."
|
||||
grep -Fq "IMAGE_TAG must look like vX.YYMM.ab12" "$bundle_script" \
|
||||
|| fail "deploy workflow must rely on the shared commit-hash image tag validation."
|
||||
grep -Fq "IMAGE_DEPLOY_ENV: production" "$deploy_workflow" \
|
||||
|| fail "deploy workflow must select the production deployment environment."
|
||||
grep -Fq 'APP_ENV=${app_env}' "$bundle_script" \
|
||||
|| fail "shared bundle script must write APP_ENV from the selected deployment environment."
|
||||
grep -Fq "deploy/templates/docker-compose.images.yaml" "$deploy_workflow" \
|
||||
|| fail "deploy workflow must pass the image-based compose template through the shared bundle script."
|
||||
grep -Fq "scripts/deploy/build_image_deploy_bundle.sh" "$deploy_workflow" \
|
||||
|| fail "production deploy workflow must use the shared bundle script."
|
||||
grep -Fq "scripts/deploy/upload_and_run_image_deploy.sh" "$deploy_workflow" \
|
||||
|| fail "production deploy workflow must use the shared remote deploy script."
|
||||
grep -Fq "Same image tag contract as staging" "$deploy_workflow" \
|
||||
|| fail "production deploy workflow must document that it uses the same image tag as staging."
|
||||
grep -Fq "TRAEFIK_PUBLIC_NETWORK=traefik-public" "$bundle_script" \
|
||||
|| fail "shared bundle script must write Traefik public network env."
|
||||
grep -Fq "docker compose --env-file .env -f docker-compose.yml pull" "$remote_deploy_script" \
|
||||
|| fail "shared remote deploy script must pull the requested image version before running."
|
||||
grep -Fq "docker compose --env-file .env -f docker-compose.yml up -d" "$remote_deploy_script" \
|
||||
|| fail "shared remote deploy script must start the stack after pulling images."
|
||||
|
||||
if grep -Eq 'docker (build|commit)' "$staging_deploy_workflow" "$deploy_workflow"; then
|
||||
fail "staging/production deploy workflows must never build or commit images remotely."
|
||||
fi
|
||||
|
||||
if grep -Eq '^[[:space:]]+build:' "$image_compose"; then
|
||||
fail "image-based production compose template must not contain build sections."
|
||||
fi
|
||||
for image_var in BACKEND_IMAGE_NAME USERFRONT_IMAGE_NAME ADMINFRONT_IMAGE_NAME DEVFRONT_IMAGE_NAME ORGFRONT_IMAGE_NAME; do
|
||||
grep -Fq "\${${image_var}}:\${IMAGE_TAG}" "$image_compose" \
|
||||
|| fail "image compose must use ${image_var} with IMAGE_TAG."
|
||||
done
|
||||
grep -Fq "APP_ENV=\${APP_ENV:-production}" "$image_compose" \
|
||||
|| fail "image compose must run services with production APP_ENV default."
|
||||
grep -Fq "traefik_public:" "$image_compose" \
|
||||
|| fail "image compose must keep Traefik public network wiring."
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
cp "$repo_root/deploy/templates/.env.template" "$tmp_dir/.env"
|
||||
cp "$image_compose" "$tmp_dir/docker-compose.yml"
|
||||
sed -i 's/{{INSTANCE_NAME}}/policy/g; s/{{PORT_PREFIX}}/26/g' "$tmp_dir/.env"
|
||||
|
||||
IMAGE_TAG=v9.9999.ab12 \
|
||||
BACKEND_IMAGE_NAME=registry.example/baron_sso/backend \
|
||||
USERFRONT_IMAGE_NAME=registry.example/baron_sso/userfront \
|
||||
ADMINFRONT_IMAGE_NAME=registry.example/baron_sso/adminfront \
|
||||
DEVFRONT_IMAGE_NAME=registry.example/baron_sso/devfront \
|
||||
ORGFRONT_IMAGE_NAME=registry.example/baron_sso/orgfront \
|
||||
docker compose --env-file "$tmp_dir/.env" -f "$tmp_dir/docker-compose.yml" config >/dev/null
|
||||
46
test/traefik_forward_auth_config_policy_test.sh
Normal file
46
test/traefik_forward_auth_config_policy_test.sh
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
compose_file="$repo_root/config/traefik-compose.yml"
|
||||
|
||||
fail() {
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ -f "$compose_file" ]] || fail "config/traefik-compose.yml must exist."
|
||||
|
||||
if grep -Eq 'thomseddon/traefik-forward-auth:2\.3\.0' "$compose_file"; then
|
||||
fail "traefik-forward-auth image tag 2.3.0 is unavailable; use a runnable pinned tag."
|
||||
fi
|
||||
|
||||
if grep -Eq 'auth/realms/master|PROVIDER_GENERIC_' "$compose_file"; then
|
||||
fail "Traefik forward-auth must use Baron/Ory Hydra endpoint variables, not legacy Keycloak or unsupported provider keys."
|
||||
fi
|
||||
|
||||
if grep -Eq 'CLIENT_SECRET=[^$]|SECRET=[^$]' "$compose_file"; then
|
||||
fail "Traefik forward-auth secrets must be injected from environment variables, not hardcoded in compose."
|
||||
fi
|
||||
|
||||
grep -Fq 'DEFAULT_PROVIDER=generic-oauth' "$compose_file" \
|
||||
|| fail "Traefik bootstrap must use generic-oauth provider to avoid OIDC discovery before Baron/Ory is running."
|
||||
grep -Fq 'PROVIDERS_GENERIC_OAUTH_AUTH_URL=${HYDRA_PUBLIC_URL:-https://app.brsw.kr/oidc}/oauth2/auth' "$compose_file" \
|
||||
|| fail "Traefik forward-auth auth URL must point to Hydra authorize endpoint."
|
||||
grep -Fq 'PROVIDERS_GENERIC_OAUTH_TOKEN_URL=${HYDRA_PUBLIC_URL:-https://app.brsw.kr/oidc}/oauth2/token' "$compose_file" \
|
||||
|| fail "Traefik forward-auth token URL must point to Hydra token endpoint."
|
||||
grep -Fq 'PROVIDERS_GENERIC_OAUTH_USER_URL=${HYDRA_PUBLIC_URL:-https://app.brsw.kr/oidc}/userinfo' "$compose_file" \
|
||||
|| fail "Traefik forward-auth user URL must point to Hydra userinfo endpoint."
|
||||
|
||||
grep -Fq 'traefik.http.routers.traefik-dashboard.middlewares=auth-forward@docker' "$compose_file" \
|
||||
|| fail "Traefik dashboard router must be protected by auth-forward middleware."
|
||||
grep -Fq 'traefik.http.services.forward-auth.loadbalancer.server.port=4181' "$compose_file" \
|
||||
|| fail "forward-auth service port must be declared for Traefik docker provider."
|
||||
grep -Fq 'traefik-public:' "$compose_file" \
|
||||
|| fail "traefik-public external network must be declared."
|
||||
grep -Fq 'name: traefik-public' "$compose_file" \
|
||||
|| fail "traefik-public network name must be explicit."
|
||||
|
||||
TRAEFIK_FORWARD_AUTH_CLIENT_SECRET=dummy \
|
||||
TRAEFIK_FORWARD_AUTH_COOKIE_SECRET=dummy \
|
||||
docker compose -f "$compose_file" config >/dev/null
|
||||
220
test/works_drive_docker_image_upload_policy_test.sh
Executable file
220
test/works_drive_docker_image_upload_policy_test.sh
Executable file
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
script="$repo_root/scripts/docker-image/upload_works_drive.sh"
|
||||
doc="$repo_root/docs/works-drive-docker-image-archive.md"
|
||||
makefile="$repo_root/Makefile"
|
||||
|
||||
fail() {
|
||||
echo "ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ -f "$script" ]] || fail "WORKS Drive Docker image upload script must exist."
|
||||
[[ -f "$doc" ]] || fail "WORKS Drive Docker image archive design document must exist."
|
||||
|
||||
grep -Fq 'WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR:-docker-build-image' "$script" \
|
||||
|| fail "script must default WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR to docker-build-image."
|
||||
grep -Fq 'docker commit' "$script" \
|
||||
|| fail "script must support committing a local container before image export."
|
||||
grep -Fq 'docker save' "$script" \
|
||||
|| fail "script must use docker save for CLI-compatible image artifacts."
|
||||
grep -Fq 'zstd' "$script" \
|
||||
|| fail "script must compress Docker image archives with zstd."
|
||||
grep -Fq 'manifest.json' "$script" \
|
||||
|| fail "script must write a manifest.json next to the image archive."
|
||||
grep -Fq 'image.tar.zst.sha256' "$script" \
|
||||
|| fail "script must write a checksum file for the compressed image archive."
|
||||
grep -Fq 'docker-build-image/baron_sso/backend/v1.2606.ab12' "$doc" \
|
||||
|| fail "document must describe the expected WORKS Drive folder layout."
|
||||
grep -Fq 'debian:trixie-slim' "$doc" \
|
||||
|| fail "document must use debian:trixie-slim for smoke image examples."
|
||||
grep -Fq 'staging과 production은 같은 image_tag' "$doc" \
|
||||
|| fail "document must state that staging and production consume the same image tag."
|
||||
grep -Fq 'docker-image-upload-works:' "$makefile" \
|
||||
|| fail "Makefile must expose a docker-image-upload-works target."
|
||||
grep -Fq 'scripts/docker-image/upload_works_drive.sh' "$makefile" \
|
||||
|| fail "Makefile target must call the WORKS Drive image upload script."
|
||||
if grep -Eq 'docker (push|pull)' "$script"; then
|
||||
fail "WORKS Drive image archive script must not pretend to be a Docker Registry push/pull backend."
|
||||
fi
|
||||
|
||||
tmp_dir="$(mktemp -d /tmp/baron-sso-works-image-test.XXXXXX)"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT INT TERM
|
||||
|
||||
fake_bin="$tmp_dir/bin"
|
||||
mkdir -p "$fake_bin"
|
||||
|
||||
cat >"$fake_bin/docker" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
printf 'docker %s\n' "$*" >>"${FAKE_DOCKER_LOG}"
|
||||
|
||||
if [[ "$1" == "commit" ]]; then
|
||||
printf 'sha256:committed-image\n'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" == "image" && "$2" == "inspect" ]]; then
|
||||
printf 'sha256:inspect-image-id\n'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$1" == "save" ]]; then
|
||||
output=""
|
||||
image_ref=""
|
||||
shift
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-o)
|
||||
output="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
image_ref="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
[[ -n "$output" ]] || exit 2
|
||||
printf 'docker image archive for %s\n' "$image_ref" >"$output"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "unexpected docker command: $*" >&2
|
||||
exit 2
|
||||
EOF
|
||||
chmod +x "$fake_bin/docker"
|
||||
|
||||
cat >"$fake_bin/zstd" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
output=""
|
||||
input=""
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-o)
|
||||
output="$2"
|
||||
shift 2
|
||||
;;
|
||||
-*)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
input="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
[[ -n "$output" && -n "$input" ]] || exit 2
|
||||
cp "$input" "$output"
|
||||
EOF
|
||||
chmod +x "$fake_bin/zstd"
|
||||
|
||||
fake_curl="$tmp_dir/fake-curl.sh"
|
||||
cat >"$fake_curl" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
printf '%s\n' "$*" >>"${FAKE_CURL_LOG}"
|
||||
last_arg="${!#}"
|
||||
|
||||
case "$last_arg" in
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/root-folder/children)
|
||||
printf '{"files":[]}'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/root-folder/createfolder)
|
||||
printf '{"fileId":"docker-build-image-id","fileName":"docker-build-image","fileType":"FOLDER"}'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/docker-build-image-id/children)
|
||||
printf '{"files":[]}'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/docker-build-image-id/createfolder)
|
||||
printf '{"fileId":"baron-sso-id","fileName":"baron_sso","fileType":"FOLDER"}'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/baron-sso-id/children)
|
||||
printf '{"files":[]}'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/baron-sso-id/createfolder)
|
||||
printf '{"fileId":"backend-id","fileName":"backend","fileType":"FOLDER"}'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/backend-id/children)
|
||||
printf '{"files":[]}'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/backend-id/createfolder)
|
||||
printf '{"fileId":"tag-id","fileName":"v1.2606.ab12","fileType":"FOLDER"}'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/tag-id)
|
||||
printf '{"uploadUrl":"https://upload.example.test/docker-image"}'
|
||||
;;
|
||||
https://upload.example.test/docker-image)
|
||||
printf '{"fileId":"uploaded-file-id"}'
|
||||
;;
|
||||
*)
|
||||
echo "unexpected curl URL: $last_arg" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "$fake_curl"
|
||||
|
||||
docker_log="$tmp_dir/docker.log"
|
||||
curl_log="$tmp_dir/curl.log"
|
||||
archive_dir="$tmp_dir/archive"
|
||||
|
||||
FAKE_DOCKER_LOG="$docker_log" \
|
||||
FAKE_CURL_LOG="$curl_log" \
|
||||
PATH="$fake_bin:$PATH" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN="test-access-token" \
|
||||
WORKS_DRIVE_TARGET="sharedrive" \
|
||||
WORKS_DRIVE_SHARED_DRIVE_ID="shared-drive-1" \
|
||||
WORKS_DRIVE_PARENT_FILE_ID="root-folder" \
|
||||
WORKS_DRIVE_CURL_BIN="$fake_curl" \
|
||||
WORKS_DOCKER_IMAGE_ARCHIVE_DIR="$archive_dir" \
|
||||
WORKS_DOCKER_COMMIT_CONTAINER="baron_backend" \
|
||||
DOCKER_IMAGE_REF="registry.example/baron_sso/backend:v1.2606.ab12" \
|
||||
"$script" >"$tmp_dir/upload.out"
|
||||
|
||||
artifact_dir="$archive_dir/baron_sso/backend/v1.2606.ab12"
|
||||
[[ -f "$artifact_dir/image.tar.zst" ]] || fail "script must create image.tar.zst."
|
||||
[[ -f "$artifact_dir/image.tar.zst.sha256" ]] || fail "script must create image.tar.zst.sha256."
|
||||
[[ -f "$artifact_dir/manifest.json" ]] || fail "script must create manifest.json."
|
||||
|
||||
jq -e \
|
||||
'.schema_version == 1
|
||||
and .format == "docker-save-zstd"
|
||||
and .image_ref == "registry.example/baron_sso/backend:v1.2606.ab12"
|
||||
and .repository == "baron_sso/backend"
|
||||
and .tag == "v1.2606.ab12"
|
||||
and .remote_path == "docker-build-image/baron_sso/backend/v1.2606.ab12"
|
||||
and .archive.file_name == "image.tar.zst"
|
||||
and (.archive.sha256 | type == "string")' \
|
||||
"$artifact_dir/manifest.json" >/dev/null || fail "manifest must describe the image archive and remote path."
|
||||
|
||||
grep -Fq "docker commit baron_backend registry.example/baron_sso/backend:v1.2606.ab12" "$docker_log" \
|
||||
|| fail "script must commit the requested container into the requested image ref."
|
||||
grep -Fq "docker save -o" "$docker_log" \
|
||||
|| fail "script must save the requested image."
|
||||
grep -Fq "sharedrives/shared-drive-1/files/root-folder/createfolder" "$curl_log" \
|
||||
|| fail "script must create the top-level docker-build-image folder when needed."
|
||||
grep -Fq "docker-build-image" "$curl_log" \
|
||||
|| fail "script must use WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR in folder creation."
|
||||
grep -Fq "baron_sso" "$curl_log" \
|
||||
|| fail "script must create repository namespace folder."
|
||||
grep -Fq "backend" "$curl_log" \
|
||||
|| fail "script must create image repository folder."
|
||||
grep -Fq "v1.2606.ab12" "$curl_log" \
|
||||
|| fail "script must create tag folder."
|
||||
grep -Fq "image.tar.zst" "$curl_log" \
|
||||
|| fail "script must upload the compressed image archive."
|
||||
grep -Fq "image.tar.zst.sha256" "$curl_log" \
|
||||
|| fail "script must upload the checksum file."
|
||||
grep -Fq "manifest.json" "$curl_log" \
|
||||
|| fail "script must upload the manifest file."
|
||||
|
||||
report_file="$artifact_dir/works-upload.json"
|
||||
[[ -f "$report_file" ]] || fail "script must write works-upload.json."
|
||||
jq -e '.status == "uploaded" and (.files | length) == 3' "$report_file" >/dev/null \
|
||||
|| fail "upload report must include three uploaded artifact files."
|
||||
|
||||
echo "OK: WORKS Drive Docker image archive upload flow commits, packages, and uploads image artifacts"
|
||||
102
test/works_drive_refresh_token_policy_test.sh
Executable file
102
test/works_drive_refresh_token_policy_test.sh
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
fail() {
|
||||
echo "ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
script="$repo_root/scripts/backup/refresh_works_drive_token.sh"
|
||||
[[ -f "$script" ]] || fail "refresh_works_drive_token.sh must exist."
|
||||
|
||||
tmp_dir="$(mktemp -d /tmp/baron-sso-works-drive-token-test.XXXXXX)"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT INT TERM
|
||||
|
||||
env_file="$tmp_dir/.env"
|
||||
cat >"$env_file" <<'EOF'
|
||||
WORKS_DRIVE_OAUTH_CLIENT_ID=client-id-1
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SECRET=client-secret-1
|
||||
WORKS_DRIVE_OAUTH_REDIRECT_URI=https://example.test/callback
|
||||
WORKS_DRIVE_OAUTH_REFRESH_TOKEN=old-refresh-token
|
||||
WORKS_DRIVE_AUTH_MODE=auto
|
||||
EOF
|
||||
chmod 600 "$env_file"
|
||||
|
||||
curl_log="$tmp_dir/curl.log"
|
||||
fake_curl="$tmp_dir/fake-curl.sh"
|
||||
cat >"$fake_curl" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
printf '%s\n' "$*" >>"${FAKE_CURL_LOG}"
|
||||
|
||||
if [[ "$*" == *"grant_type=refresh_token"* ]]; then
|
||||
printf '{"access_token":"new-access-token","refresh_token":"new-refresh-token","expires_in":"86400","token_type":"Bearer","scope":"file"}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$*" == *"grant_type=authorization_code"* ]]; then
|
||||
printf '{"access_token":"code-access-token","refresh_token":"code-refresh-token","expires_in":"86400","token_type":"Bearer","scope":"file"}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "unexpected curl arguments: $*" >&2
|
||||
exit 2
|
||||
EOF
|
||||
chmod +x "$fake_curl"
|
||||
|
||||
WORKS_DRIVE_ENV_FILE="$env_file" \
|
||||
WORKS_DRIVE_CURL_BIN="$fake_curl" \
|
||||
FAKE_CURL_LOG="$curl_log" \
|
||||
"$script" >"$tmp_dir/refresh.out"
|
||||
|
||||
grep -Fq "WORKS Drive refresh token updated" "$tmp_dir/refresh.out" || fail "refresh-token mode must update the refresh token."
|
||||
grep -Fq "grant_type=refresh_token" "$curl_log" || fail "refresh-token mode must call refresh_token grant."
|
||||
grep -Fq "WORKS_DRIVE_OAUTH_REFRESH_TOKEN=new-refresh-token" "$env_file" || fail ".env must contain the rotated refresh token."
|
||||
grep -Fq "WORKS_DRIVE_AUTH_MODE=refresh-token" "$env_file" || fail ".env must prefer refresh-token mode after token refresh."
|
||||
[[ "$(stat -c '%a' "$env_file")" == "600" ]] || fail ".env mode must be preserved after refresh token update."
|
||||
if grep -Fq "new-access-token" "$env_file"; then
|
||||
fail "short-lived access token must not be persisted by default."
|
||||
fi
|
||||
|
||||
auth_env_file="$tmp_dir/.env.auth-code"
|
||||
cat >"$auth_env_file" <<'EOF'
|
||||
WORKS_DRIVE_OAUTH_CLIENT_ID=client-id-1
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SECRET=client-secret-1
|
||||
WORKS_DRIVE_OAUTH_REDIRECT_URI=https://example.test/callback
|
||||
EOF
|
||||
|
||||
WORKS_DRIVE_ENV_FILE="$auth_env_file" \
|
||||
WORKS_DRIVE_CURL_BIN="$fake_curl" \
|
||||
WORKS_DRIVE_TOKEN_GRANT=authorization-code \
|
||||
WORKS_DRIVE_AUTH_CALLBACK_URL="https://example.test/callback?code=auth-code-1&state=state-1" \
|
||||
FAKE_CURL_LOG="$curl_log" \
|
||||
"$script" >"$tmp_dir/auth-code.out"
|
||||
|
||||
grep -Fq "WORKS Drive refresh token updated" "$tmp_dir/auth-code.out" || fail "authorization-code mode must update the refresh token."
|
||||
grep -Fq "grant_type=authorization_code" "$curl_log" || fail "authorization-code mode must call authorization_code grant."
|
||||
grep -Fq "code=auth-code-1" "$curl_log" || fail "authorization-code mode must extract code from callback URL."
|
||||
grep -Fq "WORKS_DRIVE_OAUTH_REFRESH_TOKEN=code-refresh-token" "$auth_env_file" || fail ".env must contain authorization-code refresh token."
|
||||
|
||||
authorize_url="$(
|
||||
WORKS_DRIVE_ENV_FILE="$auth_env_file" \
|
||||
WORKS_DRIVE_TOKEN_GRANT=print-authorize-url \
|
||||
"$script"
|
||||
)"
|
||||
|
||||
grep -Fq "https://auth.worksmobile.com/oauth2/v2.0/authorize" <<<"$authorize_url" || fail "print-authorize-url mode must print WORKS authorize URL."
|
||||
grep -Fq "client_id=client-id-1" <<<"$authorize_url" || fail "authorize URL must include client_id."
|
||||
grep -Fq "response_type=code" <<<"$authorize_url" || fail "authorize URL must request an authorization code."
|
||||
|
||||
make_dry_run="$(
|
||||
make --dry-run --always-make -C "$repo_root" works-drive-refresh-token WORKS_DRIVE_TOKEN_GRANT=refresh-token 2>&1
|
||||
)"
|
||||
|
||||
grep -Fq "scripts/backup/refresh_works_drive_token.sh" <<<"$make_dry_run" || fail "Makefile target must call refresh token script."
|
||||
grep -Fq "WORKS_DRIVE_TOKEN_GRANT=\"refresh-token\"" <<<"$make_dry_run" || fail "Makefile target must pass token grant."
|
||||
if grep -Fq "docker run" <<<"$make_dry_run"; then
|
||||
fail "Makefile target must refresh .env on the host, not inside Docker."
|
||||
fi
|
||||
|
||||
echo "OK: WORKS Drive refresh token helper updates env and supports authorization-code bootstrap"
|
||||
Reference in New Issue
Block a user