From cd8c8ae4bc57e873509734cbd7b41968720dd0f8 Mon Sep 17 00:00:00 2001 From: Jin Dong Date: Sat, 19 Aug 2023 00:33:26 -0700 Subject: [PATCH] Remove hashicorp/go-multierror Signed-off-by: Jin Dong --- go.mod | 2 +- pkg/cri/sbserver/podsandbox/sandbox_run.go | 3 +- pkg/cri/sbserver/sandbox_run.go | 5 +-- pkg/cri/sbserver/sandbox_stats_list.go | 8 ++-- pkg/cri/server/sandbox_stats_list.go | 9 ++--- pkg/process/io.go | 40 +++++++++--------- plugins/streaming/manager.go | 9 ++--- runtime/v2/shim.go | 17 ++++---- snapshots/devmapper/config.go | 16 ++++---- snapshots/devmapper/config_test.go | 13 +++--- snapshots/devmapper/pool_device.go | 47 ++++++++++------------ snapshots/devmapper/snapshotter.go | 27 ++++++------- snapshots/devmapper/snapshotter_test.go | 6 +-- snapshots/storage/metastore.go | 16 ++++---- 14 files changed, 101 insertions(+), 117 deletions(-) diff --git a/go.mod b/go.mod index b0e1eb09b..1acb9034e 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,6 @@ require ( github.com/google/uuid v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/hashicorp/go-multierror v1.1.1 github.com/intel/goresctrl v0.3.0 github.com/klauspost/compress v1.16.7 github.com/minio/sha256-simd v1.0.1 @@ -99,6 +98,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect diff --git a/pkg/cri/sbserver/podsandbox/sandbox_run.go b/pkg/cri/sbserver/podsandbox/sandbox_run.go index 6357be370..6ef941ccb 100644 --- a/pkg/cri/sbserver/podsandbox/sandbox_run.go +++ b/pkg/cri/sbserver/podsandbox/sandbox_run.go @@ -26,7 +26,6 @@ import ( v1 "github.com/containerd/nri/types/v1" "github.com/containerd/typeurl/v2" "github.com/davecgh/go-spew/spew" - "github.com/hashicorp/go-multierror" "github.com/opencontainers/selinux/go-selinux" runtime "k8s.io/cri-api/pkg/apis/runtime/v1" @@ -61,7 +60,7 @@ func (c *Controller) Start(ctx context.Context, id string) (cin sandbox.Controll defer func() { if retErr != nil && cleanupErr != nil { log.G(ctx).WithField("id", id).WithError(cleanupErr).Errorf("failed to fully teardown sandbox resources after earlier error: %s", retErr) - retErr = multierror.Append(retErr, CleanupErr{cleanupErr}) + retErr = errors.Join(retErr, CleanupErr{cleanupErr}) } }() diff --git a/pkg/cri/sbserver/sandbox_run.go b/pkg/cri/sbserver/sandbox_run.go index c6baa7709..1b8e520cf 100644 --- a/pkg/cri/sbserver/sandbox_run.go +++ b/pkg/cri/sbserver/sandbox_run.go @@ -28,7 +28,6 @@ import ( "github.com/containerd/go-cni" "github.com/containerd/typeurl/v2" - "github.com/hashicorp/go-multierror" runtime "k8s.io/cri-api/pkg/apis/runtime/v1" "github.com/containerd/containerd" @@ -258,8 +257,8 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox cleanupErr = fmt.Errorf("failed to cleanup sandbox: %w", cerr) // Strip last error as cleanup error to handle separately - if merr, ok := err.(*multierror.Error); ok { - if errs := merr.WrappedErrors(); len(errs) > 0 { + if merr, ok := err.(interface{ Unwrap() []error }); ok { + if errs := merr.Unwrap(); len(errs) > 0 { err = errs[0] } } diff --git a/pkg/cri/sbserver/sandbox_stats_list.go b/pkg/cri/sbserver/sandbox_stats_list.go index e91e55df4..3e5ff1399 100644 --- a/pkg/cri/sbserver/sandbox_stats_list.go +++ b/pkg/cri/sbserver/sandbox_stats_list.go @@ -18,12 +18,12 @@ package sbserver import ( "context" + "errors" "fmt" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/log" sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox" - "github.com/hashicorp/go-multierror" runtime "k8s.io/cri-api/pkg/apis/runtime/v1" ) @@ -34,7 +34,7 @@ func (c *criService) ListPodSandboxStats( ) (*runtime.ListPodSandboxStatsResponse, error) { sandboxes := c.sandboxesForListPodSandboxStatsRequest(r) - var errs *multierror.Error + var errs []error podSandboxStats := new(runtime.ListPodSandboxStatsResponse) for _, sandbox := range sandboxes { sandboxStats, err := c.podSandboxStats(ctx, sandbox) @@ -42,13 +42,13 @@ func (c *criService) ListPodSandboxStats( case errdefs.IsUnavailable(err): log.G(ctx).WithField("podsandboxid", sandbox.ID).Debugf("failed to get pod sandbox stats, this is likely a transient error: %v", err) case err != nil: - errs = multierror.Append(errs, fmt.Errorf("failed to decode sandbox container metrics for sandbox %q: %w", sandbox.ID, err)) + errs = append(errs, fmt.Errorf("failed to decode sandbox container metrics for sandbox %q: %w", sandbox.ID, err)) default: podSandboxStats.Stats = append(podSandboxStats.Stats, sandboxStats) } } - return podSandboxStats, errs.ErrorOrNil() + return podSandboxStats, errors.Join(errs...) } func (c *criService) sandboxesForListPodSandboxStatsRequest(r *runtime.ListPodSandboxStatsRequest) []sandboxstore.Sandbox { diff --git a/pkg/cri/server/sandbox_stats_list.go b/pkg/cri/server/sandbox_stats_list.go index cf21fd830..d237b18d4 100644 --- a/pkg/cri/server/sandbox_stats_list.go +++ b/pkg/cri/server/sandbox_stats_list.go @@ -18,14 +18,13 @@ package server import ( "context" + "errors" "fmt" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/log" sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox" runtime "k8s.io/cri-api/pkg/apis/runtime/v1" - - "github.com/hashicorp/go-multierror" ) // ListPodSandboxStats returns stats of all ready sandboxes. @@ -35,7 +34,7 @@ func (c *criService) ListPodSandboxStats( ) (*runtime.ListPodSandboxStatsResponse, error) { sandboxes := c.sandboxesForListPodSandboxStatsRequest(r) - var errs *multierror.Error + var errs []error podSandboxStats := new(runtime.ListPodSandboxStatsResponse) for _, sandbox := range sandboxes { sandboxStats, err := c.podSandboxStats(ctx, sandbox) @@ -43,13 +42,13 @@ func (c *criService) ListPodSandboxStats( case errdefs.IsUnavailable(err): log.G(ctx).WithField("podsandboxid", sandbox.ID).Debugf("failed to get pod sandbox stats, this is likely a transient error: %v", err) case err != nil: - errs = multierror.Append(errs, fmt.Errorf("failed to decode sandbox container metrics for sandbox %q: %w", sandbox.ID, err)) + errs = append(errs, fmt.Errorf("failed to decode sandbox container metrics for sandbox %q: %w", sandbox.ID, err)) default: podSandboxStats.Stats = append(podSandboxStats.Stats, sandboxStats) } } - return podSandboxStats, errs.ErrorOrNil() + return podSandboxStats, errors.Join(errs...) } func (c *criService) sandboxesForListPodSandboxStatsRequest(r *runtime.ListPodSandboxStatsRequest) []sandboxstore.Sandbox { diff --git a/pkg/process/io.go b/pkg/process/io.go index 687d5a2a3..a13b27c92 100644 --- a/pkg/process/io.go +++ b/pkg/process/io.go @@ -20,6 +20,7 @@ package process import ( "context" + "errors" "fmt" "io" "net/url" @@ -35,7 +36,6 @@ import ( "github.com/containerd/containerd/pkg/stdio" "github.com/containerd/fifo" runc "github.com/containerd/go-runc" - "github.com/hashicorp/go-multierror" exec "golang.org/x/sys/execabs" ) @@ -255,11 +255,11 @@ func NewBinaryIO(ctx context.Context, id string, uri *url.URL) (_ runc.IO, err e if err == nil { return } - result := multierror.Append(err) + result := []error{err} for _, fn := range closers { - result = multierror.Append(result, fn()) + result = append(result, fn()) } - err = multierror.Flatten(result) + err = errors.Join(result...) }() out, err := newPipe() @@ -313,39 +313,35 @@ type binaryIO struct { } func (b *binaryIO) CloseAfterStart() error { - var ( - result *multierror.Error - ) + var result []error for _, v := range []*pipe{b.out, b.err} { if v != nil { if err := v.r.Close(); err != nil { - result = multierror.Append(result, err) + result = append(result, err) } } } - return result.ErrorOrNil() + return errors.Join(result...) } func (b *binaryIO) Close() error { - var ( - result *multierror.Error - ) + var result []error for _, v := range []*pipe{b.out, b.err} { if v != nil { if err := v.Close(); err != nil { - result = multierror.Append(result, err) + result = append(result, err) } } } if err := b.cancel(); err != nil { - result = multierror.Append(result, err) + result = append(result, err) } - return result.ErrorOrNil() + return errors.Join(result...) } func (b *binaryIO) cancel() error { @@ -355,15 +351,15 @@ func (b *binaryIO) cancel() error { // Send SIGTERM first, so logger process has a chance to flush and exit properly if err := b.cmd.Process.Signal(syscall.SIGTERM); err != nil { - result := multierror.Append(fmt.Errorf("failed to send SIGTERM: %w", err)) + result := []error{fmt.Errorf("failed to send SIGTERM: %w", err)} log.L.WithError(err).Warn("failed to send SIGTERM signal, killing logging shim") if err := b.cmd.Process.Kill(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to kill process after faulty SIGTERM: %w", err)) + result = append(result, fmt.Errorf("failed to kill process after faulty SIGTERM: %w", err)) } - return result.ErrorOrNil() + return errors.Join(result...) } done := make(chan error, 1) @@ -424,15 +420,15 @@ type pipe struct { } func (p *pipe) Close() error { - var result *multierror.Error + var result []error if err := p.w.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close write pipe: %w", err)) + result = append(result, fmt.Errorf("pipe: failed to close write pipe: %w", err)) } if err := p.r.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close read pipe: %w", err)) + result = append(result, fmt.Errorf("pipe: failed to close read pipe: %w", err)) } - return multierror.Prefix(result.ErrorOrNil(), "pipe:") + return errors.Join(result...) } diff --git a/plugins/streaming/manager.go b/plugins/streaming/manager.go index 85a4debac..8c9546d7e 100644 --- a/plugins/streaming/manager.go +++ b/plugins/streaming/manager.go @@ -18,6 +18,7 @@ package streaming import ( "context" + "errors" "sync" "github.com/containerd/containerd/errdefs" @@ -27,8 +28,6 @@ import ( "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/pkg/streaming" "github.com/containerd/containerd/plugin" - - "github.com/hashicorp/go-multierror" ) func init() { @@ -256,12 +255,12 @@ func (cc *collectionContext) Finish() error { } cc.manager.rwlock.Unlock() - var errs *multierror.Error + var errs []error for _, s := range closeStreams { if err := s.Close(); err != nil { - errs = multierror.Append(errs, err) + errs = append(errs, err) } } - return errs.ErrorOrNil() + return errors.Join(errs...) } diff --git a/runtime/v2/shim.go b/runtime/v2/shim.go index f4265c17f..0a47b90db 100644 --- a/runtime/v2/shim.go +++ b/runtime/v2/shim.go @@ -28,7 +28,6 @@ import ( "time" "github.com/containerd/ttrpc" - "github.com/hashicorp/go-multierror" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials/insecure" @@ -354,36 +353,34 @@ func (s *shim) Close() error { } func (s *shim) Delete(ctx context.Context) error { - var ( - result *multierror.Error - ) + var result []error if ttrpcClient, ok := s.client.(*ttrpc.Client); ok { if err := ttrpcClient.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close ttrpc client: %w", err)) + result = append(result, fmt.Errorf("failed to close ttrpc client: %w", err)) } if err := ttrpcClient.UserOnCloseWait(ctx); err != nil { - result = multierror.Append(result, fmt.Errorf("close wait error: %w", err)) + result = append(result, fmt.Errorf("close wait error: %w", err)) } } if grpcClient, ok := s.client.(*grpcConn); ok { if err := grpcClient.Close(); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to close grpc client: %w", err)) + result = append(result, fmt.Errorf("failed to close grpc client: %w", err)) } if err := grpcClient.UserOnCloseWait(ctx); err != nil { - result = multierror.Append(result, fmt.Errorf("close wait error: %w", err)) + result = append(result, fmt.Errorf("close wait error: %w", err)) } } if err := s.bundle.Delete(); err != nil { log.G(ctx).WithField("id", s.ID()).WithError(err).Error("failed to delete bundle") - result = multierror.Append(result, fmt.Errorf("failed to delete bundle: %w", err)) + result = append(result, fmt.Errorf("failed to delete bundle: %w", err)) } - return result.ErrorOrNil() + return errors.Join(result...) } var _ runtime.Task = &shimTask{} diff --git a/snapshots/devmapper/config.go b/snapshots/devmapper/config.go index 5b82de539..cc38cb33a 100644 --- a/snapshots/devmapper/config.go +++ b/snapshots/devmapper/config.go @@ -19,11 +19,11 @@ package devmapper import ( + "errors" "fmt" "os" "github.com/docker/go-units" - "github.com/hashicorp/go-multierror" "github.com/pelletier/go-toml" ) @@ -100,29 +100,29 @@ func (c *Config) parse() error { // Validate makes sure configuration fields are valid func (c *Config) Validate() error { - var result *multierror.Error + var result []error if c.PoolName == "" { - result = multierror.Append(result, fmt.Errorf("pool_name is required")) + result = append(result, fmt.Errorf("pool_name is required")) } if c.RootPath == "" { - result = multierror.Append(result, fmt.Errorf("root_path is required")) + result = append(result, fmt.Errorf("root_path is required")) } if c.BaseImageSize == "" { - result = multierror.Append(result, fmt.Errorf("base_image_size is required")) + result = append(result, fmt.Errorf("base_image_size is required")) } if c.FileSystemType != "" { switch c.FileSystemType { case fsTypeExt4, fsTypeXFS, fsTypeExt2: default: - result = multierror.Append(result, fmt.Errorf("unsupported Filesystem Type: %q", c.FileSystemType)) + result = append(result, fmt.Errorf("unsupported Filesystem Type: %q", c.FileSystemType)) } } else { - result = multierror.Append(result, fmt.Errorf("filesystem type cannot be empty")) + result = append(result, fmt.Errorf("filesystem type cannot be empty")) } - return result.ErrorOrNil() + return errors.Join(result...) } diff --git a/snapshots/devmapper/config_test.go b/snapshots/devmapper/config_test.go index 1ee740e66..eeb0fc8eb 100644 --- a/snapshots/devmapper/config_test.go +++ b/snapshots/devmapper/config_test.go @@ -22,7 +22,6 @@ import ( "os" "testing" - "github.com/hashicorp/go-multierror" "github.com/pelletier/go-toml" "github.com/stretchr/testify/assert" ) @@ -80,13 +79,13 @@ func TestFieldValidation(t *testing.T) { err := config.Validate() assert.NotNil(t, err) - multErr := (err).(*multierror.Error) - assert.Len(t, multErr.Errors, 4) + multErr := err.(interface{ Unwrap() []error }).Unwrap() + assert.Len(t, multErr, 4) - assert.NotNil(t, multErr.Errors[0], "pool_name is empty") - assert.NotNil(t, multErr.Errors[1], "root_path is empty") - assert.NotNil(t, multErr.Errors[2], "base_image_size is empty") - assert.NotNil(t, multErr.Errors[3], "filesystem type cannot be empty") + assert.NotNil(t, multErr[0], "pool_name is empty") + assert.NotNil(t, multErr[1], "root_path is empty") + assert.NotNil(t, multErr[2], "base_image_size is empty") + assert.NotNil(t, multErr[3], "filesystem type cannot be empty") } func TestExistingPoolFieldValidation(t *testing.T) { diff --git a/snapshots/devmapper/pool_device.go b/snapshots/devmapper/pool_device.go index 4fe9888da..20117d1c3 100644 --- a/snapshots/devmapper/pool_device.go +++ b/snapshots/devmapper/pool_device.go @@ -26,7 +26,6 @@ import ( "strconv" "time" - "github.com/hashicorp/go-multierror" "golang.org/x/sys/unix" "github.com/containerd/containerd/log" @@ -145,7 +144,7 @@ func (p *PoolDevice) ensureDeviceStates(ctx context.Context) error { return fmt.Errorf("failed to query devices from metastore: %w", err) } - var result *multierror.Error + var result []error for _, dev := range activatedDevices { if p.IsActivated(dev.Name) { continue @@ -153,7 +152,7 @@ func (p *PoolDevice) ensureDeviceStates(ctx context.Context) error { log.G(ctx).Warnf("devmapper device %q marked as %q but not active, activating it", dev.Name, dev.State) if err := p.activateDevice(ctx, dev); err != nil { - result = multierror.Append(result, err) + result = append(result, fmt.Errorf("devmapper: %w", err)) } } @@ -165,11 +164,11 @@ func (p *PoolDevice) ensureDeviceStates(ctx context.Context) error { Warnf("devmapper device %q has invalid state %q, marking as faulty", dev.Name, dev.State) if err := p.metadata.MarkFaulty(ctx, dev.Name); err != nil { - result = multierror.Append(result, err) + result = append(result, fmt.Errorf("devmapper: %w", err)) } } - return multierror.Prefix(result.ErrorOrNil(), "devmapper:") + return errors.Join(result...) } // transition invokes 'updateStateFn' callback to perform devmapper operation and reflects device state changes/errors in meta store. @@ -186,13 +185,13 @@ func (p *PoolDevice) transition(ctx context.Context, deviceName string, tryingSt return fmt.Errorf("failed to set device %q state to %q: %w", deviceName, tryingState, uerr) } - var result *multierror.Error + var result []error // Invoke devmapper operation err := updateStateFn() if err != nil { - result = multierror.Append(result, err) + result = append(result, err) } // If operation succeeded transition to success state, otherwise save error details @@ -207,25 +206,23 @@ func (p *PoolDevice) transition(ctx context.Context, deviceName string, tryingSt }) if uerr != nil { - result = multierror.Append(result, uerr) + result = append(result, uerr) } - return unwrapError(result) + return unwrapError(errors.Join(result...)) } -// unwrapError converts multierror.Error to the original error when it is possible. -// multierror 1.1.0 has the similar function named Unwrap, but it requires Go 1.14. -func unwrapError(e *multierror.Error) error { +func unwrapError(e error) error { if e == nil { return nil } - // If the error can be expressed without multierror, return the original error. - if len(e.Errors) == 1 { - return e.Errors[0] + if joinErr, ok := e.(interface{ Unwrap() []error }); ok { + if errs := joinErr.Unwrap(); len(errs) == 1 { + return errs[0] + } } - - return e.ErrorOrNil() + return e } // CreateThinDevice creates new devmapper thin-device with given name and size. @@ -253,7 +250,7 @@ func (p *PoolDevice) CreateThinDevice(ctx context.Context, deviceName string, vi // We're unable to create the devmapper device, most likely something wrong with the deviceID if devErr != nil { - retErr = multierror.Append(retErr, p.metadata.MarkFaulty(ctx, info.Name)) + retErr = errors.Join(retErr, p.metadata.MarkFaulty(ctx, info.Name)) return } }() @@ -285,12 +282,12 @@ func (p *PoolDevice) rollbackActivate(ctx context.Context, info *DeviceInfo, act if delErr != nil { // Failed to rollback, mark the device as faulty and keep metadata in order to // preserve the faulty device ID - return multierror.Append(activateErr, delErr, p.metadata.MarkFaulty(ctx, info.Name)) + return errors.Join(activateErr, delErr, p.metadata.MarkFaulty(ctx, info.Name)) } // The devmapper device has been successfully deleted, deallocate device ID if err := p.RemoveDevice(ctx, info.Name); err != nil { - return multierror.Append(activateErr, err) + return errors.Join(activateErr, err) } return activateErr @@ -347,7 +344,7 @@ func (p *PoolDevice) CreateSnapshotDevice(ctx context.Context, deviceName string // We're unable to create the devmapper device, most likely something wrong with the deviceID if devErr != nil { - retErr = multierror.Append(retErr, p.metadata.MarkFaulty(ctx, snapInfo.Name)) + retErr = errors.Join(retErr, p.metadata.MarkFaulty(ctx, snapInfo.Name)) return } }() @@ -561,20 +558,20 @@ func (p *PoolDevice) RemovePool(ctx context.Context) error { return fmt.Errorf("can't query device names: %w", err) } - var result *multierror.Error + var result []error // Deactivate devices if any for _, name := range deviceNames { if err := p.DeactivateDevice(ctx, name, true, true); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to remove %q: %w", name, err)) + result = append(result, fmt.Errorf("failed to remove %q: %w", name, err)) } } if err := dmsetup.RemoveDevice(p.poolName, dmsetup.RemoveWithForce, dmsetup.RemoveWithRetries, dmsetup.RemoveDeferred); err != nil { - result = multierror.Append(result, fmt.Errorf("failed to remove pool %q: %w", p.poolName, err)) + result = append(result, fmt.Errorf("failed to remove pool %q: %w", p.poolName, err)) } - return result.ErrorOrNil() + return errors.Join(result...) } // MarkDeviceState changes the device's state in metastore diff --git a/snapshots/devmapper/snapshotter.go b/snapshots/devmapper/snapshotter.go index a801cbc26..a96da4c45 100644 --- a/snapshots/devmapper/snapshotter.go +++ b/snapshots/devmapper/snapshotter.go @@ -33,7 +33,6 @@ import ( "github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots/devmapper/dmsetup" "github.com/containerd/containerd/snapshots/storage" - "github.com/hashicorp/go-multierror" exec "golang.org/x/sys/execabs" ) @@ -344,14 +343,14 @@ func (s *Snapshotter) ResetPool(ctx context.Context) error { return err } - var result *multierror.Error + var result []error for _, name := range names { if err := s.pool.RemoveDevice(ctx, name); err != nil { - result = multierror.Append(result, err) + result = append(result, err) } } - return result.ErrorOrNil() + return errors.Join(result...) } // Close releases devmapper snapshotter resources. @@ -359,16 +358,16 @@ func (s *Snapshotter) ResetPool(ctx context.Context) error { func (s *Snapshotter) Close() error { log.L.Debug("close") - var result *multierror.Error + var result []error s.closeOnce.Do(func() { for _, fn := range s.cleanupFn { if err := fn(); err != nil { - result = multierror.Append(result, err) + result = append(result, err) } } }) - return result.ErrorOrNil() + return errors.Join(result...) } func (s *Snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { @@ -423,15 +422,15 @@ func (s *Snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k } log.G(ctx).Debugf("Creating file system of type: %s with options: %s for thin device %q", s.config.FileSystemType, fsOptions, deviceName) if err := mkfs(ctx, s.config.FileSystemType, fsOptions, dmsetup.GetFullDevicePath(deviceName)); err != nil { + errs := []error{err} status, sErr := dmsetup.Status(s.pool.poolName) if sErr != nil { - multierror.Append(err, sErr) + errs = append(errs, sErr) } // Rollback thin device creation if mkfs failed - log.G(ctx).WithError(err).Errorf("failed to initialize thin device %q for snapshot %s pool status %s", deviceName, snap.ID, status.RawOutput) - return nil, multierror.Append(err, - s.pool.RemoveDevice(ctx, deviceName)) + log.G(ctx).WithError(errors.Join(errs...)).Errorf("failed to initialize thin device %q for snapshot %s pool status %s", deviceName, snap.ID, status.RawOutput) + return nil, errors.Join(append(errs, s.pool.RemoveDevice(ctx, deviceName))...) } } else { parentDeviceName := s.getDeviceName(snap.ParentIDs[0]) @@ -551,16 +550,16 @@ func (s *Snapshotter) Cleanup(ctx context.Context) error { return err } - var result *multierror.Error + var result []error for _, dev := range removedDevices { log.G(ctx).WithField("device", dev.Name).Debug("cleanup device") if err := s.pool.RemoveDevice(ctx, dev.Name); err != nil { log.G(ctx).WithField("device", dev.Name).Error("failed to cleanup device") - result = multierror.Append(result, err) + result = append(result, err) } else { log.G(ctx).WithField("device", dev.Name).Debug("cleanuped device") } } - return result.ErrorOrNil() + return errors.Join(result...) } diff --git a/snapshots/devmapper/snapshotter_test.go b/snapshots/devmapper/snapshotter_test.go index a596d4e8e..1f303da72 100644 --- a/snapshots/devmapper/snapshotter_test.go +++ b/snapshots/devmapper/snapshotter_test.go @@ -21,12 +21,12 @@ package devmapper import ( "context" _ "crypto/sha256" + "errors" "fmt" "testing" "time" "github.com/containerd/continuity/fs/fstest" - "github.com/hashicorp/go-multierror" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -199,11 +199,11 @@ func createSnapshotter(ctx context.Context, t *testing.T, config *Config) (snaps // Remove device mapper pool and detach loop devices after test completes removePool := func() error { - result := multierror.Append( + result := errors.Join( snap.pool.RemovePool(ctx), mount.DetachLoopDevice(loopDataDevice, loopMetaDevice)) - return result.ErrorOrNil() + return result } // Pool cleanup should be called before closing metadata store (as we need to retrieve device names) diff --git a/snapshots/storage/metastore.go b/snapshots/storage/metastore.go index fe39fd2ed..f4623b877 100644 --- a/snapshots/storage/metastore.go +++ b/snapshots/storage/metastore.go @@ -23,12 +23,12 @@ package storage import ( "context" + "errors" "fmt" "sync" "github.com/containerd/containerd/log" "github.com/containerd/containerd/snapshots" - "github.com/hashicorp/go-multierror" bolt "go.etcd.io/bbolt" ) @@ -117,10 +117,10 @@ func (ms *MetaStore) WithTransaction(ctx context.Context, writable bool, fn Tran return err } - var result *multierror.Error + var result []error err = fn(ctx) if err != nil { - result = multierror.Append(result, err) + result = append(result, err) } // Always rollback if transaction is not writable @@ -128,22 +128,22 @@ func (ms *MetaStore) WithTransaction(ctx context.Context, writable bool, fn Tran if terr := trans.Rollback(); terr != nil { log.G(ctx).WithError(terr).Error("failed to rollback transaction") - result = multierror.Append(result, fmt.Errorf("rollback failed: %w", terr)) + result = append(result, fmt.Errorf("rollback failed: %w", terr)) } } else { if terr := trans.Commit(); terr != nil { log.G(ctx).WithError(terr).Error("failed to commit transaction") - result = multierror.Append(result, fmt.Errorf("commit failed: %w", terr)) + result = append(result, fmt.Errorf("commit failed: %w", terr)) } } - if err := result.ErrorOrNil(); err != nil { + if err := errors.Join(result...); err != nil { log.G(ctx).WithError(err).Debug("snapshotter error") // Unwrap if just one error - if len(result.Errors) == 1 { - return result.Errors[0] + if errs, ok := err.(interface{ Unwrap() []error }); ok && len(errs.Unwrap()) == 1 { + return errs.Unwrap()[0] } return err }