X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=internal%2Fsdlgoredis%2Fsdlgoredis_test.go;h=d8b8328a2738d5cbf89cff68f7676d0ea24056d4;hb=0e96b4aef53285c0a61b8ce7d1769c04df7b6061;hp=740674da1dd938593c306fb2b8313341613a29b6;hpb=7c256b622c8fd065e91a7e289937d6e692a7eb1d;p=ric-plt%2Fsdlgo.git diff --git a/internal/sdlgoredis/sdlgoredis_test.go b/internal/sdlgoredis/sdlgoredis_test.go index 740674d..d8b8328 100644 --- a/internal/sdlgoredis/sdlgoredis_test.go +++ b/internal/sdlgoredis/sdlgoredis_test.go @@ -23,9 +23,10 @@ package sdlgoredis_test import ( + "context" "errors" "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis" - "github.com/go-redis/redis/v7" + "github.com/go-redis/redis/v8" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "strconv" @@ -45,15 +46,15 @@ type MockOS struct { mock.Mock } -func (m *pubSubMock) Channel() <-chan *redis.Message { +func (m *pubSubMock) Channel(opts ...redis.ChannelOption) <-chan *redis.Message { return m.Called().Get(0).(chan *redis.Message) } -func (m *pubSubMock) Subscribe(channels ...string) error { +func (m *pubSubMock) Subscribe(ctx context.Context, channels ...string) error { return m.Called().Error(0) } -func (m *pubSubMock) Unsubscribe(channels ...string) error { +func (m *pubSubMock) Unsubscribe(ctx context.Context, channels ...string) error { return m.Called().Error(0) } @@ -61,7 +62,7 @@ func (m *pubSubMock) Close() error { return m.Called().Error(0) } -func (m *clientMock) Command() *redis.CommandsInfoCmd { +func (m *clientMock) Command(ctx context.Context) *redis.CommandsInfoCmd { return m.Called().Get(0).(*redis.CommandsInfoCmd) } @@ -69,75 +70,75 @@ func (m *clientMock) Close() error { return m.Called().Error(0) } -func (m *clientMock) Subscribe(channels ...string) *redis.PubSub { +func (m *clientMock) Subscribe(ctx context.Context, channels ...string) *redis.PubSub { return m.Called(channels).Get(0).(*redis.PubSub) } -func (m *clientMock) MSet(pairs ...interface{}) *redis.StatusCmd { +func (m *clientMock) MSet(ctx context.Context, pairs ...interface{}) *redis.StatusCmd { return m.Called(pairs).Get(0).(*redis.StatusCmd) } -func (m *clientMock) Do(args ...interface{}) *redis.Cmd { +func (m *clientMock) Do(ctx context.Context, args ...interface{}) *redis.Cmd { return m.Called(args).Get(0).(*redis.Cmd) } -func (m *clientMock) MGet(keys ...string) *redis.SliceCmd { +func (m *clientMock) MGet(ctx context.Context, keys ...string) *redis.SliceCmd { return m.Called(keys).Get(0).(*redis.SliceCmd) } -func (m *clientMock) Del(keys ...string) *redis.IntCmd { +func (m *clientMock) Del(ctx context.Context, keys ...string) *redis.IntCmd { return m.Called(keys).Get(0).(*redis.IntCmd) } -func (m *clientMock) Keys(pattern string) *redis.StringSliceCmd { +func (m *clientMock) Keys(ctx context.Context, pattern string) *redis.StringSliceCmd { return m.Called(pattern).Get(0).(*redis.StringSliceCmd) } -func (m *clientMock) SetNX(key string, value interface{}, expiration time.Duration) *redis.BoolCmd { +func (m *clientMock) SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.BoolCmd { return m.Called(key, value, expiration).Get(0).(*redis.BoolCmd) } -func (m *clientMock) SAdd(key string, members ...interface{}) *redis.IntCmd { +func (m *clientMock) SAdd(ctx context.Context, key string, members ...interface{}) *redis.IntCmd { return m.Called(key, members).Get(0).(*redis.IntCmd) } -func (m *clientMock) SRem(key string, members ...interface{}) *redis.IntCmd { +func (m *clientMock) SRem(ctx context.Context, key string, members ...interface{}) *redis.IntCmd { return m.Called(key, members).Get(0).(*redis.IntCmd) } -func (m *clientMock) SMembers(key string) *redis.StringSliceCmd { +func (m *clientMock) SMembers(ctx context.Context, key string) *redis.StringSliceCmd { return m.Called(key).Get(0).(*redis.StringSliceCmd) } -func (m *clientMock) SIsMember(key string, member interface{}) *redis.BoolCmd { +func (m *clientMock) SIsMember(ctx context.Context, key string, member interface{}) *redis.BoolCmd { return m.Called(key, member).Get(0).(*redis.BoolCmd) } -func (m *clientMock) SCard(key string) *redis.IntCmd { +func (m *clientMock) SCard(ctx context.Context, key string) *redis.IntCmd { return m.Called(key).Get(0).(*redis.IntCmd) } -func (m *clientMock) PTTL(key string) *redis.DurationCmd { +func (m *clientMock) PTTL(ctx context.Context, key string) *redis.DurationCmd { return m.Called(key).Get(0).(*redis.DurationCmd) } -func (m *clientMock) Eval(script string, keys []string, args ...interface{}) *redis.Cmd { +func (m *clientMock) Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd { return m.Called(script, keys).Get(0).(*redis.Cmd) } -func (m *clientMock) EvalSha(sha1 string, keys []string, args ...interface{}) *redis.Cmd { +func (m *clientMock) EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *redis.Cmd { return m.Called(sha1, keys, args).Get(0).(*redis.Cmd) } -func (m *clientMock) ScriptExists(scripts ...string) *redis.BoolSliceCmd { +func (m *clientMock) ScriptExists(ctx context.Context, scripts ...string) *redis.BoolSliceCmd { return m.Called(scripts).Get(0).(*redis.BoolSliceCmd) } -func (m *clientMock) ScriptLoad(script string) *redis.StringCmd { +func (m *clientMock) ScriptLoad(ctx context.Context, script string) *redis.StringCmd { return m.Called(script).Get(0).(*redis.StringCmd) } -func (m *clientMock) Info(section ...string) *redis.StringCmd { +func (m *clientMock) Info(ctx context.Context, section ...string) *redis.StringCmd { return m.Called(section).Get(0).(*redis.StringCmd) } @@ -145,24 +146,24 @@ type MockRedisSentinel struct { mock.Mock } -func (m *MockRedisSentinel) Master(name string) *redis.StringStringMapCmd { +func (m *MockRedisSentinel) Master(ctx context.Context, name string) *redis.StringStringMapCmd { a := m.Called(name) return a.Get(0).(*redis.StringStringMapCmd) } -func (m *MockRedisSentinel) Slaves(name string) *redis.SliceCmd { +func (m *MockRedisSentinel) Slaves(ctx context.Context, name string) *redis.SliceCmd { a := m.Called(name) return a.Get(0).(*redis.SliceCmd) } -func (m *MockRedisSentinel) Sentinels(name string) *redis.SliceCmd { +func (m *MockRedisSentinel) Sentinels(ctx context.Context, name string) *redis.SliceCmd { a := m.Called(name) return a.Get(0).(*redis.SliceCmd) } func setSubscribeNotifications() (*pubSubMock, sdlgoredis.SubscribeFn) { mock := new(pubSubMock) - return mock, func(client sdlgoredis.RedisClient, channels ...string) sdlgoredis.Subscriber { + return mock, func(ctx context.Context, client sdlgoredis.RedisClient, channels ...string) sdlgoredis.Subscriber { return mock } } @@ -180,11 +181,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 +193,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 +236,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 +263,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) @@ -1032,7 +1143,7 @@ func TestClientTwoSentinelRedisEnvs(t *testing.T) { setupVals.rClient[1].AssertExpectations(t) } -func TestInfoOfMasterRedisWithTwoSlavesSuccessfully(t *testing.T) { +func TestInfoOfPrimaryRedisWithTwoReplicasSuccessfully(t *testing.T) { _, r, db := setupHaEnv(true) redisInfo := "# Replication\r\n" + "role:master\r\n" + @@ -1042,7 +1153,7 @@ func TestInfoOfMasterRedisWithTwoSlavesSuccessfully(t *testing.T) { "slave1:ip=5.6.7.8,port=6379,state=online,offset=100200300,lag=0\r\n" expInfo := &sdlgoredis.DbInfo{ Fields: sdlgoredis.DbInfoFields{ - MasterRole: true, + PrimaryRole: true, ConnectedReplicaCnt: 2, }, } @@ -1054,7 +1165,7 @@ func TestInfoOfMasterRedisWithTwoSlavesSuccessfully(t *testing.T) { r.AssertExpectations(t) } -func TestInfoOfMasterRedisWithOneSlaveOnlineAndOtherSlaveNotOnlineSuccessfully(t *testing.T) { +func TestInfoOfPrimaryRedisWithOneReplicaOnlineAndOtherReplicaNotOnlineSuccessfully(t *testing.T) { _, r, db := setupHaEnv(true) redisInfo := "# Replication\r\n" + "role:master\r\n" + @@ -1064,7 +1175,7 @@ func TestInfoOfMasterRedisWithOneSlaveOnlineAndOtherSlaveNotOnlineSuccessfully(t "slave1:ip=5.6.7.8,port=6379,state=wait_bgsave,offset=100200300,lag=0\r\n" expInfo := &sdlgoredis.DbInfo{ Fields: sdlgoredis.DbInfoFields{ - MasterRole: true, + PrimaryRole: true, ConnectedReplicaCnt: 1, }, } @@ -1076,7 +1187,7 @@ func TestInfoOfMasterRedisWithOneSlaveOnlineAndOtherSlaveNotOnlineSuccessfully(t r.AssertExpectations(t) } -func TestInfoOfStandaloneMasterRedisSuccessfully(t *testing.T) { +func TestInfoOfStandalonePrimaryRedisSuccessfully(t *testing.T) { _, r, db := setupHaEnv(true) redisInfo := "# Replication\r\n" + "role:master\r\n" + @@ -1084,7 +1195,27 @@ func TestInfoOfStandaloneMasterRedisSuccessfully(t *testing.T) { "min_slaves_good_slaves:0\r\n" expInfo := &sdlgoredis.DbInfo{ Fields: sdlgoredis.DbInfoFields{ - MasterRole: true, + PrimaryRole: true, + ConnectedReplicaCnt: 0, + }, + } + + r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, nil)) + info, err := db.Info() + assert.Nil(t, err) + assert.Equal(t, expInfo, info) + r.AssertExpectations(t) +} + +func TestInfoOfStandalonePrimaryRedisFailureWhenIntConversionFails(t *testing.T) { + _, 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, }, } @@ -1113,7 +1244,7 @@ func TestInfoWithEmptyContentSuccessfully(t *testing.T) { var redisInfo string expInfo := &sdlgoredis.DbInfo{ Fields: sdlgoredis.DbInfoFields{ - MasterRole: false, + PrimaryRole: false, }, } @@ -1124,263 +1255,276 @@ func TestInfoWithEmptyContentSuccessfully(t *testing.T) { r.AssertExpectations(t) } -func TestStateWithMasterAndTwoSlaveRedisSuccessfully(t *testing.T) { - _, r, s, db := setupHaEnvWithSentinels(true) - redisMasterState := map[string]string{ - "role-reported": "master", - } - redisSlavesState := make([]interface{}, 2) - redisSlavesState[0] = []interface{}{ - "role-reported", "slave", - "ip", "10.20.30.40", - "port", "6379", - "flags", "slave", - "master-link-status", "up", - } - redisSlavesState[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{ - MasterDbState: sdlgoredis.MasterDbState{ - Fields: sdlgoredis.MasterDbStateFields{ - Role: "master", +func TestInfoWithSomeStatisticsOfStandalonePrimaryRedis(t *testing.T) { + _, r, db := setupHaEnv(true) + redisInfo := "# Replication\r\n" + + "role:master\r\n" + + "connected_slaves:0\r\n" + + "min_slaves_good_slaves:0\r\n" + + "# Server\r\n" + + "uptime_in_days:23\r\n" + + "# Clients\r\n" + + "connected_clients:2\r\n" + + "# Memory\r\n" + + "used_memory:2093808\r\n" + + "used_memory_human:2.00M\r\n" + + "mem_fragmentation_ratio:6.44\r\n" + + "# Stats\r\n" + + "total_connections_received:278\r\n" + + "# CPU\r\n" + + "used_cpu_sys:1775.254919\r\n" + + "# Commandstats\r\n" + + "cmdstat_role:calls=1,usec=3,usec_per_call=3.00\r\n" + + "# Keyspace\r\n" + + "db0:keys=4,expires=0,avg_ttl=0" + expInfo := &sdlgoredis.DbInfo{ + Fields: sdlgoredis.DbInfoFields{ + PrimaryRole: true, + ConnectedReplicaCnt: 0, + Server: sdlgoredis.ServerInfoFields{ + UptimeInDays: 23, }, - }, - ReplicasDbState: &sdlgoredis.ReplicasDbState{ - States: []*sdlgoredis.ReplicaDbState{ - &sdlgoredis.ReplicaDbState{ - Fields: sdlgoredis.ReplicaDbStateFields{ - Role: "slave", - Ip: "10.20.30.40", - Port: "6379", - MasterLinkStatus: "up", - Flags: "slave", - }, - }, - &sdlgoredis.ReplicaDbState{ - Fields: sdlgoredis.ReplicaDbStateFields{ - Role: "slave", - Ip: "10.20.30.50", - Port: "30000", - MasterLinkStatus: "up", - Flags: "slave", - }, - }, + Clients: sdlgoredis.ClientsInfoFields{ + ConnectedClients: 2, }, - }, - SentinelsDbState: &sdlgoredis.SentinelsDbState{ - States: []*sdlgoredis.SentinelDbState{ - &sdlgoredis.SentinelDbState{ - Fields: sdlgoredis.SentinelDbStateFields{ - Ip: "10.20.30.40", - Port: "26379", - Flags: "sentinel", - }, + Memory: sdlgoredis.MeroryInfoFields{ + UsedMemory: 2093808, + UsedMemoryHuman: "2.00M", + MemFragmentationRatio: 6.44, + }, + Stats: sdlgoredis.StatsInfoFields{ + TotalConnectionsReceived: 278, + }, + Cpu: sdlgoredis.CpuInfoFields{ + UsedCpuSys: 1775.254919, + }, + Commandstats: sdlgoredis.CommandstatsInfoFields{ + CmdstatRole: sdlgoredis.CommandstatsValues{ + Calls: 1, + Usec: 3, + UsecPerCall: 3.00, }, - &sdlgoredis.SentinelDbState{ - Fields: sdlgoredis.SentinelDbStateFields{ - Ip: "10.20.30.50", - Port: "30001", - Flags: "sentinel", - }, + }, + Keyspace: sdlgoredis.KeyspaceInfoFields{ + Db: sdlgoredis.KeyspaceValues{ + Keys: 4, }, }, }, } - s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisMasterState, nil)) - s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisSlavesState, nil)) - s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState, nil)) + r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, nil)) + info, err := db.Info() + assert.Nil(t, err) + assert.Equal(t, expInfo, info) + r.AssertExpectations(t) +} + +func TestStateWithPrimaryAndTwoReplicaRedisSuccessfully(t *testing.T) { + _, 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.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 TestStateWithMasterAndOneSlaveRedisFailureInMasterRedisCall(t *testing.T) { - _, r, s, db := setupHaEnvWithSentinels(true) - redisMasterState := map[string]string{} - redisSlavesState := make([]interface{}, 1) - redisSlavesState[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{ - MasterDbState: sdlgoredis.MasterDbState{ - 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", - MasterLinkStatus: "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(redisMasterState, errors.New("Some error"))) - s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisSlavesState, 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 TestStateWithMasterAndOneSlaveRedisFailureInSlavesRedisCall(t *testing.T) { - _, r, s, db := setupHaEnvWithSentinels(true) - redisMasterState := map[string]string{ - "role-reported": "master", - } - redisSlavesState := make([]interface{}, 1) - redisSlavesState[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{ - MasterDbState: sdlgoredis.MasterDbState{ - Fields: sdlgoredis.MasterDbStateFields{ - 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") - s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisMasterState, nil)) - s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisSlavesState, errors.New("Some error"))) - s[0].On("Sentinels", "dbaasmaster").Return(redis.NewSliceResult(redisSentinelsState, nil)) + 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.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 TestStateWithMasterAndOneSlaveRedisFailureInSentinelsRedisCall(t *testing.T) { - _, r, s, db := setupHaEnvWithSentinels(true) - redisMasterState := map[string]string{ - "role-reported": "master", - } - redisSlavesState := make([]interface{}, 1) - redisSlavesState[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 TestStateWithPrimaryAndOneReplicaRedisFailureInSentinelsRedisCall(t *testing.T) { + 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) +} + +// Test case to test ignoring of a sentinel entry with zero port. Implementation has been +// done because we miss the fix for the Redis Bug #9240. +func TestStateWithPrimaryAndTwoReplicaFirstSentinelStateIgnoredBecauseZeroPortBugRedisSuccessfully(t *testing.T) { + _, 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", "0", "s_down,sentinel,disconnected") + redisSentinelsState.add("10.20.30.50", "26379", "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.50", "26379", "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.Nil(t, 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{ - MasterDbState: sdlgoredis.MasterDbState{ - Fields: sdlgoredis.MasterDbStateFields{ - Role: "master", - }, - }, - ReplicasDbState: &sdlgoredis.ReplicasDbState{ - States: []*sdlgoredis.ReplicaDbState{ - &sdlgoredis.ReplicaDbState{ - Fields: sdlgoredis.ReplicaDbStateFields{ - Role: "slave", - Ip: "10.20.30.40", - Port: "6379", - MasterLinkStatus: "up", - Flags: "slave", - }, - }, + ConfigNodeCnt: 1, + PrimaryDbState: sdlgoredis.PrimaryDbState{ + Fields: sdlgoredis.PrimaryDbStateFields{ + Role: "master", + Ip: "service-ricplt-dbaas-tcp-cluster-0.ricplt", + Port: "6376", + Flags: "master", }, }, - SentinelsDbState: &sdlgoredis.SentinelsDbState{ - Err: errors.New("Some error"), - States: []*sdlgoredis.SentinelDbState{}, - }, } - s[0].On("Master", "dbaasmaster").Return(redis.NewStringStringMapResult(redisMasterState, nil)) - s[0].On("Slaves", "dbaasmaster").Return(redis.NewSliceResult(redisSlavesState, 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 TestStateWithSingleMasterRedisSuccessfully(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{ - MasterDbState: sdlgoredis.MasterDbState{ - Fields: sdlgoredis.MasterDbStateFields{ + Err: expErr, + ConfigNodeCnt: 0, + PrimaryDbState: sdlgoredis.PrimaryDbState{ + Fields: sdlgoredis.PrimaryDbStateFields{ Role: "master", + Ip: "service-ricplt-dbaas-tcp-cluster-0.ricplt", + Port: "6376", Flags: "master", }, }, @@ -1388,15 +1532,20 @@ func TestStateWithSingleMasterRedisSuccessfully(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 TestStateWithSingleMasterRedisFailureInInfoCall(t *testing.T) { - _, r, db := setupSingleEnv(true) +func TestStateWithSinglePrimaryRedisFailureInInfoCall(t *testing.T) { + 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() @@ -1404,3 +1553,36 @@ func TestStateWithSingleMasterRedisFailureInInfoCall(t *testing.T) { assert.Equal(t, expState, ret) r.AssertExpectations(t) } + +func TestStatisticsWithSinglePrimaryRedisSuccessfully(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" + + "# Server\r\n" + + "uptime_in_days:12\r\n" + + expStatistics := &sdlgoredis.DbStatistics{ + Stats: []*sdlgoredis.DbStatisticsInfo{ + { + IPAddr: "service-ricplt-dbaas-tcp-cluster-0.ricplt", + Port: "6376", + Info: &sdlgoredis.DbInfo{ + Fields: sdlgoredis.DbInfoFields{ + PrimaryRole: true, + Server: sdlgoredis.ServerInfoFields{ + UptimeInDays: 12, + }, + }, + }, + }, + }, + } + + r.On("Info", []string{"all"}).Return(redis.NewStringResult(redisInfo, nil)) + ret, err := db.Statistics() + assert.Nil(t, err) + assert.Equal(t, expStatistics, ret) + r.AssertExpectations(t) +}