From 5c1c3e6930a5acefe1a1fbe2763db8e435167e26 Mon Sep 17 00:00:00 2001 From: Cheahuychou Mao Date: Wed, 6 Jan 2021 19:39:39 +0000 Subject: SERVER-53680 Use IDL for KeysCollectionDocument --- src/mongo/db/SConscript | 3 +- src/mongo/db/key_generator.cpp | 1 + src/mongo/db/key_generator_update_test.cpp | 17 +-- src/mongo/db/keys_collection_cache.cpp | 2 +- src/mongo/db/keys_collection_cache.h | 2 +- src/mongo/db/keys_collection_cache_test.cpp | 23 ++-- src/mongo/db/keys_collection_client.h | 2 +- src/mongo/db/keys_collection_client_direct.cpp | 21 ++-- src/mongo/db/keys_collection_client_sharded.cpp | 6 +- src/mongo/db/keys_collection_document.cpp | 118 --------------------- src/mongo/db/keys_collection_document.h | 88 --------------- src/mongo/db/keys_collection_document.idl | 64 +++++++++++ src/mongo/db/keys_collection_document_test.cpp | 38 ++++--- src/mongo/db/keys_collection_manager.h | 2 +- .../db/keys_collection_manager_sharding_test.cpp | 23 ++-- src/mongo/db/logical_time.cpp | 11 +- src/mongo/db/logical_time.h | 6 ++ src/mongo/db/namespace_string.cpp | 3 + src/mongo/db/namespace_string.h | 3 + .../db/s/config/config_server_test_fixture.cpp | 7 +- src/mongo/s/catalog/sharding_catalog_client.h | 2 +- .../s/catalog/sharding_catalog_client_impl.cpp | 13 +-- .../s/catalog/sharding_catalog_client_test.cpp | 5 +- 23 files changed, 178 insertions(+), 282 deletions(-) delete mode 100644 src/mongo/db/keys_collection_document.cpp delete mode 100644 src/mongo/db/keys_collection_document.h create mode 100644 src/mongo/db/keys_collection_document.idl diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 6b70018a274..cac09b14c61 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1640,7 +1640,7 @@ env.Library( env.Library( target='keys_collection_document', source=[ - 'keys_collection_document.cpp', + 'keys_collection_document.idl', ], LIBDEPS=[ 'logical_time', @@ -1673,6 +1673,7 @@ env.Library( '$BUILD_DIR/mongo/s/catalog/sharding_catalog_client', 'keys_collection_document', 'logical_time', + 'namespace_string', ], ) diff --git a/src/mongo/db/key_generator.cpp b/src/mongo/db/key_generator.cpp index 3e899fd888b..eac600c5fd1 100644 --- a/src/mongo/db/key_generator.cpp +++ b/src/mongo/db/key_generator.cpp @@ -34,6 +34,7 @@ #include "mongo/client/read_preference.h" #include "mongo/db/keys_collection_client.h" #include "mongo/db/operation_context.h" +#include "mongo/db/time_proof_service.h" #include "mongo/db/vector_clock.h" #include "mongo/s/client/shard_registry.h" #include "mongo/util/fail_point.h" diff --git a/src/mongo/db/key_generator_update_test.cpp b/src/mongo/db/key_generator_update_test.cpp index 9590d3fda9a..d794e0caab6 100644 --- a/src/mongo/db/key_generator_update_test.cpp +++ b/src/mongo/db/key_generator_update_test.cpp @@ -36,8 +36,9 @@ #include "mongo/db/jsobj.h" #include "mongo/db/key_generator.h" #include "mongo/db/keys_collection_client_sharded.h" -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" #include "mongo/db/s/config/config_server_test_fixture.h" +#include "mongo/db/time_proof_service.h" #include "mongo/db/vector_clock_mutable.h" #include "mongo/unittest/unittest.h" #include "mongo/util/clock_source_mock.h" @@ -111,7 +112,7 @@ TEST_F(KeyGeneratorUpdateTest, ShouldCreateAnotherKeyIfOnlyOneKeyExists) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); { auto allKeys = getKeys(operationContext()); @@ -158,12 +159,12 @@ TEST_F(KeyGeneratorUpdateTest, ShouldCreateAnotherKeyIfNoValidKeyAfterCurrent) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey2.toBSON())); { auto allKeys = getKeys(operationContext()); @@ -241,12 +242,12 @@ TEST_F(KeyGeneratorUpdateTest, ShouldCreate2KeysIfAllKeysAreExpired) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey2.toBSON())); { auto allKeys = getKeys(operationContext()); @@ -339,12 +340,12 @@ TEST_F(KeyGeneratorUpdateTest, ShouldNotCreateNewKeyIfThereAre2UnexpiredKeys) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey2.toBSON())); { auto allKeys = getKeys(operationContext()); diff --git a/src/mongo/db/keys_collection_cache.cpp b/src/mongo/db/keys_collection_cache.cpp index 0e57d6b091a..5cf62f8da12 100644 --- a/src/mongo/db/keys_collection_cache.cpp +++ b/src/mongo/db/keys_collection_cache.cpp @@ -32,7 +32,7 @@ #include "mongo/db/keys_collection_cache.h" #include "mongo/db/keys_collection_client.h" -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/util/str.h" diff --git a/src/mongo/db/keys_collection_cache.h b/src/mongo/db/keys_collection_cache.h index 61989d6ae5b..17c532b72fa 100644 --- a/src/mongo/db/keys_collection_cache.h +++ b/src/mongo/db/keys_collection_cache.h @@ -32,7 +32,7 @@ #include #include "mongo/base/status_with.h" -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" #include "mongo/db/operation_context.h" #include "mongo/platform/mutex.h" diff --git a/src/mongo/db/keys_collection_cache_test.cpp b/src/mongo/db/keys_collection_cache_test.cpp index 51ada59e365..da649b06c70 100644 --- a/src/mongo/db/keys_collection_cache_test.cpp +++ b/src/mongo/db/keys_collection_cache_test.cpp @@ -32,9 +32,10 @@ #include "mongo/db/jsobj.h" #include "mongo/db/keys_collection_cache.h" #include "mongo/db/keys_collection_client_sharded.h" -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" #include "mongo/db/operation_context.h" #include "mongo/db/s/config/config_server_test_fixture.h" +#include "mongo/db/time_proof_service.h" #include "mongo/s/grid.h" #include "mongo/unittest/unittest.h" #include "mongo/util/clock_source_mock.h" @@ -79,7 +80,7 @@ TEST_F(CacheTest, GetKeyShouldReturnCorrectKeyAfterRefresh) { KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); auto refreshStatus = cache.refresh(operationContext()); ASSERT_OK(refreshStatus.getStatus()); @@ -110,7 +111,7 @@ TEST_F(CacheTest, GetKeyShouldReturnErrorIfNoKeyIsValidForGivenTime) { KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); auto refreshStatus = cache.refresh(operationContext()); ASSERT_OK(refreshStatus.getStatus()); @@ -133,17 +134,17 @@ TEST_F(CacheTest, GetKeyShouldReturnOldestKeyPossible) { KeysCollectionDocument origKey0( 0, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(100, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey0.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey0.toBSON())); KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey2.toBSON())); auto refreshStatus = cache.refresh(operationContext()); ASSERT_OK(refreshStatus.getStatus()); @@ -174,7 +175,7 @@ TEST_F(CacheTest, RefreshShouldNotGetKeysForOtherPurpose) { KeysCollectionDocument origKey0( 0, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(100, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey0.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey0.toBSON())); { auto refreshStatus = cache.refresh(operationContext()); @@ -187,7 +188,7 @@ TEST_F(CacheTest, RefreshShouldNotGetKeysForOtherPurpose) { KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); { auto refreshStatus = cache.refresh(operationContext()); @@ -218,7 +219,7 @@ TEST_F(CacheTest, RefreshCanIncrementallyGetNewKeys) { KeysCollectionDocument origKey0( 0, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(100, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey0.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey0.toBSON())); { auto refreshStatus = cache.refresh(operationContext()); @@ -238,12 +239,12 @@ TEST_F(CacheTest, RefreshCanIncrementallyGetNewKeys) { KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey2.toBSON())); { auto refreshStatus = cache.refresh(operationContext()); diff --git a/src/mongo/db/keys_collection_client.h b/src/mongo/db/keys_collection_client.h index debff147f53..a805300da2b 100644 --- a/src/mongo/db/keys_collection_client.h +++ b/src/mongo/db/keys_collection_client.h @@ -33,7 +33,7 @@ #include "mongo/base/status_with.h" #include "mongo/base/string_data.h" -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" namespace mongo { diff --git a/src/mongo/db/keys_collection_client_direct.cpp b/src/mongo/db/keys_collection_client_direct.cpp index 9fa9c6a3560..4b90c8bb23f 100644 --- a/src/mongo/db/keys_collection_client_direct.cpp +++ b/src/mongo/db/keys_collection_client_direct.cpp @@ -40,7 +40,7 @@ #include "mongo/bson/util/bson_extract.h" #include "mongo/client/read_preference.h" #include "mongo/db/dbdirectclient.h" -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" #include "mongo/db/logical_time.h" #include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" @@ -91,7 +91,7 @@ StatusWith> KeysCollectionClientDirect::getN auto findStatus = _query(opCtx, ReadPreferenceSetting(ReadPreference::Nearest, TagSet{}), readConcern, - KeysCollectionDocument::ConfigNS, + NamespaceString::kKeysCollectionNamespace, queryBuilder.obj(), BSON("expiresAt" << 1), boost::none); @@ -103,12 +103,13 @@ StatusWith> KeysCollectionClientDirect::getN const auto& keyDocs = findStatus.getValue().docs; std::vector keys; for (auto&& keyDoc : keyDocs) { - auto parseStatus = KeysCollectionDocument::fromBSON(keyDoc); - if (!parseStatus.isOK()) { - return parseStatus.getStatus(); + KeysCollectionDocument key; + try { + key = KeysCollectionDocument::parse(IDLParserErrorContext("keyDoc"), keyDoc); + } catch (...) { + return exceptionToStatus(); } - - keys.push_back(std::move(parseStatus.getValue())); + keys.push_back(std::move(key)); } return keys; @@ -173,8 +174,10 @@ Status KeysCollectionClientDirect::_insert(OperationContext* opCtx, } Status KeysCollectionClientDirect::insertNewKey(OperationContext* opCtx, const BSONObj& doc) { - return _insert( - opCtx, KeysCollectionDocument::ConfigNS, doc, ShardingCatalogClient::kMajorityWriteConcern); + return _insert(opCtx, + NamespaceString::kKeysCollectionNamespace, + doc, + ShardingCatalogClient::kMajorityWriteConcern); } } // namespace mongo diff --git a/src/mongo/db/keys_collection_client_sharded.cpp b/src/mongo/db/keys_collection_client_sharded.cpp index 5a6a37bd210..08f56481b19 100644 --- a/src/mongo/db/keys_collection_client_sharded.cpp +++ b/src/mongo/db/keys_collection_client_sharded.cpp @@ -50,8 +50,10 @@ StatusWith> KeysCollectionClientSharded::get } Status KeysCollectionClientSharded::insertNewKey(OperationContext* opCtx, const BSONObj& doc) { - return _catalogClient->insertConfigDocument( - opCtx, KeysCollectionDocument::ConfigNS, doc, ShardingCatalogClient::kMajorityWriteConcern); + return _catalogClient->insertConfigDocument(opCtx, + NamespaceString::kKeysCollectionNamespace, + doc, + ShardingCatalogClient::kMajorityWriteConcern); } } // namespace mongo diff --git a/src/mongo/db/keys_collection_document.cpp b/src/mongo/db/keys_collection_document.cpp deleted file mode 100644 index d8a83ea22cc..00000000000 --- a/src/mongo/db/keys_collection_document.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * . - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#include "mongo/platform/basic.h" - -#include "mongo/db/keys_collection_document.h" - -#include "mongo/base/status.h" -#include "mongo/bson/bsonobjbuilder.h" -#include "mongo/bson/util/bson_extract.h" -#include "mongo/db/logical_time.h" -#include "mongo/db/time_proof_service.h" - -namespace mongo { - -namespace { - -const char kKeyIdFieldName[] = "_id"; -const char kPurposeFieldName[] = "purpose"; -const char kKeyFieldName[] = "key"; -const char kExpiresAtFieldName[] = "expiresAt"; - -} // namespace - -const NamespaceString KeysCollectionDocument::ConfigNS("admin.system.keys"); - -StatusWith KeysCollectionDocument::fromBSON(const BSONObj& source) { - long long keyId; - Status status = bsonExtractIntegerField(source, kKeyIdFieldName, &keyId); - if (!status.isOK()) { - return status; - } - - std::string purpose; - status = bsonExtractStringField(source, kPurposeFieldName, &purpose); - if (!status.isOK()) { - return status; - } - - // Extract BinData type signature hash and construct a SHA1Block instance from it. - BSONElement keyElem; - status = bsonExtractTypedField(source, kKeyFieldName, BinData, &keyElem); - if (!status.isOK()) { - return status; - } - - int hashLength = 0; - auto rawBinSignature = keyElem.binData(hashLength); - BSONBinData proofBinData(rawBinSignature, hashLength, keyElem.binDataType()); - auto keyStatus = SHA1Block::fromBinData(proofBinData); - if (!keyStatus.isOK()) { - return keyStatus.getStatus(); - } - - Timestamp ts; - status = bsonExtractTimestampField(source, kExpiresAtFieldName, &ts); - if (!status.isOK()) { - return status; - } - - return KeysCollectionDocument( - keyId, std::move(purpose), std::move(keyStatus.getValue()), LogicalTime(ts)); -} - -BSONObj KeysCollectionDocument::toBSON() const { - BSONObjBuilder builder; - - builder.append(kKeyIdFieldName, _keyId); - builder.append(kPurposeFieldName, _purpose); - _key.appendAsBinData(builder, kKeyFieldName); - _expiresAt.asTimestamp().append(builder.bb(), kExpiresAtFieldName); - - return builder.obj(); -} - -long long KeysCollectionDocument::getKeyId() const { - return _keyId; -} - -const std::string& KeysCollectionDocument::getPurpose() const { - return _purpose; -} - -const TimeProofService::Key& KeysCollectionDocument::getKey() const { - return _key; -} - -const LogicalTime& KeysCollectionDocument::getExpiresAt() const { - return _expiresAt; -} - -} // namespace mongo diff --git a/src/mongo/db/keys_collection_document.h b/src/mongo/db/keys_collection_document.h deleted file mode 100644 index 802d131d939..00000000000 --- a/src/mongo/db/keys_collection_document.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (C) 2018-present MongoDB, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the Server Side Public License, version 1, - * as published by MongoDB, Inc. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Server Side Public License for more details. - * - * You should have received a copy of the Server Side Public License - * along with this program. If not, see - * . - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the Server Side Public License in all respects for - * all of the code used other than as permitted herein. If you modify file(s) - * with this exception, you may extend this exception to your version of the - * file(s), but you are not obligated to do so. If you do not wish to do so, - * delete this exception statement from your version. If you delete this - * exception statement from all source files in the program, then also delete - * it in the license file. - */ - -#pragma once - -#include "mongo/base/status.h" -#include "mongo/db/logical_time.h" -#include "mongo/db/namespace_string.h" -#include "mongo/db/time_proof_service.h" - -namespace mongo { - -/** - * Class for parsing and serializing a key document stored in admin.system.keys. - * - * Format: - * { - * _id: , - * purpose: 'signLogicalTime', - * key: <20 byte key generated with secure PRNG in BinData>, - * expiresAt: - * } - */ -class KeysCollectionDocument { -public: - static const NamespaceString ConfigNS; - - KeysCollectionDocument(long long keyId, - std::string purpose, - TimeProofService::Key key, - LogicalTime expiresAt) - : _keyId(keyId), - _purpose(std::move(purpose)), - _key(std::move(key)), - _expiresAt(std::move(expiresAt)) {} - - /** - * Parses the key document from BSON. - */ - static StatusWith fromBSON(const BSONObj& source); - - /** - * Serialize the key document as BSON. - */ - BSONObj toBSON() const; - - long long getKeyId() const; - - const std::string& getPurpose() const; - - const TimeProofService::Key& getKey() const; - - const LogicalTime& getExpiresAt() const; - -private: - long long _keyId = 0; - std::string _purpose; - TimeProofService::Key _key; - LogicalTime _expiresAt; -}; - -} // namespace mongo diff --git a/src/mongo/db/keys_collection_document.idl b/src/mongo/db/keys_collection_document.idl new file mode 100644 index 00000000000..dbffd8168a7 --- /dev/null +++ b/src/mongo/db/keys_collection_document.idl @@ -0,0 +1,64 @@ +# Copyright (C) 2021-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# . +# +# As a special exception, the copyright holders give permission to link the +# code of portions of this program with the OpenSSL library under certain +# conditions as described in each individual source file and distribute +# linked combinations including the program with the OpenSSL library. You +# must comply with the Server Side Public License in all respects for +# all of the code used other than as permitted herein. If you modify file(s) +# with this exception, you may extend this exception to your version of the +# file(s), but you are not obligated to do so. If you do not wish to do so, +# delete this exception statement from your version. If you delete this +# exception statement from all source files in the program, then also delete +# it in the license file. +# +global: + cpp_namespace: "mongo" + cpp_includes: + - "mongo/db/logical_time.h" + +imports: + - "mongo/crypto/sha1_block.idl" + - "mongo/idl/basic_types.idl" + +types: + logicalTime: + description: "A MongoDB LogicalTime." + bson_serialization_type: any + cpp_type: LogicalTime + serializer: LogicalTime::serializeToBSON + deserializer: LogicalTime::parseFromBSON + +structs: + keysCollectionDocument: + description: >- + Represents a key document stored in admin.system.keys. + strict: true + fields: + _id: + type: safeInt64 + description: >- + NumberLong representation of the cluster time at which the key was created. + cpp_name: keyId + purpose: + type: string + description: "The purpose of the key." + key: + type: sha1Block + description: "20 byte key generated with secure PRNG in BinData." + expiresAt: + type: logicalTime + description: "The logical time at which the key will expire." diff --git a/src/mongo/db/keys_collection_document_test.cpp b/src/mongo/db/keys_collection_document_test.cpp index dfa6124a212..6be5e268507 100644 --- a/src/mongo/db/keys_collection_document_test.cpp +++ b/src/mongo/db/keys_collection_document_test.cpp @@ -30,7 +30,8 @@ #include "mongo/platform/basic.h" #include "mongo/db/jsobj.h" -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" +#include "mongo/db/time_proof_service.h" #include "mongo/unittest/unittest.h" namespace mongo { @@ -52,10 +53,7 @@ TEST(KeysCollectionDocument, Roundtrip) { KeysCollectionDocument keysCollectionDoc(keyId, purpose, key, expiresAt); auto serializedObj = keysCollectionDoc.toBSON(); - - auto parseStatus = KeysCollectionDocument::fromBSON(serializedObj); - ASSERT_OK(parseStatus.getStatus()); - const auto& parsedKey = parseStatus.getValue(); + auto parsedKey = KeysCollectionDocument::parse(IDLParserErrorContext("keyDoc"), serializedObj); ASSERT_EQ(keyId, parsedKey.getKeyId()); ASSERT_EQ(purpose, parsedKey.getPurpose()); @@ -78,8 +76,10 @@ TEST(KeysCollectionDocument, MissingKeyIdShouldFailToParse) { expiresAt.asTimestamp().append(builder.bb(), "expiresAt"); auto serializedObj = builder.done(); - auto status = KeysCollectionDocument::fromBSON(serializedObj).getStatus(); - ASSERT_EQ(ErrorCodes::NoSuchKey, status); + ASSERT_THROWS_CODE( + KeysCollectionDocument::parse(IDLParserErrorContext("keyDoc"), serializedObj), + AssertionException, + 40414); } TEST(KeysCollectionDocument, MissingPurposeShouldFailToParse) { @@ -92,13 +92,15 @@ TEST(KeysCollectionDocument, MissingPurposeShouldFailToParse) { const auto expiresAt = LogicalTime(Timestamp(100, 200)); BSONObjBuilder builder; - builder.append("keyId", keyId); + builder.append("_id", keyId); builder.append("key", BSONBinData(key.data(), key.size(), BinDataGeneral)); expiresAt.asTimestamp().append(builder.bb(), "expiresAt"); auto serializedObj = builder.done(); - auto status = KeysCollectionDocument::fromBSON(serializedObj).getStatus(); - ASSERT_EQ(ErrorCodes::NoSuchKey, status); + ASSERT_THROWS_CODE( + KeysCollectionDocument::parse(IDLParserErrorContext("keyDoc"), serializedObj), + AssertionException, + 40414); } TEST(KeysCollectionDocument, MissingKeyShouldFailToParse) { @@ -109,13 +111,15 @@ TEST(KeysCollectionDocument, MissingKeyShouldFailToParse) { const auto expiresAt = LogicalTime(Timestamp(100, 200)); BSONObjBuilder builder; - builder.append("keyId", keyId); + builder.append("_id", keyId); builder.append("purpose", purpose); expiresAt.asTimestamp().append(builder.bb(), "expiresAt"); auto serializedObj = builder.done(); - auto status = KeysCollectionDocument::fromBSON(serializedObj).getStatus(); - ASSERT_EQ(ErrorCodes::NoSuchKey, status); + ASSERT_THROWS_CODE( + KeysCollectionDocument::parse(IDLParserErrorContext("keyDoc"), serializedObj), + AssertionException, + 40414); } TEST(KeysCollectionDocument, MissingExpiresAtShouldFailToParse) { @@ -128,13 +132,15 @@ TEST(KeysCollectionDocument, MissingExpiresAtShouldFailToParse) { TimeProofService::Key key(keyHash); BSONObjBuilder builder; - builder.append("keyId", keyId); + builder.append("_id", keyId); builder.append("purpose", purpose); builder.append("key", BSONBinData(key.data(), key.size(), BinDataGeneral)); auto serializedObj = builder.done(); - auto status = KeysCollectionDocument::fromBSON(serializedObj).getStatus(); - ASSERT_EQ(ErrorCodes::NoSuchKey, status); + ASSERT_THROWS_CODE( + KeysCollectionDocument::parse(IDLParserErrorContext("keyDoc"), serializedObj), + AssertionException, + 40414); } } // namespace diff --git a/src/mongo/db/keys_collection_manager.h b/src/mongo/db/keys_collection_manager.h index 0fe5ee6f732..2bb3ca00744 100644 --- a/src/mongo/db/keys_collection_manager.h +++ b/src/mongo/db/keys_collection_manager.h @@ -35,7 +35,7 @@ #include "mongo/base/status_with.h" #include "mongo/db/key_generator.h" #include "mongo/db/keys_collection_cache.h" -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" #include "mongo/db/keys_collection_manager_gen.h" #include "mongo/platform/atomic_word.h" #include "mongo/platform/mutex.h" diff --git a/src/mongo/db/keys_collection_manager_sharding_test.cpp b/src/mongo/db/keys_collection_manager_sharding_test.cpp index fb2cf3d98e0..a1c829b263b 100644 --- a/src/mongo/db/keys_collection_manager_sharding_test.cpp +++ b/src/mongo/db/keys_collection_manager_sharding_test.cpp @@ -31,9 +31,10 @@ #include "mongo/db/jsobj.h" #include "mongo/db/keys_collection_client_sharded.h" -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" #include "mongo/db/keys_collection_manager.h" #include "mongo/db/s/config/config_server_test_fixture.h" +#include "mongo/db/time_proof_service.h" #include "mongo/db/vector_clock_mutable.h" #include "mongo/unittest/unittest.h" #include "mongo/util/clock_source_mock.h" @@ -97,7 +98,7 @@ TEST_F(KeysManagerShardedTest, GetKeyWithSingleKey) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); auto keyStatus = keyManager()->getKeyForValidation(operationContext(), 1, LogicalTime(Timestamp(100, 0))); @@ -115,12 +116,12 @@ TEST_F(KeysManagerShardedTest, GetKeyWithMultipleKeys) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(205, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey2.toBSON())); auto keyStatus = keyManager()->getKeyForValidation(operationContext(), 1, LogicalTime(Timestamp(100, 0))); @@ -147,7 +148,7 @@ TEST_F(KeysManagerShardedTest, GetKeyShouldErrorIfKeyIdMismatchKey) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); auto keyStatus = keyManager()->getKeyForValidation(operationContext(), 2, LogicalTime(Timestamp(100, 0))); @@ -160,11 +161,11 @@ TEST_F(KeysManagerShardedTest, GetKeyWithoutRefreshShouldReturnRightKey) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey2.toBSON())); { auto keyStatus = keyManager()->getKeyForValidation( @@ -195,7 +196,7 @@ TEST_F(KeysManagerShardedTest, GetKeyForSigningShouldReturnRightKey) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); keyManager()->refreshNow(operationContext()); @@ -214,11 +215,11 @@ TEST_F(KeysManagerShardedTest, GetKeyForSigningShouldReturnRightOldKey) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey2.toBSON())); keyManager()->refreshNow(operationContext()); @@ -285,7 +286,7 @@ TEST_F(KeysManagerShardedTest, ShouldStillBeAbleToUpdateCacheEvenIfItCantCreateK KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( - operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); + operationContext(), NamespaceString::kKeysCollectionNamespace, origKey1.toBSON())); // Set the time to be very ahead so the updater will be forced to create new keys. const LogicalTime fakeTime(Timestamp(20000, 0)); diff --git a/src/mongo/db/logical_time.cpp b/src/mongo/db/logical_time.cpp index ae80b8b8c40..bdc8d9f040f 100644 --- a/src/mongo/db/logical_time.cpp +++ b/src/mongo/db/logical_time.cpp @@ -46,7 +46,8 @@ LogicalTime LogicalTime::fromOperationTime(const BSONObj& obj) { const auto opTimeElem(obj[kOperationTimeFieldName]); uassert(ErrorCodes::FailedToParse, "No operationTime found", !opTimeElem.eoo()); uassert(ErrorCodes::BadValue, - "Operation time is of the wrong value", + str::stream() << kOperationTimeFieldName << " is of the wrong type '" + << typeName(opTimeElem.type()) << "'", opTimeElem.type() == bsonTimestamp); return LogicalTime(opTimeElem.timestamp()); } @@ -79,4 +80,12 @@ BSONObj LogicalTime::toBSON() const { return bldr.obj(); } +void LogicalTime::serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const { + bob->appendElements(BSON(fieldName << asTimestamp())); +} + +LogicalTime LogicalTime::parseFromBSON(const BSONElement& elem) { + return LogicalTime(elem.timestamp()); +} + } // namespace mongo diff --git a/src/mongo/db/logical_time.h b/src/mongo/db/logical_time.h index 2e6b135329e..68b5c143316 100644 --- a/src/mongo/db/logical_time.h +++ b/src/mongo/db/logical_time.h @@ -85,6 +85,12 @@ public: */ BSONObj toBSON() const; + /* + * These methods support IDL parsing of logical times. + */ + static LogicalTime parseFromBSON(const BSONElement& elem); + void serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const; + /** * An uninitialized value of LogicalTime. Default constructed. */ diff --git a/src/mongo/db/namespace_string.cpp b/src/mongo/db/namespace_string.cpp index 3b28b021c50..169339c4c57 100644 --- a/src/mongo/db/namespace_string.cpp +++ b/src/mongo/db/namespace_string.cpp @@ -108,6 +108,9 @@ const NamespaceString NamespaceString::kReshardingApplierProgressNamespace( const NamespaceString NamespaceString::kReshardingTxnClonerProgressNamespace( NamespaceString::kConfigDb, "localReshardingOperations.recipient.progress_txn_cloner"); +const NamespaceString NamespaceString::kKeysCollectionNamespace(NamespaceString::kAdminDb, + "system.keys"); + bool NamespaceString::isListCollectionsCursorNS() const { return coll() == listCollectionsCursorCol; } diff --git a/src/mongo/db/namespace_string.h b/src/mongo/db/namespace_string.h index 029d9c7e18c..f702eaa2044 100644 --- a/src/mongo/db/namespace_string.h +++ b/src/mongo/db/namespace_string.h @@ -148,6 +148,9 @@ public: // Namespace for storing config.transactions cloner progress for resharding. static const NamespaceString kReshardingTxnClonerProgressNamespace; + // Namespace for storing keys for signing and validating cluster times. + static const NamespaceString kKeysCollectionNamespace; + /** * Constructs an empty NamespaceString. */ diff --git a/src/mongo/db/s/config/config_server_test_fixture.cpp b/src/mongo/db/s/config/config_server_test_fixture.cpp index 6013e807143..25092ea133c 100644 --- a/src/mongo/db/s/config/config_server_test_fixture.cpp +++ b/src/mongo/db/s/config/config_server_test_fixture.cpp @@ -369,7 +369,7 @@ std::vector ConfigServerTestFixture::getKeys(OperationCo auto findStatus = config->exhaustiveFindOnConfig(opCtx, kReadPref, repl::ReadConcernLevel::kMajorityReadConcern, - KeysCollectionDocument::ConfigNS, + NamespaceString::kKeysCollectionNamespace, BSONObj(), BSON("expiresAt" << 1), boost::none); @@ -378,9 +378,8 @@ std::vector ConfigServerTestFixture::getKeys(OperationCo std::vector keys; const auto& docs = findStatus.getValue().docs; for (const auto& doc : docs) { - auto keyStatus = KeysCollectionDocument::fromBSON(doc); - ASSERT_OK(keyStatus.getStatus()); - keys.push_back(keyStatus.getValue()); + auto key = KeysCollectionDocument::parse(IDLParserErrorContext("keyDoc"), doc); + keys.push_back(std::move(key)); } return keys; diff --git a/src/mongo/s/catalog/sharding_catalog_client.h b/src/mongo/s/catalog/sharding_catalog_client.h index a8918a2ed36..43c2c160a18 100644 --- a/src/mongo/s/catalog/sharding_catalog_client.h +++ b/src/mongo/s/catalog/sharding_catalog_client.h @@ -34,7 +34,7 @@ #include #include -#include "mongo/db/keys_collection_document.h" +#include "mongo/db/keys_collection_document_gen.h" #include "mongo/db/repl/optime_with.h" #include "mongo/db/write_concern_options.h" #include "mongo/s/client/shard.h" diff --git a/src/mongo/s/catalog/sharding_catalog_client_impl.cpp b/src/mongo/s/catalog/sharding_catalog_client_impl.cpp index a0dadf7de00..ff7053ab8ee 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_impl.cpp +++ b/src/mongo/s/catalog/sharding_catalog_client_impl.cpp @@ -902,7 +902,7 @@ StatusWith> ShardingCatalogClientImpl::getNe auto findStatus = config->exhaustiveFindOnConfig(opCtx, kConfigReadSelector, readConcernLevel, - KeysCollectionDocument::ConfigNS, + NamespaceString::kKeysCollectionNamespace, queryBuilder.obj(), BSON("expiresAt" << 1), boost::none); @@ -914,12 +914,13 @@ StatusWith> ShardingCatalogClientImpl::getNe const auto& keyDocs = findStatus.getValue().docs; std::vector keys; for (auto&& keyDoc : keyDocs) { - auto parseStatus = KeysCollectionDocument::fromBSON(keyDoc); - if (!parseStatus.isOK()) { - return parseStatus.getStatus(); + KeysCollectionDocument key; + try { + key = KeysCollectionDocument::parse(IDLParserErrorContext("keyDoc"), keyDoc); + } catch (...) { + return exceptionToStatus(); } - - keys.push_back(std::move(parseStatus.getValue())); + keys.push_back(std::move(key)); } return keys; diff --git a/src/mongo/s/catalog/sharding_catalog_client_test.cpp b/src/mongo/s/catalog/sharding_catalog_client_test.cpp index 85ec4605657..6166fb43926 100644 --- a/src/mongo/s/catalog/sharding_catalog_client_test.cpp +++ b/src/mongo/s/catalog/sharding_catalog_client_test.cpp @@ -39,6 +39,7 @@ #include "mongo/db/ops/write_ops.h" #include "mongo/db/query/query_request.h" #include "mongo/db/repl/read_concern_args.h" +#include "mongo/db/time_proof_service.h" #include "mongo/executor/task_executor.h" #include "mongo/rpc/get_status_from_command_result.h" #include "mongo/rpc/metadata/repl_set_metadata.h" @@ -1305,7 +1306,7 @@ TEST_F(ShardingCatalogClientTest, GetNewKeys) { fromjson("{purpose: 'none'," "expiresAt: {$gt: {$timestamp: {t: 1234, i: 5678}}}}")); - ASSERT_EQ(KeysCollectionDocument::ConfigNS, query->nss()); + ASSERT_EQ(NamespaceString::kKeysCollectionNamespace, query->nss()); ASSERT_BSONOBJ_EQ(expectedQuery, query->getFilter()); ASSERT_BSONOBJ_EQ(BSON("expiresAt" << 1), query->getSort()); ASSERT_FALSE(query->getLimit().is_initialized()); @@ -1356,7 +1357,7 @@ TEST_F(ShardingCatalogClientTest, GetNewKeysWithEmptyCollection) { fromjson("{purpose: 'none'," "expiresAt: {$gt: {$timestamp: {t: 1234, i: 5678}}}}")); - ASSERT_EQ(KeysCollectionDocument::ConfigNS, query->nss()); + ASSERT_EQ(NamespaceString::kKeysCollectionNamespace, query->nss()); ASSERT_BSONOBJ_EQ(expectedQuery, query->getFilter()); ASSERT_BSONOBJ_EQ(BSON("expiresAt" << 1), query->getSort()); ASSERT_FALSE(query->getLimit().is_initialized()); -- cgit v1.2.1