Skip to content

Commit 94b628a

Browse files
Divjot AroraMohammad Fahim Abrar
Divjot Arora
authored and
Mohammad Fahim Abrar
committed
GODRIVER-1931 Run tests against LBs in Evergreen (mongodb#648)
1 parent 8de3cec commit 94b628a

16 files changed

+352
-33
lines changed

.evergreen/config.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,46 @@ functions:
518518
PKG_CONFIG_PATH=$PKG_CONFIG_PATH \
519519
LD_LIBRARY_PATH=$LD_LIBRARY_PATH
520520
521+
run-load-balancer-tests:
522+
- command: shell.exec
523+
type: test
524+
params:
525+
working_dir: src/go.mongodb.org/mongo-driver
526+
script: |
527+
${PREPARE_SHELL}
528+
529+
if [ ${SSL} = "ssl" ]; then
530+
export MONGO_GO_DRIVER_CA_FILE="$PROJECT_DIRECTORY/data/certificates/ca.pem"
531+
if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin
532+
export MONGO_GO_DRIVER_CA_FILE=$(cygpath -m $MONGO_GO_DRIVER_CA_FILE)
533+
fi
534+
fi
535+
536+
# Verify that the required LB URI expansions are set to ensure that the test runner can correctly connect to
537+
# the LBs.
538+
if [ -z "${SINGLE_MONGOS_LB_URI}" ]; then
539+
echo "SINGLE_MONGOS_LB_URI must be set for testing against LBs"
540+
exit 1
541+
fi
542+
if [ -z "${MULTI_MONGOS_LB_URI}" ]; then
543+
echo "MULTI_MONGOS_LB_URI must be set for testing against LBs"
544+
exit 1
545+
fi
546+
547+
# Per the LB testing spec, the URI of an LB fronting a single mongos should be used to configure internal
548+
# testing Client instances, so we set MONGODB_URI to SINGLE_MONGOS_LB_URI.
549+
550+
export GOFLAGS=-mod=vendor
551+
set +o xtrace
552+
AUTH="${AUTH}" \
553+
SSL="${SSL}" \
554+
MONGODB_URI="${SINGLE_MONGOS_LB_URI}" \
555+
SINGLE_MONGOS_LB_URI="${SINGLE_MONGOS_LB_URI}" \
556+
MULTI_MONGOS_LB_URI="${MULTI_MONGOS_LB_URI}" \
557+
TOPOLOGY="${TOPOLOGY}" \
558+
MONGO_GO_DRIVER_COMPRESSOR=${MONGO_GO_DRIVER_COMPRESSOR} \
559+
make evg-test-load-balancers
560+
521561
run-atlas-data-lake-test:
522562
- command: shell.exec
523563
type: test
@@ -612,6 +652,21 @@ functions:
612652
-v \
613653
--fault revoked
614654
655+
run-load-balancer:
656+
- command: shell.exec
657+
params:
658+
script: |
659+
DRIVERS_TOOLS=${DRIVERS_TOOLS} MONGODB_URI=${MONGODB_URI} bash ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh start
660+
- command: expansions.update
661+
params:
662+
file: lb-expansion.yml
663+
664+
stop-load-balancer:
665+
- command: shell.exec
666+
params:
667+
script: |
668+
DRIVERS_TOOLS=${DRIVERS_TOOLS} bash ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh stop
669+
615670
add-aws-auth-variables-to-file:
616671
- command: shell.exec
617672
type: test
@@ -866,6 +921,7 @@ post:
866921
files:
867922
- "src/go.mongodb.org/mongo-driver/*.suite"
868923
- func: upload-mo-artifacts
924+
- func: stop-load-balancer
869925
- func: cleanup
870926

871927
tasks:
@@ -1379,6 +1435,28 @@ tasks:
13791435
- func: bootstrap-mongohoused
13801436
- func: run-atlas-data-lake-test
13811437

1438+
- name: test-load-balancer-noauth-nossl
1439+
tags: ["load-balancer"]
1440+
commands:
1441+
- func: bootstrap-mongo-orchestration
1442+
vars:
1443+
TOPOLOGY: "sharded_cluster"
1444+
AUTH: "noauth"
1445+
SSL: "nossl"
1446+
- func: run-load-balancer
1447+
- func: run-load-balancer-tests
1448+
1449+
- name: test-load-balancer-auth-ssl
1450+
tags: ["load-balancer"]
1451+
commands:
1452+
- func: bootstrap-mongo-orchestration
1453+
vars:
1454+
TOPOLOGY: "sharded_cluster"
1455+
AUTH: "auth"
1456+
SSL: "ssl"
1457+
- func: run-load-balancer
1458+
- func: run-load-balancer-tests
1459+
13821460
- name: test-replicaset-noauth-nossl
13831461
tags: ["test", "replicaset"]
13841462
commands:
@@ -1974,3 +2052,10 @@ buildvariants:
19742052
display_name: "KMS TLS ${version} ${os-ssl-40}"
19752053
tasks:
19762054
- name: ".kms-tls"
2055+
2056+
- matrix_name: "load-balancer-test"
2057+
# The LB software is only available on Ubuntu 18.04, so we don't test on all OSes.
2058+
matrix_spec: { version: ["latest"], os-ssl-40: ["ubuntu1804-64-go-1-15"] }
2059+
display_name: "Load Balancer Support ${version} ${os-ssl-40}"
2060+
tasks:
2061+
- name: ".load-balancer"

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ evg-test-versioned-api:
165165
go test -exec "env PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) LD_LIBRARY_PATH=$(LD_LIBRARY_PATH)" $(BUILD_TAGS) -v -timeout $(TEST_TIMEOUT)s $$TEST_PKG >> test.suite ; \
166166
done
167167

168+
.PHONY: evg-test-load-balancers
169+
evg-test-load-balancers:
170+
go test $(BUILD_TAGS) ./mongo/integration -run TestUnifiedSpecs/retryable-reads -v -timeout $(TEST_TIMEOUT)s >> test.suite
171+
go test $(BUILD_TAGS) ./mongo/integration -run TestRetryableWritesSpec -v -timeout $(TEST_TIMEOUT)s >> test.suite
172+
go test $(BUILD_TAGS) ./mongo/integration -run TestChangeStreamSpec -v -timeout $(TEST_TIMEOUT)s >> test.suite
173+
go test $(BULID_TAGS) ./mongo/integration -run TestInitialDNSSeedlistDiscoverySpec/load_balanced -v -timeout $(TEST_TIMEOUT)s >> test.suite
174+
go test $(BUILD_TAGS) ./mongo/integration -run TestLoadBalancerSupport -v -timeout $(TEST_TIMEOUT)s >> test.suite
175+
go test $(BUILD_TAGS) ./mongo/integration/unified -run TestUnifiedSpec -v -timeout $(TEST_TIMEOUT)s >> test.suite
176+
168177
.PHONY: evg-test-kms
169178
evg-test-kms:
170179
go test -exec "env PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) LD_LIBRARY_PATH=$(LD_LIBRARY_PATH)" $(BUILD_TAGS) -v -timeout $(TEST_TIMEOUT)s ./mongo/integration -run TestClientSideEncryptionProse/kms_tls_tests >> test.suite

internal/const.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@ package internal // import "go.mongodb.org/mongo-driver/internal"
88

99
// Version is the current version of the driver.
1010
var Version = "local build"
11+
12+
// SetMockServiceID enables a mode in which the driver mocks server support for returning a "serviceId" field in "hello"
13+
// command responses by using the value of "topologyVersion.processId". This is used for testing load balancer support
14+
// until an upstream service can support running behind a load balancer.
15+
var SetMockServiceID = false

mongo/description/server.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ func NewServer(addr address.Address, response bson.Raw) Server {
266266
desc.LastError = err
267267
return desc
268268
}
269+
270+
if internal.SetMockServiceID {
271+
desc.ServiceID = &desc.TopologyVersion.ProcessID
272+
}
269273
}
270274
}
271275

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (C) MongoDB, Inc. 2017-present.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
// not use this file except in compliance with the License. You may obtain
5+
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
package integration
8+
9+
import (
10+
"context"
11+
"fmt"
12+
"strings"
13+
"testing"
14+
"time"
15+
16+
"go.mongodb.org/mongo-driver/bson"
17+
"go.mongodb.org/mongo-driver/internal/testutil/assert"
18+
"go.mongodb.org/mongo-driver/mongo"
19+
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
20+
"go.mongodb.org/mongo-driver/mongo/options"
21+
)
22+
23+
func TestLoadBalancerSupport(t *testing.T) {
24+
mt := mtest.New(t, mtest.NewOptions().Topologies(mtest.LoadBalanced).CreateClient(false))
25+
defer mt.Close()
26+
27+
mt.Run("RunCommandCursor pins to a connection", func(mt *mtest.T) {
28+
// The LB spec tests cover the behavior for cursors created by CRUD operations, but RunCommandCursor is
29+
// Go-specific so there is no spec test coverage for it.
30+
31+
initCollection(mt, mt.Coll)
32+
findCmd := bson.D{
33+
{"find", mt.Coll.Name()},
34+
{"filter", bson.D{}},
35+
{"batchSize", 2},
36+
}
37+
cursor, err := mt.DB.RunCommandCursor(mtest.Background, findCmd)
38+
assert.Nil(mt, err, "RunCommandCursor error: %v", err)
39+
defer func() {
40+
_ = cursor.Close(mtest.Background)
41+
}()
42+
43+
assert.True(mt, cursor.ID() > 0, "expected cursor ID to be non-zero")
44+
assert.Equal(mt, 1, mt.NumberConnectionsCheckedOut(),
45+
"expected one connection to be checked out, got %d", mt.NumberConnectionsCheckedOut())
46+
})
47+
48+
mt.RunOpts("wait queue timeout errors include extra information", noClientOpts, func(mt *mtest.T) {
49+
// There are spec tests to assert this behavior, but they rely on the waitQueueTimeoutMS Client option, which is
50+
// not supported in Go, so we have to skip them. These prose tests make the same assertions, but use context
51+
// deadlines to force wait queue timeout errors.
52+
53+
assertErrorHasInfo := func(mt *mtest.T, err error, numCursorConns, numTxnConns, numOtherConns int) {
54+
mt.Helper()
55+
56+
assert.NotNil(mt, err, "expected wait queue timeout error, got nil")
57+
expectedMsg := fmt.Sprintf("maxPoolSize: 1, "+
58+
"connections in use by cursors: %d, "+
59+
"connections in use by transactions: %d, "+
60+
"connections in use by other operations: %d",
61+
numCursorConns, numTxnConns, numOtherConns,
62+
)
63+
assert.True(mt, strings.Contains(err.Error(), expectedMsg),
64+
"expected error %q to contain substring %q", err, expectedMsg)
65+
}
66+
maxPoolSizeMtOpts := mtest.NewOptions().
67+
ClientOptions(options.Client().SetMaxPoolSize(1))
68+
69+
mt.RunOpts("cursors", maxPoolSizeMtOpts, func(mt *mtest.T) {
70+
initCollection(mt, mt.Coll)
71+
findOpts := options.Find().SetBatchSize(2)
72+
cursor, err := mt.Coll.Find(mtest.Background, bson.M{}, findOpts)
73+
assert.Nil(mt, err, "Find error: %v", err)
74+
defer func() {
75+
_ = cursor.Close(mtest.Background)
76+
}()
77+
78+
ctx, cancel := context.WithTimeout(mtest.Background, 5*time.Millisecond)
79+
defer cancel()
80+
_, err = mt.Coll.InsertOne(ctx, bson.M{"x": 1})
81+
assertErrorHasInfo(mt, err, 1, 0, 0)
82+
})
83+
mt.RunOpts("transactions", maxPoolSizeMtOpts, func(mt *mtest.T) {
84+
sess, err := mt.Client.StartSession()
85+
assert.Nil(mt, err, "StartSession error: %v", err)
86+
defer sess.EndSession(mtest.Background)
87+
sessCtx := mongo.NewSessionContext(context.Background(), sess)
88+
89+
// Start a transaction and perform one transactional operation to pin a connection.
90+
err = sess.StartTransaction()
91+
assert.Nil(mt, err, "StartTransaction error: %v", err)
92+
_, err = mt.Coll.InsertOne(sessCtx, bson.M{"x": 1})
93+
assert.Nil(mt, err, "InsertOne error: %v", err)
94+
95+
ctx, cancel := context.WithTimeout(mtest.Background, 5*time.Millisecond)
96+
defer cancel()
97+
_, err = mt.Coll.InsertOne(ctx, bson.M{"x": 1})
98+
assertErrorHasInfo(mt, err, 0, 1, 0)
99+
})
100+
})
101+
}

