summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml2
-rw-r--r--jstests/sharding/movePrimary1.js6
-rw-r--r--jstests/views/views_all_commands.js92
-rw-r--r--jstests/views/views_sharded.js14
-rw-r--r--src/mongo/s/commands/cluster_shard_collection_cmd.cpp96
5 files changed, 151 insertions, 59 deletions
diff --git a/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml b/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml
index 882821c1490..34091bebed6 100644
--- a/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml
+++ b/buildscripts/resmokeconfig/suites/sharding_last_stable_mongos_and_mixed_shards.yml
@@ -14,6 +14,8 @@ selector:
- jstests/sharding/add_shard_to_zone.js
- jstests/sharding/remove_shard_from_zone.js
- jstests/sharding/update_zone_key_range.js
+ # TODO Assumes mongod and mongos handle read on view. Enable when 3.4 becomes 'last-stable'.
+ - jstests/sharding/movePrimary1.js
# v3.4 replace noAutoSplit flag with document in config.settings
- jstests/sharding/disable_autosplit.js
# v3.4 changes $in shard key extraction.
diff --git a/jstests/sharding/movePrimary1.js b/jstests/sharding/movePrimary1.js
index 25217879e6d..4db1a09c41a 100644
--- a/jstests/sharding/movePrimary1.js
+++ b/jstests/sharding/movePrimary1.js
@@ -10,6 +10,9 @@
c.save({a: 3});
assert.eq(3, c.count());
+ assert.commandWorked(
+ db.runCommand({create: "view", viewOn: "foo", pipeline: [{$match: {a: 3}}]}));
+
return s.getPrimaryShard(name);
};
@@ -18,6 +21,7 @@
assert.eq(3, from.getDB("test1").foo.count(), "from doesn't have data before move");
assert.eq(0, to.getDB("test1").foo.count(), "to has data before move");
+ assert.eq(1, s.s.getDB("test1").view.count(), "count on view incorrect before move");
assert.eq(s.normalize(s.config.databases.findOne({_id: "test1"}).primary),
s.normalize(from.name),
@@ -32,6 +36,7 @@
assert.eq(0, from.getDB("test1").foo.count(), "from still has data after move");
assert.eq(3, to.getDB("test1").foo.count(), "to doesn't have data after move");
+ assert.eq(1, s.s.getDB("test1").view.count(), "count on view incorrect after move");
// move back, now using shard name instead of server address
s.admin.runCommand({moveprimary: "test1", to: oldShardName});
@@ -42,6 +47,7 @@
assert.eq(3, from.getDB("test1").foo.count(), "from doesn't have data after move back");
assert.eq(0, to.getDB("test1").foo.count(), "to has data after move back");
+ assert.eq(1, s.s.getDB("test1").view.count(), "count on view incorrect after move back");
// attempting to move primary DB to non-existent shard should error out with appropriate code
var res = s.admin.runCommand({movePrimary: 'test1', to: 'dontexist'});
diff --git a/jstests/views/views_all_commands.js b/jstests/views/views_all_commands.js
index e912b894c4a..6076ca77992 100644
--- a/jstests/views/views_all_commands.js
+++ b/jstests/views/views_all_commands.js
@@ -28,8 +28,12 @@
* selection of commonly-used reasons below.)
*
* expectFailure
- * If true, assert that the command fails with a views-specific error code. Otherwise, all
- * commands are expected to succeed.
+ * If true, assert that the command fails. Otherwise, all commands are expected to succeed.
+ *
+ * expectedErrorCode
+ * When 'expectFailure' is true, specifies the error code expected. Defaults to
+ * 'CommandNotSupportedOnView' when not specified. Set to 'null' when expecting an error
+ * without an error code field.
*
* setup
* A function that will be run before the command is executed. It takes a handle to the 'test'
@@ -97,7 +101,9 @@
expectFailure: true,
},
checkShardingIndex: {skip: isUnrelated},
- cleanupOrphaned: {command: {cleanupOrphaned: 1}, skip: "TODO(SERVER-24764)"},
+ cleanupOrphaned: {
+ skip: "Tested in views/views_sharded.js",
+ },
clone: {skip: "TODO(SERVER-24506)"},
cloneCollection: {skip: "TODO(SERVER-24506)"},
cloneCollectionAsCapped: {
@@ -228,7 +234,7 @@
command: {getShardVersion: "test.view"},
isAdminCommand: true,
expectFailure: true,
- skip: "TODO(SERVER-24764)"
+ skipSharded: true, // mongos is tested in views/views_sharded.js
},
getnonce: {skip: isUnrelated},
godinsert: {skip: isAnInternalCommand},
@@ -296,15 +302,25 @@
"mapreduce.shardedfinish": {skip: isAnInternalCommand},
mergeChunks: {
command: {mergeChunks: "test.view", bounds: [{x: 0}, {x: 10}]},
+ setup: function(conn) {
+ assert.commandWorked(conn.adminCommand({enableSharding: "test"}));
+ },
+ skipStandalone: true,
isAdminCommand: true,
- skip: "TODO(SERVER-24764) Confirm/add correct mongoS error handling.",
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NamespaceNotSharded,
},
moveChunk: {
command: {moveChunk: "test.view"},
+ setup: function(conn) {
+ assert.commandWorked(conn.adminCommand({enableSharding: "test"}));
+ },
+ skipStandalone: true,
isAdminCommand: true,
- skip: "TODO(SERVER-24764) Confirm/add correct mongoS error handling.",
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NamespaceNotSharded,
},
- movePrimary: {skip: "TODO(SERVER-24764) Add test for views"},
+ movePrimary: {skip: "Tested in sharding/movePrimary1.js"},
netstat: {skip: isAnInternalCommand},
parallelCollectionScan: {command: {parallelCollectionScan: "view"}, expectFailure: true},
ping: {command: {ping: 1}},
@@ -328,7 +344,8 @@
{
isAdminCommand: true,
command: {renameCollection: "test.collection", to: "test.view"},
- expectFailure: ErrorCodes.NamespaceExists,
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NamespaceExists,
}
],
repairCursor: {command: {repairCursor: "view"}, expectFailure: true},
@@ -382,15 +399,44 @@
skipStandalone: true,
expectFailure: true,
isAdminCommand: true,
- skip: "TODO(SERVER-24764) Add view check to mongoS"
},
shardConnPoolStats: {skip: isUnrelated},
shardingState: {skip: isUnrelated},
shutdown: {skip: isUnrelated},
sleep: {skip: isUnrelated},
- split: {skip: "TODO(SERVER-24764) Test that split on view fails"},
- splitChunk: {skip: "TODO(SERVER-24764) Test that split on view fails"},
- splitVector: {skip: "TODO(SERVER-24764) Test that split on view fails"},
+ split: {
+ command: {split: "test.view", find: {_id: 1}},
+ setup: function(conn) {
+ assert.commandWorked(conn.adminCommand({enableSharding: "test"}));
+ },
+ skipStandalone: true,
+ expectFailure: true,
+ expectedErrorCode: ErrorCodes.NamespaceNotSharded,
+ isAdminCommand: true,
+ },
+ splitChunk: {
+ command: {
+ splitChunk: "test.view",
+ from: "shard0000",
+ min: {x: MinKey},
+ max: {x: 0},
+ keyPattern: {x: 1},
+ splitKeys: [{x: -2}, {x: -1}],
+ shardVersion: [1, 2]
+ },
+ skipSharded: true,
+ expectFailure: true,
+ expectedErrorCode: null,
+ isAdminCommand: true,
+ },
+ splitVector: {
+ command: {
+ splitVector: "test.view",
+ keyPattern: {x: 1},
+ maxChunkSize: 1,
+ },
+ expectFailure: true,
+ },
stageDebug: {skip: isAnInternalCommand},
top: {command: {top: "view"}, isAdminCommand: true, skip: "TODO(SERVER-24568)"},
touch: {
@@ -421,18 +467,16 @@
/**
* Helper function for failing commands or writes that checks the result 'res' of either.
- * If 'code' is undefined or true, the expected error defaults to CommandNotSupportedOnView.
- * Otherwise it is the numerical value of code. On no error, or wrong error code, the resulting
- * assert includes the message 'msg'.
+ * If 'code' is null we only check for failure, otherwise we confirm error code matches as
+ * well. On assert 'msg' is printed.
*/
let assertCommandOrWriteFailed = function(res, code, msg) {
- if (code == undefined || code === true)
- code = ErrorCodes.CommandNotSupportedOnView;
-
if (res.writeErrors !== undefined)
assert.neq(0, res.writeErrors.length, msg);
- else
+ else if (res.code !== null)
assert.commandFailedWithCode(res, code, msg);
+ else
+ assert.commandFailed(res, msg);
};
function runTests(db) {
@@ -493,11 +537,15 @@
if (subtest.isAdminCommand)
commandHandle = db.getSiblingDB("admin");
- if (subtest.expectFailure)
+ if (subtest.expectFailure) {
+ let expectedErrorCode = subtest.expectedErrorCode;
+ if (expectedErrorCode === undefined)
+ expectedErrorCode = ErrorCodes.CommandNotSupportedOnView;
+
assertCommandOrWriteFailed(commandHandle.runCommand(subtest.command),
- subtest.expectFailure,
+ expectedErrorCode,
tojson(subtest.command));
- else if (subtest.command instanceof Function)
+ } else if (subtest.command instanceof Function)
subtest.command(commandHandle);
else
assert.commandWorked(commandHandle.runCommand(subtest.command),
diff --git a/jstests/views/views_sharded.js b/jstests/views/views_sharded.js
index 07113d34dad..99408fcc9a3 100644
--- a/jstests/views/views_sharded.js
+++ b/jstests/views/views_sharded.js
@@ -75,4 +75,18 @@
assert.commandWorked(result);
assert(result.hasOwnProperty("shards"), tojson(result));
+ //
+ // Confirm cleanupOrphaned command fails.
+ //
+ result = st.getPrimaryShard(db.getName()).getDB("admin").runCommand({
+ cleanupOrphaned: view.getFullName()
+ });
+ assert.commandFailedWithCode(result, ErrorCodes.CommandNotSupportedOnView);
+
+ //
+ // Confirm getShardVersion command fails.
+ //
+ assert.commandFailedWithCode(db.adminCommand({getShardVersion: view.getFullName()}),
+ ErrorCodes.NamespaceNotSharded);
+
})();
diff --git a/src/mongo/s/commands/cluster_shard_collection_cmd.cpp b/src/mongo/s/commands/cluster_shard_collection_cmd.cpp
index 2d4d55bff4c..5471371e0e8 100644
--- a/src/mongo/s/commands/cluster_shard_collection_cmd.cpp
+++ b/src/mongo/s/commands/cluster_shard_collection_cmd.cpp
@@ -223,52 +223,74 @@ public:
}
}
- BSONObj collectionOptions;
- if (res["options"].type() == BSONType::Object) {
- collectionOptions = res["options"].Obj();
- }
+ BSONObj defaultCollation;
- // Check that collection is not capped.
- if (collectionOptions["capped"].trueValue()) {
- errmsg = "can't shard capped collection";
- conn.done();
- return false;
- }
+ if (!res.isEmpty()) {
+ // Check that namespace is not a view.
+ {
+ std::string namespaceType;
+ auto status = bsonExtractStringField(res, "type", &namespaceType);
+ if (!status.isOK()) {
+ conn.done();
+ return appendCommandStatus(result, status);
+ }
- // Get collection default collation.
- BSONObj defaultCollation;
- {
- BSONElement collationElement;
- auto status = bsonExtractTypedField(
- collectionOptions, "collation", BSONType::Object, &collationElement);
- if (status.isOK()) {
- defaultCollation = collationElement.Obj().getOwned();
- if (defaultCollation.isEmpty()) {
+ if (namespaceType == "view") {
conn.done();
return appendCommandStatus(
result,
- {ErrorCodes::BadValue,
- "Default collation in collection metadata cannot be empty."});
+ {ErrorCodes::CommandNotSupportedOnView, "Views cannot be sharded."});
}
- } else if (status != ErrorCodes::NoSuchKey) {
+ }
+
+ BSONObj collectionOptions;
+ if (res["options"].type() == BSONType::Object) {
+ collectionOptions = res["options"].Obj();
+ }
+
+ // Check that collection is not capped.
+ if (collectionOptions["capped"].trueValue()) {
+ errmsg = "can't shard capped collection";
conn.done();
- return appendCommandStatus(
- result,
- {status.code(),
- str::stream() << "Could not parse default collation in collection metadata "
- << causedBy(status)});
+ return false;
}
- }
- // If the collection has a non-simple default collation but the user did not specify the
- // simple collation explicitly, return an error.
- if (!defaultCollation.isEmpty() && !simpleCollationSpecified) {
- return appendCommandStatus(result,
- {ErrorCodes::BadValue,
- str::stream()
- << "Collection has default collation: "
- << collectionOptions["collation"]
- << ". Must specify collation {locale: 'simple'}"});
+ // Get collection default collation.
+ {
+ BSONElement collationElement;
+ auto status = bsonExtractTypedField(
+ collectionOptions, "collation", BSONType::Object, &collationElement);
+ if (status.isOK()) {
+ defaultCollation = collationElement.Obj().getOwned();
+ if (defaultCollation.isEmpty()) {
+ conn.done();
+ return appendCommandStatus(
+ result,
+ {ErrorCodes::BadValue,
+ "Default collation in collection metadata cannot be empty."});
+ }
+ } else if (status != ErrorCodes::NoSuchKey) {
+ conn.done();
+ return appendCommandStatus(
+ result,
+ {status.code(),
+ str::stream()
+ << "Could not parse default collation in collection metadata "
+ << causedBy(status)});
+ }
+ }
+
+ // If the collection has a non-simple default collation but the user did not specify the
+ // simple collation explicitly, return an error.
+ if (!defaultCollation.isEmpty() && !simpleCollationSpecified) {
+ conn.done();
+ return appendCommandStatus(result,
+ {ErrorCodes::BadValue,
+ str::stream()
+ << "Collection has default collation: "
+ << collectionOptions["collation"]
+ << ". Must specify collation {locale: 'simple'}"});
+ }
}
// The proposed shard key must be validated against the set of existing indexes.