Remove hashicorp/go-multierror

Signed-off-by: Jin Dong <jin.dong@databricks.com>
This commit is contained in:
Jin Dong 2023-08-19 00:33:26 -07:00
parent 89553637a7
commit cd8c8ae4bc
14 changed files with 101 additions and 117 deletions

2
go.mod
View File

@ -34,7 +34,6 @@ require (
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.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/intel/goresctrl v0.3.0
github.com/klauspost/compress v1.16.7 github.com/klauspost/compress v1.16.7
github.com/minio/sha256-simd v1.0.1 github.com/minio/sha256-simd v1.0.1
@ -99,6 +98,7 @@ require (
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/hashicorp/errwrap v1.1.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/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect

View File

@ -26,7 +26,6 @@ import (
v1 "github.com/containerd/nri/types/v1" v1 "github.com/containerd/nri/types/v1"
"github.com/containerd/typeurl/v2" "github.com/containerd/typeurl/v2"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/hashicorp/go-multierror"
"github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1" 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() { defer func() {
if retErr != nil && cleanupErr != nil { 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) 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})
} }
}() }()

View File

@ -28,7 +28,6 @@ import (
"github.com/containerd/go-cni" "github.com/containerd/go-cni"
"github.com/containerd/typeurl/v2" "github.com/containerd/typeurl/v2"
"github.com/hashicorp/go-multierror"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1" runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
"github.com/containerd/containerd" "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) cleanupErr = fmt.Errorf("failed to cleanup sandbox: %w", cerr)
// Strip last error as cleanup error to handle separately // Strip last error as cleanup error to handle separately
if merr, ok := err.(*multierror.Error); ok { if merr, ok := err.(interface{ Unwrap() []error }); ok {
if errs := merr.WrappedErrors(); len(errs) > 0 { if errs := merr.Unwrap(); len(errs) > 0 {
err = errs[0] err = errs[0]
} }
} }

View File

@ -18,12 +18,12 @@ package sbserver
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox" sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox"
"github.com/hashicorp/go-multierror"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1" runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
) )
@ -34,7 +34,7 @@ func (c *criService) ListPodSandboxStats(
) (*runtime.ListPodSandboxStatsResponse, error) { ) (*runtime.ListPodSandboxStatsResponse, error) {
sandboxes := c.sandboxesForListPodSandboxStatsRequest(r) sandboxes := c.sandboxesForListPodSandboxStatsRequest(r)
var errs *multierror.Error var errs []error
podSandboxStats := new(runtime.ListPodSandboxStatsResponse) podSandboxStats := new(runtime.ListPodSandboxStatsResponse)
for _, sandbox := range sandboxes { for _, sandbox := range sandboxes {
sandboxStats, err := c.podSandboxStats(ctx, sandbox) sandboxStats, err := c.podSandboxStats(ctx, sandbox)
@ -42,13 +42,13 @@ func (c *criService) ListPodSandboxStats(
case errdefs.IsUnavailable(err): 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) 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: 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: default:
podSandboxStats.Stats = append(podSandboxStats.Stats, sandboxStats) podSandboxStats.Stats = append(podSandboxStats.Stats, sandboxStats)
} }
} }
return podSandboxStats, errs.ErrorOrNil() return podSandboxStats, errors.Join(errs...)
} }
func (c *criService) sandboxesForListPodSandboxStatsRequest(r *runtime.ListPodSandboxStatsRequest) []sandboxstore.Sandbox { func (c *criService) sandboxesForListPodSandboxStatsRequest(r *runtime.ListPodSandboxStatsRequest) []sandboxstore.Sandbox {

View File

@ -18,14 +18,13 @@ package server
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox" sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1" runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
"github.com/hashicorp/go-multierror"
) )
// ListPodSandboxStats returns stats of all ready sandboxes. // ListPodSandboxStats returns stats of all ready sandboxes.
@ -35,7 +34,7 @@ func (c *criService) ListPodSandboxStats(
) (*runtime.ListPodSandboxStatsResponse, error) { ) (*runtime.ListPodSandboxStatsResponse, error) {
sandboxes := c.sandboxesForListPodSandboxStatsRequest(r) sandboxes := c.sandboxesForListPodSandboxStatsRequest(r)
var errs *multierror.Error var errs []error
podSandboxStats := new(runtime.ListPodSandboxStatsResponse) podSandboxStats := new(runtime.ListPodSandboxStatsResponse)
for _, sandbox := range sandboxes { for _, sandbox := range sandboxes {
sandboxStats, err := c.podSandboxStats(ctx, sandbox) sandboxStats, err := c.podSandboxStats(ctx, sandbox)
@ -43,13 +42,13 @@ func (c *criService) ListPodSandboxStats(
case errdefs.IsUnavailable(err): 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) 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: 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: default:
podSandboxStats.Stats = append(podSandboxStats.Stats, sandboxStats) podSandboxStats.Stats = append(podSandboxStats.Stats, sandboxStats)
} }
} }
return podSandboxStats, errs.ErrorOrNil() return podSandboxStats, errors.Join(errs...)
} }
func (c *criService) sandboxesForListPodSandboxStatsRequest(r *runtime.ListPodSandboxStatsRequest) []sandboxstore.Sandbox { func (c *criService) sandboxesForListPodSandboxStatsRequest(r *runtime.ListPodSandboxStatsRequest) []sandboxstore.Sandbox {

View File

@ -20,6 +20,7 @@ package process
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"net/url" "net/url"
@ -35,7 +36,6 @@ import (
"github.com/containerd/containerd/pkg/stdio" "github.com/containerd/containerd/pkg/stdio"
"github.com/containerd/fifo" "github.com/containerd/fifo"
runc "github.com/containerd/go-runc" runc "github.com/containerd/go-runc"
"github.com/hashicorp/go-multierror"
exec "golang.org/x/sys/execabs" 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 { if err == nil {
return return
} }
result := multierror.Append(err) result := []error{err}
for _, fn := range closers { for _, fn := range closers {
result = multierror.Append(result, fn()) result = append(result, fn())
} }
err = multierror.Flatten(result) err = errors.Join(result...)
}() }()
out, err := newPipe() out, err := newPipe()
@ -313,39 +313,35 @@ type binaryIO struct {
} }
func (b *binaryIO) CloseAfterStart() error { func (b *binaryIO) CloseAfterStart() error {
var ( var result []error
result *multierror.Error
)
for _, v := range []*pipe{b.out, b.err} { for _, v := range []*pipe{b.out, b.err} {
if v != nil { if v != nil {
if err := v.r.Close(); err != 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 { func (b *binaryIO) Close() error {
var ( var result []error
result *multierror.Error
)
for _, v := range []*pipe{b.out, b.err} { for _, v := range []*pipe{b.out, b.err} {
if v != nil { if v != nil {
if err := v.Close(); err != nil { if err := v.Close(); err != nil {
result = multierror.Append(result, err) result = append(result, err)
} }
} }
} }
if err := b.cancel(); err != nil { 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 { 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 // Send SIGTERM first, so logger process has a chance to flush and exit properly
if err := b.cmd.Process.Signal(syscall.SIGTERM); err != nil { 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") log.L.WithError(err).Warn("failed to send SIGTERM signal, killing logging shim")
if err := b.cmd.Process.Kill(); err != nil { 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) done := make(chan error, 1)
@ -424,15 +420,15 @@ type pipe struct {
} }
func (p *pipe) Close() error { func (p *pipe) Close() error {
var result *multierror.Error var result []error
if err := p.w.Close(); err != nil { 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 { 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...)
} }

View File

@ -18,6 +18,7 @@ package streaming
import ( import (
"context" "context"
"errors"
"sync" "sync"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
@ -27,8 +28,6 @@ import (
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/streaming" "github.com/containerd/containerd/pkg/streaming"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/hashicorp/go-multierror"
) )
func init() { func init() {
@ -256,12 +255,12 @@ func (cc *collectionContext) Finish() error {
} }
cc.manager.rwlock.Unlock() cc.manager.rwlock.Unlock()
var errs *multierror.Error var errs []error
for _, s := range closeStreams { for _, s := range closeStreams {
if err := s.Close(); err != nil { if err := s.Close(); err != nil {
errs = multierror.Append(errs, err) errs = append(errs, err)
} }
} }
return errs.ErrorOrNil() return errors.Join(errs...)
} }

View File

@ -28,7 +28,6 @@ import (
"time" "time"
"github.com/containerd/ttrpc" "github.com/containerd/ttrpc"
"github.com/hashicorp/go-multierror"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
@ -354,36 +353,34 @@ func (s *shim) Close() error {
} }
func (s *shim) Delete(ctx context.Context) error { func (s *shim) Delete(ctx context.Context) error {
var ( var result []error
result *multierror.Error
)
if ttrpcClient, ok := s.client.(*ttrpc.Client); ok { if ttrpcClient, ok := s.client.(*ttrpc.Client); ok {
if err := ttrpcClient.Close(); err != nil { 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 { 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 grpcClient, ok := s.client.(*grpcConn); ok {
if err := grpcClient.Close(); err != nil { 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 { 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 { if err := s.bundle.Delete(); err != nil {
log.G(ctx).WithField("id", s.ID()).WithError(err).Error("failed to delete bundle") 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{} var _ runtime.Task = &shimTask{}

View File

@ -19,11 +19,11 @@
package devmapper package devmapper
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/hashicorp/go-multierror"
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml"
) )
@ -100,29 +100,29 @@ func (c *Config) parse() error {
// Validate makes sure configuration fields are valid // Validate makes sure configuration fields are valid
func (c *Config) Validate() error { func (c *Config) Validate() error {
var result *multierror.Error var result []error
if c.PoolName == "" { 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 == "" { 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 == "" { 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 != "" { if c.FileSystemType != "" {
switch c.FileSystemType { switch c.FileSystemType {
case fsTypeExt4, fsTypeXFS, fsTypeExt2: case fsTypeExt4, fsTypeXFS, fsTypeExt2:
default: 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 { } 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...)
} }

View File

@ -22,7 +22,6 @@ import (
"os" "os"
"testing" "testing"
"github.com/hashicorp/go-multierror"
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -80,13 +79,13 @@ func TestFieldValidation(t *testing.T) {
err := config.Validate() err := config.Validate()
assert.NotNil(t, err) assert.NotNil(t, err)
multErr := (err).(*multierror.Error) multErr := err.(interface{ Unwrap() []error }).Unwrap()
assert.Len(t, multErr.Errors, 4) assert.Len(t, multErr, 4)
assert.NotNil(t, multErr.Errors[0], "pool_name is empty") assert.NotNil(t, multErr[0], "pool_name is empty")
assert.NotNil(t, multErr.Errors[1], "root_path is empty") assert.NotNil(t, multErr[1], "root_path is empty")
assert.NotNil(t, multErr.Errors[2], "base_image_size is empty") assert.NotNil(t, multErr[2], "base_image_size is empty")
assert.NotNil(t, multErr.Errors[3], "filesystem type cannot be empty") assert.NotNil(t, multErr[3], "filesystem type cannot be empty")
} }
func TestExistingPoolFieldValidation(t *testing.T) { func TestExistingPoolFieldValidation(t *testing.T) {

View File

@ -26,7 +26,6 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/hashicorp/go-multierror"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"github.com/containerd/containerd/log" "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) return fmt.Errorf("failed to query devices from metastore: %w", err)
} }
var result *multierror.Error var result []error
for _, dev := range activatedDevices { for _, dev := range activatedDevices {
if p.IsActivated(dev.Name) { if p.IsActivated(dev.Name) {
continue 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) 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 { 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) 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 { 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. // 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) 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 // Invoke devmapper operation
err := updateStateFn() err := updateStateFn()
if err != nil { if err != nil {
result = multierror.Append(result, err) result = append(result, err)
} }
// If operation succeeded transition to success state, otherwise save error details // 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 { 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. func unwrapError(e error) error {
// multierror 1.1.0 has the similar function named Unwrap, but it requires Go 1.14.
func unwrapError(e *multierror.Error) error {
if e == nil { if e == nil {
return nil return nil
} }
// If the error can be expressed without multierror, return the original error. if joinErr, ok := e.(interface{ Unwrap() []error }); ok {
if len(e.Errors) == 1 { if errs := joinErr.Unwrap(); len(errs) == 1 {
return e.Errors[0] return errs[0]
}
} }
return e
return e.ErrorOrNil()
} }
// CreateThinDevice creates new devmapper thin-device with given name and size. // 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 // We're unable to create the devmapper device, most likely something wrong with the deviceID
if devErr != nil { if devErr != nil {
retErr = multierror.Append(retErr, p.metadata.MarkFaulty(ctx, info.Name)) retErr = errors.Join(retErr, p.metadata.MarkFaulty(ctx, info.Name))
return return
} }
}() }()
@ -285,12 +282,12 @@ func (p *PoolDevice) rollbackActivate(ctx context.Context, info *DeviceInfo, act
if delErr != nil { if delErr != nil {
// Failed to rollback, mark the device as faulty and keep metadata in order to // Failed to rollback, mark the device as faulty and keep metadata in order to
// preserve the faulty device ID // 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 // The devmapper device has been successfully deleted, deallocate device ID
if err := p.RemoveDevice(ctx, info.Name); err != nil { if err := p.RemoveDevice(ctx, info.Name); err != nil {
return multierror.Append(activateErr, err) return errors.Join(activateErr, err)
} }
return activateErr 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 // We're unable to create the devmapper device, most likely something wrong with the deviceID
if devErr != nil { if devErr != nil {
retErr = multierror.Append(retErr, p.metadata.MarkFaulty(ctx, snapInfo.Name)) retErr = errors.Join(retErr, p.metadata.MarkFaulty(ctx, snapInfo.Name))
return return
} }
}() }()
@ -561,20 +558,20 @@ func (p *PoolDevice) RemovePool(ctx context.Context) error {
return fmt.Errorf("can't query device names: %w", err) return fmt.Errorf("can't query device names: %w", err)
} }
var result *multierror.Error var result []error
// Deactivate devices if any // Deactivate devices if any
for _, name := range deviceNames { for _, name := range deviceNames {
if err := p.DeactivateDevice(ctx, name, true, true); err != nil { 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 { 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 // MarkDeviceState changes the device's state in metastore

View File

@ -33,7 +33,6 @@ import (
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
"github.com/containerd/containerd/snapshots/devmapper/dmsetup" "github.com/containerd/containerd/snapshots/devmapper/dmsetup"
"github.com/containerd/containerd/snapshots/storage" "github.com/containerd/containerd/snapshots/storage"
"github.com/hashicorp/go-multierror"
exec "golang.org/x/sys/execabs" exec "golang.org/x/sys/execabs"
) )
@ -344,14 +343,14 @@ func (s *Snapshotter) ResetPool(ctx context.Context) error {
return err return err
} }
var result *multierror.Error var result []error
for _, name := range names { for _, name := range names {
if err := s.pool.RemoveDevice(ctx, name); err != nil { 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. // Close releases devmapper snapshotter resources.
@ -359,16 +358,16 @@ func (s *Snapshotter) ResetPool(ctx context.Context) error {
func (s *Snapshotter) Close() error { func (s *Snapshotter) Close() error {
log.L.Debug("close") log.L.Debug("close")
var result *multierror.Error var result []error
s.closeOnce.Do(func() { s.closeOnce.Do(func() {
for _, fn := range s.cleanupFn { for _, fn := range s.cleanupFn {
if err := fn(); err != nil { 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) { 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) 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 { if err := mkfs(ctx, s.config.FileSystemType, fsOptions, dmsetup.GetFullDevicePath(deviceName)); err != nil {
errs := []error{err}
status, sErr := dmsetup.Status(s.pool.poolName) status, sErr := dmsetup.Status(s.pool.poolName)
if sErr != nil { if sErr != nil {
multierror.Append(err, sErr) errs = append(errs, sErr)
} }
// Rollback thin device creation if mkfs failed // 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) 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, multierror.Append(err, return nil, errors.Join(append(errs, s.pool.RemoveDevice(ctx, deviceName))...)
s.pool.RemoveDevice(ctx, deviceName))
} }
} else { } else {
parentDeviceName := s.getDeviceName(snap.ParentIDs[0]) parentDeviceName := s.getDeviceName(snap.ParentIDs[0])
@ -551,16 +550,16 @@ func (s *Snapshotter) Cleanup(ctx context.Context) error {
return err return err
} }
var result *multierror.Error var result []error
for _, dev := range removedDevices { for _, dev := range removedDevices {
log.G(ctx).WithField("device", dev.Name).Debug("cleanup device") log.G(ctx).WithField("device", dev.Name).Debug("cleanup device")
if err := s.pool.RemoveDevice(ctx, dev.Name); err != nil { if err := s.pool.RemoveDevice(ctx, dev.Name); err != nil {
log.G(ctx).WithField("device", dev.Name).Error("failed to cleanup device") log.G(ctx).WithField("device", dev.Name).Error("failed to cleanup device")
result = multierror.Append(result, err) result = append(result, err)
} else { } else {
log.G(ctx).WithField("device", dev.Name).Debug("cleanuped device") log.G(ctx).WithField("device", dev.Name).Debug("cleanuped device")
} }
} }
return result.ErrorOrNil() return errors.Join(result...)
} }

View File

@ -21,12 +21,12 @@ package devmapper
import ( import (
"context" "context"
_ "crypto/sha256" _ "crypto/sha256"
"errors"
"fmt" "fmt"
"testing" "testing"
"time" "time"
"github.com/containerd/continuity/fs/fstest" "github.com/containerd/continuity/fs/fstest"
"github.com/hashicorp/go-multierror"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "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 // Remove device mapper pool and detach loop devices after test completes
removePool := func() error { removePool := func() error {
result := multierror.Append( result := errors.Join(
snap.pool.RemovePool(ctx), snap.pool.RemovePool(ctx),
mount.DetachLoopDevice(loopDataDevice, loopMetaDevice)) 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) // Pool cleanup should be called before closing metadata store (as we need to retrieve device names)

View File

@ -23,12 +23,12 @@ package storage
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"sync" "sync"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
"github.com/hashicorp/go-multierror"
bolt "go.etcd.io/bbolt" bolt "go.etcd.io/bbolt"
) )
@ -117,10 +117,10 @@ func (ms *MetaStore) WithTransaction(ctx context.Context, writable bool, fn Tran
return err return err
} }
var result *multierror.Error var result []error
err = fn(ctx) err = fn(ctx)
if err != nil { if err != nil {
result = multierror.Append(result, err) result = append(result, err)
} }
// Always rollback if transaction is not writable // 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 { if terr := trans.Rollback(); terr != nil {
log.G(ctx).WithError(terr).Error("failed to rollback transaction") 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 { } else {
if terr := trans.Commit(); terr != nil { if terr := trans.Commit(); terr != nil {
log.G(ctx).WithError(terr).Error("failed to commit transaction") 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") log.G(ctx).WithError(err).Debug("snapshotter error")
// Unwrap if just one error // Unwrap if just one error
if len(result.Errors) == 1 { if errs, ok := err.(interface{ Unwrap() []error }); ok && len(errs.Unwrap()) == 1 {
return result.Errors[0] return errs.Unwrap()[0]
} }
return err return err
} }