Files
clean-architecture-backend-…/infra/minio/smoke/object-storage-smoke.sh
T

69 lines
3.2 KiB
Bash
Executable File

#!/bin/sh
# upload -> HEAD -> download -> delete -> wrong-credential rejection, in that order, none skippable.
#
# A readiness probe says the server answers. This says an object written to it comes back byte for
# byte and then stops existing when deleted, which is the property anything storing a file depends
# on. The wrong-credential step is here because a bucket that accepts anyone is also "working".
#
# It runs on the MinIO server image rather than the mc client image, and the reason is worth keeping:
# minio/mc ships mc and almost nothing else — no sed, no grep, no cmp — so steps 2 and 3 below called
# two binaries that are not there. The script had never run to find out. The wrapper's one-shot loop
# lost its stdin to `docker compose run` and executed only the first client per lane, so this one was
# skipped in every lane that declared it while all three lanes reported green.
#
# minio/minio carries mc, and also sha256sum, cut and tr. The lane already pulls it for the server,
# so this costs no image, and the digest comparison is a stronger identity check than cmp: it fails
# on any differing byte and says so without dumping the bytes.
set -eu
KEY="smoke/$(date +%s)-$$"
WORK="$(mktemp -d)"
trap 'rm -rf "${WORK}"' EXIT
mc alias set caskeleton "${MINIO_ENDPOINT}" "${MINIO_ROOT_USER}" "${MINIO_ROOT_PASSWORD}"
# 1. upload known bytes
head -c 65536 /dev/urandom > "${WORK}/payload"
EXPECTED_SIZE="$(wc -c < "${WORK}/payload" | tr -d ' ')"
mc cp "${WORK}/payload" "caskeleton/${MINIO_BUCKET}/${KEY}"
# 2. HEAD: size must match. Parsed with tr and cut because this image has no sed or grep: the JSON is
# split onto one field per line, the size field is selected, and everything but its digits dropped.
ACTUAL_SIZE="$(mc stat --json "caskeleton/${MINIO_BUCKET}/${KEY}" \
| tr ',' '\n' | tr -d ' ' | while IFS= read -r field; do
case "${field}" in '"size":'*) echo "${field}" | cut -d: -f2 | tr -dc '0-9' ;; esac
done)"
if [ -z "${ACTUAL_SIZE}" ]; then
echo "object-storage-smoke: mc stat reported no size for the uploaded object" >&2
exit 1
fi
if [ "${ACTUAL_SIZE}" != "${EXPECTED_SIZE}" ]; then
echo "object-storage-smoke: HEAD reported ${ACTUAL_SIZE} bytes, uploaded ${EXPECTED_SIZE}" >&2
exit 1
fi
# 3. download: bytes must be identical, by digest rather than by cmp
mc cp "caskeleton/${MINIO_BUCKET}/${KEY}" "${WORK}/roundtrip"
UPLOADED_DIGEST="$(sha256sum < "${WORK}/payload" | cut -d' ' -f1)"
RETURNED_DIGEST="$(sha256sum < "${WORK}/roundtrip" | cut -d' ' -f1)"
if [ "${UPLOADED_DIGEST}" != "${RETURNED_DIGEST}" ]; then
echo "object-storage-smoke: downloaded bytes differ from what was uploaded" >&2
exit 1
fi
# 4. delete: must then be absent
mc rm "caskeleton/${MINIO_BUCKET}/${KEY}"
if mc stat "caskeleton/${MINIO_BUCKET}/${KEY}" >/dev/null 2>&1; then
echo "object-storage-smoke: object still present after delete" >&2
exit 1
fi
# 5. a deliberately wrong credential must be refused
if mc alias set rejected "${MINIO_ENDPOINT}" "${MINIO_ROOT_USER}" "definitely-not-the-password" >/dev/null 2>&1 \
&& mc ls "rejected/${MINIO_BUCKET}" >/dev/null 2>&1; then
echo "object-storage-smoke: a wrong password was accepted" >&2
exit 1
fi
echo "object-storage-smoke: upload, head, download, delete and credential rejection all passed"