Introduce a helper that collects cleanup functions
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
parent
f07387466a
commit
f2e1105056
2 changed files with 95 additions and 0 deletions
42
internal/cleanups/composite.go
Normal file
42
internal/cleanups/composite.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package cleanups
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/internal/multierror"
|
||||
)
|
||||
|
||||
type Composite struct {
|
||||
cleanups []func() error
|
||||
}
|
||||
|
||||
// Add adds a cleanup to be called.
|
||||
func (c *Composite) Add(f func() error) {
|
||||
c.cleanups = append(c.cleanups, f)
|
||||
}
|
||||
|
||||
// Call calls all cleanups in reverse order and returns an error combining all
|
||||
// non-nil errors.
|
||||
func (c *Composite) Call() error {
|
||||
err := call(c.cleanups)
|
||||
c.cleanups = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Release removes all cleanups, turning Call into a no-op.
|
||||
// Caller still can call the cleanups by calling the returned function
|
||||
// which is equivalent to calling the Call before Release was called.
|
||||
func (c *Composite) Release() func() error {
|
||||
cleanups := c.cleanups
|
||||
c.cleanups = nil
|
||||
return func() error {
|
||||
return call(cleanups)
|
||||
}
|
||||
}
|
||||
|
||||
func call(cleanups []func() error) error {
|
||||
var errs []error
|
||||
for idx := len(cleanups) - 1; idx >= 0; idx-- {
|
||||
c := cleanups[idx]
|
||||
errs = append(errs, c())
|
||||
}
|
||||
return multierror.Join(errs...)
|
||||
}
|
53
internal/cleanups/composite_test.go
Normal file
53
internal/cleanups/composite_test.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package cleanups
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestCall(t *testing.T) {
|
||||
c := Composite{}
|
||||
var err1 = errors.New("error1")
|
||||
var err2 = errors.New("error2")
|
||||
var errX = errors.New("errorX")
|
||||
var errY = errors.New("errorY")
|
||||
var errZ = errors.New("errorZ")
|
||||
var errYZ = errors.Join(errY, errZ)
|
||||
|
||||
c.Add(func() error {
|
||||
return err1
|
||||
})
|
||||
c.Add(func() error {
|
||||
return nil
|
||||
})
|
||||
c.Add(func() error {
|
||||
return fmt.Errorf("something happened: %w", err2)
|
||||
})
|
||||
c.Add(func() error {
|
||||
return errors.Join(errX, fmt.Errorf("joined: %w", errYZ))
|
||||
})
|
||||
|
||||
err := c.Call()
|
||||
|
||||
errs := err.(interface{ Unwrap() []error }).Unwrap()
|
||||
|
||||
assert.Check(t, is.ErrorContains(err, err1.Error()))
|
||||
assert.Check(t, is.ErrorContains(err, err2.Error()))
|
||||
assert.Check(t, is.ErrorContains(err, errX.Error()))
|
||||
assert.Check(t, is.ErrorContains(err, errY.Error()))
|
||||
assert.Check(t, is.ErrorContains(err, errZ.Error()))
|
||||
assert.Check(t, is.ErrorContains(err, "something happened: "+err2.Error()))
|
||||
|
||||
t.Logf(err.Error())
|
||||
assert.Assert(t, is.Len(errs, 3))
|
||||
|
||||
// Cleanups executed in reverse order.
|
||||
assert.Check(t, is.ErrorIs(errs[2], err1))
|
||||
assert.Check(t, is.ErrorIs(errs[1], err2))
|
||||
assert.Check(t, is.ErrorIs(errs[0], errX))
|
||||
assert.Check(t, is.ErrorIs(errs[0], errYZ))
|
||||
}
|
Loading…
Reference in a new issue