summaryrefslogtreecommitdiff
path: root/jstests/sharding/analyze_shard_key
diff options
context:
space:
mode:
authorCheahuychou Mao <mao.cheahuychou@gmail.com>2023-03-20 22:17:55 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-03-21 00:02:01 +0000
commitfed4630c7dacaa6b6c66c3c9efca5b0e5f0775a4 (patch)
tree1fa7181554886c971626644d58277798149a1ef9 /jstests/sharding/analyze_shard_key
parent07c5ea19d962f1c53ec88183a4ae14cfe53cdc44 (diff)
downloadmongo-fed4630c7dacaa6b6c66c3c9efca5b0e5f0775a4.tar.gz
SERVER-75030 Disallow configureQueryAnalyzer command on multi-tenant replica set
Diffstat (limited to 'jstests/sharding/analyze_shard_key')
-rw-r--r--jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js68
-rw-r--r--jstests/sharding/analyze_shard_key/configure_query_analyzer_basic.js251
2 files changed, 198 insertions, 121 deletions
diff --git a/jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js b/jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js
index 72dc63cde94..d15a9053411 100644
--- a/jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js
+++ b/jstests/sharding/analyze_shard_key/analyze_shard_key_basic.js
@@ -8,9 +8,10 @@
load("jstests/libs/catalog_shard_util.js");
-const dbName = "testDb";
+const dbNameBase = "testDb";
-function testNonExistingCollection(testCases) {
+function testNonExistingCollection(testCases, tenantId) {
+ const dbName = tenantId ? (tenantId + "-" + dbNameBase) : dbNameBase;
const collName = "testCollNonExisting";
const ns = dbName + "." + collName;
const candidateKey = {candidateKey: 1};
@@ -18,7 +19,14 @@ function testNonExistingCollection(testCases) {
testCases.forEach(testCase => {
jsTest.log(`Running analyzeShardKey command against a non-existing collection: ${
tojson(testCase)}`);
- const res = testCase.conn.adminCommand({analyzeShardKey: ns, key: candidateKey});
+ const cmdObj = {analyzeShardKey: ns, key: candidateKey};
+ if (tenantId) {
+ cmdObj.$tenant = tenantId;
+ }
+ const res = testCase.conn.adminCommand(cmdObj);
+ // If the command is not supported, it should fail even before the collection validation
+ // step. That is, it should fail with an IllegalOperation error instead of a
+ // NamespaceNotFound error.
const expectedErrorCode =
testCase.isSupported ? ErrorCodes.NamespaceNotFound : ErrorCodes.IllegalOperation;
assert.commandFailedWithCode(res, expectedErrorCode);
@@ -26,6 +34,7 @@ function testNonExistingCollection(testCases) {
}
function testExistingUnshardedCollection(writeConn, testCases) {
+ const dbName = dbNameBase;
const collName = "testCollUnsharded";
const ns = dbName + "." + collName;
const coll = writeConn.getCollection(ns);
@@ -45,7 +54,7 @@ function testExistingUnshardedCollection(writeConn, testCases) {
// This is an unsharded collection so in a sharded cluster it only exists on the
// primary shard.
let expectedErrCode = (() => {
- if (testCase.isMongos || testCase.isNonShardsvrMongod) {
+ if (testCase.isMongos || testCase.isReplSetMongod) {
return ErrorCodes.IllegalOperation;
} else if (testCase.isPrimaryShardMongod) {
return ErrorCodes.CollectionIsEmptyLocally;
@@ -71,8 +80,7 @@ function testExistingUnshardedCollection(writeConn, testCases) {
if (testCase.isSupported) {
// This is an unsharded collection so in a sharded cluster it only exists on the
// primary shard.
- if (testCase.isNonShardsvrMongod || testCase.isPrimaryShardMongod ||
- testCase.isMongos) {
+ if (testCase.isReplSetMongod || testCase.isPrimaryShardMongod || testCase.isMongos) {
assert.commandWorked(res0);
assert.commandWorked(res1);
} else {
@@ -87,6 +95,7 @@ function testExistingUnshardedCollection(writeConn, testCases) {
}
function testExistingShardedCollection(st, testCases) {
+ const dbName = dbNameBase;
const collName = "testCollSharded";
const ns = dbName + "." + collName;
const coll = st.s.getCollection(ns);
@@ -163,6 +172,7 @@ function testExistingShardedCollection(st, testCases) {
}
function testNotSupportReadWriteConcern(writeConn, testCases) {
+ const dbName = dbNameBase;
const collName = "testCollReadWriteConcern";
const ns = dbName + "." + collName;
const coll = writeConn.getCollection(ns);
@@ -195,12 +205,12 @@ function testNotSupportReadWriteConcern(writeConn, testCases) {
}
});
- assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
- st.ensurePrimaryShard(dbName, st.shard0.name);
+ assert.commandWorked(st.s.adminCommand({enableSharding: dbNameBase}));
+ st.ensurePrimaryShard(dbNameBase, st.shard0.name);
+ const testCases = [];
// The analyzeShardKey command is supported on mongos and all shardsvr mongods (both primary and
// secondary).
- const testCases = [];
testCases.push({conn: st.s, isSupported: true, isMongos: true});
st.rs0.nodes.forEach(node => {
testCases.push({conn: node, isSupported: true, isPrimaryShardMongod: true});
@@ -226,15 +236,15 @@ function testNotSupportReadWriteConcern(writeConn, testCases) {
}
{
- const rst = new ReplSetTest({nodes: 2});
+ const rst = new ReplSetTest({name: jsTest.name() + "_non_multitenant", nodes: 2});
rst.startSet();
rst.initiate();
const primary = rst.getPrimary();
- // The analyzeShardKey command is supported on all mongods (both primary and secondary).
const testCases = [];
+ // The analyzeShardKey command is supported on all mongods (both primary and secondary).
rst.nodes.forEach(node => {
- testCases.push({conn: node, isSupported: true, isNonShardsvrMongod: true});
+ testCases.push({conn: node, isSupported: true, isReplSetMongod: true});
});
testExistingUnshardedCollection(primary, testCases);
@@ -243,4 +253,38 @@ function testNotSupportReadWriteConcern(writeConn, testCases) {
rst.stopSet();
}
+
+if (!TestData.auth) {
+ const rst = new ReplSetTest({
+ name: jsTest.name() + "_multitenant",
+ nodes: 1,
+ nodeOptions: {auth: "", setParameter: {multitenancySupport: true}}
+ });
+ rst.startSet({keyFile: "jstests/libs/key1"});
+ rst.initiate();
+ const primary = rst.getPrimary();
+ const adminDb = primary.getDB("admin");
+ const tenantId = ObjectId();
+
+ // Prepare a user for testing multitenancy via $tenant.
+ // Must be authenticated as a user with ActionType::useTenant in order to use $tenant.
+ assert.commandWorked(
+ adminDb.runCommand({createUser: "admin", pwd: "pwd", roles: ["__system"]}));
+ assert(adminDb.auth("admin", "pwd"));
+
+ // The analyzeShardKey command is supported on any mongod.
+ const testCases = [{conn: adminDb, isSupported: true}];
+ testNonExistingCollection(testCases, tenantId);
+ rst.stopSet();
+}
+
+{
+ const mongod = MongoRunner.runMongod();
+
+ // The analyzeShardKey command is not supported on standalone mongod.
+ const testCases = [{conn: mongod, isSupported: false}];
+ testExistingUnshardedCollection(mongod, testCases);
+
+ MongoRunner.stopMongod(mongod);
+}
})();
diff --git a/jstests/sharding/analyze_shard_key/configure_query_analyzer_basic.js b/jstests/sharding/analyze_shard_key/configure_query_analyzer_basic.js
index 56a9f4d4b4f..fc7ee9329c3 100644
--- a/jstests/sharding/analyze_shard_key/configure_query_analyzer_basic.js
+++ b/jstests/sharding/analyze_shard_key/configure_query_analyzer_basic.js
@@ -6,148 +6,181 @@
(function() {
"use strict";
-function testNonExistingCollection(conn, ns) {
- jsTest.log(`Running configureQueryAnalyzer command against an non-existing collection ${
- ns} on ${conn}`);
- assert.commandFailedWithCode(
- conn.adminCommand({configureQueryAnalyzer: ns, mode: "full", sampleRate: 1}),
- ErrorCodes.NamespaceNotFound);
+const dbNameBase = "testDb";
+
+function testNonExistingCollection(testCases, tenantId) {
+ const dbName = tenantId ? (tenantId + "-" + dbNameBase) : dbNameBase;
+ const collName = "testCollNonExisting";
+ const ns = dbName + "." + collName;
+
+ testCases.forEach(testCase => {
+ jsTest.log(`Running configureQueryAnalyzer command against an non-existing collection: ${
+ tojson(testCase)}`);
+ const cmdObj = {configureQueryAnalyzer: ns, mode: "full", sampleRate: 1};
+ if (tenantId) {
+ cmdObj.$tenant = tenantId;
+ }
+ const res = testCase.conn.adminCommand(cmdObj);
+ // If the command is not supported, it should fail even before the collection validation
+ // step. That is, it should fail with an IllegalOperation error instead of a
+ // NamespaceNotFound error.
+ const expectedErrorCode =
+ testCase.isSupported ? ErrorCodes.NamespaceNotFound : testCase.expectedErrorCode;
+ assert.commandFailedWithCode(res, expectedErrorCode);
+ });
}
-function testExistingCollection(conn, ns) {
- jsTest.log(
- `Running configureQueryAnalyzer command against an existing collection ${ns} on ${conn}`);
-
- // Cannot set 'sampleRate' to 0.
- assert.commandFailedWithCode(
- conn.adminCommand({configureQueryAnalyzer: ns, mode: "full", sampleRate: 0}),
- ErrorCodes.InvalidOptions);
+function testExistingCollection(writeConn, testCases) {
+ const dbName = dbNameBase;
+ const collName = "testCollUnsharded";
+ const ns = dbName + "." + collName;
+ const db = writeConn.getDB(dbName);
+ assert.commandWorked(db.createCollection(collName));
+
+ testCases.forEach(testCase => {
+ jsTest.log(
+ `Running configureQueryAnalyzer command against an existing collection:
+ ${tojson(testCase)}`);
+
+ // Can set 'sampleRate' to > 0.
+ const basicRes =
+ testCase.conn.adminCommand({configureQueryAnalyzer: ns, mode: "full", sampleRate: 0.1});
+ if (!testCase.isSupported) {
+ assert.commandFailedWithCode(basicRes, testCase.expectedErrorCode);
+ // There is no need to test the remaining cases.
+ return;
+ }
+ assert.commandWorked(basicRes);
+ assert.commandWorked(
+ testCase.conn.adminCommand({configureQueryAnalyzer: ns, mode: "full", sampleRate: 1}));
+ assert.commandWorked(testCase.conn.adminCommand(
+ {configureQueryAnalyzer: ns, mode: "full", sampleRate: 1000}));
+
+ // Cannot set 'sampleRate' to 0.
+ assert.commandFailedWithCode(
+ testCase.conn.adminCommand({configureQueryAnalyzer: ns, mode: "full", sampleRate: 0}),
+ ErrorCodes.InvalidOptions);
- // Cannot set 'sampleRate' to larger than 1'000'000.
- assert.commandFailedWithCode(
- conn.adminCommand({configureQueryAnalyzer: ns, mode: "full", sampleRate: 1000001}),
- ErrorCodes.InvalidOptions);
+ // Cannot set 'sampleRate' to larger than 1'000'000.
+ assert.commandFailedWithCode(
+ testCase.conn.adminCommand(
+ {configureQueryAnalyzer: ns, mode: "full", sampleRate: 1000001}),
+ ErrorCodes.InvalidOptions);
- // Can set 'sampleRate' to > 0.
- assert.commandWorked(
- conn.adminCommand({configureQueryAnalyzer: ns, mode: "full", sampleRate: 0.1}));
- assert.commandWorked(
- conn.adminCommand({configureQueryAnalyzer: ns, mode: "full", sampleRate: 1}));
- assert.commandWorked(
- conn.adminCommand({configureQueryAnalyzer: ns, mode: "full", sampleRate: 1000}));
-
- // Cannot specify 'sampleRate' when 'mode' is "off".
- assert.commandFailedWithCode(
- conn.adminCommand({configureQueryAnalyzer: ns, mode: "off", sampleRate: 1}),
- ErrorCodes.InvalidOptions);
- assert.commandWorked(conn.adminCommand({configureQueryAnalyzer: ns, mode: "off"}));
-
- // Cannot specify read/write concern.
- assert.commandFailedWithCode(conn.adminCommand({
- configureQueryAnalyzer: ns,
- mode: "full",
- sampleRate: 1,
- readConcern: {level: "available"}
- }),
- ErrorCodes.InvalidOptions);
- assert.commandFailedWithCode(conn.adminCommand({
- configureQueryAnalyzer: ns,
- mode: "full",
- sampleRate: 1,
- writeConcern: {w: "majority"}
- }),
- ErrorCodes.InvalidOptions);
+ // Cannot specify 'sampleRate' when 'mode' is "off".
+ assert.commandFailedWithCode(
+ testCase.conn.adminCommand({configureQueryAnalyzer: ns, mode: "off", sampleRate: 1}),
+ ErrorCodes.InvalidOptions);
+ assert.commandWorked(testCase.conn.adminCommand({configureQueryAnalyzer: ns, mode: "off"}));
+
+ // Cannot specify read/write concern.
+ assert.commandFailedWithCode(testCase.conn.adminCommand({
+ configureQueryAnalyzer: ns,
+ mode: "full",
+ sampleRate: 1,
+ readConcern: {level: "available"}
+ }),
+ ErrorCodes.InvalidOptions);
+ assert.commandFailedWithCode(testCase.conn.adminCommand({
+ configureQueryAnalyzer: ns,
+ mode: "full",
+ sampleRate: 1,
+ writeConcern: {w: "majority"}
+ }),
+ ErrorCodes.InvalidOptions);
+ });
}
{
const st = new ShardingTest({shards: 1, rs: {nodes: 2}});
+
const shard0Primary = st.rs0.getPrimary();
const shard0Secondaries = st.rs0.getSecondaries();
const configPrimary = st.configRS.getPrimary();
const configSecondaries = st.configRS.getSecondaries();
- const dbName = "testDb";
- const nonExistingNs = dbName + ".nonExistingColl";
-
- const shardedNs = dbName + ".shardedColl";
- const shardKey = {key: 1};
- assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
- st.ensurePrimaryShard(dbName, st.shard0.name);
- assert.commandWorked(st.s.adminCommand({shardCollection: shardedNs, key: shardKey}));
-
- const unshardedCollName = "unshardedColl";
- const unshardedNs = dbName + "." + unshardedCollName;
- assert.commandWorked(st.s.getDB(dbName).createCollection(unshardedCollName));
-
- // Verify that the command is supported on mongos and configsvr primary mongod.
- function testSupported(conn) {
- testNonExistingCollection(conn, nonExistingNs);
- testExistingCollection(conn, unshardedNs);
- testExistingCollection(conn, shardedNs);
- }
-
- testSupported(st.s);
- testSupported(configPrimary);
-
- // Verify that the command is not supported on configsvr secondary mongods or any shardvr
- // mongods.
- function testNotSupported(conn, errorCode) {
- assert.commandFailedWithCode(
- conn.adminCommand({configureQueryAnalyzer: unshardedNs, mode: "full", sampleRate: 1}),
- errorCode);
- assert.commandFailedWithCode(
- conn.adminCommand({configureQueryAnalyzer: shardedNs, mode: "full", sampleRate: 1}),
- errorCode);
- }
-
+ const testCases = [];
+ // The configureQueryAnalyzer command is only supported on mongos and configsvr primary mongod.
+ testCases.push({conn: st.s, isSupported: true});
+ testCases.push({conn: configPrimary, isSupported: true});
configSecondaries.forEach(node => {
- testNotSupported(node, ErrorCodes.NotWritablePrimary);
+ testCases.push(
+ {conn: node, isSupported: false, expectedErrorCode: ErrorCodes.NotWritablePrimary});
});
- if (!TestData.catalogShard) {
- // If there's a catalog shard, shard0 will be the config server and can accept
- // configureQueryAnalyzer.
- testNotSupported(shard0Primary, ErrorCodes.IllegalOperation);
- }
+ // If there's a catalog shard, shard0 will be the config server and can accept
+ // configureQueryAnalyzer.
+ testCases.push(
+ Object.assign({conn: shard0Primary},
+ TestData.catalogShard
+ ? {isSupported: true}
+ : {isSupported: false, expectedErrorCode: ErrorCodes.IllegalOperation}));
shard0Secondaries.forEach(node => {
- testNotSupported(node, ErrorCodes.NotWritablePrimary);
+ testCases.push(
+ {conn: node, isSupported: false, expectedErrorCode: ErrorCodes.NotWritablePrimary});
});
+ testNonExistingCollection(testCases);
+ testExistingCollection(st.s, testCases);
+
st.stop();
}
{
- const rst = new ReplSetTest({nodes: 2});
+ const rst = new ReplSetTest({name: jsTest.name() + "_non_multitenant", nodes: 2});
rst.startSet();
rst.initiate();
const primary = rst.getPrimary();
const secondaries = rst.getSecondaries();
- const dbName = "testDb";
- const nonExistingNs = dbName + ".nonExistingColl";
+ const testCases = [];
+ // The configureQueryAnalyzer command is only supported on primary mongod.
+ testCases.push(Object.assign({conn: primary, isSupported: true}));
+ secondaries.forEach(node => {
+ testCases.push(
+ {conn: node, isSupported: false, expectedErrorCode: ErrorCodes.NotWritablePrimary});
+ });
- const unshardedCollName = "unshardedColl";
- const unshardedNs = dbName + "." + unshardedCollName;
- assert.commandWorked(primary.getDB(dbName).createCollection(unshardedCollName));
+ testNonExistingCollection(testCases);
+ testExistingCollection(primary, testCases);
- // Verify that the command is supported on primary mongod.
- function testSupported(conn) {
- testNonExistingCollection(conn, nonExistingNs);
- testExistingCollection(conn, unshardedNs);
- }
+ rst.stopSet();
+}
- testSupported(primary, unshardedNs);
+if (!TestData.auth) {
+ const rst = new ReplSetTest({
+ name: jsTest.name() + "_multitenant",
+ nodes: 2,
+ nodeOptions: {setParameter: {multitenancySupport: true}}
+ });
+ rst.startSet({keyFile: "jstests/libs/key1"});
+ rst.initiate();
+ const primary = rst.getPrimary();
+ const adminDb = primary.getDB("admin");
+ const tenantId = ObjectId();
- // Verify that the command is not supported on secondary mongods.
- function testNotSupported(conn, errorCode) {
- assert.commandFailedWithCode(
- conn.adminCommand({configureQueryAnalyzer: unshardedNs, mode: "full", sampleRate: 1}),
- errorCode);
- }
+ // Prepare a user for testing multitenancy via $tenant.
+ // Must be authenticated as a user with ActionType::useTenant in order to use $tenant.
+ assert.commandWorked(
+ adminDb.runCommand({createUser: "admin", pwd: "pwd", roles: ["__system"]}));
+ assert(adminDb.auth("admin", "pwd"));
- secondaries.forEach(node => {
- testNotSupported(node, ErrorCodes.NotWritablePrimary);
- });
+ // The configureQueryAnalyzer command is not supported even on primary mongod.
+ const testCases = [];
+ testCases.push(Object.assign(
+ {conn: primary, isSupported: false, expectedErrorCode: ErrorCodes.IllegalOperation}));
+ testNonExistingCollection(testCases, tenantId);
rst.stopSet();
}
+
+{
+ const mongod = MongoRunner.runMongod();
+
+ // The configureQueryAnalyzer command is not supported on standalone mongod.
+ const testCases =
+ [{conn: mongod, isSupported: false, expectedErrorCode: ErrorCodes.IllegalOperation}];
+ testNonExistingCollection(testCases);
+
+ MongoRunner.stopMongod(mongod);
+}
})();