#!/usr/bin/env bash # minio_local_smoke.sh — deterministic local MinIO fixture for `make test-minio` # (ARTIFACT-STORE-WP-0007 D7.2) # # Starts a throwaway MinIO container with generated one-run credentials, # creates a smoke bucket, runs the live compatibility tests against it, and # tears the container down. No external endpoint, no persistent credentials. # # Usage: # bash scripts/minio_local_smoke.sh # or: make test-minio-local # # Environment overrides: # MINIO_IMAGE (default: minio/minio:latest) # MINIO_PORT (default: 19000, bound to 127.0.0.1 only) set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" MINIO_IMAGE="${MINIO_IMAGE:-minio/minio:latest}" MINIO_PORT="${MINIO_PORT:-19000}" CONTAINER="artifactstore-minio-smoke-$$" BUCKET="artifactstore-smoke" ACCESS_KEY="smoke-$(openssl rand -hex 8)" SECRET_KEY="$(openssl rand -hex 24)" cleanup() { docker rm -f "$CONTAINER" >/dev/null 2>&1 || true; } trap cleanup EXIT echo "[minio-smoke] starting $MINIO_IMAGE on 127.0.0.1:$MINIO_PORT ..." docker run -d --name "$CONTAINER" \ -p "127.0.0.1:${MINIO_PORT}:9000" \ -e MINIO_ROOT_USER="$ACCESS_KEY" \ -e MINIO_ROOT_PASSWORD="$SECRET_KEY" \ "$MINIO_IMAGE" server /data >/dev/null for _ in $(seq 1 30); do if curl -sf "http://127.0.0.1:${MINIO_PORT}/minio/health/live" >/dev/null; then break fi sleep 1 done curl -sf "http://127.0.0.1:${MINIO_PORT}/minio/health/live" >/dev/null \ || { echo "[minio-smoke] ERROR: MinIO did not become healthy" >&2; exit 1; } echo "[minio-smoke] health/live OK" docker exec "$CONTAINER" sh -c \ 'mc alias set local http://127.0.0.1:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" >/dev/null && mc mb local/'"$BUCKET" >/dev/null echo "[minio-smoke] bucket $BUCKET created" cd "$REPO_ROOT" ARTIFACTSTORE_MINIO_ENDPOINT_URL="http://127.0.0.1:${MINIO_PORT}" \ ARTIFACTSTORE_MINIO_ACCESS_KEY="$ACCESS_KEY" \ ARTIFACTSTORE_MINIO_SECRET_KEY="$SECRET_KEY" \ ARTIFACTSTORE_MINIO_BUCKET="$BUCKET" \ make test-minio echo "[minio-smoke] static-credential compatibility PASS" # ── STS leg (D7.4): temporary credentials via MinIO AssumeRole ──────────────── # Root credentials cannot call AssumeRole, so mint a scoped user first. STS_USER="sts-$(openssl rand -hex 6)" STS_USER_SECRET="$(openssl rand -hex 24)" docker exec -e STS_USER="$STS_USER" -e STS_USER_SECRET="$STS_USER_SECRET" "$CONTAINER" sh -c \ 'mc admin user add local "$STS_USER" "$STS_USER_SECRET" >/dev/null && mc admin policy attach local readwrite --user "$STS_USER" >/dev/null' echo "[minio-smoke] scoped user created; requesting temporary credentials via STS AssumeRole" STS_JSON="$( STS_ENDPOINT="http://127.0.0.1:${MINIO_PORT}" \ STS_USER="$STS_USER" STS_USER_SECRET="$STS_USER_SECRET" \ uv run --all-extras python - <<'PY' import json import os import boto3 sts = boto3.client( "sts", endpoint_url=os.environ["STS_ENDPOINT"], aws_access_key_id=os.environ["STS_USER"], aws_secret_access_key=os.environ["STS_USER_SECRET"], region_name="us-east-1", ) creds = sts.assume_role( RoleArn="arn:minio:iam:::role/dummy", RoleSessionName="artifactstore-d74-smoke", DurationSeconds=900, )["Credentials"] print( json.dumps( { "AccessKeyId": creds["AccessKeyId"], "SecretAccessKey": creds["SecretAccessKey"], "SessionToken": creds["SessionToken"], } ) ) PY )" TEMP_ACCESS_KEY="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["AccessKeyId"])' "$STS_JSON")" TEMP_SECRET_KEY="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["SecretAccessKey"])' "$STS_JSON")" TEMP_SESSION_TOKEN="$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["SessionToken"])' "$STS_JSON")" ARTIFACTSTORE_MINIO_ENDPOINT_URL="http://127.0.0.1:${MINIO_PORT}" \ ARTIFACTSTORE_MINIO_ACCESS_KEY="$TEMP_ACCESS_KEY" \ ARTIFACTSTORE_MINIO_SECRET_KEY="$TEMP_SECRET_KEY" \ ARTIFACTSTORE_MINIO_SESSION_TOKEN="$TEMP_SESSION_TOKEN" \ ARTIFACTSTORE_MINIO_BUCKET="$BUCKET" \ make test-minio echo "[minio-smoke] temporary-credential (STS session token) compatibility PASS" echo "[minio-smoke] PASS — live MinIO static + STS round-trip/range/multipart compatibility verified"