1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #include "mongo/platform/basic.h"
32 
33 #include "mongo/client/replica_set_monitor.h"
34 #include "mongo/client/replica_set_monitor_internal.h"
35 #include "mongo/unittest/unittest.h"
36 
37 namespace {
38 
39 using namespace mongo;
40 using std::set;
41 
42 // Pull nested types to top-level scope
43 typedef ReplicaSetMonitor::SetState SetState;
44 typedef SetState::Node Node;
45 typedef SetState::Nodes Nodes;
46 
isCompatible(const Node & node,ReadPreference pref,const TagSet & tagSet)47 bool isCompatible(const Node& node, ReadPreference pref, const TagSet& tagSet) {
48     set<HostAndPort> seeds;
49     seeds.insert(node.host);
50     SetState set("name", seeds);
51     set.nodes.push_back(node);
52 
53     ReadPreferenceSetting criteria(pref, tagSet);
54     return !set.getMatchingHost(criteria).empty();
55 }
56 
57 const BSONObj SampleIsMasterDoc = BSON("tags" << BSON("dc"
58                                                       << "NYC"
59                                                       << "p"
60                                                       << "2"
61                                                       << "region"
62                                                       << "NA"));
63 const BSONObj SampleTags = SampleIsMasterDoc["tags"].Obj();
64 const BSONObj NoTags = BSONObj();
65 const BSONObj NoTagIsMasterDoc = BSON("isMaster" << true);
66 
67 
TEST(ReplSetMonitorNode,SimpleGoodMatch)68 TEST(ReplSetMonitorNode, SimpleGoodMatch) {
69     Node node(((HostAndPort())));
70     node.tags = BSON("dc"
71                      << "sf");
72     ASSERT(node.matches(BSON("dc"
73                              << "sf")));
74 }
75 
TEST(ReplSetMonitorNode,SimpleBadMatch)76 TEST(ReplSetMonitorNode, SimpleBadMatch) {
77     Node node((HostAndPort()));
78     node.tags = BSON("dc"
79                      << "nyc");
80     ASSERT(!node.matches(BSON("dc"
81                               << "sf")));
82 }
83 
TEST(ReplSetMonitorNode,ExactMatch)84 TEST(ReplSetMonitorNode, ExactMatch) {
85     Node node((HostAndPort()));
86     node.tags = SampleTags;
87     ASSERT(node.matches(SampleIsMasterDoc["tags"].Obj()));
88 }
89 
TEST(ReplSetMonitorNode,EmptyTag)90 TEST(ReplSetMonitorNode, EmptyTag) {
91     Node node((HostAndPort()));
92     node.tags = SampleTags;
93     ASSERT(node.matches(BSONObj()));
94 }
95 
TEST(ReplSetMonitorNode,MemberNoTagMatchesEmptyTag)96 TEST(ReplSetMonitorNode, MemberNoTagMatchesEmptyTag) {
97     Node node((HostAndPort()));
98     node.tags = NoTags;
99     ASSERT(node.matches(BSONObj()));
100 }
101 
TEST(ReplSetMonitorNode,MemberNoTagDoesNotMatch)102 TEST(ReplSetMonitorNode, MemberNoTagDoesNotMatch) {
103     Node node((HostAndPort()));
104     node.tags = NoTags;
105     ASSERT(!node.matches(BSON("dc"
106                               << "NYC")));
107 }
108 
TEST(ReplSetMonitorNode,IncompleteMatch)109 TEST(ReplSetMonitorNode, IncompleteMatch) {
110     Node node((HostAndPort()));
111     node.tags = SampleTags;
112     ASSERT(!node.matches(BSON("dc"
113                               << "NYC"
114                               << "p"
115                               << "2"
116                               << "hello"
117                               << "world")));
118 }
119 
TEST(ReplSetMonitorNode,PartialMatch)120 TEST(ReplSetMonitorNode, PartialMatch) {
121     Node node((HostAndPort()));
122     node.tags = SampleTags;
123     ASSERT(node.matches(BSON("dc"
124                              << "NYC"
125                              << "p"
126                              << "2")));
127 }
128 
TEST(ReplSetMonitorNode,SingleTagCrit)129 TEST(ReplSetMonitorNode, SingleTagCrit) {
130     Node node((HostAndPort()));
131     node.tags = SampleTags;
132     ASSERT(node.matches(BSON("p"
133                              << "2")));
134 }
135 
TEST(ReplSetMonitorNode,BadSingleTagCrit)136 TEST(ReplSetMonitorNode, BadSingleTagCrit) {
137     Node node((HostAndPort()));
138     node.tags = SampleTags;
139     ASSERT(!node.matches(BSON("dc"
140                               << "SF")));
141 }
142 
TEST(ReplSetMonitorNode,NonExistingFieldTag)143 TEST(ReplSetMonitorNode, NonExistingFieldTag) {
144     Node node((HostAndPort()));
145     node.tags = SampleTags;
146     ASSERT(!node.matches(BSON("noSQL"
147                               << "Mongo")));
148 }
149 
TEST(ReplSetMonitorNode,UnorederedMatching)150 TEST(ReplSetMonitorNode, UnorederedMatching) {
151     Node node((HostAndPort()));
152     node.tags = SampleTags;
153     ASSERT(node.matches(BSON("p"
154                              << "2"
155                              << "dc"
156                              << "NYC")));
157 }
158 
TEST(ReplSetMonitorNode,SameValueDiffKey)159 TEST(ReplSetMonitorNode, SameValueDiffKey) {
160     Node node((HostAndPort()));
161     node.tags = SampleTags;
162     ASSERT(!node.matches(BSON("datacenter"
163                               << "NYC")));
164 }
165 
TEST(ReplSetMonitorNode,PriNodeCompatibleTag)166 TEST(ReplSetMonitorNode, PriNodeCompatibleTag) {
167     Node node(HostAndPort("dummy", 3));
168     node.tags = SampleTags;
169 
170     node.isUp = true;
171     node.isMaster = true;
172 
173     BSONArrayBuilder builder;
174     builder.append(BSON("dc"
175                         << "NYC"));
176 
177     TagSet tags(BSONArray(builder.done()));
178 
179     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
180     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
181     ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
182     ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
183     ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
184 }
185 
TEST(ReplSetMonitorNode,SecNodeCompatibleTag)186 TEST(ReplSetMonitorNode, SecNodeCompatibleTag) {
187     Node node(HostAndPort("dummy", 3));
188     node.tags = SampleTags;
189 
190     node.isUp = true;
191     node.isMaster = false;
192 
193     BSONArrayBuilder builder;
194     builder.append(BSON("dc"
195                         << "NYC"));
196 
197     TagSet tags(BSONArray(builder.done()));
198 
199     ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
200     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
201     ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
202     ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
203     ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
204 }
205 
TEST(ReplSetMonitorNode,PriNodeNotCompatibleTag)206 TEST(ReplSetMonitorNode, PriNodeNotCompatibleTag) {
207     Node node(HostAndPort("dummy", 3));
208     node.tags = SampleTags;
209 
210     node.isUp = true;
211     node.isMaster = true;
212 
213     BSONArrayBuilder builder;
214     builder.append(BSON("dc"
215                         << "SF"));
216 
217     TagSet tags(BSONArray(builder.done()));
218 
219     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
220     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
221     ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
222     ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
223     ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
224 }
225 
TEST(ReplSetMonitorNode,SecNodeNotCompatibleTag)226 TEST(ReplSetMonitorNode, SecNodeNotCompatibleTag) {
227     Node node(HostAndPort("dummy", 3));
228     node.tags = SampleTags;
229 
230     node.isUp = true;
231     node.isMaster = false;
232 
233     BSONArrayBuilder builder;
234     builder.append(BSON("dc"
235                         << "SF"));
236 
237     TagSet tags(BSONArray(builder.done()));
238 
239     ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
240     ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
241     ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
242     ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
243     ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
244 }
245 
TEST(ReplSetMonitorNode,PriNodeCompatiblMultiTag)246 TEST(ReplSetMonitorNode, PriNodeCompatiblMultiTag) {
247     Node node(HostAndPort("dummy", 3));
248     node.tags = SampleTags;
249 
250     node.isUp = true;
251     node.isMaster = true;
252 
253     BSONArrayBuilder builder;
254     builder.append(BSON("dc"
255                         << "RP"));
256     builder.append(BSON("dc"
257                         << "NYC"
258                         << "p"
259                         << "2"));
260 
261     TagSet tags(BSONArray(builder.done()));
262 
263     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
264     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
265     ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
266     ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
267     ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
268 }
269 
TEST(ReplSetMonitorNode,SecNodeCompatibleMultiTag)270 TEST(ReplSetMonitorNode, SecNodeCompatibleMultiTag) {
271     Node node(HostAndPort("dummy", 3));
272     node.tags = SampleTags;
273 
274     node.isUp = true;
275     node.isMaster = false;
276 
277     BSONArrayBuilder builder;
278     builder.append(BSON("dc"
279                         << "RP"));
280     builder.append(BSON("dc"
281                         << "NYC"
282                         << "p"
283                         << "2"));
284 
285     TagSet tags(BSONArray(builder.done()));
286 
287     ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
288     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
289     ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
290     ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
291     ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
292 }
293 
TEST(ReplSetMonitorNode,PriNodeNotCompatibleMultiTag)294 TEST(ReplSetMonitorNode, PriNodeNotCompatibleMultiTag) {
295     Node node(HostAndPort("dummy", 3));
296     node.tags = SampleTags;
297 
298     node.isUp = true;
299     node.isMaster = true;
300 
301     BSONArrayBuilder builder;
302     builder.append(BSON("dc"
303                         << "sf"));
304     builder.append(BSON("dc"
305                         << "NYC"
306                         << "P"
307                         << "4"));
308 
309     TagSet tags(BSONArray(builder.done()));
310 
311     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
312     ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
313     ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
314     ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
315     ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
316 }
317 
TEST(ReplSetMonitorNode,SecNodeNotCompatibleMultiTag)318 TEST(ReplSetMonitorNode, SecNodeNotCompatibleMultiTag) {
319     Node node(HostAndPort("dummy", 3));
320     node.tags = SampleTags;
321 
322     node.isUp = true;
323     node.isMaster = false;
324 
325     BSONArrayBuilder builder;
326     builder.append(BSON("dc"
327                         << "sf"));
328     builder.append(BSON("dc"
329                         << "NYC"
330                         << "P"
331                         << "4"));
332 
333     TagSet tags(BSONArray(builder.done()));
334 
335     ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
336     ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
337     ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
338     ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
339     ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
340 }
341 
342 }  // namespace
343