A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go
at main 432 lines 13 kB view raw
1#!/bin/bash 2# 3# ATCR End-to-End Test Script 4# Tests single-arch and multi-arch image push/pull flows 5# 6# Usage: 7# ./test-e2e.sh # Run full test suite 8# ./test-e2e.sh --multi # Skip to multi-arch test only 9# REGISTRY=localhost:5000 ./test-e2e.sh # Custom registry 10# NAMESPACE=myuser ./test-e2e.sh # Custom namespace 11# VERBOSE=0 ./test-e2e.sh # Hide docker output 12# 13# To see bash command execution, edit this file and uncomment 'set -x' below 14# 15 16set -e 17 18# Verbose mode - shows docker command output 19# Set VERBOSE=0 to hide docker output: VERBOSE=0 ./test-e2e.sh 20VERBOSE="${VERBOSE:-1}" 21 22# Bash trace mode - shows every bash command as it executes 23# Uncomment the line below to see bash commands as they execute 24# set -x 25 26# Colors for output 27RED='\033[0;31m' 28GREEN='\033[0;32m' 29YELLOW='\033[1;33m' 30BLUE='\033[0;34m' 31NC='\033[0m' # No Color 32 33# Configuration 34REGISTRY="${REGISTRY:-127.0.0.1:5000}" 35NAMESPACE="${NAMESPACE:-evan.jarrett.net}" 36IMAGE_NAME="test-image" 37TAG="latest" 38MULTI_ARCH_TAG="multiarch" 39 40# Full image references 41SINGLE_ARCH_IMAGE="${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}" 42MULTI_ARCH_IMAGE="${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${MULTI_ARCH_TAG}" 43AMD64_IMAGE="${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}-amd64" 44ARM64_IMAGE="${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${TAG}-arm64" 45 46# Temporary directory for test images 47BUILD_DIR=$(mktemp -d) 48 49# Cleanup function 50cleanup() { 51 log_info "Cleaning up..." 52 rm -rf ${BUILD_DIR} 53 54 # Clean up any dangling manifest lists 55 docker manifest rm ${MULTI_ARCH_IMAGE} 2>/dev/null || true 56} 57 58trap cleanup EXIT 59 60# Logging functions 61log_info() { 62 echo -e "${GREEN}[INFO]${NC} $1" 63} 64 65log_error() { 66 echo -e "${RED}[ERROR]${NC} $1" 67} 68 69log_warn() { 70 echo -e "${YELLOW}[WARN]${NC} $1" 71} 72 73log_step() { 74 echo -e "\n${GREEN}==>${NC} $1" 75} 76 77log_cmd() { 78 echo -e "${BLUE}[CMD]${NC} $*" 79 if [ "$VERBOSE" = "1" ]; then 80 "$@" 81 else 82 "$@" > /dev/null 2>&1 83 fi 84} 85 86# Check if docker compose services are running 87check_compose_services() { 88 log_step "Checking docker compose services..." 89 90 if ! docker compose ps | grep -q "Up"; then 91 log_warn "Some services may not be running. Starting services..." 92 docker compose up -d 93 sleep 5 94 fi 95 96 # Check for errors in logs 97 log_info "Checking for errors in service logs..." 98 if docker compose logs --tail=50 | grep -i "error" | grep -v "level=error msg=\"error processing" | grep -v "test"; then 99 log_warn "Found some errors in logs (may be normal)" 100 else 101 log_info "No critical errors found in recent logs" 102 fi 103} 104 105# Build a simple test image 106build_single_arch_image() { 107 log_step "Building single-arch test image..." 108 109 cat > ${BUILD_DIR}/Dockerfile <<EOF 110FROM alpine:latest 111 112# Add some content to make the image non-trivial 113RUN apk add --no-cache curl bash 114 115# Create a test file with some content 116RUN echo "This is a test image created at \$(date)" > /test.txt 117RUN echo "Architecture: \$(uname -m)" >> /test.txt 118 119# Add a simple script 120RUN echo '#!/bin/sh' > /test.sh && \\ 121 echo 'echo "Test image running successfully!"' >> /test.sh && \\ 122 echo 'cat /test.txt' >> /test.sh && \\ 123 chmod +x /test.sh 124 125CMD ["/test.sh"] 126EOF 127 128 log_cmd docker build -t ${SINGLE_ARCH_IMAGE} ${BUILD_DIR} 129 log_info "Built single-arch image: ${SINGLE_ARCH_IMAGE}" 130} 131 132# Build multi-arch images using docker manifest create (old school!) 133build_multi_arch_images() { 134 log_step "Building multi-arch images (amd64 and arm64) using docker manifest..." 135 136 # Create Dockerfile for multi-arch builds 137 cat > ${BUILD_DIR}/Dockerfile.multiarch <<EOF 138FROM alpine:latest 139 140ARG TARGETARCH=unknown 141ARG TARGETOS=linux 142 143# Add some content to make the image non-trivial 144RUN apk add --no-cache curl bash 145 146# Create a test file with arch info 147RUN echo "This is a multi-arch test image" > /test.txt 148RUN echo "Target OS: \${TARGETOS}" >> /test.txt 149RUN echo "Target Architecture: \${TARGETARCH}" >> /test.txt 150RUN echo "Built at: \$(date)" >> /test.txt 151 152# Add a simple script 153RUN echo '#!/bin/sh' > /test.sh && \\ 154 echo 'echo "Multi-arch test image running!"' >> /test.sh && \\ 155 echo 'cat /test.txt' >> /test.sh && \\ 156 chmod +x /test.sh 157 158CMD ["/test.sh"] 159EOF 160 161 # Build amd64 image 162 log_info "Building amd64 image..." 163 log_cmd docker build \ 164 --platform linux/amd64 \ 165 --build-arg TARGETARCH=amd64 \ 166 --build-arg TARGETOS=linux \ 167 -t ${AMD64_IMAGE} \ 168 -f ${BUILD_DIR}/Dockerfile.multiarch \ 169 ${BUILD_DIR} 170 171 # Push amd64 image 172 log_info "Pushing amd64 image..." 173 echo -e "${BLUE}[CMD]${NC} docker push ${AMD64_IMAGE}" 174 if ! docker push ${AMD64_IMAGE}; then 175 log_error "Failed to push amd64 image" 176 return 1 177 fi 178 179 # Build arm64 image 180 log_info "Building arm64 image..." 181 log_cmd docker build \ 182 --platform linux/arm64 \ 183 --build-arg TARGETARCH=arm64 \ 184 --build-arg TARGETOS=linux \ 185 -t ${ARM64_IMAGE} \ 186 -f ${BUILD_DIR}/Dockerfile.multiarch \ 187 ${BUILD_DIR} 188 189 # Push arm64 image 190 log_info "Pushing arm64 image..." 191 echo -e "${BLUE}[CMD]${NC} docker push ${ARM64_IMAGE}" 192 if ! docker push ${ARM64_IMAGE}; then 193 log_error "Failed to push arm64 image" 194 return 1 195 fi 196 197 # Create manifest list 198 log_info "Creating multi-arch manifest list..." 199 echo -e "${BLUE}[CMD]${NC} docker manifest create --insecure ${MULTI_ARCH_IMAGE} ${AMD64_IMAGE} ${ARM64_IMAGE}" 200 docker manifest create --insecure ${MULTI_ARCH_IMAGE} \ 201 ${AMD64_IMAGE} \ 202 ${ARM64_IMAGE} 203 204 # Annotate manifests with platform info 205 log_info "Annotating manifest with platform info..." 206 docker manifest annotate ${MULTI_ARCH_IMAGE} ${AMD64_IMAGE} \ 207 --os linux --arch amd64 208 docker manifest annotate ${MULTI_ARCH_IMAGE} ${ARM64_IMAGE} \ 209 --os linux --arch arm64 210 211 # Push the manifest list 212 log_info "Pushing multi-arch manifest list..." 213 echo -e "${BLUE}[CMD]${NC} docker manifest push --insecure ${MULTI_ARCH_IMAGE}" 214 docker manifest push --insecure ${MULTI_ARCH_IMAGE} 215 216 log_info "Multi-arch manifest created and pushed: ${MULTI_ARCH_IMAGE}" 217} 218 219# Push single-arch image and verify digest 220push_single_arch_image() { 221 log_step "Pushing single-arch image..." 222 223 echo -e "${BLUE}[CMD]${NC} docker push ${SINGLE_ARCH_IMAGE}" 224 local push_output 225 push_output=$(docker push ${SINGLE_ARCH_IMAGE} 2>&1 | tee /dev/tty) 226 227 # Extract and verify digest 228 local digest 229 digest=$(echo "$push_output" | grep -oP 'digest: \K[a-z0-9:]+' | tail -1) 230 231 if [ -z "$digest" ]; then 232 log_error "Failed to get digest from push output" 233 return 1 234 fi 235 236 log_info "Pushed with digest: ${digest}" 237 238 # Verify we can reference by digest 239 log_info "Verifying digest reference..." 240 echo -e "${BLUE}[CMD]${NC} docker manifest inspect --insecure ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}@${digest}" 241 docker manifest inspect --insecure "${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}@${digest}" 242 log_info "Digest verification successful!" 243} 244 245# Verify multi-arch manifest 246verify_multi_arch_manifest() { 247 log_step "Verifying multi-arch manifest..." 248 249 echo -e "${BLUE}[CMD]${NC} docker manifest inspect --insecure ${MULTI_ARCH_IMAGE}" 250 local manifest 251 manifest=$(docker manifest inspect --insecure ${MULTI_ARCH_IMAGE} | tee /dev/tty) 252 253 # Check for both architectures (check for "amd64" and "arm64" in platform.architecture) 254 if echo "$manifest" | grep -q '"architecture": "amd64"'; then 255 log_info "Found linux/amd64 manifest" 256 else 257 log_error "Missing linux/amd64 manifest" 258 return 1 259 fi 260 261 if echo "$manifest" | grep -q '"architecture": "arm64"'; then 262 log_info "Found linux/arm64 manifest" 263 else 264 log_error "Missing linux/arm64 manifest" 265 return 1 266 fi 267 268 # Extract digest 269 local digest 270 digest=$(echo "$manifest" | jq -r '.manifests[0].digest' 2>/dev/null || echo "$manifest" | grep -oP 'sha256:[a-f0-9]+' | head -1) 271 272 if [ -n "$digest" ]; then 273 log_info "Multi-arch manifest digest: ${digest}" 274 fi 275} 276 277# Remove local images 278cleanup_local_images() { 279 log_step "Removing local images..." 280 281 echo -e "${BLUE}[CMD]${NC} docker rmi ${SINGLE_ARCH_IMAGE}" 282 docker rmi ${SINGLE_ARCH_IMAGE} || true 283 284 echo -e "${BLUE}[CMD]${NC} docker rmi ${AMD64_IMAGE}" 285 docker rmi ${AMD64_IMAGE} || true 286 287 echo -e "${BLUE}[CMD]${NC} docker rmi ${ARM64_IMAGE}" 288 docker rmi ${ARM64_IMAGE} || true 289 290 echo -e "${BLUE}[CMD]${NC} docker rmi ${MULTI_ARCH_IMAGE}" 291 docker rmi ${MULTI_ARCH_IMAGE} || true 292 293 # Clean up manifest list 294 docker manifest rm ${MULTI_ARCH_IMAGE} 2>/dev/null || true 295 296 # Also remove any cached layers 297 log_info "Pruning dangling images..." 298 log_cmd docker image prune -f 299 300 log_info "Local images removed" 301} 302 303# Pull images back 304pull_images() { 305 log_step "Pulling images back from registry..." 306 307 # Pull single-arch image 308 log_info "Pulling single-arch image..." 309 echo -e "${BLUE}[CMD]${NC} docker pull ${SINGLE_ARCH_IMAGE}" 310 if docker pull ${SINGLE_ARCH_IMAGE}; then 311 log_info "Successfully pulled: ${SINGLE_ARCH_IMAGE}" 312 else 313 log_error "Failed to pull single-arch image" 314 return 1 315 fi 316 317 # Pull multi-arch image 318 log_info "Pulling multi-arch image..." 319 echo -e "${BLUE}[CMD]${NC} docker pull ${MULTI_ARCH_IMAGE}" 320 if docker pull ${MULTI_ARCH_IMAGE}; then 321 log_info "Successfully pulled: ${MULTI_ARCH_IMAGE}" 322 else 323 log_error "Failed to pull multi-arch image" 324 return 1 325 fi 326} 327 328# Test running the images 329test_images() { 330 log_step "Testing pulled images..." 331 332 # Test single-arch image 333 log_info "Running single-arch image..." 334 log_cmd docker run --rm ${SINGLE_ARCH_IMAGE} 335 336 # Test multi-arch image 337 log_info "Running multi-arch image..." 338 log_cmd docker run --rm ${MULTI_ARCH_IMAGE} 339 340 log_info "All images ran successfully!" 341} 342 343# Check for errors in compose logs after operations 344check_compose_logs() { 345 log_step "Checking compose logs for errors..." 346 347 local error_count 348 error_count=$(docker compose logs --tail=100 | grep -i "error" | grep -v "level=error msg=\"error processing" | grep -v "test" | wc -l) 349 350 if [ "$error_count" -gt 0 ]; then 351 log_warn "Found ${error_count} error messages in logs:" 352 docker compose logs --tail=100 | grep -i "error" | grep -v "level=error msg=\"error processing" | grep -v "test" | tail -10 353 else 354 log_info "No errors found in compose logs" 355 fi 356} 357 358# Multi-arch only test flow 359multi_arch_only() { 360 log_step "Starting ATCR multi-arch test (skipping single-arch)" 361 log_info "Registry: ${REGISTRY}" 362 log_info "Namespace: ${NAMESPACE}" 363 log_info "Build directory: ${BUILD_DIR}" 364 log_info "Verbose mode: ${VERBOSE} (set VERBOSE=0 to hide docker output)" 365 366 # Check prerequisites 367 if ! command -v docker &> /dev/null; then 368 log_error "Docker is not installed" 369 exit 1 370 fi 371 372 if ! command -v jq &> /dev/null; then 373 log_warn "jq is not installed - some checks may be limited" 374 fi 375 376 # Run multi-arch test steps only 377 check_compose_services 378 build_multi_arch_images 379 verify_multi_arch_manifest 380 cleanup_local_images 381 pull_images 382 log_info "Running multi-arch image..." 383 log_cmd docker run --rm ${MULTI_ARCH_IMAGE} 384 check_compose_logs 385 386 log_step "Multi-arch test completed successfully!" 387 echo -e "${GREEN}========================================${NC}" 388 echo -e "${GREEN}Multi-arch test passed!${NC}" 389 echo -e "${GREEN}========================================${NC}" 390} 391 392# Main test flow 393main() { 394 log_step "Starting ATCR end-to-end test" 395 log_info "Registry: ${REGISTRY}" 396 log_info "Namespace: ${NAMESPACE}" 397 log_info "Build directory: ${BUILD_DIR}" 398 log_info "Verbose mode: ${VERBOSE} (set VERBOSE=0 to hide docker output)" 399 400 # Check prerequisites 401 if ! command -v docker &> /dev/null; then 402 log_error "Docker is not installed" 403 exit 1 404 fi 405 406 if ! command -v jq &> /dev/null; then 407 log_warn "jq is not installed - some checks may be limited" 408 fi 409 410 # Run test steps 411 check_compose_services 412 build_single_arch_image 413 push_single_arch_image 414 build_multi_arch_images 415 verify_multi_arch_manifest 416 cleanup_local_images 417 pull_images 418 test_images 419 check_compose_logs 420 421 log_step "End-to-end test completed successfully!" 422 echo -e "${GREEN}========================================${NC}" 423 echo -e "${GREEN}All tests passed!${NC}" 424 echo -e "${GREEN}========================================${NC}" 425} 426 427# Parse arguments 428if [ "$1" = "--multi" ]; then 429 multi_arch_only 430else 431 main "$@" 432fi