From b54bde43762393582cfec31423adbdb62923c1bf Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Wed, 16 Jul 2025 16:15:27 -0700 Subject: [PATCH] Move testutils image load to integration internal The image load is only used by integration tests but the specialimage testutils package used by many different tests. The image load relies on the client which creates a transitive client dependency from the daemon packages. Signed-off-by: Derek McGowan --- integration/image/inspect_test.go | 5 +- integration/image/list_test.go | 7 +-- integration/image/load_test.go | 5 +- integration/image/prune_test.go | 3 +- integration/image/remove_test.go | 3 +- integration/image/save_test.go | 3 +- integration/internal/image/load.go | 68 +++++++++++++++++++++++++ internal/testutils/specialimage/load.go | 63 ----------------------- 8 files changed, 84 insertions(+), 73 deletions(-) create mode 100644 integration/internal/image/load.go diff --git a/integration/image/inspect_test.go b/integration/image/inspect_test.go index a0a700c36f..f5c5e21499 100644 --- a/integration/image/inspect_test.go +++ b/integration/image/inspect_test.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" + iimage "github.com/docker/docker/integration/internal/image" "github.com/docker/docker/internal/testutils/specialimage" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "gotest.tools/v3/assert" @@ -21,7 +22,7 @@ func TestImageInspectEmptyTagsAndDigests(t *testing.T) { apiClient := testEnv.APIClient() - danglingID := specialimage.Load(ctx, t, apiClient, specialimage.Dangling) + danglingID := iimage.Load(ctx, t, apiClient, specialimage.Dangling) var raw bytes.Buffer inspect, err := apiClient.ImageInspect(ctx, danglingID, client.ImageInspectWithRawResponse(&raw)) @@ -103,7 +104,7 @@ func TestImageInspectWithPlatform(t *testing.T) { Architecture: "amd64", } - imageID := specialimage.Load(ctx, t, apiClient, func(dir string) (*ocispec.Index, error) { + imageID := iimage.Load(ctx, t, apiClient, func(dir string) (*ocispec.Index, error) { i, descs, err := specialimage.MultiPlatform(dir, "multiplatform:latest", []ocispec.Platform{nativePlatform, differentPlatform}) assert.NilError(t, err) diff --git a/integration/image/list_test.go b/integration/image/list_test.go index 35622aed61..38c8e5f904 100644 --- a/integration/image/list_test.go +++ b/integration/image/list_test.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/api/types/versions" "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" + iimage "github.com/docker/docker/integration/internal/image" "github.com/docker/docker/internal/testutils/specialimage" "github.com/docker/docker/testutil" "github.com/docker/docker/testutil/daemon" @@ -213,14 +214,14 @@ func TestAPIImagesListSizeShared(t *testing.T) { client := daemon.NewClientT(t) - specialimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) { + iimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) { return specialimage.MultiLayerCustom(dir, "multilayer:latest", []specialimage.SingleFileLayer{ {Name: "bar", Content: []byte("2")}, {Name: "foo", Content: []byte("1")}, }) }) - specialimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) { + iimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) { return specialimage.MultiLayerCustom(dir, "multilayer2:latest", []specialimage.SingleFileLayer{ {Name: "asdf", Content: []byte("3")}, {Name: "foo", Content: []byte("1")}, @@ -249,7 +250,7 @@ func TestAPIImagesListManifests(t *testing.T) { {OS: "linux", Architecture: "arm", Variant: "v7"}, {OS: "darwin", Architecture: "arm64"}, } - specialimage.Load(ctx, t, apiClient, func(dir string) (*ocispec.Index, error) { + iimage.Load(ctx, t, apiClient, func(dir string) (*ocispec.Index, error) { idx, _, err := specialimage.MultiPlatform(dir, "multiplatform:latest", testPlatforms) return idx, err }) diff --git a/integration/image/load_test.go b/integration/image/load_test.go index 589a1c04b1..29a593a34d 100644 --- a/integration/image/load_test.go +++ b/integration/image/load_test.go @@ -6,6 +6,7 @@ import ( cerrdefs "github.com/containerd/errdefs" "github.com/docker/docker/api/types/image" + iimage "github.com/docker/docker/integration/internal/image" "github.com/docker/docker/internal/testutils/specialimage" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "gotest.tools/v3/assert" @@ -20,7 +21,7 @@ func TestLoadDanglingImages(t *testing.T) { client := testEnv.APIClient() - specialimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) { + iimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) { return specialimage.MultiLayerCustom(dir, "namedimage:latest", []specialimage.SingleFileLayer{ {Name: "bar", Content: []byte("1")}, }) @@ -44,7 +45,7 @@ func TestLoadDanglingImages(t *testing.T) { assert.NilError(t, err) // Retain a copy of the old image and then replace it with a new one. - specialimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) { + iimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) { return specialimage.MultiLayerCustom(dir, "namedimage:latest", []specialimage.SingleFileLayer{ {Name: "bar", Content: []byte("2")}, }) diff --git a/integration/image/prune_test.go b/integration/image/prune_test.go index dd6d58158f..76c6f440c0 100644 --- a/integration/image/prune_test.go +++ b/integration/image/prune_test.go @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" + iimage "github.com/docker/docker/integration/internal/image" "github.com/docker/docker/internal/testutils/specialimage" "github.com/docker/docker/testutil" "github.com/docker/docker/testutil/daemon" @@ -31,7 +32,7 @@ func TestPruneDontDeleteUsedDangling(t *testing.T) { apiClient := d.NewClientT(t) defer apiClient.Close() - danglingID := specialimage.Load(ctx, t, apiClient, specialimage.Dangling) + danglingID := iimage.Load(ctx, t, apiClient, specialimage.Dangling) _, err := apiClient.ImageInspect(ctx, danglingID) assert.NilError(t, err, "Test dangling image doesn't exist") diff --git a/integration/image/remove_test.go b/integration/image/remove_test.go index ca383f172b..bc0076db0d 100644 --- a/integration/image/remove_test.go +++ b/integration/image/remove_test.go @@ -10,6 +10,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/image" "github.com/docker/docker/integration/internal/container" + iimage "github.com/docker/docker/integration/internal/image" "github.com/docker/docker/internal/testutils/specialimage" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "gotest.tools/v3/assert" @@ -115,7 +116,7 @@ func TestRemoveWithPlatform(t *testing.T) { var imageIdx *ocispec.Index var descs []ocispec.Descriptor - specialimage.Load(ctx, t, apiClient, func(dir string) (*ocispec.Index, error) { + iimage.Load(ctx, t, apiClient, func(dir string) (*ocispec.Index, error) { idx, d, err := specialimage.MultiPlatform(dir, imgName, []ocispec.Platform{ platformHost, { diff --git a/integration/image/save_test.go b/integration/image/save_test.go index 084056cde8..8696f25df6 100644 --- a/integration/image/save_test.go +++ b/integration/image/save_test.go @@ -20,6 +20,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/build" "github.com/docker/docker/integration/internal/container" + iimage "github.com/docker/docker/integration/internal/image" "github.com/docker/docker/internal/testutils" "github.com/docker/docker/internal/testutils/specialimage" "github.com/docker/docker/testutil/fakecontext" @@ -116,7 +117,7 @@ func TestSaveOCI(t *testing.T) { } if testEnv.DaemonInfo.OSType != "windows" { - multiLayerImage := specialimage.Load(ctx, t, client, specialimage.MultiLayer) + multiLayerImage := iimage.Load(ctx, t, client, specialimage.MultiLayer) // Multi-layer image testCases = append(testCases, testCase{image: multiLayerImage, expectedContainerdRef: "docker.io/library/multilayer:latest", expectedOCIRef: "latest"}) diff --git a/integration/internal/image/load.go b/integration/internal/image/load.go new file mode 100644 index 0000000000..dc0339e6b9 --- /dev/null +++ b/integration/internal/image/load.go @@ -0,0 +1,68 @@ +package image + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "io" + "strings" + "testing" + + "github.com/docker/docker/client" + "github.com/docker/docker/internal/testutils/specialimage" + "github.com/docker/docker/pkg/jsonmessage" + "github.com/moby/go-archive" + "gotest.tools/v3/assert" +) + +func Load(ctx context.Context, t *testing.T, apiClient client.APIClient, imageFunc specialimage.SpecialImageFunc) string { + tempDir := t.TempDir() + + _, err := imageFunc(tempDir) + assert.NilError(t, err) + + rc, err := archive.TarWithOptions(tempDir, &archive.TarOptions{}) + assert.NilError(t, err) + + defer rc.Close() + + resp, err := apiClient.ImageLoad(ctx, rc, client.ImageLoadWithQuiet(true)) + assert.NilError(t, err, "Failed to load dangling image") + + defer resp.Body.Close() + + if !assert.Check(t, err) { + respBody, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("Failed to read response body: %v", err) + return "" + } + t.Fatalf("Failed load: %s", string(respBody)) + } + + all, err := io.ReadAll(resp.Body) + assert.NilError(t, err) + + decoder := json.NewDecoder(bytes.NewReader(all)) + for { + var msg jsonmessage.JSONMessage + err := decoder.Decode(&msg) + if errors.Is(err, io.EOF) { + break + } + assert.NilError(t, err) + + msg.Stream = strings.TrimSpace(msg.Stream) + + if _, imageID, hasID := strings.Cut(msg.Stream, "Loaded image ID: "); hasID { + return imageID + } + if _, imageRef, hasRef := strings.Cut(msg.Stream, "Loaded image: "); hasRef { + return imageRef + } + } + + t.Fatalf("failed to read image ID\n%s", string(all)) + return "" +} diff --git a/internal/testutils/specialimage/load.go b/internal/testutils/specialimage/load.go index 2d2b9d7c09..dab8d1e89c 100644 --- a/internal/testutils/specialimage/load.go +++ b/internal/testutils/specialimage/load.go @@ -1,70 +1,7 @@ package specialimage import ( - "bytes" - "context" - "encoding/json" - "errors" - "io" - "strings" - "testing" - - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/jsonmessage" - "github.com/moby/go-archive" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "gotest.tools/v3/assert" ) type SpecialImageFunc func(string) (*ocispec.Index, error) - -func Load(ctx context.Context, t *testing.T, apiClient client.APIClient, imageFunc SpecialImageFunc) string { - tempDir := t.TempDir() - - _, err := imageFunc(tempDir) - assert.NilError(t, err) - - rc, err := archive.TarWithOptions(tempDir, &archive.TarOptions{}) - assert.NilError(t, err) - - defer rc.Close() - - resp, err := apiClient.ImageLoad(ctx, rc, client.ImageLoadWithQuiet(true)) - assert.NilError(t, err, "Failed to load dangling image") - - defer resp.Body.Close() - - if !assert.Check(t, err) { - respBody, err := io.ReadAll(resp.Body) - if err != nil { - t.Fatalf("Failed to read response body: %v", err) - return "" - } - t.Fatalf("Failed load: %s", string(respBody)) - } - - all, err := io.ReadAll(resp.Body) - assert.NilError(t, err) - - decoder := json.NewDecoder(bytes.NewReader(all)) - for { - var msg jsonmessage.JSONMessage - err := decoder.Decode(&msg) - if errors.Is(err, io.EOF) { - break - } - assert.NilError(t, err) - - msg.Stream = strings.TrimSpace(msg.Stream) - - if _, imageID, hasID := strings.Cut(msg.Stream, "Loaded image ID: "); hasID { - return imageID - } - if _, imageRef, hasRef := strings.Cut(msg.Stream, "Loaded image: "); hasRef { - return imageRef - } - } - - t.Fatalf("failed to read image ID\n%s", string(all)) - return "" -}