Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 35 additions & 27 deletions internal/cluster/is_master.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,51 @@ func GetIsMaster(session *mgo.Session) (*mdbstructs.IsMaster, error) {
return &isMaster, err
}

func isReplset(isMaster *mdbstructs.IsMaster) bool {
// Use the 'SetName' field to determine if a node is a member of replication.
// If 'SetName' is defined the node is a valid member.
//
// Use the 'SetName' field to determine if a node is a member of replication.
// If 'SetName' is defined the node is a valid member.
//
func IsReplset(isMaster *mdbstructs.IsMaster) bool {
return isMaster.SetName != ""
}

func isMongos(isMaster *mdbstructs.IsMaster) bool {
// Use a combination of the 'isMaster', 'SetName' and 'Msg'
// field to determine a node is a mongos. A mongos will always
// have 'isMaster' equal to true, no 'SetName' defined and 'Msg'
// set to 'isdbgrid'.
//
// https://github.com/mongodb/mongo/blob/v3.6/src/mongo/s/commands/cluster_is_master_cmd.cpp#L112-L113
//
return isMaster.IsMaster && !isReplset(isMaster) && isMaster.Msg == "isdbgrid"
// Use a combination of the 'isMaster', 'SetName' and 'Msg'
// field to determine a node is a mongos. A mongos will always
// have 'isMaster' equal to true, no 'SetName' defined and 'Msg'
// set to 'isdbgrid'.
//
// https://github.com/mongodb/mongo/blob/v3.6/src/mongo/s/commands/cluster_is_master_cmd.cpp#L112-L113
//
func IsMongos(isMaster *mdbstructs.IsMaster) bool {
return isMaster.IsMaster && !IsReplset(isMaster) && isMaster.Msg == "isdbgrid"
}

func isConfigServer(isMaster *mdbstructs.IsMaster) bool {
// Use the undocumented 'configsvr' field to determine a node
// is a config server. This node must have replication enabled.
// For unexplained reasons the value of 'configsvr' must be an int
// equal to '2'.
//
// https://github.com/mongodb/mongo/blob/v3.6/src/mongo/db/repl/replication_info.cpp#L355-L358
//
if isMaster.ConfigSvr == 2 && isReplset(isMaster) {
// Use the undocumented 'configsvr' field to determine a node
// is a config server. This node must have replication enabled.
// For unexplained reasons the value of 'configsvr' must be an int
// equal to '2'.
//
// https://github.com/mongodb/mongo/blob/v3.6/src/mongo/db/repl/replication_info.cpp#L355-L358
//
func IsConfigServer(isMaster *mdbstructs.IsMaster) bool {
if isMaster.ConfigSvr == 2 && IsReplset(isMaster) {
return true
}
return false
}

func isShardedCluster(isMaster *mdbstructs.IsMaster) bool {
// We are connected to a Sharded Cluster if the seed host
// is a valid mongos or config server.
//
if isConfigServer(isMaster) || isMongos(isMaster) {
// Use the existence of the '$configServerState' field to
// determine if a node is a mongod with the 'shardsvr'
// cluster role.
//
func IsShardsvr(isMaster *mdbstructs.IsMaster) bool {
return IsReplset(isMaster) && isMaster.ConfigServerState != nil
}

// We are connected to a Sharded Cluster if the seed host
// is a valid mongos or config server.
//
func IsShardedCluster(isMaster *mdbstructs.IsMaster) bool {
if IsConfigServer(isMaster) || IsMongos(isMaster) {
return true
}
return false
Expand Down
88 changes: 62 additions & 26 deletions internal/cluster/is_master_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package cluster

import (
"testing"
"time"

"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
"github.com/percona/mongodb-backup/internal/testutils"
"github.com/percona/mongodb-backup/mdbstructs"
)
Expand All @@ -25,11 +27,11 @@ func TestGetIsMaster(t *testing.T) {
}

func TestIsReplset(t *testing.T) {
if !isReplset(&mdbstructs.IsMaster{SetName: "test"}) {
t.Fatal("Expected true from .isReplset()")
if !IsReplset(&mdbstructs.IsMaster{SetName: "test"}) {
t.Fatal("Expected true from .IsReplset()")
}
if isReplset(&mdbstructs.IsMaster{SetName: ""}) {
t.Fatal("Expected false from .isReplset()")
if IsReplset(&mdbstructs.IsMaster{SetName: ""}) {
t.Fatal("Expected false from .IsReplset()")
}

session, err := mgo.DialWithInfo(testutils.PrimaryDialInfo())
Expand All @@ -43,21 +45,21 @@ func TestIsReplset(t *testing.T) {
session.Close()
t.Fatalf("Could not run 'isMaster' command: %v", err.Error())
}
if !isReplset(isMaster) {
if !IsReplset(isMaster) {
session.Close()
t.Fatalf("Expected true from .isReplset()")
t.Fatalf("Expected true from .IsReplset()")
}
}

func TestIsMongos(t *testing.T) {
if !isMongos(&mdbstructs.IsMaster{
if !IsMongos(&mdbstructs.IsMaster{
IsMaster: true,
Msg: "isdbgrid",
}) {
t.Fatal("Expected true from .isMongos()")
t.Fatal("Expected true from .IsMongos()")
}
if isMongos(&mdbstructs.IsMaster{IsMaster: true}) {
t.Fatal("Expected false from .isMongos()")
if IsMongos(&mdbstructs.IsMaster{IsMaster: true}) {
t.Fatal("Expected false from .IsMongos()")
}

session, err := mgo.DialWithInfo(testutils.MongosDialInfo())
Expand All @@ -71,21 +73,21 @@ func TestIsMongos(t *testing.T) {
session.Close()
t.Fatalf("Could not run 'isMaster' command: %v", err.Error())
}
if !isMongos(isMaster) {
if !IsMongos(isMaster) {
session.Close()
t.Fatalf("Expected true from .isMongos()")
t.Fatalf("Expected true from .IsMongos()")
}
}

func TestIsConfigServer(t *testing.T) {
if !isConfigServer(&mdbstructs.IsMaster{
if !IsConfigServer(&mdbstructs.IsMaster{
ConfigSvr: 2,
SetName: "csReplSet",
}) {
t.Fatal("Expected false from .isConfigServer()")
t.Fatal("Expected false from .IsConfigServer()")
}
if isConfigServer(&mdbstructs.IsMaster{SetName: "csReplSet"}) {
t.Fatal("Expected true from .isConfigServer()")
if IsConfigServer(&mdbstructs.IsMaster{SetName: "csReplSet"}) {
t.Fatal("Expected true from .IsConfigServer()")
}

session, err := mgo.DialWithInfo(testutils.ConfigsvrReplsetDialInfo())
Expand All @@ -99,30 +101,30 @@ func TestIsConfigServer(t *testing.T) {
session.Close()
t.Fatalf("Could not run 'isMaster' command: %v", err.Error())
}
if !isConfigServer(isMaster) {
if !IsConfigServer(isMaster) {
session.Close()
t.Fatalf("Expected true from .isConfigServer()")
t.Fatalf("Expected true from .IsConfigServer()")
}
}

func TestIsShardedCluster(t *testing.T) {
if !isShardedCluster(&mdbstructs.IsMaster{
if !IsShardedCluster(&mdbstructs.IsMaster{
ConfigSvr: 2,
SetName: "csReplSet",
}) {
t.Fatal("Expected true from isShardedCluster()")
t.Fatal("Expected true from IsShardedCluster()")
}
if !isShardedCluster(&mdbstructs.IsMaster{
if !IsShardedCluster(&mdbstructs.IsMaster{
IsMaster: true,
Msg: "isdbgrid",
}) {
t.Fatal("Expected true from isShardedCluster()")
t.Fatal("Expected true from IsShardedCluster()")
}
if isShardedCluster(&mdbstructs.IsMaster{
if IsShardedCluster(&mdbstructs.IsMaster{
IsMaster: true,
SetName: "test",
}) {
t.Fatal("Expected true false isShardedCluster()")
t.Fatal("Expected true false IsShardedCluster()")
}

session, err := mgo.DialWithInfo(testutils.ConfigsvrReplsetDialInfo())
Expand All @@ -136,8 +138,42 @@ func TestIsShardedCluster(t *testing.T) {
session.Close()
t.Fatalf("Could not run 'isMaster' command: %v", err.Error())
}
if !isShardedCluster(isMaster) {
if !IsShardedCluster(isMaster) {
session.Close()
t.Fatalf("Expected true from .isConfigServer()")
t.Fatalf("Expected true from .IsConfigServer()")
}
}

func TestIsShardsvr(t *testing.T) {
if IsShardsvr(&mdbstructs.IsMaster{
IsMaster: true,
SetName: "test",
}) {
t.Fatal(".IsShardsvr() should be false")
}
ts, _ := bson.NewMongoTimestamp(time.Now(), 0)
if IsShardsvr(&mdbstructs.IsMaster{
IsMaster: true,
Msg: "dbgrid",
ConfigServerState: &mdbstructs.ConfigServerState{
OpTime: &mdbstructs.OpTime{
Ts: ts,
Term: int64(1),
},
},
}) {
t.Fatal(".IsShardsvr() should be false")
}
if !IsShardsvr(&mdbstructs.IsMaster{
IsMaster: true,
SetName: "test",
ConfigServerState: &mdbstructs.ConfigServerState{
OpTime: &mdbstructs.OpTime{
Ts: ts,
Term: int64(1),
},
},
}) {
t.Fatal(".IsShardsvr() should be true")
}
}
4 changes: 0 additions & 4 deletions mdbstructs/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ type ClusterTime struct {
} `bson:"signature"`
}

type ConfigServerState struct {
OpTime OpTime `bson:"opTime"`
}

type GleStats struct {
LastOpTime bson.MongoTimestamp `bson:"lastOpTime"`
ElectionId bson.ObjectId `bson:"electionId"`
Expand Down
49 changes: 25 additions & 24 deletions mdbstructs/is_master.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,31 @@ import (

// IsMaster represents the document returned by db.runCommand( { isMaster: 1 } )
type IsMaster struct {
Hosts []string `bson:"hosts"`
IsMaster bool `bson:"ismaster"`
Msg string `bson:"msg"`
MaxBsonObjectSise int64 `bson:"maxBsonObjectSize"`
MaxMessageSizeBytes int64 `bson:"maxMessageSizeBytes"`
MaxWriteBatchSize int64 `bson:"maxWriteBatchSize"`
LocalTime time.Time `bson:"localTime"`
LogicalSessionTimeoutMinutes int64 `bson:"logicalSessionTimeoutMinutes"`
MaxWireVersion int64 `bson:"maxWireVersion"`
MinWireVersion int64 `bson:"minWireVersion"`
Ok int `bson:"ok"`
ClusterTime ClusterTime `bson:"$clusterTime"`
OperationTime bson.MongoTimestamp `bson:"operationTime"`
SetName string `bson:"setName"`
SetVersion string `bson:"setVersion"`
Primary string `bson:"primary"`
Secondary bool `bson:"secondary"`
ConfigSvr int `bson:"configsvr"`
Me string `bson:"me"`
Hosts []string `bson:"hosts"`
IsMaster bool `bson:"ismaster"`
Msg string `bson:"msg"`
MaxBsonObjectSise int64 `bson:"maxBsonObjectSize"`
MaxMessageSizeBytes int64 `bson:"maxMessageSizeBytes"`
MaxWriteBatchSize int64 `bson:"maxWriteBatchSize"`
LocalTime time.Time `bson:"localTime"`
LogicalSessionTimeoutMinutes int64 `bson:"logicalSessionTimeoutMinutes"`
MaxWireVersion int64 `bson:"maxWireVersion"`
MinWireVersion int64 `bson:"minWireVersion"`
Ok int `bson:"ok"`
SetName string `bson:"setName,omitempty"`
SetVersion string `bson:"setVersion"`
Primary string `bson:"primary"`
Secondary bool `bson:"secondary"`
ConfigSvr int `bson:"configsvr,omitempty"`
Me string `bson:"me"`
LastWrite struct {
OpTime OpTime `bson:"opTime"`
LastWriteDate time.Time `bdon:"lastWriteDate"`
MajorityOpTime OpTime `bson:"majorityTime"`
OpTime *OpTime `bson:"opTime"`
LastWriteDate time.Time `bson:"lastWriteDate"`
MajorityOpTime *OpTime `bson:"majorityTime"`
MajoriyWriteDate time.Time `bson:"majorityWriteDate"`
} `bson:"lastWrite"`
GleStats GleStats `bson:"$gleStats"`
ConfigServerState ConfigServerState `bson:"$configServerState"`
ClusterTime *ClusterTime `bson:"$clusterTime,omitempty"`
ConfigServerState *ConfigServerState `bson:"$configServerState,omitempty"`
GleStats *GleStats `bson:"$gleStats,omitempty"`
OperationTime *bson.MongoTimestamp `bson:"operationTime,omitempty"`
}
14 changes: 7 additions & 7 deletions mdbstructs/replset_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ type ReplsetConfig struct {

// Response document from 'replSetGetConfig': https://docs.mongodb.com/manual/reference/command/replSetGetConfig/#dbcmd.replSetGetConfig
type ReplSetGetConfig struct {
Config *ReplsetConfig `bson:"config" json:"config"`
Errmsg string `bson:"errmsg,omitempty" json:"errmsg,omitempty"`
Ok int `bson:"ok" json:"ok" json:"ok"`
ClusterTime ClusterTime `bson:"$clusterTime", json:"$clusterTime"`
ConfigServerState ConfigServerState `bson:"$configServerState", json:"$configServerState"`
GleStats GleStats `bson:"$gleStats", json:"$gleStats"`
OperationTime bson.MongoTimestamp `bson:"operationTime", json:"operationTime"`
Config *ReplsetConfig `bson:"config" json:"config"`
Errmsg string `bson:"errmsg,omitempty" json:"errmsg,omitempty"`
Ok int `bson:"ok" json:"ok" json:"ok"`
ClusterTime *ClusterTime `bson:"$clusterTime,omitempty", json:"$clusterTime,omitempty"`
ConfigServerState *ConfigServerState `bson:"$configServerState,omitempty", json:"$configServerState,omitempty"`
GleStats *GleStats `bson:"$gleStats,omitempty", json:"$gleStats,omitempty"`
OperationTime *bson.MongoTimestamp `bson:"operationTime,omitempty", json:"operationTime,omitempty"`
}
8 changes: 4 additions & 4 deletions mdbstructs/replset_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ type ReplsetStatus struct {
Optimes *StatusOpTimes `bson:"optimes,omitempty" json:"optimes,omitempty"`
Errmsg string `bson:"errmsg,omitempty" json:"errmsg,omitempty"`
Ok int `bson:"ok" json:"ok"`
ClusterTime ClusterTime `bson:"$clusterTime", json:"$clusterTime"`
ConfigServerState ConfigServerState `bson:"$configServerState", json:"$configServerState"`
GleStats GleStats `bson:"$gleStats", json:"$gleStats"`
OperationTime bson.MongoTimestamp `bson:"operationTime", json:"operationTime"`
ClusterTime *ClusterTime `bson:"$clusterTime,omitempty", json:"$clusterTime,omitempty"`
ConfigServerState *ConfigServerState `bson:"$configServerState,omitempty", json:"$configServerState,omitempty"`
GleStats *GleStats `bson:"$gleStats,omitempty", json:"$gleStats,omitempty"`
OperationTime *bson.MongoTimestamp `bson:"operationTime,omitempty", json:"operationTime,omitempty"`
}
12 changes: 8 additions & 4 deletions mdbstructs/sharding.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ type Shard struct {
// https://docs.mongodb.com/manual/reference/command/listShards/
//
type ListShards struct {
Shards []*Shard `bson:"shards"`
Ok int `bson:"ok"`
OperationTime bson.MongoTimestamp `bson:"operationTime"`
ClusterTime ClusterTime `bson:"$clusterTime"`
Shards []*Shard `bson:"shards"`
Ok int `bson:"ok"`
OperationTime *bson.MongoTimestamp `bson:"operationTime"`
ClusterTime *ClusterTime `bson:"$clusterTime"`
}

// ShardIdentity reflects the "shardIdentity" document of "system.version"
Expand All @@ -32,3 +32,7 @@ type ShardIdentity struct {
ShardName string `bson:"shardName"`
ConfigsvrConnectionString string `bson:"configsvrConnectionString"`
}

type ConfigServerState struct {
OpTime *OpTime `bson:"opTime"`
}