diff --git a/internal/failpoint/failpoint.go b/internal/failpoint/failpoint.go new file mode 100644 index 0000000000..9fe25ba63f --- /dev/null +++ b/internal/failpoint/failpoint.go @@ -0,0 +1,63 @@ +// Copyright (C) MongoDB, Inc. 2024-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +package failpoint + +import ( + "go.mongodb.org/mongo-driver/v2/bson" +) + +const ( + // ModeAlwaysOn is the fail point mode that enables the fail point for an + // indefinite number of matching commands. + ModeAlwaysOn = "alwaysOn" + + // ModeOff is the fail point mode that disables the fail point. + ModeOff = "off" +) + +// FailPoint is used to configure a server fail point. It is intended to be +// passed as the command argument to RunCommand. +// +// For more information about fail points, see +// https://github.com/mongodb/specifications/tree/HEAD/source/transactions/tests#server-fail-point +type FailPoint struct { + ConfigureFailPoint string `bson:"configureFailPoint"` + // Mode should be a string, FailPointMode, or map[string]interface{} + Mode interface{} `bson:"mode"` + Data Data `bson:"data"` +} + +// Mode configures when a fail point will be enabled. It is used to set the +// FailPoint.Mode field. +type Mode struct { + Times int32 `bson:"times"` + Skip int32 `bson:"skip"` +} + +// Data configures how a fail point will behave. It is used to set the +// FailPoint.Data field. +type Data struct { + FailCommands []string `bson:"failCommands,omitempty"` + CloseConnection bool `bson:"closeConnection,omitempty"` + ErrorCode int32 `bson:"errorCode,omitempty"` + FailBeforeCommitExceptionCode int32 `bson:"failBeforeCommitExceptionCode,omitempty"` + ErrorLabels *[]string `bson:"errorLabels,omitempty"` + WriteConcernError *WriteConcernError `bson:"writeConcernError,omitempty"` + BlockConnection bool `bson:"blockConnection,omitempty"` + BlockTimeMS int32 `bson:"blockTimeMS,omitempty"` + AppName string `bson:"appName,omitempty"` +} + +// WriteConcernError is the write concern error to return when the fail point is +// triggered. It is used to set the FailPoint.Data.WriteConcernError field. +type WriteConcernError struct { + Code int32 `bson:"code"` + Name string `bson:"codeName"` + Errmsg string `bson:"errmsg"` + ErrorLabels *[]string `bson:"errorLabels,omitempty"` + ErrInfo bson.Raw `bson:"errInfo,omitempty"` +} diff --git a/internal/integration/change_stream_test.go b/internal/integration/change_stream_test.go index 1738b67650..4bacc746bd 100644 --- a/internal/integration/change_stream_test.go +++ b/internal/integration/change_stream_test.go @@ -17,6 +17,7 @@ import ( "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/eventtest" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/require" "go.mongodb.org/mongo-driver/v2/mongo" @@ -523,12 +524,12 @@ func TestChangeStream_ReplicaSet(t *testing.T) { SetReadConcern(mtest.MajorityRc). SetRetryReads(false)) - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"aggregate"}, CloseConnection: true, }, @@ -546,12 +547,12 @@ func TestChangeStream_ReplicaSet(t *testing.T) { SetReadConcern(mtest.MajorityRc). SetRetryReads(false)) - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"getMore"}, CloseConnection: true, }, @@ -574,12 +575,12 @@ func TestChangeStream_ReplicaSet(t *testing.T) { SetPoolMonitor(tpm.PoolMonitor). SetRetryReads(true)) - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 2, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"aggregate"}, CloseConnection: true, }, diff --git a/internal/integration/client_test.go b/internal/integration/client_test.go index 98d8752e00..7b6298cd79 100644 --- a/internal/integration/client_test.go +++ b/internal/integration/client_test.go @@ -20,6 +20,7 @@ import ( "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/eventtest" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/handshake" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/integtest" @@ -678,10 +679,10 @@ func TestClient(t *testing.T) { _, err := mt.Coll.InsertOne(context.Background(), bson.D{}) require.NoError(mt, err) - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: "alwaysOn", - Data: mtest.FailPointData{ + Mode: failpoint.ModeAlwaysOn, + Data: failpoint.Data{ FailCommands: []string{"find", "insert"}, BlockConnection: true, BlockTimeMS: 500, diff --git a/internal/integration/crud_prose_test.go b/internal/integration/crud_prose_test.go index 8233763807..2072d6db67 100644 --- a/internal/integration/crud_prose_test.go +++ b/internal/integration/crud_prose_test.go @@ -14,6 +14,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" @@ -29,14 +30,14 @@ func TestWriteErrorsWithLabels(t *testing.T) { label := "ExampleError" mt.Run("InsertMany errors with label", func(mt *mtest.T) { - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, - WriteConcernError: &mtest.WriteConcernErrorData{ + WriteConcernError: &failpoint.WriteConcernError{ Code: 100, ErrorLabels: &[]string{label}, }, @@ -60,14 +61,14 @@ func TestWriteErrorsWithLabels(t *testing.T) { }) mt.Run("WriteException with label", func(mt *mtest.T) { - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"delete"}, - WriteConcernError: &mtest.WriteConcernErrorData{ + WriteConcernError: &failpoint.WriteConcernError{ Code: 100, ErrorLabels: &[]string{label}, }, @@ -83,14 +84,14 @@ func TestWriteErrorsWithLabels(t *testing.T) { }) mt.Run("BulkWriteException with label", func(mt *mtest.T) { - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"delete"}, - WriteConcernError: &mtest.WriteConcernErrorData{ + WriteConcernError: &failpoint.WriteConcernError{ Code: 100, ErrorLabels: &[]string{label}, }, @@ -333,14 +334,14 @@ func TestWriteConcernError(t *testing.T) { errInfoDoc := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendDocumentElement(nil, "writeConcern", wcDoc), ) - fp := mtest.FailPoint{ + fp := failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, - WriteConcernError: &mtest.WriteConcernErrorData{ + WriteConcernError: &failpoint.WriteConcernError{ Code: 100, Name: "UnsatisfiableWriteConcern", Errmsg: "Not enough data-bearing nodes", diff --git a/internal/integration/csot_prose_test.go b/internal/integration/csot_prose_test.go index 3923efd6e7..e8df78acbe 100644 --- a/internal/integration/csot_prose_test.go +++ b/internal/integration/csot_prose_test.go @@ -16,6 +16,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/integtest" "go.mongodb.org/mongo-driver/v2/internal/mongoutil" @@ -35,12 +36,12 @@ func TestCSOTProse(t *testing.T) { assert.Nil(mt, err, "Drop error: %v", err) // Configure a fail point to block both inserts of the multi-write for 1010ms (2020ms total). - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 2, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, BlockConnection: true, BlockTimeMS: 1010, @@ -191,12 +192,12 @@ func TestCSOTProse_GridFS(t *testing.T) { SetHosts([]string{failpointHost})) // Set a blocking "insert" fail point. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, BlockConnection: true, BlockTimeMS: 1250, @@ -251,12 +252,12 @@ func TestCSOTProse_GridFS(t *testing.T) { SetHosts([]string{failpointHost})) // Set a blocking "delete" fail point. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"delete"}, BlockConnection: true, BlockTimeMS: 1250, @@ -367,12 +368,12 @@ func TestCSOTProse_GridFS(t *testing.T) { SetHosts([]string{failpointHost})) // Set a blocking "insert" fail point. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, BlockConnection: true, BlockTimeMS: 200, diff --git a/internal/integration/csot_test.go b/internal/integration/csot_test.go index 3112ba8be0..674ec071a0 100644 --- a/internal/integration/csot_test.go +++ b/internal/integration/csot_test.go @@ -16,6 +16,7 @@ import ( "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/eventtest" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/require" "go.mongodb.org/mongo-driver/v2/mongo" @@ -351,10 +352,10 @@ func TestCSOT_maxTimeMS(t *testing.T) { require.NoError(mt, err) } - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: "alwaysOn", - Data: mtest.FailPointData{ + Mode: failpoint.ModeAlwaysOn, + Data: failpoint.Data{ FailCommands: []string{tc.commandName}, BlockConnection: true, // Note that some operations (currently Find and @@ -424,12 +425,12 @@ func TestCSOT_errors(t *testing.T) { _, err := mt.Coll.InsertOne(context.Background(), bson.D{}) require.NoError(mt, err, "InsertOne error") - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"find"}, ErrorCode: 50, // MaxTimeMSExceeded }, @@ -454,12 +455,12 @@ func TestCSOT_errors(t *testing.T) { _, err := mt.Coll.InsertOne(context.Background(), bson.D{}) require.NoError(mt, err, "InsertOne error") - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"find"}, BlockConnection: true, BlockTimeMS: 500, @@ -488,12 +489,12 @@ func TestCSOT_errors(t *testing.T) { _, err := mt.Coll.InsertOne(context.Background(), bson.D{}) require.NoError(mt, err, "InsertOne error") - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"find"}, BlockConnection: true, BlockTimeMS: 100, diff --git a/internal/integration/cursor_test.go b/internal/integration/cursor_test.go index 6c77e7cc6a..5ee9986ec2 100644 --- a/internal/integration/cursor_test.go +++ b/internal/integration/cursor_test.go @@ -15,6 +15,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" @@ -180,13 +181,13 @@ func TestCursor(t *testing.T) { mt.RunOpts("all", noClientOpts, func(mt *mtest.T) { failpointOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion("4.0") mt.RunOpts("getMore error", failpointOpts, func(mt *mtest.T) { - failpointData := mtest.FailPointData{ + failpointData := failpoint.Data{ FailCommands: []string{"getMore"}, ErrorCode: 100, } - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: "alwaysOn", + Mode: failpoint.ModeAlwaysOn, Data: failpointData, }) initCollection(mt, mt.Coll) @@ -252,13 +253,13 @@ func TestCursor(t *testing.T) { mt.RunOpts("close", noClientOpts, func(mt *mtest.T) { failpointOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion("4.0") mt.RunOpts("killCursors error", failpointOpts, func(mt *mtest.T) { - failpointData := mtest.FailPointData{ + failpointData := failpoint.Data{ FailCommands: []string{"killCursors"}, ErrorCode: 100, } - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: "alwaysOn", + Mode: failpoint.ModeAlwaysOn, Data: failpointData, }) initCollection(mt, mt.Coll) diff --git a/internal/integration/database_test.go b/internal/integration/database_test.go index 89d9d87f9f..c661b3b49b 100644 --- a/internal/integration/database_test.go +++ b/internal/integration/database_test.go @@ -16,6 +16,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/handshake" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/mongo" @@ -90,14 +91,14 @@ func TestDatabase(t *testing.T) { }) failpointOpts := mtest.NewOptions().MinServerVersion("4.0").Topologies(mtest.ReplicaSet) mt.RunOpts("gets result and error", failpointOpts, func(mt *mtest.T) { - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, - WriteConcernError: &mtest.WriteConcernErrorData{ + WriteConcernError: &failpoint.WriteConcernError{ Code: 100, }, }, diff --git a/internal/integration/errors_test.go b/internal/integration/errors_test.go index cfc4715512..a1f232bd50 100644 --- a/internal/integration/errors_test.go +++ b/internal/integration/errors_test.go @@ -21,6 +21,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/integtest" "go.mongodb.org/mongo-driver/v2/mongo" @@ -72,12 +73,12 @@ func TestErrors(t *testing.T) { authOpts := mtest.NewOptions().Auth(true).Topologies(mtest.ReplicaSet, mtest.Single).MinServerVersion("4.0") mt.RunOpts("network error during auth", authOpts, func(mt *mtest.T) { - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ // Set the fail point for saslContinue rather than saslStart because the driver will use speculative // auth on 4.4+ so there won't be an explicit saslStart command. FailCommands: []string{"saslContinue"}, @@ -316,12 +317,12 @@ func TestErrors(t *testing.T) { mt.RunOpts("Raw response", mtOpts, func(mt *mtest.T) { mt.Run("CommandError", func(mt *mtest.T) { // Mock a CommandError via failpoint with an arbitrary code. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"find"}, ErrorCode: 123, }, @@ -364,14 +365,14 @@ func TestErrors(t *testing.T) { }) mt.Run("WriteException", func(mt *mtest.T) { // Mock a WriteException via failpoint with an arbitrary WriteConcernError. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"delete"}, - WriteConcernError: &mtest.WriteConcernErrorData{ + WriteConcernError: &failpoint.WriteConcernError{ Code: 123, }, }, diff --git a/internal/integration/index_view_test.go b/internal/integration/index_view_test.go index c6af75ceaa..62625c777a 100644 --- a/internal/integration/index_view_test.go +++ b/internal/integration/index_view_test.go @@ -13,6 +13,7 @@ import ( "github.com/google/go-cmp/cmp" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" @@ -249,10 +250,10 @@ func TestIndexView(t *testing.T) { }) // Needs to run on these versions for failpoints mt.RunOpts("replace error", mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion("4.0"), func(mt *mtest.T) { - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: "alwaysOn", - Data: mtest.FailPointData{ + Mode: failpoint.ModeAlwaysOn, + Data: failpoint.Data{ FailCommands: []string{"createIndexes"}, ErrorCode: 100, }, @@ -395,10 +396,10 @@ func TestIndexView(t *testing.T) { }) // Needs to run on these versions for failpoints mt.RunOpts("replace error", mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion("4.0"), func(mt *mtest.T) { - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: "alwaysOn", - Data: mtest.FailPointData{ + Mode: failpoint.ModeAlwaysOn, + Data: failpoint.Data{ FailCommands: []string{"createIndexes"}, ErrorCode: 100, }, diff --git a/internal/integration/mtest/global_state.go b/internal/integration/mtest/global_state.go index d9f7bf916e..414112bd3f 100644 --- a/internal/integration/mtest/global_state.go +++ b/internal/integration/mtest/global_state.go @@ -11,6 +11,7 @@ import ( "fmt" "go.mongodb.org/mongo-driver/v2/bson" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring" "go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology" @@ -76,7 +77,7 @@ func ServerVersion() string { } // SetFailPoint configures the provided fail point on the cluster under test using the provided Client. -func SetFailPoint(fp FailPoint, client *mongo.Client) error { +func SetFailPoint(fp failpoint.FailPoint, client *mongo.Client) error { admin := client.Database("admin") if err := admin.RunCommand(context.Background(), fp).Err(); err != nil { return fmt.Errorf("error creating fail point: %w", err) diff --git a/internal/integration/mtest/mongotest.go b/internal/integration/mtest/mongotest.go index fb744f0b30..65a15a60c7 100644 --- a/internal/integration/mtest/mongotest.go +++ b/internal/integration/mtest/mongotest.go @@ -19,6 +19,7 @@ import ( "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/csfle" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/mongoutil" "go.mongodb.org/mongo-driver/v2/internal/require" "go.mongodb.org/mongo-driver/v2/mongo" @@ -47,44 +48,6 @@ const ( namespaceExistsErrCode int32 = 48 ) -// FailPoint is a representation of a server fail point. -// See https://github.com/mongodb/specifications/tree/HEAD/source/transactions/tests#server-fail-point -// for more information regarding fail points. -type FailPoint struct { - ConfigureFailPoint string `bson:"configureFailPoint"` - // Mode should be a string, FailPointMode, or map[string]interface{} - Mode interface{} `bson:"mode"` - Data FailPointData `bson:"data"` -} - -// FailPointMode is a representation of the Failpoint.Mode field. -type FailPointMode struct { - Times int32 `bson:"times"` - Skip int32 `bson:"skip"` -} - -// FailPointData is a representation of the FailPoint.Data field. -type FailPointData struct { - FailCommands []string `bson:"failCommands,omitempty"` - CloseConnection bool `bson:"closeConnection,omitempty"` - ErrorCode int32 `bson:"errorCode,omitempty"` - FailBeforeCommitExceptionCode int32 `bson:"failBeforeCommitExceptionCode,omitempty"` - ErrorLabels *[]string `bson:"errorLabels,omitempty"` - WriteConcernError *WriteConcernErrorData `bson:"writeConcernError,omitempty"` - BlockConnection bool `bson:"blockConnection,omitempty"` - BlockTimeMS int32 `bson:"blockTimeMS,omitempty"` - AppName string `bson:"appName,omitempty"` -} - -// WriteConcernErrorData is a representation of the FailPoint.Data.WriteConcern field. -type WriteConcernErrorData struct { - Code int32 `bson:"code"` - Name string `bson:"codeName"` - Errmsg string `bson:"errmsg"` - ErrorLabels *[]string `bson:"errorLabels,omitempty"` - ErrInfo bson.Raw `bson:"errInfo,omitempty"` -} - // T is a wrapper around testing.T. type T struct { // connsCheckedOut is the net number of connections checked out during test execution. @@ -536,7 +499,7 @@ func (t *T) ClearCollections() { // SetFailPoint sets a fail point for the client associated with T. Commands to create the failpoint will appear // in command monitoring channels. The fail point will automatically be disabled after this test has run. -func (t *T) SetFailPoint(fp FailPoint) { +func (t *T) SetFailPoint(fp failpoint.FailPoint) { // ensure mode fields are int32 if modeMap, ok := fp.Mode.(map[string]interface{}); ok { var key string @@ -585,9 +548,9 @@ func (t *T) TrackFailPoint(fpName string) { func (t *T) ClearFailPoints() { db := t.Client.Database("admin") for _, fp := range t.failPointNames { - cmd := bson.D{ - {"configureFailPoint", fp}, - {"mode", "off"}, + cmd := failpoint.FailPoint{ + ConfigureFailPoint: fp, + Mode: failpoint.ModeOff, } err := db.RunCommand(context.Background(), cmd).Err() if err != nil { diff --git a/internal/integration/primary_stepdown_test.go b/internal/integration/primary_stepdown_test.go index e308532b8e..ac7f7b4f21 100644 --- a/internal/integration/primary_stepdown_test.go +++ b/internal/integration/primary_stepdown_test.go @@ -13,6 +13,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/eventtest" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" @@ -81,12 +82,12 @@ func TestConnectionsSurvivePrimaryStepDown(t *testing.T) { // failpoints that cause SDAM state changes. SetHeartbeatInterval(defaultHeartbeatInterval)) - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, ErrorCode: tc.errCode, }, diff --git a/internal/integration/retryable_reads_prose_test.go b/internal/integration/retryable_reads_prose_test.go index 8bffd69ea8..3b2b50f2c8 100644 --- a/internal/integration/retryable_reads_prose_test.go +++ b/internal/integration/retryable_reads_prose_test.go @@ -16,6 +16,7 @@ import ( "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/eventtest" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/mongoutil" "go.mongodb.org/mongo-driver/v2/internal/require" @@ -46,12 +47,12 @@ func TestRetryableReadsProse(t *testing.T) { assert.Nil(mt, err, "InsertOne error: %v", err) // Force Find to block for 1 second once. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"find"}, ErrorCode: 91, BlockConnection: true, @@ -144,12 +145,12 @@ func TestRetryableReadsProse(t *testing.T) { "test cluster must have at least %v mongos hosts", tc.hostCount) // Configure the failpoint options for each mongos. - failPoint := mtest.FailPoint{ + failPoint := failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"find"}, ErrorCode: tc.failpointErrorCode, CloseConnection: false, diff --git a/internal/integration/retryable_writes_prose_test.go b/internal/integration/retryable_writes_prose_test.go index 355a4858d3..64f070ff7c 100644 --- a/internal/integration/retryable_writes_prose_test.go +++ b/internal/integration/retryable_writes_prose_test.go @@ -18,6 +18,7 @@ import ( "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/eventtest" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/mongoutil" "go.mongodb.org/mongo-driver/v2/internal/require" @@ -157,12 +158,12 @@ func TestRetryableWritesProse(t *testing.T) { Topologies(mtest.ReplicaSet, mtest.Sharded) mt.RunOpts("PoolClearedError retryability", mtPceOpts, func(mt *mtest.T) { // Force Find to block for 1 second once. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, ErrorCode: 91, BlockConnection: true, @@ -227,11 +228,11 @@ func TestRetryableWritesProse(t *testing.T) { mt.ResetClient(options.Client().SetRetryWrites(true).SetMonitor(monitor)) // Configure a fail point for a "ShutdownInProgress" error. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{Times: 1}, - Data: mtest.FailPointData{ - WriteConcernError: &mtest.WriteConcernErrorData{ + Mode: failpoint.Mode{Times: 1}, + Data: failpoint.Data{ + WriteConcernError: &failpoint.WriteConcernError{ Code: shutdownInProgressErrorCode, }, FailCommands: []string{"insert"}, @@ -261,10 +262,10 @@ func TestRetryableWritesProse(t *testing.T) { return } - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{Times: 1}, - Data: mtest.FailPointData{ + Mode: failpoint.Mode{Times: 1}, + Data: failpoint.Data{ ErrorCode: notWritablePrimaryErrorCode, ErrorLabels: &[]string{ driver.NoWritesPerformed, @@ -324,12 +325,12 @@ func TestRetryableWritesProse(t *testing.T) { "test cluster must have at least %v mongos hosts", tc.hostCount) // Configure the failpoint options for each mongos. - failPoint := mtest.FailPoint{ + failPoint := failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, ErrorLabels: &[]string{"RetryableWriteError"}, ErrorCode: tc.failpointErrorCode, diff --git a/internal/integration/retryable_writes_spec_test.go b/internal/integration/retryable_writes_spec_test.go index 324c508621..d4cc783554 100644 --- a/internal/integration/retryable_writes_spec_test.go +++ b/internal/integration/retryable_writes_spec_test.go @@ -14,6 +14,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" ) @@ -26,12 +27,12 @@ type retryableWritesTestFile struct { } type retryableWritesTest struct { - Description string `bson:"description"` - ClientOptions bson.Raw `bson:"clientOptions"` - UseMultipleMongoses bool `bson:"useMultipleMongoses"` - FailPoint *mtest.FailPoint `bson:"failPoint"` - Operation crudOperation `bson:"operation"` - Outcome crudOutcome `bson:"outcome"` + Description string `bson:"description"` + ClientOptions bson.Raw `bson:"clientOptions"` + UseMultipleMongoses bool `bson:"useMultipleMongoses"` + FailPoint *failpoint.FailPoint `bson:"failPoint"` + Operation crudOperation `bson:"operation"` + Outcome crudOutcome `bson:"outcome"` } func TestRetryableWritesSpec(t *testing.T) { diff --git a/internal/integration/sdam_error_handling_test.go b/internal/integration/sdam_error_handling_test.go index ee6485cff1..ceaa5a59f8 100644 --- a/internal/integration/sdam_error_handling_test.go +++ b/internal/integration/sdam_error_handling_test.go @@ -19,6 +19,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/eventtest" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" @@ -57,12 +58,12 @@ func TestSDAMErrorHandling(t *testing.T) { appName := "authConnectTimeoutTest" // Set failpoint on saslContinue instead of saslStart because saslStart isn't done when using // speculative auth. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"saslContinue"}, BlockConnection: true, BlockTimeMS: 150, @@ -101,12 +102,12 @@ func TestSDAMErrorHandling(t *testing.T) { // routine encounters a non-timeout network error during handshaking. appName := "authNetworkErrorTestBackground" - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"saslContinue"}, CloseConnection: true, AppName: appName, @@ -134,12 +135,12 @@ func TestSDAMErrorHandling(t *testing.T) { // checkout encounters a non-timeout network error during handshaking. appName := "authNetworkErrorTestForeground" - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"saslContinue"}, CloseConnection: true, AppName: appName, @@ -169,12 +170,12 @@ func TestSDAMErrorHandling(t *testing.T) { mt.Run("pool cleared on non-timeout network error", func(mt *mtest.T) { appName := "afterHandshakeNetworkError" - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, CloseConnection: true, AppName: appName, @@ -268,12 +269,12 @@ func TestSDAMErrorHandling(t *testing.T) { appName := fmt.Sprintf("command_error_%s", tc.name) // Cause the next insert to fail with an ok:0 response. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, ErrorCode: tc.errorCode, AppName: appName, @@ -290,14 +291,14 @@ func TestSDAMErrorHandling(t *testing.T) { appName := fmt.Sprintf("write_concern_error_%s", tc.name) // Cause the next insert to fail with a write concern error. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"insert"}, - WriteConcernError: &mtest.WriteConcernErrorData{ + WriteConcernError: &failpoint.WriteConcernError{ Code: tc.errorCode, }, AppName: appName, diff --git a/internal/integration/sdam_prose_test.go b/internal/integration/sdam_prose_test.go index 776c63dd6e..74e5d809fe 100644 --- a/internal/integration/sdam_prose_test.go +++ b/internal/integration/sdam_prose_test.go @@ -19,6 +19,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/handshake" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/mongoutil" @@ -118,12 +119,12 @@ func TestSDAMProse(t *testing.T) { } // Force hello requests to block for 500ms and wait until a server's average RTT goes over 250ms. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 1000, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{handshake.LegacyHello, "hello"}, BlockConnection: true, BlockTimeMS: 500, @@ -152,12 +153,12 @@ func TestSDAMProse(t *testing.T) { mt.RunOpts("client waits between failed Hellos", mtest.NewOptions().MinServerVersion("4.9").Topologies(mtest.Single), func(mt *mtest.T) { // Force hello requests to fail 5 times. - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 5, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{handshake.LegacyHello, "hello"}, ErrorCode: 1234, AppName: "SDAMMinHeartbeatFrequencyTest", diff --git a/internal/integration/server_selection_prose_test.go b/internal/integration/server_selection_prose_test.go index 746d4bf477..05641dbf75 100644 --- a/internal/integration/server_selection_prose_test.go +++ b/internal/integration/server_selection_prose_test.go @@ -16,6 +16,7 @@ import ( "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/eventtest" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/mongoutil" "go.mongodb.org/mongo-driver/v2/internal/require" @@ -126,12 +127,12 @@ func TestServerSelectionProse(t *testing.T) { failpointHost := hosts[0] mt.ResetClient(options.Client(). SetHosts([]string{failpointHost})) - mt.SetFailPoint(mtest.FailPoint{ + mt.SetFailPoint(failpoint.FailPoint{ ConfigureFailPoint: "failCommand", - Mode: mtest.FailPointMode{ + Mode: failpoint.Mode{ Times: 10000, }, - Data: mtest.FailPointData{ + Data: failpoint.Data{ FailCommands: []string{"find"}, BlockConnection: true, BlockTimeMS: 500, diff --git a/internal/integration/unified_spec_test.go b/internal/integration/unified_spec_test.go index fcee24a989..20668b9578 100644 --- a/internal/integration/unified_spec_test.go +++ b/internal/integration/unified_spec_test.go @@ -24,6 +24,7 @@ import ( "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/bsonutil" + "go.mongodb.org/mongo-driver/v2/internal/failpoint" "go.mongodb.org/mongo-driver/v2/internal/integration/mtest" "go.mongodb.org/mongo-driver/v2/internal/integtest" "go.mongodb.org/mongo-driver/v2/internal/mongoutil" @@ -472,7 +473,7 @@ func executeTestRunnerOperation(mt *mtest.T, testCase *testCase, op *operation, case "targetedFailPoint": fpDoc := op.Arguments.Lookup("failPoint") - var fp mtest.FailPoint + var fp failpoint.FailPoint if err := bson.Unmarshal(fpDoc.Document(), &fp); err != nil { return fmt.Errorf("Unmarshal error: %w", err) }