summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTess Avitabile <tess.avitabile@mongodb.com>2020-09-02 20:25:08 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-10-27 13:16:01 +0000
commit100c645fe1655c03c9fcb478f402a973e5c39278 (patch)
tree2d6f75d2136618caab3505f6ed5a3e754bce1652
parente4943822d3593552b223d8754676949ce8952eaa (diff)
downloadmongo-100c645fe1655c03c9fcb478f402a973e5c39278.tar.gz
SERVER-50116 Forbid oplog writes when replication is enabled
(cherry picked from commit 7b2913a64bc8812a9ab71ce27fe14bb2f517376f) (cherry picked from commit 73dd69d37bc64c58a800c8d1225e35d3ca6241cb) (cherry picked from commit eb7e3fb83f19ad86074b052ff8956d5e39ffabcb)
-rw-r--r--jstests/noPassthrough/oplog_writes_only_permitted_on_standalone.js43
-rw-r--r--jstests/replsets/oplog_truncated_on_recovery.js7
-rw-r--r--jstests/replsets/rollback_cmd_unrollbackable.js2
-rw-r--r--jstests/replsets/rollback_different_h.js8
-rw-r--r--jstests/replsets/rollback_refetch_no_uuid/rollback_cmd_unrollbackable.js2
-rw-r--r--src/mongo/db/ops/insert.cpp4
6 files changed, 61 insertions, 5 deletions
diff --git a/jstests/noPassthrough/oplog_writes_only_permitted_on_standalone.js b/jstests/noPassthrough/oplog_writes_only_permitted_on_standalone.js
new file mode 100644
index 00000000000..bb2af0f0ee7
--- /dev/null
+++ b/jstests/noPassthrough/oplog_writes_only_permitted_on_standalone.js
@@ -0,0 +1,43 @@
+/**
+ * Tests that oplog writes are forbidden on replica set members. In standalone mode, it is permitted
+ * to insert oplog entries.
+ * @tags: [
+ * requires_persistence,
+ * requires_replication,
+ * ]
+ */
+(function() {
+ "use strict";
+
+ const rst = new ReplSetTest({nodes: 1});
+ rst.startSet();
+ rst.initiate();
+
+ let conn = rst.getPrimary();
+ assert.writeOK(conn.getDB("test").coll.insert({_id: 0, a: 0}));
+
+ let oplog = conn.getDB("local").oplog.rs;
+
+ // Construct a valid oplog entry.
+ const lastOplogEntry = oplog.find().sort({ts: -1}).limit(1).toArray()[0];
+ const highestTS = lastOplogEntry.ts;
+ const toInsert = Object.extend(lastOplogEntry, {
+ op: "u",
+ ns: "test.coll",
+ o: {$set: {a: 1}},
+ o2: {_id: 0},
+ ts: Timestamp(highestTS.getTime(), highestTS.getInc() + 1)
+ });
+
+ jsTestLog("Test that oplog writes are banned when replication is enabled.");
+ assert.writeErrorWithCode(oplog.insert(toInsert), ErrorCodes.InvalidNamespace);
+
+ jsTestLog("Restart the node in standalone mode.");
+ rst.stop(0, undefined /*signal*/, undefined /*opts*/, {forRestart: true});
+ conn = rst.start(0, {noReplSet: true, noCleanData: true});
+
+ jsTestLog("Test that oplog writes are permitted in standalone mode.");
+ assert.writeOK(conn.getDB("local").oplog.rs.insert(toInsert));
+
+ rst.stopSet();
+}());
diff --git a/jstests/replsets/oplog_truncated_on_recovery.js b/jstests/replsets/oplog_truncated_on_recovery.js
index cca4051b369..50bb399f6a1 100644
--- a/jstests/replsets/oplog_truncated_on_recovery.js
+++ b/jstests/replsets/oplog_truncated_on_recovery.js
@@ -72,7 +72,9 @@
{upsert: true, writeConcern: {w: 1}})));
// Insert a diverged oplog entry that will be truncated after restart.
- log(assert.writeOK(localDB.oplog.rs.insert({
+ replTest.stop(0);
+ master = replTest.start(0, {noReplSet: true, noCleanData: true});
+ log(assert.writeOK(master.getDB("local").oplog.rs.insert({
_id: ObjectId(),
ns: "",
ts: divergedTS,
@@ -81,8 +83,7 @@
t: NumberLong(-1),
o: {}
})));
- log(localDB.oplog.rs.find().toArray());
- log(assert.commandWorked(localDB.adminCommand("replSetGetStatus")));
+ log(master.getDB("local").oplog.rs.find().toArray());
log("restart primary");
replTest.restart(master);
replTest.waitForState(master, ReplSetTest.State.RECOVERING);
diff --git a/jstests/replsets/rollback_cmd_unrollbackable.js b/jstests/replsets/rollback_cmd_unrollbackable.js
index 33a6f686929..d1f4af4cf02 100644
--- a/jstests/replsets/rollback_cmd_unrollbackable.js
+++ b/jstests/replsets/rollback_cmd_unrollbackable.js
@@ -47,6 +47,8 @@ options = {
};
// Inserts another oplog entry to set minValid ahead.
assert.writeOK(b_conn.getDB(name).foo.insert({x: 123}));
+replTest.stop(BID);
+b_conn = replTest.start(BID, {noReplSet: true, noCleanData: true});
var oplog_entry = b_conn.getDB("local").oplog.rs.find().sort({$natural: -1})[0];
oplog_entry["ts"] = Timestamp(oplog_entry["ts"].t, oplog_entry["ts"].i + 1);
oplog_entry["op"] = "c";
diff --git a/jstests/replsets/rollback_different_h.js b/jstests/replsets/rollback_different_h.js
index 0c4967aa691..b584f521964 100644
--- a/jstests/replsets/rollback_different_h.js
+++ b/jstests/replsets/rollback_different_h.js
@@ -49,8 +49,8 @@ assert.writeOK(a_conn.getDB(name).foo.insert({x: 1}, options));
replTest.stop(AID);
// change the h value of the most recent entry on B
-master = replTest.getPrimary();
-assert(b_conn.host === master.host, "b_conn assumed to be master");
+replTest.stop(1, undefined /*signal*/, undefined /*opts*/, {forRestart: true});
+b_conn = replTest.start(1, {noReplSet: true, noCleanData: true});
options = {
writeConcern: {w: 1, wtimeout: ReplSetTest.kDefaultTimeoutMS},
upsert: true
@@ -60,8 +60,12 @@ oplog_entry["ts"].t++;
oplog_entry["h"] = NumberLong(1);
res = b_conn.getDB("local").oplog.rs.insert(oplog_entry);
assert(res.nInserted > 0, tojson(res));
+replTest.stop(1, undefined /*signal*/, undefined /*opts*/, {forRestart: true});
+b_conn = replTest.start(1, {noCleanData: true});
// another insert to set minvalid ahead
+master = replTest.getPrimary();
+assert(b_conn.host === master.host, "b_conn assumed to be master");
assert.writeOK(b_conn.getDB(name).foo.insert({x: 123}));
// shut down B and bring back the original master
diff --git a/jstests/replsets/rollback_refetch_no_uuid/rollback_cmd_unrollbackable.js b/jstests/replsets/rollback_refetch_no_uuid/rollback_cmd_unrollbackable.js
index f544520e686..43d51e7d574 100644
--- a/jstests/replsets/rollback_refetch_no_uuid/rollback_cmd_unrollbackable.js
+++ b/jstests/replsets/rollback_refetch_no_uuid/rollback_cmd_unrollbackable.js
@@ -47,6 +47,8 @@ options = {
};
// Inserts another oplog entry to set minValid ahead.
assert.writeOK(b_conn.getDB(name).foo.insert({x: 123}));
+replTest.stop(BID);
+b_conn = replTest.start(BID, {noReplSet: true, noCleanData: true});
var oplog_entry = b_conn.getDB("local").oplog.rs.find().sort({$natural: -1})[0];
oplog_entry["ts"] = Timestamp(oplog_entry["ts"].t, oplog_entry["ts"].i + 1);
oplog_entry["op"] = "c";
diff --git a/src/mongo/db/ops/insert.cpp b/src/mongo/db/ops/insert.cpp
index ef49668a8d1..a4985726090 100644
--- a/src/mongo/db/ops/insert.cpp
+++ b/src/mongo/db/ops/insert.cpp
@@ -38,6 +38,7 @@
#include "mongo/bson/bson_depth.h"
#include "mongo/db/logical_clock.h"
#include "mongo/db/logical_time.h"
+#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/views/durable_view_catalog.h"
#include "mongo/util/mongoutils/str.h"
@@ -190,6 +191,9 @@ Status userAllowedWriteNS(StringData db, StringData coll) {
if (coll == "system.profile") {
return Status(ErrorCodes::InvalidNamespace,
str::stream() << "cannot write to '" << db << ".system.profile'");
+ } else if (db == "local" && coll == "oplog.rs" &&
+ repl::ReplicationCoordinator::get(getGlobalServiceContext())->isReplEnabled()) {
+ return Status(ErrorCodes::InvalidNamespace, "cannot write to 'local.oplog.rs'");
}
return userAllowedCreateNS(db, coll);
}