summaryrefslogtreecommitdiff
path: root/jstests/sharding/mongod_returns_no_cluster_time_without_keys.js
blob: 8cb8fe8f7fb2cfa84b8921d8aa222eb9c1fcaa7a (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
/**
 * Tests that mongod does not gossip cluster time metadata and operation time until at least one key
 * is created on the
 * config server.
 *
 * This test restarts shard replica sets, so it requires a persistent storage engine.
 * @tags: [requires_persistence]
 */
(function() {
    "use strict";

    // This test uses authentication and runs commands without authenticating, which is not
    // compatible with implicit sessions.
    TestData.disableImplicitSessions = true;

    load("jstests/multiVersion/libs/multi_rs.js");

    // TODO SERVER-32672: remove this flag.
    TestData.skipGossipingClusterTime = true;
    const keyFile = 'jstests/libs/key1';
    const adminUser = {db: "admin", username: "foo", password: "bar"};
    const rUser = {db: "test", username: "r", password: "bar"};

    function assertContainsValidLogicalTime(res) {
        assert.hasFields(res, ["$clusterTime"]);
        assert.hasFields(res.$clusterTime, ["signature", "clusterTime"]);
        // clusterTime must be greater than the uninitialzed value.
        assert.hasFields(res.$clusterTime.signature, ["hash", "keyId"]);
        // The signature must have been signed by a key with a valid generation.
        assert(res.$clusterTime.signature.keyId > NumberLong(0));

        assert.hasFields(res, ["operationTime"]);
        assert(Object.prototype.toString.call(res.operationTime) === "[object Timestamp]",
               "operationTime must be a timestamp");
    }

    let st = new ShardingTest({shards: {rs0: {nodes: 2}}, other: {keyFile: keyFile}});

    jsTestLog("Started ShardingTest");

    var adminDB = st.s.getDB("admin");
    adminDB.createUser({user: adminUser.username, pwd: adminUser.password, roles: ["__system"]});

    adminDB.auth(adminUser.username, adminUser.password);
    assert(st.s.getDB("admin").system.keys.count() >= 2);

    let priRSConn = st.rs0.getPrimary().getDB("admin");
    priRSConn.createUser({user: rUser.username, pwd: rUser.password, roles: ["root"]});

    priRSConn.auth(rUser.username, rUser.password);
    const resWithKeys = priRSConn.runCommand({isMaster: 1});
    assertContainsValidLogicalTime(resWithKeys);
    priRSConn.logout();

    // Enable the failpoint, remove all keys, and restart the config servers with the failpoint
    // still enabled to guarantee there are no keys.
    for (let i = 0; i < st.configRS.nodes.length; i++) {
        assert.commandWorked(st.configRS.nodes[i].adminCommand(
            {"configureFailPoint": "disableKeyGeneration", "mode": "alwaysOn"}));
    }

    var priCSConn = st.configRS.getPrimary();
    authutil.asCluster(priCSConn, keyFile, function() {
        priCSConn.getDB("admin").system.keys.remove({purpose: "HMAC"});
    });

    assert(adminDB.system.keys.count() == 0, "expected there to be no keys on the config server");
    adminDB.logout();

    st.configRS.stopSet(null /* signal */, true /* forRestart */);
    st.configRS.startSet(
        {restart: true, setParameter: {"failpoint.disableKeyGeneration": "{'mode':'alwaysOn'}"}});

    // bounce rs0 to clean the key cache
    st.rs0.stopSet(null /* signal */, true /* forRestart */);
    st.rs0.startSet({restart: true});

    priRSConn = st.rs0.getPrimary().getDB("admin");
    priRSConn.auth(rUser.username, rUser.password);
    const resNoKeys = assert.commandWorked(priRSConn.runCommand({isMaster: 1}));
    priRSConn.logout();

    assert.eq(resNoKeys.hasOwnProperty("$clusterTime"), false);
    assert.eq(resNoKeys.hasOwnProperty("operationTime"), false);

    st.stop();
})();