forked from baron/baron-sso
웍스 드라이브 구조 변경
This commit is contained in:
@@ -24,7 +24,7 @@ jobs:
|
||||
DEVFRONT_URL: ${{ vars.DEVFRONT_URL }}
|
||||
ORGFRONT_URL: ${{ vars.ORGFRONT_URL }}
|
||||
VITE_OIDC_AUTHORITY: ${{ vars.VITE_OIDC_AUTHORITY }}
|
||||
WORKS_DRIVE_SHARED_DRIVE_ID: ${{ vars.WORKS_DRIVE_SHARED_DRIVE_ID }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}
|
||||
WORKS_DRIVE_ACCESS_TOKEN_INPUT: ${{ secrets.WORKS_DRIVE_ACCESS_TOKEN }}
|
||||
WORKS_DRIVE_ACCESS_TOKEN_FILE: ${{ vars.WORKS_DRIVE_ACCESS_TOKEN_FILE }}
|
||||
WORKS_DRIVE_ACCESS_TOKEN_CMD: ${{ vars.WORKS_DRIVE_ACCESS_TOKEN_CMD }}
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
fi
|
||||
|
||||
required_values="
|
||||
ADMINFRONT_URL DEVFRONT_URL ORGFRONT_URL VITE_OIDC_AUTHORITY WORKS_DRIVE_SHARED_DRIVE_ID
|
||||
ADMINFRONT_URL DEVFRONT_URL ORGFRONT_URL VITE_OIDC_AUTHORITY WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID
|
||||
"
|
||||
for key in ${required_values}; do
|
||||
if [ -z "${!key:-}" ]; then
|
||||
@@ -213,18 +213,18 @@ jobs:
|
||||
- name: Upload built images to WORKS Drive archive
|
||||
env:
|
||||
IMAGE_TAG: ${{ steps.version.outputs.image_tag }}
|
||||
WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR: ${{ vars.WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DIR: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DIR }}
|
||||
WORKS_DRIVE_TARGET: sharedrive
|
||||
WORKS_DRIVE_SHARED_DRIVE_ID: ${{ vars.WORKS_DRIVE_SHARED_DRIVE_ID }}
|
||||
WORKS_DRIVE_PARENT_FILE_ID: ${{ vars.WORKS_DRIVE_PARENT_FILE_ID }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID }}
|
||||
WORKS_ADMIN_API_BASE_URL: ${{ vars.WORKS_ADMIN_API_BASE_URL }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
: "${WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR:=docker-build-image}"
|
||||
: "${WORKS_DRIVE_DOCKER_IMAGE_DIR:=baron-sso}"
|
||||
|
||||
required_values="
|
||||
IMAGE_TAG WORKS_DRIVE_SHARED_DRIVE_ID
|
||||
IMAGE_TAG WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID
|
||||
"
|
||||
for key in ${required_values}; do
|
||||
if [ -z "${!key:-}" ]; then
|
||||
@@ -236,6 +236,9 @@ jobs:
|
||||
for image in backend userfront adminfront devfront orgfront; do
|
||||
image_ref="baron_sso/${image}:${IMAGE_TAG}"
|
||||
DOCKER_IMAGE_REF="${image_ref}" \
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DIR="${WORKS_DRIVE_DOCKER_IMAGE_DIR}" \
|
||||
WORKS_DRIVE_SHARED_DRIVE_ID="${WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID}" \
|
||||
WORKS_DRIVE_PARENT_FILE_ID="${WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID:-}" \
|
||||
WORKS_DOCKER_IMAGE_ARCHIVE_DIR="${RUNNER_TEMP}/baron-sso-docker-image-upload" \
|
||||
scripts/docker-image/upload_works_drive.sh
|
||||
done
|
||||
|
||||
@@ -81,12 +81,11 @@ jobs:
|
||||
OATHKEEPER_GID: ${{ vars.PROD_OATHKEEPER_GID }}
|
||||
OATHKEEPER_INTROSPECT_CLIENT_ID: ${{ vars.PROD_OATHKEEPER_INTROSPECT_CLIENT_ID }}
|
||||
ADMIN_EMAIL: ${{ vars.PROD_ADMIN_EMAIL }}
|
||||
HARBOR_HOSTNAME: ${{ vars.HARBOR_HOSTNAME }}
|
||||
BACKEND_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/backend
|
||||
USERFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/userfront
|
||||
ADMINFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/adminfront
|
||||
DEVFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/devfront
|
||||
ORGFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/orgfront
|
||||
BACKEND_IMAGE_NAME: baron_sso/backend
|
||||
USERFRONT_IMAGE_NAME: baron_sso/userfront
|
||||
ADMINFRONT_IMAGE_NAME: baron_sso/adminfront
|
||||
DEVFRONT_IMAGE_NAME: baron_sso/devfront
|
||||
ORGFRONT_IMAGE_NAME: baron_sso/orgfront
|
||||
IMAGE_DEPLOY_DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
|
||||
IMAGE_DEPLOY_ORY_POSTGRES_PASSWORD: ${{ secrets.PROD_ORY_POSTGRES_PASSWORD }}
|
||||
IMAGE_DEPLOY_OATHKEEPER_INTROSPECT_CLIENT_SECRET: ${{ secrets.PROD_OATHKEEPER_INTROSPECT_CLIENT_SECRET }}
|
||||
@@ -107,9 +106,16 @@ jobs:
|
||||
DEPLOY_HOST: ${{ vars.PROD_HOST }}
|
||||
DEPLOY_USER: ${{ vars.PROD_USER }}
|
||||
DEPLOY_PATH: ${{ vars.PROD_DEPLOY_PATH }}
|
||||
HARBOR_ENDPOINT: ${{ vars.HARBOR_ENDPOINT }}
|
||||
HARBOR_ROBOT_ACCOUNT: ${{ vars.HARBOR_ROBOT_ACCOUNT }}
|
||||
HARBOR_ROBOT_KEY: ${{ secrets.HARBOR_ROBOT_KEY }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DIR: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DIR || 'baron-sso' }}
|
||||
WORKS_ADMIN_API_BASE_URL: ${{ vars.WORKS_ADMIN_API_BASE_URL }}
|
||||
WORKS_DRIVE_ACCESS_TOKEN_INPUT: ${{ secrets.WORKS_DRIVE_ACCESS_TOKEN }}
|
||||
WORKS_DRIVE_ACCESS_TOKEN_FILE: ${{ vars.WORKS_DRIVE_ACCESS_TOKEN_FILE }}
|
||||
WORKS_DRIVE_ACCESS_TOKEN_CMD: ${{ vars.WORKS_DRIVE_ACCESS_TOKEN_CMD }}
|
||||
WORKS_DRIVE_OAUTH_CLIENT_ID: ${{ secrets.WORKS_DRIVE_OAUTH_CLIENT_ID }}
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SECRET: ${{ secrets.WORKS_OAUTH_CLIENT_SECRET }}
|
||||
WORKS_DRIVE_OAUTH_REFRESH_TOKEN: ${{ secrets.WORKS_DRIVE_REFRESH_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
scripts/deploy/upload_and_run_image_deploy.sh
|
||||
|
||||
@@ -81,12 +81,11 @@ jobs:
|
||||
OATHKEEPER_GID: ${{ vars.STG_OATHKEEPER_GID }}
|
||||
OATHKEEPER_INTROSPECT_CLIENT_ID: ${{ vars.STG_OATHKEEPER_INTROSPECT_CLIENT_ID }}
|
||||
ADMIN_EMAIL: ${{ vars.STG_ADMIN_EMAIL }}
|
||||
HARBOR_HOSTNAME: ${{ vars.HARBOR_HOSTNAME }}
|
||||
BACKEND_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/backend
|
||||
USERFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/userfront
|
||||
ADMINFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/adminfront
|
||||
DEVFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/devfront
|
||||
ORGFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/orgfront
|
||||
BACKEND_IMAGE_NAME: baron_sso/backend
|
||||
USERFRONT_IMAGE_NAME: baron_sso/userfront
|
||||
ADMINFRONT_IMAGE_NAME: baron_sso/adminfront
|
||||
DEVFRONT_IMAGE_NAME: baron_sso/devfront
|
||||
ORGFRONT_IMAGE_NAME: baron_sso/orgfront
|
||||
IMAGE_DEPLOY_DB_PASSWORD: ${{ secrets.STG_DB_PASSWORD }}
|
||||
IMAGE_DEPLOY_ORY_POSTGRES_PASSWORD: ${{ secrets.STG_ORY_POSTGRES_PASSWORD }}
|
||||
IMAGE_DEPLOY_OATHKEEPER_INTROSPECT_CLIENT_SECRET: ${{ secrets.STG_OATHKEEPER_INTROSPECT_CLIENT_SECRET }}
|
||||
@@ -105,9 +104,16 @@ jobs:
|
||||
DEPLOY_HOST: ${{ vars.STG_HOST }}
|
||||
DEPLOY_USER: ${{ vars.STG_USER }}
|
||||
DEPLOY_PATH: ${{ vars.STG_DEPLOY_PATH }}
|
||||
HARBOR_ENDPOINT: ${{ vars.HARBOR_ENDPOINT }}
|
||||
HARBOR_ROBOT_ACCOUNT: ${{ vars.HARBOR_ROBOT_ACCOUNT }}
|
||||
HARBOR_ROBOT_KEY: ${{ secrets.HARBOR_ROBOT_KEY }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID }}
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DIR: ${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DIR || 'baron-sso' }}
|
||||
WORKS_ADMIN_API_BASE_URL: ${{ vars.WORKS_ADMIN_API_BASE_URL }}
|
||||
WORKS_DRIVE_ACCESS_TOKEN_INPUT: ${{ secrets.WORKS_DRIVE_ACCESS_TOKEN }}
|
||||
WORKS_DRIVE_ACCESS_TOKEN_FILE: ${{ vars.WORKS_DRIVE_ACCESS_TOKEN_FILE }}
|
||||
WORKS_DRIVE_ACCESS_TOKEN_CMD: ${{ vars.WORKS_DRIVE_ACCESS_TOKEN_CMD }}
|
||||
WORKS_DRIVE_OAUTH_CLIENT_ID: ${{ secrets.WORKS_DRIVE_OAUTH_CLIENT_ID }}
|
||||
WORKS_DRIVE_OAUTH_CLIENT_SECRET: ${{ secrets.WORKS_OAUTH_CLIENT_SECRET }}
|
||||
WORKS_DRIVE_OAUTH_REFRESH_TOKEN: ${{ secrets.WORKS_DRIVE_REFRESH_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
scripts/deploy/upload_and_run_image_deploy.sh
|
||||
|
||||
@@ -18,9 +18,9 @@ Gitea Actions의 shared image publish workflow는 `baron_sso/<service>:<image_ta
|
||||
|
||||
업로드를 실행하려면 최소한 다음 값을 등록해야 한다.
|
||||
|
||||
- variable `WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR=docker-build-image`
|
||||
- variable `WORKS_DRIVE_SHARED_DRIVE_ID`
|
||||
- 선택 variable `WORKS_DRIVE_PARENT_FILE_ID`
|
||||
- 선택 variable `WORKS_DRIVE_DOCKER_IMAGE_DIR=baron-sso`
|
||||
- variable `WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID`
|
||||
- 선택 variable `WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID`
|
||||
- secret `WORKS_DRIVE_ACCESS_TOKEN`, 또는 variable `WORKS_DRIVE_ACCESS_TOKEN_FILE`, 또는 variable `WORKS_DRIVE_ACCESS_TOKEN_CMD`, 또는 refresh-token 방식의 secret `WORKS_DRIVE_REFRESH_TOKEN`
|
||||
- refresh-token 방식을 쓸 경우 secret `WORKS_DRIVE_OAUTH_CLIENT_ID`, secret `WORKS_OAUTH_CLIENT_SECRET`
|
||||
|
||||
@@ -49,32 +49,32 @@ Refresh Token Rotation이 켜져 있으면 WORKS가 refresh 응답에 새 Refres
|
||||
기본 최상위 디렉터리는 다음 환경 변수로 지정한다.
|
||||
|
||||
```dotenv
|
||||
WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR=docker-build-image
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DIR=baron-sso
|
||||
```
|
||||
|
||||
이미지는 WORKS Shared Drive에서 다음 구조로 저장한다.
|
||||
|
||||
```text
|
||||
docker-build-image/<repository-path>/<tag>/
|
||||
image.tar.zst
|
||||
image.tar.zst.sha256
|
||||
manifest.json
|
||||
baron-sso/<tag>/
|
||||
<service>.<tag>.tar.zst
|
||||
<service>.<tag>.sha256
|
||||
manifest.<tag>.json
|
||||
```
|
||||
|
||||
예시:
|
||||
|
||||
```text
|
||||
docker-build-image/baron_sso/backend/v1.2606.ab12/
|
||||
image.tar.zst
|
||||
image.tar.zst.sha256
|
||||
manifest.json
|
||||
baron-sso/v1.2606.ab12/
|
||||
backend.v1.2606.ab12.tar.zst
|
||||
backend.v1.2606.ab12.sha256
|
||||
manifest.v1.2606.ab12.json
|
||||
```
|
||||
|
||||
Registry hostname은 저장 경로에서 제외한다. 예를 들어 `registry.example/baron_sso/backend:v1.2606.ab12`는 `baron_sso/backend/v1.2606.ab12` 아래에 저장한다.
|
||||
Registry hostname과 image namespace는 저장 경로에서 제외한다. 예를 들어 `registry.example/baron_sso/backend:v1.2606.ab12`는 `baron-sso/v1.2606.ab12/backend.v1.2606.ab12.tar.zst`로 저장한다.
|
||||
|
||||
## Manifest
|
||||
|
||||
`manifest.json`에는 다음 정보를 기록한다.
|
||||
`manifest.<tag>.json`에는 다음 정보를 기록한다.
|
||||
|
||||
- archive format: `docker-save-zstd`
|
||||
- 원본 `image_ref`
|
||||
@@ -83,14 +83,15 @@ Registry hostname은 저장 경로에서 제외한다. 예를 들어 `registry.e
|
||||
- Docker image id
|
||||
- Git commit
|
||||
- archive 파일명, 크기, sha256
|
||||
- 서비스별 archive 정보 (`images.<service>`)
|
||||
- WORKS Drive remote path
|
||||
- 복원 명령 예시
|
||||
|
||||
복원은 다음 흐름으로 처리한다.
|
||||
|
||||
```bash
|
||||
sha256sum -c image.tar.zst.sha256
|
||||
zstd -d -c image.tar.zst | docker load
|
||||
sha256sum -c backend.v1.2606.ab12.sha256
|
||||
zstd -d -c backend.v1.2606.ab12.tar.zst | docker load
|
||||
```
|
||||
|
||||
## 업로드 CLI
|
||||
@@ -98,7 +99,7 @@ zstd -d -c image.tar.zst | docker load
|
||||
로컬 컨테이너를 먼저 이미지로 commit한 뒤 업로드하려면 다음처럼 실행한다.
|
||||
|
||||
```bash
|
||||
WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR=docker-build-image \
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DIR=baron-sso \
|
||||
WORKS_DOCKER_COMMIT_CONTAINER=baron_backend \
|
||||
DOCKER_IMAGE_REF=registry.example/baron_sso/backend:v1.2606.ab12 \
|
||||
scripts/docker-image/upload_works_drive.sh
|
||||
@@ -111,11 +112,11 @@ DOCKER_IMAGE_REF=registry.example/baron_sso/backend:v1.2606.ab12 \
|
||||
scripts/docker-image/upload_works_drive.sh
|
||||
```
|
||||
|
||||
실제 업로드에는 기존 백업 업로드와 같은 WORKS Drive 인증 변수를 사용한다.
|
||||
실제 업로드에는 기존 백업 업로드와 같은 WORKS Drive 인증 변수를 사용하되, Docker image archive 대상 drive/folder는 백업 변수와 분리한다.
|
||||
|
||||
- `WORKS_DRIVE_TARGET=sharedrive`
|
||||
- `WORKS_DRIVE_SHARED_DRIVE_ID` 또는 `WORKS_SHAREDRIVE_ID`
|
||||
- 선택: `WORKS_DRIVE_PARENT_FILE_ID`
|
||||
- `WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID`
|
||||
- 선택: `WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID`
|
||||
- `WORKS_DRIVE_ACCESS_TOKEN`, `WORKS_DRIVE_ACCESS_TOKEN_FILE`, `WORKS_DRIVE_ACCESS_TOKEN_CMD`, `WORKS_DRIVE_OAUTH_REFRESH_TOKEN`, 또는 서비스 계정 OAuth 변수
|
||||
|
||||
업로드 전 packaging만 확인하려면 다음을 사용한다.
|
||||
@@ -133,9 +134,9 @@ dry-run도 실제 `docker save`, `zstd`, checksum, manifest 생성을 수행한
|
||||
WORKS Drive에서 다음 세 파일을 같은 로컬 디렉터리로 내려받은 뒤 검증한다.
|
||||
|
||||
```text
|
||||
image.tar.zst
|
||||
image.tar.zst.sha256
|
||||
manifest.json
|
||||
backend.v1.2606.ab12.tar.zst
|
||||
backend.v1.2606.ab12.sha256
|
||||
manifest.v1.2606.ab12.json
|
||||
```
|
||||
|
||||
checksum, manifest, zstd stream 무결성만 확인하려면 다음을 실행한다.
|
||||
@@ -160,13 +161,20 @@ scripts/docker-image/verify_archive.sh /path/to/downloaded/archive
|
||||
|
||||
검증은 다음 조건을 모두 확인한다.
|
||||
|
||||
- `image.tar.zst.sha256` checksum 성공
|
||||
- `manifest.json`의 `schema_version=1`, `format=docker-save-zstd`
|
||||
- `<service>.<tag>.sha256` checksum 성공
|
||||
- `manifest.<tag>.json`의 `schema_version=1`, `format=docker-save-zstd`
|
||||
- manifest의 archive 파일명, sha256, size와 실제 파일 일치
|
||||
- `zstd -t` 무결성 성공
|
||||
- 선택적으로 `docker load` 성공
|
||||
|
||||
현재 repo에는 WORKS Drive API에서 파일을 자동 다운로드하는 CLI는 없다. 따라서 자동 다운로드 스크립트를 만들기 전까지는 WORKS Drive UI 또는 운영자가 승인한 API 도구로 세 파일을 내려받고, 위 검증 CLI로 복원 가능성을 확인한다.
|
||||
현재 repo의 `scripts/docker-image/download_works_drive.sh`는 `WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID`와 `IMAGE_TAG`를 사용해 `baron-sso/<tag>/`의 archive, checksum, manifest를 내려받고 checksum/manifest 검증 후 `docker load`를 수행한다.
|
||||
|
||||
```bash
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID=@2001000000547281 \
|
||||
WORKS_DRIVE_ACCESS_TOKEN=<access-token> \
|
||||
IMAGE_TAG=v1.2606.ab12 \
|
||||
scripts/docker-image/download_works_drive.sh
|
||||
```
|
||||
|
||||
API로 다운로드할 때는 대상 archive 폴더의 children을 조회해 각 파일의 `fileId`를 얻은 뒤 다음 endpoint를 호출한다.
|
||||
|
||||
@@ -179,7 +187,7 @@ GET /v1.0/sharedrives/<sharedDriveId>/files/<fileId>/download
|
||||
```bash
|
||||
curl -sS -L --location-trusted \
|
||||
-H "Authorization: Bearer <access-token>" \
|
||||
-o image.tar.zst \
|
||||
-o backend.v1.2606.ab12.tar.zst \
|
||||
"https://www.worksapis.com/v1.0/sharedrives/<sharedDriveId>/files/<fileId>/download"
|
||||
```
|
||||
|
||||
@@ -224,5 +232,5 @@ WORKS Drive archive는 Action에서 로컬로 빌드된 이미지를 `docker sav
|
||||
|
||||
- 이 구조는 `docker push`/`docker pull`과 호환되는 Registry backend가 아니다.
|
||||
- layer deduplication이 없으므로 같은 기반 이미지가 반복 저장된다.
|
||||
- 배포 전에는 반드시 `image.tar.zst.sha256` 검증 후 `docker load`를 수행해야 한다.
|
||||
- 배포 전에는 반드시 `<service>.<tag>.sha256` 검증 후 `docker load`를 수행해야 한다.
|
||||
- tag 없는 image ref와 digest-only image ref는 지원하지 않는다.
|
||||
|
||||
@@ -29,7 +29,6 @@ require_env ADMINFRONT_URL
|
||||
require_env DEVFRONT_URL
|
||||
require_env ORGFRONT_URL
|
||||
require_env VITE_OIDC_AUTHORITY
|
||||
require_env HARBOR_HOSTNAME
|
||||
|
||||
if ! printf '%s' "$IMAGE_TAG" | grep -Eq '^v[0-9]+\.[0-9]{4}\.[0-9a-f]{4}$'; then
|
||||
die "IMAGE_TAG must look like vX.YYMM.ab12 (got: $IMAGE_TAG)"
|
||||
@@ -59,6 +58,10 @@ compose_template="${IMAGE_DEPLOY_COMPOSE_TEMPLATE:-$repo_root/deploy/templates/d
|
||||
rm -rf "$bundle_dir"
|
||||
TARGET_DIR="$bundle_dir" bash "$repo_root/deploy/create-instance.sh" "$instance_name" "$port_prefix"
|
||||
cp "$compose_template" "$bundle_dir/docker-compose.yml"
|
||||
mkdir -p "$bundle_dir/scripts/docker-image" "$bundle_dir/scripts/backup/lib"
|
||||
cp "$repo_root/scripts/docker-image/download_works_drive.sh" "$bundle_dir/scripts/docker-image/download_works_drive.sh"
|
||||
cp "$repo_root/scripts/backup/lib/common.sh" "$bundle_dir/scripts/backup/lib/common.sh"
|
||||
chmod +x "$bundle_dir/scripts/docker-image/download_works_drive.sh"
|
||||
|
||||
sed "s/{{BACKEND_PORT}}/${IMAGE_DEPLOY_BACKEND_PORT}/g" \
|
||||
"$repo_root/deploy/templates/gateway/nginx.conf" >"$bundle_dir/gateway/nginx.conf"
|
||||
|
||||
@@ -15,25 +15,78 @@ require_env IMAGE_DEPLOY_BUNDLE_FILE
|
||||
require_env DEPLOY_HOST
|
||||
require_env DEPLOY_USER
|
||||
require_env DEPLOY_PATH
|
||||
require_env HARBOR_ENDPOINT
|
||||
require_env HARBOR_ROBOT_ACCOUNT
|
||||
require_env HARBOR_ROBOT_KEY
|
||||
require_env WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID
|
||||
|
||||
[[ -f "$IMAGE_DEPLOY_BUNDLE_FILE" ]] || die "bundle file not found: $IMAGE_DEPLOY_BUNDLE_FILE"
|
||||
|
||||
resolve_works_drive_access_token() {
|
||||
if [[ -n "${WORKS_DRIVE_ACCESS_TOKEN:-}" ]]; then
|
||||
printf '%s\n' "$WORKS_DRIVE_ACCESS_TOKEN"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "${WORKS_DRIVE_ACCESS_TOKEN_INPUT:-}" ]]; then
|
||||
printf '%s\n' "$WORKS_DRIVE_ACCESS_TOKEN_INPUT"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "${WORKS_DRIVE_ACCESS_TOKEN_FILE:-}" ]]; then
|
||||
[[ -f "$WORKS_DRIVE_ACCESS_TOKEN_FILE" ]] || die "WORKS_DRIVE_ACCESS_TOKEN_FILE not found: $WORKS_DRIVE_ACCESS_TOKEN_FILE"
|
||||
sed -n '1p' "$WORKS_DRIVE_ACCESS_TOKEN_FILE"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "${WORKS_DRIVE_ACCESS_TOKEN_CMD:-}" ]]; then
|
||||
sh -c "$WORKS_DRIVE_ACCESS_TOKEN_CMD"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "${WORKS_DRIVE_OAUTH_REFRESH_TOKEN:-}" ]]; then
|
||||
[[ -n "${WORKS_DRIVE_OAUTH_CLIENT_ID:-}" ]] || die "WORKS_DRIVE_OAUTH_CLIENT_ID is required when using WORKS_DRIVE_OAUTH_REFRESH_TOKEN."
|
||||
[[ -n "${WORKS_DRIVE_OAUTH_CLIENT_SECRET:-}" ]] || die "WORKS_DRIVE_OAUTH_CLIENT_SECRET is required when using WORKS_DRIVE_OAUTH_REFRESH_TOKEN."
|
||||
|
||||
local token_url="${WORKS_DRIVE_OAUTH_TOKEN_URL:-https://auth.worksmobile.com/oauth2/v2.0/token}"
|
||||
local response
|
||||
local access_token
|
||||
local rotated_refresh_token
|
||||
|
||||
response="$(curl -fsS -X POST "$token_url" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
--data-urlencode "grant_type=refresh_token" \
|
||||
--data-urlencode "refresh_token=${WORKS_DRIVE_OAUTH_REFRESH_TOKEN}" \
|
||||
--data-urlencode "client_id=${WORKS_DRIVE_OAUTH_CLIENT_ID}" \
|
||||
--data-urlencode "client_secret=${WORKS_DRIVE_OAUTH_CLIENT_SECRET}")"
|
||||
access_token="$(jq -er '.access_token' <<<"$response")"
|
||||
rotated_refresh_token="$(jq -r '.refresh_token // empty' <<<"$response")"
|
||||
if [[ -n "$rotated_refresh_token" && "$rotated_refresh_token" != "$WORKS_DRIVE_OAUTH_REFRESH_TOKEN" ]]; then
|
||||
printf 'WARNING: WORKS returned a rotated refresh token. Update WORKS_DRIVE_REFRESH_TOKEN before the old token ages out.\n' >&2
|
||||
fi
|
||||
printf '%s\n' "$access_token"
|
||||
return
|
||||
fi
|
||||
|
||||
die "Missing WORKS Drive access auth. Provide WORKS_DRIVE_ACCESS_TOKEN, WORKS_DRIVE_ACCESS_TOKEN_FILE, WORKS_DRIVE_ACCESS_TOKEN_CMD, or WORKS_DRIVE_OAUTH_REFRESH_TOKEN."
|
||||
}
|
||||
|
||||
remote_bundle="/tmp/baron-sso-image-deploy-$(date -u '+%Y%m%d%H%M%S').tgz"
|
||||
works_drive_access_token="$(resolve_works_drive_access_token)"
|
||||
|
||||
ssh-keyscan -H "$DEPLOY_HOST" >>~/.ssh/known_hosts
|
||||
scp "$IMAGE_DEPLOY_BUNDLE_FILE" "${DEPLOY_USER}@${DEPLOY_HOST}:${remote_bundle}"
|
||||
|
||||
echo "$HARBOR_ROBOT_KEY" | ssh "${DEPLOY_USER}@${DEPLOY_HOST}" \
|
||||
printf '%s\n' "$works_drive_access_token" | ssh "${DEPLOY_USER}@${DEPLOY_HOST}" \
|
||||
"set -euo pipefail; \
|
||||
read -r works_drive_access_token; \
|
||||
mkdir -p '${DEPLOY_PATH}'; \
|
||||
tar -xzf '${remote_bundle}' -C '${DEPLOY_PATH}'; \
|
||||
cd '${DEPLOY_PATH}'; \
|
||||
chmod 600 .env; \
|
||||
docker network inspect traefik-public >/dev/null 2>&1 || docker network create traefik-public; \
|
||||
docker login '${HARBOR_ENDPOINT}' -u '${HARBOR_ROBOT_ACCOUNT}' --password-stdin; \
|
||||
docker compose --env-file .env -f docker-compose.yml pull; \
|
||||
export WORKS_DRIVE_ACCESS_TOKEN=\"\${works_drive_access_token}\"; \
|
||||
export WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID='${WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID}'; \
|
||||
export WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID='${WORKS_DRIVE_DOCKER_IMAGE_PARENT_FILE_ID:-}'; \
|
||||
export WORKS_DRIVE_DOCKER_IMAGE_DIR='${WORKS_DRIVE_DOCKER_IMAGE_DIR:-baron-sso}'; \
|
||||
export WORKS_ADMIN_API_BASE_URL='${WORKS_ADMIN_API_BASE_URL:-https://www.worksapis.com}'; \
|
||||
scripts/docker-image/download_works_drive.sh; \
|
||||
docker compose --env-file .env -f docker-compose.yml up -d --remove-orphans; \
|
||||
docker compose --env-file .env -f docker-compose.yml ps"
|
||||
|
||||
185
scripts/docker-image/download_works_drive.sh
Executable file
185
scripts/docker-image/download_works_drive.sh
Executable file
@@ -0,0 +1,185 @@
|
||||
#!/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}"
|
||||
@@ -68,7 +68,7 @@ image_ref="${DOCKER_IMAGE_REF:-${IMAGE_REF:-}}"
|
||||
|
||||
commit_container="${WORKS_DOCKER_COMMIT_CONTAINER:-${DOCKER_COMMIT_CONTAINER:-}}"
|
||||
archive_root="${WORKS_DOCKER_IMAGE_ARCHIVE_DIR:-/tmp/baron-sso-docker-image-upload}"
|
||||
image_root_dir="${WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR:-docker-build-image}"
|
||||
image_root_dir="${WORKS_DRIVE_DOCKER_IMAGE_DIR:-${WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR:-baron-sso}}"
|
||||
dry_run="${WORKS_DRIVE_DRY_RUN:-false}"
|
||||
target="${WORKS_DRIVE_TARGET:-sharedrive}"
|
||||
api_base_url="${WORKS_ADMIN_API_BASE_URL:-https://www.worksapis.com}"
|
||||
@@ -504,14 +504,16 @@ derive_repository_and_tag() {
|
||||
|
||||
derive_repository_and_tag "$image_ref"
|
||||
|
||||
remote_path="${image_root_dir}/${image_repository}/${image_tag}"
|
||||
artifact_dir="${archive_root}/${image_repository}/${image_tag}"
|
||||
image_name="${image_repository##*/}"
|
||||
release_repository="${image_root_dir}"
|
||||
remote_path="${release_repository}/${image_tag}"
|
||||
artifact_dir="${archive_root}/${release_repository}/${image_tag}"
|
||||
mkdir -p "$artifact_dir"
|
||||
|
||||
tar_file="$artifact_dir/image.tar"
|
||||
archive_file="$artifact_dir/image.tar.zst"
|
||||
checksum_file="$artifact_dir/image.tar.zst.sha256"
|
||||
manifest_file="$artifact_dir/manifest.json"
|
||||
tar_file="$artifact_dir/${image_name}.${image_tag}.tar"
|
||||
archive_file="$artifact_dir/${image_name}.${image_tag}.tar.zst"
|
||||
checksum_file="$artifact_dir/${image_name}.${image_tag}.sha256"
|
||||
manifest_file="$artifact_dir/manifest.${image_tag}.json"
|
||||
upload_report_file="$artifact_dir/works-upload.json"
|
||||
|
||||
rm -f "$tar_file" "$archive_file" "$checksum_file" "$manifest_file" "$upload_report_file"
|
||||
@@ -535,36 +537,77 @@ image_id="$(docker image inspect "$image_ref" --format '{{.Id}}' 2>/dev/null ||
|
||||
archive_size="$(stat -c '%s' "$archive_file")"
|
||||
git_commit="$(backup_git_commit "$repo_root")"
|
||||
|
||||
jq -n \
|
||||
--arg createdAt "$(backup_utc_now)" \
|
||||
--arg imageRef "$image_ref" \
|
||||
--arg repository "$image_repository" \
|
||||
--arg tag "$image_tag" \
|
||||
--arg sourceContainer "$commit_container" \
|
||||
--arg imageId "$image_id" \
|
||||
--arg gitCommit "$git_commit" \
|
||||
--arg remotePath "$remote_path" \
|
||||
--arg archiveFile "$(basename "$archive_file")" \
|
||||
--arg archiveSha256 "$archive_sha256" \
|
||||
--argjson archiveSize "$archive_size" \
|
||||
'{
|
||||
schema_version: 1,
|
||||
format: "docker-save-zstd",
|
||||
created_at: $createdAt,
|
||||
manifest_tmp_file="${manifest_file}.tmp"
|
||||
manifest_jq_filter='
|
||||
def image_entry: {
|
||||
image_ref: $imageRef,
|
||||
repository: $repository,
|
||||
tag: $tag,
|
||||
image_name: $imageName,
|
||||
source_container: $sourceContainer,
|
||||
docker_image_id: $imageId,
|
||||
git_commit: $gitCommit,
|
||||
remote_path: $remotePath,
|
||||
restore_command: ("zstd -d -c " + $archiveFile + " | docker load"),
|
||||
archive: {
|
||||
file_name: $archiveFile,
|
||||
size_bytes: $archiveSize,
|
||||
sha256: $archiveSha256
|
||||
},
|
||||
restore_command: ("zstd -d -c " + $archiveFile + " | docker load")
|
||||
};
|
||||
.schema_version = 1
|
||||
| .format = "docker-save-zstd"
|
||||
| .created_at = (.created_at // $createdAt)
|
||||
| .updated_at = $createdAt
|
||||
| .image_ref = $imageRef
|
||||
| .repository = $repository
|
||||
| .release_repository = $releaseRepository
|
||||
| .image_name = $imageName
|
||||
| .tag = $tag
|
||||
| .source_container = $sourceContainer
|
||||
| .docker_image_id = $imageId
|
||||
| .git_commit = $gitCommit
|
||||
| .remote_path = $remotePath
|
||||
| .restore_command = ("zstd -d -c " + $archiveFile + " | docker load")
|
||||
| .archive = {
|
||||
file_name: $archiveFile,
|
||||
size_bytes: $archiveSize,
|
||||
sha256: $archiveSha256
|
||||
}
|
||||
}' >"$manifest_file"
|
||||
| .images = ((.images // {}) + {($imageName): image_entry})
|
||||
'
|
||||
|
||||
if [[ -f "$manifest_file" ]]; then
|
||||
jq \
|
||||
--arg createdAt "$(backup_utc_now)" \
|
||||
--arg imageRef "$image_ref" \
|
||||
--arg repository "$image_repository" \
|
||||
--arg releaseRepository "$release_repository" \
|
||||
--arg imageName "$image_name" \
|
||||
--arg tag "$image_tag" \
|
||||
--arg sourceContainer "$commit_container" \
|
||||
--arg imageId "$image_id" \
|
||||
--arg gitCommit "$git_commit" \
|
||||
--arg remotePath "$remote_path" \
|
||||
--arg archiveFile "$(basename "$archive_file")" \
|
||||
--arg archiveSha256 "$archive_sha256" \
|
||||
--argjson archiveSize "$archive_size" \
|
||||
"$manifest_jq_filter" "$manifest_file" >"$manifest_tmp_file"
|
||||
else
|
||||
jq -n \
|
||||
--arg createdAt "$(backup_utc_now)" \
|
||||
--arg imageRef "$image_ref" \
|
||||
--arg repository "$image_repository" \
|
||||
--arg releaseRepository "$release_repository" \
|
||||
--arg imageName "$image_name" \
|
||||
--arg tag "$image_tag" \
|
||||
--arg sourceContainer "$commit_container" \
|
||||
--arg imageId "$image_id" \
|
||||
--arg gitCommit "$git_commit" \
|
||||
--arg remotePath "$remote_path" \
|
||||
--arg archiveFile "$(basename "$archive_file")" \
|
||||
--arg archiveSha256 "$archive_sha256" \
|
||||
--argjson archiveSize "$archive_size" \
|
||||
"$manifest_jq_filter" >"$manifest_tmp_file"
|
||||
fi
|
||||
mv "$manifest_tmp_file" "$manifest_file"
|
||||
|
||||
upload_files=("$archive_file" "$checksum_file" "$manifest_file")
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@ backup_require_command stat
|
||||
backup_require_command zstd
|
||||
|
||||
manifest_file="$archive_dir/manifest.json"
|
||||
mapfile -t versioned_manifest_files < <(find "$archive_dir" -maxdepth 1 -type f -name 'manifest.*.json' | sort)
|
||||
if [[ "${#versioned_manifest_files[@]}" -gt 0 ]]; then
|
||||
[[ "${#versioned_manifest_files[@]}" -eq 1 ]] || backup_die "archive directory must contain exactly one manifest.*.json file."
|
||||
manifest_file="${versioned_manifest_files[0]}"
|
||||
fi
|
||||
backup_require_path "$manifest_file"
|
||||
|
||||
schema_version="$(jq -er '.schema_version' "$manifest_file")"
|
||||
@@ -33,6 +38,9 @@ manifest_size="$(jq -er '.archive.size_bytes' "$manifest_file")"
|
||||
|
||||
archive_file="$archive_dir/$archive_name"
|
||||
checksum_file="$archive_dir/${archive_name}.sha256"
|
||||
if [[ ! -f "$checksum_file" && "$archive_name" == *.tar.zst ]]; then
|
||||
checksum_file="$archive_dir/${archive_name%.tar.zst}.sha256"
|
||||
fi
|
||||
|
||||
backup_require_path "$archive_file"
|
||||
backup_require_path "$checksum_file"
|
||||
|
||||
@@ -28,19 +28,19 @@ require_command jq
|
||||
require_command sha256sum
|
||||
require_command zstd
|
||||
|
||||
artifact_dir="$tmp_root/baron_sso/backend/v1.2606.ab12"
|
||||
artifact_dir="$tmp_root/baron-sso/v1.2606.ab12"
|
||||
mkdir -p "$artifact_dir"
|
||||
|
||||
printf 'docker image archive smoke\n' >"$artifact_dir/image.tar"
|
||||
zstd -q -f -o "$artifact_dir/image.tar.zst" "$artifact_dir/image.tar"
|
||||
rm -f "$artifact_dir/image.tar"
|
||||
printf 'docker image archive smoke\n' >"$artifact_dir/backend.v1.2606.ab12.tar"
|
||||
zstd -q -f -o "$artifact_dir/backend.v1.2606.ab12.tar.zst" "$artifact_dir/backend.v1.2606.ab12.tar"
|
||||
rm -f "$artifact_dir/backend.v1.2606.ab12.tar"
|
||||
|
||||
archive_sha256="$(sha256sum "$artifact_dir/image.tar.zst" | awk '{print $1}')"
|
||||
archive_size="$(wc -c <"$artifact_dir/image.tar.zst" | tr -d ' ')"
|
||||
printf '%s image.tar.zst\n' "$archive_sha256" >"$artifact_dir/image.tar.zst.sha256"
|
||||
archive_sha256="$(sha256sum "$artifact_dir/backend.v1.2606.ab12.tar.zst" | awk '{print $1}')"
|
||||
archive_size="$(wc -c <"$artifact_dir/backend.v1.2606.ab12.tar.zst" | tr -d ' ')"
|
||||
printf '%s backend.v1.2606.ab12.tar.zst\n' "$archive_sha256" >"$artifact_dir/backend.v1.2606.ab12.sha256"
|
||||
|
||||
jq -n \
|
||||
--arg remotePath "docker-build-image/baron_sso/backend/v1.2606.ab12" \
|
||||
--arg remotePath "baron-sso/v1.2606.ab12" \
|
||||
--arg archiveSha256 "$archive_sha256" \
|
||||
--argjson archiveSize "$archive_size" \
|
||||
'{
|
||||
@@ -48,33 +48,35 @@ jq -n \
|
||||
format: "docker-save-zstd",
|
||||
image_ref: "reg.hmac.kr/baron_sso/backend:v1.2606.ab12",
|
||||
repository: "baron_sso/backend",
|
||||
release_repository: "baron-sso",
|
||||
image_name: "backend",
|
||||
tag: "v1.2606.ab12",
|
||||
remote_path: $remotePath,
|
||||
archive: {
|
||||
file_name: "image.tar.zst",
|
||||
file_name: "backend.v1.2606.ab12.tar.zst",
|
||||
size_bytes: $archiveSize,
|
||||
sha256: $archiveSha256
|
||||
}
|
||||
}' >"$artifact_dir/manifest.json"
|
||||
}' >"$artifact_dir/manifest.v1.2606.ab12.json"
|
||||
|
||||
"$verify_script" "$artifact_dir" >/dev/null
|
||||
|
||||
bad_checksum_dir="$tmp_root/bad-checksum"
|
||||
cp -R "$artifact_dir" "$bad_checksum_dir"
|
||||
printf '0000000000000000000000000000000000000000000000000000000000000000 image.tar.zst\n' >"$bad_checksum_dir/image.tar.zst.sha256"
|
||||
printf '0000000000000000000000000000000000000000000000000000000000000000 backend.v1.2606.ab12.tar.zst\n' >"$bad_checksum_dir/backend.v1.2606.ab12.sha256"
|
||||
assert_fails "$verify_script" "$bad_checksum_dir"
|
||||
|
||||
bad_manifest_dir="$tmp_root/bad-manifest"
|
||||
cp -R "$artifact_dir" "$bad_manifest_dir"
|
||||
jq '.archive.sha256 = "1111111111111111111111111111111111111111111111111111111111111111"' \
|
||||
"$bad_manifest_dir/manifest.json" >"$bad_manifest_dir/manifest.json.tmp"
|
||||
mv "$bad_manifest_dir/manifest.json.tmp" "$bad_manifest_dir/manifest.json"
|
||||
"$bad_manifest_dir/manifest.v1.2606.ab12.json" >"$bad_manifest_dir/manifest.v1.2606.ab12.json.tmp"
|
||||
mv "$bad_manifest_dir/manifest.v1.2606.ab12.json.tmp" "$bad_manifest_dir/manifest.v1.2606.ab12.json"
|
||||
assert_fails "$verify_script" "$bad_manifest_dir"
|
||||
|
||||
bad_archive_dir="$tmp_root/bad-archive"
|
||||
cp -R "$artifact_dir" "$bad_archive_dir"
|
||||
printf 'not a zstd stream\n' >"$bad_archive_dir/image.tar.zst"
|
||||
sha256sum "$bad_archive_dir/image.tar.zst" | awk '{print $1 " image.tar.zst"}' >"$bad_archive_dir/image.tar.zst.sha256"
|
||||
printf 'not a zstd stream\n' >"$bad_archive_dir/backend.v1.2606.ab12.tar.zst"
|
||||
sha256sum "$bad_archive_dir/backend.v1.2606.ab12.tar.zst" | awk '{print $1 " backend.v1.2606.ab12.tar.zst"}' >"$bad_archive_dir/backend.v1.2606.ab12.sha256"
|
||||
assert_fails "$verify_script" "$bad_archive_dir"
|
||||
|
||||
echo "docker image archive verification checks passed"
|
||||
|
||||
@@ -9,6 +9,7 @@ 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"
|
||||
works_image_download_script="$repo_root/scripts/docker-image/download_works_drive.sh"
|
||||
|
||||
fail() {
|
||||
echo "$1" >&2
|
||||
@@ -22,6 +23,7 @@ fail() {
|
||||
[[ -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."
|
||||
[[ -f "$works_image_download_script" ]] || fail "shared WORKS Drive image download script must exist."
|
||||
|
||||
grep -Fq "name: Publish Baron SSO Images" "$publish_workflow" \
|
||||
|| fail "publish workflow must have the shared stage/production name."
|
||||
@@ -58,8 +60,10 @@ grep -Fq "WORKS_DRIVE_OAUTH_CLIENT_SECRET: \${{ secrets.WORKS_OAUTH_CLIENT_SECRE
|
||||
|| fail "publish workflow must use the Gitea-compatible WORKS OAuth client secret name."
|
||||
grep -Fq "WORKS_DRIVE_OAUTH_REFRESH_TOKEN: \${{ secrets.WORKS_DRIVE_REFRESH_TOKEN }}" "$publish_workflow" \
|
||||
|| fail "publish workflow must support WORKS Drive refresh-token auth."
|
||||
grep -Fq "WORKS_DRIVE_PARENT_FILE_ID" "$publish_workflow" \
|
||||
|| fail "publish workflow must target a WORKS Drive folder."
|
||||
grep -Fq "WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: \${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}" "$publish_workflow" \
|
||||
|| fail "publish workflow must use the Docker-image-specific WORKS Drive ID variable."
|
||||
grep -Fq 'WORKS_DRIVE_SHARED_DRIVE_ID="${WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID}"' "$publish_workflow" \
|
||||
|| fail "publish workflow must map WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID into the shared upload script."
|
||||
grep -Fq "Resolve WORKS Drive access token" "$publish_workflow" \
|
||||
|| fail "publish workflow must resolve a short-lived WORKS Drive access token before uploads."
|
||||
grep -Fq "WORKS_DRIVE_ACCESS_TOKEN_INPUT" "$publish_workflow" \
|
||||
@@ -90,6 +94,8 @@ 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 "WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: \${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}" "$staging_deploy_workflow" \
|
||||
|| fail "staging deploy workflow must pass the Docker-image-specific WORKS Drive ID variable."
|
||||
|
||||
grep -Fq "name: Deploy Baron SSO Production Images" "$deploy_workflow" \
|
||||
|| fail "deploy workflow must have the expected name."
|
||||
@@ -109,14 +115,23 @@ 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 "WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID: \${{ vars.WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID }}" "$deploy_workflow" \
|
||||
|| fail "production deploy workflow must pass the Docker-image-specific WORKS Drive ID variable."
|
||||
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 "scripts/docker-image/download_works_drive.sh" "$remote_deploy_script" \
|
||||
|| fail "shared remote deploy script must load requested image archives from WORKS Drive before running."
|
||||
grep -Fq "docker load" "$works_image_download_script" \
|
||||
|| fail "WORKS Drive image download script must load downloaded archives into Docker."
|
||||
grep -Fq 'baron-sso/${IMAGE_TAG}/${image}.${IMAGE_TAG}.tar.zst' "$works_image_download_script" \
|
||||
|| fail "WORKS Drive image download script must document the normalized archive path."
|
||||
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 -Eiq 'harbor|docker login|docker compose --env-file .env -f docker-compose.yml pull|HARBOR_' "$staging_deploy_workflow" "$deploy_workflow" "$remote_deploy_script"; then
|
||||
fail "image deploy workflows/scripts must not depend on Harbor registry login or compose pull."
|
||||
fi
|
||||
|
||||
if grep -Eq 'docker (build|commit)' "$staging_deploy_workflow" "$deploy_workflow"; then
|
||||
fail "staging/production deploy workflows must never build or commit images remotely."
|
||||
|
||||
146
test/works_drive_docker_image_download_policy_test.sh
Normal file
146
test/works_drive_docker_image_download_policy_test.sh
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
script="$repo_root/scripts/docker-image/download_works_drive.sh"
|
||||
tmp_root="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$tmp_root"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
fail() {
|
||||
echo "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ -x "$script" ]] || fail "download script must exist and be executable."
|
||||
grep -Fq 'baron-sso/${IMAGE_TAG}/${image}.${IMAGE_TAG}.tar.zst' "$script" \
|
||||
|| fail "download script must document the normalized archive path."
|
||||
grep -Fq -- '--location-trusted' "$script" \
|
||||
|| fail "download script must preserve Authorization across WORKS download redirects."
|
||||
|
||||
source_dir="$tmp_root/source"
|
||||
mkdir -p "$source_dir"
|
||||
printf 'backend image archive payload\n' >"$source_dir/backend.txt"
|
||||
tar -C "$source_dir" -cf "$tmp_root/backend.v1.2606.ab12.tar" backend.txt
|
||||
zstd -q -f -o "$tmp_root/backend.v1.2606.ab12.tar.zst" "$tmp_root/backend.v1.2606.ab12.tar"
|
||||
archive_sha256="$(sha256sum "$tmp_root/backend.v1.2606.ab12.tar.zst" | awk '{print $1}')"
|
||||
archive_size="$(wc -c <"$tmp_root/backend.v1.2606.ab12.tar.zst" | tr -d ' ')"
|
||||
printf '%s backend.v1.2606.ab12.tar.zst\n' "$archive_sha256" >"$tmp_root/backend.v1.2606.ab12.sha256"
|
||||
jq -n \
|
||||
--arg sha "$archive_sha256" \
|
||||
--argjson size "$archive_size" \
|
||||
'{
|
||||
schema_version: 1,
|
||||
format: "docker-save-zstd",
|
||||
image_ref: "baron_sso/backend:v1.2606.ab12",
|
||||
repository: "baron_sso/backend",
|
||||
release_repository: "baron-sso",
|
||||
image_name: "backend",
|
||||
tag: "v1.2606.ab12",
|
||||
remote_path: "baron-sso/v1.2606.ab12",
|
||||
archive: {
|
||||
file_name: "backend.v1.2606.ab12.tar.zst",
|
||||
sha256: $sha,
|
||||
size_bytes: $size
|
||||
},
|
||||
images: {
|
||||
backend: {
|
||||
archive: {
|
||||
file_name: "backend.v1.2606.ab12.tar.zst",
|
||||
sha256: $sha,
|
||||
size_bytes: $size
|
||||
}
|
||||
}
|
||||
}
|
||||
}' >"$tmp_root/manifest.v1.2606.ab12.json"
|
||||
|
||||
curl_log="$tmp_root/curl.log"
|
||||
docker_log="$tmp_root/docker.log"
|
||||
fake_curl="$tmp_root/curl"
|
||||
cat >"$fake_curl" <<'SH'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
printf '%s\n' "$*" >>"$FAKE_CURL_LOG"
|
||||
|
||||
output_file=""
|
||||
args=("$@")
|
||||
for ((i = 0; i < ${#args[@]}; i += 1)); do
|
||||
if [[ "${args[$i]}" == "-o" ]]; then
|
||||
output_file="${args[$((i + 1))]}"
|
||||
fi
|
||||
done
|
||||
url="${args[-1]}"
|
||||
|
||||
if [[ -n "$output_file" ]]; then
|
||||
case "$url" in
|
||||
*/files/backend-archive/download) cp "$FAKE_WORKS_SOURCE/backend.v1.2606.ab12.tar.zst" "$output_file" ;;
|
||||
*/files/backend-checksum/download) cp "$FAKE_WORKS_SOURCE/backend.v1.2606.ab12.sha256" "$output_file" ;;
|
||||
*/files/backend-manifest/download) cp "$FAKE_WORKS_SOURCE/manifest.v1.2606.ab12.json" "$output_file" ;;
|
||||
*) echo "unexpected download URL: $url" >&2; exit 2 ;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$url" in
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/children)
|
||||
printf '{"files":[{"fileId":"baron-sso-id","fileName":"baron-sso","fileType":"FOLDER"}]}\n200\n'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/baron-sso-id/children)
|
||||
printf '{"files":[{"fileId":"tag-id","fileName":"v1.2606.ab12","fileType":"FOLDER"}]}\n200\n'
|
||||
;;
|
||||
https://www.worksapis.com/v1.0/sharedrives/shared-drive-1/files/tag-id/children)
|
||||
printf '{"files":[{"fileId":"backend-archive","fileName":"backend.v1.2606.ab12.tar.zst","fileType":"FILE"},{"fileId":"backend-checksum","fileName":"backend.v1.2606.ab12.sha256","fileType":"FILE"},{"fileId":"backend-manifest","fileName":"manifest.v1.2606.ab12.json","fileType":"FILE"}]}\n200\n'
|
||||
;;
|
||||
*)
|
||||
echo "unexpected list URL: $url" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
SH
|
||||
chmod +x "$fake_curl"
|
||||
|
||||
fake_bin="$tmp_root/bin"
|
||||
mkdir -p "$fake_bin"
|
||||
cat >"$fake_bin/docker" <<'SH'
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ "$1" == "load" ]]; then
|
||||
bytes="$(wc -c | tr -d ' ')"
|
||||
printf 'docker load bytes=%s\n' "$bytes" >>"$FAKE_DOCKER_LOG"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "unexpected docker command: $*" >&2
|
||||
exit 2
|
||||
SH
|
||||
chmod +x "$fake_bin/docker"
|
||||
|
||||
PATH="$fake_bin:$PATH" \
|
||||
FAKE_CURL_LOG="$curl_log" \
|
||||
FAKE_DOCKER_LOG="$docker_log" \
|
||||
FAKE_WORKS_SOURCE="$tmp_root" \
|
||||
WORKS_DRIVE_CURL_BIN="$fake_curl" \
|
||||
WORKS_DRIVE_ACCESS_TOKEN="test-access-token" \
|
||||
WORKS_DRIVE_DOCKER_IMAGE_DRIVE_ID="shared-drive-1" \
|
||||
WORKS_DOCKER_IMAGE_NAMES="backend" \
|
||||
WORKS_DOCKER_IMAGE_DOWNLOAD_DIR="$tmp_root/downloaded" \
|
||||
IMAGE_TAG="v1.2606.ab12" \
|
||||
"$script" >/dev/null
|
||||
|
||||
grep -Fq "sharedrives/shared-drive-1/files/children" "$curl_log" \
|
||||
|| fail "download script must list the shared drive root."
|
||||
grep -Fq "sharedrives/shared-drive-1/files/baron-sso-id/children" "$curl_log" \
|
||||
|| fail "download script must resolve the baron-sso folder."
|
||||
grep -Fq "sharedrives/shared-drive-1/files/tag-id/children" "$curl_log" \
|
||||
|| fail "download script must resolve the image tag folder."
|
||||
grep -Fq "files/backend-archive/download" "$curl_log" \
|
||||
|| fail "download script must download the normalized backend archive."
|
||||
grep -Fq "docker load bytes=" "$docker_log" \
|
||||
|| fail "download script must load the downloaded archive into Docker."
|
||||
|
||||
echo "OK: WORKS Drive Docker image archive download flow resolves, verifies, and loads image artifacts"
|
||||
@@ -14,19 +14,19 @@ fail() {
|
||||
[[ -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 'WORKS_DRIVE_DOCKER_IMAGE_DIR:-${WORKS_SHAREDRIVE_DOCKER_IMAGE_DIR:-baron-sso}' "$script" \
|
||||
|| fail "script must default WORKS_DRIVE_DOCKER_IMAGE_DIR to baron-sso with legacy fallback."
|
||||
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" \
|
||||
grep -Fq 'manifest.${image_tag}.json' "$script" \
|
||||
|| fail "script must write a versioned manifest next to the image archive."
|
||||
grep -Fq '${image_name}.${image_tag}.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" \
|
||||
grep -Fq 'baron-sso/v1.2606.ab12/backend.v1.2606.ab12.tar.zst' "$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."
|
||||
@@ -124,24 +124,12 @@ case "$last_arg" in
|
||||
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"}'
|
||||
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)
|
||||
@@ -175,41 +163,41 @@ 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."
|
||||
artifact_dir="$archive_dir/baron-sso/v1.2606.ab12"
|
||||
[[ -f "$artifact_dir/backend.v1.2606.ab12.tar.zst" ]] || fail "script must create backend.v1.2606.ab12.tar.zst."
|
||||
[[ -f "$artifact_dir/backend.v1.2606.ab12.sha256" ]] || fail "script must create backend.v1.2606.ab12.sha256."
|
||||
[[ -f "$artifact_dir/manifest.v1.2606.ab12.json" ]] || fail "script must create manifest.v1.2606.ab12.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."
|
||||
and .release_repository == "baron-sso"
|
||||
and .image_name == "backend"
|
||||
and .tag == "v1.2606.ab12"
|
||||
and .remote_path == "baron-sso/v1.2606.ab12"
|
||||
and .archive.file_name == "backend.v1.2606.ab12.tar.zst"
|
||||
and (.archive.sha256 | type == "string")
|
||||
and .images.backend.archive.file_name == "backend.v1.2606.ab12.tar.zst"
|
||||
and .images.backend.archive.sha256 == .archive.sha256' \
|
||||
"$artifact_dir/manifest.v1.2606.ab12.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 the top-level archive folder when needed."
|
||||
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" \
|
||||
grep -Fq "backend.v1.2606.ab12.tar.zst" "$curl_log" \
|
||||
|| fail "script must upload the compressed image archive."
|
||||
grep -Fq "image.tar.zst.sha256" "$curl_log" \
|
||||
grep -Fq "backend.v1.2606.ab12.sha256" "$curl_log" \
|
||||
|| fail "script must upload the checksum file."
|
||||
grep -Fq "manifest.json" "$curl_log" \
|
||||
grep -Fq "manifest.v1.2606.ab12.json" "$curl_log" \
|
||||
|| fail "script must upload the manifest file."
|
||||
|
||||
report_file="$artifact_dir/works-upload.json"
|
||||
|
||||
Reference in New Issue
Block a user