diff --git a/.golangci.yml b/.golangci.yml index a35ab7c..d960d11 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,6 +9,7 @@ linters: - copyloopvar - dupl - errcheck + - errorlint - funlen - gocritic - govet @@ -25,6 +26,10 @@ linters: settings: dupl: threshold: 400 + errorlint: + errorf: true + asserts: true + comparison: true funlen: lines: 400 statements: 200 diff --git a/internal/app/active_nodes.go b/internal/app/active_nodes.go index 2d52d2d..d70b57c 100644 --- a/internal/app/active_nodes.go +++ b/internal/app/active_nodes.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "slices" "sort" @@ -15,7 +16,7 @@ func (app *App) GetActiveNodes() ([]string, error) { var activeNodes []string err := app.dcs.Get(pathActiveNodes, &activeNodes) if err != nil { - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { return nil, nil } return nil, fmt.Errorf("get active nodes from dcs: %s", err.Error()) diff --git a/internal/app/candidate.go b/internal/app/candidate.go index ebd65da..7b0cfd9 100644 --- a/internal/app/candidate.go +++ b/internal/app/candidate.go @@ -1,6 +1,8 @@ package app import ( + "errors" + "github.com/yandex/rdsync/internal/dcs" ) @@ -20,7 +22,7 @@ func (app *App) stateCandidate() appState { app.logger.Info().Msgf("Shard state: %v", shardState) } maintenance, err := app.GetMaintenance() - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msg("Candidate: failed to get maintenance from DCS") return stateCandidate } @@ -29,7 +31,7 @@ func (app *App) stateCandidate() appState { } poisonPill, err := app.getPoisonPill() - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msg("Candidate: failed to get poison pill from DCS") return stateCandidate } @@ -46,7 +48,7 @@ func (app *App) stateCandidate() appState { var master string err = app.dcs.Get(pathMasterNode, &master) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msg("Candidate: failed to get current master from DCS") return stateCandidate } diff --git a/internal/app/cli.go b/internal/app/cli.go index 7ed1d3b..b6525e2 100644 --- a/internal/app/cli.go +++ b/internal/app/cli.go @@ -3,6 +3,7 @@ package app import ( "bufio" "context" + "errors" "fmt" "os" @@ -69,7 +70,7 @@ func (app *App) CliInfo(verbose bool) int { err = app.dcs.Get(path, &switchover) if err == nil { data[path] = switchover.String() - } else if err != dcs.ErrNotFound { + } else if !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msgf("Failed to get %s", path) return 1 } @@ -79,7 +80,7 @@ func (app *App) CliInfo(verbose bool) int { err = app.dcs.Get(pathMaintenance, &maintenance) if err == nil { data[pathMaintenance] = maintenance.String() - } else if err != dcs.ErrNotFound { + } else if !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msgf("Failed to get %s", pathMaintenance) return 1 } @@ -88,14 +89,14 @@ func (app *App) CliInfo(verbose bool) int { err = app.dcs.Get(pathPoisonPill, &poisonPill) if err == nil { data[pathPoisonPill] = poisonPill.String() - } else if err != dcs.ErrNotFound { + } else if !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msgf("Failed to get %s", pathPoisonPill) return 1 } var manager dcs.LockOwner err = app.dcs.Get(pathManagerLock, &manager) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msgf("Failed to get %s", pathManagerLock) return 1 } @@ -103,7 +104,7 @@ func (app *App) CliInfo(verbose bool) int { var master string err = app.dcs.Get(pathMasterNode, &master) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msgf("Failed to get %s", pathMasterNode) return 1 } @@ -287,7 +288,7 @@ func (app *App) CliSwitch(switchFrom, switchTo string, waitTimeout time.Duration app.logger.Error().Msgf("Another switchover in progress %v", switchover) return 2 } - if err != dcs.ErrNotFound { + if !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msg("Unable to get current switchover status") return 2 } @@ -307,7 +308,7 @@ func (app *App) CliSwitch(switchFrom, switchTo string, waitTimeout time.Duration } err = app.dcs.Create(pathCurrentSwitch, switchover) - if err == dcs.ErrExists { + if errors.Is(err, dcs.ErrExists) { app.logger.Error().Msg("Another switchover in progress") return 2 } @@ -364,7 +365,7 @@ func (app *App) CliEnableMaintenance(waitTimeout time.Duration) int { InitiatedAt: time.Now(), } err = app.dcs.Create(pathMaintenance, maintenance) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { app.logger.Error().Err(err).Msg("Unable to create maintenance path in dcs") return 1 } @@ -410,7 +411,7 @@ func (app *App) CliDisableMaintenance(waitTimeout time.Duration) int { maintenance := &Maintenance{} err = app.dcs.Get(pathMaintenance, maintenance) - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { fmt.Println("maintenance disabled") return 0 } else if err != nil { @@ -432,7 +433,7 @@ func (app *App) CliDisableMaintenance(waitTimeout time.Duration) int { select { case <-ticker.C: err = app.dcs.Get(pathMaintenance, maintenance) - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { maintenance = nil break Out } @@ -466,15 +467,15 @@ func (app *App) CliGetMaintenance() int { var maintenance Maintenance err = app.dcs.Get(pathMaintenance, &maintenance) - switch err { - case nil: + switch { + case err == nil: if maintenance.RdSyncPaused { fmt.Println("on") } else { fmt.Println("scheduled") } return 0 - case dcs.ErrNotFound: + case errors.Is(err, dcs.ErrNotFound): fmt.Println("off") return 0 default: @@ -494,7 +495,7 @@ func (app *App) CliAbort() int { app.dcs.Initialize() err = app.dcs.Get(pathCurrentSwitch, new(Switchover)) - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { fmt.Println("no active switchover") return 0 } @@ -578,7 +579,7 @@ func (app *App) CliHostAdd(host string, priority *int, dryRun bool, skipValkeyCh // root path probably does not exist err = app.dcs.Create(dcs.JoinPath(pathHANodes), nil) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return 1 } @@ -598,7 +599,7 @@ func (app *App) CliHostAdd(host string, priority *int, dryRun bool, skipValkeyCh if !dryRun && priority == nil { err = app.dcs.Set(dcs.JoinPath(pathHANodes, host), *valkey.DefaultNodeConfiguration()) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { app.logger.Error().Err(err).Msgf("Unable to create dcs path for %s", host) return 1 } @@ -632,7 +633,7 @@ func (app *App) CliHostRemove(host string) int { app.dcs.Initialize() err = app.dcs.Delete(dcs.JoinPath(pathHANodes, host)) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msgf("Unable to delete dcs path for %s", host) return 1 } @@ -668,7 +669,7 @@ func (app *App) processPriority(priority *int, dryRun bool, host string) (change } err = app.dcs.Set(dcs.JoinPath(pathHANodes, host), targetConf) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return false, err } diff --git a/internal/app/failover.go b/internal/app/failover.go index 0c28e4d..2afdea7 100644 --- a/internal/app/failover.go +++ b/internal/app/failover.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "time" @@ -56,7 +57,7 @@ func (app *App) approveFailover(shardState map[string]*HostState, activeNodes [] var lastSwitchover Switchover err := app.dcs.Get(pathLastSwitch, &lastSwitchover) - if err != dcs.ErrNotFound { + if !errors.Is(err, dcs.ErrNotFound) { if err != nil { return err } diff --git a/internal/app/maintenance.go b/internal/app/maintenance.go index 63d4609..6254acb 100644 --- a/internal/app/maintenance.go +++ b/internal/app/maintenance.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "os" @@ -94,10 +95,10 @@ func (app *App) stateMaintenance() appState { app.createMaintenanceFile() } maintenance, err := app.GetMaintenance() - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return stateMaintenance } - if err == dcs.ErrNotFound || maintenance.ShouldLeave { + if errors.Is(err, dcs.ErrNotFound) || maintenance.ShouldLeave { if app.dcs.AcquireLock(pathManagerLock) { app.logger.Info().Msg("Leaving maintenance") err := app.leaveMaintenance() diff --git a/internal/app/manager.go b/internal/app/manager.go index e948f4e..f52b7c5 100644 --- a/internal/app/manager.go +++ b/internal/app/manager.go @@ -2,6 +2,7 @@ package app import ( "context" + "errors" "fmt" "time" @@ -50,7 +51,7 @@ func (app *App) stateManager() appState { app.logger.Info().Msgf("DCS shard state: %v", shardStateDcs) maintenance, err := app.GetMaintenance() - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msg("Failed to get maintenance from dcs") return stateManager } @@ -94,7 +95,7 @@ func (app *App) stateManager() appState { return stateManager } err = app.performSwitchover(shardState, activeNodes, &switchover, master) - if app.dcs.Get(pathCurrentSwitch, new(Switchover)) == dcs.ErrNotFound { + if errors.Is(app.dcs.Get(pathCurrentSwitch, new(Switchover)), dcs.ErrNotFound) { app.logger.Error().Msg("Switchover was aborted") } else { if err != nil { @@ -110,12 +111,12 @@ func (app *App) stateManager() appState { } } return stateManager - } else if err != dcs.ErrNotFound { + } else if !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msg("Getting current switchover failed") return stateManager } poisonPill, err := app.getPoisonPill() - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msg("Manager: failed to get poison pill from DCS") return stateManager } diff --git a/internal/app/master.go b/internal/app/master.go index dd1afe6..655433e 100644 --- a/internal/app/master.go +++ b/internal/app/master.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "time" @@ -14,8 +15,8 @@ func (app *App) getNumReplicasToWrite(activeNodes []string) int { func (app *App) getCurrentMaster(shardState map[string]*HostState) (string, error) { var master string err := app.dcs.Get(pathMasterNode, &master) - if err != nil && err != dcs.ErrNotFound { - return "", fmt.Errorf("failed to get current master from dcs: %s", err) + if err != nil && !errors.Is(err, dcs.ErrNotFound) { + return "", fmt.Errorf("failed to get current master from dcs: %w", err) } if master != "" { stateMaster, err := app.getMasterHost(shardState) @@ -84,7 +85,7 @@ func (app *App) ensureCurrentMaster(shardState map[string]*HostState) (string, e } err = app.dcs.Set(pathMasterNode, master) if err != nil { - return "", fmt.Errorf("failed to set current master in dcs: %s", err) + return "", fmt.Errorf("failed to set current master in dcs: %w", err) } return master, nil } diff --git a/internal/app/replication.go b/internal/app/replication.go index f578c51..ffc8a56 100644 --- a/internal/app/replication.go +++ b/internal/app/replication.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "slices" "time" @@ -80,7 +81,7 @@ func (app *App) closeStaleReplica(master string) error { app.logger.Debug().Msgf("Skipping staleness close due to switchover in progress: %v.", switchover) return nil } - if err != dcs.ErrNotFound { + if !errors.Is(err, dcs.ErrNotFound) { return err } shardState, err := app.getShardStateFromDcs() diff --git a/internal/app/state.go b/internal/app/state.go index ac0d076..74ab8e8 100644 --- a/internal/app/state.go +++ b/internal/app/state.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "strconv" "strings" @@ -201,7 +202,7 @@ func (app *App) getShardStateFromDcs() (map[string]*HostState, error) { getter := func(host string) (*HostState, error) { var state HostState err := app.dcs.Get(dcs.JoinPath(pathHealthPrefix, host), &state) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return nil, err } return &state, nil diff --git a/internal/app/switchover.go b/internal/app/switchover.go index 62ea6c6..6740644 100644 --- a/internal/app/switchover.go +++ b/internal/app/switchover.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "slices" "time" @@ -27,11 +28,11 @@ func countAliveHAReplicasWithinNodes(nodes []string, shardState map[string]*Host func (app *App) getLastSwitchover() Switchover { var lastSwitch, lastRejectedSwitch Switchover err := app.dcs.Get(pathLastSwitch, &lastSwitch) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { app.logger.Error().Err(err).Msg(pathLastSwitch) } errRejected := app.dcs.Get(pathLastRejectedSwitch, &lastRejectedSwitch) - if errRejected != nil && errRejected != dcs.ErrNotFound { + if errRejected != nil && !errors.Is(errRejected, dcs.ErrNotFound) { app.logger.Error().Err(errRejected).Msg(pathLastRejectedSwitch) } @@ -187,7 +188,7 @@ func (app *App) performSwitchover(shardState map[string]*HostState, activeNodes } poisonPill, err := app.getPoisonPill() - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return fmt.Errorf("unable to get poison pill: %s", err.Error()) } if !shardState[oldMaster].PingOk { @@ -342,11 +343,11 @@ func (app *App) performSwitchover(shardState map[string]*HostState, activeNodes err = app.dcs.Set(pathMasterNode, newMaster) if err != nil { - return fmt.Errorf("failed to set new master in dcs: %s", err) + return fmt.Errorf("failed to set new master in dcs: %w", err) } poisonPill, err = app.getPoisonPill() - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return fmt.Errorf("unable to get poison pill: %s", err.Error()) } needIssue := true diff --git a/internal/dcs/zk.go b/internal/dcs/zk.go index b478577..00b1023 100644 --- a/internal/dcs/zk.go +++ b/internal/dcs/zk.go @@ -178,7 +178,7 @@ func (z *zkDCS) makePath(path string) error { if err == nil { break } - if err == zk.ErrNoNode { + if errors.Is(err, zk.ErrNoNode) { createPaths = append(createPaths, path) } else { return err @@ -187,7 +187,7 @@ func (z *zkDCS) makePath(path string) error { slices.Reverse(createPaths) for _, path := range createPaths { _, err := z.retryCreate(path, []byte{}, 0, z.acl) - if err != nil && err != zk.ErrNodeExists { + if err != nil && !errors.Is(err, zk.ErrNodeExists) { return err } } @@ -278,7 +278,7 @@ func (z *zkDCS) Initialize() { func (z *zkDCS) retryRequestInternal(code func() error) error { err := code() - if err != zk.ErrConnectionClosed { + if !errors.Is(err, zk.ErrConnectionClosed) { return backoff.Permanent(err) } if !z.IsConnected() { @@ -353,18 +353,18 @@ func (z *zkDCS) AcquireLock(path string) bool { } self := z.getSelfLockOwner() data, _, err := z.retryGet(fullPath) - if err != nil && err != zk.ErrNoNode { + if err != nil && !errors.Is(err, zk.ErrNoNode) { z.logger.Error().Err(err).Msgf("Failed to get lock info %s", fullPath) return false } - if err == zk.ErrNoNode { + if errors.Is(err, zk.ErrNoNode) { data, err = json.Marshal(&self) if err != nil { panic(fmt.Sprintf("failed to serialize to JSON %#v", self)) } _, err = z.retryCreate(fullPath, data, zk.FlagEphemeral, z.acl) if err != nil { - if err != zk.ErrNodeExists { + if !errors.Is(err, zk.ErrNodeExists) { z.logger.Error().Err(err).Msgf("Failed to acquire lock %s", fullPath) } return false @@ -395,19 +395,19 @@ func (z *zkDCS) ReleaseLockOrError(path string) error { fullPath := z.buildFullPath(path) z.lockHeld.Delete(fullPath) data, stat, err := z.retryGet(fullPath) - if err != nil && err != zk.ErrNoNode { - return fmt.Errorf("failed to get lock info %s: %v", fullPath, err) + if err != nil && !errors.Is(err, zk.ErrNoNode) { + return fmt.Errorf("failed to get lock info %s: %w", fullPath, err) } owner := LockOwner{} if err = json.Unmarshal(data, &owner); err != nil { - return fmt.Errorf("unexpected lock data %s (%s): %v", fullPath, data, err) + return fmt.Errorf("unexpected lock data %s (%s): %w", fullPath, data, err) } if owner != z.getSelfLockOwner() { return fmt.Errorf("failed to release lock %s: process is not an owner", fullPath) } err = z.retryDelete(fullPath, stat.Version) if err != nil { - return fmt.Errorf("failed to delete lock node %s: %v", fullPath, err) + return fmt.Errorf("failed to delete lock node %s: %w", fullPath, err) } return nil } @@ -420,7 +420,7 @@ func (z *zkDCS) create(path string, val any, flags int32) error { } _, err = z.retryCreate(fullPath, data, flags, z.acl) if err != nil { - if err == zk.ErrNodeExists { + if errors.Is(err, zk.ErrNodeExists) { return ErrExists } z.logger.Error().Err(err).Msgf("Failed to create node %s with %+v", fullPath, val) @@ -443,11 +443,11 @@ func (z *zkDCS) set(path string, val any, flags int32) error { return fmt.Errorf("failed to serialize to JSON %#v", val) } _, stat, err := z.retryGet(fullPath) - if err != nil && err != zk.ErrNoNode { + if err != nil && !errors.Is(err, zk.ErrNoNode) { z.logger.Error().Err(err).Msgf("Failed to get node %s", fullPath) return err } - if err == zk.ErrNoNode { + if errors.Is(err, zk.ErrNoNode) { parts := strings.Split(fullPath, sep) err = z.makePath(strings.Join(parts[:len(parts)-1], sep)) if err != nil { @@ -480,7 +480,7 @@ func (z *zkDCS) SetEphemeral(path string, val any) error { func (z *zkDCS) Delete(path string) error { fullPath := z.buildFullPath(path) _, stat, err := z.retryGet(fullPath) - if err == zk.ErrNoNode { + if errors.Is(err, zk.ErrNoNode) { return nil } if err != nil { @@ -497,7 +497,7 @@ func (z *zkDCS) Delete(path string) error { func (z *zkDCS) Get(path string, dest any) error { fullPath := z.buildFullPath(path) data, _, err := z.retryGet(fullPath) - if err == zk.ErrNoNode { + if errors.Is(err, zk.ErrNoNode) { return ErrNotFound } if err != nil { @@ -549,7 +549,7 @@ func (z *zkDCS) GetTree(path string) (any, error) { func (z *zkDCS) GetChildren(path string) ([]string, error) { fullPath := z.buildFullPath(path) children, err := z.retryChildren(fullPath) - if err == zk.ErrNoNode { + if errors.Is(err, zk.ErrNoNode) { return nil, ErrNotFound } if err != nil { diff --git a/internal/valkey/shard.go b/internal/valkey/shard.go index c7c77d2..50d1a74 100644 --- a/internal/valkey/shard.go +++ b/internal/valkey/shard.go @@ -1,6 +1,7 @@ package valkey import ( + "errors" "fmt" "sort" "sync" @@ -50,7 +51,7 @@ func (s *Shard) SetDCS(newDCS dcs.DCS) { // GetShardHostsFromDcs returns current shard hosts from dcs state func (s *Shard) GetShardHostsFromDcs() ([]string, error) { fqdns, err := s.dcs.GetChildren(dcs.PathHANodesPrefix) - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { return make([]string, 0), nil } if err != nil { @@ -142,8 +143,8 @@ func (s *Shard) GetNodeConfiguration(host string) (*NodeConfiguration, error) { var nc NodeConfiguration err := s.dcs.Get(dcs.JoinPath(dcs.PathHANodesPrefix, host), &nc) if err != nil { - if err != dcs.ErrNotFound && err != dcs.ErrMalformed { - return nil, fmt.Errorf("failed to get Priority for host %s: %s", host, err) + if !errors.Is(err, dcs.ErrNotFound) && !errors.Is(err, dcs.ErrMalformed) { + return nil, fmt.Errorf("failed to get Priority for host %s: %w", host, err) } return DefaultNodeConfiguration(), nil } diff --git a/tests/rdsync_test.go b/tests/rdsync_test.go index 8e6c9fa..60e6f63 100644 --- a/tests/rdsync_test.go +++ b/tests/rdsync_test.go @@ -3,6 +3,7 @@ package tests import ( "context" json "encoding/json/v2" + "errors" "fmt" "html/template" "io" @@ -208,7 +209,7 @@ func (tctx *testContext) connectZookeeper(addrs []string, timeout time.Duration) return err == nil }, timeout, time.Second) if err != nil { - return nil, fmt.Errorf("failed to ping zookeeper within %s: %s", timeout, err) + return nil, fmt.Errorf("failed to ping zookeeper within %s: %w", timeout, err) } return conn, nil } @@ -291,11 +292,11 @@ func (tctx *testContext) getValkeyConnection(host string) (client.Client, error) } addr, err := tctx.composer.GetAddr(host, valkeyPort) if err != nil { - return nil, fmt.Errorf("failed to get valkey addr %s: %s", host, err) + return nil, fmt.Errorf("failed to get valkey addr %s: %w", host, err) } conn, err = tctx.connectValkey(addr, valkeyConnectTimeout) if err != nil { - return nil, fmt.Errorf("failed to connect to valkey %s: %s", host, err) + return nil, fmt.Errorf("failed to connect to valkey %s: %w", host, err) } tctx.conns[host] = conn return conn, nil @@ -312,11 +313,11 @@ func (tctx *testContext) getSenticacheConnection(host string) (client.Client, er } addr, err := tctx.composer.GetAddr(host, senticachePort) if err != nil { - return nil, fmt.Errorf("failed to get senticache addr %s: %s", host, err) + return nil, fmt.Errorf("failed to get senticache addr %s: %w", host, err) } conn, err = tctx.connectSenticache(addr, valkeyConnectTimeout) if err != nil { - return nil, fmt.Errorf("failed to connect to senticache %s: %s", host, err) + return nil, fmt.Errorf("failed to connect to senticache %s: %w", host, err) } tctx.senticaches[host] = conn return conn, nil @@ -392,7 +393,7 @@ func (tctx *testContext) runSenticacheCmd(host string, cmd []string) (string, er func (tctx *testContext) baseShardIsUpAndRunning() error { err := tctx.composer.Up(tctx.composerEnv) if err != nil { - return fmt.Errorf("failed to setup compose cluster: %s", err) + return fmt.Errorf("failed to setup compose cluster: %w", err) } // check zookeepers @@ -403,7 +404,7 @@ func (tctx *testContext) baseShardIsUpAndRunning() error { if strings.HasPrefix(service, zkName) { addr, err2 := tctx.composer.GetAddr(service, zkPort) if err2 != nil { - err = fmt.Errorf("failed to get zookeeper addr %s: %s", service, err2) + err = fmt.Errorf("failed to get zookeeper addr %s: %w", service, err2) return false } zkAddrs = append(zkAddrs, addr) @@ -415,28 +416,28 @@ func (tctx *testContext) baseShardIsUpAndRunning() error { }, time.Minute*5, time.Second) if err != nil { - return fmt.Errorf("failed to connect to zookeeper %s: %s", zkAddrs, err) + return fmt.Errorf("failed to connect to zookeeper %s: %w", zkAddrs, err) } err = tctx.composer.RunCommandAtHosts("/var/lib/dist/base/generate_certs.sh && supervisorctl restart rdsync", "valkey", time.Minute) if err != nil { - return fmt.Errorf("failed to generate certs in valkey hosts: %s", err) + return fmt.Errorf("failed to generate certs in valkey hosts: %w", err) } if err = tctx.createZookeeperNode("/test"); err != nil { - return fmt.Errorf("failed to create namespace zk node due %s", err) + return fmt.Errorf("failed to create namespace zk node due %w", err) } if err = tctx.createZookeeperNode(dcs.JoinPath("/test", dcs.PathHANodesPrefix)); err != nil { - return fmt.Errorf("failed to create path prefix zk node due %s", err) + return fmt.Errorf("failed to create path prefix zk node due %w", err) } // prepare valkey nodes for _, service := range tctx.composer.Services() { if strings.HasPrefix(service, valkeyName) { if err = tctx.createZookeeperNode(dcs.JoinPath("/test", dcs.PathHANodesPrefix, service)); err != nil { - return fmt.Errorf("failed to create %s zk node due %s", service, err) + return fmt.Errorf("failed to create %s zk node due %w", service, err) } } } @@ -466,11 +467,11 @@ func (tctx *testContext) stepClusteredShardIsUpAndRunning() error { if strings.HasPrefix(service, valkeyName) { addr, err := tctx.composer.GetAddr(service, valkeyPort) if err != nil { - return fmt.Errorf("failed to get valkey addr %s: %s", service, err) + return fmt.Errorf("failed to get valkey addr %s: %w", service, err) } conn, err := tctx.connectValkey(addr, valkeyInitialConnectTimeout) if err != nil { - return fmt.Errorf("failed to connect to valkey %s: %s", service, err) + return fmt.Errorf("failed to connect to valkey %s: %w", service, err) } tctx.conns[service] = conn } @@ -500,20 +501,20 @@ func (tctx *testContext) stepSentinelShardIsUpAndRunning() error { if strings.HasPrefix(service, valkeyName) { addr, err := tctx.composer.GetAddr(service, valkeyPort) if err != nil { - return fmt.Errorf("failed to get valkey addr %s: %s", service, err) + return fmt.Errorf("failed to get valkey addr %s: %w", service, err) } conn, err := tctx.connectValkey(addr, valkeyInitialConnectTimeout) if err != nil { - return fmt.Errorf("failed to connect to valkey %s: %s", service, err) + return fmt.Errorf("failed to connect to valkey %s: %w", service, err) } tctx.conns[service] = conn saddr, err2 := tctx.composer.GetAddr(service, senticachePort) if err2 != nil { - return fmt.Errorf("failed to get senticache addr %s: %s", service, err2) + return fmt.Errorf("failed to get senticache addr %s: %w", service, err2) } sconn, err2 := tctx.connectSenticache(saddr, valkeyInitialConnectTimeout) if err2 != nil { - return fmt.Errorf("failed to connect to senticache %s: %s", service, err2) + return fmt.Errorf("failed to connect to senticache %s: %w", service, err2) } tctx.senticaches[service] = sconn } @@ -658,7 +659,7 @@ func (tctx *testContext) stepPathExists(path, host string) error { cmd := fmt.Sprintf("stat %s", path) retCode, _, err := tctx.composer.RunCommand(host, cmd, 10*time.Second) if err != nil { - return fmt.Errorf("failed to check path %s on %s: %s", path, host, err) + return fmt.Errorf("failed to check path %s on %s: %w", path, host, err) } if retCode != 0 { return fmt.Errorf("expected %s to exist on %s but it's not", path, host) @@ -670,7 +671,7 @@ func (tctx *testContext) stepPathDoesNotExist(path, host string) error { cmd := fmt.Sprintf("stat %s", path) retCode, _, err := tctx.composer.RunCommand(host, cmd, 10*time.Second) if err != nil { - return fmt.Errorf("failed to check path %s on %s: %s", path, host, err) + return fmt.Errorf("failed to check path %s on %s: %w", path, host, err) } if retCode == 0 { return fmt.Errorf("expected %s to be absent on %s but it exists", path, host) @@ -807,10 +808,10 @@ func (tctx *testContext) createZookeeperNode(node string) error { func (tctx *testContext) stepISetZookeeperNode(node string, body *godog.DocString) error { data := []byte(strings.TrimSpace(body.Content)) _, stat, err := tctx.zk.Get(node) - if err != nil && err != zk.ErrNoNode { + if err != nil && !errors.Is(err, zk.ErrNoNode) { return err } - if err == zk.ErrNoNode { + if errors.Is(err, zk.ErrNoNode) { _, err = tctx.zk.Create(node, data, 0, tctx.acl) } else { _, err = tctx.zk.Set(node, data, stat.Version) @@ -866,7 +867,7 @@ func (tctx *testContext) stepZookeeperNodeShouldExistWithin(node string, timeout func (tctx *testContext) stepZookeeperNodeShouldNotExist(node string) error { err := tctx.stepIGetZookeeperNode(node) - if err == zk.ErrNoNode { + if errors.Is(err, zk.ErrNoNode) { return nil } if err != nil { @@ -936,7 +937,7 @@ func (tctx *testContext) stepReplicationOnValkeyHostShouldRunFineWithin(host str func (tctx *testContext) stepValkeyHostShouldBecomeUnavailableWithin(host string, timeout int) error { addr, err := tctx.composer.GetAddr(host, valkeyPort) if err != nil { - return fmt.Errorf("failed to get valkey addr %s: %s", host, err) + return fmt.Errorf("failed to get valkey addr %s: %w", host, err) } testutil.Retry(func() bool { var conn client.Client @@ -956,7 +957,7 @@ func (tctx *testContext) stepValkeyHostShouldBecomeUnavailableWithin(host string func (tctx *testContext) stepValkeyHostShouldBecomeAvailableWithin(host string, timeout int) error { addr, err := tctx.composer.GetAddr(host, valkeyPort) if err != nil { - return fmt.Errorf("failed to get valkey addr %s: %s", host, err) + return fmt.Errorf("failed to get valkey addr %s: %w", host, err) } testutil.Retry(func() bool { var conn client.Client diff --git a/tests/testutil/docker_composer.go b/tests/testutil/docker_composer.go index aa46986..8734c5c 100644 --- a/tests/testutil/docker_composer.go +++ b/tests/testutil/docker_composer.go @@ -82,7 +82,7 @@ func NewDockerComposer(project, config string) (*DockerComposer, error) { } config, err := filepath.Abs(config) if err != nil { - return nil, fmt.Errorf("failed to build abs path to compose file: %s", err) + return nil, fmt.Errorf("failed to build abs path to compose file: %w", err) } if project == "" { project = filepath.Base(filepath.Dir(config)) @@ -90,7 +90,7 @@ func NewDockerComposer(project, config string) (*DockerComposer, error) { dc := new(DockerComposer) api, err := client.New(client.FromEnv) if err != nil { - return nil, fmt.Errorf("failed to connect to docker: %s", err) + return nil, fmt.Errorf("failed to connect to docker: %w", err) } dc.api = api dc.config = config @@ -108,7 +108,7 @@ func (dc *DockerComposer) runCompose(args []string, env []string) error { cmd.Env = append(os.Environ(), env...) out, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("failed to run 'docker compose %s': %s\n%s", strings.Join(args2, " "), err, out) + return fmt.Errorf("failed to run 'docker compose %s': %w\n%s", strings.Join(args2, " "), err, out) } return nil } diff --git a/tests/testutil/matchers/matchers.go b/tests/testutil/matchers/matchers.go index c19405c..4c5e081 100644 --- a/tests/testutil/matchers/matchers.go +++ b/tests/testutil/matchers/matchers.go @@ -126,10 +126,10 @@ func jsonContains(a, e any, path []string) []string { func JSONMatcher(actual string, expected string) error { var a, e any if err := json.Unmarshal([]byte(actual), &a); err != nil { - return fmt.Errorf("actual value is not valid json: %s", err) + return fmt.Errorf("actual value is not valid json: %w", err) } if err := json.Unmarshal([]byte(expected), &e); err != nil { - panic(fmt.Errorf("expected value is not valid json: %s", err)) + panic(fmt.Errorf("expected value is not valid json: %w", err)) } res := jsonContains(a, e, []string{""}) if len(res) > 0 { @@ -143,10 +143,10 @@ func JSONMatcher(actual string, expected string) error { func JSONExactlyMatcher(actual string, expected string) error { var a, e any if err := json.Unmarshal([]byte(actual), &a); err != nil { - return fmt.Errorf("actual value is not valid json: %s", err) + return fmt.Errorf("actual value is not valid json: %w", err) } if err := json.Unmarshal([]byte(expected), &e); err != nil { - panic(fmt.Errorf("expected value is not valid json: %s", err)) + panic(fmt.Errorf("expected value is not valid json: %w", err)) } if !reflect.DeepEqual(a, e) { return &MatcherError{actual, expected}