c8d/history: Fill ID and Tags for all entries

Find the parent images by image set by the legacy builder.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski 2023-10-04 10:32:00 +02:00
parent bc2c8279de
commit f99c4ec798
No known key found for this signature in database
GPG key ID: B85EFCFE26DEF92A
2 changed files with 54 additions and 13 deletions

View file

@ -10,6 +10,7 @@ import (
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
@ -190,6 +191,27 @@ func (i *ImageService) parents(ctx context.Context, id image.ID) ([]imageWithRoo
return parents, nil
}
// getParentsByBuilderLabel finds images that were a base for the given image
// by an image label set by the legacy builder.
// NOTE: This only works for images built with legacy builder (not Buildkit).
func (i *ImageService) getParentsByBuilderLabel(ctx context.Context, img containerdimages.Image) ([]containerdimages.Image, error) {
parent, ok := img.Labels[imageLabelClassicBuilderParent]
if !ok || parent == "" {
return nil, nil
}
dgst, err := digest.Parse(parent)
if err != nil {
log.G(ctx).WithFields(log.Fields{
"error": err,
"value": parent,
}).Warnf("invalid %s label value", imageLabelClassicBuilderParent)
return nil, nil
}
return i.client.ImageService().List(ctx, "target.digest=="+dgst.String())
}
type imageWithRootfs struct {
img containerdimages.Image
rootfs ocispec.RootFS

View file

@ -4,6 +4,8 @@ import (
"context"
"sort"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
cplatforms "github.com/containerd/containerd/platforms"
"github.com/distribution/reference"
imagetype "github.com/docker/docker/api/types/image"
@ -17,7 +19,7 @@ import (
// ImageHistory returns a slice of HistoryResponseItem structures for the
// specified image name by walking the image lineage.
func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*imagetype.HistoryResponseItem, error) {
desc, err := i.resolveImage(ctx, name)
img, err := i.resolveImage(ctx, name)
if err != nil {
return nil, err
}
@ -27,7 +29,7 @@ func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*imaget
platform := platforms.AllPlatformsWithPreference(cplatforms.Default())
var presentImages []ocispec.Image
err = i.walkImageManifests(ctx, desc, func(img *ImageManifest) error {
err = i.walkImageManifests(ctx, img, func(img *ImageManifest) error {
conf, err := img.Config(ctx)
if err != nil {
return err
@ -89,26 +91,43 @@ func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*imaget
}}, history...)
}
if len(history) != 0 {
history[0].ID = desc.Target.Digest.String()
tagged, err := i.client.ImageService().List(ctx, "target.digest=="+desc.Target.Digest.String())
findParents := func(img images.Image) []images.Image {
imgs, err := i.getParentsByBuilderLabel(ctx, img)
if err != nil {
return nil, err
log.G(ctx).WithFields(log.Fields{
"error": err,
"image": img,
}).Warnf("failed to list parent images")
return nil
}
return imgs
}
var tags []string
for _, t := range tagged {
if isDanglingImage(t) {
currentImg := img
for _, h := range history {
h.ID = currentImg.Target.Digest.String()
imgs := findParents(currentImg)
foundNext := false
for _, img := range imgs {
if _, ok := img.Labels[imageLabelClassicBuilderParent]; ok {
currentImg = img
foundNext = true
}
if isDanglingImage(img) {
continue
}
name, err := reference.ParseNamed(t.Name)
name, err := reference.ParseNamed(img.Name)
if err != nil {
return nil, err
}
tags = append(tags, reference.FamiliarString(name))
h.Tags = append(h.Tags, reference.FamiliarString(name))
}
if !foundNext {
break
}
history[0].Tags = tags
}
return history, nil