Something went wrong. Try again.
A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
Something went wrong. Try again.
13 kB · 432 lines
Shell
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433#!/bin/bash## ATCR End-to-End Test Script# Tests single-arch and multi-arch image push/pull flows## Usage:# ./test-e2e.sh # Run full test suite# ./test-e2e.sh --multi # Skip to multi-arch test only# REGISTRY=localhost:5000 ./test-e2e.sh # Custom registry# NAMESPACE=myuser ./test-e2e.sh # Custom namespace# VERBOSE=0 ./test-e2e.sh # Hide docker output## To see bash command execution, edit this file and uncomment 'set -x' below#
set -e
# Verbose mode - shows docker command output# Set VERBOSE=0 to hide docker output: VERBOSE=0 ./test-e2e.shVERBOSE="${VERBOSE:-1}"
# Bash trace mode - shows every bash command as it executes# Uncomment the line below to see bash commands as they execute# set -x
# Colors for outputRED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'BLUE='\033[0;34m'NC='\033[0m' # No Color
# ConfigurationREGISTRY="${REGISTRY:-127.0.0.1:5000}"NAMESPACE="${NAMESPACE:-evan.jarrett.net}"IMAGE_NAME="test-image"TAG="latest"MULTI_ARCH_TAG="multiarch"
# Full image referencesSINGLE_ARCH_IMAGE="${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}"MULTI_ARCH_IMAGE="${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${MULTI_ARCH_TAG}"AMD64_IMAGE="${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}-amd64"ARM64_IMAGE="${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}-arm64"
# Temporary directory for test imagesBUILD_DIR=$(mktemp -d)
# Cleanup functioncleanup() { log_info "Cleaning up..." rm -rf ${BUILD_DIR}
# Clean up any dangling manifest lists docker manifest rm ${MULTI_ARCH_IMAGE} 2>/dev/null || true}
trap cleanup EXIT
# Logging functionslog_info() { echo -e "${GREEN}[INFO]${NC} $1"}
log_error() { echo -e "${RED}[ERROR]${NC} $1"}
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"}
log_step() { echo -e "\n${GREEN}==>${NC} $1"}
log_cmd() { echo -e "${BLUE}[CMD]${NC} $*" if [ "$VERBOSE" = "1" ]; then "$@" else "$@" > /dev/null 2>&1 fi}
# Check if docker compose services are runningcheck_compose_services() { log_step "Checking docker compose services..."
if ! docker compose ps | grep -q "Up"; then log_warn "Some services may not be running. Starting services..." docker compose up -d sleep 5 fi
# Check for errors in logs log_info "Checking for errors in service logs..." if docker compose logs --tail=50 | grep -i "error" | grep -v "level=error msg=\"error processing" | grep -v "test"; then log_warn "Found some errors in logs (may be normal)" else log_info "No critical errors found in recent logs" fi}
# Build a simple test imagebuild_single_arch_image() { log_step "Building single-arch test image..."
cat > ${BUILD_DIR}/Dockerfile <<EOFFROM alpine:latest
# Add some content to make the image non-trivialRUN apk add --no-cache curl bash
# Create a test file with some contentRUN echo "This is a test image created at \$(date)" > /test.txtRUN echo "Architecture: \$(uname -m)" >> /test.txt
# Add a simple scriptRUN echo '#!/bin/sh' > /test.sh && \\ echo 'echo "Test image running successfully!"' >> /test.sh && \\ echo 'cat /test.txt' >> /test.sh && \\ chmod +x /test.sh
CMD ["/test.sh"]EOF
log_cmd docker build -t ${SINGLE_ARCH_IMAGE} ${BUILD_DIR} log_info "Built single-arch image: ${SINGLE_ARCH_IMAGE}"}
# Build multi-arch images using docker manifest create (old school!)build_multi_arch_images() { log_step "Building multi-arch images (amd64 and arm64) using docker manifest..."
# Create Dockerfile for multi-arch builds cat > ${BUILD_DIR}/Dockerfile.multiarch <<EOFFROM alpine:latest
ARG TARGETARCH=unknownARG TARGETOS=linux
# Add some content to make the image non-trivialRUN apk add --no-cache curl bash
# Create a test file with arch infoRUN echo "This is a multi-arch test image" > /test.txtRUN echo "Target OS: \${TARGETOS}" >> /test.txtRUN echo "Target Architecture: \${TARGETARCH}" >> /test.txtRUN echo "Built at: \$(date)" >> /test.txt
# Add a simple scriptRUN echo '#!/bin/sh' > /test.sh && \\ echo 'echo "Multi-arch test image running!"' >> /test.sh && \\ echo 'cat /test.txt' >> /test.sh && \\ chmod +x /test.sh
CMD ["/test.sh"]EOF
# Build amd64 image log_info "Building amd64 image..." log_cmd docker build \ --platform linux/amd64 \ --build-arg TARGETARCH=amd64 \ --build-arg TARGETOS=linux \ -t ${AMD64_IMAGE} \ -f ${BUILD_DIR}/Dockerfile.multiarch \ ${BUILD_DIR}
# Push amd64 image log_info "Pushing amd64 image..." echo -e "${BLUE}[CMD]${NC} docker push ${AMD64_IMAGE}" if ! docker push ${AMD64_IMAGE}; then log_error "Failed to push amd64 image" return 1 fi
# Build arm64 image log_info "Building arm64 image..." log_cmd docker build \ --platform linux/arm64 \ --build-arg TARGETARCH=arm64 \ --build-arg TARGETOS=linux \ -t ${ARM64_IMAGE} \ -f ${BUILD_DIR}/Dockerfile.multiarch \ ${BUILD_DIR}
# Push arm64 image log_info "Pushing arm64 image..." echo -e "${BLUE}[CMD]${NC} docker push ${ARM64_IMAGE}" if ! docker push ${ARM64_IMAGE}; then log_error "Failed to push arm64 image" return 1 fi
# Create manifest list log_info "Creating multi-arch manifest list..." echo -e "${BLUE}[CMD]${NC} docker manifest create --insecure ${MULTI_ARCH_IMAGE} ${AMD64_IMAGE} ${ARM64_IMAGE}" docker manifest create --insecure ${MULTI_ARCH_IMAGE} \ ${AMD64_IMAGE} \ ${ARM64_IMAGE}
# Annotate manifests with platform info log_info "Annotating manifest with platform info..." docker manifest annotate ${MULTI_ARCH_IMAGE} ${AMD64_IMAGE} \ --os linux --arch amd64 docker manifest annotate ${MULTI_ARCH_IMAGE} ${ARM64_IMAGE} \ --os linux --arch arm64
# Push the manifest list log_info "Pushing multi-arch manifest list..." echo -e "${BLUE}[CMD]${NC} docker manifest push --insecure ${MULTI_ARCH_IMAGE}" docker manifest push --insecure ${MULTI_ARCH_IMAGE}
log_info "Multi-arch manifest created and pushed: ${MULTI_ARCH_IMAGE}"}
# Push single-arch image and verify digestpush_single_arch_image() { log_step "Pushing single-arch image..."
echo -e "${BLUE}[CMD]${NC} docker push ${SINGLE_ARCH_IMAGE}" local push_output push_output=$(docker push ${SINGLE_ARCH_IMAGE} 2>&1 | tee /dev/tty)
# Extract and verify digest local digest digest=$(echo "$push_output" | grep -oP 'digest: \K[a-z0-9:]+' | tail -1)
if [ -z "$digest" ]; then log_error "Failed to get digest from push output" return 1 fi
log_info "Pushed with digest: ${digest}"
# Verify we can reference by digest log_info "Verifying digest reference..." echo -e "${BLUE}[CMD]${NC} docker manifest inspect --insecure ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}@${digest}" docker manifest inspect --insecure "${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}@${digest}" log_info "Digest verification successful!"}
# Verify multi-arch manifestverify_multi_arch_manifest() { log_step "Verifying multi-arch manifest..."
echo -e "${BLUE}[CMD]${NC} docker manifest inspect --insecure ${MULTI_ARCH_IMAGE}" local manifest manifest=$(docker manifest inspect --insecure ${MULTI_ARCH_IMAGE} | tee /dev/tty)
# Check for both architectures (check for "amd64" and "arm64" in platform.architecture) if echo "$manifest" | grep -q '"architecture": "amd64"'; then log_info "Found linux/amd64 manifest" else log_error "Missing linux/amd64 manifest" return 1 fi
if echo "$manifest" | grep -q '"architecture": "arm64"'; then log_info "Found linux/arm64 manifest" else log_error "Missing linux/arm64 manifest" return 1 fi
# Extract digest local digest digest=$(echo "$manifest" | jq -r '.manifests[0].digest' 2>/dev/null || echo "$manifest" | grep -oP 'sha256:[a-f0-9]+' | head -1)
if [ -n "$digest" ]; then log_info "Multi-arch manifest digest: ${digest}" fi}
# Remove local imagescleanup_local_images() { log_step "Removing local images..."
echo -e "${BLUE}[CMD]${NC} docker rmi ${SINGLE_ARCH_IMAGE}" docker rmi ${SINGLE_ARCH_IMAGE} || true
echo -e "${BLUE}[CMD]${NC} docker rmi ${AMD64_IMAGE}" docker rmi ${AMD64_IMAGE} || true
echo -e "${BLUE}[CMD]${NC} docker rmi ${ARM64_IMAGE}" docker rmi ${ARM64_IMAGE} || true
echo -e "${BLUE}[CMD]${NC} docker rmi ${MULTI_ARCH_IMAGE}" docker rmi ${MULTI_ARCH_IMAGE} || true
# Clean up manifest list docker manifest rm ${MULTI_ARCH_IMAGE} 2>/dev/null || true
# Also remove any cached layers log_info "Pruning dangling images..." log_cmd docker image prune -f
log_info "Local images removed"}
# Pull images backpull_images() { log_step "Pulling images back from registry..."
# Pull single-arch image log_info "Pulling single-arch image..." echo -e "${BLUE}[CMD]${NC} docker pull ${SINGLE_ARCH_IMAGE}" if docker pull ${SINGLE_ARCH_IMAGE}; then log_info "Successfully pulled: ${SINGLE_ARCH_IMAGE}" else log_error "Failed to pull single-arch image" return 1 fi
# Pull multi-arch image log_info "Pulling multi-arch image..." echo -e "${BLUE}[CMD]${NC} docker pull ${MULTI_ARCH_IMAGE}" if docker pull ${MULTI_ARCH_IMAGE}; then log_info "Successfully pulled: ${MULTI_ARCH_IMAGE}" else log_error "Failed to pull multi-arch image" return 1 fi}
# Test running the imagestest_images() { log_step "Testing pulled images..."
# Test single-arch image log_info "Running single-arch image..." log_cmd docker run --rm ${SINGLE_ARCH_IMAGE}
# Test multi-arch image log_info "Running multi-arch image..." log_cmd docker run --rm ${MULTI_ARCH_IMAGE}
log_info "All images ran successfully!"}
# Check for errors in compose logs after operationscheck_compose_logs() { log_step "Checking compose logs for errors..."
local error_count error_count=$(docker compose logs --tail=100 | grep -i "error" | grep -v "level=error msg=\"error processing" | grep -v "test" | wc -l)
if [ "$error_count" -gt 0 ]; then log_warn "Found ${error_count} error messages in logs:" docker compose logs --tail=100 | grep -i "error" | grep -v "level=error msg=\"error processing" | grep -v "test" | tail -10 else log_info "No errors found in compose logs" fi}
# Multi-arch only test flowmulti_arch_only() { log_step "Starting ATCR multi-arch test (skipping single-arch)" log_info "Registry: ${REGISTRY}" log_info "Namespace: ${NAMESPACE}" log_info "Build directory: ${BUILD_DIR}" log_info "Verbose mode: ${VERBOSE} (set VERBOSE=0 to hide docker output)"
# Check prerequisites if ! command -v docker &> /dev/null; then log_error "Docker is not installed" exit 1 fi
if ! command -v jq &> /dev/null; then log_warn "jq is not installed - some checks may be limited" fi
# Run multi-arch test steps only check_compose_services build_multi_arch_images verify_multi_arch_manifest cleanup_local_images pull_images log_info "Running multi-arch image..." log_cmd docker run --rm ${MULTI_ARCH_IMAGE} check_compose_logs
log_step "Multi-arch test completed successfully!" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN}Multi-arch test passed!${NC}" echo -e "${GREEN}========================================${NC}"}
# Main test flowmain() { log_step "Starting ATCR end-to-end test" log_info "Registry: ${REGISTRY}" log_info "Namespace: ${NAMESPACE}" log_info "Build directory: ${BUILD_DIR}" log_info "Verbose mode: ${VERBOSE} (set VERBOSE=0 to hide docker output)"
# Check prerequisites if ! command -v docker &> /dev/null; then log_error "Docker is not installed" exit 1 fi
if ! command -v jq &> /dev/null; then log_warn "jq is not installed - some checks may be limited" fi
# Run test steps check_compose_services build_single_arch_image push_single_arch_image build_multi_arch_images verify_multi_arch_manifest cleanup_local_images pull_images test_images check_compose_logs
log_step "End-to-end test completed successfully!" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN}All tests passed!${NC}" echo -e "${GREEN}========================================${NC}"}
# Parse argumentsif [ "$1" = "--multi" ]; then multi_arch_onlyelse main "$@"fi