summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjannaerin <golden.janna@gmail.com>2022-11-14 17:20:04 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-11-15 19:13:40 +0000
commit919b31026d9d3c629bfd5f29556e3a532395dc62 (patch)
tree5278ae2d4f46d540b33cbf09213a7b3eb54bfa0e
parente94c11f55f5e5eb7f764523359fa908c8efef68c (diff)
downloadmongo-919b31026d9d3c629bfd5f29556e3a532395dc62.tar.gz
SERVER-70415 Attach tenantId to dbStats during initial sync
-rw-r--r--src/mongo/db/auth/authorization_manager.h11
-rw-r--r--src/mongo/db/auth/authorization_manager_impl.cpp33
-rw-r--r--src/mongo/db/auth/authorization_manager_impl.h4
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.h17
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.cpp46
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.h5
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.h10
-rw-r--r--src/mongo/db/repl/SConscript2
-rw-r--r--src/mongo/db/repl/all_database_cloner.cpp124
-rw-r--r--src/mongo/db/repl/all_database_cloner.h4
-rw-r--r--src/mongo/db/repl/all_database_cloner_test.cpp293
-rw-r--r--src/mongo/db/repl/storage_interface.h5
-rw-r--r--src/mongo/db/repl/storage_interface_impl.cpp58
-rw-r--r--src/mongo/db/repl/storage_interface_impl.h5
-rw-r--r--src/mongo/db/repl/storage_interface_mock.h9
-rw-r--r--src/mongo/embedded/embedded_auth_manager.cpp8
16 files changed, 498 insertions, 136 deletions
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index 69077aebe6a..a23905406da 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -187,6 +187,11 @@ public:
virtual bool isAuthEnabled() const = 0;
/**
+ * Returns whether a schema version document exists.
+ */
+ virtual Status hasValidAuthSchemaVersionDocumentForInitialSync(OperationContext* opCtx) = 0;
+
+ /**
* Returns via the output parameter "version" the version number of the authorization
* system. Returns Status::OK() if it was able to successfully fetch the current
* authorization version. If it has problems fetching the most up to date version it
@@ -223,6 +228,12 @@ public:
BSONObj* result) = 0;
/**
+ * Returns true if there exists at least one user document in the system. If `tenantId` is set,
+ * only looks for users associated with `tenantId`.
+ */
+ virtual bool hasUser(OperationContext* opCtx, const boost::optional<TenantId>& tenantId) = 0;
+
+ /**
* Delegates method call to the underlying AuthzManagerExternalState.
*/
virtual Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) = 0;
diff --git a/src/mongo/db/auth/authorization_manager_impl.cpp b/src/mongo/db/auth/authorization_manager_impl.cpp
index db1081b8bf3..6b95652f6bb 100644
--- a/src/mongo/db/auth/authorization_manager_impl.cpp
+++ b/src/mongo/db/auth/authorization_manager_impl.cpp
@@ -449,6 +449,39 @@ Status AuthorizationManagerImpl::getUserDescription(OperationContext* opCtx,
return _externalState->getUserDescription(opCtx, UserRequest(userName, boost::none), result);
}
+Status AuthorizationManagerImpl::hasValidAuthSchemaVersionDocumentForInitialSync(
+ OperationContext* opCtx) {
+ BSONObj foundDoc;
+ auto status = _externalState->hasValidStoredAuthorizationVersion(opCtx, &foundDoc);
+
+ if (status == ErrorCodes::NoSuchKey || status == ErrorCodes::TypeMismatch) {
+ std::string msg = str::stream()
+ << "During initial sync, found malformed auth schema version document: "
+ << status.toString() << "; document: " << foundDoc;
+ return Status(ErrorCodes::AuthSchemaIncompatible, msg);
+ }
+
+ if (status.isOK()) {
+ auto version = foundDoc.getIntField(AuthorizationManager::schemaVersionFieldName);
+ if ((version != AuthorizationManager::schemaVersion26Final) &&
+ (version != AuthorizationManager::schemaVersion28SCRAM)) {
+ std::string msg = str::stream()
+ << "During initial sync, found auth schema version " << version
+ << ", but this version of MongoDB only supports schema versions "
+ << AuthorizationManager::schemaVersion26Final << " and "
+ << AuthorizationManager::schemaVersion28SCRAM;
+ return {ErrorCodes::AuthSchemaIncompatible, msg};
+ }
+ }
+
+ return status;
+}
+
+bool AuthorizationManagerImpl::hasUser(OperationContext* opCtx,
+ const boost::optional<TenantId>& tenantId) {
+ return _externalState->hasAnyUserDocuments(opCtx, tenantId).isOK();
+}
+
Status AuthorizationManagerImpl::rolesExist(OperationContext* opCtx,
const std::vector<RoleName>& roleNames) {
return _externalState->rolesExist(opCtx, roleNames);
diff --git a/src/mongo/db/auth/authorization_manager_impl.h b/src/mongo/db/auth/authorization_manager_impl.h
index fcec7b97e32..0cb99f041f9 100644
--- a/src/mongo/db/auth/authorization_manager_impl.h
+++ b/src/mongo/db/auth/authorization_manager_impl.h
@@ -67,12 +67,16 @@ public:
OID getCacheGeneration() override;
+ Status hasValidAuthSchemaVersionDocumentForInitialSync(OperationContext* opCtx) override;
+
bool hasAnyPrivilegeDocuments(OperationContext* opCtx) override;
Status getUserDescription(OperationContext* opCtx,
const UserName& userName,
BSONObj* result) override;
+ bool hasUser(OperationContext* opCtx, const boost::optional<TenantId>& tenantId) override;
+
Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) override;
StatusWith<ResolvedRoleData> resolveRoles(OperationContext* opCtx,
diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h
index 96cf71257e0..ad1442361ed 100644
--- a/src/mongo/db/auth/authz_manager_external_state.h
+++ b/src/mongo/db/auth/authz_manager_external_state.h
@@ -84,6 +84,13 @@ public:
* Retrieves the schema version of the persistent data describing users and roles.
* Will leave *outVersion unmodified on non-OK status return values.
*/
+ virtual Status hasValidStoredAuthorizationVersion(OperationContext* opCtx,
+ BSONObj* foundVersionDoc) = 0;
+
+ /**
+ * Retrieves the schema version of the persistent data describing users and roles.
+ * Modifies *outVersion if status is NoMatchingDocument.
+ */
virtual Status getStoredAuthorizationVersion(OperationContext* opCtx, int* outVersion) = 0;
/**
@@ -169,7 +176,15 @@ public:
std::vector<BSONObj>* result) = 0;
/**
- * Returns true if there exists at least one privilege document in the system.
+ * Returns true if there exists at least one user document in the system. If `tenantId` is
+ * set, checks whether a doc associated with this tenantId exists.
+ */
+ virtual Status hasAnyUserDocuments(OperationContext* opCtx,
+ const boost::optional<TenantId>& tenantId) = 0;
+
+ /**
+ * Returns true if there exists at least one privilege document in the system. If `tenantId` is
+ * set, checks whether a doc associated with this tenantId exists.
*/
virtual bool hasAnyPrivilegeDocuments(OperationContext* opCtx) = 0;
diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp
index 9f98e5f7032..39d6da595f3 100644
--- a/src/mongo/db/auth/authz_manager_external_state_local.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp
@@ -58,17 +58,16 @@ namespace mongo {
using std::vector;
using ResolveRoleOption = AuthzManagerExternalStateLocal::ResolveRoleOption;
-Status AuthzManagerExternalStateLocal::getStoredAuthorizationVersion(OperationContext* opCtx,
- int* outVersion) {
- BSONObj versionDoc;
+Status AuthzManagerExternalStateLocal::hasValidStoredAuthorizationVersion(
+ OperationContext* opCtx, BSONObj* foundVersionDoc) {
Status status = findOne(opCtx,
AuthorizationManager::versionCollectionNamespace,
AuthorizationManager::versionDocumentQuery,
- &versionDoc);
+ foundVersionDoc);
if (status.isOK()) {
- BSONElement versionElement = versionDoc[AuthorizationManager::schemaVersionFieldName];
+ BSONElement versionElement =
+ (*foundVersionDoc)[AuthorizationManager::schemaVersionFieldName];
if (versionElement.isNumber()) {
- *outVersion = versionElement.numberInt();
return Status::OK();
} else if (versionElement.eoo()) {
return Status(ErrorCodes::NoSuchKey,
@@ -83,12 +82,24 @@ Status AuthzManagerExternalStateLocal::getStoredAuthorizationVersion(OperationCo
<< ") for " << AuthorizationManager::schemaVersionFieldName
<< " field in version document");
}
+ } else {
+ return status;
+ }
+}
+
+Status AuthzManagerExternalStateLocal::getStoredAuthorizationVersion(OperationContext* opCtx,
+ int* outVersion) {
+ BSONObj foundVersionDoc;
+ auto status = hasValidStoredAuthorizationVersion(opCtx, &foundVersionDoc);
+ if (status.isOK()) {
+ *outVersion = foundVersionDoc.getIntField(AuthorizationManager::schemaVersionFieldName);
+ return status;
} else if (status == ErrorCodes::NoMatchingDocument) {
*outVersion = AuthorizationManager::schemaVersion28SCRAM;
return Status::OK();
- } else {
- return status;
}
+
+ return status;
}
namespace {
@@ -239,16 +250,23 @@ void handleAuthLocalGetUserFailPoint(const std::vector<RoleName>& directRoles) {
}
} // namespace
-// We ignore tenant-specific collections here, since hasAnyPrivilegeDocuments
-// only impacts localhost auth bypass which by definition will be a local user.
+Status AuthzManagerExternalStateLocal::hasAnyUserDocuments(
+ OperationContext* opCtx, const boost::optional<TenantId>& tenantId) {
+ BSONObj userBSONObj;
+ return findOne(opCtx,
+ NamespaceString(tenantId, AuthorizationManager::usersCollectionNamespace.ns()),
+ BSONObj(),
+ &userBSONObj);
+}
+
+// If tenantId is none, we're checking whether to enable localhost auth bypass which by definition
+// will be a local user.
bool AuthzManagerExternalStateLocal::hasAnyPrivilegeDocuments(OperationContext* opCtx) {
if (_hasAnyPrivilegeDocuments.load()) {
return true;
}
- BSONObj userBSONObj;
- Status statusFindUsers =
- findOne(opCtx, AuthorizationManager::usersCollectionNamespace, BSONObj(), &userBSONObj);
+ Status statusFindUsers = hasAnyUserDocuments(opCtx, boost::none);
// If we were unable to complete the query,
// it's best to assume that there _are_ privilege documents.
@@ -256,6 +274,8 @@ bool AuthzManagerExternalStateLocal::hasAnyPrivilegeDocuments(OperationContext*
_hasAnyPrivilegeDocuments.store(true);
return true;
}
+
+ BSONObj userBSONObj;
Status statusFindRoles =
findOne(opCtx, AuthorizationManager::rolesCollectionNamespace, BSONObj(), &userBSONObj);
if (statusFindRoles != ErrorCodes::NoMatchingDocument) {
diff --git a/src/mongo/db/auth/authz_manager_external_state_local.h b/src/mongo/db/auth/authz_manager_external_state_local.h
index afaf6732b17..c93641176d2 100644
--- a/src/mongo/db/auth/authz_manager_external_state_local.h
+++ b/src/mongo/db/auth/authz_manager_external_state_local.h
@@ -54,6 +54,8 @@ class AuthzManagerExternalStateLocal : public AuthzManagerExternalState {
public:
virtual ~AuthzManagerExternalStateLocal() = default;
+ Status hasValidStoredAuthorizationVersion(OperationContext* opCtx,
+ BSONObj* foundVersionDoc) override;
Status getStoredAuthorizationVersion(OperationContext* opCtx, int* outVersion) override;
StatusWith<User> getUserObject(OperationContext* opCtx, const UserRequest& userReq) override;
Status getUserDescription(OperationContext* opCtx,
@@ -79,6 +81,9 @@ public:
bool showBuiltinRoles,
std::vector<BSONObj>* result) override;
+ Status hasAnyUserDocuments(OperationContext* opCtx,
+ const boost::optional<TenantId>& tenantId) final;
+
bool hasAnyPrivilegeDocuments(OperationContext* opCtx) final;
/**
diff --git a/src/mongo/db/auth/authz_manager_external_state_s.h b/src/mongo/db/auth/authz_manager_external_state_s.h
index a1ac0feee41..5789b464337 100644
--- a/src/mongo/db/auth/authz_manager_external_state_s.h
+++ b/src/mongo/db/auth/authz_manager_external_state_s.h
@@ -53,6 +53,11 @@ public:
std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
AuthorizationManager* authzManager) final;
+
+ Status hasValidStoredAuthorizationVersion(OperationContext* opCtx,
+ BSONObj* foundVersionDoc) override {
+ return {ErrorCodes::NotImplemented, "AuthzMongos::hasValidStoredAuthorizationVersion"};
+ }
Status getStoredAuthorizationVersion(OperationContext* opCtx, int* outVersion) override;
Status rolesExist(OperationContext* opCtx, const std::vector<RoleName>& roleNames) final;
StatusWith<User> getUserObject(OperationContext* opCtx, const UserRequest& userReq) final;
@@ -88,6 +93,11 @@ public:
}
bool hasAnyPrivilegeDocuments(OperationContext* opCtx) final;
+
+ Status hasAnyUserDocuments(OperationContext* opCtx,
+ const boost::optional<TenantId>& tenantId) final {
+ return {ErrorCodes::NotImplemented, "AuthzMongos::hasValidStoredAuthorizationVersion"};
+ }
};
} // namespace mongo
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index ad8be47af0a..b293c042111 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -1046,6 +1046,7 @@ env.Library(
LIBDEPS=[
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/client/clientdriver_network',
+ '$BUILD_DIR/mongo/db/multitenancy',
'$BUILD_DIR/mongo/db/server_feature_flags',
'$BUILD_DIR/mongo/util/concurrency/thread_pool',
'$BUILD_DIR/mongo/util/net/network',
@@ -1061,6 +1062,7 @@ env.Library(
'$BUILD_DIR/mongo/db/commands/list_collections_filter',
'$BUILD_DIR/mongo/db/index_build_entry_helpers',
'$BUILD_DIR/mongo/db/index_builds_coordinator_interface',
+ '$BUILD_DIR/mongo/util/namespace_string_database_name_util',
'$BUILD_DIR/mongo/util/progress_meter',
'repl_server_parameters',
'replication_auth',
diff --git a/src/mongo/db/repl/all_database_cloner.cpp b/src/mongo/db/repl/all_database_cloner.cpp
index 014dbc81cef..9472d320052 100644
--- a/src/mongo/db/repl/all_database_cloner.cpp
+++ b/src/mongo/db/repl/all_database_cloner.cpp
@@ -33,6 +33,7 @@
#include <algorithm>
#include "mongo/base/string_data.h"
+#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/multitenancy_gen.h"
#include "mongo/db/repl/all_database_cloner.h"
#include "mongo/db/repl/replication_consistency_markers_gen.h"
@@ -41,6 +42,7 @@
#include "mongo/logv2/log.h"
#include "mongo/rpc/get_status_from_command_result.h"
#include "mongo/util/assert_util.h"
+#include "mongo/util/database_name_util.h"
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kReplicationInitialSync
@@ -143,16 +145,17 @@ BaseCloner::AfterStageBehavior AllDatabaseCloner::getInitialSyncIdStage() {
BaseCloner::AfterStageBehavior AllDatabaseCloner::listDatabasesStage() {
std::vector<mongo::BSONObj> databasesArray;
- if (gMultitenancySupport && serverGlobalParams.featureCompatibility.isVersionInitialized() &&
- gFeatureFlagRequireTenantID.isEnabled(serverGlobalParams.featureCompatibility)) {
- databasesArray = getClient()->getDatabaseInfos(BSONObj(),
- true /* nameOnly */,
- false /*authorizedDatabases*/,
- true /*useListDatabsesForAllTenants*/);
- } else {
- databasesArray = getClient()->getDatabaseInfos(BSONObj(), true /* nameOnly */);
- }
+ const bool multiTenancyAndRequireTenantIdEnabled = gMultitenancySupport &&
+ serverGlobalParams.featureCompatibility.isVersionInitialized() &&
+ gFeatureFlagRequireTenantID.isEnabled(serverGlobalParams.featureCompatibility);
+
+ databasesArray = getClient()->getDatabaseInfos(
+ BSONObj(),
+ true /* nameOnly */,
+ false /*authorizedDatabases*/,
+ multiTenancyAndRequireTenantIdEnabled /*useListDatabsesForAllTenants*/);
+ size_t idxToInsertNextAdmin = 0;
for (const auto& dbBSON : databasesArray) {
if (!dbBSON.hasField("name")) {
LOGV2_DEBUG(21055,
@@ -164,8 +167,13 @@ BaseCloner::AfterStageBehavior AllDatabaseCloner::listDatabasesStage() {
"db"_attr = dbBSON);
continue;
}
- const auto& dbName = dbBSON["name"].str();
- if (dbName == "local") {
+
+ boost::optional<TenantId> tenantId = dbBSON.hasField("tenantId")
+ ? boost::make_optional<TenantId>(TenantId::parseFromBSON(dbBSON["tenantId"]))
+ : boost::none;
+ DatabaseName dbName = DatabaseNameUtil::deserialize(tenantId, dbBSON["name"].str());
+
+ if (dbName.db() == "local") {
LOGV2_DEBUG(21056,
1,
"Excluding database from the 'listDatabases' response: {db}",
@@ -174,15 +182,37 @@ BaseCloner::AfterStageBehavior AllDatabaseCloner::listDatabasesStage() {
continue;
} else {
_databases.emplace_back(dbName);
- // Make sure "admin" comes first.
- if (dbName == "admin" && _databases.size() > 1) {
- std::swap(_databases.front(), _databases.back());
+
+ // Put admin dbs in the front of the vector.
+ if (dbName.db() == "admin" && _databases.size() > 1) {
+ std::iter_swap(_databases.begin() + idxToInsertNextAdmin, _databases.end() - 1);
+ idxToInsertNextAdmin++;
}
}
}
+
+ // Ensure the global admin comes first. We inserted all admin dbs at the front of '_databases',
+ // find the global admin and move it to the front.
+ for (auto i = _databases.begin(); size_t(i - _databases.begin()) != idxToInsertNextAdmin; ++i) {
+ if (!(*i).tenantId()) {
+ std::iter_swap(_databases.begin(), i);
+ break;
+ }
+ }
+
+
return kContinueNormally;
}
+void AllDatabaseCloner::handleAdminDbNotValid(const Status& errorStatus) {
+ LOGV2_DEBUG(21059,
+ 1,
+ "Validation failed on 'admin' db due to {error}",
+ "Validation failed on 'admin' db",
+ "error"_attr = errorStatus);
+ setSyncFailedStatus(errorStatus);
+}
+
void AllDatabaseCloner::postStage() {
{
stdx::lock_guard<Latch> lk(_mutex);
@@ -191,10 +221,22 @@ void AllDatabaseCloner::postStage() {
_stats.databaseStats.reserve(_databases.size());
for (const auto& dbName : _databases) {
_stats.databaseStats.emplace_back();
- _stats.databaseStats.back().dbname = dbName;
+ _stats.databaseStats.back().dbname = dbName.toStringWithTenantId();
+
+ auto db = DatabaseNameUtil::serialize(dbName);
+
+ BSONObj cmdObj = BSON("dbStats" << 1);
+ BSONObjBuilder b(cmdObj);
+ if (gMultitenancySupport &&
+ serverGlobalParams.featureCompatibility.isVersionInitialized() &&
+ gFeatureFlagRequireTenantID.isEnabled(serverGlobalParams.featureCompatibility) &&
+ dbName.tenantId()) {
+ dbName.tenantId()->serializeToBSON("$tenant", &b);
+ }
BSONObj res;
- getClient()->runCommand(dbName, BSON("dbStats" << 1), res);
+ getClient()->runCommand(db, b.obj(), res);
+
// It is possible for the call to 'dbStats' to fail if the sync source contains invalid
// views. We should not fail initial sync in this case due to the situation where the
// replica set may have lost majority availability and therefore have no access to a
@@ -212,10 +254,13 @@ void AllDatabaseCloner::postStage() {
}
}
}
+ bool foundAuthSchemaDoc = false;
+ bool foundUser = false;
for (const auto& dbName : _databases) {
{
stdx::lock_guard<Latch> lk(_mutex);
- _currentDatabaseCloner = std::make_unique<DatabaseCloner>(dbName,
+ // TODO SERVER-70430: Pass in dbName directly to the constructor.
+ _currentDatabaseCloner = std::make_unique<DatabaseCloner>(dbName.toStringWithTenantId(),
getSharedData(),
getSource(),
getClient(),
@@ -242,10 +287,9 @@ void AllDatabaseCloner::postStage() {
setSyncFailedStatus(dbStatus);
return;
}
- if (StringData(dbName).equalCaseInsensitive("admin")) {
+ if (!foundUser && StringData(dbName.db()).equalCaseInsensitive("admin")) {
LOGV2_DEBUG(21058, 1, "Finished the 'admin' db, now validating it");
// Do special checks for the admin database because of auth. collections.
- auto adminStatus = Status(ErrorCodes::NotYetInitialized, "");
{
OperationContext* opCtx = cc().getOperationContext();
ServiceContext::UniqueOperationContext opCtxPtr;
@@ -253,15 +297,41 @@ void AllDatabaseCloner::postStage() {
opCtxPtr = cc().makeOperationContext();
opCtx = opCtxPtr.get();
}
- adminStatus = getStorageInterface()->isAdminDbValid(opCtx);
+ auto authzManager = AuthorizationManager::get(opCtx->getServiceContext());
+
+ // Check if global admin has a valid auth schema version document.
+ if (!dbName.tenantId() && !foundAuthSchemaDoc) {
+ auto status =
+ authzManager->hasValidAuthSchemaVersionDocumentForInitialSync(opCtx);
+ if (status == ErrorCodes::AuthSchemaIncompatible) {
+ handleAdminDbNotValid(status);
+ return;
+ }
+
+ foundAuthSchemaDoc = status.isOK();
+ }
+
+ // We haven't yet found a user document, look for one. In a multitenant environment,
+ // user documents will live in tenant-specific admin collections.
+ foundUser = authzManager->hasUser(opCtx, dbName.tenantId());
}
- if (!adminStatus.isOK()) {
- LOGV2_DEBUG(21059,
- 1,
- "Validation failed on 'admin' db due to {error}",
- "Validation failed on 'admin' db",
- "error"_attr = adminStatus);
- setSyncFailedStatus(adminStatus);
+
+ // The global admin db sorts first even in a multitenant environemnt, so if we've found
+ // a user and haven't found an auth schema doc, we can fail early.
+ if (!foundAuthSchemaDoc && foundUser) {
+ std::string msg = str::stream()
+ << "During initial sync, found documents in "
+ << AuthorizationManager::usersCollectionNamespace.ns()
+ << " but could not find an auth schema version document in "
+ << AuthorizationManager::versionCollectionNamespace.ns() << ". "
+ << "This indicates that the primary of this replica set was not "
+ "successfully "
+ "upgraded to schema version "
+ << AuthorizationManager::schemaVersion26Final
+ << ", which is the minimum supported schema version in this version of "
+ "MongoDB";
+ auto errorStatus = Status(ErrorCodes::AuthSchemaIncompatible, msg);
+ handleAdminDbNotValid(errorStatus);
return;
}
}
diff --git a/src/mongo/db/repl/all_database_cloner.h b/src/mongo/db/repl/all_database_cloner.h
index d538af3869c..4f747545000 100644
--- a/src/mongo/db/repl/all_database_cloner.h
+++ b/src/mongo/db/repl/all_database_cloner.h
@@ -127,6 +127,8 @@ private:
return "admin db: { " + stage->getName() + ": 1 }";
}
+ void handleAdminDbNotValid(const Status& errorStatus);
+
// All member variables are labeled with one of the following codes indicating the
// synchronization rules for accessing them.
//
@@ -139,7 +141,7 @@ private:
ConnectStage _connectStage; // (R)
ConnectStage _getInitialSyncIdStage; // (R)
ClonerStage<AllDatabaseCloner> _listDatabasesStage; // (R)
- std::vector<std::string> _databases; // (X)
+ std::vector<DatabaseName> _databases; // (X)
std::unique_ptr<DatabaseCloner> _currentDatabaseCloner; // (MX)
Stats _stats; // (MX)
};
diff --git a/src/mongo/db/repl/all_database_cloner_test.cpp b/src/mongo/db/repl/all_database_cloner_test.cpp
index 99bc4d30e4a..78770847aa6 100644
--- a/src/mongo/db/repl/all_database_cloner_test.cpp
+++ b/src/mongo/db/repl/all_database_cloner_test.cpp
@@ -37,6 +37,7 @@
#include "mongo/db/repl/storage_interface_mock.h"
#include "mongo/db/service_context_test_fixture.h"
#include "mongo/dbtests/mock/mock_dbclient_connection.h"
+#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/logv2/log.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/clock_source_mock.h"
@@ -61,11 +62,92 @@ protected:
_dbWorkThreadPool.get());
}
- std::vector<std::string> getDatabasesFromCloner(AllDatabaseCloner* cloner) {
+ std::vector<DatabaseName> getDatabasesFromCloner(AllDatabaseCloner* cloner) {
return cloner->_databases;
}
};
+TEST_F(AllDatabaseClonerTest, ListDatabaseStageSortsAdminCorrectlyGlobalAdminBeforeTenantAdmin) {
+ RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true);
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true);
+ auto atid = TenantId(OID::gen());
+ auto btid = TenantId(OID::gen());
+ // global Admin before tenant secific admins.
+ _mockServer->setCommandReply("listDatabasesForAllTenants",
+ BSON("ok" << 1 << "databases"
+ << BSON_ARRAY(BSON("name"
+ << "aab"
+ << "tenantId" << btid)
+ << BSON("name"
+ << "a"
+ << "tenantId" << atid)
+ << BSON("name"
+ << "admin"
+ << "tenantId" << atid)
+ << BSON("name"
+ << "admin"
+ << "tenantId" << btid)
+ << BSON("name"
+ << "admin"))));
+
+ auto cloner = makeAllDatabaseCloner();
+ cloner->setStopAfterStage_forTest("listDatabases");
+
+ ASSERT_OK(cloner->run());
+ auto databases = getDatabasesFromCloner(cloner.get());
+
+ ASSERT_EQUALS(5u, databases.size());
+ ASSERT_EQUALS("admin", databases[0].db());
+ ASSERT(!databases[0].tenantId());
+ ASSERT_EQUALS("admin", databases[1].db());
+ ASSERT(databases[1].tenantId());
+ ASSERT_EQUALS("admin", databases[2].db());
+ ASSERT(databases[2].tenantId());
+ ASSERT_EQUALS("a", databases[3].db());
+ ASSERT_EQUALS("aab", databases[4].db());
+}
+
+TEST_F(AllDatabaseClonerTest, ListDatabaseStageSortsAdminCorrectlyTenantAdminSetToFirst) {
+ RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true);
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true);
+ auto atid = TenantId(OID::gen());
+ auto btid = TenantId(OID::gen());
+ // tenant specific admin is the first database.
+ _mockServer->setCommandReply("listDatabasesForAllTenants",
+ BSON("ok" << 1 << "databases"
+ << BSON_ARRAY(BSON("name"
+ << "admin"
+ << "tenantId" << btid)
+ << BSON("name"
+ << "a"
+ << "tenantId" << atid)
+ << BSON("name"
+ << "admin")
+ << BSON("name"
+ << "admin"
+ << "tenantId" << atid)
+ << BSON("name"
+ << "aab"
+ << "tenantId" << btid))));
+
+ auto cloner = makeAllDatabaseCloner();
+ cloner->setStopAfterStage_forTest("listDatabases");
+
+ ASSERT_OK(cloner->run());
+ auto databases = getDatabasesFromCloner(cloner.get());
+
+ ASSERT_EQUALS(5u, databases.size());
+ ASSERT_EQUALS("admin", databases[0].db());
+ ASSERT(!databases[0].tenantId());
+ ASSERT_EQUALS("admin", databases[1].db());
+ ASSERT(databases[1].tenantId());
+ ASSERT_EQUALS("admin", databases[2].db());
+ ASSERT(databases[2].tenantId());
+ ASSERT_EQUALS("a", databases[3].db());
+ ASSERT_EQUALS("aab", databases[4].db());
+}
+
+
TEST_F(AllDatabaseClonerTest, RetriesConnect) {
// Bring the server down.
_mockServer->shutdown();
@@ -413,7 +495,7 @@ TEST_F(AllDatabaseClonerTest, AdminIsSetToFirst) {
ASSERT_OK(cloner->run());
auto databases = getDatabasesFromCloner(cloner.get());
- ASSERT_EQUALS("admin", databases[0]);
+ ASSERT_EQUALS("admin", databases[0].db());
_mockServer->setCommandReply(
"listDatabases", fromjson("{ok:1, databases:[{name:'admin'}, {name:'a'}, {name:'b'}]}"));
@@ -423,7 +505,7 @@ TEST_F(AllDatabaseClonerTest, AdminIsSetToFirst) {
ASSERT_OK(cloner->run());
databases = getDatabasesFromCloner(cloner.get());
- ASSERT_EQUALS("admin", databases[0]);
+ ASSERT_EQUALS("admin", databases[0].db());
}
TEST_F(AllDatabaseClonerTest, LocalIsRemoved) {
@@ -436,8 +518,8 @@ TEST_F(AllDatabaseClonerTest, LocalIsRemoved) {
auto databases = getDatabasesFromCloner(cloner.get());
ASSERT_EQUALS(2u, databases.size());
- ASSERT_EQUALS("a", databases[0]);
- ASSERT_EQUALS("aab", databases[1]);
+ ASSERT_EQUALS("a", databases[0].db());
+ ASSERT_EQUALS("aab", databases[1].db());
_mockServer->setCommandReply(
"listDatabases", fromjson("{ok:1, databases:[{name:'local'}, {name:'a'}, {name:'b'}]}"));
@@ -448,17 +530,11 @@ TEST_F(AllDatabaseClonerTest, LocalIsRemoved) {
databases = getDatabasesFromCloner(cloner.get());
ASSERT_EQUALS(2u, databases.size());
- ASSERT_EQUALS("a", databases[0]);
- ASSERT_EQUALS("b", databases[1]);
+ ASSERT_EQUALS("a", databases[0].db());
+ ASSERT_EQUALS("b", databases[1].db());
}
TEST_F(AllDatabaseClonerTest, DatabaseStats) {
- bool isAdminDbValidFnCalled = false;
- _storageInterface.isAdminDbValidFn = [&isAdminDbValidFnCalled](OperationContext* opCtx) {
- isAdminDbValidFnCalled = true;
- return Status::OK();
- };
-
_mockServer->setCommandReply(
"listDatabases", fromjson("{ok:1, databases:[{name:'a'}, {name:'aab'}, {name: 'admin'}]}"));
@@ -492,9 +568,9 @@ TEST_F(AllDatabaseClonerTest, DatabaseStats) {
auto databases = getDatabasesFromCloner(cloner.get());
ASSERT_EQUALS(3u, databases.size());
- ASSERT_EQUALS("admin", databases[0]);
- ASSERT_EQUALS("aab", databases[1]);
- ASSERT_EQUALS("a", databases[2]);
+ ASSERT_EQUALS("admin", databases[0].db());
+ ASSERT_EQUALS("aab", databases[1].db());
+ ASSERT_EQUALS("a", databases[2].db());
auto stats = cloner->getStats();
ASSERT_EQUALS(0, stats.databasesCloned);
@@ -522,7 +598,6 @@ TEST_F(AllDatabaseClonerTest, DatabaseStats) {
// Wait for the failpoint to be reached.
dbClonerBeforeFailPoint->waitForTimesEntered(timesEntered + 1);
-
stats = cloner->getStats();
ASSERT_EQUALS(1, stats.databasesCloned);
ASSERT_EQUALS(3, stats.databaseStats.size());
@@ -535,7 +610,6 @@ TEST_F(AllDatabaseClonerTest, DatabaseStats) {
ASSERT_EQUALS(Date_t(), stats.databaseStats[2].start);
ASSERT_EQUALS(Date_t(), stats.databaseStats[2].end);
_clock.advance(Minutes(1));
- ASSERT(isAdminDbValidFnCalled);
// Allow the cloner to move to the last DB.
timesEntered = dbClonerBeforeFailPoint->setMode(
@@ -574,6 +648,189 @@ TEST_F(AllDatabaseClonerTest, DatabaseStats) {
ASSERT_EQUALS(_clock.now(), stats.databaseStats[2].end);
}
+
+TEST_F(AllDatabaseClonerTest,
+ DatabaseStatsMultitenancySupportAndFeatureFlagRequireTenantIdEnabled) {
+ RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true);
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true);
+
+ auto tid = TenantId(OID::gen());
+ _mockServer->setCommandReply("listDatabasesForAllTenants",
+ BSON("ok" << 1 << "databases"
+ << BSON_ARRAY(BSON("name"
+ << "aab"
+ << "tenantId" << tid)
+ << BSON("name"
+ << "a"
+ << "tenantId" << tid)
+ << BSON("name"
+ << "admin"
+ << "tenantId" << tid)
+ << BSON("name"
+ << "admin")
+ << BSON("name"
+ << "local"
+ << "tenantId" << tid))));
+
+ // Make the DatabaseCloner do nothing
+ _mockServer->setCommandReply("listCollections", createCursorResponse("admin.$cmd", {}));
+ auto cloner = makeAllDatabaseCloner();
+ // Set up the DatabaseCloner to pause so we can check stats.
+ // We need to use two fail points to do this because fail points cannot have their data
+ // modified atomically.
+ auto dbClonerBeforeFailPoint = globalFailPointRegistry().find("hangBeforeClonerStage");
+ auto dbClonerAfterFailPoint = globalFailPointRegistry().find("hangAfterClonerStage");
+ auto timesEntered =
+ dbClonerBeforeFailPoint->setMode(
+ FailPoint::alwaysOn,
+ 0,
+ fromjson(str::stream()
+ << "{cloner: 'DatabaseCloner', stage: 'listCollections', database: 'admin'}"));
+ dbClonerAfterFailPoint->setMode(
+ FailPoint::alwaysOn,
+ 0,
+ fromjson(str::stream()
+ << "{cloner: 'DatabaseCloner', stage: 'listCollections', database: 'admin'}"));
+ _clock.advance(Minutes(1));
+ // Run the cloner in a separate thread.
+ stdx::thread clonerThread([&] {
+ Client::initThread("ClonerRunner");
+ ASSERT_OK(cloner->run());
+ });
+
+ // Wait for the failpoint to be reached
+ dbClonerBeforeFailPoint->waitForTimesEntered(timesEntered + 1);
+ auto databases = getDatabasesFromCloner(cloner.get());
+ // Expect 4 dbs, since "local" should be removed
+ std::string adminWithTenantId = str::stream() << tid.toString() << "_admin";
+ std::string aWithTenantId = str::stream() << tid.toString() << "_a";
+ std::string aabWithTenantId = str::stream() << tid.toString() << "_aab";
+ // Checks admin is first db.
+ ASSERT_EQUALS(4u, databases.size());
+ ASSERT_EQUALS("admin", databases[0].db());
+ ASSERT_EQUALS("admin", databases[1].db());
+ ASSERT_EQUALS("aab", databases[2].db());
+ ASSERT_EQUALS("a", databases[3].db());
+
+ auto stats = cloner->getStats();
+ ASSERT_EQUALS(0, stats.databasesCloned);
+ ASSERT_EQUALS(4, stats.databaseStats.size());
+ ASSERT_EQUALS("admin", stats.databaseStats[0].dbname);
+ ASSERT_EQUALS(adminWithTenantId, stats.databaseStats[1].dbname);
+ ASSERT_EQUALS(aabWithTenantId, stats.databaseStats[2].dbname);
+ ASSERT_EQUALS(aWithTenantId, stats.databaseStats[3].dbname);
+ ASSERT_EQUALS(_clock.now(), stats.databaseStats[0].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[0].end);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[1].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[1].end);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[2].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[2].end);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[3].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[3].end);
+ _clock.advance(Minutes(1));
+
+ // Allow the cloner to move to the next DB.
+ timesEntered = dbClonerBeforeFailPoint->setMode(
+ FailPoint::alwaysOn,
+ 0,
+ fromjson(str::stream() << "{cloner: 'DatabaseCloner', stage: 'listCollections', database: '"
+ << adminWithTenantId << "'}"));
+ dbClonerAfterFailPoint->setMode(
+ FailPoint::alwaysOn,
+ 0,
+ fromjson(str::stream() << "{cloner: 'DatabaseCloner', stage: 'listCollections', database: '"
+ << adminWithTenantId << "'}"));
+
+ // Wait for the failpoint to be reached.
+ dbClonerBeforeFailPoint->waitForTimesEntered(timesEntered + 1);
+
+ stats = cloner->getStats();
+ ASSERT_EQUALS(1, stats.databasesCloned);
+ ASSERT_EQUALS(4, stats.databaseStats.size());
+ ASSERT_EQUALS("admin", stats.databaseStats[0].dbname);
+ ASSERT_EQUALS(adminWithTenantId, stats.databaseStats[1].dbname);
+ ASSERT_EQUALS(aabWithTenantId, stats.databaseStats[2].dbname);
+ ASSERT_EQUALS(aWithTenantId, stats.databaseStats[3].dbname);
+ ASSERT_EQUALS(_clock.now(), stats.databaseStats[0].end);
+ ASSERT_EQUALS(_clock.now(), stats.databaseStats[1].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[1].end);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[2].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[2].end);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[3].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[3].end);
+ _clock.advance(Minutes(1));
+
+ // Allow the cloner to move to the tenant admin DB.
+ timesEntered = dbClonerBeforeFailPoint->setMode(
+ FailPoint::alwaysOn,
+ 0,
+ fromjson(str::stream() << "{cloner: 'DatabaseCloner', stage: 'listCollections', database: '"
+ << aabWithTenantId << "'}"));
+ dbClonerAfterFailPoint->setMode(
+ FailPoint::alwaysOn,
+ 0,
+ fromjson(str::stream() << "{cloner: 'DatabaseCloner', stage: 'listCollections', database: '"
+ << aabWithTenantId << "'}"));
+
+ // Wait for the failpoint to be reached.
+ dbClonerBeforeFailPoint->waitForTimesEntered(timesEntered + 1);
+ stats = cloner->getStats();
+ ASSERT_EQUALS(2, stats.databasesCloned);
+ ASSERT_EQUALS(4, stats.databaseStats.size());
+ ASSERT_EQUALS("admin", stats.databaseStats[0].dbname);
+ ASSERT_EQUALS(adminWithTenantId, stats.databaseStats[1].dbname);
+ ASSERT_EQUALS(aabWithTenantId, stats.databaseStats[2].dbname);
+ ASSERT_EQUALS(aWithTenantId, stats.databaseStats[3].dbname);
+ ASSERT_EQUALS(_clock.now(), stats.databaseStats[1].end);
+ ASSERT_EQUALS(_clock.now(), stats.databaseStats[2].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[2].end);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[3].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[3].end);
+ _clock.advance(Minutes(1));
+
+
+ // Allow the cloner to move to the last DB.
+ timesEntered = dbClonerBeforeFailPoint->setMode(
+ FailPoint::alwaysOn,
+ 0,
+ fromjson(str::stream() << "{cloner: 'DatabaseCloner', stage: 'listCollections', database: '"
+ << aWithTenantId << "'}"));
+ dbClonerAfterFailPoint->setMode(
+ FailPoint::alwaysOn,
+ 0,
+ fromjson(str::stream() << "{cloner: 'DatabaseCloner', stage: 'listCollections', database: '"
+ << aWithTenantId << "'}"));
+
+ // Wait for the failpoint to be reached.
+ dbClonerBeforeFailPoint->waitForTimesEntered(timesEntered + 1);
+
+ stats = cloner->getStats();
+ ASSERT_EQUALS(3, stats.databasesCloned);
+ ASSERT_EQUALS(4, stats.databaseStats.size());
+ ASSERT_EQUALS("admin", stats.databaseStats[0].dbname);
+ ASSERT_EQUALS(adminWithTenantId, stats.databaseStats[1].dbname);
+ ASSERT_EQUALS(aabWithTenantId, stats.databaseStats[2].dbname);
+ ASSERT_EQUALS(aWithTenantId, stats.databaseStats[3].dbname);
+ ASSERT_EQUALS(_clock.now(), stats.databaseStats[2].end);
+ ASSERT_EQUALS(_clock.now(), stats.databaseStats[3].start);
+ ASSERT_EQUALS(Date_t(), stats.databaseStats[3].end);
+ _clock.advance(Minutes(1));
+
+ // Allow the cloner to finish
+ dbClonerBeforeFailPoint->setMode(FailPoint::off, 0);
+ dbClonerAfterFailPoint->setMode(FailPoint::off, 0);
+ clonerThread.join();
+
+ stats = cloner->getStats();
+ ASSERT_EQUALS(4, stats.databasesCloned);
+ ASSERT_EQUALS("admin", stats.databaseStats[0].dbname);
+ ASSERT_EQUALS(adminWithTenantId, stats.databaseStats[1].dbname);
+ ASSERT_EQUALS(aabWithTenantId, stats.databaseStats[2].dbname);
+ ASSERT_EQUALS(aWithTenantId, stats.databaseStats[3].dbname);
+ ASSERT_EQUALS(_clock.now(), stats.databaseStats[3].end);
+}
+
+
TEST_F(AllDatabaseClonerTest, FailsOnListCollectionsOnOnlyDatabase) {
_mockServer->setCommandReply("listDatabases", fromjson("{ok:1, databases:[{name:'a'}]}"));
_mockServer->setCommandReply("listCollections", Status{ErrorCodes::NoSuchKey, "fake"});
diff --git a/src/mongo/db/repl/storage_interface.h b/src/mongo/db/repl/storage_interface.h
index aff0634f035..44302db357f 100644
--- a/src/mongo/db/repl/storage_interface.h
+++ b/src/mongo/db/repl/storage_interface.h
@@ -213,11 +213,6 @@ public:
virtual Status dropReplicatedDatabases(OperationContext* opCtx) = 0;
/**
- * Validates that the admin database is valid during initial sync.
- */
- virtual Status isAdminDbValid(OperationContext* opCtx) = 0;
-
- /**
* Finds at most "limit" documents returned by a collection or index scan on the collection in
* the requested direction.
* The documents returned will be copied and buffered. No cursors on the underlying collection
diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp
index b1697942c24..98dd971cb8d 100644
--- a/src/mongo/db/repl/storage_interface_impl.cpp
+++ b/src/mongo/db/repl/storage_interface_impl.cpp
@@ -1422,64 +1422,6 @@ boost::optional<Timestamp> StorageInterfaceImpl::getRecoveryTimestamp(
return serviceCtx->getStorageEngine()->getRecoveryTimestamp();
}
-Status StorageInterfaceImpl::isAdminDbValid(OperationContext* opCtx) {
- AutoGetDb autoDB(opCtx, DatabaseName(boost::none, "admin"), MODE_X);
- auto adminDb = autoDB.getDb();
- if (!adminDb) {
- return Status::OK();
- }
-
- auto catalog = CollectionCatalog::get(opCtx);
- CollectionPtr usersCollection =
- catalog->lookupCollectionByNamespace(opCtx, AuthorizationManager::usersCollectionNamespace);
- const bool hasUsers =
- usersCollection && !Helpers::findOne(opCtx, usersCollection, BSONObj()).isNull();
- CollectionPtr adminVersionCollection = catalog->lookupCollectionByNamespace(
- opCtx, AuthorizationManager::versionCollectionNamespace);
- BSONObj authSchemaVersionDocument;
- if (!adminVersionCollection ||
- !Helpers::findOne(opCtx,
- adminVersionCollection,
- AuthorizationManager::versionDocumentQuery,
- authSchemaVersionDocument)) {
- if (!hasUsers) {
- // It's OK to have no auth version document if there are no user documents.
- return Status::OK();
- }
- std::string msg = str::stream()
- << "During initial sync, found documents in "
- << AuthorizationManager::usersCollectionNamespace.ns()
- << " but could not find an auth schema version document in "
- << AuthorizationManager::versionCollectionNamespace.ns() << ". "
- << "This indicates that the primary of this replica set was not successfully "
- "upgraded to schema version "
- << AuthorizationManager::schemaVersion26Final
- << ", which is the minimum supported schema version in this version of MongoDB";
- return {ErrorCodes::AuthSchemaIncompatible, msg};
- }
- long long foundSchemaVersion;
- Status status = bsonExtractIntegerField(authSchemaVersionDocument,
- AuthorizationManager::schemaVersionFieldName,
- &foundSchemaVersion);
- if (!status.isOK()) {
- std::string msg = str::stream()
- << "During initial sync, found malformed auth schema version document: "
- << status.toString() << "; document: " << authSchemaVersionDocument;
- return {ErrorCodes::AuthSchemaIncompatible, msg};
- }
- if ((foundSchemaVersion != AuthorizationManager::schemaVersion26Final) &&
- (foundSchemaVersion != AuthorizationManager::schemaVersion28SCRAM)) {
- std::string msg = str::stream()
- << "During initial sync, found auth schema version " << foundSchemaVersion
- << ", but this version of MongoDB only supports schema versions "
- << AuthorizationManager::schemaVersion26Final << " and "
- << AuthorizationManager::schemaVersion28SCRAM;
- return {ErrorCodes::AuthSchemaIncompatible, msg};
- }
-
- return Status::OK();
-}
-
void StorageInterfaceImpl::waitForAllEarlierOplogWritesToBeVisible(OperationContext* opCtx,
bool primaryOnly) {
// Waiting for oplog writes to be visible in the oplog does not use any storage engine resources
diff --git a/src/mongo/db/repl/storage_interface_impl.h b/src/mongo/db/repl/storage_interface_impl.h
index 3dce1e53c61..c5d2f7ad5b2 100644
--- a/src/mongo/db/repl/storage_interface_impl.h
+++ b/src/mongo/db/repl/storage_interface_impl.h
@@ -190,11 +190,6 @@ public:
Timestamp getAllDurableTimestamp(ServiceContext* serviceCtx) const override;
- /**
- * Checks that the "admin" database contains a supported version of the auth data schema.
- */
- Status isAdminDbValid(OperationContext* opCtx) override;
-
void waitForAllEarlierOplogWritesToBeVisible(OperationContext* opCtx,
bool primaryOnly) override;
void oplogDiskLocRegister(OperationContext* opCtx,
diff --git a/src/mongo/db/repl/storage_interface_mock.h b/src/mongo/db/repl/storage_interface_mock.h
index 747f82776c5..59c470da432 100644
--- a/src/mongo/db/repl/storage_interface_mock.h
+++ b/src/mongo/db/repl/storage_interface_mock.h
@@ -127,7 +127,6 @@ public:
std::size_t)>;
using PutSingletonFn =
std::function<Status(OperationContext*, const NamespaceString&, const TimestampedBSONObj&)>;
- using IsAdminDbValidFn = std::function<Status(OperationContext*)>;
using GetCollectionUUIDFn =
std::function<StatusWith<UUID>(OperationContext*, const NamespaceString&)>;
@@ -346,10 +345,6 @@ public:
Timestamp getAllDurableTimestamp(ServiceContext* serviceCtx) const override;
- Status isAdminDbValid(OperationContext* opCtx) override {
- return isAdminDbValidFn(opCtx);
- };
-
void waitForAllEarlierOplogWritesToBeVisible(OperationContext* opCtx,
bool primaryOnly) override {
return;
@@ -437,9 +432,7 @@ public:
[](OperationContext* opCtx, const NamespaceString& nss, const TimestampedBSONObj& update) {
return Status{ErrorCodes::IllegalOperation, "PutSingletonFn not implemented."};
};
- IsAdminDbValidFn isAdminDbValidFn = [](OperationContext*) {
- return Status{ErrorCodes::IllegalOperation, "IsAdminDbValidFn not implemented."};
- };
+
GetCollectionUUIDFn getCollectionUUIDFn = [](OperationContext* opCtx,
const NamespaceString& nss) -> StatusWith<UUID> {
return Status{ErrorCodes::IllegalOperation, "GetCollectionUUIDFn not implemented."};
diff --git a/src/mongo/embedded/embedded_auth_manager.cpp b/src/mongo/embedded/embedded_auth_manager.cpp
index ec2c7dc9184..6604e4a2756 100644
--- a/src/mongo/embedded/embedded_auth_manager.cpp
+++ b/src/mongo/embedded/embedded_auth_manager.cpp
@@ -66,6 +66,10 @@ public:
UASSERT_NOT_IMPLEMENTED;
}
+ Status hasValidAuthSchemaVersionDocumentForInitialSync(OperationContext* opCtx) override {
+ UASSERT_NOT_IMPLEMENTED;
+ }
+
bool hasAnyPrivilegeDocuments(OperationContext*) override {
UASSERT_NOT_IMPLEMENTED;
}
@@ -74,6 +78,10 @@ public:
UASSERT_NOT_IMPLEMENTED;
}
+ bool hasUser(OperationContext* opCtx, const boost::optional<TenantId>& tenantId) override {
+ UASSERT_NOT_IMPLEMENTED;
+ }
+
Status rolesExist(OperationContext*, const std::vector<RoleName>&) override {
UASSERT_NOT_IMPLEMENTED;
}