diff --git a/.golangci.yml b/.golangci.yml index 004fa164..8a0afc00 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,6 +9,7 @@ linters: - copyloopvar - dupl - errcheck + - errorlint - funlen - gocritic - gocyclo @@ -25,6 +26,10 @@ linters: settings: dupl: threshold: 400 + errorlint: + errorf: true + asserts: true + comparison: true funlen: lines: 150 statements: 100 diff --git a/internal/app/app.go b/internal/app/app.go index 486a737f..8296dccc 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -146,7 +146,7 @@ func (app *App) connectDCS() error { // TODO: support other DCS systems app.dcs, err = dcs.NewZookeeper(app.baseContext(), &app.config.Zookeeper, app.logger) if err != nil { - return fmt.Errorf("failed to connect to zkDCS: %s", err.Error()) + return fmt.Errorf("failed to connect to zkDCS: %w", err) } return nil } @@ -155,7 +155,7 @@ func (app *App) newDBCluster() error { var err error app.cluster, err = mysql.NewCluster(app.config, app.logger, app.dcs) if err != nil { - return fmt.Errorf("failed to create database cluster %s", err.Error()) + return fmt.Errorf("failed to create database cluster %w", err) } return nil } @@ -348,7 +348,7 @@ func (app *App) checkHAReplicasRunning(local *mysql.Node) (replicasRunning bool, } ssstatus, err := node.SemiSyncStatus() if err != nil { - return fmt.Errorf("%s %v", host, err) + return fmt.Errorf("%s %w", host, err) } if ssstatus.SlaveEnabled == 0 { return fmt.Errorf("replica %s is not semi-sync", host) @@ -459,8 +459,8 @@ func (app *App) stateLost() appState { if localNodeState.IsMaster { err = node.SetReadOnlyWithForce(app.config.ExcludeUsers, true) - merr, ok := err.(*mysql_driver.MySQLError) - if !errors.Is(err, context.DeadlineExceeded) && (!ok || merr.Number != 1205) { // Error 1205: Lock wait timeout exceeded; try restarting transaction + var merr *mysql_driver.MySQLError + if !errors.Is(err, context.DeadlineExceeded) && (!errors.As(err, &merr) || merr.Number != 1205) { // Error 1205: Lock wait timeout exceeded; try restarting transaction app.logger.Error().Err(err).Msgf("failed to set node %s read-only", node.Host()) return stateLost } @@ -519,10 +519,10 @@ func (app *App) stateMaintenance() appState { app.writeMaintenanceFile() } 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 { return app.tryLeaveMaintenance() } @@ -587,7 +587,7 @@ func (app *App) stateManager() appState { // check if we are in maintenance 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 zk") // If maintenance file doesn't exist we were in light maintenance mode @@ -662,7 +662,7 @@ func (app *App) stateManager() appState { return stateManager } err = app.performSwitchover(clusterState, 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().Msgf("switchover was aborted") } else { if err != nil { @@ -682,7 +682,7 @@ 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("") return stateManager } @@ -911,7 +911,7 @@ func (app *App) approveFailover(clusterState, clusterStateDcs map[string]*nodest var lastSwitchover Switchover err = app.dcs.Get(pathLastSwitch, &lastSwitchover) - if err != dcs.ErrNotFound { + if !errors.Is(err, dcs.ErrNotFound) { if err != nil { return err } @@ -936,7 +936,7 @@ func (app *App) stateCandidate() appState { return stateCandidate } 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 zk") return stateCandidate } @@ -1253,7 +1253,7 @@ func (app *App) disableSemiSyncOnSlaves(becomeInactive, becomeDataLag []string) for _, host := range becomeInactive { err := app.disableSemiSyncOnSlave(host, true) if err != nil { - err = fmt.Errorf("[%s]: %s", host, err) + err = fmt.Errorf("[%s]: %w", host, err) nodeErrors = append(nodeErrors, err) } } @@ -1261,7 +1261,7 @@ func (app *App) disableSemiSyncOnSlaves(becomeInactive, becomeDataLag []string) for _, host := range becomeDataLag { err := app.disableSemiSyncOnSlave(host, false) if err != nil { - err = fmt.Errorf("[%s]: %s", host, err) + err = fmt.Errorf("[%s]: %w", host, err) nodeErrors = append(nodeErrors, err) continue } @@ -1378,7 +1378,7 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, if app.config.ForceSwitchover { err := node.SetOfflineForce() if err != nil { - return fmt.Errorf("failed to set node %s force offline: %v", host, err) + return fmt.Errorf("failed to set node %s force offline: %w", host, err) } defer func() { @@ -1393,7 +1393,7 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, if err != nil || app.emulateError("freeze_ro") { app.logger.Info().Msgf("switchover: failed to set node %s read-only, trying kill bad queries: %v", host, err) if err := node.SetReadOnlyWithForce(app.config.ExcludeUsers, true); err != nil { - return fmt.Errorf("failed to set node %s read-only: %v", host, err) + return fmt.Errorf("failed to set node %s read-only: %w", host, err) } } app.logger.Info().Msgf("switchover: host %s set read-only", host) @@ -1403,11 +1403,11 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, // if master was not among activeNodes - there will be no key in errs // MasterTransition may not be set if issued from worker if err, ok := errs[oldMaster]; ok && err != nil && switchover.MasterTransition != FailoverTransition { - err = fmt.Errorf("switchover: failed to set old master %s read-only %s", oldMaster, err) + err = fmt.Errorf("switchover: failed to set old master %s read-only %w", oldMaster, err) app.logger.Info().Msg(err.Error()) switchErr := app.FinishSwitchover(switchover, err) if switchErr != nil { - return fmt.Errorf("switchover: failed to reject switchover %s", switchErr) + return fmt.Errorf("switchover: failed to reject switchover %w", switchErr) } app.logger.Info().Msg("switchover: rejected") return err @@ -1419,7 +1419,7 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, if clusterState[oldMaster].PingOk { err := app.externalReplication.Stop(oldMasterNode) if err != nil { - return fmt.Errorf("got error: %s while stopping external replication on old master: %s", err, oldMaster) + return fmt.Errorf("got error: %w while stopping external replication on old master: %s", err, oldMaster) } } @@ -1522,7 +1522,7 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, } caught, err := app.waitForCatchUp(newMasterNode, mostRecentGtidSet, app.config.SlaveCatchUpTimeout, time.Second) if err != nil || app.emulateError("catchup_master_status") { - return fmt.Errorf("failed to get gtid executed from %s: %s", newMaster, err) + return fmt.Errorf("failed to get gtid executed from %s: %w", newMaster, err) } if !caught || app.emulateError("catchup_failed") { return fmt.Errorf("new master %s failed to catch up %s within %s", @@ -1547,7 +1547,7 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, app.logger.Info().Msg("switchover: phase 5: turn to the new master") err = app.cluster.Get(newMaster).SetOnline() if err != nil { - return fmt.Errorf("got error on setting new master %s online %v", newMaster, err) + return fmt.Errorf("got error on setting new master %s online %w", newMaster, err) } errs = util.RunParallel(func(host string) error { if host == newMaster || !clusterState[host].PingOk { @@ -1571,13 +1571,13 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, if err != nil || oldMasterSlaveStatus == nil || isSlavePermanentlyLost(oldMasterSlaveStatus, mostRecentGtidSet) { err = app.SetRecovery(oldMaster) if err != nil { - return fmt.Errorf("failed to set old master %s to recovery: %v", oldMaster, err) + return fmt.Errorf("failed to set old master %s to recovery: %w", oldMaster, err) } } else { app.logger.Info().Msgf("switchover: old master %s does not need recovery", oldMaster) err = app.externalReplication.Reset(oldMasterNode) if err != nil { - return fmt.Errorf("got error: %s while resetting external replication on old master: %s", err, oldMaster) + return fmt.Errorf("got error: %w while resetting external replication on old master: %s", err, oldMaster) } } @@ -1585,11 +1585,11 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, app.logger.Info().Msg("switchover: phase 6: promote new master") err = newMasterNode.StopSlave() if err != nil || app.emulateError("promote_stop_slave") { - return fmt.Errorf("failed to stop slave on new master %s: %s", newMaster, err) + return fmt.Errorf("failed to stop slave on new master %s: %w", newMaster, err) } err = newMasterNode.ResetSlaveAll() if err != nil || app.emulateError("promote_reset_slave") { - return fmt.Errorf("failed to promote new master %s: %s", newMaster, err) + return fmt.Errorf("failed to promote new master %s: %w", newMaster, err) } app.logger.Info().Msgf("switchover: new master %s promoted", newMaster) @@ -1603,14 +1603,14 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, // set new master writable err = newMasterNode.SetWritable() if err != nil || app.emulateError("promote_set_writable") { - return fmt.Errorf("failed to set new master %s writable: %s", newMaster, err) + return fmt.Errorf("failed to set new master %s writable: %w", newMaster, err) } app.logger.Info().Msgf("switchover: new master %s set writable", newMaster) // reenable events events, err := newMasterNode.ReenableEventsRetry() if err != nil || app.emulateError("promote_reenable_events") { - return fmt.Errorf("failed to reenable slaveside disabled events on %s: %s", newMaster, err) + return fmt.Errorf("failed to reenable slaveside disabled events on %s: %w", newMaster, err) } if len(events) > 0 { app.logger.Info().Msgf("switchover: events reenabled on %s: %v", newMaster, events) @@ -1625,7 +1625,7 @@ func (app *App) performSwitchover(clusterState map[string]*nodestate.NodeState, // set new master in dcs err = app.dcs.Set(pathMasterNode, newMaster) if err != nil || app.emulateError("promote_set_to_dcs") { - return fmt.Errorf("failed to set new master to dcs: %s", err) + return fmt.Errorf("failed to set new master to dcs: %w", err) } return nil @@ -2239,19 +2239,19 @@ func (app *App) performChangeMaster(host, master string) error { node := app.cluster.Get(host) err := node.StopSlave() if err != nil { - return fmt.Errorf("failed to stop slave on host %s: %s", host, err) + return fmt.Errorf("failed to stop slave on host %s: %w", host, err) } app.logger.Info().Msgf("changemaster: host %s replication stopped", host) err = node.ChangeMaster(master) if err != nil { - return fmt.Errorf("failed to change master on host %s: %s", host, err) + return fmt.Errorf("failed to change master on host %s: %w", host, err) } app.logger.Info().Msgf("changemaster: host %s turned to the new master %s", host, master) err = node.StartSlave() if err != nil { - return fmt.Errorf("failed to start slave on host %s: %s", host, err) + return fmt.Errorf("failed to start slave on host %s: %w", host, err) } deadline := time.Now().Add(app.config.WaitReplicationStartTimeout) @@ -2436,7 +2436,7 @@ func (app *App) getClusterStateFromDcs() (map[string]*nodestate.NodeState, error getter := func(host string) (*nodestate.NodeState, error) { nodeState := new(nodestate.NodeState) err := app.dcs.Get(dcs.JoinPath(pathHealthPrefix, host), nodeState) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return nil, err } nodeState.ShowOnlyGTIDDiff = app.config.ShowOnlyGTIDDiff @@ -2457,7 +2457,7 @@ func (app *App) waitForCatchUp(node *mysql.Node, gtidset gtids.GTIDSet, timeout return true, nil } switchover := new(Switchover) - if app.dcs.Get(pathCurrentSwitch, switchover) == dcs.ErrNotFound { + if errors.Is(app.dcs.Get(pathCurrentSwitch, switchover), dcs.ErrNotFound) { return false, nil } if app.CheckAsyncSwitchAllowed(node, switchover) { @@ -2491,12 +2491,12 @@ func (app *App) stopReplicationOnMaster(masterNode *mysql.Node) error { app.logger.Info().Msgf("setting master %s offline", host) err := masterNode.SetOffline() if err != nil { - return fmt.Errorf("cannot set master %s offline: %s", host, err) + return fmt.Errorf("cannot set master %s offline: %w", host, err) } app.logger.Info().Msgf("disable semi-sync on master %s", host) err = masterNode.SemiSyncDisable() if err != nil { - return fmt.Errorf("failed to disable semi-sync on master %s: %v", host, err) + return fmt.Errorf("failed to disable semi-sync on master %s: %w", host, err) } return nil @@ -2530,11 +2530,11 @@ func (app *App) getNodePositions(activeNodes []string) ([]nodePosition, error) { node := app.cluster.Get(host) sstatus, err := node.GetReplicaStatus() if err != nil || app.emulateError("freeze_slave_status") { - return fmt.Errorf("failed to get slave status on host %s: %s", host, err) + return fmt.Errorf("failed to get slave status on host %s: %w", host, err) } lag, err := node.ReplicationLag(sstatus) if err != nil || app.emulateError("freeze_slave_status2") { - return fmt.Errorf("failed to get slave replication lag on host %s: %s", host, err) + return fmt.Errorf("failed to get slave replication lag on host %s: %w", host, err) } if lag == nil { // we can treat unknown lag as infinite for the replica compare purpose @@ -2548,7 +2548,7 @@ func (app *App) getNodePositions(activeNodes []string) ([]nodePosition, error) { app.logger.Info().Msgf("switchover: current master found: %s", host) gtidset, err = node.GTIDExecutedParsed() if err != nil || app.emulateError("freeze_master_status") { - return fmt.Errorf("failed to get master status on host %s: %s", host, err) + return fmt.Errorf("failed to get master status on host %s: %w", host, err) } } else { gtidset = gtids.ParseGtidSet(sstatus.GetExecutedGtidSet()) @@ -2556,7 +2556,7 @@ func (app *App) getNodePositions(activeNodes []string) ([]nodePosition, error) { // slave may have downloaded but not applied transactions err := gtidset.Update(sstatus.GetRetrievedGtidSet()) if err != nil { - return fmt.Errorf("failed to parse RetrievedGtidSet from host %s: %s", host, err) + return fmt.Errorf("failed to parse RetrievedGtidSet from host %s: %w", host, err) } } } @@ -2564,8 +2564,8 @@ func (app *App) getNodePositions(activeNodes []string) ([]nodePosition, error) { var nc mysql.NodeConfiguration err = app.dcs.Get(dcs.JoinPath(pathHANodes, host), &nc) if err != nil { - if err != dcs.ErrNotFound && err != dcs.ErrMalformed { - return fmt.Errorf("failed to get priority for host %s: %s", host, err) + if !errors.Is(err, dcs.ErrNotFound) && !errors.Is(err, dcs.ErrMalformed) { + return fmt.Errorf("failed to get priority for host %s: %w", host, err) } // Default value nc = mysql.NodeConfiguration{Priority: 0} diff --git a/internal/app/app_dcs.go b/internal/app/app_dcs.go index 3c35e07c..639579e1 100644 --- a/internal/app/app_dcs.go +++ b/internal/app/app_dcs.go @@ -15,17 +15,17 @@ func (app *App) GetActiveNodes() ([]string, error) { var activeNodes []string err := app.dcs.Get(pathActiveNodes, &activeNodes) if err != nil { - if err == dcs.ErrNotFound || err == dcs.ErrMalformed { + if errors.Is(err, dcs.ErrNotFound) || errors.Is(err, dcs.ErrMalformed) { return nil, nil } - return nil, fmt.Errorf("failed to get active nodes from zk %v", err) + return nil, fmt.Errorf("failed to get active nodes from zk %w", err) } return activeNodes, nil } func (app *App) GetClusterCascadeFqdnsFromDcs() ([]string, error) { fqdns, err := app.dcs.GetChildren(dcs.PathCascadeNodesPrefix) - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { return make([]string, 0), nil } if err != nil { @@ -46,7 +46,7 @@ func (app *App) GetMaintenance() (*Maintenance, error) { func (app *App) GetHostsOnRecovery() ([]string, error) { hosts, err := app.dcs.GetChildren(pathRecovery) - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { return nil, nil } return hosts, err @@ -69,11 +69,11 @@ func (app *App) SetRecovery(host string) error { return err } err = app.dcs.Create(pathRecovery, nil) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return err } err = app.dcs.Create(dcs.JoinPath(pathRecovery, host), nil) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return err } return nil @@ -88,7 +88,7 @@ func (app *App) IsRecoveryNeeded(host string) bool { func (app *App) setResetupStatus(host string, status bool) error { err := app.dcs.Create(pathResetupStatus, nil) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return err } resetupStatus := &mysql.ResetupStatus{ @@ -177,11 +177,11 @@ func (app *App) StartSwitchover(switchover *Switchover) error { 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) } @@ -205,7 +205,7 @@ func (app *App) IssueFailover(master string) error { func (app *App) SetMasterHost(master string) (string, error) { err := app.dcs.Set(pathMasterNode, master) if err != nil { - return "", fmt.Errorf("failed to set current master to dcs: %s", err) + return "", fmt.Errorf("failed to set current master to dcs: %w", err) } return master, nil } @@ -213,8 +213,8 @@ func (app *App) SetMasterHost(master string) (string, error) { func (app *App) GetMasterHostFromDcs() (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 != "" { return master, nil @@ -224,7 +224,7 @@ func (app *App) GetMasterHostFromDcs() (string, error) { func (app *App) SetReplMonTS(ts string) error { err := app.dcs.Create(pathMasterReplMonTS, ts) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return err } err = app.dcs.Set(pathMasterReplMonTS, ts) diff --git a/internal/app/async.go b/internal/app/async.go index ebf5eb24..90409923 100644 --- a/internal/app/async.go +++ b/internal/app/async.go @@ -33,7 +33,7 @@ func (app *App) updateReplMonTS(master string) error { masterNode := app.cluster.Get(master) ts, err := masterNode.GetReplMonTS(app.config.ReplMonSchemeName, app.config.ReplMonTableName) if err != nil { - return fmt.Errorf("failed to get master repl_mon timestamp: %v", err) + return fmt.Errorf("failed to get master repl_mon timestamp: %w", err) } return app.SetReplMonTS(ts) } diff --git a/internal/app/cli_host.go b/internal/app/cli_host.go index ea928e42..4d0dbd65 100644 --- a/internal/app/cli_host.go +++ b/internal/app/cli_host.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "sort" @@ -79,11 +80,11 @@ func (app *App) CliHostAdd(host string, streamFrom *string, priority *int64, dry // root paths may 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 } err = app.dcs.Create(dcs.JoinPath(pathCascadeNodesPrefix), nil) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return 1 } @@ -116,7 +117,7 @@ func (app *App) CliHostAdd(host string, streamFrom *string, priority *int64, dry // node may not exist, we should add it to HA hosts if !app.cluster.IsHAHost(host) && !app.cluster.IsCascadeHost(host) { err = app.dcs.Set(dcs.JoinPath(pathHANodes, host), mysql.NodeConfiguration{Priority: 0}) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return 1 } } @@ -170,15 +171,15 @@ func (app *App) CliHostRemove(host string) int { return 1 } err = app.dcs.Delete(dcs.JoinPath(pathHANodes, host)) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return 1 } err = app.dcs.Delete(dcs.JoinPath(pathCascadeNodesPrefix, host)) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return 1 } err = app.dcs.Delete(dcs.JoinPath(pathResetupStatus, host)) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return 1 } fmt.Println("host has been removed") @@ -204,11 +205,11 @@ func (app *App) processReplicationSource(streamFrom string, dryRun bool, host st return true, nil } err := app.dcs.Delete(dcs.JoinPath(pathCascadeNodesPrefix, host)) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return false, err } err = app.dcs.Set(dcs.JoinPath(pathHANodes, host), mysql.NodeConfiguration{Priority: 0}) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return false, err } } else { @@ -240,7 +241,7 @@ func (app *App) processReplicationSource(streamFrom string, dryRun bool, host st if dryRun { var cns mysql.CascadeNodeConfiguration err = app.dcs.Get(dcs.JoinPath(pathCascadeNodesPrefix, host), &cns) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return false, err } if cns.StreamFrom == streamFrom { @@ -252,11 +253,11 @@ func (app *App) processReplicationSource(streamFrom string, dryRun bool, host st } err = app.dcs.Delete(dcs.JoinPath(pathHANodes, host)) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return false, err } err = app.dcs.Set(dcs.JoinPath(pathCascadeNodesPrefix, host), mysql.CascadeNodeConfiguration{StreamFrom: streamFrom}) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return false, err } } @@ -269,7 +270,7 @@ func (app *App) processPriority(priority int64, dryRun bool, host string) (chang var nc mysql.NodeConfiguration err = app.dcs.Get(dcs.JoinPath(pathHANodes, host), &nc) if err != nil { - if err != dcs.ErrNotFound { + if !errors.Is(err, dcs.ErrNotFound) { return false, err } fmt.Printf("dry run: node %s is not HA node, priority cannot be set", host) @@ -289,7 +290,7 @@ func (app *App) processPriority(priority int64, dryRun bool, host string) (chang } err = app.dcs.Set(dcs.JoinPath(pathHANodes, host), mysql.NodeConfiguration{Priority: priority}) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return false, err } diff --git a/internal/app/cli_info.go b/internal/app/cli_info.go index e622af8d..9eee9d1a 100644 --- a/internal/app/cli_info.go +++ b/internal/app/cli_info.go @@ -1,6 +1,7 @@ package app import ( + "errors" "fmt" "sort" "strconv" @@ -60,13 +61,13 @@ func (app *App) buildShortInfo(zone string, hostFilter string, parsedLag *lagFil haNodes, err := app.cluster.GetClusterHAHostsFromDcs() if err != nil { - return nil, fmt.Errorf("failed to get ha nodes: %v", err) + return nil, fmt.Errorf("failed to get ha nodes: %w", err) } data[pathHANodes] = filterHostsMap(haNodes, zone, hostFilter, app.config.OfflineModeAZSeparator) cascadeNodes, err := app.cluster.GetClusterCascadeHostsFromDcs() if err != nil { - return nil, fmt.Errorf("failed to get cascade nodes: %v", err) + return nil, fmt.Errorf("failed to get cascade nodes: %w", err) } data[pathCascadeNodesPrefix] = filterHostsMap(cascadeNodes, zone, hostFilter, app.config.OfflineModeAZSeparator) @@ -80,7 +81,7 @@ func (app *App) buildShortInfo(zone string, hostFilter string, parsedLag *lagFil nodesOnRecovery, err := app.GetHostsOnRecovery() if err != nil { - return nil, fmt.Errorf("failed to get nodes on recovery: %v", err) + return nil, fmt.Errorf("failed to get nodes on recovery: %w", err) } if nodesOnRecovery != nil { nodesOnRecovery = filterHostsByFilters(nodesOnRecovery, zone, hostFilter, app.config.OfflineModeAZSeparator) @@ -92,13 +93,13 @@ func (app *App) buildShortInfo(zone string, hostFilter string, parsedLag *lagFil clusterState, err := app.getClusterStateFromDcs() if err != nil { - return nil, fmt.Errorf("failed to get cluster state: %v", err) + return nil, fmt.Errorf("failed to get cluster state: %w", err) } var master string err = app.dcs.Get(pathMasterNode, &master) - if err != nil && err != dcs.ErrNotFound { - return nil, fmt.Errorf("failed to get %s: %v", pathMasterNode, err) + if err != nil && !errors.Is(err, dcs.ErrNotFound) { + return nil, fmt.Errorf("failed to get %s: %w", pathMasterNode, err) } health := make([]string, 0, len(clusterState)) @@ -129,8 +130,8 @@ func (app *App) buildShortInfo(zone string, hostFilter string, parsedLag *lagFil err = app.dcs.Get(path, &switchover) if err == nil { data[path] = switchover.String() - } else if err != dcs.ErrNotFound { - return nil, fmt.Errorf("failed to get %s: %v", path, err) + } else if !errors.Is(err, dcs.ErrNotFound) { + return nil, fmt.Errorf("failed to get %s: %w", path, err) } } @@ -138,14 +139,14 @@ func (app *App) buildShortInfo(zone string, hostFilter string, parsedLag *lagFil err = app.dcs.Get(pathMaintenance, &maintenance) if err == nil { data[pathMaintenance] = maintenance.String() - } else if err != dcs.ErrNotFound { - return nil, fmt.Errorf("failed to get %s: %v", pathMaintenance, err) + } else if !errors.Is(err, dcs.ErrNotFound) { + return nil, fmt.Errorf("failed to get %s: %w", pathMaintenance, err) } var manager dcs.LockOwner err = app.dcs.Get(pathManagerLock, &manager) - if err != nil && err != dcs.ErrNotFound { - return nil, fmt.Errorf("failed to get %s: %v", pathManagerLock, err) + if err != nil && !errors.Is(err, dcs.ErrNotFound) { + return nil, fmt.Errorf("failed to get %s: %w", pathManagerLock, err) } data[pathManagerLock] = manager.Hostname data[pathMasterNode] = master @@ -204,7 +205,7 @@ func parseLagFilter(raw string) (*lagFilter, error) { } value, err := strconv.ParseFloat(strings.TrimSpace(raw[1:]), 64) if err != nil { - return nil, fmt.Errorf("invalid lag filter %q: %v", raw, err) + return nil, fmt.Errorf("invalid lag filter %q: %w", raw, err) } return &lagFilter{op: op, value: value}, nil } diff --git a/internal/app/cli_maintenance.go b/internal/app/cli_maintenance.go index 51cba19c..8bd59a54 100644 --- a/internal/app/cli_maintenance.go +++ b/internal/app/cli_maintenance.go @@ -2,6 +2,7 @@ package app import ( "context" + "errors" "fmt" "time" @@ -27,7 +28,7 @@ func (app *App) CliEnableMaintenance(waitTimeout time.Duration, reason string, m Mode: mode, } 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("") return 1 } @@ -75,7 +76,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 { @@ -97,7 +98,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 } @@ -130,15 +131,14 @@ func (app *App) CliGetMaintenance() int { app.dcs.Initialize() err = app.dcs.Get(pathMaintenance, new(Maintenance)) - switch err { - case nil: + if err == nil { fmt.Println("on") return 0 - case dcs.ErrNotFound: + } + if errors.Is(err, dcs.ErrNotFound) { fmt.Println("off") return 0 - default: - app.logger.Error().Err(err).Msg("") - return 1 } + app.logger.Error().Err(err).Msg("") + return 1 } diff --git a/internal/app/cli_switch.go b/internal/app/cli_switch.go index 0f22f423..92824f0a 100644 --- a/internal/app/cli_switch.go +++ b/internal/app/cli_switch.go @@ -3,6 +3,7 @@ package app import ( "bufio" "context" + "errors" "fmt" "os" "slices" @@ -119,7 +120,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("") return 2 } @@ -136,7 +137,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 } @@ -189,7 +190,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 } diff --git a/internal/app/dcs/optimization.go b/internal/app/dcs/optimization.go index 5cd2448a..5239fb30 100644 --- a/internal/app/dcs/optimization.go +++ b/internal/app/dcs/optimization.go @@ -1,6 +1,7 @@ package dcs import ( + "errors" "sync" nodestate "github.com/yandex/mysync/internal/app/node_state" @@ -67,7 +68,7 @@ func (oda *OptimizationDCSAdapter) initDcs() error { oda.dcsInitiator.Do(func() { err = oda.dcs.Create(pathOptimizationNodes, "") }) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { oda.dcsInitiator = sync.Once{} return err } @@ -104,10 +105,10 @@ func (oda *OptimizationDCSAdapter) GetState(hostname string) (*optimization.DCSS path := dcs.JoinPath(pathOptimizationNodes, hostname) err = oda.dcs.Get(path, state) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return nil, err } - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { return nil, nil } return state, nil @@ -122,7 +123,7 @@ func (oda *OptimizationDCSAdapter) DeleteHosts(hostnames ...string) error { for _, hostname := range hostnames { path := dcs.JoinPath(pathOptimizationNodes, hostname) err := oda.dcs.Delete(path) - if err != nil && err != dcs.ErrNotFound { + if err != nil && !errors.Is(err, dcs.ErrNotFound) { return err } } @@ -139,7 +140,7 @@ func (oda *OptimizationDCSAdapter) CreateHosts(hostnames ...string) error { for _, hostname := range hostnames { path := dcs.JoinPath(pathOptimizationNodes, hostname) err := oda.dcs.Create(path, state) - if err != nil && err != dcs.ErrExists { + if err != nil && !errors.Is(err, dcs.ErrExists) { return err } } diff --git a/internal/app/optimization/controller.go b/internal/app/optimization/controller.go index b15083fb..f9c368d5 100644 --- a/internal/app/optimization/controller.go +++ b/internal/app/optimization/controller.go @@ -134,7 +134,7 @@ func (m *Controller) DisableAll(master Node, nodes []Node) error { err := m.disable(rs, node) if err != nil { - errors = append(errors, fmt.Errorf("%s:%s", hostname, err)) + errors = append(errors, fmt.Errorf("%s:%w", hostname, err)) } } diff --git a/internal/app/replication.go b/internal/app/replication.go index e7792f0a..fbd94597 100644 --- a/internal/app/replication.go +++ b/internal/app/replication.go @@ -415,7 +415,7 @@ func (app *App) optimizationPhase( app.logger.Info().Msgf("switchover: phase 0: turbo mode failed: %v", err) switchErr := app.FinishSwitchover(switchover, fmt.Errorf("turbo mode exceeded deadline")) if switchErr != nil { - return fmt.Errorf("switchover: failed to reject switchover %s", switchErr) + return fmt.Errorf("switchover: failed to reject switchover %w", switchErr) } app.logger.Info().Msg("switchover: rejected") return err diff --git a/internal/config/config.go b/internal/config/config.go index 6b99f90b..344a6fda 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -228,7 +228,7 @@ func ReadFromFile(configFile string) (*Config, error) { } loader := confita.NewLoader(file.NewBackend(configFile)) if err = loader.Load(context.Background(), &config); err != nil { - err = fmt.Errorf("failed to load config from %s: %s", configFile, err.Error()) + err = fmt.Errorf("failed to load config from %s: %w", configFile, err) return nil, err } if config.DevMode { diff --git a/internal/dcs/zk.go b/internal/dcs/zk.go index a1dfd5a6..6fbab298 100644 --- a/internal/dcs/zk.go +++ b/internal/dcs/zk.go @@ -174,7 +174,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 @@ -183,7 +183,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 } } @@ -274,7 +274,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() { @@ -349,19 +349,19 @@ 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 @@ -385,7 +385,7 @@ func (z *zkDCS) ReleaseLock(path string) { fullPath := z.buildFullPath(path) z.lockHeld.Delete(fullPath) data, 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 lock info %s", fullPath) return } @@ -412,7 +412,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) @@ -435,11 +435,11 @@ func (z *zkDCS) set(path string, val any, flags int32) error { panic(fmt.Sprintf("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 { @@ -472,7 +472,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 { @@ -489,7 +489,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 { @@ -541,7 +541,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/mysql/cluster.go b/internal/mysql/cluster.go index afc794ab..5ee13165 100644 --- a/internal/mysql/cluster.go +++ b/internal/mysql/cluster.go @@ -1,6 +1,7 @@ package mysql import ( + "errors" "fmt" "sort" "sync" @@ -41,7 +42,7 @@ func (c *Cluster) IsCascadeHost(hostname string) bool { func (c *Cluster) GetClusterHAFqdnsFromDcs() ([]string, error) { fqdns, err := c.dcs.GetChildren(dcs.PathHANodesPrefix) - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { return make([]string, 0), nil } if err != nil { @@ -69,7 +70,7 @@ func (c *Cluster) GetClusterHAHostsFromDcs() (map[string]NodeConfiguration, erro func (c *Cluster) GetClusterCascadeFqdnsFromDcs() ([]string, error) { fqdns, err := c.dcs.GetChildren(dcs.PathCascadeNodesPrefix) - if err == dcs.ErrNotFound { + if errors.Is(err, dcs.ErrNotFound) { return make([]string, 0), nil } if err != nil { @@ -109,7 +110,7 @@ func (c *Cluster) registerLocalNode() error { node, err := NewNode(c.config, c.logger, c.config.Hostname) if err != nil { c.Close() - return fmt.Errorf("failed to configure local node due (%v)", err) + return fmt.Errorf("failed to configure local node due (%w)", err) } // just configure local node, but do not register it in cluster c.local = node @@ -312,8 +313,8 @@ func (c *Cluster) GetNodeConfiguration(host string) (*NodeConfiguration, error) var nc NodeConfiguration err := c.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/internal/mysql/node.go b/internal/mysql/node.go index 0f1eec27..b2da3bb8 100644 --- a/internal/mysql/node.go +++ b/internal/mysql/node.go @@ -563,7 +563,7 @@ func isTestFileSystemReadonly(f string) (bool, error) { } value, err := strconv.ParseBool(strings.TrimSpace(string(data))) if err != nil { - return false, fmt.Errorf("error while parce test file: %s", err) + return false, fmt.Errorf("error while parce test file: %w", err) } return value, nil } @@ -642,7 +642,7 @@ func (n *Node) ReplicaStatusWithTimeout(timeout time.Duration, channel string) ( err = n.queryRowMogrifyWithTimeout(query, map[string]any{ "channel": channel, }, status, timeout) - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } return status, err @@ -668,7 +668,7 @@ func (n *Node) ReplicationLag(sstatus ReplicaStatus) (*float64, error) { if n.getQuery(queryReplicationLag) != "" { lag := new(replicationLag) err = n.queryRow(queryReplicationLag, nil, lag) - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { // looks like master return new(float64), nil } @@ -689,7 +689,7 @@ func (n *Node) ReplicationLag(sstatus ReplicaStatus) (*float64, error) { func (n *Node) GTIDExecuted() (*GTIDExecuted, error) { status := new(GTIDExecuted) err := n.queryRow(queryGTIDExecuted, nil, status) - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } return status, err @@ -925,7 +925,8 @@ func (n *Node) SemiSyncStatus() (*SemiSyncStatus, error) { status := new(SemiSyncStatus) err := n.queryRow(querySemiSyncStatus, nil, status) if err != nil { - if err2, ok := err.(*mysql.MySQLError); ok && err2.Number == 1193 { + var err2 *mysql.MySQLError + if errors.As(err, &err2) && err2.Number == 1193 { // Error: Unknown system variable // means semisync plugin is not loaded return status, nil @@ -1135,7 +1136,7 @@ func SaveCAFile(data string, path string) error { } err := os.WriteFile(path, byteData, 0o644) if err != nil { - err2 := fmt.Errorf("got error while writing CA file: %s", err) + err2 := fmt.Errorf("got error while writing CA file: %w", err) return err2 } return nil @@ -1404,7 +1405,7 @@ func (n *Node) GetExternalReplicationSources() (*[]ReplicationSource, error) { *replicationSources = append(*replicationSources, source) return nil }) - if IsErrorTableDoesNotExists(err) || err == sql.ErrNoRows { + if IsErrorTableDoesNotExists(err) || errors.Is(err, sql.ErrNoRows) { return nil, nil } return replicationSources, err diff --git a/internal/mysql/replication.go b/internal/mysql/replication.go index 58b7d0f2..48d4afcb 100644 --- a/internal/mysql/replication.go +++ b/internal/mysql/replication.go @@ -2,6 +2,7 @@ package mysql import ( "database/sql" + "errors" "github.com/yandex/mysync/internal/log" "github.com/yandex/mysync/internal/util" @@ -116,7 +117,7 @@ func (er *ExternalReplication) Set(n *Node) error { return nil } // If there is no rows in table for external replication - do nothing - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { n.logger.Info().Msgf("no external replication records found in replication table on host %s", n.host) return nil } diff --git a/internal/mysql/util.go b/internal/mysql/util.go index 6f34f200..b13859d6 100644 --- a/internal/mysql/util.go +++ b/internal/mysql/util.go @@ -1,6 +1,7 @@ package mysql import ( + "errors" "slices" "github.com/go-sql-driver/mysql" @@ -28,8 +29,8 @@ func IsErrorDubious(err error) bool { if err == nil { return false } - mysqlErr, ok := err.(*mysql.MySQLError) - if !ok { + var mysqlErr *mysql.MySQLError + if !errors.As(err, &mysqlErr) { return false } return slices.Contains(dubiousErrorNumbers, mysqlErr.Number) @@ -39,8 +40,8 @@ func IsErrorChannelDoesNotExists(err error) bool { if err == nil { return false } - mysqlErr, ok := err.(*mysql.MySQLError) - if !ok { + var mysqlErr *mysql.MySQLError + if !errors.As(err, &mysqlErr) { return false } if mysqlErr.Number == channelDoesNotExists { @@ -53,8 +54,8 @@ func IsErrorTableDoesNotExists(err error) bool { if err == nil { return false } - mysqlErr, ok := err.(*mysql.MySQLError) - if !ok { + var mysqlErr *mysql.MySQLError + if !errors.As(err, &mysqlErr) { return false } if mysqlErr.Number == tableDoesNotExists { diff --git a/tests/mysync_test.go b/tests/mysync_test.go index de63b61e..d1bf89f8 100644 --- a/tests/mysync_test.go +++ b/tests/mysync_test.go @@ -240,7 +240,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 } @@ -284,11 +284,11 @@ func (tctx *testContext) getMysqlConnection(host string) (*sqlx.DB, error) { } addr, err := tctx.composer.GetAddr(host, mysqlPort) if err != nil { - return nil, fmt.Errorf("failed to get mysql addr %s: %s", host, err) + return nil, fmt.Errorf("failed to get mysql addr %s: %w", host, err) } db, err = tctx.connectMysql(addr, mysqlConnectTimeout) if err != nil { - return nil, fmt.Errorf("failed to connect to mysql %s: %s", host, err) + return nil, fmt.Errorf("failed to connect to mysql %s: %w", host, err) } tctx.dbs[host] = db return db, nil @@ -443,7 +443,7 @@ func (tctx *testContext) stepClusterEnvironmentIs(body *godog.DocString) error { func (tctx *testContext) stepClusterIsUpAndRunning(createHaNodes bool) 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 @@ -454,7 +454,7 @@ func (tctx *testContext) stepClusterIsUpAndRunning(createHaNodes bool) 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) @@ -466,22 +466,22 @@ func (tctx *testContext) stepClusterIsUpAndRunning(createHaNodes bool) error { }, time.Minute, 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 mysync", "mysql", time.Minute) if err != nil { - return fmt.Errorf("failed to generate certs in mysql hosts: %s", err) + return fmt.Errorf("failed to generate certs in mysql 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 createHaNodes { 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) } } @@ -490,16 +490,16 @@ func (tctx *testContext) stepClusterIsUpAndRunning(createHaNodes bool) error { if strings.HasPrefix(service, mysqlName) { if createHaNodes { 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) } } addr, err2 := tctx.composer.GetAddr(service, mysqlPort) if err2 != nil { - return fmt.Errorf("failed to get mysql addr %s: %s", service, err2) + return fmt.Errorf("failed to get mysql addr %s: %w", service, err2) } db, err2 := tctx.connectMysql(addr, mysqlInitialConnectTimeout) if err2 != nil { - return fmt.Errorf("failed to connect to mysql %s: %s", service, err2) + return fmt.Errorf("failed to connect to mysql %s: %w", service, err2) } tctx.dbs[service] = db } @@ -512,7 +512,7 @@ func (tctx *testContext) stepClusterIsUpAndRunning(createHaNodes bool) error { if strings.HasPrefix(service, mysqlName) { err3 := tctx.stepMysqlHostShouldHaveVariableSetWithin(service, "offline_mode", "0", mysqlWaitOnlineTimeout) if err3 != nil { - return fmt.Errorf("failed to set up mysql for host %s: %s", service, err) + return fmt.Errorf("failed to set up mysql for host %s: %w", service, err) } } } @@ -593,11 +593,11 @@ func (tctx *testContext) stepRunHeavyUserRequests(host string, sleepTime int) er // don't use cached connections: addr, err := tctx.composer.GetAddr(host, mysqlPort) if err != nil { - return fmt.Errorf("failed to get mysql addr %s: %s", host, err) + return fmt.Errorf("failed to get mysql addr %s: %w", host, err) } db, err := tctx.connectMysqlWithCredentials(mysqlOrdinaryUser, mysqlOrdinaryPassword, addr, mysqlConnectTimeout) if err != nil { - return fmt.Errorf("failed to connect to mysql %s: %s", host, err) + return fmt.Errorf("failed to connect to mysql %s: %w", host, err) } if _, err := tctx.queryMysqlViaConnection(db, host, "CREATE TABLE IF NOT EXISTS t1(i INT)", nil, mysqlQueryTimeout); err != nil { @@ -628,11 +628,11 @@ func (tctx *testContext) stepRunHeavyReadUserRequests(host string, sleepTime int // don't use cached connections: addr, err := tctx.composer.GetAddr(host, mysqlPort) if err != nil { - return fmt.Errorf("failed to get mysql addr %s: %s", host, err) + return fmt.Errorf("failed to get mysql addr %s: %w", host, err) } db, err := tctx.connectMysqlWithCredentials(mysqlOrdinaryUser, mysqlOrdinaryPassword, addr, mysqlConnectTimeout) if err != nil { - return fmt.Errorf("failed to connect to mysql %s: %s", host, err) + return fmt.Errorf("failed to connect to mysql %s: %w", host, err) } go func() { @@ -930,13 +930,12 @@ func (tctx *testContext) stepIRunSQLOnHost(host string, body *godog.DocString) e func (tctx *testContext) stepIRunSQLOnHostExpectingErrorOfNumber(host string, errorNumber int, body *godog.DocString) error { query := strings.TrimSpace(body.Content) _, err := tctx.queryMysql(host, query, struct{}{}) - mysqlErr, ok := err.(*mysql.MySQLError) - if mysqlErr == nil { - err = fmt.Errorf("there is no expected sql error") - return err - } - if !ok { - return err + var mysqlErr *mysql.MySQLError + if !errors.As(err, &mysqlErr) { + if err != nil { + return err + } + return fmt.Errorf("there is no expected sql error") } num := uint16(errorNumber) if mysqlErr.Number == num { @@ -1073,10 +1072,10 @@ func (tctx *testContext) createZookeeperNode(node string) error { func (tctx *testContext) setZookeeperNode(node string, data []byte) error { _, 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) @@ -1170,7 +1169,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 { @@ -1353,7 +1352,7 @@ func (tctx *testContext) stepMysqlReplicationOnHostShouldNotRunFineWithin(host s func (tctx *testContext) stepMysqlHostShouldBecomeUnavailableWithin(host string, timeout int) error { addr, err := tctx.composer.GetAddr(host, mysqlPort) if err != nil { - return fmt.Errorf("failed to get mysql addr %s: %s", host, err) + return fmt.Errorf("failed to get mysql addr %s: %w", host, err) } testutil.Retry(func() bool { var db *sqlx.DB @@ -1373,7 +1372,7 @@ func (tctx *testContext) stepMysqlHostShouldBecomeUnavailableWithin(host string, func (tctx *testContext) stepMysqlHostShouldBecomeAvailableWithin(host string, timeout int) error { addr, err := tctx.composer.GetAddr(host, mysqlPort) if err != nil { - return fmt.Errorf("failed to get mysql addr %s: %s", host, err) + return fmt.Errorf("failed to get mysql addr %s: %w", host, err) } testutil.Retry(func() bool { var db *sqlx.DB diff --git a/tests/testutil/docker_composer.go b/tests/testutil/docker_composer.go index 8ebf2e4a..6f881cd2 100644 --- a/tests/testutil/docker_composer.go +++ b/tests/testutil/docker_composer.go @@ -77,7 +77,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)) @@ -85,7 +85,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 @@ -103,7 +103,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 %s': %s\n%s", strings.Join(args2, " "), err, out) + return fmt.Errorf("failed to run 'docker %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 ad8a89fc..7fce1e99 100644 --- a/tests/testutil/matchers/matchers.go +++ b/tests/testutil/matchers/matchers.go @@ -144,10 +144,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 { @@ -161,10 +161,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}