summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2017-02-13 11:22:40 -0500
committerRandolph Tan <randolph@10gen.com>2017-02-27 13:15:43 -0500
commit2e68e8bf05fd7c6cb94b82fcfd0fe237c9f7ccb8 (patch)
tree2374dc732dbe4f1f59b63e2680e205533fecf976 /src/mongo
parenta4e1443629b733c7c0fd44dddcd78e884da848bd (diff)
downloadmongo-2e68e8bf05fd7c6cb94b82fcfd0fe237c9f7ccb8.tar.gz
SERVER-27748 Implement LogicalTimeMetadata
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/crypto/sha1_block.cpp2
-rw-r--r--src/mongo/crypto/sha1_block.h2
-rw-r--r--src/mongo/db/SConscript1
-rw-r--r--src/mongo/db/signed_logical_time.cpp2
-rw-r--r--src/mongo/db/signed_logical_time.h2
-rw-r--r--src/mongo/db/time_proof_service.h6
-rw-r--r--src/mongo/rpc/SConscript3
-rw-r--r--src/mongo/rpc/metadata/logical_time_metadata.cpp94
-rw-r--r--src/mongo/rpc/metadata/logical_time_metadata.h68
-rw-r--r--src/mongo/rpc/metadata/logical_time_metadata_test.cpp113
10 files changed, 286 insertions, 7 deletions
diff --git a/src/mongo/crypto/sha1_block.cpp b/src/mongo/crypto/sha1_block.cpp
index 52880cd0438..c978e545b20 100644
--- a/src/mongo/crypto/sha1_block.cpp
+++ b/src/mongo/crypto/sha1_block.cpp
@@ -71,7 +71,7 @@ std::string SHA1Block::toString() const {
return base64::encode(reinterpret_cast<const char*>(_hash.data()), _hash.size());
}
-void SHA1Block::appendAsBinData(BSONObjBuilder& builder, StringData fieldName) {
+void SHA1Block::appendAsBinData(BSONObjBuilder& builder, StringData fieldName) const {
builder.appendBinData(fieldName, _hash.size(), BinDataGeneral, _hash.data());
}
diff --git a/src/mongo/crypto/sha1_block.h b/src/mongo/crypto/sha1_block.h
index 588ceb8da52..3398cea6018 100644
--- a/src/mongo/crypto/sha1_block.h
+++ b/src/mongo/crypto/sha1_block.h
@@ -81,7 +81,7 @@ public:
/**
* Append this to a builder using the given name as a BSON BinData type value.
*/
- void appendAsBinData(BSONObjBuilder& builder, StringData fieldName);
+ void appendAsBinData(BSONObjBuilder& builder, StringData fieldName) const;
/**
* Do a bitwise xor against another SHA1Block and replace the current contents of this block
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 55ea57fe7fc..20aa7fe127e 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -941,6 +941,7 @@ env.Library(
],
LIBDEPS=[
'logical_time',
+ '$BUILD_DIR/mongo/crypto/sha1_block',
],
)
diff --git a/src/mongo/db/signed_logical_time.cpp b/src/mongo/db/signed_logical_time.cpp
index bcfb89f027a..16404950ac5 100644
--- a/src/mongo/db/signed_logical_time.cpp
+++ b/src/mongo/db/signed_logical_time.cpp
@@ -34,7 +34,7 @@ namespace mongo {
std::string SignedLogicalTime::toString() const {
StringBuilder buf;
- buf << _time.toString() << "|" << _proof;
+ buf << _time.toString() << "|" << _proof.toString();
return buf.str();
}
diff --git a/src/mongo/db/signed_logical_time.h b/src/mongo/db/signed_logical_time.h
index 52cf1230e58..7b96e51df79 100644
--- a/src/mongo/db/signed_logical_time.h
+++ b/src/mongo/db/signed_logical_time.h
@@ -36,7 +36,7 @@ namespace mongo {
/**
* The SignedLogicalTime class is a pair of value i.e. time and a signature i.e. _proof
- * The class is immutable and is used to hold the cryptogrphically protected LogicalTime.
+ * The class is immutable and is used to hold the cryptographically protected LogicalTime.
*/
class SignedLogicalTime {
public:
diff --git a/src/mongo/db/time_proof_service.h b/src/mongo/db/time_proof_service.h
index bc43e1ceee3..5be6bf46e38 100644
--- a/src/mongo/db/time_proof_service.h
+++ b/src/mongo/db/time_proof_service.h
@@ -29,6 +29,7 @@
#pragma once
#include "mongo/base/status.h"
+#include "mongo/crypto/sha1_block.h"
#include "mongo/db/logical_time.h"
namespace mongo {
@@ -40,14 +41,13 @@ namespace mongo {
class TimeProofService {
public:
// This type must be synchronized with the library that generates SHA1 or other proof.
- using TimeProof = std::string;
+ using TimeProof = SHA1Block;
/**
* Returns the proof matching the time argument.
*/
TimeProof getProof(LogicalTime time) {
- TimeProof proof = "12345678901234567890";
- return proof;
+ return SHA1Block();
}
/**
diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript
index 93764bdbd92..a4945dcf9c6 100644
--- a/src/mongo/rpc/SConscript
+++ b/src/mongo/rpc/SConscript
@@ -150,6 +150,7 @@ env.Clone().InjectModule("enterprise").Library(
'metadata/audit_metadata.cpp',
'metadata/config_server_metadata.cpp',
'metadata/egress_metadata_hook_list.cpp',
+ 'metadata/logical_time_metadata.cpp',
'metadata/server_selection_metadata.cpp',
'metadata/sharding_metadata.cpp',
'metadata/repl_set_metadata.cpp',
@@ -162,6 +163,7 @@ env.Clone().InjectModule("enterprise").Library(
'$BUILD_DIR/mongo/bson/util/bson_extract',
'$BUILD_DIR/mongo/client/read_preference',
'$BUILD_DIR/mongo/db/repl/optime',
+ '$BUILD_DIR/mongo/db/signed_logical_time',
'$BUILD_DIR/mongo/util/decorable',
],
)
@@ -172,6 +174,7 @@ env.CppUnitTest(
],
source=[
'metadata/egress_metadata_hook_list_test.cpp',
+ 'metadata/logical_time_metadata_test.cpp',
'metadata/server_selection_metadata_test.cpp',
'metadata/sharding_metadata_test.cpp',
'metadata/tracking_metadata_test.cpp',
diff --git a/src/mongo/rpc/metadata/logical_time_metadata.cpp b/src/mongo/rpc/metadata/logical_time_metadata.cpp
new file mode 100644
index 00000000000..ef056a2fd46
--- /dev/null
+++ b/src/mongo/rpc/metadata/logical_time_metadata.cpp
@@ -0,0 +1,94 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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/rpc/metadata/logical_time_metadata.h"
+
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/bson/util/bson_extract.h"
+
+namespace mongo {
+namespace rpc {
+
+namespace {
+
+const char kClusterTimeFieldName[] = "clusterTime";
+const char kSignatureFieldName[] = "signature";
+
+} // unnamed namespace
+
+
+LogicalTimeMetadata::LogicalTimeMetadata(SignedLogicalTime time) : _clusterTime(std::move(time)) {}
+StatusWith<LogicalTimeMetadata> LogicalTimeMetadata::readFromMetadata(const BSONObj& metadata) {
+ return readFromMetadata(metadata.getField(fieldName()));
+}
+
+StatusWith<LogicalTimeMetadata> LogicalTimeMetadata::readFromMetadata(
+ const BSONElement& metadataElem) {
+ const auto& obj = metadataElem.Obj();
+
+ Timestamp ts;
+ Status status = bsonExtractTimestampField(obj, kClusterTimeFieldName, &ts);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ // Extract BinData type signature and construct a SHA1Block instance from it.
+ BSONElement signatureElem;
+ status = bsonExtractTypedField(obj, kSignatureFieldName, BinData, &signatureElem);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ int hashLength = 0;
+ auto rawBinSignature = signatureElem.binData(hashLength);
+ BSONBinData proofBinData(rawBinSignature, hashLength, signatureElem.binDataType());
+ auto proofStatus = SHA1Block::fromBinData(proofBinData);
+
+ if (!proofStatus.isOK()) {
+ return proofStatus.getStatus();
+ }
+
+ return LogicalTimeMetadata(
+ SignedLogicalTime(LogicalTime(ts), std::move(proofStatus.getValue())));
+}
+
+void LogicalTimeMetadata::writeToMetadata(BSONObjBuilder* metadataBuilder) const {
+ BSONObjBuilder subObjBuilder(metadataBuilder->subobjStart(fieldName()));
+ _clusterTime.getTime().asTimestamp().append(subObjBuilder.bb(), kClusterTimeFieldName);
+ _clusterTime.getProof().appendAsBinData(subObjBuilder, kSignatureFieldName);
+ subObjBuilder.doneFast();
+}
+
+const SignedLogicalTime& LogicalTimeMetadata::getSignedTime() const {
+ return _clusterTime;
+}
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/metadata/logical_time_metadata.h b/src/mongo/rpc/metadata/logical_time_metadata.h
new file mode 100644
index 00000000000..aafd4640231
--- /dev/null
+++ b/src/mongo/rpc/metadata/logical_time_metadata.h
@@ -0,0 +1,68 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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_with.h"
+#include "mongo/db/signed_logical_time.h"
+
+namespace mongo {
+
+class BSONElement;
+class BSONObjBuilder;
+
+namespace rpc {
+
+/**
+ * Format:
+ * logicalTime: {
+ * clusterTime: <Timestamp>,
+ * signature: <SHA1 hash of clusterTime as BinData>
+ * }
+ */
+class LogicalTimeMetadata {
+public:
+ explicit LogicalTimeMetadata(SignedLogicalTime time);
+
+ static StatusWith<LogicalTimeMetadata> readFromMetadata(const BSONObj& metadata);
+ static StatusWith<LogicalTimeMetadata> readFromMetadata(const BSONElement& metadataElem);
+
+ void writeToMetadata(BSONObjBuilder* metadataBuilder) const;
+
+ const SignedLogicalTime& getSignedTime() const;
+
+ static StringData fieldName() {
+ return "logicalTime";
+ }
+
+private:
+ SignedLogicalTime _clusterTime;
+};
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/metadata/logical_time_metadata_test.cpp b/src/mongo/rpc/metadata/logical_time_metadata_test.cpp
new file mode 100644
index 00000000000..61106f3d469
--- /dev/null
+++ b/src/mongo/rpc/metadata/logical_time_metadata_test.cpp
@@ -0,0 +1,113 @@
+/**
+ * Copyright (C) 2017 MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/jsobj.h"
+#include "mongo/rpc/metadata/logical_time_metadata.h"
+#include "mongo/unittest/unittest.h"
+
+#include "mongo/util/log.h"
+
+namespace mongo {
+namespace rpc {
+namespace {
+
+TEST(LogicalTimeMetadataTest, Roundtrip) {
+ const auto ts = LogicalTime(Timestamp(100, 200));
+
+ SHA1Block::HashType proof;
+ proof.fill(0);
+ proof[19] = 6;
+ proof[0] = 12;
+
+ SignedLogicalTime signedTs(LogicalTime(ts), proof);
+
+ LogicalTimeMetadata origMetadata(signedTs);
+ BSONObjBuilder builder;
+ origMetadata.writeToMetadata(&builder);
+
+ auto serializedObj = builder.done();
+ auto parseStatus = LogicalTimeMetadata::readFromMetadata(serializedObj);
+ ASSERT_OK(parseStatus.getStatus());
+
+ const auto& parsedMetadata = parseStatus.getValue();
+ const auto& parsedTs = parsedMetadata.getSignedTime();
+ ASSERT_EQ(ts.asTimestamp(), parsedTs.getTime().asTimestamp());
+ ASSERT_TRUE(SHA1Block(proof) == parsedTs.getProof());
+}
+
+TEST(LogicalTimeMetadataTest, MissingClusterTimeShouldFailToParse) {
+ std::array<uint8_t, 20> proof;
+ proof.fill(0);
+
+ BSONObjBuilder builder;
+ BSONObjBuilder subObjBuilder(builder.subobjStart("logicalTime"));
+ subObjBuilder.append("signature", BSONBinData(proof.data(), proof.size(), BinDataGeneral));
+ subObjBuilder.doneFast();
+
+ auto serializedObj = builder.done();
+ auto status = LogicalTimeMetadata::readFromMetadata(serializedObj).getStatus();
+ ASSERT_EQ(ErrorCodes::NoSuchKey, status);
+}
+
+TEST(LogicalTimeMetadataTest, MissingProofShouldFailToParse) {
+ const auto ts = Timestamp(100, 200);
+
+ BSONObjBuilder builder;
+ BSONObjBuilder subObjBuilder(builder.subobjStart("logicalTime"));
+ ts.append(subObjBuilder.bb(), "clusterTime");
+ subObjBuilder.doneFast();
+
+ auto serializedObj = builder.done();
+ auto status = LogicalTimeMetadata::readFromMetadata(serializedObj).getStatus();
+ ASSERT_EQ(ErrorCodes::NoSuchKey, status);
+}
+
+TEST(LogicalTimeMetadataTest, ProofWithWrongLengthShouldFailToParse) {
+ const auto ts = Timestamp(100, 200);
+
+ std::array<uint8_t, 10> proof;
+ proof.fill(0);
+
+ BSONObjBuilder builder;
+ BSONObjBuilder subObjBuilder(builder.subobjStart("logicalTime"));
+ ts.append(subObjBuilder.bb(), "clusterTime");
+ subObjBuilder.append("signature", BSONBinData(proof.data(), proof.size(), BinDataGeneral));
+ subObjBuilder.doneFast();
+
+ auto serializedObj = builder.done();
+ auto status = LogicalTimeMetadata::readFromMetadata(serializedObj).getStatus();
+ ASSERT_EQ(ErrorCodes::UnsupportedFormat, status);
+}
+
+} // namespace rpc
+} // namespace mongo
+}