diff --git a/integration/containerd_image_test.go b/integration/containerd_image_test.go index 4f6affe1114d..e4bf3eb27ff0 100644 --- a/integration/containerd_image_test.go +++ b/integration/containerd_image_test.go @@ -20,6 +20,8 @@ import ( "context" "errors" "fmt" + goruntime "runtime" + "strings" "testing" "time" @@ -234,3 +236,41 @@ func TestContainerdSandboxImage(t *testing.T) { t.Log("verify pinned field is set for pause image") assert.True(t, pimg.Pinned) } + +func TestContainerdImageWithDockerSchema1(t *testing.T) { + if goruntime.GOOS == "windows" { + t.Skip("Skipped on Windows because the test image is not a multi-platform one.") + } + + var testImage = images.Get(images.DockerSchema1) + digest := strings.Split(testImage, "@")[1] + ctx := context.Background() + + t.Logf("make sure the test image doesn't exist in the cri plugin") + i, err := imageService.ImageStatus(&runtime.ImageSpec{Image: testImage}) + require.NoError(t, err) + if i != nil { + require.NoError(t, imageService.RemoveImage(&runtime.ImageSpec{Image: testImage})) + } + + t.Logf("pull the image into containerd") + //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. + _, err = containerdClient.Pull(ctx, testImage, containerd.WithPullUnpack, containerd.WithSchema1Conversion) + require.NoError(t, err) + defer func() { + // Make sure the image is cleaned up in any case. + if err := containerdClient.ImageService().Delete(ctx, testImage); err != nil { + assert.True(t, errdefs.IsNotFound(err), err) + } + assert.NoError(t, imageService.RemoveImage(&runtime.ImageSpec{Image: testImage})) + }() + + imgByRef, err := containerdClient.GetImage(ctx, testImage) + require.NoError(t, err) + + t.Logf("the image should be marked as managed") + assert.Equal(t, "managed", imgByRef.Labels()["io.cri-containerd.image"]) + + t.Logf("the image should be marked as dokcker schema1 with its original digest") + assert.Equal(t, digest, imgByRef.Labels()["io.containerd.image/converted-docker-schema1"]) +} diff --git a/integration/images/image_list.go b/integration/images/image_list.go index 1fec3038b77b..eb2a1db34f0d 100644 --- a/integration/images/image_list.go +++ b/integration/images/image_list.go @@ -37,6 +37,7 @@ type ImageList struct { VolumeCopyUp string VolumeOwnership string ArgsEscaped string + DockerSchema1 string } var ( @@ -55,6 +56,7 @@ func initImages(imageListFile string) { VolumeCopyUp: "ghcr.io/containerd/volume-copy-up:2.2", VolumeOwnership: "ghcr.io/containerd/volume-ownership:2.1", ArgsEscaped: "cplatpublic.azurecr.io/args-escaped-test-image-ns:1.0", + DockerSchema1: "registry.k8s.io/busybox@sha256:4bdd623e848417d96127e16037743f0cd8b528c026e9175e22a84f639eca58ff", } if imageListFile != "" { @@ -92,6 +94,8 @@ const ( VolumeOwnership // ArgsEscaped tests image for ArgsEscaped windows bug ArgsEscaped + // DockerSchema1 image with docker schema 1 + DockerSchema1 ) func initImageMap(imageList ImageList) map[int]string { @@ -103,6 +107,7 @@ func initImageMap(imageList ImageList) map[int]string { images[VolumeCopyUp] = imageList.VolumeCopyUp images[VolumeOwnership] = imageList.VolumeOwnership images[ArgsEscaped] = imageList.ArgsEscaped + images[DockerSchema1] = imageList.DockerSchema1 return images } diff --git a/pull.go b/pull.go index 5d96c8cc72d1..c35278aa2a30 100644 --- a/pull.go +++ b/pull.go @@ -34,7 +34,8 @@ import ( ) const ( - pullSpanPrefix = "pull" + pullSpanPrefix = "pull" + convertedDockerSchema1LabelKey = "io.containerd.image/converted-docker-schema1" ) // Pull downloads the provided content into containerd's content store @@ -189,9 +190,10 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim var ( handler images.Handler - isConvertible bool - converterFunc func(context.Context, ocispec.Descriptor) (ocispec.Descriptor, error) - limiter *semaphore.Weighted + isConvertible bool + originalSchema1Digest string + converterFunc func(context.Context, ocispec.Descriptor) (ocispec.Descriptor, error) + limiter *semaphore.Weighted ) if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 { @@ -204,6 +206,8 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim converterFunc = func(ctx context.Context, _ ocispec.Descriptor) (ocispec.Descriptor, error) { return schema1Converter.Convert(ctx) } + + originalSchema1Digest = desc.Digest.String() } else { // Get all the children for a descriptor childrenHandler := images.ChildrenHandler(store) @@ -270,6 +274,13 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim } } + if originalSchema1Digest != "" { + if rCtx.Labels == nil { + rCtx.Labels = make(map[string]string) + } + rCtx.Labels[convertedDockerSchema1LabelKey] = originalSchema1Digest + } + return images.Image{ Name: name, Target: desc,