From 081b74b97d0652472293ddaa71f6ab3e2b52c6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 7 Sep 2023 18:00:33 +0200 Subject: [PATCH 1/2] c8d/builder: Store parent in c8d image label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Images built by classic builder will have an additional label (in the containerd image object, not image config) pointing to a parent of that image. This allows to differentiate intermediate images (dangling images created as a result of a each Dockerfile instruction) from the final images. Signed-off-by: Paweł Gronowski --- daemon/containerd/image_builder.go | 8 ++++++++ daemon/containerd/image_commit.go | 3 +++ daemon/containerd/image_list.go | 28 +++++++++++++++++++++++++++- daemon/containerd/image_tag.go | 11 +++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/daemon/containerd/image_builder.go b/daemon/containerd/image_builder.go index 66fdad8b20..b2c410cd84 100644 --- a/daemon/containerd/image_builder.go +++ b/daemon/containerd/image_builder.go @@ -36,6 +36,8 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) +const imageLabelClassicBuilderParent = "org.mobyproject.image.parent" + // GetImageAndReleasableLayer returns an image and releaseable layer for a // reference or ID. Every call to GetImageAndReleasableLayer MUST call // releasableLayer.Release() to prevent leaking of layers. @@ -392,6 +394,8 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st ociImgToCreate := dockerImageToDockerOCIImage(*imgToCreate) var layers []ocispec.Descriptor + + var parentDigest digest.Digest // if the image has a parent, we need to start with the parents layers descriptors if parent != "" { parentDesc, err := i.resolveDescriptor(ctx, parent) @@ -404,6 +408,7 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st } layers = parentImageManifest.Layers + parentDigest = parentDesc.Digest } // get the info for the new layers @@ -443,6 +448,9 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st Name: danglingImageName(commitManifestDesc.Digest), Target: commitManifestDesc, CreatedAt: time.Now(), + Labels: map[string]string{ + imageLabelClassicBuilderParent: parentDigest.String(), + }, } createdImage, err := i.client.ImageService().Update(ctx, img) diff --git a/daemon/containerd/image_commit.go b/daemon/containerd/image_commit.go index 10d56f401f..e9ba7983c6 100644 --- a/daemon/containerd/image_commit.go +++ b/daemon/containerd/image_commit.go @@ -106,6 +106,9 @@ func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig) Name: danglingImageName(commitManifestDesc.Digest), Target: commitManifestDesc, CreatedAt: time.Now(), + Labels: map[string]string{ + imageLabelClassicBuilderParent: cc.ParentImageID, + }, } if _, err := i.client.ImageService().Update(ctx, img); err != nil { diff --git a/daemon/containerd/image_list.go b/daemon/containerd/image_list.go index 4a71c05fd9..3543d76303 100644 --- a/daemon/containerd/image_list.go +++ b/daemon/containerd/image_list.go @@ -98,8 +98,34 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) contentStore := i.client.ContentStore() uniqueImages := map[digest.Digest]images.Image{} tagsByDigest := map[digest.Digest][]string{} + intermediateImages := map[digest.Digest]struct{}{} + + hideIntermediate := !opts.All + if hideIntermediate { + for _, img := range imgs { + parent, ok := img.Labels[imageLabelClassicBuilderParent] + if ok && parent != "" { + dgst, err := digest.Parse(parent) + if err != nil { + log.G(ctx).WithFields(log.Fields{ + "error": err, + "value": parent, + }).Warnf("invalid %s label value", imageLabelClassicBuilderParent) + } + intermediateImages[dgst] = struct{}{} + } + } + } for _, img := range imgs { + isDangling := isDanglingImage(img) + + if hideIntermediate && isDangling { + if _, ok := intermediateImages[img.Target.Digest]; ok { + continue + } + } + if !filter(img) { continue } @@ -107,7 +133,7 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) dgst := img.Target.Digest uniqueImages[dgst] = img - if isDanglingImage(img) { + if isDangling { continue } diff --git a/daemon/containerd/image_tag.go b/daemon/containerd/image_tag.go index 944403c7ce..946cec904f 100644 --- a/daemon/containerd/image_tag.go +++ b/daemon/containerd/image_tag.go @@ -73,6 +73,17 @@ func (i *ImageService) TagImage(ctx context.Context, imageID image.ID, newTag re return nil } + builderLabel, ok := sourceDanglingImg.Labels[imageLabelClassicBuilderParent] + if ok { + newImg.Labels = map[string]string{ + imageLabelClassicBuilderParent: builderLabel, + } + + if _, err := is.Update(context.Background(), newImg, "labels"); err != nil { + logger.WithError(err).Warnf("failed to set %s label on the newly tagged image", imageLabelClassicBuilderParent) + } + } + // Delete the source dangling image, as it's no longer dangling. if err := is.Delete(context.Background(), sourceDanglingImg.Name); err != nil { logger.WithError(err).Warn("unexpected error when deleting dangling image") From c0ece8d4f37be5f5cae77a11319b32ec43cfbb66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 7 Sep 2023 18:06:19 +0200 Subject: [PATCH 2/2] integration-cli: Use different label in TestBuildMultiStageGlobalArg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid collision with TestBuildMultiStageArg. Signed-off-by: Paweł Gronowski --- integration-cli/docker_cli_build_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 07184cfd1e..d97a94099e 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -4642,7 +4642,7 @@ func (s *DockerCLIBuildSuite) TestBuildMultiStageGlobalArg(c *testing.T) { imgName := "multifrombldargtest" dockerfile := `ARG tag=nosuchtag FROM busybox:${tag} - LABEL multifromtest=1 + LABEL multifromtest2=1 RUN env > /out FROM busybox:${tag} ARG tag @@ -4653,7 +4653,7 @@ func (s *DockerCLIBuildSuite) TestBuildMultiStageGlobalArg(c *testing.T) { cli.WithFlags("--build-arg", "tag=latest")) result.Assert(c, icmd.Success) - result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1") + result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest2=1") result.Assert(c, icmd.Success) imgs := strings.Split(strings.TrimSpace(result.Stdout()), "\n")