Purpose
Your QA team just received a new CleanStart image. This guide tells you exactly what to test before promoting it to production. These are YOUR tests — independent validation beyond CleanStart's internal 78-test inspection suite.
key principle: Trust, but verify. CleanStart has rigorous testing, but your organization must independently validate that images work in YOUR environment.
The following diagram illustrates the acceptance testing pyramid with layers from infrastructure to functional verification:
graph TB A["Acceptance Testing<br/>Pyramid"] -->|Level 1| B["Image Integrity<br/>5 Tests"] B -->|Verify| C["Digest<br/>Matching"] C -->|Verify| D["SBOM<br/>Validation"] D -->|Verify| E["Signature<br/>Verification"] A -->|Level 2| F["Security<br/>Hardening<br/>8 Tests"] F -->|Scan| G["CVE<br/>Check"] G -->|Verify| H["FIPS<br/>Compliance"] H -->|Check| I["No<br/>Root Access"] A -->|Level 3| J["Functional<br/>Compatibility<br/>10 Tests"] J -->|Test with| K["Your<br/>Database"] K -->|Test with| L["Your<br/>Message Queue"] L -->|Test with| M["Your<br/>Workloads"] A -->|Level 4| N["Performance<br/>Baseline<br/>5 Tests"] N -->|Measure| O["Startup<br/>Time"] O -->|Measure| P["Memory<br/>Usage"] P -->|Measure| Q["CPU<br/>Performance"] A -->|Level 5| R["Compliance<br/>Verification<br/>5 Tests"] R -->|Audit| S["Provenance<br/>Chain"] S -->|Audit| T["Licensing<br/>Compliance"] T -->|Audit| U["Regulatory<br/>Requirements"] style B fill:#fff3cd style F fill:#ffcccc style J fill:#cce5ff style N fill:#ccffcc style R fill:#f0ccffAcceptance Testing Philosophy
Design Principles
- Defense in Depth: CleanStart provides 11 verification artifacts (signatures, SBOMs, provenance); your acceptance tests are an additional quality gate
- Environment-Specific: CleanStart tests in generic environments; you test with your actual database engines, message queues, service mesh, and workloads
- Risk-Based: Prioritize testing of critical functionality over nice-to-haves
- Automation First: All tests should be automated and integrated into your CI/CD pipeline
- Audit Trail: Record all test results for compliance and incident investigation
Test Coverage Matrix
Category | Test Count | Automation | Env |
|---|---|---|---|
Image Integrity | 5 | Fully Auto | Any |
Security Hardening | 8 | Fully Auto | Any |
Functional Compatibility | 10 | Mostly Auto | Staging |
Performance Baseline | 5 | Fully Auto | Staging |
Compliance Verification | 5 | Fully Auto | Any |
Total | 33 |
Pre-Requisites
Tools Required
Tool | Purpose | Installation |
|---|---|---|
cosign | Verify image signatures and attestations |
|
crane | Inspect image metadata without pulling |
|
skopeo | Copy/inspect images across registries |
|
docker or podman | Pull and run images |
|
trivy | Independent vulnerability scanning |
|
grype | Alternative vulnerability scanner |
|
jq | JSON parsing and filtering |
|
kubectl (optional) | Deploy to Kubernetes cluster for testing |
|
Environment Setup
# 1. Get CleanStart's public Cosign keycurl -O https://cleanstart.dev/keys/cosign.pub # 2. Set registry credentialsexport DOCKER_CONFIG=$HOME/.dockerdocker login registry.cleanstart.com # Use your API key as password # 3. Create test directoriesmkdir -p /tmp/acceptance-tests/sbom /tmp/acceptance-tests/logs # 4. Configure test cluster (optional, for functional tests)# Use kind, minikube, or connect to staging clusterkind create cluster --name qa-test || minikube startTest Data and Reference Images
Reference image: The previously-deployed production image (comparison baseline) New image: The candidate image being tested Sample application: A minimal app that exercises critical functionality. Example reference images to test against:
# Test a Python imageNEW_IMAGE=registry.cleanstart.com/cleanstart/python:3.12-build20250315OLD_IMAGE=registry.cleanstart.com/cleanstart/python:3.12-build20250228 # Test a Node.js imageNEW_IMAGE=registry.cleanstart.com/cleanstart/node:20-build20250315OLD_IMAGE=registry.cleanstart.com/cleanstart/node:20-build20250228Test Category 1: Image Integrity Verification (5 Tests)
purpose: Verify the image hasn't been tampered with and is genuinely from CleanStart.
when to run: Before ANY other testing (these are prerequisite checks).
pass criteria: All 5 tests pass.
Test 1.1: Verify Cosign Signature
Confirm the image is cryptographically signed by CleanStart.
#!/bin/bash# tests/test-1.1-cosign-signature.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}COSIGN_KEY=cosign.pub echo "[1.1] Verifying Cosign signature for $IMAGE" if cosign verify --key $COSIGN_KEY $IMAGE > /dev/null 2>&1; then echo "✅ PASS: Image signature verified" exit 0else echo "❌ FAIL: Image signature verification failed" echo " This image may be tampered with or unsigned." exit 1fiwhat it tests: Supply chain integrity, prevents tampering expected outcome: Signature verification succeeds failure investigation Confirm cosign.pub is from official CleanStart source (https://cleanstart.dev/keys/cosign.pub) Verify image digest hasn't changed: crane digest $IMAGE Check image was pulled from correct registry (registry.cleanstart.com).
Test 1.2: Verify SBOM Attestation
Confirm SBOM (Software Bill of Materials) is signed and present.
#!/bin/bash# tests/test-1.2-sbom-attestation.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}COSIGN_KEY=cosign.pubSBOM_FILE=/tmp/acceptance-tests/sbom/$(echo $IMAGE | sed 's|/|-|g').spdx.json echo "[1.2] Verifying SBOM attestation for $IMAGE" if cosign verify-attestation --key $COSIGN_KEY --type spdxjson $IMAGE > $SBOM_FILE 2>&1; then COMPONENT_COUNT=$(jq '.payload | @base64d | fromjson | .components | length' $SBOM_FILE) echo "✅ PASS: SBOM attestation verified" echo " Components found: $COMPONENT_COUNT" exit 0else echo "❌ FAIL: SBOM attestation verification failed" exit 1fiwhat it tests: Bill of materials accuracy, component transparency expected outcome: SBOM attestation present and signed failure investigation Image may predate SBOM attestation feature Confirm image version is recent (post-2024) Check CleanStart release notes for SBOM support. next step: Archive downloaded SBOM for compliance audit trail.
cp $SBOM_FILE ./compliance/sbom-$(date +%Y%m%d).spdx.jsonTest 1.3: Verify SLSA Provenance
Confirm build provenance is recorded and signed (SLSA Level 4).
#!/bin/bash# tests/test-1.3-slsa-provenance.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}COSIGN_KEY=cosign.pubPROVENANCE_FILE=/tmp/acceptance-tests/sbom/$(echo $IMAGE | sed 's|/|-|g').slsaprovenance.json echo "[1.3] Verifying SLSA provenance for $IMAGE" if cosign verify-attestation --key $COSIGN_KEY --type slsaprovenance $IMAGE > $PROVENANCE_FILE 2>&1; then BUILDER=$(jq -r '.payload | @base64d | fromjson | .builder.id' $PROVENANCE_FILE) MATERIALS=$(jq '.payload | @base64d | fromjson | .materials | length' $PROVENANCE_FILE) echo "✅ PASS: SLSA provenance verified" echo " Builder: $BUILDER" echo " Materials tracked: $MATERIALS" exit 0else echo "❌ FAIL: SLSA provenance verification failed" exit 1fiwhat it tests: Build transparency, supply chain authenticity expected outcome: Provenance attestation present and signed failure investigation Confirm image version supports SLSA (post-2024) Check CleanStart documentation for SLSA support timeline. provenance verification.
# Extract and inspect provenance detailsjq '.payload | @base64d | fromjson' $PROVENANCE_FILE | jq '. { builder: .builder.id, invocation: .invocation.parameters, buildStartTime: .startTime, buildEndTime: .finishTime, materials: [.materials[].uri] }'Test 1.4: Verify Image Digest Stability
Confirm pulling the same image always yields the same digest (deterministic).
#!/bin/bash# tests/test-1.4-digest-stability.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[1.4] Verifying image digest stability for $IMAGE" DIGEST_1=$(crane digest $IMAGE)DIGEST_2=$(crane digest $IMAGE)DIGEST_3=$(crane digest $IMAGE) echo " Pull 1: $DIGEST_1"echo " Pull 2: $DIGEST_2"echo " Pull 3: $DIGEST_3" if [[ "$DIGEST_1" == "$DIGEST_2" ]] && [[ "$DIGEST_2" == "$DIGEST_3" ]]; then echo "✅ PASS: Image digest is stable" exit 0else echo "❌ FAIL: Image digest changed between pulls" echo " This could indicate tampering or registry issues" exit 1fiwhat it tests: Deterministic builds, prevents accidental mutations expected outcome: Same digest on repeated pulls failure investigation Confirm registry credentials are correct Check for in-flight image mutations (should not happen) Verify network connectivity is stable.
Test 1.5: Verify Image Layers Integrity
Confirm image structure matches expected layer count and sizes.
#!/bin/bash# tests/test-1.5-layer-integrity.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}EXPECTED_LAYERS=${2:-7} # Adjust based on image type echo "[1.5] Verifying image layer integrity for $IMAGE" ACTUAL_LAYERS=$(crane manifest $IMAGE | jq '.layers | length') echo " Expected layers: $EXPECTED_LAYERS"echo " Actual layers: $ACTUAL_LAYERS" if [[ $ACTUAL_LAYERS -ge $(($EXPECTED_LAYERS - 1)) ]]; then echo "✅ PASS: Image layer count is reasonable" # Show layer breakdown crane manifest $IMAGE | jq -r '.layers[] | "\(.digest | .[0:12]) \(.size / 1024 / 1024 | round) MB \(.mediaType)"' | \ sort -k2 -rn | head -5 exit 0else echo "❌ FAIL: Image layer count is suspiciously low" exit 1fiwhat it tests: Image structure consistency, detects stripped or corrupted images expected outcome: Expected layer count present failure investigation Layer count varies by image type (base OS, runtime, application) Compare against reference image: crane manifest $OLD_IMAGE | jq '.layers | length' If significantly different, may indicate incomplete build.
Test Category 2: Security Hardening Verification (8 Tests)
purpose: Confirm the image implements all security hardening controls.
when to run: After integrity verification passes.
pass criteria: All 8 tests pass (or documented exceptions).
Test 2.1: Confirm No Shell
Verify shell binaries have been removed from the image.
#!/bin/bash# tests/test-2.1-no-shell.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[2.1] Verifying absence of shell for $IMAGE" # Try to execute shell — should failif docker run --rm --entrypoint /bin/sh $IMAGE -c "echo fail" 2>&1 | grep -q "not found\|exec"; then echo "✅ PASS: No shell found in image" exit 0else echo "❌ FAIL: Shell appears to be present in image" exit 1fiwhat it tests: Reduced attack surface, prevents shell escape vulnerabilities expected outcome: /bin/sh not found why it matters: Even with non-root, shell access allows privilege escalation attacks.
additional verification.
# Inspect image for common shellsdocker run --rm --entrypoint find $IMAGE / -type f \ \( -name 'sh' -o -name 'bash' -o -name 'dash' -o -name 'zsh' \) 2>/dev/null # Should return no resultsTest 2.2: Confirm Non-Root User
Verify container runs as non-root UID (recommended: 65532).
#!/bin/bash# tests/test-2.2-non-root-user.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}EXPECTED_UID=65532 echo "[2.2] Verifying non-root user for $IMAGE" UID_OUTPUT=$(docker run --rm $IMAGE id -u) echo " Running as UID: $UID_OUTPUT" if [[ "$UID_OUTPUT" != "0" ]]; then echo "PASS: Image runs as non-root (UID $UID_OUTPUT)" if [[ "$UID_OUTPUT" == "$EXPECTED_UID" ]]; then echo " Matches CleanStart standard (UID 65532)" fi exit 0else echo "FAIL: Image runs as root (UID 0)" exit 1fiwhat it tests: Privilege isolation, limits damage from container escape expected outcome: UID 65532 (or other non-zero) why it matters: Root containers can write to host filesystem, install packages, modify system.
Test 2.3: Confirm Read-Only Root Filesystem
Verify filesystem is mounted read-only (Docker: read_only=true).
#!/bin/bash# tests/test-2.3-readonly-filesystem.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[2.3] Verifying read-only filesystem for $IMAGE" # Try to create a file in root — should failif docker run --rm $IMAGE touch /test-file 2>&1 | grep -q "Read-only\|Permission denied"; then echo "✅ PASS: Root filesystem is read-only" exit 0else docker run --rm $IMAGE touch /test-file 2>&1 echo "❌ FAIL: Root filesystem appears to be writable" exit 1fiwhat it tests: Immutability, prevents malware persistence expected outcome: Touch fails with "Read-only file system" why it matters: Read-only FS prevents rootkits, malware, unauthorized modifications.
Kubernetes annotation (recommended):
spec: securityContext: readOnlyRootFilesystem: trueTest 2.4: Confirm No Package Manager
Verify package manager (apk) has been removed.
#!/bin/bash# tests/test-2.4-no-package-manager.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[2.4] Verifying absence of package manager for $IMAGE" # Try to install package — should failif docker run --rm $IMAGE apk add curl 2>&1 | grep -q "not found\|command not found"; then echo "✅ PASS: Package manager removed from image" exit 0else echo "❌ FAIL: Package manager appears to be present" exit 1fiwhat it tests: Reduced supply chain risk, prevents package injection expected outcome: apk: command not found why it matters: No package manager = no ability to install backdoored packages.
Test 2.5: Verify Minimal SUID/SGID Binaries
Check for setuid/setgid binaries (should be minimal).
#!/bin/bash# tests/test-2.5-minimal-suid.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}MAX_SUID=5 # Threshold for "too many" echo "[2.5] Checking SUID/SGID binaries for $IMAGE" # Extract image to temp locationTEMPDIR=$(mktemp -d)docker run --rm -v $TEMPDIR:/target $IMAGE \ sh -c 'find / -perm /4000 -o -perm /2000 2>/dev/null | head -20' > $TEMPDIR/suid.txt SUID_COUNT=$(wc -l < $TEMPDIR/suid.txt) echo " SUID/SGID binaries found: $SUID_COUNT"echo " Expected threshold: < $MAX_SUID" if [[ $SUID_COUNT -le $MAX_SUID ]]; then echo "✅ PASS: Minimal SUID/SGID binaries" echo " Found:" head -5 $TEMPDIR/suid.txt | sed 's/^/ /' exit 0else echo "⚠️ WARNING: More SUID binaries than expected" head -10 $TEMPDIR/suid.txt | sed 's/^/ /' exit 1fi rm -rf $TEMPDIRwhat it tests: Privilege escalation surface area expected outcome: < 5 SUID binaries why it matters: SUID binaries can be exploited for privilege escalation even from non-root.
Test 2.6: Independent Vulnerability Scan (Trivy)
Run independent CVE scanner and compare against SBOM.
#!/bin/bash# tests/test-2.6-trivy-scan.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}SBOM_FILE=${2:-/tmp/acceptance-tests/sbom/sbom.spdx.json} echo "[2.6] Running Trivy vulnerability scan for $IMAGE" # Run Trivy scantrivy image --severity HIGH,CRITICAL \ --format json \ --output /tmp/acceptance-tests/logs/trivy-scan.json \ $IMAGE CRITICAL_COUNT=$(jq '[.Results[]? | select(.Vulnerabilities[]? | .Severity=="CRITICAL")] | length' \ /tmp/acceptance-tests/logs/trivy-scan.json) HIGH_COUNT=$(jq '[.Results[]? | select(.Vulnerabilities[]? | .Severity=="HIGH")] | length' \ /tmp/acceptance-tests/logs/trivy-scan.json) echo " Critical vulnerabilities: $CRITICAL_COUNT"echo " High vulnerabilities: $HIGH_COUNT" if [[ $CRITICAL_COUNT -eq 0 ]]; then echo "✅ PASS: No Critical vulnerabilities found" exit 0else echo "❌ FAIL: Found $CRITICAL_COUNT Critical vulnerabilities" jq '.Results[] | select(.Vulnerabilities[]? | .Severity=="CRITICAL") | .Vulnerabilities[]' \ /tmp/acceptance-tests/logs/trivy-scan.json exit 1fiwhat it tests: Known CVE detection, validates SBOM accuracy expected outcome: Zero Critical CVEs, minimal High CVEs why it matters: Confirms CleanStart's SBOM is accurate and up-to-date.
compare against reference.
# Scan old image for comparisontrivy image $OLD_IMAGE > /tmp/trivy-old.jsontrivy image $NEW_IMAGE > /tmp/trivy-new.json # Show improvementecho "Old image vulnerabilities:"jq '.Results | length' /tmp/trivy-old.json echo "New image vulnerabilities:"jq '.Results | length' /tmp/trivy-new.jsonTest 2.7: Verify FIPS Mode (FIPS Images Only)
For FIPS-variant images, confirm FIPS cryptographic modules are enabled.
#!/bin/bash# tests/test-2.7-fips-validation.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12-fips} # Only run for FIPS imagesif [[ ! "$IMAGE" =~ "fips" ]]; then echo "[2.7] Skipping FIPS validation (non-FIPS image)" exit 0fi echo "[2.7] Verifying FIPS mode for $IMAGE" FIPS_STATUS=$(docker run --rm $IMAGE openssl fips show 2>/dev/null || echo "0") echo " FIPS status: $FIPS_STATUS" if [[ "$FIPS_STATUS" == "1" ]]; then echo "✅ PASS: FIPS mode enabled" exit 0else echo "⚠️ WARNING: FIPS mode not enabled or openssl command failed" exit 1fiwhat it tests: FIPS 140-3 compliance expected outcome: openssl fips show returns 1 when to run: Only for FIPS-variant images.
Test 2.8: CIS Docker Benchmark Compliance
Run CIS Docker Benchmark audit (automated).
#!/bin/bash# tests/test-2.8-cis-benchmark.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[2.8] Running CIS Docker Benchmark scan for $IMAGE" # Use container-level audit tool (e.g., Benchmark Runner)# This is pseudo-code; actual tool variesdocker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy image --severity HIGH,CRITICAL \ --compliance docker-cis \ --format json \ --output /tmp/cis-benchmark.json \ $IMAGE PASSING=$(jq '.[] | select(.status=="PASS") | length' /tmp/cis-benchmark.json)FAILING=$(jq '.[] | select(.status=="FAIL") | length' /tmp/cis-benchmark.json) echo " Passing checks: $PASSING"echo " Failing checks: $FAILING" if [[ $FAILING -lt 3 ]]; then # Allow minor failures echo "✅ PASS: CIS Benchmark mostly compliant" exit 0else echo "⚠️ WARNING: Multiple CIS Benchmark failures" exit 1fiwhat it tests: CIS Docker Benchmark compliance expected outcome: 94/94 controls passing (or documented exceptions) reference: https://www.cisecurity.org/benchmark/docker.
Test Category 3: Functional Compatibility Testing (10 Tests)
purpose: Verify the image works correctly in YOUR specific environment.
when to run: After security hardening verification passes.
environment: Staging cluster (Kubernetes, Docker Compose, or isolated VM).
pass criteria: All 10 tests pass in your production-like environment.
Test 3.1: Application Startup
Deploy application using new image and verify it starts successfully.
#!/bin/bash# tests/test-3.1-app-startup.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}APP_MANIFEST=${2:-./manifests/app-deployment.yaml} echo "[3.1] Verifying application startup with $IMAGE" # Create temporary deployment with new imagekubectl set image deployment/test-app app=$IMAGE --record \ -f $APP_MANIFEST || kubectl apply -f $APP_MANIFEST # Wait for deployment to be readykubectl rollout status deployment/test-app --timeout=5m # Check pod logs for startup errorsLOGS=$(kubectl logs deployment/test-app --tail=20) if [[ $? -eq 0 ]] && ! echo "$LOGS" | grep -i "error\|fail\|critical"; then echo "✅ PASS: Application started successfully" echo "$LOGS" | head -5 exit 0else echo "❌ FAIL: Application failed to start" echo "$LOGS" | tail -20 exit 1fiwhat it tests: Basic functionality, image compatibility with application expected outcome: Pod reaches Running state, no error logs debugging.
kubectl describe pod deployment/test-appkubectl logs deployment/test-appTest 3.2: Health Check Endpoint
Verify application health check succeeds.
#!/bin/bash# tests/test-3.2-health-check.sh HEALTH_URL=${1:-http://localhost:8080/health}TIMEOUT=30 echo "[3.2] Testing health check endpoint: $HEALTH_URL" for i in {1..$TIMEOUT}; do RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_URL) if [[ "$RESPONSE" == "200" ]]; then echo "✅ PASS: Health check successful (HTTP $RESPONSE)" exit 0 fi echo " Attempt $i/$TIMEOUT: HTTP $RESPONSE (retrying...)" sleep 1done echo "❌ FAIL: Health check failed after $TIMEOUT seconds"exit 1what it tests: Application readiness, basic functionality expected outcome: HTTP 200 response timeout: Adjust based on application startup time (default: 30 seconds).
Test 3.3: Database Connectivity
Verify application can connect to configured database.
#!/bin/bash# tests/test-3.3-database-connectivity.sh DB_TYPE=${1:-postgresql} # postgresql, mysql, mongodbDB_HOST=${2:-localhost}DB_PORT=${3:-5432}DB_NAME=${4:-testdb} echo "[3.3] Testing $DB_TYPE connectivity ($DB_HOST:$DB_PORT)" case $DB_TYPE in postgresql) docker run --rm postgres:15-alpine \ psql -h $DB_HOST -U postgres -d $DB_NAME -c "SELECT 1" 2>/dev/null && \ echo "✅ PASS: PostgreSQL connection successful" && exit 0 ;; mysql) docker run --rm mysql:8 \ mysql -h $DB_HOST -u root -p$MYSQL_ROOT_PASSWORD -e "SELECT 1" 2>/dev/null && \ echo "✅ PASS: MySQL connection successful" && exit 0 ;; mongodb) docker run --rm mongo:6 \ mongosh mongodb://$DB_HOST:27017/$DB_NAME --eval "db.adminCommand('ping')" 2>/dev/null && \ echo "✅ PASS: MongoDB connection successful" && exit 0 ;;esac echo "❌ FAIL: Database connection failed"exit 1what it tests: External service connectivity, network configuration expected outcome: Successful database connection from container prerequisites: Database running and accessible from test environment.
Test 3.4: Cache Connectivity
Verify application can connect to configured cache (Redis, Memcached).
#!/bin/bash# tests/test-3.4-cache-connectivity.sh CACHE_TYPE=${1:-redis}CACHE_HOST=${2:-localhost}CACHE_PORT=${3:-6379} echo "[3.4] Testing $CACHE_TYPE connectivity ($CACHE_HOST:$CACHE_PORT)" case $CACHE_TYPE in redis) docker run --rm redis:7 \ redis-cli -h $CACHE_HOST -p $CACHE_PORT ping && \ echo "✅ PASS: Redis connection successful" && exit 0 ;; memcached) docker run --rm memcached:1.6 \ memcached-tool $CACHE_HOST:$CACHE_PORT get key 2>/dev/null && \ echo "✅ PASS: Memcached connection successful" && exit 0 ;;esac echo "❌ FAIL: Cache connection failed"exit 1what it tests: Cache layer connectivity, data layer integration expected outcome: Successful ping/connection prerequisites: Cache running and accessible from test environment.
Test 3.5: External API Calls
Verify application can make outbound HTTPS calls (TLS verification, DNS resolution).
#!/bin/bash# tests/test-3.5-external-api-calls.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[3.5] Testing external API calls from $IMAGE" # Test HTTPS connectivitydocker run --rm $IMAGE \ wget -q -O - https://api.github.com/repos/google/go-containerregistry \ >/dev/null 2>&1 && \ echo "✅ PASS: External HTTPS API call successful" && exit 0 # Alternative: curldocker run --rm $IMAGE \ curl -s https://api.github.com/repos/google/go-containerregistry >/dev/null 2>&1 && \ echo "✅ PASS: External API call successful" && exit 0 echo "❌ FAIL: External API call failed"echo " Check: DNS resolution, TLS certificates, firewall rules"exit 1what it tests: Network connectivity, TLS certificate validation, DNS resolution expected outcome: Successful outbound HTTPS call prerequisites: Network access to public APIs, DNS resolution working.
Test 3.6: File I/O on Mounted Volumes
Verify application can read/write to mounted volumes (respects read-only FS).
#!/bin/bash# tests/test-3.6-volume-mount-io.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}VOLUME_PATH=${2:-/mnt/data} echo "[3.6] Testing file I/O on mounted volume: $VOLUME_PATH" # Create test volumemkdir -p /tmp/test-volumeecho "test-data-12345" > /tmp/test-volume/test.txt # Mount volume in container and read/writedocker run --rm -v /tmp/test-volume:$VOLUME_PATH:rw \ $IMAGE cat $VOLUME_PATH/test.txt | grep -q "test-data-12345" && \ echo "✅ PASS: File I/O on mounted volume successful" && exit 0 echo "❌ FAIL: File I/O on mounted volume failed"exit 1what it tests: Volume mount functionality, read/write permissions expected outcome: Can read from and write to mounted volume note: Root filesystem is read-only, but mounted volumes can be writable.
Test 3.7: Environment Variable Injection
Verify application receives environment variables correctly.
#!/bin/bash# tests/test-3.7-env-vars.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[3.7] Testing environment variable injection" # Run with environment variablesENV_VALUE=$(docker run --rm \ -e TEST_VAR="hello-world-12345" \ $IMAGE \ printenv TEST_VAR) if [[ "$ENV_VALUE" == "hello-world-12345" ]]; then echo "✅ PASS: Environment variables injected correctly" exit 0else echo "❌ FAIL: Environment variables not set correctly" echo " Expected: hello-world-12345" echo " Got: $ENV_VALUE" exit 1fiwhat it tests: Container environment configuration expected outcome: Environment variables accessible to application kubernetes variant: ConfigMap and Secret injection.
Test 3.8: Secrets Injection (Kubernetes Secrets or Vault)
Verify application can access injected secrets.
#!/bin/bash# tests/test-3.8-secrets-injection.sh SECRET_NAME=${1:-test-secret}SECRET_KEY=${2:-api-key} echo "[3.8] Testing secrets injection from Kubernetes Secret: $SECRET_NAME" # Create test secretkubectl create secret generic $SECRET_NAME \ --from-literal=$SECRET_KEY="secret-value-12345" \ --dry-run=client -o yaml | kubectl apply -f - # Create pod that mounts secretkubectl run test-pod \ --image=registry.cleanstart.com/cleanstart/python:3.12 \ --overrides='{ "spec": { "containers": [{ "name": "test-pod", "volumeMounts": [{ "name": "secret-volume", "mountPath": "/etc/secrets" }] }], "volumes": [{ "name": "secret-volume", "secret": { "secretName": "'$SECRET_NAME'" } }] } }' # Check secret is mountedSECRET_VALUE=$(kubectl exec test-pod -- cat /etc/secrets/$SECRET_KEY) if [[ "$SECRET_VALUE" == "secret-value-12345" ]]; then echo "✅ PASS: Secrets injected successfully" exit 0else echo "❌ FAIL: Secret not mounted correctly" exit 1fiwhat it tests: Secret management integration, Kubernetes Secret/Vault access expected outcome: Application can read mounted secrets prerequisites: Kubernetes cluster with Secret management.
Test 3.9: Timezone and Locale Settings
Verify timezone and locale configurations work correctly.
#!/bin/bash# tests/test-3.9-timezone-locale.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[3.9] Testing timezone and locale settings" # Test timezoneTZ_OUTPUT=$(docker run --rm \ -e TZ=America/New_York \ $IMAGE \ date +%Z) if [[ ! -z "$TZ_OUTPUT" ]]; then echo "✅ PASS: Timezone setting works (TZ=$TZ_OUTPUT)"else echo "⚠️ WARNING: Timezone may not be set correctly"fi # Test locale (may be limited in minimal image)LOCALE_OUTPUT=$(docker run --rm \ -e LANG=en_US.UTF-8 \ $IMAGE \ locale 2>/dev/null || echo "locale not available") echo " Locale: $LOCALE_OUTPUT"exit 0what it tests: Timezone/locale configuration expected outcome: Timezone settings apply correctly note: Minimal images may have limited locale support (expected).
Test 3.10: Logging and Stdout Capture
Verify application logs are captured correctly by container logging driver.
#!/bin/bash# tests/test-3.10-logging.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[3.10] Testing logging and stdout capture" # Create test app that logs to stdoutdocker run --rm $IMAGE \ sh -c "echo 'application-log-12345'; sleep 1" > /tmp/test-logs.txt 2>&1 if grep -q "application-log-12345" /tmp/test-logs.txt; then echo "✅ PASS: Logs captured to stdout successfully" exit 0else echo "❌ FAIL: Logs not captured correctly" echo " Output:" cat /tmp/test-logs.txt exit 1fiwhat it tests: Container logging, stdout/stderr capture expected outcome: Logs appear in container logging system note: Essential for observability and debugging.
Test Category 4: Performance Baseline Testing (5 Tests)
purpose: Establish performance metrics and detect regressions.
when to run: After functional compatibility passes.
environment: Staging cluster (consistent hardware as production).
Test 4.1: Container Startup Time
Measure time from container start to application ready.
#!/bin/bash# tests/test-4.1-startup-time.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}RUNS=${2:-5} echo "[4.1] Measuring container startup time (${RUNS} runs)" TIMES=() for i in $(seq 1 $RUNS); do START=$(date +%s%N) docker run --rm -d $IMAGE sleep 100 > /tmp/container-id.txt CONTAINER_ID=$(cat /tmp/container-id.txt) # Wait for container to be ready docker exec $CONTAINER_ID true 2>/dev/null || true sleep 1 END=$(date +%s%N) ELAPSED_MS=$(( ($END - $START) / 1000000 )) TIMES+=($ELAPSED_MS) docker stop $CONTAINER_ID >/dev/null 2>&1 echo " Run $i: ${ELAPSED_MS}ms"done # Calculate averageAVERAGE=$(( $(IFS=+; echo "${TIMES[*]}") / ${#TIMES[@]} )) echo "✅ Average startup time: ${AVERAGE}ms"echo " Expected baseline: < 5000ms" if [[ $AVERAGE -lt 5000 ]]; then echo "✅ PASS: Startup time within acceptable range" exit 0else echo "⚠️ WARNING: Startup time higher than baseline" exit 1fiwhat it tests: Container initialization overhead, performance regression baseline: Typical range 1000-3000ms for application images compare: Against previous version to detect regressions.
Test 4.2: Memory Footprint
Measure memory usage at steady state.
#!/bin/bash# tests/test-4.2-memory-footprint.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}RUNS=${2:-5} echo "[4.2] Measuring memory footprint (${RUNS} samples)" # Start containerdocker run --rm -d --name test-app $IMAGE sleep 600 > /tmp/container-id.txtCONTAINER_ID=$(cat /tmp/container-id.txt) sleep 3 # Allow container to stabilize MEMORY_SAMPLES=() for i in $(seq 1 $RUNS); do MEM_MB=$(docker stats --no-stream $CONTAINER_ID --format "{{.MemUsage}}" | \ grep -o '^[0-9\.]*' | awk '{print int($1)}') MEMORY_SAMPLES+=($MEM_MB) echo " Sample $i: ${MEM_MB}MB" sleep 2done # Calculate averageAVERAGE=$(( $(IFS=+; echo "${MEMORY_SAMPLES[*]}") / ${#MEMORY_SAMPLES[@]} )) docker stop $CONTAINER_ID >/dev/null 2>&1 echo "✅ Average memory: ${AVERAGE}MB"echo " Baseline: record for comparison" # Store baseline for comparison with future versionsecho "${AVERAGE}MB" > /tmp/memory-baseline.txtexit 0what it tests: Memory footprint, resource efficiency expected outcome: Record baseline, compare against previous version typical range: 50-300MB for application images.
Test 4.3: CPU Usage
Measure CPU utilization under load.
#!/bin/bash# tests/test-4.3-cpu-usage.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}DURATION=30 echo "[4.3] Measuring CPU usage under load (${DURATION}s)" # Start containerdocker run --rm -d --name test-app $IMAGE \ sh -c "while true; do sha256sum /dev/urandom; done" > /tmp/container-id.txt CONTAINER_ID=$(cat /tmp/container-id.txt) sleep 2 # Allow container to stabilize # Measure CPUCPU_AVG=$(docker stats --no-stream $CONTAINER_ID --format "{{.CPUPerc}}" | \ grep -o '[0-9\.]*' | head -1) docker stop $CONTAINER_ID >/dev/null 2>&1 echo "✅ Average CPU usage: ${CPU_AVG}%"echo " Expected: record for baseline comparison" exit 0what it tests: CPU efficiency, resource contention expected outcome: Record baseline for comparison note: CPU measurement varies; focus on relative comparison against baseline.
Test 4.4: Request Latency
Measure application request latency (p50, p95, p99).
#!/bin/bash# tests/test-4.4-request-latency.sh ENDPOINT=${1:-http://localhost:8080/api/health}REQUESTS=${2:-100} echo "[4.4] Measuring request latency (${REQUESTS} requests)" # Start server (pseudo-code; adjust for your application)# docker run -d -p 8080:8080 $IMAGE LATENCIES=() for i in $(seq 1 $REQUESTS); do START=$(date +%s%N) curl -s -o /dev/null $ENDPOINT END=$(date +%s%N) LATENCY_MS=$(( ($END - $START) / 1000000 )) LATENCIES+=($LATENCY_MS)done # Sort and calculate percentilesIFS=$'\n' SORTED=($(sort -n <<<"${LATENCIES[*]}")) P50_IDX=$(( ${#SORTED[@]} / 2 ))P95_IDX=$(( ${#SORTED[@]} * 95 / 100 ))P99_IDX=$(( ${#SORTED[@]} * 99 / 100 )) echo "✅ PASS: Request latency measured"echo " P50: ${SORTED[$P50_IDX]}ms"echo " P95: ${SORTED[$P95_IDX]}ms"echo " P99: ${SORTED[$P99_IDX]}ms" exit 0what it tests: Application response time, performance regression baselines: Varies by application; establish baseline from first run.
Test 4.5: Throughput Under Load
Measure requests per second under sustained load.
#!/bin/bash# tests/test-4.5-throughput.sh ENDPOINT=${1:-http://localhost:8080/api/health}CONCURRENCY=${2:-10}DURATION=${3:-30} echo "[4.5] Measuring throughput ($CONCURRENCY concurrent, ${DURATION}s)" # Use Apache Bench or equivalentab -c $CONCURRENCY -t $DURATION -q $ENDPOINT > /tmp/ab-results.txt 2>&1 # Extract metricsRPS=$(grep "Requests per second" /tmp/ab-results.txt | awk '{print $4}')AVG_TIME=$(grep "Time per request" /tmp/ab-results.txt | head -1 | awk '{print $4}') echo "✅ PASS: Throughput measured"echo " Requests/sec: ${RPS}"echo " Avg response: ${AVG_TIME}ms" exit 0what it tests: Application scalability, sustained load handling tool: Apache Bench, wrk, or equivalent load testing tool baseline: Record for comparison against future versions.
Test Category 5: Compliance Verification (5 Tests)
purpose: Confirm compliance artifacts are present and valid.
when to run: Throughout testing as reference checks.
Test 5.1: CIS Docker Benchmark Comprehensive
Full CIS Docker Benchmark assessment.
#!/bin/bash# tests/test-5.1-cis-benchmark-full.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12} echo "[5.1] Full CIS Docker Benchmark Assessment" # Run comprehensive benchmarkdocker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy image --severity HIGH,CRITICAL \ --compliance docker-cis \ --format sarif \ --output /tmp/cis-results.sarif \ $IMAGE # Parse resultsPASSING=$(jq '[.runs[].results[] | select(.level=="pass")] | length' /tmp/cis-results.sarif)FAILING=$(jq '[.runs[].results[] | select(.level!="pass")] | length' /tmp/cis-results.sarif) echo " Passing: $PASSING"echo " Failing: $FAILING" echo "✅ CIS Benchmark assessment complete"exit 0Test 5.2: SBOM Completeness Check
Verify SBOM has sufficient component coverage.
#!/bin/bash# tests/test-5.2-sbom-completeness.sh SBOM_FILE=${1:-/tmp/acceptance-tests/sbom/sbom.spdx.json} echo "[5.2] Verifying SBOM completeness" # Extract and decode SBOM payloadCOMPONENTS=$(jq '.payload | @base64d | fromjson | .components | length' $SBOM_FILE)METADATA=$(jq '.payload | @base64d | fromjson | .creationInfo' $SBOM_FILE) echo " Components listed: $COMPONENTS"echo " Creation timestamp: $(echo $METADATA | jq -r '.created')" if [[ $COMPONENTS -gt 10 ]]; then echo "✅ PASS: SBOM is reasonably complete ($COMPONENTS components)" exit 0else echo "⚠️ WARNING: SBOM appears sparse ($COMPONENTS components)" exit 1fiTest 5.3: Image Size Validation
Confirm image size is within expected range (efficiency indicator).
#!/bin/bash# tests/test-5.3-image-size.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}MAX_SIZE_MB=${2:-300} echo "[5.3] Verifying image size" # Get image sizeSIZE_BYTES=$(docker inspect --format='{{.Size}}' $IMAGE)SIZE_MB=$(( $SIZE_BYTES / 1024 / 1024 )) echo " Image size: ${SIZE_MB}MB"echo " Expected max: ${MAX_SIZE_MB}MB" if [[ $SIZE_MB -le $MAX_SIZE_MB ]]; then echo "✅ PASS: Image size is reasonable" exit 0else echo "⚠️ WARNING: Image size larger than expected" echo " Compare against previous version to detect bloat" exit 1fiTest 5.4: CVE Threshold Validation
Verify vulnerability count is below threshold.
#!/bin/bash# tests/test-5.4-cve-threshold.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}MAX_CRITICAL=${2:-0}MAX_HIGH=${3:-5} echo "[5.4] Validating CVE thresholds" # Run scantrivy image --severity HIGH,CRITICAL --format json \ --output /tmp/cves.json $IMAGE CRITICAL=$(jq '[.Results[]? | .Vulnerabilities[]? | select(.Severity=="CRITICAL")] | length' /tmp/cves.json)HIGH=$(jq '[.Results[]? | .Vulnerabilities[]? | select(.Severity=="HIGH")] | length' /tmp/cves.json) echo " Critical: $CRITICAL (max allowed: $MAX_CRITICAL)"echo " High: $HIGH (max allowed: $MAX_HIGH)" if [[ $CRITICAL -le $MAX_CRITICAL ]] && [[ $HIGH -le $MAX_HIGH ]]; then echo "✅ PASS: CVE count within acceptable thresholds" exit 0else echo "❌ FAIL: CVE count exceeds thresholds" exit 1fiTest 5.5: Signature and Attestation Chain Validation
Comprehensive verification of cryptographic chain.
#!/bin/bash# tests/test-5.5-crypto-chain.sh IMAGE=${1:-registry.cleanstart.com/cleanstart/python:3.12}COSIGN_KEY=${2:-cosign.pub} echo "[5.5] Validating cryptographic signature chain" # Verify signatureecho "[5.5.1] Verifying image signature..."if cosign verify --key $COSIGN_KEY $IMAGE > /dev/null 2>&1; then echo " ✅ Image signature valid"else echo " ❌ Image signature invalid" exit 1fi # Verify SBOM attestationecho "[5.5.2] Verifying SBOM attestation..."if cosign verify-attestation --key $COSIGN_KEY --type spdxjson $IMAGE > /dev/null 2>&1; then echo " ✅ SBOM attestation valid"else echo " ❌ SBOM attestation invalid" exit 1fi # Verify SLSA provenanceecho "[5.5.3] Verifying SLSA provenance..."if cosign verify-attestation --key $COSIGN_KEY --type slsaprovenance $IMAGE > /dev/null 2>&1; then echo " ✅ SLSA provenance valid"else echo " ⚠️ SLSA provenance not found (expected for older images)"fi echo "✅ PASS: Cryptographic chain validated"exit 0Automation: GitHub Actions Workflow
Complete CI/CD workflow for running all acceptance tests:
# .github/workflows/acceptance-tests.yml name: Acceptance Tests on: workflow_dispatch: inputs: new_image: required: true description: "New image to test (e.g., registry.cleanstart.com/cleanstart/python:3.12)" old_image: required: false description: "Previous image for comparison (optional)" jobs: acceptance-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install tools run: | brew install cosign crane trivy jq - name: Get Cosign key run: | curl -O https://cleanstart.dev/keys/cosign.pub - name: Test 1: Image Integrity run: | bash tests/test-1.1-cosign-signature.sh ${{ github.event.inputs.new_image }} bash tests/test-1.2-sbom-attestation.sh ${{ github.event.inputs.new_image }} bash tests/test-1.3-slsa-provenance.sh ${{ github.event.inputs.new_image }} bash tests/test-1.4-digest-stability.sh ${{ github.event.inputs.new_image }} bash tests/test-1.5-layer-integrity.sh ${{ github.event.inputs.new_image }} - name: Test 2: Security Hardening run: | bash tests/test-2.1-no-shell.sh ${{ github.event.inputs.new_image }} bash tests/test-2.2-non-root-user.sh ${{ github.event.inputs.new_image }} bash tests/test-2.3-readonly-filesystem.sh ${{ github.event.inputs.new_image }} bash tests/test-2.4-no-package-manager.sh ${{ github.event.inputs.new_image }} bash tests/test-2.5-minimal-suid.sh ${{ github.event.inputs.new_image }} bash tests/test-2.6-trivy-scan.sh ${{ github.event.inputs.new_image }} bash tests/test-2.7-fips-validation.sh ${{ github.event.inputs.new_image }} bash tests/test-2.8-cis-benchmark.sh ${{ github.event.inputs.new_image }} - name: Test 5: Compliance run: | bash tests/test-5.1-cis-benchmark-full.sh ${{ github.event.inputs.new_image }} bash tests/test-5.2-sbom-completeness.sh bash tests/test-5.3-image-size.sh ${{ github.event.inputs.new_image }} bash tests/test-5.4-cve-threshold.sh ${{ github.event.inputs.new_image }} bash tests/test-5.5-crypto-chain.sh ${{ github.event.inputs.new_image }} - name: Comparison Report if: ${{ github.event.inputs.old_image }} run: | echo "## Image Comparison" echo "| Metric | Old | New |" echo "|--------|-----|-----|" OLD_SIZE=$(docker inspect --format='{{.Size}}' ${{ github.event.inputs.old_image }} | awk '{print $1/1024/1024 " MB"}') NEW_SIZE=$(docker inspect --format='{{.Size}}' ${{ github.event.inputs.new_image }} | awk '{print $1/1024/1024 " MB"}') echo "| Image Size | $OLD_SIZE | $NEW_SIZE |" - name: Upload test results if: always() uses: actions/upload-artifact@v3 with: name: acceptance-test-results path: /tmp/acceptance-tests/logs/ - name: Post results comment if: always() uses: actions/github-script@v7 with: script: | const fs = require('fs'); const logs = fs.readdirSync('/tmp/acceptance-tests/logs/'); let comment = '## Acceptance Test Results\n'; comment += `Image: ${{ github.event.inputs.new_image }}\n`; comment += `Status: ✅ All tests passed\n`; github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: comment });Acceptance Test Report Template
Save test results in this format for audit trail:
# Acceptance Test Report **Image**: `registry.cleanstart.com/cleanstart/python:3.12-build20250315`**Date**: 2025-03-15**Tested By**: Jane Smith (QA Lead)**Test Environment**: Staging K8s cluster (us-west-2) ## Summary | Category | Tests | Passed | Failed | Status ||----------|-------|--------|--------|--------|| Integrity | 5 | 5 | 0 | ✅ || Security Hardening | 8 | 8 | 0 | ✅ || Functional | 10 | 10 | 0 | ✅ || Performance | 5 | 5 | 0 | ✅ || Compliance | 5 | 5 | 0 | ✅ || **TOTAL** | **33** | **33** | **0** | **✅ PASS** | ## Detailed Results ### Category 1: Image Integrity- [x] 1.1 Cosign signature verification- [x] 1.2 SBOM attestation- [x] 1.3 SLSA provenance- [x] 1.4 Digest stability- [x] 1.5 Layer integrity ### Category 2: Security Hardening- [x] 2.1 No shell- [x] 2.2 Non-root user (UID 65532)- [x] 2.3 Read-only filesystem- [x] 2.4 No package manager- [x] 2.5 Minimal SUID binaries (4 found)- [x] 2.6 Trivy scan (0 Critical, 2 High)- [x] 2.7 FIPS validation (N/A for non-FIPS image)- [x] 2.8 CIS Benchmark (94/94 controls) ### Category 3: Functional Compatibility- [x] 3.1 Application startup- [x] 3.2 Health check endpoint (HTTP 200)- [x] 3.3 PostgreSQL connectivity- [x] 3.4 Redis connectivity- [x] 3.5 External API calls- [x] 3.6 Volume mount I/O- [x] 3.7 Environment variable injection- [x] 3.8 Kubernetes Secret injection- [x] 3.9 Timezone/locale- [x] 3.10 Logging/stdout ### Category 4: Performance- [x] 4.1 Startup time: 1,240ms (baseline: 1,150ms) ⚠️ +7.8% regression- [x] 4.2 Memory footprint: 87MB (baseline: 82MB)- [x] 4.3 CPU usage: 45% (under load)- [x] 4.4 Request latency p95: 23ms- [x] 4.5 Throughput: 1,245 RPS ### Category 5: Compliance- [x] 5.1 CIS Docker Benchmark: 94/94 controls passing- [x] 5.2 SBOM completeness: 156 components- [x] 5.3 Image size: 127MB (within 300MB threshold)- [x] 5.4 CVE threshold: 0 Critical, 2 High (within 0/5 threshold)- [x] 5.5 Signature chain: all valid ## Comparison vs. Previous Build (build20250228) | Metric | Old | New | Delta ||--------|-----|-----|-------|| Size | 125MB | 127MB | +1.6% || Startup | 1,150ms | 1,240ms | +7.8% || Memory | 82MB | 87MB | +6.1% || CVEs (Critical) | 0 | 0 | — || CVEs (High) | 2 | 2 | — | Performance regression minor (+7.8% startup); within acceptable 10% threshold. ## Approval This image is **APPROVED FOR PRODUCTION**. - **QA Sign-off**: Jane Smith, 2025-03-15- **Security Review**: John Doe, 2025-03-15- **Promoted to**: production/python:3.12 (2025-03-16)Escalation Process
What to do when a test fails:
Step 1: Determine Root Cause (15 minutes)
Failure Type | Investigation | Common Cause |
|---|---|---|
Signature verification fails | Check cosign.pub is current, verify image digest | Image tampered with or unsigned |
Startup fails | Review pod logs, check dependencies | App incompatibility with image environment |
Health check timeout | Check pod status, verify port config | Application not listening on configured port |
Database connectivity fails | Ping database from pod, check credentials | Network isolation, wrong connection string |
Shell exec fails | Expected behavior (feature, not bug) | Shell has been successfully removed |
Step 2: Environmental vs. Image Issue
Environmental Issue (don't block promotion): Network unreachable Database not running Wrong credentials provided Test environment misconfiguration. Image Issue (block promotion): Application won't start Security check failed CVE threshold exceeded Signature invalid.
Step 3: Escalation Path
- QA Engineer → Run root cause analysis (Step 1)
- QA Lead → If environmental, fix environment; if image issue, escalate
- Engineering Lead → Engage with CleanStart support if image defect
- CTO → Final approval decision for exceptions
Step 4: Report to CleanStart (if image defect)
When creating issue with CleanStart support:
Subject: Acceptance Test Failure: [Image Name] [Build Date] Image: registry.cleanstart.com/cleanstart/python:3.12-build20250315Failed Test: test-3.2-health-check (application health check timeout)Environment: Kubernetes 1.28 on AWS ECSError Details: [paste test output]Reproducibility: 100% (consistent failure)Workaround: [if any] Steps to Reproduce:1. Pull image: docker pull registry.cleanstart.com/cleanstart/python:3.12-build202503152. Run test: bash tests/test-3.2-health-check.sh3. Expected: HTTP 200 within 30s4. Actual: timeout after 30sWhat to Read Next
vendor-risk-assessment.md: Understand CleanStart as a vendor before testing 78-test-inspection-suite.md: CleanStart's internal testing (confidence in builds) regression-testing-strategy.md: How to test upgrade paths and regressions performance-baseline-testing.md: Detailed performance measurement and analysis troubleshooting.md: Common test failures and solutions.
document version: 1.0 last updated: 2025-03-22 next review: 2025-09-22 owner: QA Team.
