summaryrefslogtreecommitdiff
path: root/src/mongo/db/free_mon
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2018-04-10 13:29:16 -0400
committerMark Benvenuto <mark.benvenuto@mongodb.com>2018-04-10 13:29:16 -0400
commitb9cbfd8fd3cc8438a3e8e3986505f348416f5da4 (patch)
treec1b602f7ebc359f026e8120510ca86418b6514c1 /src/mongo/db/free_mon
parent2dc3299c530e41f05c032e8eb42a5b3b8a14f93e (diff)
downloadmongo-b9cbfd8fd3cc8438a3e8e3986505f348416f5da4.tar.gz
SERVER-34224 Implement FreeMonStorage Standalone and IDL for storage
Diffstat (limited to 'src/mongo/db/free_mon')
-rw-r--r--src/mongo/db/free_mon/SConscript3
-rw-r--r--src/mongo/db/free_mon/free_mon_storage.cpp135
-rw-r--r--src/mongo/db/free_mon/free_mon_storage.h67
-rw-r--r--src/mongo/db/free_mon/free_mon_storage.idl57
-rw-r--r--src/mongo/db/free_mon/free_mon_storage_test.cpp191
5 files changed, 453 insertions, 0 deletions
diff --git a/src/mongo/db/free_mon/SConscript b/src/mongo/db/free_mon/SConscript
index 0064e28495d..6a2b57d4a05 100644
--- a/src/mongo/db/free_mon/SConscript
+++ b/src/mongo/db/free_mon/SConscript
@@ -11,6 +11,8 @@ fmEnv.Library(
target='free_mon',
source=[
'free_mon_queue.cpp',
+ 'free_mon_storage.cpp',
+ env.Idlc('free_mon_storage.idl')[0],
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/base',
@@ -26,6 +28,7 @@ env.CppUnitTest(
target='free_mon_test',
source=[
'free_mon_queue_test.cpp',
+ 'free_mon_storage_test.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/db/repl/replmocks',
diff --git a/src/mongo/db/free_mon/free_mon_storage.cpp b/src/mongo/db/free_mon/free_mon_storage.cpp
new file mode 100644
index 00000000000..627ad076424
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_storage.cpp
@@ -0,0 +1,135 @@
+/**
+ * Copyright (C) 2018 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/db/free_mon/free_mon_storage.h"
+
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonelement.h"
+#include "mongo/bson/bsonmisc.h"
+#include "mongo/db/concurrency/d_concurrency.h"
+#include "mongo/db/concurrency/lock_manager_defs.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/repl/storage_interface.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+
+namespace {
+
+static const NamespaceString adminSystemVersionNss("admin.system.version");
+constexpr auto kFreeMonDocIdKey = "free_monitoring";
+
+// mms-automation stores its document in local.clustermanager
+static const NamespaceString localClusterManagerNss("local.clustermanager");
+
+} // namespace
+
+boost::optional<FreeMonStorageState> FreeMonStorage::read(OperationContext* opCtx) {
+ BSONObj deleteKey = BSON("_id" << kFreeMonDocIdKey);
+ BSONElement elementKey = deleteKey.firstElement();
+
+ auto storageInterface = repl::StorageInterface::get(opCtx);
+
+ Lock::DBLock dblk(opCtx, adminSystemVersionNss.db(), MODE_IS);
+ Lock::CollectionLock lk(opCtx->lockState(), adminSystemVersionNss.ns(), MODE_IS);
+
+ auto swObj = storageInterface->findById(opCtx, adminSystemVersionNss, elementKey);
+ if (!swObj.isOK()) {
+ if (swObj.getStatus() == ErrorCodes::NoSuchKey) {
+ return {};
+ }
+
+ uassertStatusOK(swObj.getStatus());
+ }
+
+ return FreeMonStorageState::parse(IDLParserErrorContext("FreeMonStorage"), swObj.getValue());
+}
+
+void FreeMonStorage::replace(OperationContext* opCtx, const FreeMonStorageState& doc) {
+ BSONObj deleteKey = BSON("_id" << kFreeMonDocIdKey);
+ BSONElement elementKey = deleteKey.firstElement();
+
+ BSONObj obj = doc.toBSON();
+
+ auto storageInterface = repl::StorageInterface::get(opCtx);
+ {
+ Lock::DBLock dblk(opCtx, adminSystemVersionNss.db(), MODE_IS);
+ Lock::CollectionLock lk(opCtx->lockState(), adminSystemVersionNss.ns(), MODE_IS);
+
+ auto swObj = storageInterface->upsertById(opCtx, adminSystemVersionNss, elementKey, obj);
+ if (!swObj.isOK()) {
+ uassertStatusOK(swObj);
+ }
+ }
+}
+
+void FreeMonStorage::deleteState(OperationContext* opCtx) {
+ BSONObj deleteKey = BSON("_id" << kFreeMonDocIdKey);
+ BSONElement elementKey = deleteKey.firstElement();
+
+ auto storageInterface = repl::StorageInterface::get(opCtx);
+ {
+ Lock::DBLock dblk(opCtx, adminSystemVersionNss.db(), MODE_IS);
+ Lock::CollectionLock lk(opCtx->lockState(), adminSystemVersionNss.ns(), MODE_IS);
+
+ auto swObj = storageInterface->deleteById(opCtx, adminSystemVersionNss, elementKey);
+ if (!swObj.isOK()) {
+ // Ignore errors about no document
+ if (swObj.getStatus() == ErrorCodes::NoSuchKey) {
+ return;
+ }
+
+ uassertStatusOK(swObj);
+ }
+ }
+}
+
+boost::optional<BSONObj> FreeMonStorage::readClusterManagerState(OperationContext* opCtx) {
+ auto storageInterface = repl::StorageInterface::get(opCtx);
+
+ Lock::DBLock dblk(opCtx, adminSystemVersionNss.db(), MODE_IS);
+ Lock::CollectionLock lk(opCtx->lockState(), adminSystemVersionNss.ns(), MODE_IS);
+
+ auto swObj = storageInterface->findSingleton(opCtx, localClusterManagerNss);
+ if (!swObj.isOK()) {
+ // Ignore errors about not-finding documents or having too many documents
+ if (swObj.getStatus() == ErrorCodes::NamespaceNotFound ||
+ swObj.getStatus() == ErrorCodes::CollectionIsEmpty ||
+ swObj.getStatus() == ErrorCodes::TooManyMatchingDocuments) {
+ return {};
+ }
+
+ uassertStatusOK(swObj.getStatus());
+ }
+
+ return swObj.getValue();
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/free_mon/free_mon_storage.h b/src/mongo/db/free_mon/free_mon_storage.h
new file mode 100644
index 00000000000..fd7d81e68bf
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_storage.h
@@ -0,0 +1,67 @@
+/**
+ * Copyright (C) 2018 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 <boost/optional.hpp>
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/free_mon/free_mon_storage_gen.h"
+#include "mongo/db/operation_context.h"
+
+namespace mongo {
+
+/**
+ * Storage tier for Free Monitoring. Provides access to storage engine.
+ */
+class FreeMonStorage {
+public:
+ /**
+ * Reads document from disk if it exists.
+ */
+ static boost::optional<FreeMonStorageState> read(OperationContext* opCtx);
+
+ /**
+ * Replaces document on disk with contents of document. Creates document if it does not exist.
+ */
+ static void replace(OperationContext* opCtx, const FreeMonStorageState& doc);
+
+ /**
+ * Deletes document on disk if it exists.
+ */
+ static void deleteState(OperationContext* opCtx);
+
+ /**
+ * Reads the singelton document from local.clustermanager.
+ *
+ * Returns nothing if there are more then one document or it does not exist.
+ */
+ static boost::optional<BSONObj> readClusterManagerState(OperationContext* opCtx);
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/free_mon/free_mon_storage.idl b/src/mongo/db/free_mon/free_mon_storage.idl
new file mode 100644
index 00000000000..a181d39f23a
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_storage.idl
@@ -0,0 +1,57 @@
+# 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/>.
+#
+global:
+ cpp_namespace: "mongo"
+
+imports:
+ - "mongo/idl/basic_types.idl"
+
+enums:
+ StorageState:
+ description: "Action types"
+ type: string
+ values:
+ disabled: disabled
+ enabled: enabled
+
+structs:
+ FreeMonStorageState:
+ description: "Persisted document in admin.system.version"
+ strict: false
+ generate_comparison_operators: true
+ fields:
+ _id:
+ description: "Key of the Free Monitoring singleton document"
+ type: "string"
+ default: '"free_monitoring"'
+ version:
+ description: "Storage version, initial version is 1"
+ type: long
+ state:
+ description: "Indicates whether it is disabled or enabled"
+ type: StorageState
+ registrationId:
+ description: "Registration Id"
+ type: string
+ informationalURL:
+ description: "Informational HTTP web page for metrics"
+ type: string
+ message:
+ description: "Informational message for shell to display to user"
+ type: string
+ userReminder:
+ description: "Message to display to user to remind them about service"
+ type: string
+
diff --git a/src/mongo/db/free_mon/free_mon_storage_test.cpp b/src/mongo/db/free_mon/free_mon_storage_test.cpp
new file mode 100644
index 00000000000..550691f2a24
--- /dev/null
+++ b/src/mongo/db/free_mon/free_mon_storage_test.cpp
@@ -0,0 +1,191 @@
+/**
+ * Copyright (C) 2018 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/base/string_data.h"
+#include "mongo/bson/bsonelement.h"
+#include "mongo/bson/bsonmisc.h"
+#include "mongo/db/catalog/collection_options.h"
+#include "mongo/db/concurrency/d_concurrency.h"
+#include "mongo/db/concurrency/lock_manager_defs.h"
+#include "mongo/db/free_mon/free_mon_storage.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/repl/replication_coordinator_mock.h"
+#include "mongo/db/repl/storage_interface.h"
+#include "mongo/db/repl/storage_interface_impl.h"
+#include "mongo/db/service_context.h"
+#include "mongo/db/service_context_d_test_fixture.h"
+#include "mongo/executor/network_interface_mock.h"
+#include "mongo/executor/thread_pool_task_executor.h"
+#include "mongo/executor/thread_pool_task_executor_test_fixture.h"
+#include "mongo/unittest/unittest.h"
+#include "mongo/util/uuid.h"
+
+namespace mongo {
+namespace {
+
+class FreeMonStorageTest : public ServiceContextMongoDTest {
+private:
+ void setUp() final;
+ void tearDown() final;
+
+protected:
+ /**
+ * Looks up the current ReplicationCoordinator.
+ * The result is cast to a ReplicationCoordinatorMock to provide access to test features.
+ */
+ repl::ReplicationCoordinatorMock* _getReplCoord() const;
+
+ ServiceContext::UniqueOperationContext _opCtx;
+
+ executor::NetworkInterfaceMock* _mockNetwork{nullptr};
+
+ std::unique_ptr<executor::ThreadPoolTaskExecutor> _mockThreadPool;
+
+ repl::StorageInterface* _storage{nullptr};
+};
+
+void FreeMonStorageTest::setUp() {
+ ServiceContextMongoDTest::setUp();
+ auto service = getServiceContext();
+
+ repl::ReplicationCoordinator::set(service,
+ std::make_unique<repl::ReplicationCoordinatorMock>(service));
+
+ _opCtx = cc().makeOperationContext();
+
+ repl::StorageInterface::set(service, std::make_unique<repl::StorageInterfaceImpl>());
+ _storage = repl::StorageInterface::get(service);
+
+ // Transition to PRIMARY so that the server can accept writes.
+ ASSERT_OK(_getReplCoord()->setFollowerMode(repl::MemberState::RS_PRIMARY));
+
+ // Create collection with one document.
+ CollectionOptions collectionOptions;
+ collectionOptions.uuid = UUID::gen();
+ auto statusCC = _storage->createCollection(
+ _opCtx.get(), NamespaceString("admin", "system.version"), collectionOptions);
+ ASSERT_OK(statusCC);
+}
+
+void FreeMonStorageTest::tearDown() {
+ _opCtx = {};
+ ServiceContextMongoDTest::tearDown();
+}
+
+repl::ReplicationCoordinatorMock* FreeMonStorageTest::_getReplCoord() const {
+ auto replCoord = repl::ReplicationCoordinator::get(_opCtx.get());
+ ASSERT(replCoord) << "No ReplicationCoordinator installed";
+ auto replCoordMock = dynamic_cast<repl::ReplicationCoordinatorMock*>(replCoord);
+ ASSERT(replCoordMock) << "Unexpected type for installed ReplicationCoordinator";
+ return replCoordMock;
+}
+
+// Positive: Test Storage works
+TEST_F(FreeMonStorageTest, TestStorage) {
+
+ FreeMonStorageState initialState =
+ FreeMonStorageState::parse(IDLParserErrorContext("foo"),
+ BSON("version" << 1LL << "state"
+ << "enabled"
+ << "registrationId"
+ << "1234"
+ << "informationalURL"
+ << "http://example.com"
+ << "message"
+ << "hello"
+ << "userReminder"
+ << ""));
+
+ {
+ auto emptyDoc = FreeMonStorage::read(_opCtx.get());
+ ASSERT_FALSE(emptyDoc.is_initialized());
+ }
+
+ FreeMonStorage::replace(_opCtx.get(), initialState);
+
+ {
+ auto persistedDoc = FreeMonStorage::read(_opCtx.get());
+
+ ASSERT_TRUE(persistedDoc.is_initialized());
+
+ ASSERT_TRUE(persistedDoc == initialState);
+ }
+
+ FreeMonStorage::deleteState(_opCtx.get());
+
+ {
+ auto emptyDoc = FreeMonStorage::read(_opCtx.get());
+ ASSERT_FALSE(emptyDoc.is_initialized());
+ }
+
+ // Verfiy delete of nothing succeeds
+ FreeMonStorage::deleteState(_opCtx.get());
+}
+
+void insertDoc(OperationContext* optCtx, const NamespaceString nss, StringData id) {
+ auto storageInterface = repl::StorageInterface::get(optCtx);
+
+ Lock::DBLock dblk(optCtx, nss.db(), MODE_IS);
+ Lock::CollectionLock lk(optCtx->lockState(), nss.ns(), MODE_IS);
+
+ BSONObj fakeDoc = BSON("_id" << id);
+ BSONElement elementKey = fakeDoc.firstElement();
+
+ ASSERT_OK(storageInterface->upsertById(optCtx, nss, elementKey, fakeDoc));
+}
+
+// Positive: Test local.clustermanager
+TEST_F(FreeMonStorageTest, TestClusterManagerStorage) {
+ const NamespaceString localClusterManagerNss("local.clustermanager");
+
+ // Verify read of non-existent collection works
+ ASSERT_FALSE(FreeMonStorage::readClusterManagerState(_opCtx.get()).is_initialized());
+
+ CollectionOptions collectionOptions;
+ collectionOptions.uuid = UUID::gen();
+ auto statusCC =
+ _storage->createCollection(_opCtx.get(), localClusterManagerNss, collectionOptions);
+ ASSERT_OK(statusCC);
+
+ // Verify read of empty collection works
+ ASSERT_FALSE(FreeMonStorage::readClusterManagerState(_opCtx.get()).is_initialized());
+
+ insertDoc(_opCtx.get(), localClusterManagerNss, "foo1");
+
+ // Verify read of singleton collection works
+ ASSERT_TRUE(FreeMonStorage::readClusterManagerState(_opCtx.get()).is_initialized());
+
+ insertDoc(_opCtx.get(), localClusterManagerNss, "bar1");
+
+ // Verify read of two doc collection fails
+ ASSERT_FALSE(FreeMonStorage::readClusterManagerState(_opCtx.get()).is_initialized());
+}
+} // namespace
+} // namespace mongo