From: Timo Tietavainen Date: Sun, 28 Nov 2021 21:05:08 +0000 (+0200) Subject: Fix sdlcli healthcheck DBAAS status in SEP install X-Git-Tag: v0.9.0~1 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=6f724aa3950cdc01246351644348fdc0f6e9ca4a;p=ric-plt%2Fsdlgo.git Fix sdlcli healthcheck DBAAS status in SEP install Implement reading of the new environment variable 'DBAAS_NODE_COUNT' what defines how many DBAAS nodes there has been configured to be in one SDL cluster group. For the time being in HA deployment value is 3 and in standalone deployment 1. With help of this variable sdlcli 'healthcheck' -command is able to show NOK status in case any configured DBAAS pod is not present. Refactor also output writing of the sdlcli 'healthcheck' -command to show health status in a table layout. Issue-Id: RIC-113 Signed-off-by: Timo Tietavainen Change-Id: I1d677c6a78eda67e3bcfdf814654710b00dac270 --- diff --git a/internal/cli/healthcheck.go b/internal/cli/healthcheck.go index 2ede93e..257bd5d 100644 --- a/internal/cli/healthcheck.go +++ b/internal/cli/healthcheck.go @@ -27,6 +27,7 @@ import ( "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis" "github.com/spf13/cobra" "os" + "text/tabwriter" ) func init() { @@ -36,15 +37,12 @@ func init() { func newHealthCheckCmd(dbCreateCb DbCreateCb) *cobra.Command { cmd := &cobra.Command{ Use: "healthcheck", - Short: "Validate database healthiness", - Long: `Validate database healthiness`, + Short: "Validate SDL database healthiness", + Long: `Validate SDL database healthiness`, Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - out, err := runHealthCheck(dbCreateCb) - cmd.Println(out) - if err != nil { - cmd.PrintErrf("%s\n", buf.String()) - } + states, err := runHealthCheck(dbCreateCb) + printHealthStatus(cmd, states) return err }, } @@ -52,9 +50,8 @@ func newHealthCheckCmd(dbCreateCb DbCreateCb) *cobra.Command { return cmd } -func runHealthCheck(dbCreateCb DbCreateCb) (string, error) { +func runHealthCheck(dbCreateCb DbCreateCb) ([]sdlgoredis.DbState, error) { var anyErr error - var str string var states []sdlgoredis.DbState for _, dbInst := range dbCreateCb().Instances { state, err := dbInst.State() @@ -63,54 +60,103 @@ func runHealthCheck(dbCreateCb DbCreateCb) (string, error) { } states = append(states, *state) } - str = writeStateResults(states) - return str, anyErr + return states, anyErr } -func writeStateResults(dbStates []sdlgoredis.DbState) string { - var str string +func printHealthStatus(cmd *cobra.Command, dbStates []sdlgoredis.DbState) { var anyErr error + w := tabwriter.NewWriter(cmd.OutOrStdout(), 6, 4, 3, ' ', 0) + fmt.Fprintln(w, "CLUSTER\tROLE\tADDRESS\tSTATUS\tERROR\t") + for i, dbState := range dbStates { if err := dbState.IsOnline(); err != nil { anyErr = err } - str = str + fmt.Sprintf(" SDL DB backend #%d\n", (i+1)) - pAddr := dbState.PrimaryDbState.GetAddress() - err := dbState.PrimaryDbState.IsOnline() - if err == nil { - str = str + fmt.Sprintf(" Primary (%s): OK\n", pAddr) - } else { - str = str + fmt.Sprintf(" Primary (%s): NOK\n", pAddr) - str = str + fmt.Sprintf(" %s\n", err.Error()) + if err := printPrimaryHealthStatus(w, i, &dbState); err != nil { + anyErr = err + } + if err := printReplicasHealthStatus(w, i, &dbState); err != nil { + anyErr = err } - if dbState.ReplicasDbState != nil { - for j, rInfo := range dbState.ReplicasDbState.States { - err := rInfo.IsOnline() - if err == nil { - str = str + fmt.Sprintf(" Replica #%d (%s): OK\n", (j+1), rInfo.GetAddress()) - } else { - str = str + fmt.Sprintf(" Replica #%d (%s): NOK\n", (j+1), rInfo.GetAddress()) - str = str + fmt.Sprintf(" %s\n", err.Error()) - } - } + if err := printSentinelsHealthStatus(w, i, &dbState); err != nil { + anyErr = err } - if dbState.SentinelsDbState != nil { - for k, sInfo := range dbState.SentinelsDbState.States { - err := sInfo.IsOnline() - if err != nil { - str = str + fmt.Sprintf(" Sentinel #%d (%s): NOK\n", (k+1), sInfo.GetAddress()) - str = str + fmt.Sprintf(" %s\n", err.Error()) - } + } + if anyErr == nil { + cmd.Println("Overall status: OK") + } else { + cmd.Println("Overall status: NOK") + } + cmd.Println("") + w.Flush() +} + +func printPrimaryHealthStatus(w *tabwriter.Writer, clusterID int, dbState *sdlgoredis.DbState) error { + addr := printAddress(dbState.PrimaryDbState.GetAddress()) + err := dbState.PrimaryDbState.IsOnline() + if err != nil { + fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t\n", clusterID, "primary", addr, "NOK", err.Error()) + } else { + fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t\n", clusterID, "primary", addr, "OK", "") + } + return err +} + +func printReplicasHealthStatus(w *tabwriter.Writer, clusterID int, dbState *sdlgoredis.DbState) error { + var anyErr error + + if dbState.ReplicasDbState != nil { + if dbState.ConfigNodeCnt > len(dbState.ReplicasDbState.States)+1 { + err := fmt.Errorf("Configured DBAAS nodes %d but only 1 primary and %d replicas", + dbState.ConfigNodeCnt, len(dbState.ReplicasDbState.States)) + fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t\n", clusterID, "replica", "", "NOK", err.Error()) + anyErr = err + } + for _, state := range dbState.ReplicasDbState.States { + if err := printReplicaHealthStatus(w, clusterID, state); err != nil { + anyErr = err } } } - if anyErr == nil { - str = fmt.Sprintf("Overall status: OK\n\n") + str + return anyErr +} + +func printReplicaHealthStatus(w *tabwriter.Writer, clusterID int, dbState *sdlgoredis.ReplicaDbState) error { + addr := printAddress(dbState.GetAddress()) + err := dbState.IsOnline() + if err != nil { + fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t\n", clusterID, "replica", addr, "NOK", err.Error()) } else { - str = fmt.Sprintf("Overall status: NOK\n\n") + str + fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t\n", clusterID, "replica", addr, "OK", "") + } + return err +} + +func printSentinelsHealthStatus(w *tabwriter.Writer, clusterID int, dbState *sdlgoredis.DbState) error { + var anyErr error + if dbState.SentinelsDbState != nil { + for _, state := range dbState.SentinelsDbState.States { + if err := printSentinelHealthStatus(w, clusterID, state); err != nil { + anyErr = err + } + } + } + return anyErr +} +func printSentinelHealthStatus(w *tabwriter.Writer, clusterID int, dbState *sdlgoredis.SentinelDbState) error { + err := dbState.IsOnline() + if err != nil { + fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t\n", clusterID, "sentinel", dbState.GetAddress(), "NOK", err.Error()) + } + return err +} + +func printAddress(address string) string { + if address == "" { + return "" } - return str + return address } diff --git a/internal/cli/healthcheck_test.go b/internal/cli/healthcheck_test.go index 0de895a..f3cf65f 100644 --- a/internal/cli/healthcheck_test.go +++ b/internal/cli/healthcheck_test.go @@ -40,37 +40,33 @@ type healthCheckMocks struct { dbState sdlgoredis.DbState } -func setupHcMockPrimaryDb(ip, port string) { +func setupHcMockPrimaryDb(ip, port string, nodes int) { hcMocks = new(healthCheckMocks) + hcMocks.dbState.ConfigNodeCnt = nodes hcMocks.dbState.PrimaryDbState.Fields.Role = "master" hcMocks.dbState.PrimaryDbState.Fields.Ip = ip hcMocks.dbState.PrimaryDbState.Fields.Port = port hcMocks.dbState.PrimaryDbState.Fields.Flags = "master" + hcMocks.dbState.ReplicasDbState = new(sdlgoredis.ReplicasDbState) + hcMocks.dbState.ReplicasDbState.States = []*sdlgoredis.ReplicaDbState{} + hcMocks.dbState.SentinelsDbState = new(sdlgoredis.SentinelsDbState) + hcMocks.dbState.SentinelsDbState.States = []*sdlgoredis.SentinelDbState{} } -func setupHcMockReplicaDb(ip, port string) { +func setupHcMockReplicaDb(nodes int) { hcMocks = new(healthCheckMocks) + hcMocks.dbState.ConfigNodeCnt = nodes hcMocks.dbState.ReplicasDbState = new(sdlgoredis.ReplicasDbState) - hcMocks.dbState.ReplicasDbState.States = []*sdlgoredis.ReplicaDbState{ - &sdlgoredis.ReplicaDbState{ - Fields: sdlgoredis.ReplicaDbStateFields{ - Role: "slave", - }, - }, - } + hcMocks.dbState.ReplicasDbState.States = []*sdlgoredis.ReplicaDbState{} + hcMocks.dbState.SentinelsDbState = new(sdlgoredis.SentinelsDbState) + hcMocks.dbState.SentinelsDbState.States = []*sdlgoredis.SentinelDbState{} } -func setupHcMockSentinelDb(ip, port string) { +func setupHcMockSentinelDb(ip, port string, nodes int) { hcMocks = new(healthCheckMocks) + hcMocks.dbState.ConfigNodeCnt = nodes hcMocks.dbState.SentinelsDbState = new(sdlgoredis.SentinelsDbState) - hcMocks.dbState.SentinelsDbState.States = []*sdlgoredis.SentinelDbState{ - &sdlgoredis.SentinelDbState{ - Fields: sdlgoredis.SentinelDbStateFields{ - Ip: ip, - Port: port, - }, - }, - } + hcMocks.dbState.SentinelsDbState.States = []*sdlgoredis.SentinelDbState{} } func addHcMockReplicaDbState(ip, port, primaryLinkOk string) { @@ -154,7 +150,13 @@ func TestCliHealthCheckCanShowHelp(t *testing.T) { } func TestCliHealthCheckCanShowHaDeploymentOkStatusCorrectly(t *testing.T) { - setupHcMockPrimaryDb("10.20.30.40", "6379") + expOut := + "Overall status: OK\n\n" + + "CLUSTER ROLE ADDRESS STATUS ERROR \n" + + "0 primary 10.20.30.40:6379 OK \n" + + "0 replica 1.2.3.4:6379 OK \n" + + "0 replica 5.6.7.8:6379 OK \n" + setupHcMockPrimaryDb("10.20.30.40", "6379", 3) addHcMockReplicaDbState("1.2.3.4", "6379", "ok") addHcMockReplicaDbState("5.6.7.8", "6379", "ok") addHcMockSentinelDbState("1.2.3.4", "26379", "sentinel") @@ -163,15 +165,17 @@ func TestCliHealthCheckCanShowHaDeploymentOkStatusCorrectly(t *testing.T) { stdout, err := runHcCli() assert.Nil(t, err) - assert.Contains(t, stdout, "Overall status: OK") - assert.Contains(t, stdout, "Primary (10.20.30.40:6379): OK") - assert.Contains(t, stdout, "Replica #1 (1.2.3.4:6379): OK") - assert.Contains(t, stdout, "Replica #2 (5.6.7.8:6379): OK") - + assert.Equal(t, expOut, stdout) } func TestCliHealthCheckCanShowHaDeploymentStatusCorrectlyWhenOneReplicaStateNotUp(t *testing.T) { - setupHcMockPrimaryDb("10.20.30.40", "6379") + expOut := + "Overall status: NOK\n\n" + + "CLUSTER ROLE ADDRESS STATUS ERROR \n" + + "0 primary 10.20.30.40:6379 OK \n" + + "0 replica 1.2.3.4:6379 OK \n" + + "0 replica 5.6.7.8:6379 NOK Replica link to the primary is down \n" + setupHcMockPrimaryDb("10.20.30.40", "6379", 3) addHcMockReplicaDbState("1.2.3.4", "6379", "ok") addHcMockReplicaDbState("5.6.7.8", "6379", "nok") addHcMockSentinelDbState("1.2.3.4", "26379", "sentinel") @@ -180,13 +184,18 @@ func TestCliHealthCheckCanShowHaDeploymentStatusCorrectlyWhenOneReplicaStateNotU stdout, err := runHcCli() assert.Nil(t, err) - assert.Contains(t, stdout, "Overall status: NOK") - assert.Contains(t, stdout, "Replica #2 (5.6.7.8:6379): NOK") - assert.Contains(t, stdout, "Replica link to the primary is down") + assert.Equal(t, expOut, stdout) } func TestCliHealthCheckCanShowHaDeploymentStatusCorrectlyWhenOneSentinelStateNotUp(t *testing.T) { - setupHcMockPrimaryDb("10.20.30.40", "6379") + expOut := + "Overall status: NOK\n\n" + + "CLUSTER ROLE ADDRESS STATUS ERROR \n" + + "0 primary 10.20.30.40:6379 OK \n" + + "0 replica 1.2.3.4:6379 OK \n" + + "0 replica 5.6.7.8:6379 OK \n" + + "0 sentinel 1.2.3.4:26379 NOK Sentinel flags are 'some-failure', expected 'sentinel' \n" + setupHcMockPrimaryDb("10.20.30.40", "6379", 3) addHcMockReplicaDbState("1.2.3.4", "6379", "ok") addHcMockReplicaDbState("5.6.7.8", "6379", "ok") addHcMockSentinelDbState("1.2.3.4", "26379", "some-failure") @@ -195,15 +204,27 @@ func TestCliHealthCheckCanShowHaDeploymentStatusCorrectlyWhenOneSentinelStateNot stdout, err := runHcCli() assert.Nil(t, err) - assert.Contains(t, stdout, "Overall status: NOK") - assert.Contains(t, stdout, "Replica #1 (1.2.3.4:6379): OK") - assert.Contains(t, stdout, "Replica #2 (5.6.7.8:6379): OK") - assert.Contains(t, stdout, "Sentinel #1 (1.2.3.4:26379): NOK") - assert.Contains(t, stdout, "Sentinel flags are 'some-failure', expected 'sentinel'") + assert.Equal(t, expOut, stdout) +} + +func TestCliHealthCheckCanShowHaDeploymentStatusCorrectlyWhenNoReplicas(t *testing.T) { + expOut := + "Overall status: NOK\n\n" + + "CLUSTER ROLE ADDRESS STATUS ERROR \n" + + "0 primary 10.20.30.40:6379 OK \n" + + "0 replica NOK Configured DBAAS nodes 3 but only 1 primary and 0 replicas \n" + setupHcMockPrimaryDb("10.20.30.40", "6379", 3) + addHcMockSentinelDbState("1.2.3.4", "26379", "sentinel") + addHcMockSentinelDbState("5.6.7.8", "26379", "sentinel") + + stdout, err := runHcCli() + + assert.Nil(t, err) + assert.Equal(t, expOut, stdout) } func TestCliHealthCheckCanShowHaDeploymentStatusCorrectlyWhenDbStateQueryFails(t *testing.T) { - setupHcMockPrimaryDb("10.20.30.40", "6379") + setupHcMockPrimaryDb("10.20.30.40", "6379", 3) hcMocks.dbErr = errors.New("Some error") buf := new(bytes.Buffer) @@ -214,35 +235,48 @@ func TestCliHealthCheckCanShowHaDeploymentStatusCorrectlyWhenDbStateQueryFails(t stderr := buf.String() assert.Equal(t, hcMocks.dbErr, err) - assert.Contains(t, stderr, "Error: "+hcMocks.dbErr.Error()) + assert.Contains(t, stderr, "Error: Some error") } func TestCliHealthCheckCanShowHaDeploymentOkStatusCorrectlyWhenDbStateIsFromReplicaOnly(t *testing.T) { - setupHcMockReplicaDb("1.2.3.4", "6379") + expOut := + "Overall status: NOK\n\n" + + "CLUSTER ROLE ADDRESS STATUS ERROR \n" + + "0 primary NOK No primary DB, current role '' \n" + + "0 replica 1.2.3.4:6379 NOK Replica link to the primary is down \n" + + "0 replica 5.6.7.8:6379 NOK Replica link to the primary is down \n" + setupHcMockReplicaDb(3) + addHcMockReplicaDbState("1.2.3.4", "6379", "nok") + addHcMockReplicaDbState("5.6.7.8", "6379", "nok") stdout, err := runHcCli() assert.Nil(t, err) - assert.Contains(t, stdout, "Overall status: NOK") - assert.Contains(t, stdout, "Primary (): NOK") + assert.Equal(t, expOut, stdout) } func TestCliHealthCheckCanShowHaDeploymentOkStatusCorrectlyWhenDbStateIsFromSentinelOnly(t *testing.T) { - setupHcMockSentinelDb("1.2.3.4", "26379") + expOut := + "Overall status: NOK\n\n" + + "CLUSTER ROLE ADDRESS STATUS ERROR \n" + + "0 primary NOK No primary DB, current role '' \n" + setupHcMockSentinelDb("1.2.3.4", "26379", 3) stdout, err := runHcCli() assert.Nil(t, err) - assert.Contains(t, stdout, "Overall status: NOK") - assert.Contains(t, stdout, "Primary (): NOK") + assert.Equal(t, expOut, stdout) } func TestCliHealthCheckCanShowStandaloneDeploymentOkStatusCorrectly(t *testing.T) { - setupHcMockPrimaryDb("10.20.30.40", "6379") + expOut := + "Overall status: OK\n\n" + + "CLUSTER ROLE ADDRESS STATUS ERROR \n" + + "0 primary 10.20.30.40:6379 OK \n" + setupHcMockPrimaryDb("10.20.30.40", "6379", 1) stdout, err := runHcCli() assert.Nil(t, err) - assert.Contains(t, stdout, "Overall status: OK") - assert.Contains(t, stdout, "Primary (10.20.30.40:6379): OK") + assert.Equal(t, expOut, stdout) } diff --git a/internal/sdlgoredis/dbstate.go b/internal/sdlgoredis/dbstate.go index e3b9d0a..6bbe40f 100644 --- a/internal/sdlgoredis/dbstate.go +++ b/internal/sdlgoredis/dbstate.go @@ -29,6 +29,9 @@ import ( //DbState struct is a holder for DB state information, which is received from //sdlgoredis sentinel 'Master' and 'Slaves' calls output. type DbState struct { + Err error + ConfigNodeCnt int + PrimaryDbState PrimaryDbState ReplicasDbState *ReplicasDbState SentinelsDbState *SentinelsDbState @@ -90,6 +93,10 @@ type SentinelDbStateFields struct { } func (dbst *DbState) IsOnline() error { + if err := dbst.Err; err != nil { + return err + } + if err := dbst.PrimaryDbState.IsOnline(); err != nil { return err } diff --git a/internal/sdlgoredis/dbstate_test.go b/internal/sdlgoredis/dbstate_test.go index 1727bbc..9a3a5e9 100644 --- a/internal/sdlgoredis/dbstate_test.go +++ b/internal/sdlgoredis/dbstate_test.go @@ -37,6 +37,10 @@ func setupDbState() *dbStateMock { return new(dbStateMock) } +func (ds *dbStateMock) setError(err error) { + ds.state.Err = err +} + func (ds *dbStateMock) setPrimaryError(err error) { ds.state.PrimaryDbState.Err = err } @@ -86,6 +90,14 @@ func (ds *dbStateMock) addSentinelFields(ip, port, flags string) { ds.state.SentinelsDbState.States = append(ds.state.SentinelsDbState.States, newState) } +func TestIsOnlineFailureIfErrorHasSet(t *testing.T) { + testErr := errors.New("Some error") + st := setupDbState() + st.setError(testErr) + err := st.state.IsOnline() + assert.Equal(t, testErr, err) +} + func TestIsOnlineWhenSinglePrimarySuccessfully(t *testing.T) { st := setupDbState() st.setPrimaryFields("master", "1.2.3.4", "60000", "0", "master") diff --git a/internal/sdlgoredis/sdlgoredis.go b/internal/sdlgoredis/sdlgoredis.go index cefe4be..77dce09 100644 --- a/internal/sdlgoredis/sdlgoredis.go +++ b/internal/sdlgoredis/sdlgoredis.go @@ -24,6 +24,7 @@ package sdlgoredis import ( "errors" + "fmt" "github.com/go-redis/redis/v7" "io" "log" @@ -54,6 +55,7 @@ type Config struct { masterName string sentinelPort string clusterAddrList string + nodeCnt string } type DB struct { @@ -174,6 +176,7 @@ func readConfig(osI OS) Config { masterName: osI.Getenv("DBAAS_MASTER_NAME", ""), sentinelPort: osI.Getenv("DBAAS_SERVICE_SENTINEL_PORT", ""), clusterAddrList: osI.Getenv("DBAAS_CLUSTER_ADDR_LIST", ""), + nodeCnt: osI.Getenv("DBAAS_NODE_COUNT", "1"), } return cfg } @@ -487,12 +490,16 @@ func (db *DB) PTTL(key string) (time.Duration, error) { func (db *DB) Info() (*DbInfo, error) { var info DbInfo resultStr, err := db.client.Info("all").Result() + if err != nil { + return &info, err + } + result := strings.Split(strings.ReplaceAll(resultStr, "\r\n", "\n"), "\n") - readRedisInfoReplyFields(result, &info) + err = readRedisInfoReplyFields(result, &info) return &info, err } -func readRedisInfoReplyFields(input []string, info *DbInfo) { +func readRedisInfoReplyFields(input []string, info *DbInfo) error { for _, line := range input { if idx := strings.Index(line, "role:"); idx != -1 { roleStr := line[idx+len("role:"):] @@ -501,15 +508,18 @@ func readRedisInfoReplyFields(input []string, info *DbInfo) { } } else if idx := strings.Index(line, "connected_slaves:"); idx != -1 { cntStr := line[idx+len("connected_slaves:"):] - if cnt, err := strconv.ParseUint(cntStr, 10, 32); err == nil { - info.Fields.ConnectedReplicaCnt = uint32(cnt) + cnt, err := strconv.ParseUint(cntStr, 10, 32) + if err != nil { + return fmt.Errorf("Info reply error: %s", err.Error()) } - + info.Fields.ConnectedReplicaCnt = uint32(cnt) } } + return nil } func (db *DB) State() (*DbState, error) { + dbState := new(DbState) if db.cfg.sentinelPort != "" { //Establish connection to Redis sentinel. The reason why connection is done //here instead of time of the SDL instance creation is that for the time being @@ -520,17 +530,16 @@ func (db *DB) State() (*DbState, error) { sentinelClient := db.sentinel(&db.cfg, db.addr) return sentinelClient.GetDbState() } else { - var dbState DbState info, err := db.Info() if err != nil { - return &dbState, err + dbState.PrimaryDbState.Err = err + return dbState, err } - dbState = fillDbStateFromDbInfo(info) - return &dbState, err + return db.fillDbStateFromDbInfo(info) } } -func fillDbStateFromDbInfo(info *DbInfo) DbState { +func (db *DB) fillDbStateFromDbInfo(info *DbInfo) (*DbState, error) { var dbState DbState if info.Fields.PrimaryRole == true { dbState = DbState{ @@ -542,7 +551,15 @@ func fillDbStateFromDbInfo(info *DbInfo) DbState { }, } } - return dbState + + cnt, err := strconv.Atoi(db.cfg.nodeCnt) + if err != nil { + dbState.Err = fmt.Errorf("DBAAS_NODE_COUNT configuration value '%s' conversion to integer failed", db.cfg.nodeCnt) + } else { + dbState.ConfigNodeCnt = cnt + } + + return &dbState, dbState.Err } var luaRefresh = redis.NewScript(`if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("pexpire", KEYS[1], ARGV[2]) else return 0 end`) diff --git a/internal/sdlgoredis/sdlgoredis_test.go b/internal/sdlgoredis/sdlgoredis_test.go index 16b9c75..34e057f 100644 --- a/internal/sdlgoredis/sdlgoredis_test.go +++ b/internal/sdlgoredis/sdlgoredis_test.go @@ -180,11 +180,11 @@ type setupEv struct { } func setupHaEnv(commandsExists bool) (*pubSubMock, *clientMock, *sdlgoredis.DB) { - psm, cm, _, db := setupHaEnvWithSentinels(commandsExists) + psm, cm, _, db := setupHaEnvWithSentinels(commandsExists, "3") return psm, cm, db } -func setupHaEnvWithSentinels(commandsExists bool) (*pubSubMock, *clientMock, []*MockRedisSentinel, *sdlgoredis.DB) { +func setupHaEnvWithSentinels(commandsExists bool, nodeCnt string) (*pubSubMock, *clientMock, []*MockRedisSentinel, *sdlgoredis.DB) { setupVals := setupEnv( commandsExists, "service-ricplt-dbaas-tcp-cluster-0.ricplt", @@ -192,16 +192,16 @@ func setupHaEnvWithSentinels(commandsExists bool) (*pubSubMock, *clientMock, []* "dbaasmaster", "26376", "", - "3", + nodeCnt, ) return setupVals.pubSubMock[0], setupVals.rClient[0], setupVals.rSentinel, setupVals.db[0] } -func setupSingleEnv(commandsExists bool) (*pubSubMock, *clientMock, *sdlgoredis.DB) { +func setupSingleEnv(commandsExists bool, nodeCnt string) (*pubSubMock, *clientMock, *sdlgoredis.DB) { setupVals := setupEnv( commandsExists, "service-ricplt-dbaas-tcp-cluster-0.ricplt", - "6376", "", "", "", "", + "6376", "", "", "", nodeCnt, ) return setupVals.pubSubMock[0], setupVals.rClient[0], setupVals.db[0] } @@ -235,7 +235,7 @@ func setupEnv(commandsExists bool, host, port, msname, sntport, clsaddrlist, nod osmock.On("Getenv", "DBAAS_MASTER_NAME", "").Return(msname) osmock.On("Getenv", "DBAAS_SERVICE_SENTINEL_PORT", "").Return(sntport) osmock.On("Getenv", "DBAAS_CLUSTER_ADDR_LIST", "").Return(clsaddrlist) - osmock.On("Getenv", "DBAAS_SERVICE_NODE_COUNT", "").Return(nodeCnt) + osmock.On("Getenv", "DBAAS_NODE_COUNT", "1").Return(nodeCnt) pubSubMock, subscribeNotifications := setSubscribeNotifications() smock := new(MockRedisSentinel) @@ -262,6 +262,116 @@ func setupEnv(commandsExists bool, host, port, msname, sntport, clsaddrlist, nod return ret } +func newMockRedisMasterCallResp(role, ip, port, flag string) map[string]string { + resp := map[string]string{} + if role != "" { + resp["role-reported"] = role + resp["ip"] = ip + resp["port"] = port + resp["flags"] = flag + } + return resp +} + +type mockRedisSlaves struct { + resp []interface{} +} + +func newMockRedisSlavesCall() *mockRedisSlaves { + return new(mockRedisSlaves) +} + +func (mrr *mockRedisSlaves) add(role, ip, port, link, flag string) { + mrr.resp = append(mrr.resp, + []interface{}{ + "role-reported", role, + "ip", ip, + "port", port, + "master-link-status", link, + "flags", flag, + }, + ) +} + +type mockRedisSentinels struct { + resp []interface{} +} + +func newMockRedisSentinelsCall() *mockRedisSentinels { + return new(mockRedisSentinels) +} + +func (mrs *mockRedisSentinels) add(ip, port, flag string) { + mrs.resp = append(mrs.resp, + []interface{}{ + "ip", ip, + "port", port, + "flags", flag, + }, + ) +} + +type ExpDbState struct { + s sdlgoredis.DbState +} + +func newExpDbState(nodeCnt int, err error) *ExpDbState { + state := new(ExpDbState) + state.s.ConfigNodeCnt = nodeCnt + state.s.Err = err + return state +} + +func (edbs *ExpDbState) addPrimary(role, ip, port, flag string, err error) { + edbs.s.PrimaryDbState.Err = err + edbs.s.PrimaryDbState.Fields = sdlgoredis.PrimaryDbStateFields{ + Role: role, + Ip: ip, + Port: port, + Flags: flag, + } +} + +func (edbs *ExpDbState) addReplica(role, ip, port, link, flag string, err error) { + if edbs.s.ReplicasDbState == nil { + edbs.s.ReplicasDbState = new(sdlgoredis.ReplicasDbState) + edbs.s.ReplicasDbState.States = make([]*sdlgoredis.ReplicaDbState, 0) + } + edbs.s.ReplicasDbState.Err = err + if ip != "" || port != "" || link != "" || flag != "" { + edbs.s.ReplicasDbState.States = append(edbs.s.ReplicasDbState.States, + &sdlgoredis.ReplicaDbState{ + Fields: sdlgoredis.ReplicaDbStateFields{ + Role: role, + Ip: ip, + Port: port, + PrimaryLinkStatus: link, + Flags: flag, + }, + }, + ) + } +} + +func (edbs *ExpDbState) addSentinel(ip, port, flag string, err error) { + if edbs.s.SentinelsDbState == nil { + edbs.s.SentinelsDbState = new(sdlgoredis.SentinelsDbState) + edbs.s.SentinelsDbState.States = make([]*sdlgoredis.SentinelDbState, 0) + } + edbs.s.SentinelsDbState.Err = err + if ip != "" || port != "" || flag != "" { + edbs.s.SentinelsDbState.States = append(edbs.s.SentinelsDbState.States, + &sdlgoredis.SentinelDbState{ + Fields: sdlgoredis.SentinelDbStateFields{ + Ip: ip, + Port: port, + Flags: flag, + }, + }, + ) + } +} + func TestCloseDbSuccessfully(t *testing.T) { _, r, db := setupHaEnv(true) r.On("Close").Return(nil) @@ -1096,6 +1206,27 @@ func TestInfoOfStandalonePrimaryRedisSuccessfully(t *testing.T) { r.AssertExpectations(t) } +func TestInfoOfStandalonePrimaryRedisFailureWhenIntConversionFails(t *testing.T) { + expErr := errors.New("Info reply error: strconv.ParseUint: parsing \"not-int\": invalid syntax") + _, r, db := setupHaEnv(true) + redisInfo := "# Replication\r\n" + + "role:master\r\n" + + "connected_slaves:not-int\r\n" + + "min_slaves_good_slaves:0\r\n" + expInfo := &sdlgoredis.DbInfo{ + Fields: sdlgoredis.DbInfoFields{ + PrimaryRole: true, + ConnectedReplicaCnt: 0, + }, + } + + r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, nil)) + info, err := db.Info() + assert.Equal(t, expErr, err) + assert.Equal(t, expInfo, info) + r.AssertExpectations(t) +} + func TestInfoWithGibberishContentSuccessfully(t *testing.T) { _, r, db := setupHaEnv(true) redisInfo := "!#¤%&?+?´-\r\n" @@ -1125,259 +1256,175 @@ func TestInfoWithEmptyContentSuccessfully(t *testing.T) { } func TestStateWithPrimaryAndTwoReplicaRedisSuccessfully(t *testing.T) { - _, r, s, db := setupHaEnvWithSentinels(true) - redisPrimaryState := map[string]string{ - "role-reported": "master", - } - redisReplicasState := make([]interface{}, 2) - redisReplicasState[0] = []interface{}{ - "role-reported", "slave", - "ip", "10.20.30.40", - "port", "6379", - "flags", "slave", - "master-link-status", "up", - } - redisReplicasState[1] = []interface{}{ - "master-link-status", "up", - "ip", "10.20.30.50", - "flags", "slave", - "port", "30000", - "role-reported", "slave", - } - redisSentinelsState := make([]interface{}, 2) - redisSentinelsState[0] = []interface{}{ - "ip", "10.20.30.40", - "port", "26379", - "flags", "sentinel", - } - redisSentinelsState[1] = []interface{}{ - "ip", "10.20.30.50", - "flags", "sentinel", - "port", "30001", - } - - expState := &sdlgoredis.DbState{ - PrimaryDbState: sdlgoredis.PrimaryDbState{ - Fields: sdlgoredis.PrimaryDbStateFields{ - Role: "master", - }, - }, - ReplicasDbState: &sdlgoredis.ReplicasDbState{ - States: []*sdlgoredis.ReplicaDbState{ - &sdlgoredis.ReplicaDbState{ - Fields: sdlgoredis.ReplicaDbStateFields{ - Role: "slave", - Ip: "10.20.30.40", - Port: "6379", - PrimaryLinkStatus: "up", - Flags: "slave", - }, - }, - &sdlgoredis.ReplicaDbState{ - Fields: sdlgoredis.ReplicaDbStateFields{ - Role: "slave", - Ip: "10.20.30.50", - Port: "30000", - PrimaryLinkStatus: "up", - Flags: "slave", - }, - }, - }, - }, - SentinelsDbState: &sdlgoredis.SentinelsDbState{ - States: []*sdlgoredis.SentinelDbState{ - &sdlgoredis.SentinelDbState{ - Fields: sdlgoredis.SentinelDbStateFields{ - Ip: "10.20.30.40", - Port: "26379", - Flags: "sentinel", - }, - }, - &sdlgoredis.SentinelDbState{ - Fields: sdlgoredis.SentinelDbStateFields{ - Ip: "10.20.30.50", - Port: "30001", - Flags: "sentinel", - }, - }, - }, - }, - } + _, r, s, db := setupHaEnvWithSentinels(true, "3") + + redisPrimaryState := newMockRedisMasterCallResp("master", "10.20.30.30", "6379", "master") + redisReplicasState := newMockRedisSlavesCall() + redisReplicasState.add("slave", "10.20.30.40", "6379", "up", "slave") + redisReplicasState.add("slave", "10.20.30.50", "30000", "up", "slave") + redisSentinelsState := newMockRedisSentinelsCall() + redisSentinelsState.add("10.20.30.40", "26379", "sentinel") + redisSentinelsState.add("10.20.30.50", "30001", "sentinel") + + expState := newExpDbState(3, nil) + expState.addPrimary("master", "10.20.30.30", "6379", "master", nil) + expState.addReplica("slave", "10.20.30.40", "6379", "up", "slave", nil) + expState.addReplica("slave", "10.20.30.50", "30000", "up", "slave", nil) + expState.addSentinel("10.20.30.40", "26379", "sentinel", nil) + expState.addSentinel("10.20.30.50", "30001", "sentinel", nil) s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisPrimaryState, nil)) - s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState, nil)) - s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState, nil)) + s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState.resp, nil)) + s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState.resp, nil)) ret, err := db.State() assert.Nil(t, err) - assert.Equal(t, expState, ret) + assert.Equal(t, expState.s, *ret) r.AssertExpectations(t) } -func TestStateWithPrimaryAndOneReplicaRedisFailureInPrimaryRedisCall(t *testing.T) { - _, r, s, db := setupHaEnvWithSentinels(true) - redisPrimaryState := map[string]string{} - redisReplicasState := make([]interface{}, 1) - redisReplicasState[0] = []interface{}{ - "role-reported", "slave", - "ip", "10.20.30.40", - "port", "6379", - "flags", "slave", - "master-link-status", "up", - } - redisSentinelsState := make([]interface{}, 1) - redisSentinelsState[0] = []interface{}{ - "ip", "10.20.30.40", - "port", "26379", - "flags", "sentinel", - } +func TestStateWithPrimaryAndTwoReplicaRedisFailureInPrimaryRedisCall(t *testing.T) { + expErr := errors.New("Some error") + _, r, s, db := setupHaEnvWithSentinels(true, "3") - expState := &sdlgoredis.DbState{ - PrimaryDbState: sdlgoredis.PrimaryDbState{ - Err: errors.New("Some error"), - }, - ReplicasDbState: &sdlgoredis.ReplicasDbState{ - States: []*sdlgoredis.ReplicaDbState{ - &sdlgoredis.ReplicaDbState{ - Fields: sdlgoredis.ReplicaDbStateFields{ - Role: "slave", - Ip: "10.20.30.40", - Port: "6379", - PrimaryLinkStatus: "up", - Flags: "slave", - }, - }, - }, - }, - SentinelsDbState: &sdlgoredis.SentinelsDbState{ - States: []*sdlgoredis.SentinelDbState{ - &sdlgoredis.SentinelDbState{ - Fields: sdlgoredis.SentinelDbStateFields{ - Ip: "10.20.30.40", - Port: "26379", - Flags: "sentinel", - }, - }, - }, - }, - } + redisPrimaryState := newMockRedisMasterCallResp("master", "10.20.30.30", "6379", "master") + redisReplicasState := newMockRedisSlavesCall() + redisReplicasState.add("slave", "10.20.30.40", "6379", "up", "slave") + redisReplicasState.add("slave", "10.20.30.50", "30000", "up", "slave") + redisSentinelsState := newMockRedisSentinelsCall() + redisSentinelsState.add("10.20.30.40", "26379", "sentinel") + redisSentinelsState.add("10.20.30.50", "30001", "sentinel") + + expState := newExpDbState(3, nil) + expState.addPrimary("", "", "", "", expErr) + expState.addReplica("slave", "10.20.30.40", "6379", "up", "slave", nil) + expState.addReplica("slave", "10.20.30.50", "30000", "up", "slave", nil) + expState.addSentinel("10.20.30.40", "26379", "sentinel", nil) + expState.addSentinel("10.20.30.50", "30001", "sentinel", nil) - s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisPrimaryState, errors.New("Some error"))) - s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState, nil)) - s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState, nil)) + s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisPrimaryState, expErr)) + s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState.resp, nil)) + s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState.resp, nil)) ret, err := db.State() assert.NotNil(t, err) - assert.Equal(t, expState, ret) + assert.Equal(t, expState.s, *ret) r.AssertExpectations(t) } -func TestStateWithPrimaryAndOneReplicaRedisFailureInReplicasRedisCall(t *testing.T) { - _, r, s, db := setupHaEnvWithSentinels(true) - redisPrimaryState := map[string]string{ - "role-reported": "master", - } - redisReplicasState := make([]interface{}, 1) - redisReplicasState[0] = []interface{}{} - redisSentinelsState := make([]interface{}, 1) - redisSentinelsState[0] = []interface{}{ - "ip", "10.20.30.40", - "port", "26379", - "flags", "sentinel", - } +func TestStateWithPrimaryAndTwoReplicaRedisFailureInReplicasRedisCall(t *testing.T) { + expErr := errors.New("Some error") + _, r, s, db := setupHaEnvWithSentinels(true, "3") - expState := &sdlgoredis.DbState{ - PrimaryDbState: sdlgoredis.PrimaryDbState{ - Fields: sdlgoredis.PrimaryDbStateFields{ - Role: "master", - }, - }, - ReplicasDbState: &sdlgoredis.ReplicasDbState{ - Err: errors.New("Some error"), - States: []*sdlgoredis.ReplicaDbState{}, - }, - SentinelsDbState: &sdlgoredis.SentinelsDbState{ - States: []*sdlgoredis.SentinelDbState{ - &sdlgoredis.SentinelDbState{ - Fields: sdlgoredis.SentinelDbStateFields{ - Ip: "10.20.30.40", - Port: "26379", - Flags: "sentinel", - }, - }, - }, - }, - } + redisPrimaryState := newMockRedisMasterCallResp("master", "10.20.30.30", "6379", "master") + redisReplicasState := newMockRedisSlavesCall() + redisReplicasState.add("slave", "10.20.30.40", "6379", "up", "slave") + redisReplicasState.add("slave", "10.20.30.50", "30000", "up", "slave") + redisSentinelsState := newMockRedisSentinelsCall() + redisSentinelsState.add("10.20.30.40", "26379", "sentinel") + redisSentinelsState.add("10.20.30.50", "30001", "sentinel") + + expState := newExpDbState(3, nil) + expState.addPrimary("master", "10.20.30.30", "6379", "master", nil) + expState.addReplica("", "", "", "", "", expErr) + expState.addReplica("", "", "", "", "", expErr) + expState.addSentinel("10.20.30.40", "26379", "sentinel", nil) + expState.addSentinel("10.20.30.50", "30001", "sentinel", nil) s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisPrimaryState, nil)) - s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState, errors.New("Some error"))) - s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState, nil)) + s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState.resp, errors.New("Some error"))) + s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState.resp, nil)) ret, err := db.State() assert.NotNil(t, err) - assert.Equal(t, expState, ret) + assert.Equal(t, expState.s, *ret) r.AssertExpectations(t) } func TestStateWithPrimaryAndOneReplicaRedisFailureInSentinelsRedisCall(t *testing.T) { - _, r, s, db := setupHaEnvWithSentinels(true) - redisPrimaryState := map[string]string{ - "role-reported": "master", - } - redisReplicasState := make([]interface{}, 1) - redisReplicasState[0] = []interface{}{ - "role-reported", "slave", - "ip", "10.20.30.40", - "port", "6379", - "flags", "slave", - "master-link-status", "up", - } - redisSentinelsState := make([]interface{}, 1) - redisSentinelsState[0] = []interface{}{ - "ip", "10.20.30.40", - "port", "26379", - "flags", "sentinel", - } + expErr := errors.New("Some error") + _, r, s, db := setupHaEnvWithSentinels(true, "2") + + redisPrimaryState := newMockRedisMasterCallResp("master", "10.20.30.30", "6379", "master") + redisReplicasState := newMockRedisSlavesCall() + redisReplicasState.add("slave", "10.20.30.40", "6379", "up", "slave") + redisSentinelsState := newMockRedisSentinelsCall() + redisSentinelsState.add("10.20.30.40", "26379", "sentinel") + + expState := newExpDbState(2, nil) + expState.addPrimary("master", "10.20.30.30", "6379", "master", nil) + expState.addReplica("slave", "10.20.30.40", "6379", "up", "slave", nil) + expState.addSentinel("", "", "", expErr) + + s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisPrimaryState, nil)) + s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState.resp, nil)) + s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState.resp, expErr)) + ret, err := db.State() + assert.NotNil(t, err) + assert.Equal(t, expState.s, *ret) + r.AssertExpectations(t) +} + +func TestStateWithPrimaryAndTwoReplicaRedisFailureWhenIntConversionFails(t *testing.T) { + expErr := errors.New("Sentinel DBAAS_NODE_COUNT configuration value 'no-int' conversion to integer failed") + _, r, s, db := setupHaEnvWithSentinels(true, "no-int") + + redisPrimaryState := newMockRedisMasterCallResp("master", "10.20.30.30", "6379", "master") + redisReplicasState := newMockRedisSlavesCall() + redisReplicasState.add("slave", "10.20.30.40", "6379", "up", "slave") + redisReplicasState.add("slave", "10.20.30.50", "30000", "up", "slave") + redisSentinelsState := newMockRedisSentinelsCall() + redisSentinelsState.add("10.20.30.40", "26379", "sentinel") + redisSentinelsState.add("10.20.30.50", "30001", "sentinel") + + expState := newExpDbState(0, expErr) + expState.addPrimary("master", "10.20.30.30", "6379", "master", nil) + expState.addReplica("slave", "10.20.30.40", "6379", "up", "slave", nil) + expState.addReplica("slave", "10.20.30.50", "30000", "up", "slave", nil) + expState.addSentinel("10.20.30.40", "26379", "sentinel", nil) + expState.addSentinel("10.20.30.50", "30001", "sentinel", nil) + + s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisPrimaryState, nil)) + s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState.resp, nil)) + s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState.resp, nil)) + ret, err := db.State() + assert.Equal(t, expErr, err) + assert.Equal(t, expState.s, *ret) + r.AssertExpectations(t) +} + +func TestStateWithSinglePrimaryRedisSuccessfully(t *testing.T) { + _, r, db := setupSingleEnv(true, "1") + redisInfo := "# Replication\r\n" + + "role:master\r\n" + + "connected_slaves:0\r\n" + + "min_slaves_good_slaves:0\r\n" expState := &sdlgoredis.DbState{ + ConfigNodeCnt: 1, PrimaryDbState: sdlgoredis.PrimaryDbState{ Fields: sdlgoredis.PrimaryDbStateFields{ - Role: "master", - }, - }, - ReplicasDbState: &sdlgoredis.ReplicasDbState{ - States: []*sdlgoredis.ReplicaDbState{ - &sdlgoredis.ReplicaDbState{ - Fields: sdlgoredis.ReplicaDbStateFields{ - Role: "slave", - Ip: "10.20.30.40", - Port: "6379", - PrimaryLinkStatus: "up", - Flags: "slave", - }, - }, + Role: "master", + Flags: "master", }, }, - SentinelsDbState: &sdlgoredis.SentinelsDbState{ - Err: errors.New("Some error"), - States: []*sdlgoredis.SentinelDbState{}, - }, } - s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisPrimaryState, nil)) - s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisReplicasState, nil)) - s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState, errors.New("Some error"))) + r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, nil)) ret, err := db.State() - assert.NotNil(t, err) + assert.Nil(t, err) assert.Equal(t, expState, ret) r.AssertExpectations(t) } -func TestStateWithSinglePrimaryRedisSuccessfully(t *testing.T) { - _, r, db := setupSingleEnv(true) +func TestStateWithSinglePrimaryRedisFailureWhenIntConversionFails(t *testing.T) { + expErr := errors.New("DBAAS_NODE_COUNT configuration value 'no-int' conversion to integer failed") + _, r, db := setupSingleEnv(true, "no-int") redisInfo := "# Replication\r\n" + "role:master\r\n" + "connected_slaves:0\r\n" + "min_slaves_good_slaves:0\r\n" expState := &sdlgoredis.DbState{ + Err: expErr, + ConfigNodeCnt: 0, PrimaryDbState: sdlgoredis.PrimaryDbState{ Fields: sdlgoredis.PrimaryDbStateFields{ Role: "master", @@ -1388,15 +1435,20 @@ func TestStateWithSinglePrimaryRedisSuccessfully(t *testing.T) { r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, nil)) ret, err := db.State() - assert.Nil(t, err) + assert.Equal(t, expErr, err) assert.Equal(t, expState, ret) r.AssertExpectations(t) } func TestStateWithSinglePrimaryRedisFailureInInfoCall(t *testing.T) { - _, r, db := setupSingleEnv(true) + expErr := errors.New("Some error") + _, r, db := setupSingleEnv(true, "1") redisInfo := "" - expState := &sdlgoredis.DbState{} + expState := &sdlgoredis.DbState{ + PrimaryDbState: sdlgoredis.PrimaryDbState{ + Err: expErr, + }, + } r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, errors.New("Some error"))) ret, err := db.State() diff --git a/internal/sdlgoredis/sdlgosentinel.go b/internal/sdlgoredis/sdlgosentinel.go index f439570..19468d7 100644 --- a/internal/sdlgoredis/sdlgosentinel.go +++ b/internal/sdlgoredis/sdlgosentinel.go @@ -23,7 +23,9 @@ package sdlgoredis import ( + "fmt" "github.com/go-redis/redis/v7" + "strconv" ) type Sentinel struct { @@ -61,6 +63,14 @@ func (s *Sentinel) GetDbState() (*DbState, error) { state.PrimaryDbState = *pState state.ReplicasDbState = rState state.SentinelsDbState = sState + + cnt, err := strconv.Atoi(s.Cfg.nodeCnt) + if err != nil { + state.Err = fmt.Errorf("Sentinel DBAAS_NODE_COUNT configuration value '%s' conversion to integer failed", s.Cfg.nodeCnt) + return state, state.Err + } + state.ConfigNodeCnt = cnt + if pErr != nil { return state, pErr }