summaryrefslogtreecommitdiff
path: root/jstests/hooks/run_validate_collections.js
blob: 0fd699b8e0e476dba6705ea8e6370426cd911a2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Runner for validateCollections that runs full validation on all collections when loaded into
// the mongo shell.
'use strict';

(function() {
    assert.eq(typeof db, 'object', 'Invalid `db` object, is the shell connected to a mongod?');
    load('jstests/libs/parallelTester.js');

    function getDirectConnections(conn) {
        // If conn does not point to a repl set, then this function returns [conn].
        const res = conn.adminCommand({isMaster: 1});
        const connections = [];

        if (res.hasOwnProperty('setName')) {
            for (let hostString of res.hosts) {
                connections.push(new Mongo(hostString));
            }
            if (res.hasOwnProperty('passives')) {
                for (let hostString of res.passives) {
                    connections.push(new Mongo(hostString));
                }
            }
        } else {
            connections.push(conn);
        }

        return connections;
    }

    function getConfigConnStr() {
        const shardMap = db.adminCommand({getShardMap: 1});
        if (!shardMap.hasOwnProperty('map')) {
            throw new Error('Expected getShardMap() to return an object a "map" field: ' +
                            tojson(shardMap));
        }

        const map = shardMap.map;

        if (!map.hasOwnProperty('config')) {
            throw new Error('Expected getShardMap().map to have a "config" field: ' + tojson(map));
        }

        return map.config;
    }

    function isMongos() {
        return db.isMaster().msg === 'isdbgrid';
    }

    function getServerList() {
        const serverList = [];

        if (isMongos()) {
            // We're connected to a sharded cluster through a mongos.

            // 1) Add all the config servers to the server list.
            const configConnStr = getConfigConnStr();
            const configServerReplSetConn = new Mongo(configConnStr);
            serverList.push(...getDirectConnections(configServerReplSetConn));

            // 2) Add shard members to the server list.
            const configDB = db.getSiblingDB('config');
            const cursor = configDB.shards.find();

            while (cursor.hasNext()) {
                const shard = cursor.next();
                const shardReplSetConn = new Mongo(shard.host);
                serverList.push(...getDirectConnections(shardReplSetConn));
            }
        } else {
            // We're connected to a mongod.
            serverList.push(...getDirectConnections(db.getMongo()));
        }

        return serverList;
    }

    // Run a separate thread to validate collections on each server in parallel.
    var validateCollectionsThread = function(host, testData) {
        load('jstests/hooks/validate_collections.js');  // For validateCollections.
        TestData = testData;  // Pass the TestData object from main thread.

        try {
            print('Running validate() on ' + host);
            const conn = new Mongo(host);
            conn.setSlaveOk();
            jsTest.authenticate(conn);

            const dbNames = conn.getDBNames();
            for (let dbName of dbNames) {
                if (!validateCollections(conn.getDB(dbName), {full: true})) {
                    return {ok: 0};
                }
            }
            return {ok: 1};
        } catch (e) {
            print('Exception caught in scoped thread running validationCollections on server: ' +
                  host);
            return {ok: 0, error: e.toString(), stack: e.stack};
        }
    };

    // We run the scoped threads in a try/finally block in case any thread throws an exception, in
    // which case we want to still join all the threads.
    let threads = [];
    const serverList = getServerList();

    try {
        serverList.forEach(server => {
            const thread = new ScopedThread(validateCollectionsThread, server.host, TestData);
            threads.push(thread);
            thread.start();
        });
    } finally {
        // Wait for each thread to finish. Throw an error if any thread fails.
        const returnData = threads.map(thread => {
            thread.join();
            return thread.returnData();
        });

        returnData.forEach(res => {
            assert.commandWorked(res, 'Collection validation failed');
        });
    }
})();