mongo/integration/main_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,22 @@ package integration
99
import (
1010
"log"
1111
"os"
12+
"strings"
1213
"testing"
1314

15+
"go.mongodb.org/mongo-driver/internal"
1416
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
1517
)
1618

1719
func TestMain(m *testing.M) {
20+
// If the cluster is behind a load balancer, enable the SetMockServiceID flag to mock server-side LB support.
21+
if strings.Contains(os.Getenv("MONGODB_URI"), "loadBalanced=true") {
22+
internal.SetMockServiceID = true
23+
defer func() {
24+
internal.SetMockServiceID = false
25+
}()
26+
}
27+
1828
if err := mtest.Setup(); err != nil {
1929
log.Fatal(err)
2030
}

mongo/integration/mtest/global_state.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ func ClusterURI() string {
3535
return testContext.connString.Original
3636
}
3737

38+
// SingleMongosLoadBalancerURI returns the URI for a load balancer fronting a single mongos. This will only be set
39+
// if the cluster is load balanced.
40+
func SingleMongosLoadBalancerURI() string {
41+
return testContext.singleMongosLoadBalancerURI
42+
}
43+
44+
// MultiMongosLoadBalancerURI returns the URI for a load balancer fronting multiple mongoses. This will only be set
45+
// if the cluster is load balanced.
46+
func MultiMongosLoadBalancerURI() string {
47+
return testContext.multiMongosLoadBalancerURI
48+
}
49+
3850
// ClusterConnString returns the parsed ConnString for the cluster.
3951
func ClusterConnString() connstring.ConnString {
4052
return testContext.connString

mongo/integration/mtest/mongotest.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,11 @@ func (t *T) GetProxiedMessages() []*ProxyMessage {
359359
return t.proxyDialer.Messages()
360360
}
361361

362+
// NumberConnectionsCheckedOut returns the number of connections checked out from the test Client.
363+
func (t *T) NumberConnectionsCheckedOut() int {
364+
return t.connsCheckedOut
365+
}
366+
362367
// ClearEvents clears the existing command monitoring events.
363368
func (t *T) ClearEvents() {
364369
t.started = t.started[:0]

mongo/integration/mtest/setup.go

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,17 @@ var testContext struct {
4343
// shardedReplicaSet will be true if we're connected to a sharded cluster and each shard is backed by a replica set.
4444
// We track this as a separate boolean rather than setting topoKind to ShardedReplicaSet because a general
4545
// "Sharded" constraint in a test should match both Sharded and ShardedReplicaSet.
46-
shardedReplicaSet bool
47-
client *mongo.Client // client used for setup and teardown
48-
serverVersion string
49-
authEnabled bool
50-
sslEnabled bool
51-
enterpriseServer bool
52-
dataLake bool
53-
requireAPIVersion bool
54-
serverParameters bson.Raw
46+
shardedReplicaSet bool
47+
client *mongo.Client // client used for setup and teardown
48+
serverVersion string
49+
authEnabled bool
50+
sslEnabled bool
51+
enterpriseServer bool
52+
dataLake bool
53+
requireAPIVersion bool
54+
serverParameters bson.Raw
55+
singleMongosLoadBalancerURI string
56+
multiMongosLoadBalancerURI string
5557
}
5658

5759
func setupClient(cs connstring.ConnString, opts *options.ClientOptions) (*mongo.Client, error) {
@@ -75,7 +77,7 @@ func Setup(setupOpts ...*SetupOptions) error {
7577
case opts.URI != nil:
7678
testContext.connString, err = connstring.ParseAndValidate(*opts.URI)
7779
default:
78-
testContext.connString, err = getConnString()
80+
testContext.connString, err = getClusterConnString()
7981
}
8082
if err != nil {
8183
return fmt.Errorf("error getting connection string: %v", err)
@@ -176,6 +178,22 @@ func Setup(setupOpts ...*SetupOptions) error {
176178
}
177179
}
178180

181+
// For load balanced clusters, retrieve the required LB URIs and add additional information (e.g. TLS options) to
182+
// them if necessary.
183+
if testContext.topoKind == LoadBalanced {
184+
singleMongosURI := os.Getenv("SINGLE_MONGOS_LB_URI")
185+
if singleMongosURI == "" {
186+
return errors.New("SINGLE_MONGOS_LB_URI must be set when running against load balanced clusters")
187+
}
188+
testContext.singleMongosLoadBalancerURI = addNecessaryParamsToURI(singleMongosURI)
189+
190+
multiMongosURI := os.Getenv("MULTI_MONGOS_LB_URI")
191+
if multiMongosURI == "" {
192+
return errors.New("MULTI_MONGOS_LB_URI must be set when running against load balanced clusters")
193+
}
194+
testContext.multiMongosLoadBalancerURI = addNecessaryParamsToURI(multiMongosURI)
195+
}
196+
179197
testContext.authEnabled = os.Getenv("AUTH") == "auth"
180198
testContext.sslEnabled = os.Getenv("SSL") == "ssl"
181199
biRes, err := testContext.client.Database("admin").RunCommand(Background, bson.D{{"buildInfo", 1}}).DecodeBytes()
@@ -282,17 +300,21 @@ func addCompressors(uri string) string {
282300
return addOptions(uri, "compressors=", comp)
283301
}
284302

285-
// ConnString gets the globally configured connection string.
286-
func getConnString() (connstring.ConnString, error) {
303+
// getClusterConnString gets the globally configured connection string.
304+
func getClusterConnString() (connstring.ConnString, error) {
287305
uri := os.Getenv("MONGODB_URI")
288306
if uri == "" {
289307
uri = "mongodb://localhost:27017"
290308
}
291-
uri = addTLSConfig(uri)
292-
uri = addCompressors(uri)
309+
uri = addNecessaryParamsToURI(uri)
293310
return connstring.ParseAndValidate(uri)
294311
}
295312

313+
func addNecessaryParamsToURI(uri string) string {
314+
uri = addTLSConfig(uri)
315+
return addCompressors(uri)
316+
}
317+
296318
// CompareServerVersions compares two version number strings (i.e. positive integers separated by
297319
// periods). Comparisons are done to the lesser precision of the two versions. For example, 3.2 is
298320
// considered equal to 3.2.11, whereas 3.2.0 is considered less than 3.2.11.

0 commit comments

Comments
 (0)