Files
moby/integration/image/inspect_test.go
Paweł Gronowski 59169d0f97 image/inspect: Add platform selection
`GET /image/{name}/json` now supports `platform` parameter allowing to
specify which platform variant of a multi-platform image to inspect.

For servers that do not use containerd image store integration, this
option will cause an error if the requested platform doesn't match the
image's actual platform

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-04-03 13:57:51 +02:00

204 lines
5.8 KiB
Go

package image
import (
"bytes"
"encoding/json"
"testing"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
"github.com/docker/docker/internal/testutils/specialimage"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
// Regression test for: https://github.com/moby/moby/issues/45556
func TestImageInspectEmptyTagsAndDigests(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "build-empty-images is not called on Windows")
ctx := setupTest(t)
apiClient := testEnv.APIClient()
danglingID := specialimage.Load(ctx, t, apiClient, specialimage.Dangling)
var raw bytes.Buffer
inspect, err := apiClient.ImageInspect(ctx, danglingID, client.ImageInspectWithRawResponse(&raw))
assert.NilError(t, err)
// Must be a zero length array, not null.
assert.Check(t, is.Len(inspect.RepoTags, 0))
assert.Check(t, is.Len(inspect.RepoDigests, 0))
var rawJson map[string]interface{}
err = json.Unmarshal(raw.Bytes(), &rawJson)
assert.NilError(t, err)
// Check if the raw json is also an array, not null.
assert.Check(t, is.Len(rawJson["RepoTags"], 0))
assert.Check(t, is.Len(rawJson["RepoDigests"], 0))
}
// Regression test for: https://github.com/moby/moby/issues/48747
func TestImageInspectUniqueRepoDigests(t *testing.T) {
ctx := setupTest(t)
client := testEnv.APIClient()
before, err := client.ImageInspect(ctx, "busybox")
assert.NilError(t, err)
for _, tag := range []string{"master", "newest"} {
imgName := "busybox:" + tag
err := client.ImageTag(ctx, "busybox", imgName)
assert.NilError(t, err)
defer func() {
_, _ = client.ImageRemove(ctx, imgName, image.RemoveOptions{Force: true})
}()
}
after, err := client.ImageInspect(ctx, "busybox")
assert.NilError(t, err)
assert.Check(t, is.Len(after.RepoDigests, len(before.RepoDigests)))
}
func TestImageInspectDescriptor(t *testing.T) {
ctx := setupTest(t)
client := testEnv.APIClient()
inspect, err := client.ImageInspect(ctx, "busybox")
assert.NilError(t, err)
if !testEnv.UsingSnapshotter() {
assert.Check(t, is.Nil(inspect.Descriptor))
return
}
assert.Assert(t, inspect.Descriptor != nil)
assert.Check(t, inspect.Descriptor.Digest.String() == inspect.ID)
assert.Check(t, inspect.Descriptor.Size > 0)
}
func TestImageInspectWithPlatform(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "The test image is a Linux image")
ctx := setupTest(t)
apiClient := testEnv.APIClient()
nativePlatform := ocispec.Platform{
OS: testEnv.DaemonInfo.OSType,
Architecture: testEnv.DaemonInfo.Architecture,
}
// Create a platform that does not match the host platform
differentOS := "linux"
if nativePlatform.OS == "linux" {
differentOS = "windows"
}
differentPlatform := ocispec.Platform{
OS: differentOS,
Architecture: "amd64",
}
imageID := specialimage.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)
err = specialimage.LegacyManifest(dir, "multiplatform:latest", descs[0])
assert.NilError(t, err)
return i, err
})
for _, tc := range []struct {
name string
requestedPlatform *ocispec.Platform
expectedPlatform *ocispec.Platform
expectedError string
withManifests bool
snapshotterOnly bool
graphdriverOnly bool
}{
{
name: "default",
requestedPlatform: nil,
expectedPlatform: &nativePlatform,
},
{
name: "snapshotter/with-manifests",
requestedPlatform: nil,
expectedPlatform: &nativePlatform,
snapshotterOnly: true,
withManifests: true,
},
{
name: "native",
requestedPlatform: &nativePlatform,
expectedPlatform: &nativePlatform,
},
{
name: "different",
requestedPlatform: &differentPlatform,
expectedPlatform: &differentPlatform,
snapshotterOnly: true,
},
{
name: "different not supported on graphdriver",
requestedPlatform: &differentPlatform,
graphdriverOnly: true,
// image with reference multiplatform:latest was found but its platform (linux/aarch64) does not match the specified platform (windows/amd64)
expectedError: "image with reference multiplatform:latest was found but its platform",
},
} {
if tc.snapshotterOnly && !testEnv.UsingSnapshotter() {
continue
}
if tc.graphdriverOnly && testEnv.UsingSnapshotter() {
continue
}
t.Run(tc.name, func(t *testing.T) {
var opts []client.ImageInspectOption
if tc.requestedPlatform != nil {
opts = append(opts, client.ImageInspectWithPlatform(tc.requestedPlatform))
}
if tc.withManifests {
opts = append(opts, client.ImageInspectWithManifests(true))
}
inspect, err := apiClient.ImageInspect(ctx, imageID, opts...)
if tc.expectedError != "" {
assert.Assert(t, is.ErrorContains(err, tc.expectedError))
return
}
assert.NilError(t, err)
assert.Check(t, is.Equal(inspect.Architecture, tc.expectedPlatform.Architecture))
assert.Check(t, is.Equal(inspect.Os, tc.expectedPlatform.OS))
if testEnv.UsingSnapshotter() {
assert.Assert(t, inspect.Descriptor != nil)
if tc.requestedPlatform != nil {
if assert.Check(t, inspect.Descriptor.Platform != nil) {
assert.Check(t, is.DeepEqual(*inspect.Descriptor.Platform, *tc.expectedPlatform))
}
}
} else {
assert.Check(t, inspect.Descriptor == nil)
}
if tc.withManifests {
t.Run("has manifests", func(t *testing.T) {
assert.Check(t, is.Len(inspect.Manifests, 2))
})
} else {
t.Run("has no manifests", func(t *testing.T) {
assert.Check(t, is.Nil(inspect.Manifests))
})
}
})
}
}