From 36c81c0f2181ece497ba181ca05740ee1f50ef3c Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 29 Feb 2024 12:55:15 +0900 Subject: [PATCH 01/20] initial --- cmd/envtool/envtool.go | 105 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index 289e90b8b358..5e60a8912fd0 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -40,6 +40,9 @@ import ( "github.com/FerretDB/FerretDB/build/version" mysqlpool "github.com/FerretDB/FerretDB/internal/backends/mysql/metadata/pool" "github.com/FerretDB/FerretDB/internal/backends/postgresql/metadata/pool" + "github.com/FerretDB/FerretDB/internal/clientconn" + "github.com/FerretDB/FerretDB/internal/clientconn/connmetrics" + "github.com/FerretDB/FerretDB/internal/handler/registry" "github.com/FerretDB/FerretDB/internal/util/ctxutil" "github.com/FerretDB/FerretDB/internal/util/debug" "github.com/FerretDB/FerretDB/internal/util/lazyerrors" @@ -219,6 +222,108 @@ func setupMongodb(ctx context.Context, logger *zap.SugaredLogger) error { return ctx.Err() } +// setupUser creates a default user username/password for PostgreSQL backend. +// If the user already exists, it does nothing. +func setupUser(ctx context.Context, logger *zap.SugaredLogger) error { + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + if err := waitForPort(ctx, logger.Named("postgreSQL"), 5432); err != nil { + return err + } + + sp, err := state.NewProvider("") + if err != nil { + return err + } + + listenerMetrics := connmetrics.NewListenerMetrics() + handlerOpts := ®istry.NewHandlerOpts{ + Logger: logger.Desugar(), + ConnMetrics: listenerMetrics.ConnMetrics, + StateProvider: sp, + PostgreSQLURL: "postgres://username:password@127.0.0.1:5432/ferretdb?search_path=", + TestOpts: registry.TestOpts{ + CappedCleanupPercentage: 20, + EnableNewAuth: true, + }, + } + + h, closeBackend, err := registry.NewHandler("postgresql", handlerOpts) + if err != nil { + return err + } + + defer closeBackend() + + listenerOpts := clientconn.NewListenerOpts{ + Mode: clientconn.NormalMode, + Metrics: listenerMetrics, + Handler: h, + Logger: logger.Desugar(), + TCP: ":27017", + } + + l := clientconn.NewListener(&listenerOpts) + + runDone := make(chan struct{}) + + go func() { + defer close(runDone) + + err := l.Run(ctx) + if err == nil || errors.Is(err, context.Canceled) { + logger.Info("Listener stopped without error") + } else { + logger.Error("Listener stopped", zap.Error(err)) + } + }() + + defer func() { + <-runDone + }() + + port := l.TCPAddr().(*net.TCPAddr).Port + + var buf1 bytes.Buffer + var retry int64 + + eval := `' + if (db.getSiblingDB("admin").getUser("username") == null){ + db.getSiblingDB("admin").createUser( + {user: "username", pwd: "password", roles: [], mechanisms: ["SCRAM-SHA-1","SCRAM-SHA-256","PLAIN"]} + ) + } +'` + args := []string{ + "compose", + "exec", + "-T", + "mongodb", + "mongosh", + "--host=host.docker.internal", + fmt.Sprintf("--port=%d", port), + "--eval", + eval, + } + + for ctx.Err() == nil { + buf1.Reset() + + err := runCommand("docker", args, &buf1, logger) + if err == nil { + break + } + + logger.Infof("%s:\n%s", err, buf1.String()) + + retry++ + ctxutil.SleepWithJitter(ctx, time.Second, retry) + } + + return ctx.Err() +} + // setupMongodbSecured configures `mongodb_secured` container. func setupMongodbSecured(ctx context.Context, logger *zap.SugaredLogger) error { if err := waitForPort(ctx, logger.Named("mongodb_secured"), 47018); err != nil { From d3a2bef7c69daca2615f008539e97a0ad385d25c Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 29 Feb 2024 14:39:02 +0900 Subject: [PATCH 02/20] return error if address is already used --- cmd/envtool/envtool.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index 5e60a8912fd0..c2afbb99a543 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -266,16 +266,15 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger) error { l := clientconn.NewListener(&listenerOpts) - runDone := make(chan struct{}) + runDone := make(chan error) go func() { defer close(runDone) - err := l.Run(ctx) - if err == nil || errors.Is(err, context.Canceled) { - logger.Info("Listener stopped without error") - } else { - logger.Error("Listener stopped", zap.Error(err)) + if err := l.Run(ctx); err != nil && !errors.Is(err, context.Canceled) { + runDone <- err + + return } }() @@ -283,9 +282,16 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger) error { <-runDone }() + select { + case err := <-runDone: + if err != nil { + return err + } + case <-time.After(time.Millisecond): + } + port := l.TCPAddr().(*net.TCPAddr).Port - var buf1 bytes.Buffer var retry int64 eval := `' @@ -307,20 +313,23 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger) error { eval, } + var buf bytes.Buffer for ctx.Err() == nil { - buf1.Reset() + buf.Reset() - err := runCommand("docker", args, &buf1, logger) + err := runCommand("docker", args, &buf, logger) if err == nil { break } - logger.Infof("%s:\n%s", err, buf1.String()) + logger.Infof("%s:\n%s", err, buf.String()) retry++ ctxutil.SleepWithJitter(ctx, time.Second, retry) } + close(runDone) + return ctx.Err() } From 2043e925ea8b229d125f49a8507324c05899709b Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 29 Feb 2024 15:56:12 +0900 Subject: [PATCH 03/20] update comment --- cmd/envtool/envtool.go | 27 +++++++++++++++++---------- cmd/envtool/envtool_test.go | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index c2afbb99a543..11270c4eef03 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -222,12 +222,13 @@ func setupMongodb(ctx context.Context, logger *zap.SugaredLogger) error { return ctx.Err() } -// setupUser creates a default user username/password for PostgreSQL backend. -// If the user already exists, it does nothing. -func setupUser(ctx context.Context, logger *zap.SugaredLogger) error { - ctx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() - +// setupUser creates a user in admin database with supported mechanisms. +// The user uses username/password credential which is the same as the PostgreSQL +// credentials. +// +// Without this, once the first user is created, the authentication fails +// as username/password does not exist in admin.system.users collection. +func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQlURL string) error { if err := waitForPort(ctx, logger.Named("postgreSQL"), 5432); err != nil { return err } @@ -242,7 +243,7 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger) error { Logger: logger.Desugar(), ConnMetrics: listenerMetrics.ConnMetrics, StateProvider: sp, - PostgreSQLURL: "postgres://username:password@127.0.0.1:5432/ferretdb?search_path=", + PostgreSQLURL: postgreSQlURL, TestOpts: registry.TestOpts{ CappedCleanupPercentage: 20, EnableNewAuth: true, @@ -261,7 +262,7 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger) error { Metrics: listenerMetrics, Handler: h, Logger: logger.Desugar(), - TCP: ":27017", + TCP: "127.0.0.1:0", } l := clientconn.NewListener(&listenerOpts) @@ -273,8 +274,6 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger) error { if err := l.Run(ctx); err != nil && !errors.Is(err, context.Canceled) { runDone <- err - - return } }() @@ -379,6 +378,14 @@ func setup(ctx context.Context, logger *zap.SugaredLogger) error { } } + if err := setupUser(ctx, logger, "postgres://username:password@localhost:5432/ferretdb"); err != nil { + return err + } + + if err := setupUser(ctx, logger, "postgres://username:password@localhost:5433/ferretdb"); err != nil { + return err + } + logger.Info("Done.") return nil } diff --git a/cmd/envtool/envtool_test.go b/cmd/envtool/envtool_test.go index 0ebcef8c0f89..8f532d39c7e2 100644 --- a/cmd/envtool/envtool_test.go +++ b/cmd/envtool/envtool_test.go @@ -16,8 +16,10 @@ package main import ( "bytes" + "context" "os" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -92,3 +94,23 @@ func TestPackageVersion(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "1.0.0", output.String()) } + +func TestSetupUser(t *testing.T) { + if testing.Short() { + t.Skip("skipping in -short mode") + } + + t.Parallel() + + ctx, cancel := context.WithTimeout(testutil.Ctx(t), 5*time.Second) + t.Cleanup(cancel) + + l := testutil.Logger(t).Sugar() + + err := setupUser(ctx, l, "postgres://username@localhost:5432/ferretdb") + require.NoError(t, err) + + // if the user already exists, it should not fail + err = setupUser(ctx, l, "postgres://username@localhost:5432/ferretdb") + require.NoError(t, err) +} From b2f6205218ed7bc4e5464347ebcdd92c836d90a0 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 29 Feb 2024 16:03:34 +0900 Subject: [PATCH 04/20] use port number --- cmd/envtool/envtool.go | 10 +++++----- cmd/envtool/envtool_test.go | 7 +++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index 11270c4eef03..56a4c3938f43 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -228,8 +228,8 @@ func setupMongodb(ctx context.Context, logger *zap.SugaredLogger) error { // // Without this, once the first user is created, the authentication fails // as username/password does not exist in admin.system.users collection. -func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQlURL string) error { - if err := waitForPort(ctx, logger.Named("postgreSQL"), 5432); err != nil { +func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort uint16) error { + if err := waitForPort(ctx, logger.Named("postgreSQL"), postgreSQLPort); err != nil { return err } @@ -237,7 +237,7 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQlURL str if err != nil { return err } - + postgreSQlURL := fmt.Sprintf("postgres://username:password@localhost:%d/ferretdb", postgreSQLPort) listenerMetrics := connmetrics.NewListenerMetrics() handlerOpts := ®istry.NewHandlerOpts{ Logger: logger.Desugar(), @@ -378,11 +378,11 @@ func setup(ctx context.Context, logger *zap.SugaredLogger) error { } } - if err := setupUser(ctx, logger, "postgres://username:password@localhost:5432/ferretdb"); err != nil { + if err := setupUser(ctx, logger, 5432); err != nil { return err } - if err := setupUser(ctx, logger, "postgres://username:password@localhost:5433/ferretdb"); err != nil { + if err := setupUser(ctx, logger, 5433); err != nil { return err } diff --git a/cmd/envtool/envtool_test.go b/cmd/envtool/envtool_test.go index 8f532d39c7e2..4af406fb344b 100644 --- a/cmd/envtool/envtool_test.go +++ b/cmd/envtool/envtool_test.go @@ -107,10 +107,13 @@ func TestSetupUser(t *testing.T) { l := testutil.Logger(t).Sugar() - err := setupUser(ctx, l, "postgres://username@localhost:5432/ferretdb") + err := setupUser(ctx, l, 5432) require.NoError(t, err) // if the user already exists, it should not fail - err = setupUser(ctx, l, "postgres://username@localhost:5432/ferretdb") + err = setupUser(ctx, l, 5432) + require.NoError(t, err) + + err = setupUser(ctx, l, 5433) require.NoError(t, err) } From ff25f6f7360f62163635884acee933d4e43d2d9f Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 29 Feb 2024 17:08:48 +0900 Subject: [PATCH 05/20] setup user as part of postgresql setup --- cmd/envtool/envtool.go | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index 56a4c3938f43..c3f9ae39b295 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -135,7 +135,7 @@ func setupAnyPostgres(ctx context.Context, logger *zap.SugaredLogger, uri string return ctx.Err() } - return nil + return setupUser(ctx, logger, uint16(port)) } // setupPostgres configures `postgres` container. @@ -237,6 +237,7 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui if err != nil { return err } + postgreSQlURL := fmt.Sprintf("postgres://username:password@localhost:%d/ferretdb", postgreSQLPort) listenerMetrics := connmetrics.NewListenerMetrics() handlerOpts := ®istry.NewHandlerOpts{ @@ -267,22 +268,20 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui l := clientconn.NewListener(&listenerOpts) - runDone := make(chan error) + runErr := make(chan error) go func() { - defer close(runDone) + if err := l.Run(ctx); err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { + runErr <- err - if err := l.Run(ctx); err != nil && !errors.Is(err, context.Canceled) { - runDone <- err + return } }() - defer func() { - <-runDone - }() + defer close(runErr) select { - case err := <-runDone: + case err := <-runErr: if err != nil { return err } @@ -313,10 +312,11 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui } var buf bytes.Buffer + for ctx.Err() == nil { buf.Reset() - err := runCommand("docker", args, &buf, logger) + err = runCommand("docker", args, &buf, logger) if err == nil { break } @@ -327,7 +327,9 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui ctxutil.SleepWithJitter(ctx, time.Second, retry) } - close(runDone) + if err != nil { + return err + } return ctx.Err() } @@ -378,14 +380,6 @@ func setup(ctx context.Context, logger *zap.SugaredLogger) error { } } - if err := setupUser(ctx, logger, 5432); err != nil { - return err - } - - if err := setupUser(ctx, logger, 5433); err != nil { - return err - } - logger.Info("Done.") return nil } From d68a77738458cc1ade1cca5ce3e80722131b7edd Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 29 Feb 2024 17:20:16 +0900 Subject: [PATCH 06/20] update task file --- Taskfile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index c50d557f3306..7e6c3ce49ca4 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -419,7 +419,7 @@ tasks: --proxy-addr=127.0.0.1:47017 --mode=diff-normal --handler=pg - --postgresql-url='postgres://127.0.0.1:5433/ferretdb?search_path=' + --postgresql-url='postgres://username:password@127.0.0.1:5433/ferretdb?search_path=' --test-records-dir=tmp/records run-proxy: From 1f1c728b40967416b59b9afba6146fe0edd5b2ee Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 1 Mar 2024 09:44:11 +0900 Subject: [PATCH 07/20] use mongo driver instead of command --- cmd/envtool/envtool.go | 56 +++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index c3f9ae39b295..ab668503a769 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -34,6 +34,9 @@ import ( "github.com/alecthomas/kong" "github.com/prometheus/client_golang/prometheus" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -271,7 +274,7 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui runErr := make(chan error) go func() { - if err := l.Run(ctx); err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { + if err = l.Run(ctx); err != nil && !errors.Is(err, context.Canceled) { runErr <- err return @@ -281,7 +284,7 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui defer close(runErr) select { - case err := <-runErr: + case err = <-runErr: if err != nil { return err } @@ -289,45 +292,26 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui } port := l.TCPAddr().(*net.TCPAddr).Port + uri := fmt.Sprintf("mongodb://username:password@localhost:%d/", port) + clientOpts := options.Client().ApplyURI(uri) - var retry int64 - - eval := `' - if (db.getSiblingDB("admin").getUser("username") == null){ - db.getSiblingDB("admin").createUser( - {user: "username", pwd: "password", roles: [], mechanisms: ["SCRAM-SHA-1","SCRAM-SHA-256","PLAIN"]} - ) - } -'` - args := []string{ - "compose", - "exec", - "-T", - "mongodb", - "mongosh", - "--host=host.docker.internal", - fmt.Sprintf("--port=%d", port), - "--eval", - eval, + client, err := mongo.Connect(ctx, clientOpts) + if err != nil { + return err } - var buf bytes.Buffer - - for ctx.Err() == nil { - buf.Reset() - - err = runCommand("docker", args, &buf, logger) - if err == nil { - break + //nolint:forbidigo // allow usage of bson for setup dev and test environment + if err = client.Database("admin").RunCommand(ctx, bson.D{ + bson.E{Key: "createUser", Value: "username"}, + bson.E{Key: "roles", Value: bson.A{}}, + bson.E{Key: "pwd", Value: "password"}, + bson.E{Key: "mechanisms", Value: bson.A{"PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-256"}}, + }).Err(); err != nil { + var cmdErr mongo.CommandError + if errors.As(err, &cmdErr) && cmdErr.Code == 51003 { + return nil } - logger.Infof("%s:\n%s", err, buf.String()) - - retry++ - ctxutil.SleepWithJitter(ctx, time.Second, retry) - } - - if err != nil { return err } From af0bdffbfcb4d185fafff76f155b4cbfe73de86d Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 1 Mar 2024 11:12:21 +0900 Subject: [PATCH 08/20] create user in template1 --- cmd/envtool/envtool.go | 22 ++++++++++++++------ cmd/envtool/envtool_test.go | 40 ++++++++++++++++++++++++++++++++----- integration/setup/setup.go | 26 ------------------------ 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index ab668503a769..cee52f85ce54 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -88,6 +88,10 @@ func waitForPort(ctx context.Context, logger *zap.SugaredLogger, port uint16) er } // setupAnyPostgres configures given PostgreSQL. +// +// It also creates a user in ferretdb and template1 databases so all subsequent +// databases created from template1 have the user. +// See also https://www.postgresql.org/docs/current/manage-ag-templatedbs.html. func setupAnyPostgres(ctx context.Context, logger *zap.SugaredLogger, uri string) error { u, err := url.Parse(uri) if err != nil { @@ -138,7 +142,14 @@ func setupAnyPostgres(ctx context.Context, logger *zap.SugaredLogger, uri string return ctx.Err() } - return setupUser(ctx, logger, uint16(port)) + dbName := strings.Trim(u.Path, "/") + + err = setupUser(ctx, logger, uint16(port), dbName) + if err != nil { + return err + } + + return setupUser(ctx, logger, uint16(port), "template1") } // setupPostgres configures `postgres` container. @@ -226,12 +237,11 @@ func setupMongodb(ctx context.Context, logger *zap.SugaredLogger) error { } // setupUser creates a user in admin database with supported mechanisms. -// The user uses username/password credential which is the same as the PostgreSQL -// credentials. +// The user uses username/password credential which is the same as the PostgreSQL credentials. // // Without this, once the first user is created, the authentication fails // as username/password does not exist in admin.system.users collection. -func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort uint16) error { +func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort uint16, dbName string) error { if err := waitForPort(ctx, logger.Named("postgreSQL"), postgreSQLPort); err != nil { return err } @@ -241,7 +251,7 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui return err } - postgreSQlURL := fmt.Sprintf("postgres://username:password@localhost:%d/ferretdb", postgreSQLPort) + postgreSQlURL := fmt.Sprintf("postgres://username:password@localhost:%d/%s", postgreSQLPort, dbName) listenerMetrics := connmetrics.NewListenerMetrics() handlerOpts := ®istry.NewHandlerOpts{ Logger: logger.Desugar(), @@ -292,7 +302,7 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui } port := l.TCPAddr().(*net.TCPAddr).Port - uri := fmt.Sprintf("mongodb://username:password@localhost:%d/", port) + uri := fmt.Sprintf("mongodb://username:password@localhost:%d/?authMechanism=PLAIN", port) clientOpts := options.Client().ApplyURI(uri) client, err := mongo.Connect(ctx, clientOpts) diff --git a/cmd/envtool/envtool_test.go b/cmd/envtool/envtool_test.go index 4af406fb344b..27b92e4f264d 100644 --- a/cmd/envtool/envtool_test.go +++ b/cmd/envtool/envtool_test.go @@ -17,10 +17,15 @@ package main import ( "bytes" "context" + "fmt" "os" "testing" "time" + zapadapter "github.com/jackc/pgx-zap" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" + "github.com/jackc/pgx/v5/tracelog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -105,15 +110,40 @@ func TestSetupUser(t *testing.T) { ctx, cancel := context.WithTimeout(testutil.Ctx(t), 5*time.Second) t.Cleanup(cancel) - l := testutil.Logger(t).Sugar() + baseURI := "postgres://username@127.0.0.1:5432/ferretdb" + l := testutil.Logger(t) + cfg, err := pgxpool.ParseConfig(baseURI) + require.NoError(t, err) + + cfg.MinConns = 0 + cfg.MaxConns = 1 + cfg.ConnConfig.Tracer = &tracelog.TraceLog{ + Logger: zapadapter.NewLogger(l), + LogLevel: tracelog.LogLevelTrace, + } - err := setupUser(ctx, l, 5432) + p, err := pgxpool.NewWithConfig(ctx, cfg) require.NoError(t, err) - // if the user already exists, it should not fail - err = setupUser(ctx, l, 5432) + dbName := testutil.DatabaseName(t) + + // use template0 because template1 may already have the user created + q := fmt.Sprintf("CREATE DATABASE %s TEMPLATE template0", pgx.Identifier{dbName}.Sanitize()) + _, err = p.Exec(ctx, q) + require.NoError(t, err) + + t.Cleanup(func() { + defer p.Close() + + q = fmt.Sprintf("DROP DATABASE %s", pgx.Identifier{dbName}.Sanitize()) + _, err = p.Exec(context.Background(), q) + require.NoError(t, err) + }) + + err = setupUser(ctx, l.Sugar(), 5432, dbName) require.NoError(t, err) - err = setupUser(ctx, l, 5433) + // if the user already exists, it should not fail + err = setupUser(ctx, l.Sugar(), 5432, dbName) require.NoError(t, err) } diff --git a/integration/setup/setup.go b/integration/setup/setup.go index 8cbd81b06098..ec3395a59fae 100644 --- a/integration/setup/setup.go +++ b/integration/setup/setup.go @@ -163,8 +163,6 @@ func SetupWithOpts(tb testtb.TB, opts *SetupOpts) *SetupResult { collection := setupCollection(tb, setupCtx, client, opts) - setupUser(tb, setupCtx, client) - level.SetLevel(*logLevelF) return &SetupResult{ @@ -324,27 +322,3 @@ func insertBenchmarkProvider(tb testtb.TB, ctx context.Context, collection *mong return } - -// setupUser creates a user in admin database with supported mechanisms. -// The user uses username/password credential which is the same as the database -// credentials. This is done to avoid the need to reconnect as different credential. -// -// Without this, once the first user is created, the authentication fails -// as username/password does not exist in admin.system.users collection. -func setupUser(tb testtb.TB, ctx context.Context, client *mongo.Client) { - tb.Helper() - - if IsMongoDB(tb) { - return - } - - username, password := "username", "password" - - err := client.Database("admin").RunCommand(ctx, bson.D{ - {"createUser", username}, - {"roles", bson.A{}}, - {"pwd", password}, - {"mechanisms", bson.A{"PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-256"}}, - }).Err() - require.NoErrorf(tb, err, "cannot create user") -} From 563e165d4688cec90ad191ce9b2bf9abe7fb4200 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 1 Mar 2024 11:22:22 +0900 Subject: [PATCH 09/20] remove timeout from test --- cmd/envtool/envtool_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cmd/envtool/envtool_test.go b/cmd/envtool/envtool_test.go index 27b92e4f264d..1ce7b70e17d8 100644 --- a/cmd/envtool/envtool_test.go +++ b/cmd/envtool/envtool_test.go @@ -20,7 +20,6 @@ import ( "fmt" "os" "testing" - "time" zapadapter "github.com/jackc/pgx-zap" "github.com/jackc/pgx/v5" @@ -107,9 +106,7 @@ func TestSetupUser(t *testing.T) { t.Parallel() - ctx, cancel := context.WithTimeout(testutil.Ctx(t), 5*time.Second) - t.Cleanup(cancel) - + ctx := testutil.Ctx(t) baseURI := "postgres://username@127.0.0.1:5432/ferretdb" l := testutil.Logger(t) cfg, err := pgxpool.ParseConfig(baseURI) From 96d34ef5415c49f54fa9cf42ff63b4ac9908b308 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 1 Mar 2024 11:41:52 +0900 Subject: [PATCH 10/20] fix tests related to having admin database --- internal/backends/backend_test.go | 42 +++++++++++++++---- .../postgresql/metadata/registry_test.go | 4 +- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/internal/backends/backend_test.go b/internal/backends/backend_test.go index 56a0a5552bf0..9aee6ea1d1ef 100644 --- a/internal/backends/backend_test.go +++ b/internal/backends/backend_test.go @@ -112,21 +112,45 @@ func TestListDatabases(t *testing.T) { t.Parallel() dbRes, err := b.ListDatabases(ctx, nil) require.NoError(t, err) - require.Equal(t, 3, len(dbRes.Databases), "expected full list len 3") - require.Equal(t, dbNames[1], dbRes.Databases[0].Name, "expected name testDB1") - require.Equal(t, dbNames[0], dbRes.Databases[1].Name, "expected name testDB2") - require.Equal(t, dbNames[2], dbRes.Databases[2].Name, "expected name testDB3") + require.GreaterOrEqual(t, len(dbRes.Databases), 3, "expected at least 3") + + expected := []string{dbNames[1], dbNames[0], dbNames[2]} + actual := []string{dbRes.Databases[0].Name, dbRes.Databases[1].Name, dbRes.Databases[2].Name} + + if len(dbRes.Databases) > 3 { + expected = []string{"admin", dbNames[1], dbNames[0], dbNames[2]} + actual = []string{ + dbRes.Databases[0].Name, + dbRes.Databases[1].Name, + dbRes.Databases[2].Name, + dbRes.Databases[3].Name, + } + } + + require.Equalf(t, expected, actual, "expected %s databases", len(expected)) }) - t.Run("ListDatabasesWithEMptyParam", func(t *testing.T) { + t.Run("ListDatabasesWithEmptyParam", func(t *testing.T) { t.Parallel() var param backends.ListDatabasesParams dbRes, err := b.ListDatabases(ctx, ¶m) require.NoError(t, err) - require.Equal(t, 3, len(dbRes.Databases), "expected full list len 3") - require.Equal(t, dbNames[1], dbRes.Databases[0].Name, "expected name testDB1") - require.Equal(t, dbNames[0], dbRes.Databases[1].Name, "expected name testDB2") - require.Equal(t, dbNames[2], dbRes.Databases[2].Name, "expected name testDB3") + require.GreaterOrEqual(t, len(dbRes.Databases), 3, "expected at least 3") + + expected := []string{dbNames[1], dbNames[0], dbNames[2]} + actual := []string{dbRes.Databases[0].Name, dbRes.Databases[1].Name, dbRes.Databases[2].Name} + + if len(dbRes.Databases) > 3 { + expected = []string{"admin", dbNames[1], dbNames[0], dbNames[2]} + actual = []string{ + dbRes.Databases[0].Name, + dbRes.Databases[1].Name, + dbRes.Databases[2].Name, + dbRes.Databases[3].Name, + } + } + + require.Equalf(t, expected, actual, "expected %s databases", len(expected)) }) }) } diff --git a/internal/backends/postgresql/metadata/registry_test.go b/internal/backends/postgresql/metadata/registry_test.go index ac29e0003042..d06a5f0f7513 100644 --- a/internal/backends/postgresql/metadata/registry_test.go +++ b/internal/backends/postgresql/metadata/registry_test.go @@ -276,7 +276,7 @@ func TestDefaultEmptySchema(t *testing.T) { list, err := r.DatabaseList(ctx) require.NoError(t, err) - assert.Equal(t, []string{dbName}, list) + assert.Equal(t, []string{dbName, "admin"}, list) created, err := r.CollectionCreate(ctx, &CollectionCreateParams{DBName: "public", Name: testutil.CollectionName(t)}) require.NoError(t, err) @@ -284,7 +284,7 @@ func TestDefaultEmptySchema(t *testing.T) { list, err = r.DatabaseList(ctx) require.NoError(t, err) - assert.Equal(t, []string{dbName, "public"}, list) + assert.Equal(t, []string{dbName, "admin", "public"}, list) } func TestCheckDatabaseUpdated(t *testing.T) { From 9db3718afbee65a7afc67322366760917fb3826e Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 1 Mar 2024 12:51:47 +0900 Subject: [PATCH 11/20] fix logout test --- integration/commands_authentication_test.go | 76 ++++++++++++++++++--- internal/handler/commands.go | 5 +- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/integration/commands_authentication_test.go b/integration/commands_authentication_test.go index e8f0c885182f..a7ea6d8db8b7 100644 --- a/integration/commands_authentication_test.go +++ b/integration/commands_authentication_test.go @@ -17,8 +17,14 @@ package integration import ( "testing" + "github.com/FerretDB/FerretDB/internal/types" + "github.com/FerretDB/FerretDB/internal/util/must" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" "github.com/FerretDB/FerretDB/integration/setup" "github.com/FerretDB/FerretDB/internal/util/testutil" @@ -27,12 +33,55 @@ import ( func TestCommandsAuthenticationLogout(t *testing.T) { t.Parallel() - ctx, collection := setup.Setup(t) - db := collection.Database() + s := setup.SetupWithOpts(t, nil) + ctx, db := s.Ctx, s.Collection.Database() + username, password, mechanism := "testuser", "testpass", "SCRAM-SHA-256" + + err := db.RunCommand(ctx, bson.D{ + {"createUser", username}, + {"roles", bson.A{}}, + {"pwd", password}, + {"mechanisms", bson.A{mechanism}}, + }).Err() + require.NoError(t, err, "cannot create user") + + credential := options.Credential{ + AuthMechanism: mechanism, + AuthSource: db.Name(), + Username: username, + Password: password, + } + + opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(credential) + + client, err := mongo.Connect(ctx, opts) + require.NoError(t, err, "cannot connect to MongoDB") + + t.Cleanup(func() { + require.NoError(t, client.Disconnect(ctx)) + }) + + db = client.Database(db.Name()) - // the test user logs out var res bson.D - err := db.RunCommand(ctx, bson.D{{"logout", 1}}).Decode(&res) + err = db.RunCommand(ctx, bson.D{{"connectionStatus", 1}}).Decode(&res) + assert.NoError(t, err) + + actualAuth := must.NotFail(ConvertDocument(t, res).Get("authInfo")).(*types.Document) + actualUsers := must.NotFail(actualAuth.Get("authenticatedUsers")).(*types.Array) + + var hasUser bool + for i := 0; i < actualUsers.Len(); i++ { + actualUser := must.NotFail(must.NotFail(actualUsers.Get(i)).(*types.Document).Get("user")) + if actualUser == username { + hasUser = true + break + } + } + + assert.True(t, hasUser, res) + + err = db.RunCommand(ctx, bson.D{{"logout", 1}}).Decode(&res) assert.NoError(t, err) actual := ConvertDocument(t, res) @@ -42,15 +91,22 @@ func TestCommandsAuthenticationLogout(t *testing.T) { expected := ConvertDocument(t, bson.D{{"ok", float64(1)}}) testutil.AssertEqual(t, expected, actual) - // the test user logs out again, it has no effect - err = db.RunCommand(ctx, bson.D{{"logout", 1}}).Decode(&res) + err = db.RunCommand(ctx, bson.D{{"connectionStatus", 1}}).Decode(&res) assert.NoError(t, err) - actual = ConvertDocument(t, res) - actual.Remove("$clusterTime") - actual.Remove("operationTime") + actualAuth = must.NotFail(ConvertDocument(t, res).Get("authInfo")).(*types.Document) + actualUsers = must.NotFail(actualAuth.Get("authenticatedUsers")).(*types.Array) - testutil.AssertEqual(t, expected, actual) + for i := 0; i < actualUsers.Len(); i++ { + actualUser := must.NotFail(must.NotFail(actualUsers.Get(i)).(*types.Document).Get("user")) + if actualUser == username { + assert.Fail(t, "user is still authenticated", res) + } + } + + // the test user logs out again, it has no effect + err = db.RunCommand(ctx, bson.D{{"logout", 1}}).Err() + assert.NoError(t, err) } func TestCommandsAuthenticationLogoutTLS(t *testing.T) { diff --git a/internal/handler/commands.go b/internal/handler/commands.go index 03d6868697de..ded0b2b853d5 100644 --- a/internal/handler/commands.go +++ b/internal/handler/commands.go @@ -203,8 +203,9 @@ func (h *Handler) initCommands() { Help: "Returns a summary of indexes of the specified collection.", }, "logout": { - Handler: h.MsgLogout, - Help: "Logs out from the current session.", + Handler: h.MsgLogout, + anonymous: true, + Help: "Logs out from the current session.", }, "ping": { Handler: h.MsgPing, From 34bd8037ea96edcf79652a9b4a56e63d2fda9fff Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 1 Mar 2024 12:59:22 +0900 Subject: [PATCH 12/20] close client --- cmd/envtool/envtool.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index cee52f85ce54..c63431009dfd 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -310,6 +310,10 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui return err } + defer func() { + err = client.Disconnect(ctx) + }() + //nolint:forbidigo // allow usage of bson for setup dev and test environment if err = client.Database("admin").RunCommand(ctx, bson.D{ bson.E{Key: "createUser", Value: "username"}, From f41adb2b6dd38d42577e7e205aed7dfd418098c2 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 1 Mar 2024 14:55:07 +0900 Subject: [PATCH 13/20] replace test --- cmd/envtool/envtool.go | 9 +++- integration/commands_authentication_test.go | 51 ++------------------- 2 files changed, 10 insertions(+), 50 deletions(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index c63431009dfd..339af402a8df 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -283,11 +283,12 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui runErr := make(chan error) + ctx, cancel := context.WithCancel(ctx) + defer cancel() + go func() { if err = l.Run(ctx); err != nil && !errors.Is(err, context.Canceled) { runErr <- err - - return } }() @@ -329,6 +330,10 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui return err } + if ctx.Err() == context.Canceled { + return nil + } + return ctx.Err() } diff --git a/integration/commands_authentication_test.go b/integration/commands_authentication_test.go index a7ea6d8db8b7..6ddf2230dfeb 100644 --- a/integration/commands_authentication_test.go +++ b/integration/commands_authentication_test.go @@ -17,9 +17,6 @@ package integration import ( "testing" - "github.com/FerretDB/FerretDB/internal/types" - "github.com/FerretDB/FerretDB/internal/util/must" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" @@ -27,6 +24,8 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" "github.com/FerretDB/FerretDB/integration/setup" + "github.com/FerretDB/FerretDB/internal/types" + "github.com/FerretDB/FerretDB/internal/util/must" "github.com/FerretDB/FerretDB/internal/util/testutil" ) @@ -71,6 +70,7 @@ func TestCommandsAuthenticationLogout(t *testing.T) { actualUsers := must.NotFail(actualAuth.Get("authenticatedUsers")).(*types.Array) var hasUser bool + for i := 0; i < actualUsers.Len(); i++ { actualUser := must.NotFail(must.NotFail(actualUsers.Get(i)).(*types.Document).Get("user")) if actualUser == username { @@ -108,48 +108,3 @@ func TestCommandsAuthenticationLogout(t *testing.T) { err = db.RunCommand(ctx, bson.D{{"logout", 1}}).Err() assert.NoError(t, err) } - -func TestCommandsAuthenticationLogoutTLS(t *testing.T) { - setup.SkipForMongoDB(t, "tls is not enabled for mongodb backend") - - t.Parallel() - - ctx, collection := setup.Setup(t) - db := collection.Database() - - // the test user is authenticated - expectedAuthenticated := bson.D{ - { - "authInfo", bson.D{ - {"authenticatedUsers", bson.A{bson.D{{"user", "username"}}}}, - {"authenticatedUserRoles", bson.A{}}, - {"authenticatedUserPrivileges", bson.A{}}, - }, - }, - {"ok", float64(1)}, - } - var res bson.D - err := db.RunCommand(ctx, bson.D{{"connectionStatus", 1}}).Decode(&res) - assert.NoError(t, err) - assert.Equal(t, expectedAuthenticated, res) - - // the test user logs out - err = db.RunCommand(ctx, bson.D{{"logout", 1}}).Decode(&res) - assert.NoError(t, err) - assert.Equal(t, bson.D{{"ok", float64(1)}}, res) - - // the test user is no longer authenticated - expectedUnauthenticated := bson.D{ - { - "authInfo", bson.D{ - {"authenticatedUsers", bson.A{}}, - {"authenticatedUserRoles", bson.A{}}, - {"authenticatedUserPrivileges", bson.A{}}, - }, - }, - {"ok", float64(1)}, - } - err = db.RunCommand(ctx, bson.D{{"connectionStatus", 1}}).Decode(&res) - assert.NoError(t, err) - assert.Equal(t, expectedUnauthenticated, res) -} From 80bf3a0b30a5a7c6ee398a554aba95e1a70ed4c0 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 1 Mar 2024 15:34:00 +0900 Subject: [PATCH 14/20] fix name --- cmd/envtool/envtool.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index 339af402a8df..829bb963ec3a 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -251,13 +251,13 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort ui return err } - postgreSQlURL := fmt.Sprintf("postgres://username:password@localhost:%d/%s", postgreSQLPort, dbName) + postgreSQLURL := fmt.Sprintf("postgres://username:password@localhost:%d/%s", postgreSQLPort, dbName) listenerMetrics := connmetrics.NewListenerMetrics() handlerOpts := ®istry.NewHandlerOpts{ Logger: logger.Desugar(), ConnMetrics: listenerMetrics.ConnMetrics, StateProvider: sp, - PostgreSQLURL: postgreSQlURL, + PostgreSQLURL: postgreSQLURL, TestOpts: registry.TestOpts{ CappedCleanupPercentage: 20, EnableNewAuth: true, From aba7e4eeac32a982086e3d9bcc3bb42a5cd869f9 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 1 Mar 2024 15:53:44 +0900 Subject: [PATCH 15/20] sqlite user is still created in test setup --- integration/setup/setup.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/integration/setup/setup.go b/integration/setup/setup.go index ec3395a59fae..2ea43a694fac 100644 --- a/integration/setup/setup.go +++ b/integration/setup/setup.go @@ -163,6 +163,8 @@ func SetupWithOpts(tb testtb.TB, opts *SetupOpts) *SetupResult { collection := setupCollection(tb, setupCtx, client, opts) + setupUser(tb, setupCtx, client) + level.SetLevel(*logLevelF) return &SetupResult{ @@ -322,3 +324,28 @@ func insertBenchmarkProvider(tb testtb.TB, ctx context.Context, collection *mong return } + +// setupUser creates a user in admin database with supported mechanisms. +// The user uses username/password credential which is the same as the database +// credentials. This is done to avoid the need to reconnect as different credential. +// +// Without this, once the first user is created, the authentication fails +// as username/password does not exist in admin.system.users collection. +func setupUser(tb testtb.TB, ctx context.Context, client *mongo.Client) { + tb.Helper() + + if IsPostgreSQL(tb) || IsMongoDB(tb) { + // the user is created in env tool for PostgreSQL + return + } + + username, password := "username", "password" + + err := client.Database("admin").RunCommand(ctx, bson.D{ + {"createUser", username}, + {"roles", bson.A{}}, + {"pwd", password}, + {"mechanisms", bson.A{"PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-256"}}, + }).Err() + require.NoErrorf(tb, err, "cannot create user") +} From 6192940020eb4a1a369fa61481d4a1cc76b74a6f Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Tue, 5 Mar 2024 12:51:47 +0900 Subject: [PATCH 16/20] use sql directly --- cmd/envtool/envtool.go | 160 +++++++++++++++++----------------- cmd/envtool/envtool_test.go | 24 ++--- cmd/envtool/system_users.json | 84 ++++++++++++++++++ cmd/envtool/user.json | 145 ++++++++++++++++++++++++++++++ 4 files changed, 322 insertions(+), 91 deletions(-) create mode 100644 cmd/envtool/system_users.json create mode 100644 cmd/envtool/user.json diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index 829bb963ec3a..681aea1304bb 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -33,19 +33,15 @@ import ( "time" "github.com/alecthomas/kong" + "github.com/jackc/pgerrcode" + "github.com/jackc/pgx/v5/pgconn" "github.com/prometheus/client_golang/prometheus" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" "go.uber.org/zap/zapcore" "github.com/FerretDB/FerretDB/build/version" mysqlpool "github.com/FerretDB/FerretDB/internal/backends/mysql/metadata/pool" "github.com/FerretDB/FerretDB/internal/backends/postgresql/metadata/pool" - "github.com/FerretDB/FerretDB/internal/clientconn" - "github.com/FerretDB/FerretDB/internal/clientconn/connmetrics" - "github.com/FerretDB/FerretDB/internal/handler/registry" "github.com/FerretDB/FerretDB/internal/util/ctxutil" "github.com/FerretDB/FerretDB/internal/util/debug" "github.com/FerretDB/FerretDB/internal/util/lazyerrors" @@ -58,6 +54,12 @@ var ( //go:embed error.tmpl errorTemplateB []byte + //go:embed user.json + user string + + //go:embed system_users.json + systemUsers string + // Parsed error template. errorTemplate = template.Must(template.New("error").Option("missingkey=error").Parse(string(errorTemplateB))) ) @@ -138,29 +140,42 @@ func setupAnyPostgres(ctx context.Context, logger *zap.SugaredLogger, uri string ctxutil.SleepWithJitter(ctx, time.Second, retry) } - if ctx.Err() != nil { - return ctx.Err() - } + return ctx.Err() +} - dbName := strings.Trim(u.Path, "/") +// setupPostgres configures `postgres` container. +func setupPostgres(ctx context.Context, logger *zap.SugaredLogger) error { + l := logger.Named("postgres") - err = setupUser(ctx, logger, uint16(port), dbName) + // user `username` must exist, but password may be any, even empty + err := setupAnyPostgres(ctx, l, "postgres://username@127.0.0.1:5432/ferretdb") if err != nil { return err } - return setupUser(ctx, logger, uint16(port), "template1") -} + err = setupPostgresUser(ctx, logger, "postgres://username@127.0.0.1:5432/ferretdb") + if err != nil { + return err + } -// setupPostgres configures `postgres` container. -func setupPostgres(ctx context.Context, logger *zap.SugaredLogger) error { - // user `username` must exist, but password may be any, even empty - return setupAnyPostgres(ctx, logger.Named("postgres"), "postgres://username@127.0.0.1:5432/ferretdb") + return setupPostgresUser(ctx, logger, "postgres://username@127.0.0.1:5432/template1") } // setupPostgresSecured configures `postgres_secured` container. func setupPostgresSecured(ctx context.Context, logger *zap.SugaredLogger) error { - return setupAnyPostgres(ctx, logger.Named("postgres_secured"), "postgres://username:password@127.0.0.1:5433/ferretdb") + l := logger.Named("postgres_secured") + + err := setupAnyPostgres(ctx, l, "postgres://username:password@127.0.0.1:5433/ferretdb") + if err != nil { + return err + } + + err = setupPostgresUser(ctx, logger, "postgres://username:password@127.0.0.1:5433/ferretdb") + if err != nil { + return err + } + + return setupPostgresUser(ctx, logger, "postgres://username:password@127.0.0.1:5433/template1") } // setupMySQL configures `mysql` container. @@ -236,105 +251,88 @@ func setupMongodb(ctx context.Context, logger *zap.SugaredLogger) error { return ctx.Err() } -// setupUser creates a user in admin database with supported mechanisms. -// The user uses username/password credential which is the same as the PostgreSQL credentials. +// setupPostgresUser creates a user with username/password in admin database with supported mechanisms. +// The user uses the same credential as the PostgreSQL credentials. +// It creates admin database (PostgreSQL admin schema), if it does not exist. +// It also creates system.users collection, if it does not exist. // // Without this, once the first user is created, the authentication fails // as username/password does not exist in admin.system.users collection. -func setupUser(ctx context.Context, logger *zap.SugaredLogger, postgreSQLPort uint16, dbName string) error { - if err := waitForPort(ctx, logger.Named("postgreSQL"), postgreSQLPort); err != nil { - return err - } - +func setupPostgresUser(ctx context.Context, logger *zap.SugaredLogger, uri string) error { sp, err := state.NewProvider("") if err != nil { return err } - postgreSQLURL := fmt.Sprintf("postgres://username:password@localhost:%d/%s", postgreSQLPort, dbName) - listenerMetrics := connmetrics.NewListenerMetrics() - handlerOpts := ®istry.NewHandlerOpts{ - Logger: logger.Desugar(), - ConnMetrics: listenerMetrics.ConnMetrics, - StateProvider: sp, - PostgreSQLURL: postgreSQLURL, - TestOpts: registry.TestOpts{ - CappedCleanupPercentage: 20, - EnableNewAuth: true, - }, + u, err := url.Parse(uri) + if err != nil { + return err } - h, closeBackend, err := registry.NewHandler("postgresql", handlerOpts) + p, err := pool.New(uri, logger.Desugar(), sp) if err != nil { return err } - defer closeBackend() + defer p.Close() + + username := u.User.Username() + password, _ := u.User.Password() - listenerOpts := clientconn.NewListenerOpts{ - Mode: clientconn.NormalMode, - Metrics: listenerMetrics, - Handler: h, - Logger: logger.Desugar(), - TCP: "127.0.0.1:0", + dbPool, err := p.Get(username, password) + if err != nil { + return err } - l := clientconn.NewListener(&listenerOpts) + defer dbPool.Close() - runErr := make(chan error) + var pgErr *pgconn.PgError - ctx, cancel := context.WithCancel(ctx) - defer cancel() + q := `CREATE SCHEMA admin` + if _, err = dbPool.Exec(ctx, q); err != nil && (!errors.As(err, &pgErr) || pgErr.Code != pgerrcode.DuplicateSchema) { + return err + } - go func() { - if err = l.Run(ctx); err != nil && !errors.Is(err, context.Canceled) { - runErr <- err + if err == nil { + q = `CREATE TABLE admin._ferretdb_database_metadata (_jsonb jsonb)` + if _, err = dbPool.Exec(ctx, q); err != nil { + return err } - }() - defer close(runErr) + q = `CREATE UNIQUE INDEX _ferretdb_database_metadata_id_idx ON admin._ferretdb_database_metadata (((_jsonb->'_id')))` + if _, err = dbPool.Exec(ctx, q); err != nil { + return err + } - select { - case err = <-runErr: - if err != nil { + q = `CREATE UNIQUE INDEX _ferretdb_database_metadata_table_idx ON admin._ferretdb_database_metadata (((_jsonb->'table')))` + if _, err = dbPool.Exec(ctx, q); err != nil { return err } - case <-time.After(time.Millisecond): } - port := l.TCPAddr().(*net.TCPAddr).Port - uri := fmt.Sprintf("mongodb://username:password@localhost:%d/?authMechanism=PLAIN", port) - clientOpts := options.Client().ApplyURI(uri) - - client, err := mongo.Connect(ctx, clientOpts) - if err != nil { + q = `CREATE TABLE admin.system_users_aff2f7ce (_jsonb jsonb)` + if _, err = dbPool.Exec(ctx, q); err != nil && (!errors.As(err, &pgErr) || pgErr.Code != pgerrcode.DuplicateTable) { return err } - defer func() { - err = client.Disconnect(ctx) - }() - - //nolint:forbidigo // allow usage of bson for setup dev and test environment - if err = client.Database("admin").RunCommand(ctx, bson.D{ - bson.E{Key: "createUser", Value: "username"}, - bson.E{Key: "roles", Value: bson.A{}}, - bson.E{Key: "pwd", Value: "password"}, - bson.E{Key: "mechanisms", Value: bson.A{"PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-256"}}, - }).Err(); err != nil { - var cmdErr mongo.CommandError - if errors.As(err, &cmdErr) && cmdErr.Code == 51003 { - return nil + if err == nil { + q = `CREATE UNIQUE INDEX "system_users_aff2f7ce__id__67399184_idx" ON admin.system_users_aff2f7ce (((_jsonb->'_id')))` + if _, err = dbPool.Exec(ctx, q); err != nil { + return err } - return err + q = `INSERT INTO admin._ferretdb_database_metadata (_jsonb) VALUES ($1)` + if _, err = dbPool.Exec(ctx, q, systemUsers); err != nil { + return err + } } - if ctx.Err() == context.Canceled { - return nil + q = `INSERT INTO admin.system_users_aff2f7ce (_jsonb) VALUES ($1)` + if _, err = dbPool.Exec(ctx, q, user); err != nil && (!errors.As(err, &pgErr) || pgErr.Code != pgerrcode.UniqueViolation) { + return err } - return ctx.Err() + return nil } // setupMongodbSecured configures `mongodb_secured` container. diff --git a/cmd/envtool/envtool_test.go b/cmd/envtool/envtool_test.go index 1ce7b70e17d8..60a99d98c981 100644 --- a/cmd/envtool/envtool_test.go +++ b/cmd/envtool/envtool_test.go @@ -99,19 +99,18 @@ func TestPackageVersion(t *testing.T) { assert.Equal(t, "1.0.0", output.String()) } -func TestSetupUser(t *testing.T) { +func TestSetupPostgresUser(t *testing.T) { if testing.Short() { t.Skip("skipping in -short mode") } t.Parallel() - ctx := testutil.Ctx(t) - baseURI := "postgres://username@127.0.0.1:5432/ferretdb" - l := testutil.Logger(t) + baseURI := "postgres://username@127.0.0.1:5432/ferretdb?search_path=" cfg, err := pgxpool.ParseConfig(baseURI) require.NoError(t, err) + l := testutil.Logger(t) cfg.MinConns = 0 cfg.MaxConns = 1 cfg.ConnConfig.Tracer = &tracelog.TraceLog{ @@ -119,28 +118,33 @@ func TestSetupUser(t *testing.T) { LogLevel: tracelog.LogLevelTrace, } + ctx := testutil.Ctx(t) p, err := pgxpool.NewWithConfig(ctx, cfg) require.NoError(t, err) dbName := testutil.DatabaseName(t) + sanitizedName := pgx.Identifier{dbName}.Sanitize() + + _, err = p.Exec(ctx, fmt.Sprintf("DROP DATABASE IF EXISTS %s", sanitizedName)) + require.NoError(t, err) // use template0 because template1 may already have the user created - q := fmt.Sprintf("CREATE DATABASE %s TEMPLATE template0", pgx.Identifier{dbName}.Sanitize()) - _, err = p.Exec(ctx, q) + _, err = p.Exec(ctx, fmt.Sprintf("CREATE DATABASE %s TEMPLATE template0", sanitizedName)) require.NoError(t, err) t.Cleanup(func() { defer p.Close() - q = fmt.Sprintf("DROP DATABASE %s", pgx.Identifier{dbName}.Sanitize()) - _, err = p.Exec(context.Background(), q) + _, err = p.Exec(context.Background(), fmt.Sprintf("DROP DATABASE %s", sanitizedName)) require.NoError(t, err) }) - err = setupUser(ctx, l.Sugar(), 5432, dbName) + uri := fmt.Sprintf("postgres://username@127.0.0.1:5432/%s", dbName) + + err = setupPostgresUser(ctx, l.Sugar(), uri) require.NoError(t, err) // if the user already exists, it should not fail - err = setupUser(ctx, l.Sugar(), 5432, dbName) + err = setupPostgresUser(ctx, l.Sugar(), uri) require.NoError(t, err) } diff --git a/cmd/envtool/system_users.json b/cmd/envtool/system_users.json new file mode 100644 index 000000000000..616364268838 --- /dev/null +++ b/cmd/envtool/system_users.json @@ -0,0 +1,84 @@ +{ + "$s": { + "p": { + "_id": { + "t": "string" + }, + "uuid": { + "t": "string" + }, + "table": { + "t": "string" + }, + "indexes": { + "t": "array", + "i": [ + { + "t": "object", + "$s": { + "p": { + "pgindex": { + "t": "string" + }, + "name": { + "t": "string" + }, + "key": { + "t": "object", + "$s": { + "p": { + "_id": { + "t": "int" + } + }, + "$k": [ + "_id" + ] + } + }, + "unique": { + "t": "bool" + } + }, + "$k": [ + "pgindex", + "name", + "key", + "unique" + ] + } + } + ] + }, + "cappedSize": { + "t": "long" + }, + "cappedDocs": { + "t": "long" + } + }, + "$k": [ + "_id", + "uuid", + "table", + "indexes", + "cappedSize", + "cappedDocs" + ] + }, + "_id": "system.users", + "uuid": "b34c2094-2086-4184-8761-f5692fadb2f2", + "table": "system_users_aff2f7ce", + "indexes": [ + { + "pgindex": "system_users_aff2f7ce__id__67399184_idx", + "name": "_id_", + "key": { + "_id": 1 + }, + "unique": true + } + ], + "cappedSize": 0, + "cappedDocs": 0 +} diff --git a/cmd/envtool/user.json b/cmd/envtool/user.json new file mode 100644 index 000000000000..7156c405b700 --- /dev/null +++ b/cmd/envtool/user.json @@ -0,0 +1,145 @@ +{ + "$s": { + "p": { + "_id": { + "t": "string" + }, + "credentials": { + "t": "object", + "$s": { + "p": { + "SCRAM-SHA-1": { + "t": "object", + "$s": { + "p": { + "storedKey": { + "t": "string" + }, + "iterationCount": { + "t": "int" + }, + "salt": { + "t": "string" + }, + "serverKey": { + "t": "string" + } + }, + "$k": [ + "storedKey", + "iterationCount", + "salt", + "serverKey" + ] + } + }, + "SCRAM-SHA-256": { + "t": "object", + "$s": { + "p": { + "storedKey": { + "t": "string" + }, + "iterationCount": { + "t": "int" + }, + "salt": { + "t": "string" + }, + "serverKey": { + "t": "string" + } + }, + "$k": [ + "storedKey", + "iterationCount", + "salt", + "serverKey" + ] + } + }, + "PLAIN": { + "t": "object", + "$s": { + "p": { + "algo": { + "t": "string" + }, + "iterationCount": { + "t": "long" + }, + "hash": { + "t": "binData", + "s": 0 + }, + "salt": { + "t": "binData", + "s": 0 + } + }, + "$k": [ + "algo", + "iterationCount", + "hash", + "salt" + ] + } + } + }, + "$k": [ + "SCRAM-SHA-1", + "SCRAM-SHA-256", + "PLAIN" + ] + } + }, + "user": { + "t": "string" + }, + "db": { + "t": "string" + }, + "roles": { + "t": "array", + "i": [] + }, + "userId": { + "t": "binData", + "s": 4 + } + }, + "$k": [ + "_id", + "credentials", + "user", + "db", + "roles", + "userId" + ] + }, + "_id": "admin.username", + "credentials": { + "SCRAM-SHA-1": { + "storedKey": "o+viQ4miKbpHYVuv62ys3qlfcHE=", + "iterationCount": 10000, + "salt": "a8nzn5BcBhXRhScuRy39cQ==", + "serverKey": "xzgG8EYUmN+NQ9vwbQuVLgtyifQ=" + }, + "SCRAM-SHA-256": { + "storedKey": "ADDyKR9ukJMC2wXQztMbaIrTNZh7RdBSNtQ3ehnAv+U=", + "iterationCount": 15000, + "salt": "ZSTAnwxr7HEZ44F5+1qOH06asrPR/30CyhgjYw==", + "serverKey": "nu1KrJQnTXhLMFBPxv6lVqakf1lEtB8gF/NFpd3aXdc=" + }, + "PLAIN": { + "algo": "PBKDF2-HMAC-SHA256", + "iterationCount": 600000, + "hash": "1oHFV6ZG+3eBSMv/HPpfcs50dvdidhArDHmF6pPtU54=", + "salt": "sxK4yjYYSss9JmYhfBRzwA==" + } + }, + "user": "username", + "db": "admin", + "roles": [], + "userId": "Qi0njDnoRT66fs6AKwtfoA==" +} From 4c4add2c6c07ecdcdee1d95a45bbc5d5442c60d0 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Tue, 5 Mar 2024 13:01:59 +0900 Subject: [PATCH 17/20] update comment --- cmd/envtool/envtool.go | 26 +++++++++++++++----------- cmd/envtool/envtool_test.go | 4 ++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index 681aea1304bb..06e626d24990 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -90,10 +90,6 @@ func waitForPort(ctx context.Context, logger *zap.SugaredLogger, port uint16) er } // setupAnyPostgres configures given PostgreSQL. -// -// It also creates a user in ferretdb and template1 databases so all subsequent -// databases created from template1 have the user. -// See also https://www.postgresql.org/docs/current/manage-ag-templatedbs.html. func setupAnyPostgres(ctx context.Context, logger *zap.SugaredLogger, uri string) error { u, err := url.Parse(uri) if err != nil { @@ -144,6 +140,10 @@ func setupAnyPostgres(ctx context.Context, logger *zap.SugaredLogger, uri string } // setupPostgres configures `postgres` container. +// +// It also creates a user in ferretdb and template1 databases so all subsequent +// databases created from template1 have the user. +// See also https://www.postgresql.org/docs/current/manage-ag-templatedbs.html. func setupPostgres(ctx context.Context, logger *zap.SugaredLogger) error { l := logger.Named("postgres") @@ -153,15 +153,19 @@ func setupPostgres(ctx context.Context, logger *zap.SugaredLogger) error { return err } - err = setupPostgresUser(ctx, logger, "postgres://username@127.0.0.1:5432/ferretdb") + err = setupUser(ctx, logger, "postgres://username@127.0.0.1:5432/ferretdb") if err != nil { return err } - return setupPostgresUser(ctx, logger, "postgres://username@127.0.0.1:5432/template1") + return setupUser(ctx, logger, "postgres://username@127.0.0.1:5432/template1") } // setupPostgresSecured configures `postgres_secured` container. +// +// It also creates a user in ferretdb and template1 databases so all subsequent +// databases created from template1 have the user. +// See also https://www.postgresql.org/docs/current/manage-ag-templatedbs.html. func setupPostgresSecured(ctx context.Context, logger *zap.SugaredLogger) error { l := logger.Named("postgres_secured") @@ -170,12 +174,12 @@ func setupPostgresSecured(ctx context.Context, logger *zap.SugaredLogger) error return err } - err = setupPostgresUser(ctx, logger, "postgres://username:password@127.0.0.1:5433/ferretdb") + err = setupUser(ctx, logger, "postgres://username:password@127.0.0.1:5433/ferretdb") if err != nil { return err } - return setupPostgresUser(ctx, logger, "postgres://username:password@127.0.0.1:5433/template1") + return setupUser(ctx, logger, "postgres://username:password@127.0.0.1:5433/template1") } // setupMySQL configures `mysql` container. @@ -251,14 +255,14 @@ func setupMongodb(ctx context.Context, logger *zap.SugaredLogger) error { return ctx.Err() } -// setupPostgresUser creates a user with username/password in admin database with supported mechanisms. -// The user uses the same credential as the PostgreSQL credentials. +// setupUser creates a user with username/password credential in admin database +// with supported mechanisms. // It creates admin database (PostgreSQL admin schema), if it does not exist. // It also creates system.users collection, if it does not exist. // // Without this, once the first user is created, the authentication fails // as username/password does not exist in admin.system.users collection. -func setupPostgresUser(ctx context.Context, logger *zap.SugaredLogger, uri string) error { +func setupUser(ctx context.Context, logger *zap.SugaredLogger, uri string) error { sp, err := state.NewProvider("") if err != nil { return err diff --git a/cmd/envtool/envtool_test.go b/cmd/envtool/envtool_test.go index 60a99d98c981..96b2706e834f 100644 --- a/cmd/envtool/envtool_test.go +++ b/cmd/envtool/envtool_test.go @@ -141,10 +141,10 @@ func TestSetupPostgresUser(t *testing.T) { uri := fmt.Sprintf("postgres://username@127.0.0.1:5432/%s", dbName) - err = setupPostgresUser(ctx, l.Sugar(), uri) + err = setupUser(ctx, l.Sugar(), uri) require.NoError(t, err) // if the user already exists, it should not fail - err = setupPostgresUser(ctx, l.Sugar(), uri) + err = setupUser(ctx, l.Sugar(), uri) require.NoError(t, err) } From 5278da86b25a7104ac68d86610a3f4b9413e62ea Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Tue, 5 Mar 2024 16:05:01 +0900 Subject: [PATCH 18/20] renaming --- cmd/envtool/envtool.go | 26 ++++++++++--------- cmd/envtool/envtool_test.go | 6 ++--- ...stem_users.json => test_system_users.json} | 0 cmd/envtool/{user.json => test_user.json} | 0 4 files changed, 17 insertions(+), 15 deletions(-) rename cmd/envtool/{system_users.json => test_system_users.json} (100%) rename cmd/envtool/{user.json => test_user.json} (100%) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index 06e626d24990..cb38f2afecfc 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -54,11 +54,11 @@ var ( //go:embed error.tmpl errorTemplateB []byte - //go:embed user.json - user string + //go:embed test_user.json + testUser string - //go:embed system_users.json - systemUsers string + //go:embed test_system_users.json + testSystemUsers string // Parsed error template. errorTemplate = template.Must(template.New("error").Option("missingkey=error").Parse(string(errorTemplateB))) @@ -153,12 +153,12 @@ func setupPostgres(ctx context.Context, logger *zap.SugaredLogger) error { return err } - err = setupUser(ctx, logger, "postgres://username@127.0.0.1:5432/ferretdb") + err = setupUserInPostgres(ctx, logger, "postgres://username@127.0.0.1:5432/ferretdb") if err != nil { return err } - return setupUser(ctx, logger, "postgres://username@127.0.0.1:5432/template1") + return setupUserInPostgres(ctx, logger, "postgres://username@127.0.0.1:5432/template1") } // setupPostgresSecured configures `postgres_secured` container. @@ -174,12 +174,12 @@ func setupPostgresSecured(ctx context.Context, logger *zap.SugaredLogger) error return err } - err = setupUser(ctx, logger, "postgres://username:password@127.0.0.1:5433/ferretdb") + err = setupUserInPostgres(ctx, logger, "postgres://username:password@127.0.0.1:5433/ferretdb") if err != nil { return err } - return setupUser(ctx, logger, "postgres://username:password@127.0.0.1:5433/template1") + return setupUserInPostgres(ctx, logger, "postgres://username:password@127.0.0.1:5433/template1") } // setupMySQL configures `mysql` container. @@ -255,14 +255,14 @@ func setupMongodb(ctx context.Context, logger *zap.SugaredLogger) error { return ctx.Err() } -// setupUser creates a user with username/password credential in admin database +// setupUserInPostgres creates a user with username/password credential in admin database // with supported mechanisms. // It creates admin database (PostgreSQL admin schema), if it does not exist. // It also creates system.users collection, if it does not exist. // // Without this, once the first user is created, the authentication fails // as username/password does not exist in admin.system.users collection. -func setupUser(ctx context.Context, logger *zap.SugaredLogger, uri string) error { +func setupUserInPostgres(ctx context.Context, logger *zap.SugaredLogger, uri string) error { sp, err := state.NewProvider("") if err != nil { return err @@ -326,13 +326,15 @@ func setupUser(ctx context.Context, logger *zap.SugaredLogger, uri string) error } q = `INSERT INTO admin._ferretdb_database_metadata (_jsonb) VALUES ($1)` - if _, err = dbPool.Exec(ctx, q, systemUsers); err != nil { + if _, err = dbPool.Exec(ctx, q, testSystemUsers); err != nil { return err } } q = `INSERT INTO admin.system_users_aff2f7ce (_jsonb) VALUES ($1)` - if _, err = dbPool.Exec(ctx, q, user); err != nil && (!errors.As(err, &pgErr) || pgErr.Code != pgerrcode.UniqueViolation) { + + _, err = dbPool.Exec(ctx, q, testUser) + if err != nil && (!errors.As(err, &pgErr) || pgErr.Code != pgerrcode.UniqueViolation) { return err } diff --git a/cmd/envtool/envtool_test.go b/cmd/envtool/envtool_test.go index 96b2706e834f..5a2d4fea168e 100644 --- a/cmd/envtool/envtool_test.go +++ b/cmd/envtool/envtool_test.go @@ -99,7 +99,7 @@ func TestPackageVersion(t *testing.T) { assert.Equal(t, "1.0.0", output.String()) } -func TestSetupPostgresUser(t *testing.T) { +func TestSetupUserInPostgres(t *testing.T) { if testing.Short() { t.Skip("skipping in -short mode") } @@ -141,10 +141,10 @@ func TestSetupPostgresUser(t *testing.T) { uri := fmt.Sprintf("postgres://username@127.0.0.1:5432/%s", dbName) - err = setupUser(ctx, l.Sugar(), uri) + err = setupUserInPostgres(ctx, l.Sugar(), uri) require.NoError(t, err) // if the user already exists, it should not fail - err = setupUser(ctx, l.Sugar(), uri) + err = setupUserInPostgres(ctx, l.Sugar(), uri) require.NoError(t, err) } diff --git a/cmd/envtool/system_users.json b/cmd/envtool/test_system_users.json similarity index 100% rename from cmd/envtool/system_users.json rename to cmd/envtool/test_system_users.json diff --git a/cmd/envtool/user.json b/cmd/envtool/test_user.json similarity index 100% rename from cmd/envtool/user.json rename to cmd/envtool/test_user.json From 58014761b69d13937569ed5d7fd830ca8834a197 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Wed, 6 Mar 2024 10:44:29 +0900 Subject: [PATCH 19/20] use require in test for not panicing when adding new backend --- integration/commands_authentication_test.go | 31 +++++++++++++-------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/integration/commands_authentication_test.go b/integration/commands_authentication_test.go index 6ddf2230dfeb..22213abf49fd 100644 --- a/integration/commands_authentication_test.go +++ b/integration/commands_authentication_test.go @@ -17,7 +17,6 @@ package integration import ( "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" @@ -64,10 +63,15 @@ func TestCommandsAuthenticationLogout(t *testing.T) { var res bson.D err = db.RunCommand(ctx, bson.D{{"connectionStatus", 1}}).Decode(&res) - assert.NoError(t, err) + require.NoError(t, err) - actualAuth := must.NotFail(ConvertDocument(t, res).Get("authInfo")).(*types.Document) - actualUsers := must.NotFail(actualAuth.Get("authenticatedUsers")).(*types.Array) + actualAuth, _ := ConvertDocument(t, res).Get("authInfo") + require.NotNil(t, actualAuth) + + actualUsersV, _ := actualAuth.(*types.Document).Get("authenticatedUsers") + require.NotNil(t, actualUsersV) + + actualUsers := actualUsersV.(*types.Array) var hasUser bool @@ -79,10 +83,10 @@ func TestCommandsAuthenticationLogout(t *testing.T) { } } - assert.True(t, hasUser, res) + require.True(t, hasUser, res) err = db.RunCommand(ctx, bson.D{{"logout", 1}}).Decode(&res) - assert.NoError(t, err) + require.NoError(t, err) actual := ConvertDocument(t, res) actual.Remove("$clusterTime") @@ -92,19 +96,24 @@ func TestCommandsAuthenticationLogout(t *testing.T) { testutil.AssertEqual(t, expected, actual) err = db.RunCommand(ctx, bson.D{{"connectionStatus", 1}}).Decode(&res) - assert.NoError(t, err) + require.NoError(t, err) + + actualAuth, _ = ConvertDocument(t, res).Get("authInfo") + require.NotNil(t, actualAuth) + + actualUsersV, _ = actualAuth.(*types.Document).Get("authenticatedUsers") + require.NotNil(t, actualUsersV) - actualAuth = must.NotFail(ConvertDocument(t, res).Get("authInfo")).(*types.Document) - actualUsers = must.NotFail(actualAuth.Get("authenticatedUsers")).(*types.Array) + actualUsers = actualUsersV.(*types.Array) for i := 0; i < actualUsers.Len(); i++ { actualUser := must.NotFail(must.NotFail(actualUsers.Get(i)).(*types.Document).Get("user")) if actualUser == username { - assert.Fail(t, "user is still authenticated", res) + require.Fail(t, "user is still authenticated", res) } } // the test user logs out again, it has no effect err = db.RunCommand(ctx, bson.D{{"logout", 1}}).Err() - assert.NoError(t, err) + require.NoError(t, err) } From afae527eff3b5fee315486c014d91027ca7e246c Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Wed, 6 Mar 2024 13:09:44 +0900 Subject: [PATCH 20/20] undo --- cmd/envtool/envtool.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/envtool/envtool.go b/cmd/envtool/envtool.go index cb38f2afecfc..bebccf841df0 100644 --- a/cmd/envtool/envtool.go +++ b/cmd/envtool/envtool.go @@ -136,7 +136,11 @@ func setupAnyPostgres(ctx context.Context, logger *zap.SugaredLogger, uri string ctxutil.SleepWithJitter(ctx, time.Second, retry) } - return ctx.Err() + if ctx.Err() != nil { + return ctx.Err() + } + + return nil } // setupPostgres configures `postgres` container.