0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-27 23:27:11 +01:00
mongodb/jstests/libs/discover_topology.js
Erin McNulty 8e04853009 SERVER-94626 Enable sharding core passthroughs with gRPC (#26928)
GitOrigin-RevId: 0b3e76ce08cb028be1c5551f697bea4bfb42e056
2024-09-27 21:12:02 +00:00

162 lines
6.0 KiB
JavaScript

// The tojson() function that is commonly used to build up assertion messages doesn't support the
// Symbol type, so we just use unique string values instead.
export var Topology = {
kStandalone: 'stand-alone',
kRouter: 'mongos router',
kReplicaSet: 'replica set',
kShardedCluster: 'sharded cluster',
};
export var DiscoverTopology = (function() {
const kDefaultConnectFn = (host) => new Mongo(host, undefined, {gRPC: false});
// Nodes discovered via isMaster have a MongoRPC port and must not use gRPC.
const kDisableGRPCConnString = (hostConn) =>
jsTestOptions().shellGRPC ? `mongodb://${hostConn}/?grpc=false` : hostConn;
function getDataMemberConnectionStrings(conn) {
const res = assert.commandWorked(conn.adminCommand({isMaster: 1}));
if (!res.hasOwnProperty('setName')) {
// 'conn' represents a connection to a stand-alone mongod.
return {type: Topology.kStandalone, mongod: conn.host};
}
// The "passives" field contains the list of unelectable (priority=0) secondaries
// and is omitted from the server's response when there are none.
res.passives = res.passives || [];
return {
type: Topology.kReplicaSet,
configsvr: res.configsvr == 2,
primary: kDisableGRPCConnString(res.primary),
nodes: [...res.hosts, ...res.passives].map(host => kDisableGRPCConnString(host))
};
}
function findConnectedNodesViaMongos(conn, options) {
function getConfigServerConnectionString() {
const shardMap = conn.adminCommand({getShardMap: 1});
if (!shardMap.hasOwnProperty('map')) {
throw new Error(
'Expected "getShardMap" command to return an object with a "map" field: ' +
tojson(shardMap));
}
if (!shardMap.map.hasOwnProperty('config')) {
throw new Error(
'Expected "getShardMap" command to return an object with a "map.config"' +
' field: ' + tojson(shardMap));
}
return shardMap.map.config;
}
const connectFn =
options.hasOwnProperty('connectFn') ? options.connectFn : kDefaultConnectFn;
const configsvrConn = connectFn(getConfigServerConnectionString());
const configsvrHosts = getDataMemberConnectionStrings(configsvrConn);
configsvrConn.close();
const shards = assert.commandWorked(conn.adminCommand({listShards: 1})).shards;
const shardHosts = {};
for (let shardInfo of shards) {
const shardConn = connectFn(shardInfo.host);
shardHosts[shardInfo._id] = getDataMemberConnectionStrings(shardConn);
shardConn.close();
}
// Discover mongos URIs from the connection string. If a mongos is not passed in explicitly,
// it will not be discovered.
const mongosUris = new MongoURI("mongodb://" + conn.host);
const mongos = {
type: Topology.kRouter,
nodes: mongosUris.servers.map(uriObj => uriObj.server),
};
return {
type: Topology.kShardedCluster,
configsvr: configsvrHosts,
shards: shardHosts,
mongos: mongos,
};
}
/**
* Returns an object describing the topology of the mongod processes reachable from 'conn'.
* The "connectFn" property can be optionally specified to support custom retry logic when
* making connection attempts without overriding the Mongo constructor itself.
*
* For a stand-alone mongod, an object of the form
* {type: Topology.kStandalone, mongod: <conn-string>}
* is returned.
*
* For a replica set, an object of the form
* {
* type: Topology.kReplicaSet,
* primary: <primary-conn-string>,
* nodes: [<conn-string1>, <conn-string2>, ...],
* }
* is returned.
*
* For a sharded cluster, an object of the form
* {
* type: Topology.kShardedCluster,
* configsvr: {nodes: [...]},
* shards: {
* <shard-name1>: {type: Topology.kStandalone, mongod: ...},
* <shard-name2>: {type: Topology.kReplicaSet,
* primary: <primary-conn-string>,
* nodes: [...]},
* ...
* },
* mongos: {
* type: Topology.kRouter,
* nodes: [...],
* }
* }
* is returned, where the description for each shard depends on whether it is a stand-alone
* shard or a replica set shard.
*/
function findConnectedNodes(conn, options = {connectFn: kDefaultConnectFn}) {
const isMongod = assert.commandWorked(conn.adminCommand({isMaster: 1})).msg !== 'isdbgrid';
if (isMongod) {
return getDataMemberConnectionStrings(conn);
}
return findConnectedNodesViaMongos(conn, options);
}
function addNonConfigNodesToList(topology, hostList) {
if (topology.type === Topology.kStandalone) {
hostList.push(topology.mongod);
} else if (topology.type === Topology.kReplicaSet) {
hostList.push(...topology.nodes);
} else if (topology.type === Topology.kShardedCluster) {
for (let shardName of Object.keys(topology.shards)) {
const shardTopology = topology.shards[shardName];
addNonConfigNodesToList(shardTopology, hostList);
}
hostList.push(...topology.mongos.nodes);
} else {
throw new Error('Unrecognized topology format: ' + tojson(topology));
}
}
/**
* Return list of nodes in the cluster given a connection NOT including config servers (if
* there are any).
*/
function findNonConfigNodes(conn) {
const topology = findConnectedNodes(conn);
let hostList = [];
addNonConfigNodesToList(topology, hostList);
return hostList;
}
return {findConnectedNodes: findConnectedNodes, findNonConfigNodes: findNonConfigNodes};
})();