/**
* Copyright (C) 2016 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 .
*
* 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::kSharding
#include "mongo/platform/basic.h"
#include "mongo/base/status_with.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context_noop.h"
#include "mongo/db/s/collection_metadata.h"
#include "mongo/db/s/collection_sharding_state.h"
#include "mongo/db/s/sharding_state.h"
#include "mongo/db/s/type_shard_identity.h"
#include "mongo/db/server_options.h"
#include "mongo/db/service_context_noop.h"
#include "mongo/db/service_context_noop.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/clock_source_mock.h"
namespace mongo {
namespace {
class CollShardingStateTest : public mongo::unittest::Test {
public:
void setUp() override {
_service.setFastClockSource(stdx::make_unique());
_service.setPreciseClockSource(stdx::make_unique());
serverGlobalParams.clusterRole = ClusterRole::ShardServer;
_client = _service.makeClient("ShardingStateTest");
// This skips version checking to avoid accessing a null ReplicationCoordinator.
_client->setInDirectClient(true);
_opCtx = _client->makeOperationContext();
// Note: this assumes that globalInit will always be called on the same thread as the main
// test thread.
ShardingState::get(opCtx())->setGlobalInitMethodForTest(
[this](OperationContext*, const ConnectionString&, StringData) {
_initCallCount++;
return Status::OK();
});
}
void tearDown() override {}
OperationContext* opCtx() {
return _opCtx.get();
}
int getInitCallCount() const {
return _initCallCount;
}
ServiceContext* getServiceContext() {
return &_service;
}
protected:
ServiceContextNoop _service;
private:
ServiceContext::UniqueClient _client;
ServiceContext::UniqueOperationContext _opCtx;
int _initCallCount = 0;
};
TEST_F(CollShardingStateTest, GlobalInitGetsCalledAfterWriteCommits) {
CollectionShardingState collShardingState(&_service,
NamespaceString::kConfigCollectionNamespace);
ShardIdentityType shardIdentity;
shardIdentity.setConfigsvrConnString(
ConnectionString(ConnectionString::SET, "a:1,b:2", "config"));
shardIdentity.setShardName("a");
shardIdentity.setClusterId(OID::gen());
WriteUnitOfWork wuow(opCtx());
collShardingState.onInsertOp(opCtx(), shardIdentity.toBSON());
ASSERT_EQ(0, getInitCallCount());
wuow.commit();
ASSERT_EQ(1, getInitCallCount());
}
TEST_F(CollShardingStateTest, GlobalInitDoesntGetCalledIfWriteAborts) {
CollectionShardingState collShardingState(getServiceContext(),
NamespaceString::kConfigCollectionNamespace);
ShardIdentityType shardIdentity;
shardIdentity.setConfigsvrConnString(
ConnectionString(ConnectionString::SET, "a:1,b:2", "config"));
shardIdentity.setShardName("a");
shardIdentity.setClusterId(OID::gen());
{
WriteUnitOfWork wuow(opCtx());
collShardingState.onInsertOp(opCtx(), shardIdentity.toBSON());
ASSERT_EQ(0, getInitCallCount());
}
ASSERT_EQ(0, getInitCallCount());
}
TEST_F(CollShardingStateTest, GlobalInitDoesntGetsCalledIfNSIsNotForShardIdentity) {
CollectionShardingState collShardingState(getServiceContext(), NamespaceString("admin.user"));
ShardIdentityType shardIdentity;
shardIdentity.setConfigsvrConnString(
ConnectionString(ConnectionString::SET, "a:1,b:2", "config"));
shardIdentity.setShardName("a");
shardIdentity.setClusterId(OID::gen());
WriteUnitOfWork wuow(opCtx());
collShardingState.onInsertOp(opCtx(), shardIdentity.toBSON());
ASSERT_EQ(0, getInitCallCount());
wuow.commit();
ASSERT_EQ(0, getInitCallCount());
}
TEST_F(CollShardingStateTest, OnInsertOpThrowWithIncompleteShardIdentityDocument) {
CollectionShardingState collShardingState(getServiceContext(),
NamespaceString::kConfigCollectionNamespace);
ShardIdentityType shardIdentity;
shardIdentity.setShardName("a");
ASSERT_THROWS(collShardingState.onInsertOp(opCtx(), shardIdentity.toBSON()),
AssertionException);
}
TEST_F(CollShardingStateTest, GlobalInitDoesntGetsCalledIfShardIdentityDocWasNotInserted) {
CollectionShardingState collShardingState(getServiceContext(),
NamespaceString::kConfigCollectionNamespace);
WriteUnitOfWork wuow(opCtx());
collShardingState.onInsertOp(opCtx(), BSON("_id" << 1));
ASSERT_EQ(0, getInitCallCount());
wuow.commit();
ASSERT_EQ(0, getInitCallCount());
}
} // unnamed namespace
} // namespace mongo