diff --git a/daemon/containerd/image.go b/daemon/containerd/image.go index 3af499644d..ea779ebfad 100644 --- a/daemon/containerd/image.go +++ b/daemon/containerd/image.go @@ -273,6 +273,11 @@ func (i *ImageService) resolveDescriptor(ctx context.Context, refOrID string) (o return img.Target, nil } +// ResolveImage looks up an image by reference or identifier in the image store. +func (i *ImageService) ResolveImage(ctx context.Context, refOrID string) (containerdimages.Image, error) { + return i.resolveImage(ctx, refOrID) +} + func (i *ImageService) resolveImage(ctx context.Context, refOrID string) (containerdimages.Image, error) { parsed, err := reference.ParseAnyReference(refOrID) if err != nil { diff --git a/daemon/start.go b/daemon/start.go index b967947af2..17c555dd49 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -4,10 +4,13 @@ import ( "context" "time" + "github.com/containerd/containerd" + "github.com/containerd/containerd/containers" "github.com/containerd/log" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/events" "github.com/docker/docker/container" + mobyc8dstore "github.com/docker/docker/daemon/containerd" "github.com/docker/docker/errdefs" "github.com/docker/docker/internal/compatcontext" "github.com/docker/docker/libcontainerd" @@ -166,7 +169,22 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *configStore return err } - ctr, err := libcontainerd.ReplaceContainer(ctx, daemon.containerd, container.ID, spec, shim, createOptions) + ctr, err := libcontainerd.ReplaceContainer(ctx, daemon.containerd, container.ID, spec, shim, createOptions, func(ctx context.Context, client *containerd.Client, c *containers.Container) error { + // Only set the image if we are using containerd for image storage. + // This is for metadata purposes only. + // Other lower-level components may make use of this information. + is, ok := daemon.imageService.(*mobyc8dstore.ImageService) + if !ok { + return nil + } + img, err := is.ResolveImage(ctx, container.Config.Image) + if err != nil { + log.G(ctx).WithError(err).WithField("container", container.ID).Warn("Failed to resolve containerd image reference") + return nil + } + c.Image = img.Name + return nil + }) if err != nil { return setExitCodeFromError(container.SetExitCode, err) } diff --git a/integration/container/create_test.go b/integration/container/create_test.go index a4541d12a1..7c1a34ce87 100644 --- a/integration/container/create_test.go +++ b/integration/container/create_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/containerd/containerd" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/versions" @@ -668,3 +669,40 @@ func TestCreateWithCustomMACs(t *testing.T) { assert.Equal(t, mac, "02:32:1c:23:00:04") } } + +// Tests that when using containerd backed storage the containerd container has the image referenced stored. +func TestContainerdContainerImageInfo(t *testing.T) { + skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.45"), "requires API v1.45") + + ctx := setupTest(t) + + apiClient := testEnv.APIClient() + defer apiClient.Close() + + info, err := apiClient.Info(ctx) + assert.NilError(t, err) + + skip.If(t, info.Containerd == nil, "requires containerd") + + // Currently a containerd container is only created when the container is started. + // So start the container and then inspect the containerd container to verify the image info. + id := ctr.Run(ctx, t, apiClient, func(cfg *ctr.TestContainerConfig) { + // busybox is the default (as of this writing) used by the test client, but lets be explicit here. + cfg.Config.Image = "busybox" + }) + defer apiClient.ContainerRemove(ctx, id, container.RemoveOptions{Force: true}) + + client, err := containerd.New(info.Containerd.Address, containerd.WithDefaultNamespace(info.Containerd.Namespaces.Containers)) + assert.NilError(t, err) + defer client.Close() + + ctr, err := client.ContainerService().Get(ctx, id) + assert.NilError(t, err) + + if testEnv.UsingSnapshotter() { + assert.Equal(t, ctr.Image, "docker.io/library/busybox:latest") + } else { + // This field is not set when not using contianerd backed storage. + assert.Equal(t, ctr.Image, "") + } +}