SERVER-118828 Allow applyOps to execute upgradeDowngradeViewlessTimeseries c-entry for system-authenticated users (#51066)
GitOrigin-RevId: 3db433cd8fb97a5159e85947f45f76d53ffad6f4
This commit is contained in:
parent
4258f2ba01
commit
ecbdf9983c
92
jstests/auth/timeseries_upgrade_downgrade_apply_ops.js
Normal file
92
jstests/auth/timeseries_upgrade_downgrade_apply_ops.js
Normal file
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Tests that the upgradeDowngradeViewlessTimeseries oplog entry applied via applyOps requires
|
||||
* the __system role when auth is enabled.
|
||||
*
|
||||
* TODO(SERVER-114573): Remove once 9.0 becomes last-lts, as the oplog entry won't be
|
||||
* supported anymore.
|
||||
*
|
||||
* @tags: [
|
||||
* featureFlagCreateViewlessTimeseriesCollections,
|
||||
* ]
|
||||
*/
|
||||
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
||||
|
||||
if (lastLTSFCV != "8.0") {
|
||||
jsTest.log.info("Skipping test because last LTS FCV is no longer 8.0");
|
||||
quit();
|
||||
}
|
||||
|
||||
const keyFile = "jstests/libs/key1";
|
||||
const rst = new ReplSetTest({nodes: 1, keyFile: keyFile});
|
||||
rst.startSet();
|
||||
rst.initiate();
|
||||
|
||||
const primary = rst.getPrimary();
|
||||
const adminDB = primary.getDB("admin");
|
||||
|
||||
adminDB.createUser({user: "admin", pwd: "pwd", roles: ["root"]});
|
||||
assert(adminDB.auth("admin", "pwd"));
|
||||
|
||||
const dbName = jsTestName();
|
||||
const collName = "ts";
|
||||
const testDB = primary.getDB(dbName);
|
||||
const bucketsColl = testDB.getCollection("system.buckets." + collName);
|
||||
|
||||
const timeseriesOptions = {
|
||||
timeField: "t",
|
||||
granularity: "seconds",
|
||||
bucketMaxSpanSeconds: 3600,
|
||||
};
|
||||
|
||||
function makeCreateCmd(collectionName) {
|
||||
return {
|
||||
applyOps: [
|
||||
{
|
||||
op: "c",
|
||||
ns: dbName + ".$cmd",
|
||||
o: {
|
||||
create: collectionName,
|
||||
clusteredIndex: true,
|
||||
timeseries: timeseriesOptions,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function makeUpgradeDowngradeCmd(isUpgrade, uuid) {
|
||||
return {
|
||||
applyOps: [
|
||||
{
|
||||
op: "c",
|
||||
ns: dbName + ".$cmd",
|
||||
o: {upgradeDowngradeViewlessTimeseries: collName, isUpgrade: isUpgrade},
|
||||
ui: uuid,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Create a legacy (viewful) timeseries collection via applyOps at FCV 9.0.
|
||||
assert.commandWorked(testDB.adminCommand(makeCreateCmd("system.buckets." + collName)));
|
||||
const collUUID = bucketsColl.getUUID();
|
||||
|
||||
// A root user should not be able to applyOps an internal-only oplog entry.
|
||||
assert.commandFailedWithCode(testDB.adminCommand(makeUpgradeDowngradeCmd(true, collUUID)), ErrorCodes.Unauthorized);
|
||||
|
||||
adminDB.logout();
|
||||
|
||||
// The __system role is required to applyOps internal-only oplog entries.
|
||||
rst.asCluster(primary, () => {
|
||||
// Upgrade: viewful -> viewless via applyOps.
|
||||
assert.commandWorked(testDB.adminCommand(makeUpgradeDowngradeCmd(true, collUUID)));
|
||||
assert.eq(null, bucketsColl.exists(), "Expected no system.buckets collection after upgrade");
|
||||
|
||||
// Downgrade: viewless -> viewful via applyOps.
|
||||
// First downgrade FCV so the feature flag is disabled (required for downgrade path).
|
||||
assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: lastLTSFCV, confirm: true}));
|
||||
assert.commandWorked(testDB.adminCommand(makeUpgradeDowngradeCmd(false, collUUID)));
|
||||
assert.neq(null, bucketsColl.exists(), "Expected system.buckets collection after downgrade");
|
||||
});
|
||||
|
||||
rst.stopSet();
|
||||
@ -24,3 +24,6 @@ filters:
|
||||
- "delinquent*":
|
||||
approvers:
|
||||
- 10gen/server-workload-resilience
|
||||
- "timeseries_upgrade_downgrade_apply_ops.js":
|
||||
approvers:
|
||||
- 10gen/server-catalog-and-routing-shard-catalog
|
||||
|
||||
102
jstests/noPassthrough/timeseries_upgrade_downgrade_apply_ops.js
Normal file
102
jstests/noPassthrough/timeseries_upgrade_downgrade_apply_ops.js
Normal file
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Tests that the upgradeDowngradeViewlessTimeseries oplog entry can be applied via applyOps.
|
||||
*
|
||||
* TODO(SERVER-114573): Remove once 9.0 becomes last-lts, as the oplog entry won't be
|
||||
* supported anymore.
|
||||
*
|
||||
* @tags: [
|
||||
* featureFlagCreateViewlessTimeseriesCollections,
|
||||
* ]
|
||||
*/
|
||||
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
||||
|
||||
if (lastLTSFCV != "8.0") {
|
||||
jsTest.log.info("Skipping test because last LTS FCV is no longer 8.0");
|
||||
quit();
|
||||
}
|
||||
|
||||
const rst = new ReplSetTest({nodes: 1});
|
||||
rst.startSet();
|
||||
rst.initiate();
|
||||
|
||||
const primary = rst.getPrimary();
|
||||
const adminDB = primary.getDB("admin");
|
||||
const dbName = jsTestName();
|
||||
const collName = "ts";
|
||||
const testDB = primary.getDB(dbName);
|
||||
const bucketsColl = testDB.getCollection("system.buckets." + collName);
|
||||
const mainColl = testDB.getCollection(collName);
|
||||
|
||||
const timeseriesOptions = {
|
||||
timeField: "t",
|
||||
granularity: "seconds",
|
||||
bucketMaxSpanSeconds: 3600,
|
||||
};
|
||||
|
||||
function makeCreateCmd(collectionName) {
|
||||
return {
|
||||
applyOps: [
|
||||
{
|
||||
op: "c",
|
||||
ns: dbName + ".$cmd",
|
||||
o: {
|
||||
create: collectionName,
|
||||
clusteredIndex: true,
|
||||
timeseries: timeseriesOptions,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function makeUpgradeDowngradeCmd(isUpgrade, uuid) {
|
||||
return {
|
||||
applyOps: [
|
||||
{
|
||||
op: "c",
|
||||
ns: dbName + ".$cmd",
|
||||
o: {upgradeDowngradeViewlessTimeseries: collName, isUpgrade: isUpgrade},
|
||||
ui: uuid,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Phase 1: Test upgrade path at FCV 9.0 (feature flag enabled).
|
||||
// Use applyOps to create a legacy (viewful) timeseries collection, then upgrade it.
|
||||
{
|
||||
jsTest.log("Phase 1: Testing timeseries upgrade via applyOps at latest FCV");
|
||||
|
||||
// Create a legacy system.buckets collection via applyOps.
|
||||
assert.commandWorked(testDB.adminCommand(makeCreateCmd("system.buckets." + collName)));
|
||||
const collUUID = bucketsColl.getUUID();
|
||||
assert.neq(null, bucketsColl.exists(), "system.buckets collection should exist after create");
|
||||
|
||||
// Upgrade: viewful -> viewless via applyOps.
|
||||
assert.commandWorked(testDB.adminCommand(makeUpgradeDowngradeCmd(true, collUUID)));
|
||||
assert.eq(null, bucketsColl.exists(), "Expected no system.buckets collection after upgrade");
|
||||
assert.neq(null, mainColl.exists(), "Expected main collection after upgrade");
|
||||
|
||||
// Clean up for phase 2.
|
||||
assert.commandWorked(testDB.runCommand({drop: collName}));
|
||||
}
|
||||
|
||||
// Phase 2: Test downgrade path at FCV 8.0 (feature flag disabled).
|
||||
// Use applyOps to create a viewless timeseries collection, then downgrade it.
|
||||
{
|
||||
jsTest.log("Phase 2: Testing downgrade via applyOps at last-LTS FCV");
|
||||
|
||||
assert.commandWorked(adminDB.runCommand({setFeatureCompatibilityVersion: lastLTSFCV, confirm: true}));
|
||||
|
||||
// Create a viewless timeseries collection via applyOps.
|
||||
assert.commandWorked(testDB.adminCommand(makeCreateCmd(collName)));
|
||||
const collUUID = mainColl.getUUID();
|
||||
assert.neq(null, mainColl.exists(), "Main collection should exist after create");
|
||||
assert.eq(null, bucketsColl.exists(), "system.buckets should not exist for viewless collection");
|
||||
|
||||
// Downgrade: viewless -> viewful via applyOps.
|
||||
assert.commandWorked(testDB.adminCommand(makeUpgradeDowngradeCmd(false, collUUID)));
|
||||
assert.neq(null, bucketsColl.exists(), "Expected system.buckets collection after downgrade");
|
||||
}
|
||||
|
||||
rst.stopSet();
|
||||
@ -54,6 +54,7 @@
|
||||
#include "mongo/rpc/op_msg.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
#include "mongo/util/str.h"
|
||||
#include "mongo/util/string_map.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -111,6 +112,21 @@ Status OplogApplicationChecks::checkOperationAuthorization(OperationContext* opC
|
||||
StringData commandName = o.firstElement().fieldNameStringData();
|
||||
Command* commandInOplogEntry = CommandHelpers::findCommand(opCtx, commandName);
|
||||
if (!commandInOplogEntry) {
|
||||
// Some oplog entries are internal-only and not registered in the global command
|
||||
// registry. Allow them through if the user has ActionType::internal (e.g. __system).
|
||||
// TODO(SERVER-114573): Remove upgradeDowngradeViewlessTimeseries from this list once
|
||||
// 9.0 becomes last-lts, as the oplog entry will no longer exist.
|
||||
static const StringDataSet kInternalOplogCommands{
|
||||
"upgradeDowngradeViewlessTimeseries"_sd,
|
||||
};
|
||||
if (kInternalOplogCommands.contains(commandName)) {
|
||||
if (!authSession->isAuthorizedForActionsOnResource(
|
||||
ResourcePattern::forClusterResource(dbName.tenantId()),
|
||||
ActionType::internal)) {
|
||||
return Status(ErrorCodes::Unauthorized, "Unauthorized");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
return Status(ErrorCodes::FailedToParse, "Unrecognized command in op");
|
||||
}
|
||||
|
||||
@ -123,8 +139,8 @@ Status OplogApplicationChecks::checkOperationAuthorization(OperationContext* opC
|
||||
nss.tenantId(), "admin", SerializationContext::stateDefault());
|
||||
}
|
||||
|
||||
// TODO reuse the parse result for when we run() later. Note that when running,
|
||||
// we must use a potentially different dbname.
|
||||
// TODO SERVER-123371 reuse the parse result for when we run() later. Note that when
|
||||
// running, we must use a potentially different dbname.
|
||||
return [&] {
|
||||
try {
|
||||
boost::optional<auth::ValidatedTenancyScope> vts;
|
||||
|
||||
@ -1368,6 +1368,8 @@ const StringMap<ApplyOpMetadata> kOpsMap = {
|
||||
return Status::OK();
|
||||
},
|
||||
{ErrorCodes::NamespaceNotFound}}},
|
||||
// TODO(SERVER-114573): Remove once 9.0 becomes last-lts, as this oplog entry won't be
|
||||
// supported anymore.
|
||||
{"upgradeDowngradeViewlessTimeseries",
|
||||
{[](OperationContext* opCtx, const ApplierOperation& op, OplogApplication::Mode mode)
|
||||
-> Status {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user