mirror of
https://github.com/docker/compose.git
synced 2026-10-12 02:13:08 +00:00
fix: report every tagged-image removal failure, not just one
removeTaggedImagesOp switched to a plain errgroup.Group so one image's failure can't cancel the others still in flight, but Wait() only ever surfaces the first error returned to it, silently dropping any other concurrent failures. Aggregate every failure instead, the same way removeImages already separates tolerated outcomes (still-in-use, already-gone) from real errors before joining them. Also give a tagged-image listing failure its own "Tagged images" label instead of the generic Compose-wide one, matching the dedicated label the dangling-image path already has. Signed-off-by: Guillaume Lours <[email protected]>
This commit is contained in:
committed by
Nicolas De loof
parent
2ae716faac
commit
a5eb970da5
+14
-4
@@ -18,8 +18,10 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
@@ -186,22 +188,30 @@ func (s *composeService) ensureImagesDown(ctx context.Context, project *types.Pr
|
||||
func (s *composeService) removeTaggedImagesOp(ctx context.Context, project *types.Project, pruneOpts ImagePruneOptions) error {
|
||||
images, err := NewImagePruner(s.apiClient(), project).ImagesToPrune(ctx, pruneOpts)
|
||||
if err != nil {
|
||||
s.events.On(errorEvent(api.ResourceCompose, err.Error()))
|
||||
s.events.On(errorEvent("Tagged images", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
var mu sync.Mutex
|
||||
var errs []error
|
||||
var eg errgroup.Group
|
||||
eg.SetLimit(s.maxConcurrency)
|
||||
for i := range images {
|
||||
img := images[i]
|
||||
eg.Go(func() error {
|
||||
return s.removeResource("Image "+img, func() error {
|
||||
if err := s.removeResource("Image "+img, func() error {
|
||||
_, err := s.apiClient().ImageRemove(ctx, img, client.ImageRemoveOptions{})
|
||||
return err
|
||||
})
|
||||
}); err != nil {
|
||||
mu.Lock()
|
||||
errs = append(errs, err)
|
||||
mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return eg.Wait()
|
||||
_ = eg.Wait() // errgroup is only used for fan-out here; goroutines never return an error
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// removeDanglingImagesOp lists a project's dangling images and removes those
|
||||
|
||||
@@ -506,7 +506,7 @@ func TestEnsureImagesDown_TaggedImageListingFailureStaysVisibleWithoutAbortingDo
|
||||
assert.NilError(t, ops[1]()) // dangling-images op is independent, still runs fine
|
||||
|
||||
assert.Equal(t, len(rec.resources), 1)
|
||||
assert.Equal(t, rec.resources[0].ID, compose.ResourceCompose)
|
||||
assert.Equal(t, rec.resources[0].ID, "Tagged images")
|
||||
assert.Equal(t, rec.resources[0].Status, compose.Error)
|
||||
assert.Equal(t, rec.resources[0].Details, listErr.Error())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user