Sfoglia il codice sorgente

c8d: Handle nil container.ImageManifest

Don't panic when processing containers created under fork containerd
integration (this field was added in the upstream and didn't exist in
fork).

Co-authored-by: Djordje Lukic <djordje.lukic@docker.com>
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Paweł Gronowski 2 anni fa
parent
commit
9fe5ac6614

+ 6 - 1
daemon/containerd/image_changes.go

@@ -17,7 +17,12 @@ import (
 func (i *ImageService) Changes(ctx context.Context, container *container.Container) ([]archive.Change, error) {
 	cs := i.client.ContentStore()
 
-	imageManifestBytes, err := content.ReadBlob(ctx, cs, *container.ImageManifest)
+	imageManifest, err := getContainerImageManifest(container)
+	if err != nil {
+		return nil, err
+	}
+
+	imageManifestBytes, err := content.ReadBlob(ctx, cs, imageManifest)
 	if err != nil {
 		return nil, err
 	}

+ 6 - 1
daemon/containerd/image_commit.go

@@ -40,7 +40,12 @@ func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig)
 	container := i.containers.Get(cc.ContainerID)
 	cs := i.client.ContentStore()
 
-	imageManifestBytes, err := content.ReadBlob(ctx, cs, *container.ImageManifest)
+	imageManifest, err := getContainerImageManifest(container)
+	if err != nil {
+		return "", err
+	}
+
+	imageManifestBytes, err := content.ReadBlob(ctx, cs, imageManifest)
 	if err != nil {
 		return "", err
 	}

+ 40 - 7
daemon/containerd/service.go

@@ -10,6 +10,7 @@ import (
 	"github.com/containerd/containerd/plugin"
 	"github.com/containerd/containerd/remotes/docker"
 	"github.com/containerd/containerd/snapshots"
+	imagetypes "github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/container"
 	daemonevents "github.com/docker/docker/daemon/events"
 	"github.com/docker/docker/daemon/images"
@@ -20,6 +21,7 @@ import (
 	"github.com/opencontainers/image-spec/identity"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
 )
 
 // ImageService implements daemon.ImageService
@@ -154,9 +156,35 @@ func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID st
 	if ctr == nil {
 		return 0, 0, nil
 	}
+
+	snapshotter := i.client.SnapshotService(ctr.Driver)
+
+	usage, err := snapshotter.Usage(ctx, containerID)
+	if err != nil {
+		return 0, 0, err
+	}
+
+	imageManifest, err := getContainerImageManifest(ctr)
+	if err != nil {
+		// Best efforts attempt to pick an image.
+		// We don't have platform information at this point, so we can only
+		// assume that the platform matches host.
+		// Otherwise this will give a wrong base image size (different
+		// platform), but should be close enough.
+		mfst, err := i.GetImageManifest(ctx, ctr.Config.Image, imagetypes.GetImageOpts{})
+		if err != nil {
+			// Log error, don't error out whole operation.
+			logrus.WithFields(logrus.Fields{
+				logrus.ErrorKey: err,
+				"container":     containerID,
+			}).Warn("empty ImageManifest, can't calculate base image size")
+			return usage.Size, 0, nil
+		}
+		imageManifest = *mfst
+	}
 	cs := i.client.ContentStore()
 
-	imageManifestBytes, err := content.ReadBlob(ctx, cs, *ctr.ImageManifest)
+	imageManifestBytes, err := content.ReadBlob(ctx, cs, imageManifest)
 	if err != nil {
 		return 0, 0, err
 	}
@@ -175,12 +203,6 @@ func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID st
 		return 0, 0, err
 	}
 
-	snapshotter := i.client.SnapshotService(ctr.Driver)
-	usage, err := snapshotter.Usage(ctx, containerID)
-	if err != nil {
-		return 0, 0, err
-	}
-
 	sizeCache := make(map[digest.Digest]int64)
 	snapshotSizeFn := func(d digest.Digest) (int64, error) {
 		if s, ok := sizeCache[d]; ok {
@@ -203,3 +225,14 @@ func (i *ImageService) GetContainerLayerSize(ctx context.Context, containerID st
 	// TODO(thaJeztah): include content-store size for the image (similar to "GET /images/json")
 	return usage.Size, usage.Size + snapShotSize, nil
 }
+
+// getContainerImageManifest safely dereferences ImageManifest.
+// ImageManifest can be nil for containers created with Docker Desktop with old
+// containerd image store integration enabled which didn't set this field.
+func getContainerImageManifest(ctr *container.Container) (ocispec.Descriptor, error) {
+	if ctr.ImageManifest == nil {
+		return ocispec.Descriptor{}, errdefs.InvalidParameter(errors.New("container is missing ImageManifest (probably created on old version), please recreate it"))
+	}
+
+	return *ctr.ImageManifest, nil
+}