images/Children: Return error

Change return value in function signature and return fatal errors so
they can actually be reported to the caller instead of just being logged
to daemon log.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski 2023-04-13 15:07:17 +02:00
parent 45483a1d0d
commit 9cb5da400c
No known key found for this signature in database
GPG key ID: B85EFCFE26DEF92A
4 changed files with 43 additions and 31 deletions

View file

@ -7,46 +7,49 @@ import (
cerrdefs "github.com/containerd/containerd/errdefs"
containerdimages "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// Children returns a slice of image ID which rootfs is a superset of the
// rootfs of the given image ID, excluding images with exactly the same rootfs.
// Called from list.go to filter containers.
func (i *ImageService) Children(ctx context.Context, id image.ID) []image.ID {
func (i *ImageService) Children(ctx context.Context, id image.ID) ([]image.ID, error) {
target, err := i.resolveDescriptor(ctx, id.String())
if err != nil {
logrus.WithError(err).Error("failed to get parent image")
return []image.ID{}
return []image.ID{}, errors.Wrap(err, "failed to get parent image")
}
is := i.client.ImageService()
cs := i.client.ContentStore()
log := logrus.WithField("id", id)
allPlatforms, err := containerdimages.Platforms(ctx, cs, target)
if err != nil {
log.WithError(err).Error("failed to list supported platorms of image")
return []image.ID{}
return []image.ID{}, errdefs.System(errors.Wrap(err, "failed to list platforms supported by image"))
}
parentRootFS := []ocispec.RootFS{}
for _, platform := range allPlatforms {
rootfs, err := platformRootfs(ctx, cs, target, platform)
if err != nil {
if !cerrdefs.IsNotFound(err) {
logrus.WithFields(logrus.Fields{
logrus.ErrorKey: err,
"image": target.Digest,
"platform": platform,
}).Warning("failed to get platform-specific rootfs")
}
continue
}
parentRootFS = append(parentRootFS, rootfs)
}
imgs, err := is.List(ctx)
imgs, err := i.client.ImageService().List(ctx)
if err != nil {
log.WithError(err).Error("failed to list all images")
return []image.ID{}
return []image.ID{}, errdefs.System(errors.Wrap(err, "failed to list all images"))
}
children := []image.ID{}
@ -55,6 +58,13 @@ func (i *ImageService) Children(ctx context.Context, id image.ID) []image.ID {
for _, platform := range allPlatforms {
rootfs, err := platformRootfs(ctx, cs, img.Target, platform)
if err != nil {
if !cerrdefs.IsNotFound(err) {
logrus.WithFields(logrus.Fields{
logrus.ErrorKey: err,
"image": img.Target.Digest,
"platform": platform,
}).Warning("failed to get platform-specific rootfs")
}
continue
}
@ -68,29 +78,21 @@ func (i *ImageService) Children(ctx context.Context, id image.ID) []image.ID {
}
return children
return children, nil
}
// platformRootfs returns a rootfs for a specified platform.
func platformRootfs(ctx context.Context, store content.Store, desc ocispec.Descriptor, platform ocispec.Platform) (ocispec.RootFS, error) {
empty := ocispec.RootFS{}
log := logrus.WithField("desc", desc.Digest).WithField("platform", platforms.Format(platform))
configDesc, err := containerdimages.Config(ctx, store, desc, platforms.OnlyStrict(platform))
if err != nil {
if !cerrdefs.IsNotFound(err) {
log.WithError(err).Warning("failed to get parent image config")
}
return empty, err
return empty, errors.Wrapf(err, "failed to get config for platform %s", platforms.Format(platform))
}
log = log.WithField("configDesc", configDesc)
diffs, err := containerdimages.RootFS(ctx, store, configDesc)
if err != nil {
if !cerrdefs.IsNotFound(err) {
log.WithError(err).Warning("failed to get parent image rootfs")
}
return empty, err
return empty, errors.Wrapf(err, "failed to obtain rootfs")
}
return ocispec.RootFS{

View file

@ -73,7 +73,7 @@ type ImageService interface {
// Other
DistributionServices() images.DistributionServices
Children(ctx context.Context, id image.ID) []image.ID
Children(ctx context.Context, id image.ID) ([]image.ID, error)
Cleanup() error
StorageDriver() string
UpdateConfig(maxDownloads, maxUploads int)

View file

@ -109,8 +109,8 @@ func (i *ImageService) CountImages() int {
// Children returns the children image.IDs for a parent image.
// called from list.go to filter containers
// TODO: refactor to expose an ancestry for image.ID?
func (i *ImageService) Children(_ context.Context, id image.ID) []image.ID {
return i.imageStore.Children(id)
func (i *ImageService) Children(_ context.Context, id image.ID) ([]image.ID, error) {
return i.imageStore.Children(id), nil
}
// CreateLayer creates a filesystem layer for a container.

View file

@ -317,7 +317,7 @@ func (daemon *Daemon) foldFilter(ctx context.Context, view *container.View, conf
var ancestorFilter bool
if psFilters.Contains("ancestor") {
ancestorFilter = true
psFilters.WalkValues("ancestor", func(ancestor string) error {
err := psFilters.WalkValues("ancestor", func(ancestor string) error {
img, err := daemon.imageService.GetImage(ctx, ancestor, imagetypes.GetImageOpts{})
if err != nil {
logrus.Warnf("Error while looking up for image %v", ancestor)
@ -328,9 +328,12 @@ func (daemon *Daemon) foldFilter(ctx context.Context, view *container.View, conf
return nil
}
// Then walk down the graph and put the imageIds in imagesFilter
populateImageFilterByParents(ctx, imagesFilter, img.ID(), daemon.imageService.Children)
return nil
return populateImageFilterByParents(ctx, imagesFilter, img.ID(), daemon.imageService.Children)
})
if err != nil {
return nil, err
}
}
publishFilter := map[nat.Port]bool{}
@ -594,11 +597,18 @@ func (daemon *Daemon) refreshImage(ctx context.Context, s *container.Snapshot, f
return &c, nil
}
func populateImageFilterByParents(ctx context.Context, ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(context.Context, image.ID) []image.ID) {
func populateImageFilterByParents(ctx context.Context, ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(context.Context, image.ID) ([]image.ID, error)) error {
if !ancestorMap[imageID] {
for _, id := range getChildren(ctx, imageID) {
populateImageFilterByParents(ctx, ancestorMap, id, getChildren)
children, err := getChildren(ctx, imageID)
if err != nil {
return err
}
for _, id := range children {
if err := populateImageFilterByParents(ctx, ancestorMap, id, getChildren); err != nil {
return err
}
}
ancestorMap[imageID] = true
}
return nil
}