diff --git a/api/v3rpc/rpctypes/error_test.go b/api/v3rpc/rpctypes/error_test.go index bf3e0c68027..73a83b2329a 100644 --- a/api/v3rpc/rpctypes/error_test.go +++ b/api/v3rpc/rpctypes/error_test.go @@ -15,6 +15,7 @@ package rpctypes import ( + "errors" "testing" "google.golang.org/grpc/codes" @@ -24,19 +25,20 @@ import ( func TestConvert(t *testing.T) { e1 := status.Error(codes.InvalidArgument, "etcdserver: key is not provided") e2 := ErrGRPCEmptyKey - e3 := ErrEmptyKey + var e3 EtcdError + errors.As(ErrEmptyKey, &e3) if e1.Error() != e2.Error() { t.Fatalf("expected %q == %q", e1.Error(), e2.Error()) } - if ev1, ok := status.FromError(e1); ok && ev1.Code() != e3.(EtcdError).Code() { - t.Fatalf("expected them to be equal, got %v / %v", ev1.Code(), e3.(EtcdError).Code()) + if ev1, ok := status.FromError(e1); ok && ev1.Code() != e3.Code() { + t.Fatalf("expected them to be equal, got %v / %v", ev1.Code(), e3.Code()) } if e1.Error() == e3.Error() { t.Fatalf("expected %q != %q", e1.Error(), e3.Error()) } - if ev2, ok := status.FromError(e2); ok && ev2.Code() != e3.(EtcdError).Code() { - t.Fatalf("expected them to be equal, got %v / %v", ev2.Code(), e3.(EtcdError).Code()) + if ev2, ok := status.FromError(e2); ok && ev2.Code() != e3.Code() { + t.Fatalf("expected them to be equal, got %v / %v", ev2.Code(), e3.Code()) } } diff --git a/client/pkg/fileutil/lock_linux.go b/client/pkg/fileutil/lock_linux.go index c33a2f4afc7..609ac397849 100644 --- a/client/pkg/fileutil/lock_linux.go +++ b/client/pkg/fileutil/lock_linux.go @@ -17,6 +17,7 @@ package fileutil import ( + "errors" "fmt" "io" "os" @@ -58,13 +59,13 @@ func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) { func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) { f, err := os.OpenFile(path, flag, perm) if err != nil { - return nil, fmt.Errorf("ofdTryLockFile failed to open %q (%v)", path, err) + return nil, fmt.Errorf("ofdTryLockFile failed to open %q (%w)", path, err) } flock := wrlck if err = syscall.FcntlFlock(f.Fd(), unix.F_OFD_SETLK, &flock); err != nil { f.Close() - if err == syscall.EWOULDBLOCK { + if errors.Is(err, syscall.EWOULDBLOCK) { err = ErrLocked } return nil, err @@ -79,7 +80,7 @@ func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) { func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) { f, err := os.OpenFile(path, flag, perm) if err != nil { - return nil, fmt.Errorf("ofdLockFile failed to open %q (%v)", path, err) + return nil, fmt.Errorf("ofdLockFile failed to open %q (%w)", path, err) } flock := wrlck diff --git a/client/pkg/fileutil/preallocate_unix.go b/client/pkg/fileutil/preallocate_unix.go index b02070b30b3..b0a8166ae14 100644 --- a/client/pkg/fileutil/preallocate_unix.go +++ b/client/pkg/fileutil/preallocate_unix.go @@ -17,6 +17,7 @@ package fileutil import ( + "errors" "os" "syscall" ) @@ -25,10 +26,10 @@ func preallocExtend(f *os.File, sizeInBytes int64) error { // use mode = 0 to change size err := syscall.Fallocate(int(f.Fd()), 0, 0, sizeInBytes) if err != nil { - errno, ok := err.(syscall.Errno) + var errno syscall.Errno // not supported; fallback // fallocate EINTRs frequently in some environments; fallback - if ok && (errno == syscall.ENOTSUP || errno == syscall.EINTR) { + if errors.As(err, &errno) && (errno == syscall.ENOTSUP || errno == syscall.EINTR) { return preallocExtendTrunc(f, sizeInBytes) } } @@ -39,9 +40,9 @@ func preallocFixed(f *os.File, sizeInBytes int64) error { // use mode = 1 to keep size; see FALLOC_FL_KEEP_SIZE err := syscall.Fallocate(int(f.Fd()), 1, 0, sizeInBytes) if err != nil { - errno, ok := err.(syscall.Errno) + var errno syscall.Errno // treat not supported as nil error - if ok && errno == syscall.ENOTSUP { + if errors.As(err, &errno) && errno == syscall.ENOTSUP { return nil } } diff --git a/client/pkg/srv/srv.go b/client/pkg/srv/srv.go index 15fda134d6a..e0a1ccef35c 100644 --- a/client/pkg/srv/srv.go +++ b/client/pkg/srv/srv.go @@ -87,7 +87,7 @@ func GetCluster(serviceScheme, service, name, dns string, apurls types.URLs) ([] err := updateNodeMap(service, serviceScheme) if err != nil { - return nil, fmt.Errorf("error querying DNS SRV records for _%s %s", service, err) + return nil, fmt.Errorf("error querying DNS SRV records for _%s %w", service, err) } return stringParts, nil } @@ -123,7 +123,7 @@ func GetClient(service, domain string, serviceName string) (*SRVClients, error) errHTTP := updateURLs(GetSRVService(service, serviceName, "http"), "http") if errHTTPS != nil && errHTTP != nil { - return nil, fmt.Errorf("dns lookup errors: %s and %s", errHTTPS, errHTTP) + return nil, fmt.Errorf("dns lookup errors: %w and %w", errHTTPS, errHTTP) } endpoints := make([]string, len(urls)) diff --git a/client/pkg/transport/timeout_dialer_test.go b/client/pkg/transport/timeout_dialer_test.go index fe3993593b1..c0676f17852 100644 --- a/client/pkg/transport/timeout_dialer_test.go +++ b/client/pkg/transport/timeout_dialer_test.go @@ -15,6 +15,7 @@ package transport import ( + "errors" "net" "testing" "time" @@ -57,7 +58,8 @@ func TestReadWriteTimeoutDialer(t *testing.T) { t.Fatal("wait timeout") } - if operr, ok := err.(*net.OpError); !ok || operr.Op != "write" || !operr.Timeout() { + var operr *net.OpError + if !errors.As(err, &operr) || operr.Op != "write" || !operr.Timeout() { t.Errorf("err = %v, want write i/o timeout error", err) } @@ -77,7 +79,7 @@ func TestReadWriteTimeoutDialer(t *testing.T) { t.Fatal("wait timeout") } - if operr, ok := err.(*net.OpError); !ok || operr.Op != "read" || !operr.Timeout() { + if !errors.As(err, &operr) || operr.Op != "read" || !operr.Timeout() { t.Errorf("err = %v, want read i/o timeout error", err) } } diff --git a/client/pkg/transport/timeout_listener_test.go b/client/pkg/transport/timeout_listener_test.go index 036cfd71c44..387a4610e7e 100644 --- a/client/pkg/transport/timeout_listener_test.go +++ b/client/pkg/transport/timeout_listener_test.go @@ -15,6 +15,7 @@ package transport import ( + "errors" "net" "testing" "time" @@ -82,7 +83,8 @@ func TestWriteReadTimeoutListener(t *testing.T) { t.Fatal("wait timeout") } - if operr, ok := err.(*net.OpError); !ok || operr.Op != "write" || !operr.Timeout() { + var operr *net.OpError + if !errors.As(err, &operr) || operr.Op != "write" || !operr.Timeout() { t.Errorf("err = %v, want write i/o timeout error", err) } writerStopCh <- struct{}{} @@ -109,7 +111,7 @@ func TestWriteReadTimeoutListener(t *testing.T) { t.Fatal("wait timeout") } - if operr, ok := err.(*net.OpError); !ok || operr.Op != "read" || !operr.Timeout() { + if !errors.As(err, &operr) || operr.Op != "read" || !operr.Timeout() { t.Errorf("err = %v, want read i/o timeout error", err) } readerStopCh <- struct{}{} diff --git a/client/v3/client.go b/client/v3/client.go index 17c746366fc..1620e65806f 100644 --- a/client/v3/client.go +++ b/client/v3/client.go @@ -294,7 +294,7 @@ func (c *Client) getToken(ctx context.Context) error { resp, err := c.Auth.Authenticate(ctx, c.Username, c.Password) if err != nil { - if err == rpctypes.ErrAuthNotEnabled { + if errors.Is(err, rpctypes.ErrAuthNotEnabled) { c.authTokenBundle.UpdateAuthToken("") return nil } @@ -605,7 +605,8 @@ func ContextError(ctx context.Context, err error) error { return nil } err = rpctypes.Error(err) - if _, ok := err.(rpctypes.EtcdError); ok { + var serverErr rpctypes.EtcdError + if errors.As(err, &serverErr) { return err } if ev, ok := status.FromError(err); ok { @@ -627,7 +628,7 @@ func canceledByCaller(stopCtx context.Context, err error) bool { return false } - return err == context.Canceled || err == context.DeadlineExceeded + return errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) } // IsConnCanceled returns true, if error is from a closed gRPC connection. @@ -645,7 +646,7 @@ func IsConnCanceled(err error) bool { } // >= gRPC v1.10.x - if err == context.Canceled { + if errors.Is(err, context.Canceled) { return true } diff --git a/client/v3/client_test.go b/client/v3/client_test.go index dd651e85611..360b903157d 100644 --- a/client/v3/client_test.go +++ b/client/v3/client_test.go @@ -430,8 +430,8 @@ func TestClientRejectOldCluster(t *testing.T) { }, } - if err := c.checkVersion(); err != tt.expectedError { - t.Errorf("heckVersion err:%v", err) + if err := c.checkVersion(); !errors.Is(err, tt.expectedError) { + t.Errorf("checkVersion err:%v", err) } }) } diff --git a/client/v3/experimental/recipes/key.go b/client/v3/experimental/recipes/key.go index 10362c18fbe..ce90010538b 100644 --- a/client/v3/experimental/recipes/key.go +++ b/client/v3/experimental/recipes/key.go @@ -16,6 +16,7 @@ package recipe import ( "context" + "errors" "fmt" "strings" "time" @@ -51,7 +52,7 @@ func newUniqueKV(kv v3.KV, prefix string, val string) (*RemoteKV, error) { if err == nil { return &RemoteKV{kv, newKey, rev, val}, nil } - if err != ErrKeyExists { + if !errors.Is(err, ErrKeyExists) { return nil, err } } @@ -155,7 +156,7 @@ func newUniqueEphemeralKV(s *concurrency.Session, prefix, val string) (ek *Ephem for { newKey := fmt.Sprintf("%s/%v", prefix, time.Now().UnixNano()) ek, err = newEphemeralKV(s, newKey, val) - if err == nil || err != ErrKeyExists { + if err == nil || !errors.Is(err, ErrKeyExists) { break } } diff --git a/client/v3/lease.go b/client/v3/lease.go index 0f0c57a9572..bed24bc0dce 100644 --- a/client/v3/lease.go +++ b/client/v3/lease.go @@ -16,6 +16,7 @@ package clientv3 import ( "context" + "errors" "sync" "time" @@ -464,7 +465,7 @@ func (l *lessor) recvKeepAliveLoop() (gerr error) { return err } - if ContextError(l.stopCtx, err) == rpctypes.ErrNoLeader { + if errors.Is(ContextError(l.stopCtx, err), rpctypes.ErrNoLeader) { l.closeRequireLeader() } break diff --git a/client/v3/leasing/kv.go b/client/v3/leasing/kv.go index 35792f42086..c14af78d629 100644 --- a/client/v3/leasing/kv.go +++ b/client/v3/leasing/kv.go @@ -16,6 +16,7 @@ package leasing import ( "context" + "errors" "strings" "sync" "time" @@ -282,7 +283,8 @@ func (lkv *leasingKV) acquire(ctx context.Context, key string, op v3.Op) (*v3.Tx return resp, nil } // retry if transient error - if _, ok := err.(rpctypes.EtcdError); ok { + var serverErr rpctypes.EtcdError + if errors.As(err, &serverErr) { return nil, err } if ev, ok := status.FromError(err); ok && ev.Code() != codes.Unavailable { diff --git a/client/v3/maintenance.go b/client/v3/maintenance.go index 3244820dee6..00aaacd15fd 100644 --- a/client/v3/maintenance.go +++ b/client/v3/maintenance.go @@ -117,7 +117,7 @@ func NewMaintenance(c *Client) Maintenance { dial: func(endpoint string) (pb.MaintenanceClient, func(), error) { conn, err := c.Dial(endpoint) if err != nil { - return nil, nil, fmt.Errorf("failed to dial endpoint %s with maintenance client: %v", endpoint, err) + return nil, nil, fmt.Errorf("failed to dial endpoint %s with maintenance client: %w", endpoint, err) } cancel := func() { conn.Close() } @@ -297,8 +297,8 @@ func (m *maintenance) Snapshot(ctx context.Context) (io.ReadCloser, error) { } func (m *maintenance) logAndCloseWithError(err error, pw *io.PipeWriter) { - switch err { - case io.EOF: + switch { + case errors.Is(err, io.EOF): m.lg.Info("completed snapshot read; closing") default: m.lg.Warn("failed to receive from snapshot stream; closing", zap.Error(err)) diff --git a/client/v3/mock/mockserver/mockserver.go b/client/v3/mock/mockserver/mockserver.go index 7d14e027810..0467cfd5df5 100644 --- a/client/v3/mock/mockserver/mockserver.go +++ b/client/v3/mock/mockserver/mockserver.go @@ -85,12 +85,12 @@ func startMockServersUnix(count int) (ms *MockServers, err error) { for i := 0; i < count; i++ { f, err := os.CreateTemp(dir, "etcd-unix-so-") if err != nil { - return nil, fmt.Errorf("failed to allocate temp file for unix socket: %v", err) + return nil, fmt.Errorf("failed to allocate temp file for unix socket: %w", err) } fn := f.Name() err = os.Remove(fn) if err != nil { - return nil, fmt.Errorf("failed to remove temp file before creating unix socket: %v", err) + return nil, fmt.Errorf("failed to remove temp file before creating unix socket: %w", err) } addrs = append(addrs, fn) } @@ -110,7 +110,7 @@ func startMockServers(network string, addrs []string) (ms *MockServers, err erro for idx, addr := range addrs { ln, err := net.Listen(network, addr) if err != nil { - return nil, fmt.Errorf("failed to listen %v", err) + return nil, fmt.Errorf("failed to listen %w", err) } ms.Servers[idx] = &MockServer{ln: ln, Network: network, Address: ln.Addr().String()} ms.StartAt(idx) @@ -126,7 +126,7 @@ func (ms *MockServers) StartAt(idx int) (err error) { if ms.Servers[idx].ln == nil { ms.Servers[idx].ln, err = net.Listen(ms.Servers[idx].Network, ms.Servers[idx].Address) if err != nil { - return fmt.Errorf("failed to listen %v", err) + return fmt.Errorf("failed to listen %w", err) } } diff --git a/client/v3/retry.go b/client/v3/retry.go index 2c80d3a74ac..f04a8298217 100644 --- a/client/v3/retry.go +++ b/client/v3/retry.go @@ -16,6 +16,7 @@ package clientv3 import ( "context" + "errors" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -52,7 +53,8 @@ func (rp retryPolicy) String() string { // handle itself even with retries. func isSafeRetryImmutableRPC(err error) bool { eErr := rpctypes.Error(err) - if serverErr, ok := eErr.(rpctypes.EtcdError); ok && serverErr.Code() != codes.Unavailable { + var serverErr rpctypes.EtcdError + if errors.As(eErr, &serverErr) && serverErr.Code() != codes.Unavailable { // interrupted by non-transient server-side or gRPC-side error // client cannot handle itself (e.g. rpctypes.ErrCompacted) return false diff --git a/client/v3/retry_interceptor.go b/client/v3/retry_interceptor.go index b354abb104c..f1949cebefb 100644 --- a/client/v3/retry_interceptor.go +++ b/client/v3/retry_interceptor.go @@ -349,10 +349,10 @@ func isContextError(err error) bool { } func contextErrToGRPCErr(err error) error { - switch err { - case context.DeadlineExceeded: + switch { + case errors.Is(err, context.DeadlineExceeded): return status.Errorf(codes.DeadlineExceeded, err.Error()) - case context.Canceled: + case errors.Is(err, context.Canceled): return status.Errorf(codes.Canceled, err.Error()) default: return status.Errorf(codes.Unknown, err.Error()) diff --git a/client/v3/snapshot/v3_snapshot.go b/client/v3/snapshot/v3_snapshot.go index b2db5f0c137..324e497cfd0 100644 --- a/client/v3/snapshot/v3_snapshot.go +++ b/client/v3/snapshot/v3_snapshot.go @@ -62,7 +62,7 @@ func SaveWithVersion(ctx context.Context, lg *zap.Logger, cfg clientv3.Config, d var f *os.File f, err = os.OpenFile(partpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fileutil.PrivateFileMode) if err != nil { - return "", fmt.Errorf("could not open %s (%v)", partpath, err) + return "", fmt.Errorf("could not open %s (%w)", partpath, err) } lg.Info("created temporary db file", zap.String("path", partpath)) @@ -95,7 +95,7 @@ func SaveWithVersion(ctx context.Context, lg *zap.Logger, cfg clientv3.Config, d ) if err = os.Rename(partpath, dbPath); err != nil { - return resp.Version, fmt.Errorf("could not rename %s to %s (%v)", partpath, dbPath, err) + return resp.Version, fmt.Errorf("could not rename %s to %s (%w)", partpath, dbPath, err) } lg.Info("saved", zap.String("path", dbPath)) return resp.Version, nil diff --git a/client/v3/watch.go b/client/v3/watch.go index 215d7ba0405..857a2917aeb 100644 --- a/client/v3/watch.go +++ b/client/v3/watch.go @@ -395,7 +395,7 @@ func (w *watcher) Close() (err error) { } } // Consider context.Canceled as a successful close - if err == context.Canceled { + if errors.Is(err, context.Canceled) { err = nil } return err @@ -653,7 +653,7 @@ func (w *watchGRPCStream) run() { // watch client failed on Recv; spawn another if possible case err := <-w.errc: - if isHaltErr(w.ctx, err) || ContextError(w.ctx, err) == v3rpc.ErrNoLeader { + if isHaltErr(w.ctx, err) || errors.Is(ContextError(w.ctx, err), v3rpc.ErrNoLeader) { closeErr = err return } diff --git a/pkg/featuregate/feature_gate.go b/pkg/featuregate/feature_gate.go index cdc4ca72756..b9e0175bef2 100644 --- a/pkg/featuregate/feature_gate.go +++ b/pkg/featuregate/feature_gate.go @@ -201,7 +201,7 @@ func (f *featureGate) Set(value string) error { v := strings.TrimSpace(arr[1]) boolValue, err := strconv.ParseBool(v) if err != nil { - return fmt.Errorf("invalid value of %s=%s, err: %v", k, v, err) + return fmt.Errorf("invalid value of %s=%s, err: %w", k, v, err) } m[k] = boolValue } diff --git a/pkg/flags/flag.go b/pkg/flags/flag.go index b96436c6b35..b48921c6dcd 100644 --- a/pkg/flags/flag.go +++ b/pkg/flags/flag.go @@ -108,7 +108,7 @@ func setFlagFromEnv(lg *zap.Logger, fs flagSetter, prefix, fname string, usedEnv if val != "" { usedEnvKey[key] = true if serr := fs.Set(fname, val); serr != nil { - return fmt.Errorf("invalid value %q for %s: %v", val, key, serr) + return fmt.Errorf("invalid value %q for %s: %w", val, key, serr) } if log && lg != nil { lg.Info( diff --git a/pkg/grpctesting/stub_server.go b/pkg/grpctesting/stub_server.go index ba000b2a5bf..bdc991cbed2 100644 --- a/pkg/grpctesting/stub_server.go +++ b/pkg/grpctesting/stub_server.go @@ -63,7 +63,7 @@ func (ss *StubServer) Start(sopts []grpc.ServerOption, dopts ...grpc.DialOption) lis, err := net.Listen(ss.Network, ss.Address) if err != nil { - return fmt.Errorf("net.Listen(%q, %q) = %v", ss.Network, ss.Address, err) + return fmt.Errorf("net.Listen(%q, %q) = %w", ss.Network, ss.Address, err) } ss.Address = lis.Addr().String() ss.cleanups = append(ss.cleanups, func() { lis.Close() }) diff --git a/pkg/ioutil/readcloser_test.go b/pkg/ioutil/readcloser_test.go index 6d13bdcec02..381a797f7af 100644 --- a/pkg/ioutil/readcloser_test.go +++ b/pkg/ioutil/readcloser_test.go @@ -16,6 +16,7 @@ package ioutil import ( "bytes" + "errors" "io" "testing" ) @@ -28,7 +29,7 @@ func (rc *readerNilCloser) Close() error { return nil } func TestExactReadCloserExpectEOF(t *testing.T) { buf := bytes.NewBuffer(make([]byte, 10)) rc := NewExactReadCloser(&readerNilCloser{buf}, 1) - if _, err := rc.Read(make([]byte, 10)); err != ErrExpectEOF { + if _, err := rc.Read(make([]byte, 10)); !errors.Is(err, ErrExpectEOF) { t.Fatalf("expected %v, got %v", ErrExpectEOF, err) } } @@ -40,7 +41,7 @@ func TestExactReadCloserShort(t *testing.T) { if _, err := rc.Read(make([]byte, 10)); err != nil { t.Fatalf("Read expected nil err, got %v", err) } - if err := rc.Close(); err != ErrShortRead { + if err := rc.Close(); !errors.Is(err, ErrShortRead) { t.Fatalf("Close expected %v, got %v", ErrShortRead, err) } } diff --git a/pkg/netutil/netutil.go b/pkg/netutil/netutil.go index d4e2515d8bc..0f1a685855c 100644 --- a/pkg/netutil/netutil.go +++ b/pkg/netutil/netutil.go @@ -16,6 +16,7 @@ package netutil import ( "context" + "errors" "fmt" "net" "net/url" @@ -70,14 +71,14 @@ func resolveTCPAddrs(ctx context.Context, lg *zap.Logger, urls [][]url.URL) ([][ for i, u := range us { nu, err := url.Parse(u.String()) if err != nil { - return nil, fmt.Errorf("failed to parse %q (%v)", u.String(), err) + return nil, fmt.Errorf("failed to parse %q (%w)", u.String(), err) } nus[i] = *nu } for i, u := range nus { h, err := resolveURL(ctx, lg, u) if err != nil { - return nil, fmt.Errorf("failed to resolve %q (%v)", u.String(), err) + return nil, fmt.Errorf("failed to resolve %q (%w)", u.String(), err) } if h != "" { nus[i].Host = h @@ -217,6 +218,6 @@ func stringsToURLs(us []string) ([]url.URL, error) { } func IsNetworkTimeoutError(err error) bool { - nerr, ok := err.(net.Error) - return ok && nerr.Timeout() + var nerr net.Error + return errors.As(err, &nerr) && nerr.Timeout() }