304 lines
13 KiB
JavaScript
304 lines
13 KiB
JavaScript
/**
|
|
* Tests upgrading a standalone node or replica set through several major versions.
|
|
*
|
|
* For each version downloaded by the multiversion setup:
|
|
* - Start a node or replica set of that version, without clearing data files from the previous
|
|
* iteration.
|
|
* - Create a new collection.
|
|
* - Insert a document into the new collection.
|
|
* - Create an index on the new collection.
|
|
*/
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
load('jstests/libs/get_index_helpers.js');
|
|
load('jstests/multiVersion/libs/multi_rs.js');
|
|
load('jstests/multiVersion/libs/verify_versions.js');
|
|
|
|
// Setup the dbpath for this test.
|
|
const dbpath = MongoRunner.dataPath + 'major_version_upgrade';
|
|
resetDbpath(dbpath);
|
|
|
|
// We set noCleanData to true in order to preserve the data files between iterations.
|
|
const defaultOptions = {
|
|
dbpath: dbpath,
|
|
noCleanData: true,
|
|
};
|
|
|
|
// This lists all supported releases and needs to be kept up to date as versions are added and
|
|
// dropped.
|
|
// TODO SERVER-26792: In the future, we should have a common place from which both the
|
|
// multiversion setup procedure and this test get information about supported major releases.
|
|
const versions = [
|
|
{binVersion: '3.2', testCollection: 'three_two'},
|
|
{binVersion: '3.4', featureCompatibilityVersion: '3.4', testCollection: 'three_four'},
|
|
{binVersion: '3.6', featureCompatibilityVersion: '3.6', testCollection: 'three_six'},
|
|
{binVersion: '4.0', featureCompatibilityVersion: '4.0', testCollection: 'four_zero'},
|
|
{binVersion: '4.2', featureCompatibilityVersion: '4.2', testCollection: 'four_two'},
|
|
{binVersion: '4.4', featureCompatibilityVersion: '4.4', testCollection: 'four_four'},
|
|
{binVersion: 'last-lts', testCollection: 'last_lts'},
|
|
{binVersion: 'last-continuous', testCollection: 'last_continuous'},
|
|
{binVersion: 'latest', featureCompatibilityVersion: '4.7', testCollection: 'latest'},
|
|
];
|
|
|
|
// These key patterns are considered valid for existing v:0 and v:1 indexes, but are considered
|
|
// invalid for v:2 indexes or new index builds.
|
|
var invalidIndexSpecs = [
|
|
{a: 0},
|
|
{a: NaN},
|
|
{a: true},
|
|
];
|
|
|
|
// When running the oldest supported version, insert indexes with bad key patterns.
|
|
function insertBadIndexes(testDB) {
|
|
invalidIndexSpecs.forEach((spec) => {
|
|
// Generate a unique and identifiable collection name.
|
|
let collName = 'bad_index_' + tojson(spec.a);
|
|
assert.commandWorked(testDB[collName].createIndex(spec, {name: 'badkp'}),
|
|
'failed to create index with key pattern' + tojson(spec));
|
|
});
|
|
}
|
|
|
|
// When running the newest version, check that the indexes with bad key patterns are readable.
|
|
function validateBadIndexesStandalone(testDB) {
|
|
invalidIndexSpecs.forEach((spec) => {
|
|
// Generate a unique and identifiable collection name.
|
|
let collName = 'bad_index_' + tojson(spec.a);
|
|
let indexSpec = GetIndexHelpers.findByName(testDB[collName].getIndexes(), 'badkp');
|
|
assert.neq(null, indexSpec, 'could not find index "badkp"');
|
|
assert.eq(1, indexSpec.v, tojson(indexSpec));
|
|
|
|
// Collection compact command should succeed, despite the presence of the v:1 index
|
|
// which would fail v:2 validation rules.
|
|
assert.commandWorked(testDB.runCommand({compact: collName}));
|
|
|
|
// reIndex will fail because when featureCompatibilityVersion>=3.4, reIndex
|
|
// automatically upgrades v=1 indexes to v=2.
|
|
assert.commandFailed(testDB[collName].reIndex());
|
|
|
|
// reIndex should not drop the index.
|
|
indexSpec = GetIndexHelpers.findByName(testDB[collName].getIndexes(), 'badkp');
|
|
assert.neq(null, indexSpec, 'could not find index "badkp" after reIndex');
|
|
assert.eq(1, indexSpec.v, tojson(indexSpec));
|
|
|
|
// A query that hints the index should succeed.
|
|
assert.commandWorked(testDB.runCommand({find: collName, hint: "badkp"}));
|
|
|
|
// Newly created indexes will do stricter validation and should fail if the
|
|
// key pattern is invalid.
|
|
assert.commandWorked(testDB[collName].dropIndexes());
|
|
assert.commandFailedWithCode(
|
|
testDB[collName].createIndex(spec),
|
|
ErrorCodes.CannotCreateIndex,
|
|
'creating index with key pattern ' + tojson(spec) + ' unexpectedly succeeded');
|
|
// Index build should also fail if v:1 or v:2 is explicitly requested.
|
|
assert.commandFailedWithCode(
|
|
testDB[collName].createIndex(spec, {v: 1}),
|
|
ErrorCodes.CannotCreateIndex,
|
|
'creating index with key pattern ' + tojson(spec) + ' unexpectedly succeeded');
|
|
assert.commandFailedWithCode(
|
|
testDB[collName].createIndex(spec, {v: 2}),
|
|
ErrorCodes.CannotCreateIndex,
|
|
'creating index with key pattern ' + tojson(spec) + ' unexpectedly succeeded');
|
|
});
|
|
}
|
|
|
|
// Check that secondary nodes have the v:1 indexes.
|
|
function validateBadIndexesSecondary(testDB) {
|
|
invalidIndexSpecs.forEach((spec) => {
|
|
// Generate a unique and identifiable collection name.
|
|
let collName = 'bad_index_' + tojson(spec.a);
|
|
// Verify that the secondary has the v:1 index.
|
|
let indexSpec = GetIndexHelpers.findByName(testDB[collName].getIndexes(), 'badkp');
|
|
assert.neq(null, indexSpec, 'could not find index "badkp"');
|
|
assert.eq(1, indexSpec.v, tojson(indexSpec));
|
|
});
|
|
}
|
|
|
|
// Standalone
|
|
// Iterate from earliest to latest versions specified in the versions list, and follow the steps
|
|
// outlined at the top of this test file.
|
|
let authSchemaUpgraded = false;
|
|
for (let i = 0; i < versions.length; i++) {
|
|
let version = versions[i];
|
|
let mongodOptions = Object.extend({binVersion: version.binVersion}, defaultOptions);
|
|
|
|
// Start a mongod with specified version.
|
|
let conn = MongoRunner.runMongod(mongodOptions);
|
|
|
|
if ((conn === null) && (i > 0) && !authSchemaUpgraded) {
|
|
// As of 4.0, mongod will refuse to start up with authSchema 3
|
|
// until the schema has been upgraded.
|
|
// Step back a version (to 3.6) in order to perform the upgrade,
|
|
// Then try startuing 4.0 again.
|
|
print(
|
|
"Failed starting mongod, going to try upgrading the auth schema on the prior version");
|
|
conn = MongoRunner.runMongod(
|
|
Object.extend({binVersion: versions[i - 1].binVersion}, defaultOptions));
|
|
assert.neq(null,
|
|
conn,
|
|
'mongod was previously able to start with version ' +
|
|
tojson(version.binVersion) + " but now can't");
|
|
assert.commandWorked(conn.getDB('admin').runCommand({authSchemaUpgrade: 1}));
|
|
MongoRunner.stopMongod(conn);
|
|
|
|
authSchemaUpgraded = true;
|
|
conn = MongoRunner.runMongod(mongodOptions);
|
|
}
|
|
|
|
assert.neq(null, conn, 'mongod was unable to start up with options: ' + tojson(mongodOptions));
|
|
assert.binVersion(conn, version.binVersion);
|
|
|
|
if ((i === 0) && (version.binVersion <= 3.6)) {
|
|
// Simulate coming from a <= 2.6 installation where MONGODB-CR was the default/only
|
|
// authentication mechanism. Eventually, the upgrade process will fail (above) when
|
|
// running on 4.0 where support for MONGODB-CR has been removed.
|
|
conn.getDB('admin').system.version.save({"_id": "authSchema", "currentVersion": 3});
|
|
}
|
|
|
|
// Connect to the 'test' database.
|
|
let testDB = conn.getDB('test');
|
|
|
|
// Verify that the data and indices from previous iterations are still accessible.
|
|
for (let j = 0; j < i; j++) {
|
|
let oldVersionCollection = versions[j].testCollection;
|
|
assert.eq(1,
|
|
testDB[oldVersionCollection].count(),
|
|
`data from ${oldVersionCollection} should be available; options: ` +
|
|
tojson(mongodOptions));
|
|
assert.neq(
|
|
null,
|
|
GetIndexHelpers.findByKeyPattern(testDB[oldVersionCollection].getIndexes(), {a: 1}),
|
|
`index from ${oldVersionCollection} should be available; options: ` +
|
|
tojson(mongodOptions));
|
|
}
|
|
|
|
// Create a new collection.
|
|
assert.commandWorked(testDB.createCollection(version.testCollection));
|
|
|
|
// Insert a document into the new collection.
|
|
assert.commandWorked(testDB[version.testCollection].insert({a: 1}));
|
|
assert.eq(1,
|
|
testDB[version.testCollection].count(),
|
|
`mongo should have inserted 1 document into collection ${version.testCollection}; ` +
|
|
'options: ' + tojson(mongodOptions));
|
|
|
|
// Create an index on the new collection.
|
|
assert.commandWorked(testDB[version.testCollection].createIndex({a: 1}));
|
|
|
|
if (i === 0) {
|
|
// We're on the earliest version, insert indexes with bad key patterns.
|
|
insertBadIndexes(testDB);
|
|
} else if (i === versions.length - 1) {
|
|
// We're on the latest version, check bad indexes are still readable.
|
|
validateBadIndexesStandalone(testDB);
|
|
}
|
|
|
|
// Set the appropriate featureCompatibilityVersion upon upgrade, if applicable.
|
|
if (version.hasOwnProperty('featureCompatibilityVersion')) {
|
|
let adminDB = conn.getDB("admin");
|
|
assert.commandWorked(adminDB.runCommand(
|
|
{"setFeatureCompatibilityVersion": version.featureCompatibilityVersion}));
|
|
}
|
|
|
|
// Shutdown the current mongod.
|
|
MongoRunner.stopMongod(conn);
|
|
}
|
|
|
|
// Replica Sets
|
|
// Setup the ReplSetTest object.
|
|
let nodes = {
|
|
n1: {binVersion: versions[0].binVersion},
|
|
n2: {binVersion: versions[0].binVersion},
|
|
n3: {binVersion: versions[0].binVersion},
|
|
};
|
|
let rst = new ReplSetTest({nodes});
|
|
|
|
// Start up and initiate the replica set.
|
|
rst.startSet();
|
|
rst.initiate();
|
|
|
|
// Iterate from earliest to latest versions specified in the versions list, and follow the steps
|
|
// outlined at the top of this test file.
|
|
for (let i = 0; i < versions.length; i++) {
|
|
let version = versions[i];
|
|
|
|
// Connect to the primary running the old version to ensure that the test can insert and
|
|
// create indices.
|
|
let primary = rst.getPrimary();
|
|
|
|
// Upgrade the secondary nodes first.
|
|
rst.upgradeSecondaries(primary, {binVersion: version.binVersion});
|
|
|
|
assert.neq(null,
|
|
primary,
|
|
`replica set was unable to start up after upgrading secondaries to version: ${
|
|
version.binVersion}`);
|
|
|
|
// Connect to the 'test' database.
|
|
let testDB = primary.getDB('test');
|
|
assert.commandWorked(testDB.createCollection(version.testCollection));
|
|
assert.commandWorked(testDB[version.testCollection].insert({a: 1}));
|
|
assert.eq(1,
|
|
testDB[version.testCollection].count(),
|
|
`mongo should have inserted 1 document into collection ${version.testCollection}; ` +
|
|
'nodes: ' + tojson(nodes));
|
|
|
|
// Create an index on the new collection.
|
|
assert.commandWorked(testDB[version.testCollection].createIndex({a: 1}));
|
|
|
|
if (i === 0) {
|
|
// We're on the earliest version, insert indexes with bad key patterns.
|
|
insertBadIndexes(testDB);
|
|
} else if (i === versions.length - 1) {
|
|
// We're on the latest version, check bad indexes are still readable.
|
|
for (let secondary of rst.getSecondaries()) {
|
|
validateBadIndexesSecondary(secondary.getDB('test'));
|
|
}
|
|
}
|
|
|
|
// Do the index creation and insertion again after upgrading the primary node.
|
|
primary = rst.upgradePrimary(primary, {binVersion: version.binVersion});
|
|
assert.neq(
|
|
null, primary, `replica set was unable to start up with version: ${version.binVersion}`);
|
|
assert.binVersion(primary, version.binVersion);
|
|
testDB = primary.getDB('test');
|
|
|
|
assert.commandWorked(testDB[version.testCollection].insert({b: 1}));
|
|
assert.eq(2,
|
|
testDB[version.testCollection].count(),
|
|
`mongo should have inserted 2 documents into collection ${version.testCollection}; ` +
|
|
'nodes: ' + tojson(nodes));
|
|
|
|
assert.commandWorked(testDB[version.testCollection].createIndex({b: 1}));
|
|
|
|
// Verify that all previously inserted data and indices are accessible.
|
|
for (let j = 0; j <= i; j++) {
|
|
let oldVersionCollection = versions[j].testCollection;
|
|
assert.eq(2,
|
|
testDB[oldVersionCollection].count(),
|
|
`data from ${oldVersionCollection} should be available; nodes: ${tojson(nodes)}`);
|
|
assert.neq(
|
|
null,
|
|
GetIndexHelpers.findByKeyPattern(testDB[oldVersionCollection].getIndexes(), {a: 1}),
|
|
`index from ${oldVersionCollection} should be available; nodes: ${tojson(nodes)}`);
|
|
assert.neq(
|
|
null,
|
|
GetIndexHelpers.findByKeyPattern(testDB[oldVersionCollection].getIndexes(), {b: 1}),
|
|
`index from ${oldVersionCollection} should be available; nodes: ${tojson(nodes)}`);
|
|
}
|
|
|
|
// Set the appropriate featureCompatibilityVersion upon upgrade, if applicable.
|
|
if (version.hasOwnProperty('featureCompatibilityVersion')) {
|
|
let primaryAdminDB = primary.getDB("admin");
|
|
assert.commandWorked(primaryAdminDB.runCommand(
|
|
{setFeatureCompatibilityVersion: version.featureCompatibilityVersion}));
|
|
rst.awaitReplication();
|
|
}
|
|
}
|
|
|
|
// Stop the replica set.
|
|
rst.stopSet();
|
|
})();
|