Repository: moby/moby
File: daemon/containerd/image_prune.go (Go)

Code before:
	return usedDigests
}

// pruneAll deletes all images in the imagesToPrune map.
func (i *ImageService) pruneAll(ctx context.Context, imagesToPrune map[string]c8dimages.Image) (*image.PruneReport, error) {
	report := image.PruneReport{}

	ctx, span := tracing.StartSpan(ctx, "ImageService.pruneAll")
	span.SetAttributes(tracing.Attribute("count", len(imagesToPrune)))
	defer span.End()

	var errs error
	for _, img := range imagesToPrune {
		log.G(ctx).WithField("image", img).Debug("pruning image")

		blobs := []ocispec.Descriptor{}

		err := i.walkPresentChildren(ctx, img.Target, func(_ context.Context, desc ocispec.Descriptor) error {
			blobs = append(blobs, desc)
			return nil
		})
		if err != nil {
			errs = errors.Join(errs, err)
			if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
				return &report, errs
			}
			continue
		}
		err = i.images.Delete(ctx, img.Name, c8dimages.SynchronousDelete())
		if err != nil && !cerrdefs.IsNotFound(err) {
			errs = errors.Join(errs, err)
			if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
				return &report, errs
			}
			continue
		}

		report.ImagesDeleted = append(report.ImagesDeleted,
			image.DeleteResponse{
				Untagged: imageFamiliarName(img),
			},
		)

		// Check which blobs have been deleted and sum their sizes
		for _, blob := range blobs {
			_, err := i.content.ReaderAt(ctx, blob)

			if cerrdefs.IsNotFound(err) {
				report.ImagesDeleted = append(report.ImagesDeleted,
					image.DeleteResponse{
						Deleted: blob.Digest.String(),

Review comment (suggestion):
If this doesn't return, so `err` just gets appended, I think this will produce nested lists of errors?

In which case - if `errs` is declared as `var errs []error`, then ...

```suggestion
			errs = append(errs, err)
			if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
				return &report, return errors.Join(errs...)
			}
```

And the same in the next block, with the return at the end of the function being `return &report, errors.Join(errs...)`

Code after:
	return usedDigests
}

// pruneAll deletes all images in the imagesToPrune map.
func (i *ImageService) pruneAll(ctx context.Context, imagesToPrune map[string]c8dimages.Image) (*image.PruneReport, error) {
	report := image.PruneReport{}

	ctx, span := tracing.StartSpan(ctx, "ImageService.pruneAll")
	span.SetAttributes(tracing.Attribute("count", len(imagesToPrune)))
	defer span.End()

	var errs []error
	for _, img := range imagesToPrune {
		log.G(ctx).WithField("image", img).Debug("pruning image")

		blobs := []ocispec.Descriptor{}

		err := i.walkPresentChildren(ctx, img.Target, func(_ context.Context, desc ocispec.Descriptor) error {
			blobs = append(blobs, desc)
			return nil
		})
		if err != nil {
			errs = append(errs, err)
			if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
				return &report, errors.Join(errs...)
			}
			continue
		}
		err = i.images.Delete(ctx, img.Name, c8dimages.SynchronousDelete())
		if err != nil && !cerrdefs.IsNotFound(err) {
			errs = append(errs, err)
			if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
				return &report, errors.Join(errs...)
			}
			continue
		}

		report.ImagesDeleted = append(report.ImagesDeleted,
			image.DeleteResponse{
				Untagged: imageFamiliarName(img),
			},
		)

		// Check which blobs have been deleted and sum their sizes
		for _, blob := range blobs {
			_, err := i.content.ReaderAt(ctx, blob)

			if cerrdefs.IsNotFound(err) {
				report.ImagesDeleted = append(report.ImagesDeleted,
					image.DeleteResponse{
						Deleted: blob.Digest.String(),