feat: 添加了 imager 工具 package
refactor: 将 images 的获取分散到各个组件里面
This commit is contained in:
429
pkg/imager/pull_test.go
Normal file
429
pkg/imager/pull_test.go
Normal file
@@ -0,0 +1,429 @@
|
||||
package imager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.loveuer.com/yizhisec/pkg3/logger"
|
||||
)
|
||||
|
||||
// TestPullImage_PublicImage tests pulling a public image from Docker Hub
|
||||
func TestPullImage_PublicImage(t *testing.T) {
|
||||
logger.SetLogLevel(logger.LogLevelDebug)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// Create temp directory for test output
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "alpine.tar")
|
||||
outputFile = "./redis.alpine.tar"
|
||||
|
||||
// Create output file
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Pull alpine image (small and commonly available)
|
||||
imageName := "docker-mirror.yizhisec.com/library/redis:alpine"
|
||||
err = PullImage(ctx, imageName, f)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
// Verify file was created and has content
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithRegistry tests pulling from a specific registry
|
||||
func TestPullImage_WithRegistry(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "nginx.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Pull from docker.io with explicit registry
|
||||
imageName := "docker.io/library/nginx:alpine"
|
||||
err = PullImage(ctx, imageName, f)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithRename tests pulling and renaming an image
|
||||
func TestPullImage_WithRename(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "renamed.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
imageName := "alpine:3.19"
|
||||
newName := "my-custom-alpine:latest"
|
||||
|
||||
err = PullImage(ctx, imageName, f, WithRename(newName))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s and renamed to %s, size: %d bytes", imageName, newName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithAuth tests pulling with authentication (skip if no credentials)
|
||||
func TestPullImage_WithAuth(t *testing.T) {
|
||||
// This test requires actual credentials, so it's skipped by default
|
||||
t.Skip("Skipping authentication test - requires valid credentials")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "private.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Replace with your private registry details
|
||||
imageName := "your-registry.com/your-repo/your-image:tag"
|
||||
username := "your-username"
|
||||
password := "your-password"
|
||||
|
||||
err = PullImage(ctx, imageName, f, WithAuth(username, password))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull private image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled private image %s, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithRetry tests retry functionality
|
||||
func TestPullImage_WithRetry(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "retry.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Pull with custom retry settings
|
||||
imageName := "alpine:3.19"
|
||||
err = PullImage(ctx, imageName, f,
|
||||
WithRetry(5), // 5 retries
|
||||
WithRetryDelay(1*time.Second), // 1 second delay
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s with retry, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithZeroRetry tests with retry disabled
|
||||
func TestPullImage_WithZeroRetry(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "no-retry.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Pull with retry disabled
|
||||
imageName := "alpine:3.19"
|
||||
err = PullImage(ctx, imageName, f, WithRetry(0))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s without retry, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestPullImage_WithSkipTLSVerify tests pulling with TLS verification disabled
|
||||
func TestPullImage_WithSkipTLSVerify(t *testing.T) {
|
||||
// This test is for registries with self-signed certificates
|
||||
t.Skip("Skipping TLS skip test - requires a registry with self-signed cert")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "insecure.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
imageName := "your-insecure-registry.local/image:tag"
|
||||
err = PullImage(ctx, imageName, f, WithSkipTLSVerify())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to pull image %s: %v", imageName, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatalf("failed to close file: %v", err)
|
||||
}
|
||||
|
||||
info, err := os.Stat(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to stat output file: %v", err)
|
||||
}
|
||||
|
||||
if info.Size() == 0 {
|
||||
t.Error("output file is empty")
|
||||
}
|
||||
|
||||
t.Logf("Successfully pulled %s with TLS verification skipped, size: %d bytes", imageName, info.Size())
|
||||
}
|
||||
|
||||
// TestParseImageReference tests the image reference parsing logic
|
||||
func TestParseImageReference(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
imageName string
|
||||
wantRegistry string
|
||||
wantRepository string
|
||||
wantTag string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "simple image with tag",
|
||||
imageName: "alpine:3.19",
|
||||
wantRegistry: "registry-1.docker.io",
|
||||
wantRepository: "library/alpine",
|
||||
wantTag: "3.19",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "simple image without tag",
|
||||
imageName: "alpine",
|
||||
wantRegistry: "registry-1.docker.io",
|
||||
wantRepository: "library/alpine",
|
||||
wantTag: "latest",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "image with namespace",
|
||||
imageName: "library/nginx:alpine",
|
||||
wantRegistry: "registry-1.docker.io",
|
||||
wantRepository: "library/nginx",
|
||||
wantTag: "alpine",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "custom registry",
|
||||
imageName: "hub.yizhisec.com/external/alpine:3.22.2",
|
||||
wantRegistry: "hub.yizhisec.com",
|
||||
wantRepository: "external/alpine",
|
||||
wantTag: "3.22.2",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "docker.io explicit",
|
||||
imageName: "docker.io/library/redis:8.2.2",
|
||||
wantRegistry: "docker.io",
|
||||
wantRepository: "library/redis",
|
||||
wantTag: "8.2.2",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "registry with port",
|
||||
imageName: "localhost:5000/myimage:v1.0",
|
||||
wantRegistry: "localhost:5000",
|
||||
wantRepository: "myimage",
|
||||
wantTag: "v1.0",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "quay.io registry",
|
||||
imageName: "quay.io/k0sproject/pause:3.10.1",
|
||||
wantRegistry: "quay.io",
|
||||
wantRepository: "k0sproject/pause",
|
||||
wantTag: "3.10.1",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ref, err := parseImageReference(tt.imageName)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("parseImageReference() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if ref.Registry != tt.wantRegistry {
|
||||
t.Errorf("parseImageReference() registry = %v, want %v", ref.Registry, tt.wantRegistry)
|
||||
}
|
||||
if ref.Repository != tt.wantRepository {
|
||||
t.Errorf("parseImageReference() repository = %v, want %v", ref.Repository, tt.wantRepository)
|
||||
}
|
||||
if ref.Tag != tt.wantTag {
|
||||
t.Errorf("parseImageReference() tag = %v, want %v", ref.Tag, tt.wantTag)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestPullImage_ContextCancellation tests that context cancellation works
|
||||
func TestPullImage_ContextCancellation(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
outputFile := filepath.Join(tmpDir, "cancelled.tar")
|
||||
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Cancel context immediately
|
||||
cancel()
|
||||
|
||||
imageName := "alpine:3.19"
|
||||
err = PullImage(ctx, imageName, f)
|
||||
if err == nil {
|
||||
t.Error("expected error due to cancelled context, got nil")
|
||||
}
|
||||
|
||||
t.Logf("Context cancellation correctly handled: %v", err)
|
||||
}
|
||||
|
||||
// BenchmarkPullImage benchmarks image pulling performance
|
||||
func BenchmarkPullImage(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
tmpDir := b.TempDir()
|
||||
|
||||
imageName := "alpine:3.19"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
outputFile := filepath.Join(tmpDir, "alpine_bench.tar")
|
||||
f, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create output file: %v", err)
|
||||
}
|
||||
|
||||
err = PullImage(ctx, imageName, f)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to pull image: %v", err)
|
||||
}
|
||||
|
||||
f.Close()
|
||||
os.Remove(outputFile)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user