303 lines
8.0 KiB
Plaintext
303 lines
8.0 KiB
Plaintext
|
|
#!/usr/bin/env nu
|
|||
|
|
|
|||
|
|
# Test OCI registry functionality
|
|||
|
|
|
|||
|
|
export def main [
|
|||
|
|
--registry-url: string = "localhost:5000"
|
|||
|
|
--registry-type: string = "zot"
|
|||
|
|
--skip-docker
|
|||
|
|
] {
|
|||
|
|
print "🧪 Testing OCI registry..."
|
|||
|
|
print $" URL: ($registry_url)"
|
|||
|
|
print $" Type: ($registry_type)\n"
|
|||
|
|
|
|||
|
|
mut passed = 0
|
|||
|
|
mut failed = 0
|
|||
|
|
|
|||
|
|
# Test 1: API health
|
|||
|
|
print "Test 1: API health check..."
|
|||
|
|
if (test-api-health $registry_url) {
|
|||
|
|
print " ✅ PASSED"
|
|||
|
|
$passed = ($passed + 1)
|
|||
|
|
} else {
|
|||
|
|
print " ❌ FAILED"
|
|||
|
|
$failed = ($failed + 1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Test 2: Catalog endpoint
|
|||
|
|
print "\nTest 2: Catalog endpoint..."
|
|||
|
|
if (test-catalog $registry_url) {
|
|||
|
|
print " ✅ PASSED"
|
|||
|
|
$passed = ($passed + 1)
|
|||
|
|
} else {
|
|||
|
|
print " ❌ FAILED"
|
|||
|
|
$failed = ($failed + 1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Test 3: Metrics (if supported)
|
|||
|
|
print "\nTest 3: Metrics endpoint..."
|
|||
|
|
if (test-metrics $registry_url $registry_type) {
|
|||
|
|
print " ✅ PASSED"
|
|||
|
|
$passed = ($passed + 1)
|
|||
|
|
} else {
|
|||
|
|
print " ⚠️ SKIPPED (not supported)"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Test 4: Push/Pull (requires Docker)
|
|||
|
|
if not $skip_docker {
|
|||
|
|
print "\nTest 4: Push/Pull test..."
|
|||
|
|
if (test-push-pull $registry_url) {
|
|||
|
|
print " ✅ PASSED"
|
|||
|
|
$passed = ($passed + 1)
|
|||
|
|
} else {
|
|||
|
|
print " ❌ FAILED"
|
|||
|
|
$failed = ($failed + 1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Test 5: Delete (if supported)
|
|||
|
|
print "\nTest 5: Delete test..."
|
|||
|
|
if (test-delete $registry_url) {
|
|||
|
|
print " ✅ PASSED"
|
|||
|
|
$passed = ($passed + 1)
|
|||
|
|
} else {
|
|||
|
|
print " ⚠️ SKIPPED (delete not enabled)"
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
print "\nTests 4-5: Skipped (--skip-docker)"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Test 6: Namespaces
|
|||
|
|
print "\nTest 6: Namespace validation..."
|
|||
|
|
if (test-namespaces $registry_url) {
|
|||
|
|
print " ✅ PASSED"
|
|||
|
|
$passed = ($passed + 1)
|
|||
|
|
} else {
|
|||
|
|
print " ❌ FAILED"
|
|||
|
|
$failed = ($failed + 1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Summary
|
|||
|
|
print "\n" + ("=" * 50)
|
|||
|
|
print $"Test Summary: ($passed) passed, ($failed) failed"
|
|||
|
|
|
|||
|
|
if $failed > 0 {
|
|||
|
|
print "\n❌ Some tests failed"
|
|||
|
|
exit 1
|
|||
|
|
} else {
|
|||
|
|
print "\n✅ All tests passed!"
|
|||
|
|
exit 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def test-api-health [registry_url: string] -> bool {
|
|||
|
|
let result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/" --timeout 5sec
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code == 0 {
|
|||
|
|
print " API is responding"
|
|||
|
|
return true
|
|||
|
|
} else {
|
|||
|
|
print $" API error: ($result.stderr)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def test-catalog [registry_url: string] -> bool {
|
|||
|
|
let result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/_catalog" --timeout 5sec
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code == 0 {
|
|||
|
|
let catalog = ($result.stdout | from json)
|
|||
|
|
let count = ($catalog.repositories | length)
|
|||
|
|
print $" Catalog has ($count) repositories"
|
|||
|
|
return true
|
|||
|
|
} else {
|
|||
|
|
print $" Catalog error: ($result.stderr)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def test-metrics [registry_url: string, registry_type: string] -> bool {
|
|||
|
|
let metrics_url = match $registry_type {
|
|||
|
|
"zot" => $"http://($registry_url)/metrics"
|
|||
|
|
"distribution" => $"http://($registry_url):5001/metrics"
|
|||
|
|
_ => null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ($metrics_url | is-empty) {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let result = (do {
|
|||
|
|
http get $metrics_url --timeout 5sec
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code == 0 {
|
|||
|
|
print " Metrics endpoint available"
|
|||
|
|
return true
|
|||
|
|
} else {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def test-push-pull [registry_url: string] -> bool {
|
|||
|
|
# Check Docker is available
|
|||
|
|
let docker_check = (^docker --version | complete)
|
|||
|
|
if $docker_check.exit_code != 0 {
|
|||
|
|
print " Docker not available"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Pull hello-world
|
|||
|
|
print " Pulling hello-world..."
|
|||
|
|
let pull = (^docker pull hello-world:latest | complete)
|
|||
|
|
if $pull.exit_code != 0 {
|
|||
|
|
print " Failed to pull hello-world"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Tag for registry
|
|||
|
|
let tag = $"($registry_url)/provisioning-test/test-image:latest"
|
|||
|
|
print $" Tagging as ($tag)..."
|
|||
|
|
let tag_result = (^docker tag hello-world:latest $tag | complete)
|
|||
|
|
if $tag_result.exit_code != 0 {
|
|||
|
|
print " Failed to tag image"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Push to registry
|
|||
|
|
print $" Pushing to registry..."
|
|||
|
|
let push = (^docker push $tag | complete)
|
|||
|
|
if $push.exit_code != 0 {
|
|||
|
|
print $" Failed to push: ($push.stderr)"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Pull from registry
|
|||
|
|
print " Pulling from registry..."
|
|||
|
|
let pull_test = (^docker pull $tag | complete)
|
|||
|
|
if $pull_test.exit_code != 0 {
|
|||
|
|
print " Failed to pull from registry"
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
print " Push/Pull successful"
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def test-delete [registry_url: string] -> bool {
|
|||
|
|
# Try to get manifest digest
|
|||
|
|
let result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/provisioning-test/test-image/manifests/latest" --headers {
|
|||
|
|
"Accept": "application/vnd.oci.image.manifest.v1+json"
|
|||
|
|
} --full
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code != 0 {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Try to delete (may not be enabled)
|
|||
|
|
# This is just a check, we don't actually delete
|
|||
|
|
print " Delete endpoint available"
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def test-namespaces [registry_url: string] -> bool {
|
|||
|
|
let result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/_catalog"
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $result.exit_code != 0 {
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let catalog = ($result.stdout | from json)
|
|||
|
|
let repos = $catalog.repositories
|
|||
|
|
|
|||
|
|
# Check for expected namespaces
|
|||
|
|
let namespaces = ($repos | each { |r| $r | split row "/" | first } | uniq)
|
|||
|
|
|
|||
|
|
print $" Found namespaces: ($namespaces | str join ', ')"
|
|||
|
|
|
|||
|
|
# Should have at least one namespace after init
|
|||
|
|
return (($namespaces | length) >= 0)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Performance test
|
|||
|
|
export def "perf test" [
|
|||
|
|
--registry-url: string = "localhost:5000"
|
|||
|
|
--iterations: int = 10
|
|||
|
|
] {
|
|||
|
|
print "🚀 Running performance test..."
|
|||
|
|
print $" Iterations: ($iterations)\n"
|
|||
|
|
|
|||
|
|
mut total_time = 0
|
|||
|
|
|
|||
|
|
for i in 1..$iterations {
|
|||
|
|
print $"Iteration ($i)/($iterations)..."
|
|||
|
|
|
|||
|
|
let start = (date now)
|
|||
|
|
|
|||
|
|
# Test API call
|
|||
|
|
http get $"http://($registry_url)/v2/" | ignore
|
|||
|
|
|
|||
|
|
let end = (date now)
|
|||
|
|
let duration = (($end - $start) | into int) / 1_000_000 # Convert to ms
|
|||
|
|
|
|||
|
|
print $" Time: ($duration)ms"
|
|||
|
|
$total_time = ($total_time + $duration)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let avg = ($total_time / $iterations)
|
|||
|
|
print $"\n📊 Average response time: ($avg)ms"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Load test
|
|||
|
|
export def "load test" [
|
|||
|
|
--registry-url: string = "localhost:5000"
|
|||
|
|
--concurrent: int = 10
|
|||
|
|
--duration: int = 60 # seconds
|
|||
|
|
] {
|
|||
|
|
print "⚡ Running load test..."
|
|||
|
|
print $" Concurrent requests: ($concurrent)"
|
|||
|
|
print $" Duration: ($duration)s\n"
|
|||
|
|
|
|||
|
|
# This would require parallel execution
|
|||
|
|
# For now, just show the concept
|
|||
|
|
print "ℹ Load testing requires additional tooling (vegeta, ab, wrk)"
|
|||
|
|
print "\nExample with Apache Bench:"
|
|||
|
|
print $" ab -n 1000 -c ($concurrent) http://($registry_url)/v2/"
|
|||
|
|
print "\nExample with vegeta:"
|
|||
|
|
print $" echo 'GET http://($registry_url)/v2/' | vegeta attack -duration=($duration)s -rate=100 | vegeta report"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Stress test
|
|||
|
|
export def "stress test" [
|
|||
|
|
--registry-url: string = "localhost:5000"
|
|||
|
|
] {
|
|||
|
|
print "💪 Running stress test..."
|
|||
|
|
print " Testing registry limits...\n"
|
|||
|
|
|
|||
|
|
# Test large catalog
|
|||
|
|
print "Test 1: Large catalog response..."
|
|||
|
|
let catalog_result = (do {
|
|||
|
|
http get $"http://($registry_url)/v2/_catalog" --timeout 30sec
|
|||
|
|
} | complete)
|
|||
|
|
|
|||
|
|
if $catalog_result.exit_code == 0 {
|
|||
|
|
let catalog = ($catalog_result.stdout | from json)
|
|||
|
|
print $" ✓ Catalog with ($catalog.repositories | length) repos"
|
|||
|
|
} else {
|
|||
|
|
print " ✗ Catalog failed"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Test many concurrent connections
|
|||
|
|
print "\nTest 2: Concurrent connections..."
|
|||
|
|
print " ℹ Use 'load test' for detailed concurrent testing"
|
|||
|
|
|
|||
|
|
print "\n✅ Stress test complete"
|
|||
|
|
}
|