summaryrefslogtreecommitdiff
path: root/jstests/sharding/addshard2.js
blob: 28ffaf05e6f18c46c01bb6e62a96ad8d84f6b33b (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
 * Tests adding standalones and replica sets as shards under a variety of configurations (setName,
 * valid and invalid hosts, shardName matching or not matching a setName, etc).
 */
(function() {

// TODO SERVER-50144 Remove this and allow orphan checking.
// This test calls removeShard which can leave docs in config.rangeDeletions in state "pending",
// therefore preventing orphans from being cleaned up.
TestData.skipCheckOrphans = true;

let addShardRes;

const assertAddShardSucceeded = function(res, shardName) {
    assert.commandWorked(res);

    // If a shard name was specified, make sure that the name the addShard command reports the
    // shard was added with matches the specified name.
    if (shardName) {
        assert.eq(shardName,
                  res.shardAdded,
                  "name returned by addShard does not match name specified in addShard");
    }

    // Make sure the shard shows up in config.shards with the shardName reported by the
    // addShard command.
    assert.neq(null,
               st.s.getDB('config').shards.findOne({_id: res.shardAdded}),
               "newly added shard " + res.shardAdded + " not found in config.shards");
};

// Note: this method expects that the failure is *not* that the specified shardName is already
// the shardName of an existing shard.
const assertAddShardFailed = function(res, shardName) {
    assert.commandFailed(res);

    // If a shard name was specified in the addShard, make sure no shard with its name shows up
    // in config.shards.
    if (shardName) {
        assert.eq(
            null,
            st.s.getDB('config').shards.findOne({_id: shardName}),
            "addShard for " + shardName + " reported failure, but shard shows up in config.shards");
    }
};

const removeShardWithName = function(shardName) {
    let res = st.s.adminCommand({removeShard: shardName});
    assert.commandWorked(res);
    assert.eq('started', res.state);
    assert.soon(function() {
        res = st.s.adminCommand({removeShard: shardName});
        assert.commandWorked(res);
        return ('completed' === res.state);
    }, "removeShard never completed for shard " + shardName);
};

const st = new ShardingTest({
    shards: 0,
    mongos: 1,
});

// Add one shard since the last shard cannot be removed.
const normalShard = new ReplSetTest({name: "addshard2-1", nodes: 1, nodeOptions: {shardsvr: ""}});
normalShard.startSet();
normalShard.initiate();

st.s.adminCommand({addShard: normalShard.getURL(), name: 'normalShard'});

// Allocate a port that can be used to test adding invalid hosts.
const portWithoutHostRunning = allocatePort();

// 1. Test adding a *replica set* with an ordinary set name

// 1.a. with or without specifying the shardName.

jsTest.log("Adding a replica set without a specified shardName should succeed.");
const rst1 = new ReplSetTest({nodes: 1});
rst1.startSet({shardsvr: ''});
rst1.initiate();
addShardRes = st.s.adminCommand({addShard: rst1.getURL()});
assertAddShardSucceeded(addShardRes);
assert.eq(rst1.name, addShardRes.shardAdded);
removeShardWithName(addShardRes.shardAdded);
rst1.stopSet();

jsTest.log(
    "Adding a replica set with a specified shardName that matches the set's name should succeed.");
const rst2 = new ReplSetTest({nodes: 1});
rst2.startSet({shardsvr: ''});
rst2.initiate();
addShardRes = st.s.adminCommand({addShard: rst2.getURL(), name: rst2.name});
assertAddShardSucceeded(addShardRes, rst2.name);
removeShardWithName(addShardRes.shardAdded);
rst2.stopSet();

let rst3 = new ReplSetTest({nodes: 1});
rst3.startSet({shardsvr: ''});
rst3.initiate();

jsTest.log(
    "Adding a replica set with a specified shardName that differs from the set's name should succeed.");
addShardRes = st.s.adminCommand({addShard: rst3.getURL(), name: "differentShardName"});
assertAddShardSucceeded(addShardRes, "differentShardName");
removeShardWithName(addShardRes.shardAdded);

jsTest.log("Adding a replica with a specified shardName of 'config' should fail.");
addShardRes = st.s.adminCommand({addShard: rst3.getURL(), name: "config"});
assertAddShardFailed(addShardRes, "config");

// 1.b. with invalid hostnames.

jsTest.log("Adding a replica set with only non-existing hosts should fail.");
addShardRes =
    st.s.adminCommand({addShard: rst3.name + "/NonExistingHost:" + portWithoutHostRunning});
assertAddShardFailed(addShardRes);

jsTest.log("Adding a replica set with mixed existing/non-existing hosts should fail.");
addShardRes = st.s.adminCommand({
    addShard:
        rst3.name + "/" + rst3.getPrimary().name + ",NonExistingHost:" + portWithoutHostRunning
});
assertAddShardFailed(addShardRes);

rst3.stopSet();

// 2. Test adding a replica set whose *set name* is "config" with or without specifying the
// shardName.

let rst4 = new ReplSetTest({name: "config", nodes: 1});
rst4.startSet({shardsvr: ''});
rst4.initiate();

jsTest.log(
    "Adding a replica set whose setName is config without specifying shardName should fail.");
addShardRes = st.s.adminCommand({addShard: rst4.getURL()});
assertAddShardFailed(addShardRes);

jsTest.log(
    "Adding a replica set whose setName is config with specified shardName 'config' should fail.");
addShardRes = st.s.adminCommand({addShard: rst4.getURL(), name: rst4.name});
assertAddShardFailed(addShardRes, rst4.name);

jsTest.log(
    "Adding a replica set whose setName is config with a non-'config' shardName should succeed");
addShardRes = st.s.adminCommand({addShard: rst4.getURL(), name: "nonConfig"});
assertAddShardSucceeded(addShardRes, "nonConfig");
removeShardWithName(addShardRes.shardAdded);

rst4.stopSet();

// 3. Test that a replica set whose *set name* is "admin" can be written to (SERVER-17232).

let rst5 = new ReplSetTest({name: "admin", nodes: 1});
rst5.startSet({shardsvr: ''});
rst5.initiate();

jsTest.log("A replica set whose set name is 'admin' should be able to be written to.");

addShardRes = st.s.adminCommand({addShard: rst5.getURL()});
assertAddShardSucceeded(addShardRes);

// Ensure the write goes to the newly added shard.
assert.commandWorked(st.s.getDB('test').runCommand({create: "foo"}));
const res = st.s.getDB('config').getCollection('databases').findOne({_id: 'test'});
assert.neq(null, res);
if (res.primary != addShardRes.shardAdded) {
    assert.commandWorked(st.s.adminCommand({movePrimary: 'test', to: addShardRes.shardAdded}));
}

assert.commandWorked(st.s.getDB('test').foo.insert({x: 1}));
assert.neq(null, rst5.getPrimary().getDB('test').foo.findOne());

assert.commandWorked(st.s.getDB('test').runCommand({dropDatabase: 1}));

removeShardWithName(addShardRes.shardAdded);

rst5.stopSet();

st.stop();
normalShard.stopSet();
})();