From 8d6dee09a92eeb2646978b8231041d4bfe3c8c95 Mon Sep 17 00:00:00 2001 From: Jim Date: Thu, 30 Sep 2021 09:17:27 -0400 Subject: [PATCH] refact: Add db.DB wrapper and refact all test fixtures to use it. (#1535) * refact: Changes required for gorm v2 * refact: Add db.DB wrapper and refact all test fixtures to use it Stop leaking the gorm abstraction within our unit tests. * refact: Migrate direct calls to pq to pgx driver The refactor will remove the remaining direct dependencies on the pq driver which has been deprecated in favor of the pgx driver. It includes the addition of common.SqlOpen which is a wrapper around sql.Open, which is needed to deal with the driver name changing from "postgres" to "pgx" but in our code base the dialect and driver are synonyms. --- depowners.md | 3 + go.mod | 3 +- go.sum | 3 +- internal/auth/oidc/repository_account_test.go | 2 +- .../oidc/repository_managed_group_test.go | 2 +- internal/auth/oidc/testing.go | 9 ++- internal/auth/password/argon2_test.go | 3 +- .../auth/password/repository_account_test.go | 3 +- .../password/repository_authmethod_test.go | 2 +- internal/auth/password/testing.go | 9 ++- internal/authtoken/testing.go | 3 +- internal/cmd/base/dev.go | 2 +- .../cmd/base/internal/docker/supported.go | 7 +- internal/cmd/base/servers.go | 42 +++-------- internal/cmd/commands/database/funcs.go | 4 +- internal/cmd/commands/database/funcs_test.go | 20 +++--- internal/cmd/commands/database/init.go | 10 +-- internal/cmd/commands/dev/dev.go | 2 +- internal/cmd/commands/server/server.go | 4 +- internal/credential/vault/jobs_test.go | 5 +- .../repository_credential_library_test.go | 2 +- .../vault/repository_credential_store_test.go | 31 ++++---- internal/credential/vault/supported.go | 7 +- internal/credential/vault/testing.go | 17 +++-- internal/db/assert/asserts_test.go | 12 ++-- internal/db/common/db.go | 15 ++++ internal/db/db.go | 70 +++++++++++++++++-- internal/db/db_test.go | 4 +- internal/db/domains_test.go | 67 ++++++++++-------- internal/db/option.go | 20 ++++++ internal/db/option_test.go | 27 +++++++ internal/db/read_writer.go | 22 +++--- internal/db/read_writer_test.go | 21 +++--- internal/db/schema/manager_test.go | 23 +++--- .../postgres/11/01_server_type_enum_test.go | 7 +- .../12/01_timestamp_sub_funcs_test.go | 3 +- .../postgres/14/warehouse_user_dim_test.go | 7 +- .../16/warehouse_credential_dim_test.go | 5 +- .../migrations/postgres/2/07_iam_test.go | 4 +- .../postgres/8/08_connection_test.go | 5 +- internal/db/schema/postgres/postgres.go | 27 +++---- internal/db/schema/postgres/postgres_test.go | 2 +- internal/db/schema/postgres/testing.go | 4 +- internal/db/schema/schema.go | 4 +- internal/db/schema/schema_test.go | 4 +- internal/db/testing.go | 5 +- internal/errors/is_test.go | 6 +- internal/host/static/host_catalog_test.go | 3 +- .../static/repository_host_catalog_test.go | 3 +- .../host/static/repository_host_set_test.go | 3 +- internal/host/static/repository_host_test.go | 3 +- internal/host/static/testing.go | 9 ++- internal/iam/account_test.go | 3 +- internal/iam/group_test.go | 3 +- internal/iam/immutable_fields_test.go | 3 +- internal/iam/repository_group_test.go | 3 +- internal/iam/repository_role_test.go | 3 +- internal/iam/repository_user_iam_pkg_test.go | 3 +- internal/iam/repository_user_test.go | 6 +- internal/iam/role_test.go | 3 +- internal/iam/testing.go | 27 +++---- internal/kms/testing.go | 27 ++++--- internal/oplog/testing.go | 4 +- internal/oplog/testing_test.go | 4 +- internal/scheduler/job/testing.go | 9 ++- internal/scheduler/testing.go | 3 +- .../handlers/authmethods/oidc_test.go | 3 +- .../handlers/targets/target_service_test.go | 3 +- internal/servers/controller/testing.go | 10 +-- internal/session/repository_session_test.go | 17 +++-- internal/session/testing.go | 15 ++-- internal/target/repository_tcp_target_test.go | 3 +- internal/target/tcp_target_test.go | 3 +- internal/target/testing.go | 5 +- testing/dbtest/docs.go | 2 +- testing/dbtest/template.go | 14 ++-- testing/dbtest/template_test.go | 6 +- 77 files changed, 441 insertions(+), 321 deletions(-) create mode 100644 internal/db/common/db.go diff --git a/depowners.md b/depowners.md index de75af9e1..089092798 100644 --- a/depowners.md +++ b/depowners.md @@ -86,6 +86,9 @@ the first place. * github.com/kr/pretty * Jeff * github.com/lib/pq + * Jim + * Mike +* github.com/jackc/pgx * Jim * Mike * github.com/mattn/go-colorable diff --git a/go.mod b/go.mod index e62f65e4f..c89dffc48 100644 --- a/go.mod +++ b/go.mod @@ -52,12 +52,11 @@ require ( github.com/hashicorp/vault/sdk v0.2.1 github.com/iancoleman/strcase v0.2.0 github.com/jackc/pgconn v1.10.0 + github.com/jackc/pgx/v4 v4.11.0 github.com/jefferai/keyring v1.1.7-0.20210105022822-8749b3d9ce79 - github.com/jinzhu/gorm v1.9.12 github.com/json-iterator/go v1.1.10 // indirect github.com/kr/pretty v0.3.0 github.com/kr/text v0.2.0 - github.com/lib/pq v1.10.3 github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-isatty v0.0.13 // indirect github.com/mitchellh/cli v1.1.2 diff --git a/go.sum b/go.sum index 625890ad7..d1e9edf79 100644 --- a/go.sum +++ b/go.sum @@ -708,9 +708,8 @@ github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.6.0/go.mod h1:4vXEAYvW1fRQ2/FhZ78H73A60MHw1geSm145z2mdY1g= +github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg= -github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= diff --git a/internal/auth/oidc/repository_account_test.go b/internal/auth/oidc/repository_account_test.go index 11a22ac64..62dc111da 100644 --- a/internal/auth/oidc/repository_account_test.go +++ b/internal/auth/oidc/repository_account_test.go @@ -1160,7 +1160,7 @@ func TestRepository_UpdateAccount(t *testing.T) { assert.Equal(tt.wantCount, gotCount, "row count") assert.NotSame(tt.orig, got) assert.Equal(tt.orig.AuthMethodId, got.AuthMethodId) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.want.Name == "" { diff --git a/internal/auth/oidc/repository_managed_group_test.go b/internal/auth/oidc/repository_managed_group_test.go index e7600db3a..12cbc8db9 100644 --- a/internal/auth/oidc/repository_managed_group_test.go +++ b/internal/auth/oidc/repository_managed_group_test.go @@ -952,7 +952,7 @@ func TestRepository_UpdateManagedGroup(t *testing.T) { assert.Equal(tt.wantCount, gotCount, "row count") assert.NotSame(tt.orig, got) assert.Equal(tt.orig.AuthMethodId, got.AuthMethodId) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.want.Name == "" { diff --git a/internal/auth/oidc/testing.go b/internal/auth/oidc/testing.go index 4fc07d8fb..d46ff8c6d 100644 --- a/internal/auth/oidc/testing.go +++ b/internal/auth/oidc/testing.go @@ -31,7 +31,6 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/timestamppb" - "gorm.io/gorm" ) const TestFakeManagedGroupFilter = `"/foo" == "bar"` @@ -41,7 +40,7 @@ const TestFakeManagedGroupFilter = `"/foo" == "bar"` // WithSigningAlgs options are supported. func TestAuthMethod( t *testing.T, - conn *gorm.DB, + conn *db.DB, databaseWrapper wrapping.Wrapper, scopeId string, state AuthMethodState, @@ -158,7 +157,7 @@ func TestSortAuthMethods(t *testing.T, methods []*AuthMethod) { } // TestAccount creates a test oidc auth account. -func TestAccount(t *testing.T, conn *gorm.DB, am *AuthMethod, subject string, opt ...Option) *Account { +func TestAccount(t *testing.T, conn *db.DB, am *AuthMethod, subject string, opt ...Option) *Account { t.Helper() require := require.New(t) rw := db.New(conn) @@ -179,7 +178,7 @@ func TestAccount(t *testing.T, conn *gorm.DB, am *AuthMethod, subject string, op } // TestManagedGroup creates a test oidc managed group. -func TestManagedGroup(t *testing.T, conn *gorm.DB, am *AuthMethod, filter string, opt ...Option) *ManagedGroup { +func TestManagedGroup(t *testing.T, conn *db.DB, am *AuthMethod, filter string, opt ...Option) *ManagedGroup { t.Helper() require := require.New(t) rw := db.New(conn) @@ -197,7 +196,7 @@ func TestManagedGroup(t *testing.T, conn *gorm.DB, am *AuthMethod, filter string } // TestManagedGroupMember adds given account IDs to a managed group -func TestManagedGroupMember(t *testing.T, conn *gorm.DB, managedGroupId, memberId string, opt ...Option) *ManagedGroupMemberAccount { +func TestManagedGroupMember(t *testing.T, conn *db.DB, managedGroupId, memberId string, opt ...Option) *ManagedGroupMemberAccount { t.Helper() require := require.New(t) rw := db.New(conn) diff --git a/internal/auth/password/argon2_test.go b/internal/auth/password/argon2_test.go index 0a81f2bc5..f53fb4534 100644 --- a/internal/auth/password/argon2_test.go +++ b/internal/auth/password/argon2_test.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) func TestArgon2Configuration_New(t *testing.T) { @@ -289,7 +288,7 @@ func TestArgon2Configuration_Validate(t *testing.T) { } } -func testArgon2Confs(t *testing.T, conn *gorm.DB, authMethodId string, count int) []*Argon2Configuration { +func testArgon2Confs(t *testing.T, conn *db.DB, authMethodId string, count int) []*Argon2Configuration { t.Helper() assert, require := assert.New(t), require.New(t) rw := db.New(conn) diff --git a/internal/auth/password/repository_account_test.go b/internal/auth/password/repository_account_test.go index 031d5efb3..13ee140a7 100644 --- a/internal/auth/password/repository_account_test.go +++ b/internal/auth/password/repository_account_test.go @@ -589,6 +589,7 @@ func TestRepository_UpdateAccount(t *testing.T) { wrapper := db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) + ctx := context.Background() changeLoginName := func(s string) func(*Account) *Account { return func(a *Account) *Account { @@ -949,7 +950,7 @@ func TestRepository_UpdateAccount(t *testing.T) { assert.Equal(tt.wantCount, gotCount, "row count") assert.NotSame(tt.orig, got) assert.Equal(tt.orig.AuthMethodId, got.AuthMethodId) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.want.Name == "" { diff --git a/internal/auth/password/repository_authmethod_test.go b/internal/auth/password/repository_authmethod_test.go index 33783b0e6..1c5a073a6 100644 --- a/internal/auth/password/repository_authmethod_test.go +++ b/internal/auth/password/repository_authmethod_test.go @@ -769,7 +769,7 @@ func TestRepository_UpdateAuthMethod(t *testing.T) { require.NoError(err) assert.Empty(cmp.Diff(updatedAM, foundAuthMethod, protocmp.Transform())) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if amToUpdate.Name == "" && contains(tt.args.fieldMaskPaths, "name") { diff --git a/internal/auth/password/testing.go b/internal/auth/password/testing.go index 1f6995403..975ed4cb9 100644 --- a/internal/auth/password/testing.go +++ b/internal/auth/password/testing.go @@ -8,13 +8,12 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) // TestAuthMethod creates a password auth methods to the provided DB with the // provided scope id. If any errors are encountered during the creation of the // auth methods, the test will fail. -func TestAuthMethod(t *testing.T, conn *gorm.DB, scopeId string, opt ...Option) *AuthMethod { +func TestAuthMethod(t *testing.T, conn *db.DB, scopeId string, opt ...Option) *AuthMethod { t.Helper() assert, require := assert.New(t), require.New(t) w := db.New(conn) @@ -48,7 +47,7 @@ func TestAuthMethod(t *testing.T, conn *gorm.DB, scopeId string, opt ...Option) // TestAuthMethods creates count number of password auth methods to the provided DB // with the provided scope id. If any errors are encountered during the creation of // the auth methods, the test will fail. -func TestAuthMethods(t *testing.T, conn *gorm.DB, scopeId string, count int) []*AuthMethod { +func TestAuthMethods(t *testing.T, conn *db.DB, scopeId string, count int) []*AuthMethod { t.Helper() var auts []*AuthMethod for i := 0; i < count; i++ { @@ -60,7 +59,7 @@ func TestAuthMethods(t *testing.T, conn *gorm.DB, scopeId string, count int) []* // TestMultipleAccounts creates count number of password account to the provided DB // with the provided auth method id. The auth method must have been created previously. // If any errors are encountered during the creation of the account, the test will fail. -func TestMultipleAccounts(t *testing.T, conn *gorm.DB, authMethodId string, count int) []*Account { +func TestMultipleAccounts(t *testing.T, conn *db.DB, authMethodId string, count int) []*Account { t.Helper() var auts []*Account for i := 0; i < count; i++ { @@ -73,7 +72,7 @@ func TestMultipleAccounts(t *testing.T, conn *gorm.DB, authMethodId string, coun // auth method id and loginName. The auth method must have been created // previously. See password.NewAccount(...) for a list of supported options. // If any errors are encountered during the creation of the account, the test will fail. -func TestAccount(t *testing.T, conn *gorm.DB, authMethodId, loginName string, opt ...Option) *Account { +func TestAccount(t *testing.T, conn *db.DB, authMethodId, loginName string, opt ...Option) *Account { t.Helper() assert, require := assert.New(t), require.New(t) require.NotEmpty(loginName) diff --git a/internal/authtoken/testing.go b/internal/authtoken/testing.go index 2b95e00c4..b34cded2f 100644 --- a/internal/authtoken/testing.go +++ b/internal/authtoken/testing.go @@ -9,10 +9,9 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) -func TestAuthToken(t *testing.T, conn *gorm.DB, kms *kms.Kms, scopeId string, opt ...Option) *AuthToken { +func TestAuthToken(t *testing.T, conn *db.DB, kms *kms.Kms, scopeId string, opt ...Option) *AuthToken { t.Helper() authMethod := password.TestAuthMethods(t, conn, scopeId, 1)[0] // auth account is only used to join auth method to user. diff --git a/internal/cmd/base/dev.go b/internal/cmd/base/dev.go index 5db90e915..58185f21e 100644 --- a/internal/cmd/base/dev.go +++ b/internal/cmd/base/dev.go @@ -94,7 +94,7 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error { b.Info["dev database container"] = strings.TrimPrefix(container, "/") } - if err := b.ConnectToDatabase(dialect); err != nil { + if err := b.ConnectToDatabase(ctx, dialect); err != nil { if c != nil { err = multierror.Append(err, c()) } diff --git a/internal/cmd/base/internal/docker/supported.go b/internal/cmd/base/internal/docker/supported.go index 55867a8e2..50460fc18 100644 --- a/internal/cmd/base/internal/docker/supported.go +++ b/internal/cmd/base/internal/docker/supported.go @@ -4,11 +4,12 @@ package docker import ( - "database/sql" "fmt" "os" "strings" + "github.com/hashicorp/boundary/internal/db/common" + _ "github.com/jackc/pgx/v4/stdlib" "github.com/ory/dockertest/v3" ) @@ -37,7 +38,7 @@ func startDbInDockerSupported(dialect string, opt ...Option) (cleanup func() err } switch dialect { - case "postgres": + case "postgres", "pgx": switch { case os.Getenv("BOUNDARY_TESTING_PG_URL") != "": url = os.Getenv("BOUNDARY_TESTING_PG_URL") @@ -68,7 +69,7 @@ func startDbInDockerSupported(dialect string, opt ...Option) (cleanup func() err } if err := pool.Retry(func() error { - db, err := sql.Open(dialect, url) + db, err := common.SqlOpen(dialect, url) if err != nil { return fmt.Errorf("error opening %s dev container: %w", dialect, err) } diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index e9edaf369..90d7671b3 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -39,8 +39,6 @@ import ( "github.com/hashicorp/go-secure-stdlib/strutil" "github.com/mitchellh/cli" "google.golang.org/grpc/grpclog" - "gorm.io/gorm" - "gorm.io/gorm/logger" ) const ( @@ -120,7 +118,7 @@ type Server struct { DatabaseMaxOpenConnections int DevDatabaseCleanupFunc func() error - Database *gorm.DB + Database *db.DB // StatusGracePeriodDuration represents the period of time (as a // duration) that the controller will wait before marking @@ -548,38 +546,22 @@ func (b *Server) RunShutdownFuncs() error { return mErr.ErrorOrNil() } -func (b *Server) ConnectToDatabase(dialect string) error { +func (b *Server) ConnectToDatabase(ctx context.Context, dialect string) error { dbType, err := db.StringToDbType(dialect) if err != nil { return fmt.Errorf("unable to create db object with dialect %s: %w", dialect, err) } - dbase, err := db.Open(dbType, b.DatabaseUrl) + opts := []db.Option{ + db.WithMaxOpenConnections(b.DatabaseMaxOpenConnections), + } + if os.Getenv("BOUNDARY_DISABLE_GORM_FORMATTER") == "" { + opts = append(opts, db.WithGormFormatter(b.Logger)) + } + dbase, err := db.Open(dbType, b.DatabaseUrl, opts...) if err != nil { return fmt.Errorf("unable to create db object with dialect %s: %w", dialect, err) } - if b.DatabaseMaxOpenConnections == 1 { - return fmt.Errorf("unable to create db object with dialect %s: %s", dialect, "max_open_connections must be unlimited by setting 0 or at least 2") - } else { - underlyingDB, err := dbase.DB() - if err != nil { - return fmt.Errorf("unable retreive db: %w", err) - } - underlyingDB.SetMaxOpenConns(b.DatabaseMaxOpenConnections) - } b.Database = dbase - if os.Getenv("BOUNDARY_DISABLE_GORM_FORMATTER") == "" { - newLogger := logger.New( - db.GetGormLogger(b.Logger), - logger.Config{ - LogLevel: logger.Error, // Log level - Colorful: false, // Disable color - }, - ) - dbase = dbase.Session(&gorm.Session{Logger: newLogger}) - } - - b.Database = dbase - return nil } @@ -618,13 +600,11 @@ func (b *Server) CreateGlobalKmsKeys(ctx context.Context) error { return nil } -func (b *Server) DestroyDevDatabase() error { +func (b *Server) DestroyDevDatabase(ctx context.Context) error { if b.Database != nil { - underlyingDB, err := b.Database.DB() - if err != nil { + if err := b.Database.Close(ctx); err != nil { return err } - underlyingDB.Close() } if b.DevDatabaseCleanupFunc != nil { return b.DevDatabaseCleanupFunc() diff --git a/internal/cmd/commands/database/funcs.go b/internal/cmd/commands/database/funcs.go index f1ea8d486..9438b8725 100644 --- a/internal/cmd/commands/database/funcs.go +++ b/internal/cmd/commands/database/funcs.go @@ -2,10 +2,10 @@ package database import ( "context" - "database/sql" "fmt" "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/internal/errors" "github.com/mitchellh/cli" @@ -20,7 +20,7 @@ func migrateDatabase(ctx context.Context, ui cli.Ui, dialect, u string, initiali noop := func() {} // This database is used to keep an exclusive lock on the database for the // remainder of the command - dBase, err := sql.Open(dialect, u) + dBase, err := common.SqlOpen(dialect, u) if err != nil { ui.Error(fmt.Errorf("Error establishing db connection: %w", err).Error()) return noop, 2 diff --git a/internal/cmd/commands/database/funcs_test.go b/internal/cmd/commands/database/funcs_test.go index fe41b4656..6ad2dd1b5 100644 --- a/internal/cmd/commands/database/funcs_test.go +++ b/internal/cmd/commands/database/funcs_test.go @@ -2,10 +2,10 @@ package database import ( "context" - "database/sql" "testing" "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/testing/dbtest" "github.com/mitchellh/cli" @@ -49,7 +49,7 @@ func TestMigrateDatabase(t *testing.T) { require.NoError(t, c()) }) - dBase, err := sql.Open(dialect, u) + dBase, err := common.SqlOpen(dialect, u) require.NoError(t, err) earlyMigrationVersion := 2000 @@ -75,7 +75,7 @@ func TestMigrateDatabase(t *testing.T) { require.NoError(t, c()) }) - dBase, err := sql.Open(dialect, u) + dBase, err := common.SqlOpen(dialect, u) require.NoError(t, err) earlyMigrationVersion := 2000 @@ -100,7 +100,7 @@ func TestMigrateDatabase(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - dBase, err := sql.Open(dialect, u) + dBase, err := common.SqlOpen(dialect, u) require.NoError(t, err) createStmt := `create table if not exists schema_migrations (version bigint primary key, dirty boolean not null)` @@ -120,7 +120,7 @@ func TestMigrateDatabase(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - dBase, err := sql.Open(dialect, u) + dBase, err := common.SqlOpen(dialect, u) require.NoError(t, err) createStmt := `create table if not exists schema_migrations (version bigint primary key, dirty boolean not null)` @@ -154,7 +154,7 @@ func TestMigrateDatabase(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - dBase, err := sql.Open(dialect, u) + dBase, err := common.SqlOpen(dialect, u) require.NoError(t, err) man, err := schema.NewManager(ctx, dialect, dBase) @@ -177,7 +177,7 @@ func TestMigrateDatabase(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - dBase, err := sql.Open(dialect, u) + dBase, err := common.SqlOpen(dialect, u) require.NoError(t, err) man, err := schema.NewManager(ctx, dialect, dBase) @@ -217,7 +217,7 @@ func TestVerifyOplogIsEmpty(t *testing.T) { }) _ = c - dBase, err := sql.Open(dialect, u) + dBase, err := common.SqlOpen(dialect, u) require.NoError(t, err) man, err := schema.NewManager(ctx, dialect, dBase) @@ -228,7 +228,7 @@ func TestVerifyOplogIsEmpty(t *testing.T) { cmd.srv = base.NewServer(&base.Command{UI: cmd.UI}) cmd.srv.DatabaseUrl = u - require.NoError(t, cmd.srv.ConnectToDatabase(dialect)) + require.NoError(t, cmd.srv.ConnectToDatabase(ctx, dialect)) - assert.NoError(t, cmd.verifyOplogIsEmpty()) + assert.NoError(t, cmd.verifyOplogIsEmpty(ctx)) } diff --git a/internal/cmd/commands/database/init.go b/internal/cmd/commands/database/init.go index 47c40cd48..6c8a81abf 100644 --- a/internal/cmd/commands/database/init.go +++ b/internal/cmd/commands/database/init.go @@ -1,6 +1,7 @@ package database import ( + "context" "fmt" "github.com/hashicorp/boundary/internal/cmd/base" @@ -164,6 +165,7 @@ func (c *InitCommand) AutocompleteFlags() complete.Flags { } func (c *InitCommand) Run(args []string) (retCode int) { + ctx := context.Background() if result := c.ParseFlagsAndConfig(args); result > 0 { return result } @@ -277,11 +279,11 @@ func (c *InitCommand) Run(args []string) (retCode int) { return base.CommandUserError } // Everything after is done with normal database URL and is affecting actual data - if err := c.srv.ConnectToDatabase(dialect); err != nil { + if err := c.srv.ConnectToDatabase(ctx, dialect); err != nil { c.UI.Error(fmt.Errorf("Error connecting to database after migrations: %w", err).Error()) return base.CommandCliError } - if err := c.verifyOplogIsEmpty(); err != nil { + if err := c.verifyOplogIsEmpty(ctx); err != nil { c.UI.Error(fmt.Sprintf("The database appears to have already been initialized: %v", err)) return base.CommandCliError } @@ -491,9 +493,9 @@ func (c *InitCommand) ParseFlagsAndConfig(args []string) int { return base.CommandSuccess } -func (c *InitCommand) verifyOplogIsEmpty() error { +func (c *InitCommand) verifyOplogIsEmpty(ctx context.Context) error { const op = "database.(InitCommand).verifyOplogIsEmpty" - underlyingDB, err := c.srv.Database.DB() + underlyingDB, err := c.srv.Database.SqlDB(ctx) if err != nil { return errors.NewDeprecated(errors.Internal, op, "unable to retreive db", errors.WithWrap(err)) } diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 6cb3934f6..8df71fd6c 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -556,7 +556,7 @@ func (c *Command) Run(args []string) int { } if !c.flagDisableDatabaseDestruction { - c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase) + c.ShutdownFuncs = append(c.ShutdownFuncs, func() error { return c.DestroyDevDatabase(ctx) }) } default: c.DatabaseUrl, err = parseutil.ParsePath(c.flagDatabaseUrl) diff --git a/internal/cmd/commands/server/server.go b/internal/cmd/commands/server/server.go index 1139573a9..a2851a79d 100644 --- a/internal/cmd/commands/server/server.go +++ b/internal/cmd/commands/server/server.go @@ -389,12 +389,12 @@ func (c *Command) Run(args []string) int { } c.DatabaseMaxOpenConnections = c.Config.Controller.Database.MaxOpenConnections - if err := c.ConnectToDatabase("postgres"); err != nil { + if err := c.ConnectToDatabase(ctx, "postgres"); err != nil { c.UI.Error(fmt.Errorf("Error connecting to database: %w", err).Error()) return base.CommandCliError } - underlyingDB, err := c.Database.DB() + underlyingDB, err := c.Database.SqlDB(ctx) if err != nil { c.UI.Error(fmt.Errorf("Can't get db: %w.", err).Error()) return base.CommandCliError diff --git a/internal/credential/vault/jobs_test.go b/internal/credential/vault/jobs_test.go index 35f963c98..3ceeaadf2 100644 --- a/internal/credential/vault/jobs_test.go +++ b/internal/credential/vault/jobs_test.go @@ -21,7 +21,6 @@ import ( vault "github.com/hashicorp/vault/api" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) const testUpdateTokenStatusExpirationQuery = ` @@ -33,7 +32,7 @@ update credential_vault_token ` func testVaultToken(t *testing.T, - conn *gorm.DB, + conn *db.DB, wrapper wrapping.Wrapper, v *TestVaultServer, cs *CredentialStore, @@ -87,7 +86,7 @@ func testVaultToken(t *testing.T, } func testVaultCred(t *testing.T, - conn *gorm.DB, + conn *db.DB, v *TestVaultServer, cl *CredentialLibrary, sess *session.Session, diff --git a/internal/credential/vault/repository_credential_library_test.go b/internal/credential/vault/repository_credential_library_test.go index 6a1e9320f..039675242 100644 --- a/internal/credential/vault/repository_credential_library_test.go +++ b/internal/credential/vault/repository_credential_library_test.go @@ -795,7 +795,7 @@ func TestRepository_UpdateCredentialLibrary(t *testing.T) { assert.Equal(tt.wantCount, gotCount, "row count") assert.NotSame(tt.orig, got) assert.Equal(tt.orig.StoreId, got.StoreId) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.want.Name == "" { diff --git a/internal/credential/vault/repository_credential_store_test.go b/internal/credential/vault/repository_credential_store_test.go index e44b0bde7..2b7f72484 100644 --- a/internal/credential/vault/repository_credential_store_test.go +++ b/internal/credential/vault/repository_credential_store_test.go @@ -23,7 +23,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" - "gorm.io/gorm" ) func TestRepository_CreateCredentialStoreResource(t *testing.T) { @@ -756,7 +755,7 @@ func TestRepository_UpdateCredentialStore_Attributes(t *testing.T) { assert.Equal(tt.wantCount, gotCount, "row count") assert.NotSame(tt.orig, got) assert.Equal(tt.orig.ScopeId, got.ScopeId) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.want.Name == "" { @@ -887,7 +886,7 @@ func TestRepository_UpdateCredentialStore_Attributes(t *testing.T) { assert.Equal(1, gotCount2, "count of updated records") require.NotNil(got2) assert.Nil(got2.CaCert) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) dbassert.IsNull(got2, "CaCert") @@ -1270,9 +1269,9 @@ func TestRepository_DeleteCredentialStore(t *testing.T) { type tokenMap map[string]*tokenCount - type setupFn func(t *testing.T, conn *gorm.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) + type setupFn func(t *testing.T, conn *db.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) - baseSetup := func(t *testing.T, conn *gorm.DB) (wrapper wrapping.Wrapper, repo *Repository, scopeId string) { + baseSetup := func(t *testing.T, conn *db.DB) (wrapper wrapping.Wrapper, repo *Repository, scopeId string) { wrapper = db.TestWrapper(t) kms := kms.TestKms(t, conn, wrapper) sche := scheduler.TestScheduler(t, conn, wrapper) @@ -1286,7 +1285,7 @@ func TestRepository_DeleteCredentialStore(t *testing.T) { return } - testStores := func(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, tokens tokenMap, scopeId string, count int) ([]*CredentialStore, tokenMap) { + testStores := func(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, tokens tokenMap, scopeId string, count int) ([]*CredentialStore, tokenMap) { if tokens == nil { tokens = make(tokenMap) } @@ -1299,13 +1298,13 @@ func TestRepository_DeleteCredentialStore(t *testing.T) { return css, tokens } - makeMaintainingTokens := func(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, tokens tokenMap, scopeId, storeId string, count int) tokenMap { + makeMaintainingTokens := func(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, tokens tokenMap, scopeId, storeId string, count int) tokenMap { testTokens(t, conn, wrapper, scopeId, storeId, count) tokens[storeId].maintaining = count return tokens } - makeRevokedTokens := func(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, tokens tokenMap, scopeId, storeId string, count int) tokenMap { + makeRevokedTokens := func(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, tokens tokenMap, scopeId, storeId string, count int) tokenMap { require.NotNil(t, tokens) const query = ` update credential_vault_token @@ -1327,7 +1326,7 @@ update credential_vault_token return tokens } - makeExpiredTokens := func(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, tokens tokenMap, scopeId, storeId string, count int) tokenMap { + makeExpiredTokens := func(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, tokens tokenMap, scopeId, storeId string, count int) tokenMap { require.NotNil(t, tokens) const query = ` update credential_vault_token @@ -1349,7 +1348,7 @@ update credential_vault_token return tokens } - assertTokens := func(t *testing.T, conn *gorm.DB, want tokenMap) { + assertTokens := func(t *testing.T, conn *db.DB, want tokenMap) { const query = ` select store_id, status, count(token_hmac) from credential_vault_token @@ -1399,7 +1398,7 @@ group by store_id, status; }{ { name: "simple", - setup: func(t *testing.T, conn *gorm.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { + setup: func(t *testing.T, conn *db.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { wrapper, repo, scopeId := baseSetup(t, conn) css, tokens := testStores(t, conn, wrapper, nil, scopeId, 1) cs := css[0] @@ -1409,7 +1408,7 @@ group by store_id, status; }, { name: "with-libraries", - setup: func(t *testing.T, conn *gorm.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { + setup: func(t *testing.T, conn *db.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { wrapper, repo, scopeId := baseSetup(t, conn) css, tokens := testStores(t, conn, wrapper, nil, scopeId, 1) cs := css[0] @@ -1421,7 +1420,7 @@ group by store_id, status; }, { name: "with-maintaining-tokens", - setup: func(t *testing.T, conn *gorm.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { + setup: func(t *testing.T, conn *db.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { wrapper, repo, scopeId := baseSetup(t, conn) css, tokens := testStores(t, conn, wrapper, nil, scopeId, 1) cs := css[0] @@ -1434,7 +1433,7 @@ group by store_id, status; }, { name: "with-revoked-tokens", - setup: func(t *testing.T, conn *gorm.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { + setup: func(t *testing.T, conn *db.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { wrapper, repo, scopeId := baseSetup(t, conn) css, tokens := testStores(t, conn, wrapper, nil, scopeId, 1) cs := css[0] @@ -1447,7 +1446,7 @@ group by store_id, status; }, { name: "with-expired-tokens", - setup: func(t *testing.T, conn *gorm.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { + setup: func(t *testing.T, conn *db.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { wrapper, repo, scopeId := baseSetup(t, conn) css, tokens := testStores(t, conn, wrapper, nil, scopeId, 1) cs := css[0] @@ -1460,7 +1459,7 @@ group by store_id, status; }, { name: "with-all-token-statuses", - setup: func(t *testing.T, conn *gorm.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { + setup: func(t *testing.T, conn *db.DB) (storeId string, libs []*CredentialLibrary, tokens tokenMap, repo *Repository) { wrapper, repo, scopeId := baseSetup(t, conn) css, tokens := testStores(t, conn, wrapper, nil, scopeId, 1) cs := css[0] diff --git a/internal/credential/vault/supported.go b/internal/credential/vault/supported.go index 206bee5f9..1cfcf95ac 100644 --- a/internal/credential/vault/supported.go +++ b/internal/credential/vault/supported.go @@ -5,7 +5,6 @@ package vault import ( "crypto/tls" - "database/sql" "fmt" "io/ioutil" "net/http" @@ -16,8 +15,10 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/go-rootcerts" vault "github.com/hashicorp/vault/api" + _ "github.com/jackc/pgx/v4/stdlib" "github.com/ory/dockertest/v3" "github.com/stretchr/testify/require" ) @@ -224,7 +225,7 @@ func gotMountDatabase(t *testing.T, v *TestVaultServer, opt ...TestOption) *Test dburl := fmt.Sprintf(dbUrlTemplate, "postgres", "password") err = pool.Retry(func() error { var err error - db, err := sql.Open("postgres", dburl) + db, err := common.SqlOpen("postgres", dburl) if err != nil { return err } @@ -244,7 +245,7 @@ func gotMountDatabase(t *testing.T, v *TestVaultServer, opt ...TestOption) *Test grantClosedRole = `grant select, insert, update, delete on boundary_closed to closed_role` ) - db, err := sql.Open("postgres", dburl) + db, err := common.SqlOpen("postgres", dburl) require.NoError(err) defer db.Close() diff --git a/internal/credential/vault/testing.go b/internal/credential/vault/testing.go index bc9bb2741..53042d626 100644 --- a/internal/credential/vault/testing.go +++ b/internal/credential/vault/testing.go @@ -7,7 +7,6 @@ import ( "crypto/rand" "crypto/x509" "crypto/x509/pkix" - "database/sql" "encoding/pem" "fmt" "math/big" @@ -19,6 +18,7 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/db" @@ -28,14 +28,13 @@ import ( vault "github.com/hashicorp/vault/api" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) // TestCredentialStore creates a vault credential store in the provided DB with // the provided scope, vault address, token, and accessor and any values passed // in through the Options vargs. If any errors are encountered during the // creation of the store, the test will fail. -func TestCredentialStore(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, scopeId, vaultAddr, vaultToken, accessor string, opts ...Option) *CredentialStore { +func TestCredentialStore(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, scopeId, vaultAddr, vaultToken, accessor string, opts ...Option) *CredentialStore { t.Helper() ctx := context.Background() kmsCache := kms.TestKms(t, conn, wrapper) @@ -76,7 +75,7 @@ func TestCredentialStore(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, // the provided DB with the provided scope id. If any errors are // encountered during the creation of the credential stores, the test will // fail. -func TestCredentialStores(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, scopeId string, count int) []*CredentialStore { +func TestCredentialStores(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, scopeId string, count int) []*CredentialStore { t.Helper() ctx := context.Background() kmsCache := kms.TestKms(t, conn, wrapper) @@ -114,7 +113,7 @@ func TestCredentialStores(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, // libraries in the provided DB with the provided store id. If any errors // are encountered during the creation of the credential libraries, the // test will fail. -func TestCredentialLibraries(t *testing.T, conn *gorm.DB, _ wrapping.Wrapper, storeId string, count int) []*CredentialLibrary { +func TestCredentialLibraries(t *testing.T, conn *db.DB, _ wrapping.Wrapper, storeId string, count int) []*CredentialLibrary { t.Helper() assert, require := assert.New(t), require.New(t) w := db.New(conn) @@ -145,7 +144,7 @@ func TestCredentialLibraries(t *testing.T, conn *gorm.DB, _ wrapping.Wrapper, st // TestCredentials creates count number of vault credentials in the provided DB with // the provided library id and session id. If any errors are encountered // during the creation of the credentials, the test will fail. -func TestCredentials(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, libraryId, sessionId string, count int) []*Credential { +func TestCredentials(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, libraryId, sessionId string, count int) []*Credential { t.Helper() assert, require := assert.New(t), require.New(t) rw := db.New(conn) @@ -189,7 +188,7 @@ func TestCredentials(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, libr return credentials } -func createTestToken(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, scopeId, storeId, token, accessor string) *Token { +func createTestToken(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, scopeId, storeId, token, accessor string) *Token { t.Helper() w := db.New(conn) ctx := context.Background() @@ -211,7 +210,7 @@ func createTestToken(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, scop return inToken } -func testTokens(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, scopeId, storeId string, count int) []*Token { +func testTokens(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, scopeId, storeId string, count int) []*Token { t.Helper() assert, require := assert.New(t), require.New(t) w := db.New(conn) @@ -871,7 +870,7 @@ func (d *TestDatabase) ValidateCredential(t *testing.T, s *vault.Secret) error { require := require.New(t) require.NotNil(s) dburl := d.URL.Encode(t, s) - db, err := sql.Open("postgres", dburl) + db, err := common.SqlOpen("postgres", dburl) if err != nil { return err } diff --git a/internal/db/assert/asserts_test.go b/internal/db/assert/asserts_test.go index fff28927f..e19c2ca0a 100644 --- a/internal/db/assert/asserts_test.go +++ b/internal/db/assert/asserts_test.go @@ -1,6 +1,7 @@ package dbassert import ( + "context" "testing" "github.com/hashicorp/boundary/internal/db" @@ -10,7 +11,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) func Test_FieldDomain(t *testing.T) { @@ -59,11 +59,12 @@ func Test_FieldNullable(t *testing.T) { func Test_FieldIsNull(t *testing.T) { t.Parallel() + ctx := context.Background() assert := assert.New(t) conn, _ := db.TestSetup(t, "postgres") initStore(t, conn) require := require.New(t) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) assert.NoError(err) mockery := new(dbassert.MockTesting) @@ -82,10 +83,11 @@ func Test_FieldIsNull(t *testing.T) { func Test_FieldNotNull(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") initStore(t, conn) require := require.New(t) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) mockery := new(dbassert.MockTesting) dbassert := New(mockery, underlyingDB) @@ -113,7 +115,7 @@ type testModel struct { func (*testModel) TableName() string { return "test_table_dbasserts" } // CreateTestModel is a helper that creates a testModel in the DB. -func createTestModel(t dbassert.TestingT, db *gorm.DB, nullable *string, typeInt *int) *testModel { +func createTestModel(t dbassert.TestingT, db *db.DB, nullable *string, typeInt *int) *testModel { publicId, err := base62.Random(12) assert.NoError(t, err) m := testModel{ @@ -126,7 +128,7 @@ func createTestModel(t dbassert.TestingT, db *gorm.DB, nullable *string, typeInt return &m } -func initStore(t *testing.T, db *gorm.DB) { +func initStore(t *testing.T, db *db.DB) { t.Helper() const ( createDomainType = ` diff --git a/internal/db/common/db.go b/internal/db/common/db.go new file mode 100644 index 000000000..0a166f632 --- /dev/null +++ b/internal/db/common/db.go @@ -0,0 +1,15 @@ +package common + +import ( + "database/sql" + + _ "github.com/jackc/pgx/v4" +) + +func SqlOpen(driverName, dataSourceName string) (*sql.DB, error) { + switch driverName { + case "postgres", "pgx": + driverName = "pgx" + } + return sql.Open(driverName, dataSourceName) +} diff --git a/internal/db/db.go b/internal/db/db.go index 6331a08dd..2f5ff3d6d 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -2,14 +2,18 @@ package db import ( "context" + "database/sql" stderrors "errors" "fmt" + "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/go-hclog" "github.com/jackc/pgconn" + _ "github.com/jackc/pgx/v4" "gorm.io/driver/postgres" "gorm.io/gorm" + "gorm.io/gorm/logger" ) type DbType int @@ -34,9 +38,44 @@ func StringToDbType(dialect string) (DbType, error) { } } -// Open a database connection which is long-lived. -// You need to call Close() on the returned gorm.DB -func Open(dbType DbType, connectionUrl string) (*gorm.DB, error) { +// DB is a wrapper around the ORM +type DB struct { + *gorm.DB +} + +// SqlDB returns the underlying sql.DB +// +// Note: This func is not named DB(), because our choice of ORM (gorm) which is +// embedded for various reasons, already exports a DB() func and it's base type +// is also an export DB type +func (d *DB) SqlDB(ctx context.Context) (*sql.DB, error) { + const op = "db.(DB).SqlDB" + if d.DB == nil { + return nil, errors.New(ctx, errors.Internal, op, "missing underlying database") + } + return d.DB.DB() +} + +// Close the underlying sql.DB +func (d *DB) Close(ctx context.Context) error { + const op = "db.(DB).Close" + if d.DB == nil { + return errors.New(ctx, errors.Internal, op, "missing underlying database") + } + underlying, err := d.DB.DB() + if err != nil { + return errors.Wrap(ctx, err, op) + } + return underlying.Close() +} + +// Open a database connection which is long-lived. The options of +// WithGormFormatter and WithMaxOpenConnections are supported. +// +// Note: Consider if you need to call Close() on the returned DB. Typically the +// answer is no, but there are occasions when it's necessary. See the sql.DB +// docs for more information. +func Open(dbType DbType, connectionUrl string, opt ...Option) (*DB, error) { var dialect gorm.Dialector switch dbType { case Postgres: @@ -52,7 +91,28 @@ func Open(dbType DbType, connectionUrl string) (*gorm.DB, error) { if err != nil { return nil, fmt.Errorf("unable to open database: %w", err) } - return db, nil + opts := GetOpts(opt...) + if opts.withGormFormatter != nil { + newLogger := logger.New( + getGormLogger(opts.withGormFormatter), + logger.Config{ + LogLevel: logger.Error, // Log level + Colorful: false, // Disable color + }, + ) + db = db.Session(&gorm.Session{Logger: newLogger}) + } + if opts.withMaxOpenConnections > 0 { + if dialect.Name() == "postgres" && opts.withMaxOpenConnections == 1 { + return nil, fmt.Errorf("unable to create db object with dialect %s: %s", dialect, "max_open_connections must be unlimited by setting 0 or at least 2") + } + underlyingDB, err := db.DB() + if err != nil { + return nil, fmt.Errorf("unable retreive db: %w", err) + } + underlyingDB.SetMaxOpenConns(opts.withMaxOpenConnections) + } + return &DB{db}, nil } func GetGormLogFormatter(log hclog.Logger) func(values ...interface{}) (messages []interface{}) { @@ -85,6 +145,6 @@ func (g gormLogger) Printf(msg string, values ...interface{}) { } } -func GetGormLogger(log hclog.Logger) gormLogger { +func getGormLogger(log hclog.Logger) gormLogger { return gormLogger{logger: log} } diff --git a/internal/db/db_test.go b/internal/db/db_test.go index 915da9e26..d0730c790 100644 --- a/internal/db/db_test.go +++ b/internal/db/db_test.go @@ -1,6 +1,7 @@ package db import ( + "context" "testing" "github.com/hashicorp/boundary/testing/dbtest" @@ -8,6 +9,7 @@ import ( ) func TestOpen(t *testing.T) { + ctx := context.Background() cleanup, url, _, err := dbtest.StartUsingTemplate(dbtest.Postgres) if err != nil { t.Fatal(err) @@ -48,7 +50,7 @@ func TestOpen(t *testing.T) { got, err := Open(tt.args.dbType, tt.args.connectionUrl) defer func() { if err == nil { - sqlDB, err := got.DB() + sqlDB, err := got.SqlDB(ctx) require.NoError(t, err) err = sqlDB.Close() require.NoError(t, err) diff --git a/internal/db/domains_test.go b/internal/db/domains_test.go index bd99618da..bf6adc467 100644 --- a/internal/db/domains_test.go +++ b/internal/db/domains_test.go @@ -1,6 +1,7 @@ package db import ( + "context" "strings" "testing" "time" @@ -25,8 +26,10 @@ returning id; ` ) + ctx := context.Background() + conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) for _, typ := range []string{"public", "private", "role", "scope"} { @@ -107,9 +110,9 @@ insert into test_table values ($1); ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) if _, err := db.Exec(createTable); err != nil { @@ -179,9 +182,9 @@ insert into test_table values ($1); ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) if _, err := db.Exec(createTable); err != nil { @@ -256,8 +259,9 @@ returning id; ` ) + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) if _, err := db.Exec(createTable); err != nil { t.Fatalf("query: \n%s\n error: %s", createTable, err) @@ -322,9 +326,9 @@ update test_update_time_column set update_time = null; ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) _, err = db.Exec(createTable) assert.NoError(err) @@ -396,8 +400,8 @@ returning public_id; ) conn, _ := TestSetup(t, "postgres") assert, require := assert.New(t), require.New(t) - - db, err := conn.DB() + ctx := context.Background() + db, err := conn.SqlDB(ctx) require.NoError(err) _, err = db.Exec(createTable) @@ -457,8 +461,8 @@ returning private_id; ) conn, _ := TestSetup(t, "postgres") assert, require := assert.New(t), require.New(t) - - db, err := conn.DB() + ctx := context.Background() + db, err := conn.SqlDB(ctx) require.NoError(err) _, err = db.Exec(createTable) @@ -491,8 +495,9 @@ returning private_id; } func TestDomain_DefaultUsersExist(t *testing.T) { + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) for _, val := range []string{"u_anon", "u_auth"} { rows, err := db.Query(`select from iam_user where public_id = $1`, val) @@ -579,9 +584,9 @@ set id = null; Name string Updatable string } - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) _, err = db.Exec(createTable) assert.NoError(t, err) @@ -693,9 +698,9 @@ values ($1) returning id; ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) if _, err := db.Exec(createTable); err != nil { @@ -741,9 +746,9 @@ values ($1) returning id; ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) if _, err := db.Exec(createTable); err != nil { @@ -788,9 +793,9 @@ values ($1) returning id; ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) if _, err := db.Exec(createTable); err != nil { @@ -835,9 +840,9 @@ values ($1) returning id; ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) if _, err := db.Exec(createTable); err != nil { @@ -882,9 +887,9 @@ values ($1) returning id; ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) if _, err := db.Exec(createTable); err != nil { @@ -929,9 +934,9 @@ values ($1) returning id; ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) if _, err := db.Exec(createTable); err != nil { @@ -980,9 +985,9 @@ func TestDomain_wt_is_sentinel(t *testing.T) { select wt_is_sentinel($1); ` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) tests := []struct { @@ -1043,9 +1048,9 @@ insert on test_not_null_columns ` dropTriggers = `drop trigger test_insert_not_null_columns on test_not_null_columns` ) - + ctx := context.Background() conn, _ := TestSetup(t, "postgres") - db, err := conn.DB() + db, err := conn.SqlDB(ctx) assert.NoError(t, err) _, err = db.Exec(createTable) diff --git a/internal/db/option.go b/internal/db/option.go index 1547bab9a..32b50dcc4 100644 --- a/internal/db/option.go +++ b/internal/db/option.go @@ -2,6 +2,7 @@ package db import ( "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/go-hclog" wrapping "github.com/hashicorp/go-kms-wrapping" ) @@ -43,6 +44,9 @@ type Options struct { // withPrngValues is used to switch the ID generation to a pseudo-random mode withPrngValues []string + + withGormFormatter hclog.Logger + withMaxOpenConnections int } type oplogOpts struct { @@ -165,3 +169,19 @@ func WithPrngValues(withPrngValues []string) Option { o.withPrngValues = withPrngValues } } + +// WithGormFormatter specifies an optional hclog to use for gorm's log +// formmater +func WithGormFormatter(l hclog.Logger) Option { + return func(o *Options) { + o.withGormFormatter = l + } +} + +// WithMaxOpenConnections specifices and optional max open connections for the +// database +func WithMaxOpenConnections(max int) Option { + return func(o *Options) { + o.withMaxOpenConnections = max + } +} diff --git a/internal/db/option_test.go b/internal/db/option_test.go index 8a1abda47..963b9d008 100644 --- a/internal/db/option_test.go +++ b/internal/db/option_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/assert" ) @@ -175,4 +176,30 @@ func Test_getOpts(t *testing.T) { testOpts.withOrder = "version desc" assert.Equal(opts, testOpts) }) + t.Run("WithGormFormatter", func(t *testing.T) { + assert := assert.New(t) + // test default of false + opts := GetOpts() + testOpts := getDefaultOptions() + assert.Equal(opts, testOpts) + + testLogger := hclog.New(&hclog.LoggerOptions{}) + + // try setting to false + opts = GetOpts(WithGormFormatter(testLogger)) + testOpts.withGormFormatter = testLogger + assert.Equal(opts, testOpts) + }) + t.Run("WithMaxOpenConnections", func(t *testing.T) { + assert := assert.New(t) + // test default of false + opts := GetOpts() + testOpts := getDefaultOptions() + assert.Equal(opts, testOpts) + + // try setting to false + opts = GetOpts(WithMaxOpenConnections(22)) + testOpts.withMaxOpenConnections = 22 + assert.Equal(opts, testOpts) + }) } diff --git a/internal/db/read_writer.go b/internal/db/read_writer.go index f6946017b..119bb8f42 100644 --- a/internal/db/read_writer.go +++ b/internal/db/read_writer.go @@ -190,7 +190,7 @@ type VetForWriter interface { // Db uses a gorm DB connection for read/write type Db struct { - underlying *gorm.DB + underlying *DB } // ensure that Db implements the interfaces of: Reader and Writer @@ -199,7 +199,7 @@ var ( _ Writer = (*Db)(nil) ) -func New(underlying *gorm.DB) *Db { +func New(underlying *DB) *Db { return &Db{underlying: underlying} } @@ -597,7 +597,7 @@ func (rw *Db) Delete(ctx context.Context, i interface{}, opt ...Option) (int, er return NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) } } - db := rw.underlying + db := rw.underlying.DB if opts.withWhereClause != "" { db = db.Where(opts.withWhereClause, opts.withWhereClauseArgs...) } @@ -713,7 +713,7 @@ func (rw *Db) getTicketFor(aggregateName string) (*store.Ticket, error) { if rw.underlying == nil { return nil, errors.NewDeprecated(errors.InvalidParameter, op, fmt.Sprintf("%s: underlying db missing", aggregateName), errors.WithoutEvent()) } - ticketer, err := oplog.NewGormTicketer(rw.underlying, oplog.WithAggregateNames(true)) + ticketer, err := oplog.NewGormTicketer(rw.underlying.DB, oplog.WithAggregateNames(true)) if err != nil { return nil, errors.WrapDeprecated(err, op, errors.WithMsg(fmt.Sprintf("%s: unable to get Ticketer", aggregateName)), errors.WithoutEvent()) } @@ -793,7 +793,7 @@ func (rw *Db) addOplogForItems(ctx context.Context, opType OpType, opts Options, if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("oplog validation failed"), errors.WithoutEvent()) } - ticketer, err := oplog.NewGormTicketer(rw.underlying, oplog.WithAggregateNames(true)) + ticketer, err := oplog.NewGormTicketer(rw.underlying.DB, oplog.WithAggregateNames(true)) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get Ticketer"), errors.WithoutEvent()) } @@ -808,7 +808,7 @@ func (rw *Db) addOplogForItems(ctx context.Context, opType OpType, opts Options, } if err := entry.WriteEntryWith( ctx, - &oplog.GormWriter{Tx: rw.underlying}, + &oplog.GormWriter{Tx: rw.underlying.DB}, ticket, oplogMsgs..., ); err != nil { @@ -827,7 +827,7 @@ func (rw *Db) addOplog(ctx context.Context, opType OpType, opts Options, ticket if ticket == nil { return errors.New(ctx, errors.InvalidParameter, op, "missing ticket", errors.WithoutEvent()) } - ticketer, err := oplog.NewGormTicketer(rw.underlying, oplog.WithAggregateNames(true)) + ticketer, err := oplog.NewGormTicketer(rw.underlying.DB, oplog.WithAggregateNames(true)) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get Ticketer"), errors.WithoutEvent()) } @@ -846,7 +846,7 @@ func (rw *Db) addOplog(ctx context.Context, opType OpType, opts Options, ticket } err = entry.WriteEntryWith( ctx, - &oplog.GormWriter{Tx: rw.underlying}, + &oplog.GormWriter{Tx: rw.underlying.DB}, ticket, msg, ) @@ -876,7 +876,7 @@ func (rw *Db) WriteOplogEntryWith(ctx context.Context, wrapper wrapping.Wrapper, return errors.New(ctx, errors.InvalidParameter, op, "missing metadata") } - ticketer, err := oplog.NewGormTicketer(rw.underlying, oplog.WithAggregateNames(true)) + ticketer, err := oplog.NewGormTicketer(rw.underlying.DB, oplog.WithAggregateNames(true)) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get Ticketer")) } @@ -892,7 +892,7 @@ func (rw *Db) WriteOplogEntryWith(ctx context.Context, wrapper wrapping.Wrapper, } err = entry.WriteEntryWith( ctx, - &oplog.GormWriter{Tx: rw.underlying}, + &oplog.GormWriter{Tx: rw.underlying.DB}, ticket, msgs..., ) @@ -947,7 +947,7 @@ func (w *Db) DoTx(ctx context.Context, retries uint, backOff Backoff, Handler Tx newTx := w.underlying.WithContext(ctx) newTx = newTx.Begin() - rw := &Db{newTx} + rw := &Db{underlying: &DB{newTx}} if err := Handler(rw, rw); err != nil { if err := newTx.Rollback().Error; err != nil { return info, errors.Wrap(ctx, err, op, errors.WithoutEvent()) diff --git a/internal/db/read_writer_test.go b/internal/db/read_writer_test.go index 9efedb326..14404793c 100644 --- a/internal/db/read_writer_test.go +++ b/internal/db/read_writer_test.go @@ -22,7 +22,6 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" - "gorm.io/gorm" ) func TestDb_UpdateUnsetField(t *testing.T) { @@ -1270,7 +1269,7 @@ func TestDb_Delete(t *testing.T) { } tests := []struct { name string - underlying *gorm.DB + underlying *DB wrapper wrapping.Wrapper args args want int @@ -1540,7 +1539,7 @@ func TestDb_CreateItems(t *testing.T) { } tests := []struct { name string - underlying *gorm.DB + underlying *DB args args wantOplogId string wantOplogMsgs bool @@ -1741,7 +1740,7 @@ func TestDb_DeleteItems(t *testing.T) { } tests := []struct { name string - underlying *gorm.DB + underlying *DB args args wantRowsDeleted int wantOplogId string @@ -1914,7 +1913,7 @@ func TestDb_DeleteItems(t *testing.T) { } } -func testUser(t *testing.T, conn *gorm.DB, name, email, phoneNumber string) *db_test.TestUser { +func testUser(t *testing.T, conn *DB, name, email, phoneNumber string) *db_test.TestUser { t.Helper() require := require.New(t) @@ -1935,7 +1934,7 @@ func testUser(t *testing.T, conn *gorm.DB, name, email, phoneNumber string) *db_ return u } -func testCar(t *testing.T, conn *gorm.DB, name, model string, mpg int32) *db_test.TestCar { +func testCar(t *testing.T, conn *DB, name, model string, mpg int32) *db_test.TestCar { t.Helper() require := require.New(t) @@ -1964,7 +1963,7 @@ func testId(t *testing.T) string { return id } -func testScooter(t *testing.T, conn *gorm.DB, model string, mpg int32) *db_test.TestScooter { +func testScooter(t *testing.T, conn *DB, model string, mpg int32) *db_test.TestScooter { t.Helper() require := require.New(t) @@ -1995,7 +1994,7 @@ func TestDb_LookupById(t *testing.T) { } tests := []struct { name string - underlying *gorm.DB + underlying *DB args args wantErr bool want proto.Message @@ -2095,7 +2094,7 @@ func TestDb_GetTicket(t *testing.T) { type notReplayable struct{} tests := []struct { name string - underlying *gorm.DB + underlying *DB aggregateType interface{} wantErr bool wantErrIs errors.Code @@ -2181,7 +2180,7 @@ func TestDb_WriteOplogEntryWith(t *testing.T) { } tests := []struct { name string - underlying *gorm.DB + underlying *DB args args wantErr bool wantErrIs errors.Code @@ -2889,7 +2888,7 @@ func TestDb_lookupAfterWrite(t *testing.T) { } tests := []struct { name string - underlying *gorm.DB + underlying *DB args args wantErr bool want proto.Message diff --git a/internal/db/schema/manager_test.go b/internal/db/schema/manager_test.go index 84b522d56..ea58b0fb1 100644 --- a/internal/db/schema/manager_test.go +++ b/internal/db/schema/manager_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema/postgres" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/testing/dbtest" @@ -22,7 +23,7 @@ func TestNewManager(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(t, err) ctx := context.Background() @@ -47,7 +48,7 @@ func TestCurrentState(t *testing.T) { }) require.NoError(t, err) ctx := context.Background() - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(t, err) m, err := NewManager(ctx, dialect, d) @@ -83,7 +84,7 @@ func TestRollForward(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(t, err) ctx := context.Background() @@ -112,7 +113,7 @@ func TestRollForward_NotFromFresh(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(t, err) // Initialize the DB with only a portion of the current sql scripts. @@ -146,7 +147,7 @@ func TestRunMigration_canceledContext(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(t, err) ctx := context.Background() @@ -174,7 +175,7 @@ func TestRollForward_BadSQL(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(t, err) // Initialize the DB with only a portion of the current sql scripts. @@ -198,12 +199,12 @@ func TestManager_ExclusiveLock(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - d1, err := sql.Open(dialect, u) + d1, err := common.SqlOpen(dialect, u) require.NoError(t, err) m1, err := NewManager(ctx, dialect, d1) require.NoError(t, err) - d2, err := sql.Open(dialect, u) + d2, err := common.SqlOpen(dialect, u) require.NoError(t, err) m2, err := NewManager(ctx, dialect, d2) require.NoError(t, err) @@ -223,12 +224,12 @@ func TestManager_SharedLock(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - d1, err := sql.Open("postgres", u) + d1, err := common.SqlOpen("postgres", u) require.NoError(t, err) m1, err := NewManager(ctx, "postgres", d1) require.NoError(t, err) - d2, err := sql.Open("postgres", u) + d2, err := common.SqlOpen("postgres", u) require.NoError(t, err) m2, err := NewManager(ctx, "postgres", d2) require.NoError(t, err) @@ -252,7 +253,7 @@ func Test_GetMigrationLog(t *testing.T) { t.Cleanup(func() { require.NoError(t, c()) }) - d, err := sql.Open("postgres", u) + d, err := common.SqlOpen("postgres", u) require.NoError(t, err) m, err := NewManager(ctx, "postgres", d) require.NoError(t, err) diff --git a/internal/db/schema/migrations/postgres/11/01_server_type_enum_test.go b/internal/db/schema/migrations/postgres/11/01_server_type_enum_test.go index 6d8779519..4f3c675b2 100644 --- a/internal/db/schema/migrations/postgres/11/01_server_type_enum_test.go +++ b/internal/db/schema/migrations/postgres/11/01_server_type_enum_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/require" @@ -14,8 +15,8 @@ import ( const ( insertServerQuery = "insert into server (private_id, type, address) values ($1, $2, $3)" selectServerQuery = "select * from server where private_id = $1" - expectEnumConstraintErr = `pq: new row for relation "server_type_enm" violates check constraint "only_predefined_server_types_allowed"` - expectServerConstraintErr = `pq: insert or update on table "server" violates foreign key constraint "server_type_enm_fkey"` + expectEnumConstraintErr = `ERROR: new row for relation "server_type_enm" violates check constraint "only_predefined_server_types_allowed" (SQLSTATE 23514)` + expectServerConstraintErr = `ERROR: insert or update on table "server" violates foreign key constraint "server_type_enm_fkey" (SQLSTATE 23503)` ) type testServer struct { @@ -48,7 +49,7 @@ func Test_ServerEnumChanges(t *testing.T) { t.Cleanup(func() { require.NoError(c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(err) // migration to the prior migration (before the one we want to test) diff --git a/internal/db/schema/migrations/postgres/12/01_timestamp_sub_funcs_test.go b/internal/db/schema/migrations/postgres/12/01_timestamp_sub_funcs_test.go index eb9fac8d7..7519b73ab 100644 --- a/internal/db/schema/migrations/postgres/12/01_timestamp_sub_funcs_test.go +++ b/internal/db/schema/migrations/postgres/12/01_timestamp_sub_funcs_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/require" @@ -41,7 +42,7 @@ func testSetupDb(ctx context.Context, t *testing.T) *sql.DB { t.Cleanup(func() { require.NoError(c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(err) oState := schema.TestCloneMigrationStates(t) diff --git a/internal/db/schema/migrations/postgres/14/warehouse_user_dim_test.go b/internal/db/schema/migrations/postgres/14/warehouse_user_dim_test.go index 9b82f9eb8..749190bbe 100644 --- a/internal/db/schema/migrations/postgres/14/warehouse_user_dim_test.go +++ b/internal/db/schema/migrations/postgres/14/warehouse_user_dim_test.go @@ -2,12 +2,12 @@ package migration import ( "context" - "database/sql" "testing" "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" @@ -18,7 +18,6 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) func TestMigrations_UserDimension(t *testing.T) { @@ -37,7 +36,7 @@ func TestMigrations_UserDimension(t *testing.T) { t.Cleanup(func() { require.NoError(c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(err) // migration to the prior migration (before the one we want to test) @@ -139,7 +138,7 @@ func TestMigrations_UserDimension(t *testing.T) { assert.False(state.Dirty) } -func testOidcAuthToken(t *testing.T, conn *gorm.DB, kms *kms.Kms, wrapper wrapping.Wrapper, scopeId string) *authtoken.AuthToken { +func testOidcAuthToken(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper, scopeId string) *authtoken.AuthToken { t.Helper() authMethod := oidc.TestAuthMethod( diff --git a/internal/db/schema/migrations/postgres/16/warehouse_credential_dim_test.go b/internal/db/schema/migrations/postgres/16/warehouse_credential_dim_test.go index cb51f4856..2f82503f0 100644 --- a/internal/db/schema/migrations/postgres/16/warehouse_credential_dim_test.go +++ b/internal/db/schema/migrations/postgres/16/warehouse_credential_dim_test.go @@ -20,7 +20,6 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) func TestMigrations_CredentialDimension(t *testing.T) { @@ -144,7 +143,7 @@ func TestMigrations_CredentialDimension(t *testing.T) { assert.False(state.Dirty) } -func testOidcAuthToken(t *testing.T, conn *gorm.DB, kms *kms.Kms, wrapper wrapping.Wrapper, scopeId string) *authtoken.AuthToken { +func testOidcAuthToken(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper, scopeId string) *authtoken.AuthToken { t.Helper() authMethod := oidc.TestAuthMethod( @@ -171,7 +170,7 @@ func testOidcAuthToken(t *testing.T, conn *gorm.DB, kms *kms.Kms, wrapper wrappi return at } -func testSessionCredentialParams(t *testing.T, conn *gorm.DB, kms *kms.Kms, wrapper wrapping.Wrapper, tar *target.TcpTarget) []*session.DynamicCredential { +func testSessionCredentialParams(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper, tar *target.TcpTarget) []*session.DynamicCredential { t.Helper() rw := db.New(conn) diff --git a/internal/db/schema/migrations/postgres/2/07_iam_test.go b/internal/db/schema/migrations/postgres/2/07_iam_test.go index c52be8e2a..04e76927b 100644 --- a/internal/db/schema/migrations/postgres/2/07_iam_test.go +++ b/internal/db/schema/migrations/postgres/2/07_iam_test.go @@ -2,11 +2,11 @@ package migration import ( "context" - "database/sql" "testing" "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/testing/dbtest" @@ -38,7 +38,7 @@ func Test_PrimaryAuthMethodChanges(t *testing.T) { t.Cleanup(func() { require.NoError(c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(err) // migration to the prior migration (before the one we want to test) diff --git a/internal/db/schema/migrations/postgres/8/08_connection_test.go b/internal/db/schema/migrations/postgres/8/08_connection_test.go index 0556c8de5..9de3eb8d3 100644 --- a/internal/db/schema/migrations/postgres/8/08_connection_test.go +++ b/internal/db/schema/migrations/postgres/8/08_connection_test.go @@ -1,6 +1,7 @@ package migration import ( + "context" "database/sql" "testing" @@ -37,9 +38,9 @@ func TestMigration(t *testing.T) { ` selectQuery = `select session_id, server_id from session_connection_testing order by session_id` ) - + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres", db.WithTemplate("template1")) - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(err) _, err = db.Exec(createTables) require.NoError(err) diff --git a/internal/db/schema/postgres/postgres.go b/internal/db/schema/postgres/postgres.go index 1c30af45e..8a038abca 100644 --- a/internal/db/schema/postgres/postgres.go +++ b/internal/db/schema/postgres/postgres.go @@ -34,12 +34,11 @@ import ( "fmt" "io" "io/ioutil" - "strconv" "strings" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/go-multierror" - "github.com/lib/pq" + "github.com/jackc/pgconn" ) // schemaAccessLockId is a Lock key used to ensure a single boundary binary is operating @@ -232,14 +231,12 @@ func (p *Postgres) Run(ctx context.Context, migration io.Reader, version int) er if rollbackErr := rollback(); rollbackErr != nil { err = multierror.Append(err, rollbackErr) } - if pgErr, ok := err.(*pq.Error); ok { + if pgErr, ok := err.(*pgconn.PgError); ok { var line uint var col uint var lineColOK bool - if pgErr.Position != "" { - if pos, err := strconv.ParseUint(pgErr.Position, 10, 64); err == nil { - line, col, lineColOK = computeLineFromPos(query, int(pos)) - } + if pgErr.Position != 0 { + line, col, lineColOK = computeLineFromPos(query, int(pgErr.Position)) } message := "migration failed" if lineColOK { @@ -308,8 +305,7 @@ func (p *Postgres) setVersion(ctx context.Context, version int, dirty bool) erro defer func() { p.tx = nil }() return tx.Rollback() } - - query := `truncate ` + pq.QuoteIdentifier(defaultMigrationsTable) + query := `truncate ` + quoteIdentifier(defaultMigrationsTable) if _, err := tx.ExecContext(ctx, query); err != nil { if errRollback := rollback(); errRollback != nil { err = multierror.Append(err, errRollback) @@ -321,7 +317,7 @@ func (p *Postgres) setVersion(ctx context.Context, version int, dirty bool) erro // empty schema Version for failed down migration on the first migration // See: https://github.com/golang-migrate/migrate/issues/330 if version >= 0 || (version == nilVersion && dirty) { - query = `insert into ` + pq.QuoteIdentifier(defaultMigrationsTable) + + query = `insert into ` + quoteIdentifier(defaultMigrationsTable) + ` (version, dirty) values ($1, $2)` if _, err := tx.ExecContext(ctx, query, version, dirty); err != nil { if errRollback := rollback(); errRollback != nil { @@ -368,7 +364,7 @@ func (p *Postgres) CurrentState(ctx context.Context) (version int, previouslyRan return nilVersion, previouslyRan, dirty, errors.New(ctx, errors.MigrationIntegrity, op, "both old and new migration tables exist") } - query := `select version, dirty from ` + pq.QuoteIdentifier(tableName) + query := `select version, dirty from ` + quoteIdentifier(tableName) results, err := p.conn.QueryContext(ctx, query) if err != nil { return nilVersion, previouslyRan, dirty, errors.Wrap(ctx, err, op) @@ -420,7 +416,7 @@ func (p *Postgres) drop(ctx context.Context) (err error) { if len(tableNames) > 0 { // delete one by one ... for _, t := range tableNames { - query = `drop table if exists ` + pq.QuoteIdentifier(t) + ` cascade` + query = `drop table if exists ` + quoteIdentifier(t) + ` cascade` if _, err := p.conn.ExecContext(ctx, query); err != nil { return errors.Wrap(ctx, err, op) } @@ -464,7 +460,7 @@ func (p *Postgres) EnsureVersionTable(ctx context.Context) (err error) { return errors.Wrap(ctx, err, op) } - createStmt := `create table if not exists ` + pq.QuoteIdentifier(defaultMigrationsTable) + ` (version bigint primary key, dirty boolean not null)` + createStmt := `create table if not exists ` + quoteIdentifier(defaultMigrationsTable) + ` (version bigint primary key, dirty boolean not null)` if _, err = extr.ExecContext(ctx, createStmt); err != nil { if wpErr := rollback(); wpErr != nil { err = multierror.Append(err, wpErr) @@ -474,3 +470,8 @@ func (p *Postgres) EnsureVersionTable(ctx context.Context) (err error) { return nil } + +func quoteIdentifier(input string) (output string) { + output = `"` + strings.Replace(input, `"`, `""`, -1) + `"` + return +} diff --git a/internal/db/schema/postgres/postgres_test.go b/internal/db/schema/postgres/postgres_test.go index 2b16c7317..185e9f3f9 100644 --- a/internal/db/schema/postgres/postgres_test.go +++ b/internal/db/schema/postgres/postgres_test.go @@ -28,7 +28,7 @@ package postgres -// error codes https://github.com/lib/pq/blob/master/error.go +// errors: https://pkg.go.dev/github.com/jackc/pgconn#PgError import ( "bytes" diff --git a/internal/db/schema/postgres/testing.go b/internal/db/schema/postgres/testing.go index 42d978d24..854495a36 100644 --- a/internal/db/schema/postgres/testing.go +++ b/internal/db/schema/postgres/testing.go @@ -31,11 +31,11 @@ package postgres import ( "bytes" "context" - "database/sql" "testing" "time" "github.com/golang-migrate/migrate/v4/database" + "github.com/hashicorp/boundary/internal/db/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -123,7 +123,7 @@ func testSetVersion(t *testing.T, d *Postgres) { func open(t *testing.T, ctx context.Context, u string) (*Postgres, error) { t.Helper() - db, err := sql.Open("postgres", u) + db, err := common.SqlOpen("postgres", u) require.NoError(t, err) px, err := New(ctx, db) diff --git a/internal/db/schema/schema.go b/internal/db/schema/schema.go index f300c7308..c33bd505d 100644 --- a/internal/db/schema/schema.go +++ b/internal/db/schema/schema.go @@ -2,8 +2,8 @@ package schema import ( "context" - "database/sql" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/errors" ) @@ -13,7 +13,7 @@ import ( func MigrateStore(ctx context.Context, dialect string, url string, opt ...Option) (bool, error) { const op = "schema.MigrateStore" - d, err := sql.Open(dialect, url) + d, err := common.SqlOpen(dialect, url) if err != nil { return false, errors.Wrap(ctx, err, op) } diff --git a/internal/db/schema/schema_test.go b/internal/db/schema/schema_test.go index 634e881ef..ad2d354c9 100644 --- a/internal/db/schema/schema_test.go +++ b/internal/db/schema/schema_test.go @@ -2,9 +2,9 @@ package schema import ( "context" - "database/sql" "testing" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -53,7 +53,7 @@ func Test_MigrateStore_WithMigrationStates(t *testing.T) { t.Cleanup(func() { require.NoError(c()) }) - d, err := sql.Open(dialect, u) + d, err := common.SqlOpen(dialect, u) require.NoError(err) // migration to the prior migration (before the one we want to test) diff --git a/internal/db/testing.go b/internal/db/testing.go index f95b3694c..c8b76a293 100644 --- a/internal/db/testing.go +++ b/internal/db/testing.go @@ -17,12 +17,11 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/wrappers/aead" "github.com/stretchr/testify/assert" - "gorm.io/gorm" "gorm.io/gorm/logger" ) // setup the tests (initialize the database one-time and intialized testDatabaseURL). Do not close the returned db. -func TestSetup(t *testing.T, dialect string, opt ...TestOption) (*gorm.DB, string) { +func TestSetup(t *testing.T, dialect string, opt ...TestOption) (*DB, string) { var cleanup func() error var url string var err error @@ -57,7 +56,7 @@ func TestSetup(t *testing.T, dialect string, opt ...TestOption) (*gorm.DB, strin } db.Logger.LogMode(logger.Error) t.Cleanup(func() { - sqlDB, err := db.DB() + sqlDB, err := db.SqlDB(ctx) assert.NoError(t, err) assert.NoError(t, sqlDB.Close(), "Got error closing gorm db.") }) diff --git a/internal/errors/is_test.go b/internal/errors/is_test.go index f142b7210..beb98c6c8 100644 --- a/internal/errors/is_test.go +++ b/internal/errors/is_test.go @@ -44,7 +44,7 @@ func TestError_IsUnique(t *testing.T) { want: true, }, { - name: "wrapped-pq-is-unique", + name: "wrapped-pg-is-unique", in: errors.E( context.TODO(), errors.WithWrap(&pgconn.PgError{ @@ -107,7 +107,7 @@ func TestError_IsCheckConstraint(t *testing.T) { want: true, }, { - name: "wrapped-pq-is-check-constraint", + name: "wrapped-pg-is-check-constraint", in: errors.E(context.TODO(), errors.WithCode(errors.CheckConstraint), errors.WithWrap(&pgconn.PgError{ Code: "23514", @@ -176,7 +176,7 @@ func TestError_IsNotNullError(t *testing.T) { want: true, }, { - name: "wrapped-pq-is-not-null", + name: "wrapped-pg-is-not-null", in: errors.E(context.TODO(), errors.WithCode(errors.NotNull), errors.WithWrap(&pgconn.PgError{ Code: "23502", diff --git a/internal/host/static/host_catalog_test.go b/internal/host/static/host_catalog_test.go index 292020d2d..0dba8443e 100644 --- a/internal/host/static/host_catalog_test.go +++ b/internal/host/static/host_catalog_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gorm.io/gorm" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/host/static/store" @@ -109,7 +108,7 @@ func TestHostCatalog_New(t *testing.T) { } } -func testCatalog(t *testing.T, conn *gorm.DB, scopeId string) *HostCatalog { +func testCatalog(t *testing.T, conn *db.DB, scopeId string) *HostCatalog { t.Helper() cats := TestCatalogs(t, conn, scopeId, 1) return cats[0] diff --git a/internal/host/static/repository_host_catalog_test.go b/internal/host/static/repository_host_catalog_test.go index 4af721d32..3a733193f 100644 --- a/internal/host/static/repository_host_catalog_test.go +++ b/internal/host/static/repository_host_catalog_test.go @@ -179,6 +179,7 @@ func TestRepository_UpdateCatalog(t *testing.T) { conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) + ctx := context.Background() changeName := func(s string) func(*HostCatalog) *HostCatalog { return func(c *HostCatalog) *HostCatalog { @@ -461,7 +462,7 @@ func TestRepository_UpdateCatalog(t *testing.T) { assert.Equal(tt.wantCount, gotCount, "row count") assert.NotSame(tt.orig, got) assert.Equal(tt.orig.ScopeId, got.ScopeId) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(t, err) dbassert := dbassert.New(t, underlyingDB) if tt.want.Name == "" { diff --git a/internal/host/static/repository_host_set_test.go b/internal/host/static/repository_host_set_test.go index 3e118cbf9..05f3eb43a 100644 --- a/internal/host/static/repository_host_set_test.go +++ b/internal/host/static/repository_host_set_test.go @@ -202,6 +202,7 @@ func TestRepository_CreateSet(t *testing.T) { } func TestRepository_UpdateSet(t *testing.T) { + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -493,7 +494,7 @@ func TestRepository_UpdateSet(t *testing.T) { assert.Equal(tt.wantCount, gotCount, "row count") assert.NotSame(tt.orig, got) assert.Equal(tt.orig.CatalogId, got.CatalogId) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.want.Name == "" { diff --git a/internal/host/static/repository_host_test.go b/internal/host/static/repository_host_test.go index 0f699375c..aebf419b7 100644 --- a/internal/host/static/repository_host_test.go +++ b/internal/host/static/repository_host_test.go @@ -251,6 +251,7 @@ func TestRepository_CreateHost(t *testing.T) { } func TestRepository_UpdateHost(t *testing.T) { + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -620,7 +621,7 @@ func TestRepository_UpdateHost(t *testing.T) { assert.Equal(tt.wantCount, gotCount, "row count") assert.NotSame(tt.orig, got) assert.Equal(tt.orig.CatalogId, got.CatalogId) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.want.Name == "" { diff --git a/internal/host/static/testing.go b/internal/host/static/testing.go index 3d3a94d3a..8dbca5444 100644 --- a/internal/host/static/testing.go +++ b/internal/host/static/testing.go @@ -7,13 +7,12 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/stretchr/testify/assert" - "gorm.io/gorm" ) // TestCatalogs creates count number of static host catalogs to the provided DB // with the provided scope id. If any errors are encountered during the creation of // the host catalog, the test will fail. -func TestCatalogs(t *testing.T, conn *gorm.DB, scopeId string, count int) []*HostCatalog { +func TestCatalogs(t *testing.T, conn *db.DB, scopeId string, count int) []*HostCatalog { t.Helper() assert := assert.New(t) var cats []*HostCatalog @@ -37,7 +36,7 @@ func TestCatalogs(t *testing.T, conn *gorm.DB, scopeId string, count int) []*Hos // TestHosts creates count number of static hosts to the provided DB // with the provided catalog id. The catalog must have been created previously. // If any errors are encountered during the creation of the host, the test will fail. -func TestHosts(t *testing.T, conn *gorm.DB, catalogId string, count int) []*Host { +func TestHosts(t *testing.T, conn *db.DB, catalogId string, count int) []*Host { t.Helper() assert := assert.New(t) var hosts []*Host @@ -63,7 +62,7 @@ func TestHosts(t *testing.T, conn *gorm.DB, catalogId string, count int) []*Host // TestSets creates count number of static host sets in the provided DB // with the provided catalog id. The catalog must have been created // previously. The test will fail if any errors are encountered. -func TestSets(t *testing.T, conn *gorm.DB, catalogId string, count int) []*HostSet { +func TestSets(t *testing.T, conn *db.DB, catalogId string, count int) []*HostSet { t.Helper() assert := assert.New(t) var sets []*HostSet @@ -88,7 +87,7 @@ func TestSets(t *testing.T, conn *gorm.DB, catalogId string, count int) []*HostS // TestSetMembers adds hosts to the specified setId in the provided DB. // The set and hosts must have been created previously and belong to the // same catalog. The test will fail if any errors are encountered. -func TestSetMembers(t *testing.T, conn *gorm.DB, setId string, hosts []*Host) []*HostSetMember { +func TestSetMembers(t *testing.T, conn *db.DB, setId string, hosts []*Host) []*HostSetMember { t.Helper() assert := assert.New(t) diff --git a/internal/iam/account_test.go b/internal/iam/account_test.go index c6ac60eac..76cee0902 100644 --- a/internal/iam/account_test.go +++ b/internal/iam/account_test.go @@ -65,6 +65,7 @@ func TestAccount_GetScope(t *testing.T) { func TestAccount_Clone(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) repo := TestRepo(t, conn, wrapper) @@ -83,7 +84,7 @@ func TestAccount_Clone(t *testing.T) { authMethodPublicId := testAuthMethod(t, conn, org.PublicId) acct := testAccount(t, conn, org.PublicId, authMethodPublicId, u.PublicId) acct2 := testAccount(t, conn, org.PublicId, authMethodPublicId, "") - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(t, err) dbassert := dbassert.New(t, underlyingDB) dbassert.IsNull(acct2, "IamUserId") diff --git a/internal/iam/group_test.go b/internal/iam/group_test.go index be6720d95..d08e9b4c7 100644 --- a/internal/iam/group_test.go +++ b/internal/iam/group_test.go @@ -206,6 +206,7 @@ func Test_GroupCreate(t *testing.T) { func Test_GroupUpdate(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) repo := TestRepo(t, conn, wrapper) @@ -351,7 +352,7 @@ func Test_GroupUpdate(t *testing.T) { require.NoError(err) assert.True(proto.Equal(updateGrp, foundGrp)) if len(tt.args.nullPaths) != 0 { - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) for _, f := range tt.args.nullPaths { diff --git a/internal/iam/immutable_fields_test.go b/internal/iam/immutable_fields_test.go index cecfb4e38..7bffd66a6 100644 --- a/internal/iam/immutable_fields_test.go +++ b/internal/iam/immutable_fields_test.go @@ -92,10 +92,11 @@ func TestConcreteScope_ImmutableFields(t *testing.T) { update = `update {{rep}} set scope_id = $1 where scope_id = $2;` ) t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) repo := TestRepo(t, conn, wrapper) - db, err := conn.DB() + db, err := conn.SqlDB(ctx) require.NoError(t, err) org, proj := TestScopes(t, repo) diff --git a/internal/iam/repository_group_test.go b/internal/iam/repository_group_test.go index 4c5d1bb44..5ea3b4b53 100644 --- a/internal/iam/repository_group_test.go +++ b/internal/iam/repository_group_test.go @@ -173,6 +173,7 @@ func TestRepository_CreateGroup(t *testing.T) { func TestRepository_UpdateGroup(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") a := assert.New(t) rw := db.New(conn) @@ -450,7 +451,7 @@ func TestRepository_UpdateGroup(t *testing.T) { foundGrp, _, err := repo.LookupGroup(context.Background(), u.PublicId) require.NoError(err) assert.True(proto.Equal(groupAfterUpdate, foundGrp)) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.args.name == "" { diff --git a/internal/iam/repository_role_test.go b/internal/iam/repository_role_test.go index da1ebec39..f1551334c 100644 --- a/internal/iam/repository_role_test.go +++ b/internal/iam/repository_role_test.go @@ -174,6 +174,7 @@ func TestRepository_CreateRole(t *testing.T) { func TestRepository_UpdateRole(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -447,7 +448,7 @@ func TestRepository_UpdateRole(t *testing.T) { foundRole, _, _, err := repo.LookupRole(context.Background(), r.PublicId) assert.NoError(err) assert.True(proto.Equal(roleAfterUpdate, foundRole)) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.args.name == "" { diff --git a/internal/iam/repository_user_iam_pkg_test.go b/internal/iam/repository_user_iam_pkg_test.go index f537db9af..690441d2d 100644 --- a/internal/iam/repository_user_iam_pkg_test.go +++ b/internal/iam/repository_user_iam_pkg_test.go @@ -184,6 +184,7 @@ func TestRepository_associateUserWithAccounts(t *testing.T) { func TestRepository_dissociateUserWithAccount(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -309,7 +310,7 @@ func TestRepository_dissociateUserWithAccount(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) err = dissociateUserFromAccounts(context.Background(), kms, rw, rw, tt.args.Ids.user, tt.args.Ids.accts, tt.args.opt...) diff --git a/internal/iam/repository_user_test.go b/internal/iam/repository_user_test.go index da777bfc5..88d8c5240 100644 --- a/internal/iam/repository_user_test.go +++ b/internal/iam/repository_user_test.go @@ -198,6 +198,7 @@ func TestRepository_LookupUser_WithDifferentPrimaryAuthMethods(t *testing.T) { func TestRepository_UpdateUser(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -475,7 +476,7 @@ func TestRepository_UpdateUser(t *testing.T) { assert.True(proto.Equal(userAfterUpdate, foundUser)) sort.Strings(foundAccountIds) assert.Equal(accountIds, foundAccountIds) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.args.name == "" { @@ -710,6 +711,7 @@ func TestRepository_ListUsers_Multiple_Scopes(t *testing.T) { func TestRepository_LookupUserWithLogin(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -847,7 +849,7 @@ func TestRepository_LookupUserWithLogin(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) got, err := repo.LookupUserWithLogin(context.Background(), tt.args.withAccountId, tt.args.opt...) diff --git a/internal/iam/role_test.go b/internal/iam/role_test.go index 9af79e7c6..749f9da18 100644 --- a/internal/iam/role_test.go +++ b/internal/iam/role_test.go @@ -206,6 +206,7 @@ func Test_RoleCreate(t *testing.T) { func Test_RoleUpdate(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) repo := TestRepo(t, conn, wrapper) @@ -440,7 +441,7 @@ func Test_RoleUpdate(t *testing.T) { } assert.True(proto.Equal(updateRole, foundRole)) if len(tt.args.nullPaths) != 0 { - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) for _, f := range tt.args.nullPaths { diff --git a/internal/iam/testing.go b/internal/iam/testing.go index a0f7197be..3e693a9a5 100644 --- a/internal/iam/testing.go +++ b/internal/iam/testing.go @@ -13,12 +13,11 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) // TestRepo creates a repo that can be used for various purposes. Crucially, it // ensures that the global scope contains a valid root key. -func TestRepo(t *testing.T, conn *gorm.DB, rootWrapper wrapping.Wrapper, opt ...Option) *Repository { +func TestRepo(t *testing.T, conn *db.DB, rootWrapper wrapping.Wrapper, opt ...Option) *Repository { require := require.New(t) rw := db.New(conn) kmsCache := kms.TestKms(t, conn, rootWrapper) @@ -156,7 +155,7 @@ func TestUser(t *testing.T, repo *Repository, scopeId string, opt ...Option) *Us } // TestRole creates a role suitable for testing. -func TestRole(t *testing.T, conn *gorm.DB, scopeId string, opt ...Option) *Role { +func TestRole(t *testing.T, conn *db.DB, scopeId string, opt ...Option) *Role { t.Helper() require := require.New(t) rw := db.New(conn) @@ -176,7 +175,7 @@ func TestRole(t *testing.T, conn *gorm.DB, scopeId string, opt ...Option) *Role return role } -func TestRoleGrant(t *testing.T, conn *gorm.DB, roleId, grant string, opt ...Option) *RoleGrant { +func TestRoleGrant(t *testing.T, conn *db.DB, roleId, grant string, opt ...Option) *RoleGrant { t.Helper() require := require.New(t) rw := db.New(conn) @@ -189,7 +188,7 @@ func TestRoleGrant(t *testing.T, conn *gorm.DB, roleId, grant string, opt ...Opt } // TestGroup creates a group suitable for testing. -func TestGroup(t *testing.T, conn *gorm.DB, scopeId string, opt ...Option) *Group { +func TestGroup(t *testing.T, conn *db.DB, scopeId string, opt ...Option) *Group { t.Helper() require := require.New(t) rw := db.New(conn) @@ -205,7 +204,7 @@ func TestGroup(t *testing.T, conn *gorm.DB, scopeId string, opt ...Option) *Grou return grp } -func TestGroupMember(t *testing.T, conn *gorm.DB, groupId, userId string, opt ...Option) *GroupMemberUser { +func TestGroupMember(t *testing.T, conn *db.DB, groupId, userId string, opt ...Option) *GroupMemberUser { t.Helper() require := require.New(t) rw := db.New(conn) @@ -218,7 +217,7 @@ func TestGroupMember(t *testing.T, conn *gorm.DB, groupId, userId string, opt .. return gm } -func TestUserRole(t *testing.T, conn *gorm.DB, roleId, userId string, opt ...Option) *UserRole { +func TestUserRole(t *testing.T, conn *db.DB, roleId, userId string, opt ...Option) *UserRole { t.Helper() require := require.New(t) rw := db.New(conn) @@ -230,7 +229,7 @@ func TestUserRole(t *testing.T, conn *gorm.DB, roleId, userId string, opt ...Opt return r } -func TestGroupRole(t *testing.T, conn *gorm.DB, roleId, grpId string, opt ...Option) *GroupRole { +func TestGroupRole(t *testing.T, conn *db.DB, roleId, grpId string, opt ...Option) *GroupRole { t.Helper() require := require.New(t) rw := db.New(conn) @@ -242,7 +241,7 @@ func TestGroupRole(t *testing.T, conn *gorm.DB, roleId, grpId string, opt ...Opt return r } -func TestManagedGroupRole(t *testing.T, conn *gorm.DB, roleId, managedGrpId string, opt ...Option) *ManagedGroupRole { +func TestManagedGroupRole(t *testing.T, conn *db.DB, roleId, managedGrpId string, opt ...Option) *ManagedGroupRole { t.Helper() require := require.New(t) rw := db.New(conn) @@ -257,11 +256,13 @@ func TestManagedGroupRole(t *testing.T, conn *gorm.DB, roleId, managedGrpId stri // testAccount is a temporary test function. TODO - replace with an auth // subsystem testAccount function. If userId is zero value, then an auth // account will be created with a null IamUserId -func testAccount(t *testing.T, conn *gorm.DB, scopeId, authMethodId, userId string) *authAccount { +func testAccount(t *testing.T, conn *db.DB, scopeId, authMethodId, userId string) *authAccount { const ( accountPrefix = "aa_" ) t.Helper() + ctx := context.Background() + rw := db.New(conn) require := require.New(t) require.NotNil(conn) @@ -277,7 +278,7 @@ func testAccount(t *testing.T, conn *gorm.DB, scopeId, authMethodId, userId stri } var count int - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) err = underlyingDB.QueryRow(whereValidAuthMethod, authMethodId, scopeId).Scan(&count) require.NoError(err) @@ -299,7 +300,7 @@ func testAccount(t *testing.T, conn *gorm.DB, scopeId, authMethodId, userId stri require.NotEmpty(acct.PublicId) if userId == "" { - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) dbassert.IsNull(acct, "IamUserId") @@ -309,7 +310,7 @@ func testAccount(t *testing.T, conn *gorm.DB, scopeId, authMethodId, userId stri // testAuthMethod is a temporary test function. TODO - replace with an auth // subsystem testAuthMethod function. -func testAuthMethod(t *testing.T, conn *gorm.DB, scopeId string) string { +func testAuthMethod(t *testing.T, conn *db.DB, scopeId string) string { const ( authMethodPrefix = "am_" ) diff --git a/internal/kms/testing.go b/internal/kms/testing.go index e05759772..37ec6da7e 100644 --- a/internal/kms/testing.go +++ b/internal/kms/testing.go @@ -8,10 +8,9 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/wrappers/aead" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) -func TestRootKey(t *testing.T, conn *gorm.DB, scopeId string) *RootKey { +func TestRootKey(t *testing.T, conn *db.DB, scopeId string) *RootKey { t.Helper() require := require.New(t) rw := db.New(conn) @@ -26,7 +25,7 @@ func TestRootKey(t *testing.T, conn *gorm.DB, scopeId string) *RootKey { return k } -func TestRootKeyVersion(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, rootId string) (kv *RootKeyVersion, kvWrapper wrapping.Wrapper) { +func TestRootKeyVersion(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, rootId string) (kv *RootKeyVersion, kvWrapper wrapping.Wrapper) { t.Helper() require := require.New(t) rw := db.New(conn) @@ -48,7 +47,7 @@ func TestRootKeyVersion(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, r return k, rootKeyVersionWrapper } -func TestKms(t *testing.T, conn *gorm.DB, rootWrapper wrapping.Wrapper) *Kms { +func TestKms(t *testing.T, conn *db.DB, rootWrapper wrapping.Wrapper) *Kms { t.Helper() require := require.New(t) rw := db.New(conn) @@ -61,7 +60,7 @@ func TestKms(t *testing.T, conn *gorm.DB, rootWrapper wrapping.Wrapper) *Kms { return kms } -func TestDatabaseKey(t *testing.T, conn *gorm.DB, rootKeyId string) *DatabaseKey { +func TestDatabaseKey(t *testing.T, conn *db.DB, rootKeyId string) *DatabaseKey { t.Helper() require := require.New(t) rw := db.New(conn) @@ -77,7 +76,7 @@ func TestDatabaseKey(t *testing.T, conn *gorm.DB, rootKeyId string) *DatabaseKey return k } -func TestDatabaseKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrapping.Wrapper, databaseKeyId string, key []byte) *DatabaseKeyVersion { +func TestDatabaseKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, databaseKeyId string, key []byte) *DatabaseKeyVersion { t.Helper() require := require.New(t) rw := db.New(conn) @@ -95,7 +94,7 @@ func TestDatabaseKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper w return k } -func TestOplogKey(t *testing.T, conn *gorm.DB, rootKeyId string) *OplogKey { +func TestOplogKey(t *testing.T, conn *db.DB, rootKeyId string) *OplogKey { t.Helper() require := require.New(t) rw := db.New(conn) @@ -111,7 +110,7 @@ func TestOplogKey(t *testing.T, conn *gorm.DB, rootKeyId string) *OplogKey { return k } -func TestOplogKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrapping.Wrapper, oplogKeyId string, key []byte) *OplogKeyVersion { +func TestOplogKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, oplogKeyId string, key []byte) *OplogKeyVersion { t.Helper() require := require.New(t) rw := db.New(conn) @@ -129,7 +128,7 @@ func TestOplogKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrap return k } -func TestTokenKey(t *testing.T, conn *gorm.DB, rootKeyId string) *TokenKey { +func TestTokenKey(t *testing.T, conn *db.DB, rootKeyId string) *TokenKey { t.Helper() require := require.New(t) rw := db.New(conn) @@ -145,7 +144,7 @@ func TestTokenKey(t *testing.T, conn *gorm.DB, rootKeyId string) *TokenKey { return k } -func TestTokenKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrapping.Wrapper, tokenKeyId string, key []byte) *TokenKeyVersion { +func TestTokenKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, tokenKeyId string, key []byte) *TokenKeyVersion { t.Helper() require := require.New(t) rw := db.New(conn) @@ -163,7 +162,7 @@ func TestTokenKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrap return k } -func TestSessionKey(t *testing.T, conn *gorm.DB, rootKeyId string) *SessionKey { +func TestSessionKey(t *testing.T, conn *db.DB, rootKeyId string) *SessionKey { t.Helper() require := require.New(t) rw := db.New(conn) @@ -179,7 +178,7 @@ func TestSessionKey(t *testing.T, conn *gorm.DB, rootKeyId string) *SessionKey { return k } -func TestSessionKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrapping.Wrapper, sessionKeyId string, key []byte) *SessionKeyVersion { +func TestSessionKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, sessionKeyId string, key []byte) *SessionKeyVersion { t.Helper() require := require.New(t) rw := db.New(conn) @@ -197,7 +196,7 @@ func TestSessionKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wr return k } -func TestOidcKey(t *testing.T, conn *gorm.DB, rootKeyId string) *OidcKey { +func TestOidcKey(t *testing.T, conn *db.DB, rootKeyId string) *OidcKey { t.Helper() require := require.New(t) rw := db.New(conn) @@ -213,7 +212,7 @@ func TestOidcKey(t *testing.T, conn *gorm.DB, rootKeyId string) *OidcKey { return k } -func TestOidcKeyVersion(t *testing.T, conn *gorm.DB, rootKeyVersionWrapper wrapping.Wrapper, oidcKeyId string, key []byte) *OidcKeyVersion { +func TestOidcKeyVersion(t *testing.T, conn *db.DB, rootKeyVersionWrapper wrapping.Wrapper, oidcKeyId string, key []byte) *OidcKeyVersion { t.Helper() require := require.New(t) rw := db.New(conn) diff --git a/internal/oplog/testing.go b/internal/oplog/testing.go index f8d7bdc17..dac8f502c 100644 --- a/internal/oplog/testing.go +++ b/internal/oplog/testing.go @@ -3,9 +3,9 @@ package oplog import ( "context" "crypto/rand" - "database/sql" "testing" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/internal/oplog/oplog_test" "github.com/hashicorp/boundary/testing/dbtest" @@ -86,7 +86,7 @@ func testInitStore(t *testing.T, cleanup func() error, url string) { ctx := context.Background() dialect := "postgres" - d, err := sql.Open(dialect, url) + d, err := common.SqlOpen(dialect, url) require.NoError(t, err) sm, err := schema.NewManager(ctx, dialect, d) require.NoError(t, err) diff --git a/internal/oplog/testing_test.go b/internal/oplog/testing_test.go index b5d1030a5..93436e7c1 100644 --- a/internal/oplog/testing_test.go +++ b/internal/oplog/testing_test.go @@ -1,9 +1,9 @@ package oplog import ( - "database/sql" "testing" + "github.com/hashicorp/boundary/internal/db/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -77,7 +77,7 @@ func Test_testInitStore(t *testing.T) { const query = ` select count(*) from information_schema."tables" t where table_name = 'db_test_user'; ` - db, err := sql.Open("postgres", url) + db, err := common.SqlOpen("postgres", url) require.NoError(err) var cnt int diff --git a/internal/scheduler/job/testing.go b/internal/scheduler/job/testing.go index 69552c4b3..bd192c93e 100644 --- a/internal/scheduler/job/testing.go +++ b/internal/scheduler/job/testing.go @@ -12,10 +12,9 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) -func testJob(t *testing.T, conn *gorm.DB, name, description string, wrapper wrapping.Wrapper, opt ...Option) *Job { +func testJob(t *testing.T, conn *db.DB, name, description string, wrapper wrapping.Wrapper, opt ...Option) *Job { t.Helper() require := require.New(t) rw := db.New(conn) @@ -31,7 +30,7 @@ func testJob(t *testing.T, conn *gorm.DB, name, description string, wrapper wrap return job } -func testRun(conn *gorm.DB, pluginId, name, cId string) (*Run, error) { +func testRun(conn *db.DB, pluginId, name, cId string) (*Run, error) { query := ` insert into job_run ( job_plugin_id, job_name, server_id @@ -59,7 +58,7 @@ func testRun(conn *gorm.DB, pluginId, name, cId string) (*Run, error) { return run, nil } -func testRunWithUpdateTime(conn *gorm.DB, pluginId, name, cId string, updateTime time.Time) (*Run, error) { +func testRunWithUpdateTime(conn *db.DB, pluginId, name, cId string, updateTime time.Time) (*Run, error) { query := ` insert into job_run ( job_plugin_id, job_name, server_id, update_time @@ -87,7 +86,7 @@ func testRunWithUpdateTime(conn *gorm.DB, pluginId, name, cId string, updateTime return run, nil } -func testController(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper) *servers.Server { +func testController(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper) *servers.Server { t.Helper() rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) diff --git a/internal/scheduler/testing.go b/internal/scheduler/testing.go index d28e556ba..71646d7d0 100644 --- a/internal/scheduler/testing.go +++ b/internal/scheduler/testing.go @@ -14,7 +14,6 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) // TestScheduler creates a mock controller and a new Scheduler attached to that controller id. @@ -22,7 +21,7 @@ import ( // // WithRunJobsLimit, WithRunJobsInterval, WithMonitorInterval and WithInterruptThreshold are // the only valid options. -func TestScheduler(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, opt ...Option) *Scheduler { +func TestScheduler(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *Scheduler { t.Helper() rw := db.New(conn) diff --git a/internal/servers/controller/handlers/authmethods/oidc_test.go b/internal/servers/controller/handlers/authmethods/oidc_test.go index 2f6ed2ca4..d843c4603 100644 --- a/internal/servers/controller/handlers/authmethods/oidc_test.go +++ b/internal/servers/controller/handlers/authmethods/oidc_test.go @@ -41,12 +41,11 @@ import ( "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/wrapperspb" - "gorm.io/gorm" ) type setup struct { ctx context.Context - conn *gorm.DB + conn *db.DB rw *db.Db rootWrapper wrapping.Wrapper kmsCache *kms.Kms diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index 777a815db..29b185fa3 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -47,7 +47,6 @@ import ( "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/wrapperspb" - "gorm.io/gorm" ) var testAuthorizedActions = []string{ @@ -67,7 +66,7 @@ var testAuthorizedActions = []string{ "authorize-session", } -func testService(t *testing.T, conn *gorm.DB, kms *kms.Kms, wrapper wrapping.Wrapper) (targets.Service, error) { +func testService(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper) (targets.Service, error) { rw := db.New(conn) sche := scheduler.TestScheduler(t, conn, wrapper) repoFn := func() (*target.Repository, error) { diff --git a/internal/servers/controller/testing.go b/internal/servers/controller/testing.go index f458efcac..85c62ace7 100644 --- a/internal/servers/controller/testing.go +++ b/internal/servers/controller/testing.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/config" + "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/schema" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/intglobals" @@ -26,7 +27,6 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/strutil" - "gorm.io/gorm" ) const ( @@ -126,7 +126,7 @@ func (tc *TestController) ClusterAddrs() []string { return tc.addrs("cluster") } -func (tc *TestController) DbConn() *gorm.DB { +func (tc *TestController) DbConn() *db.DB { return tc.b.Database } @@ -272,8 +272,8 @@ func (tc *TestController) Shutdown() { tc.t.Error(err) } if !tc.opts.DisableDatabaseDestruction { - if tc.b.DestroyDevDatabase() != nil { - if err := tc.b.DestroyDevDatabase(); err != nil { + if tc.b.DestroyDevDatabase(tc.ctx) != nil { + if err := tc.b.DestroyDevDatabase(tc.ctx); err != nil { tc.t.Error(err) } } @@ -536,7 +536,7 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController { if _, err := schema.MigrateStore(ctx, "postgres", tc.b.DatabaseUrl); err != nil { t.Fatal(err) } - if err := tc.b.ConnectToDatabase("postgres"); err != nil { + if err := tc.b.ConnectToDatabase(tc.ctx, "postgres"); err != nil { t.Fatal(err) } if !opts.DisableKmsKeyCreation { diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index 9ea29d3d4..890e5b260 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -19,7 +19,6 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/jackc/pgconn" "google.golang.org/protobuf/types/known/timestamppb" - "gorm.io/gorm" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" @@ -1408,13 +1407,13 @@ func TestRepository_CancelSessionViaFKNull(t *testing.T) { rowsDeleted, err := rw.Delete(context.Background(), tt.cancelFk.fkType) if err != nil { - var pqError *pgconn.PgError - if errors.As(err, &pqError) { - t.Log(pqError.Message) - t.Log(pqError.Detail) - t.Log(pqError.Where) - t.Log(pqError.ConstraintName) - t.Log(pqError.TableName) + var pgError *pgconn.PgError + if errors.As(err, &pgError) { + t.Log(pgError.Message) + t.Log(pgError.Detail) + t.Log(pgError.Where) + t.Log(pgError.ConstraintName) + t.Log(pgError.TableName) } } require.NoError(err) @@ -1642,7 +1641,7 @@ func TestRepository_DeleteSession(t *testing.T) { } } -func testSessionCredentialParams(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository) ComposedOf { +func testSessionCredentialParams(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository) ComposedOf { t.Helper() params := TestSessionParams(t, conn, wrapper, iamRepo) require := require.New(t) diff --git a/internal/session/testing.go b/internal/session/testing.go index 445ff499e..652f5ac98 100644 --- a/internal/session/testing.go +++ b/internal/session/testing.go @@ -20,11 +20,10 @@ import ( "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/timestamppb" - "gorm.io/gorm" ) // TestConnection creates a test connection for the sessionId in the repository. -func TestConnection(t *testing.T, conn *gorm.DB, sessionId, clientTcpAddr string, clientTcpPort uint32, endpointTcpAddr string, endpointTcpPort uint32) *Connection { +func TestConnection(t *testing.T, conn *db.DB, sessionId, clientTcpAddr string, clientTcpPort uint32, endpointTcpAddr string, endpointTcpPort uint32) *Connection { t.Helper() require := require.New(t) rw := db.New(conn) @@ -44,7 +43,7 @@ func TestConnection(t *testing.T, conn *gorm.DB, sessionId, clientTcpAddr string } // TestConnectionState creates a test connection state for the connectionId in the repository. -func TestConnectionState(t *testing.T, conn *gorm.DB, connectionId string, state ConnectionStatus) *ConnectionState { +func TestConnectionState(t *testing.T, conn *db.DB, connectionId string, state ConnectionStatus) *ConnectionState { t.Helper() require := require.New(t) rw := db.New(conn) @@ -56,7 +55,7 @@ func TestConnectionState(t *testing.T, conn *gorm.DB, connectionId string, state } // TestState creates a test state for the sessionId in the repository. -func TestState(t *testing.T, conn *gorm.DB, sessionId string, state Status) *State { +func TestState(t *testing.T, conn *db.DB, sessionId string, state Status) *State { t.Helper() require := require.New(t) rw := db.New(conn) @@ -69,7 +68,7 @@ func TestState(t *testing.T, conn *gorm.DB, sessionId string, state Status) *Sta // TestSession creates a test session composed of c in the repository. Options // are passed into New, and withServerId is handled locally. -func TestSession(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, c ComposedOf, opt ...Option) *Session { +func TestSession(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, c ComposedOf, opt ...Option) *Session { t.Helper() ctx := context.Background() opts := getOpts(opt...) @@ -110,7 +109,7 @@ func TestSession(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, c Compos } // TestDefaultSession creates a test session in the repository using defaults. -func TestDefaultSession(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository, opt ...Option) *Session { +func TestDefaultSession(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository, opt ...Option) *Session { t.Helper() composedOf := TestSessionParams(t, conn, wrapper, iamRepo) future := timestamppb.New(time.Now().Add(time.Hour)) @@ -120,7 +119,7 @@ func TestDefaultSession(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, i // TestSessionParams returns an initialized ComposedOf which can be used to // create a session in the repository. -func TestSessionParams(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository) ComposedOf { +func TestSessionParams(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, iamRepo *iam.Repository) ComposedOf { t.Helper() ctx := context.Background() @@ -176,7 +175,7 @@ func TestTofu(t *testing.T) []byte { // TestWorker inserts a worker into the db to satisfy foreign key constraints. // Supports the WithServerId option. -func TestWorker(t *testing.T, conn *gorm.DB, wrapper wrapping.Wrapper, opt ...Option) *servers.Server { +func TestWorker(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, opt ...Option) *servers.Server { t.Helper() rw := db.New(conn) kms := kms.TestKms(t, conn, wrapper) diff --git a/internal/target/repository_tcp_target_test.go b/internal/target/repository_tcp_target_test.go index 02b3bfd65..89b34dfce 100644 --- a/internal/target/repository_tcp_target_test.go +++ b/internal/target/repository_tcp_target_test.go @@ -219,6 +219,7 @@ func TestRepository_CreateTcpTarget(t *testing.T) { func TestRepository_UpdateTcpTarget(t *testing.T) { t.Parallel() + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -491,7 +492,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { foundTarget, _, _, err := repo.LookupTarget(context.Background(), target.PublicId) assert.NoError(err) assert.True(proto.Equal(targetAfterUpdate.((*TcpTarget)), foundTarget.((*TcpTarget)))) - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) if tt.args.description == "" { diff --git a/internal/target/tcp_target_test.go b/internal/target/tcp_target_test.go index e0890e55e..faa059033 100644 --- a/internal/target/tcp_target_test.go +++ b/internal/target/tcp_target_test.go @@ -146,6 +146,7 @@ func TestTcpTarget_Delete(t *testing.T) { func TestTcpTarget_Update(t *testing.T) { t.Parallel() id := testId(t) + ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) @@ -279,7 +280,7 @@ func TestTcpTarget_Update(t *testing.T) { require.NoError(err) assert.True(proto.Equal(updateTarget, foundTarget)) if len(tt.args.nullPaths) != 0 { - underlyingDB, err := conn.DB() + underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) for _, f := range tt.args.nullPaths { diff --git a/internal/target/testing.go b/internal/target/testing.go index 38b35d6e8..a1761a659 100644 --- a/internal/target/testing.go +++ b/internal/target/testing.go @@ -8,10 +8,9 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" - "gorm.io/gorm" ) -func TestTcpTarget(t *testing.T, conn *gorm.DB, scopeId, name string, opt ...Option) *TcpTarget { +func TestTcpTarget(t *testing.T, conn *db.DB, scopeId, name string, opt ...Option) *TcpTarget { t.Helper() opt = append(opt, WithName(name)) opts := getOpts(opt...) @@ -62,7 +61,7 @@ func testId(t *testing.T) string { // TestCredentialLibrary creates a CredentialLibrary for targetId and // libraryId. -func TestCredentialLibrary(t *testing.T, conn *gorm.DB, targetId, libraryId string) *CredentialLibrary { +func TestCredentialLibrary(t *testing.T, conn *db.DB, targetId, libraryId string) *CredentialLibrary { t.Helper() require := require.New(t) rw := db.New(conn) diff --git a/testing/dbtest/docs.go b/testing/dbtest/docs.go index ada0d0321..546d004b4 100644 --- a/testing/dbtest/docs.go +++ b/testing/dbtest/docs.go @@ -12,7 +12,7 @@ // require.NoError(t, c()) // }) // // use u to get a connection to the new database -// dBase, err := sql.Open("postgres", u) +// dBase, err := common.SqlOpen("postgres", u) // // By default this uses a template that already has all of the boundary // migrations run. If a test needs a database without any migrations, like in diff --git a/testing/dbtest/template.go b/testing/dbtest/template.go index d7ecd88da..5a3a9892e 100644 --- a/testing/dbtest/template.go +++ b/testing/dbtest/template.go @@ -1,14 +1,15 @@ package dbtest import ( - "database/sql" "fmt" "math/rand" "os" "time" // postgres dialect - _ "github.com/lib/pq" + + "github.com/hashicorp/boundary/internal/db/common" + _ "github.com/jackc/pgx/v4/stdlib" ) var testDBPort = "5432" @@ -75,7 +76,10 @@ func startUsingTemplate(dialect string, opt ...Option) (cleanup func() error, re return func() error { return nil }, "", "", fmt.Errorf("unsupported dialect: %s", dialect) } - db, err := sql.Open(dialect, fmt.Sprintf("postgres://boundary:boundary@127.0.0.1:%s/boundary?sslmode=disable", testDBPort)) + if dialect == "postgres" { + dialect = "pgx" + } + db, err := common.SqlOpen(dialect, fmt.Sprintf("postgres://boundary:boundary@127.0.0.1:%s/boundary?sslmode=disable", testDBPort)) if err != nil { return func() error { return nil }, "", "", fmt.Errorf("could not connect to source database: %w", err) } @@ -103,7 +107,7 @@ func startUsingTemplate(dialect string, opt ...Option) (cleanup func() error, re return dropDatabase(dialect, dbname) } - tdb, err := sql.Open(dialect, url) + tdb, err := common.SqlOpen(dialect, url) if err != nil { return func() error { return nil }, "", "", fmt.Errorf("could not create test database: %w", err) } @@ -130,7 +134,7 @@ func dropDatabase(dialect, dbname string) error { return fmt.Errorf("empty dbname") } - db, err := sql.Open(dialect, fmt.Sprintf("postgres://boundary:boundary@127.0.0.1:%s/boundary?sslmode=disable", testDBPort)) + db, err := common.SqlOpen(dialect, fmt.Sprintf("postgres://boundary:boundary@127.0.0.1:%s/boundary?sslmode=disable", testDBPort)) if err != nil { return err } diff --git a/testing/dbtest/template_test.go b/testing/dbtest/template_test.go index dcf305199..786edceae 100644 --- a/testing/dbtest/template_test.go +++ b/testing/dbtest/template_test.go @@ -1,10 +1,10 @@ package dbtest_test import ( - "database/sql" "errors" "testing" + "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/testing/dbtest" "github.com/stretchr/testify/require" ) @@ -56,7 +56,7 @@ func TestStartUsingTemplate(t *testing.T) { } else { require.NoError(t, err) - db, err := sql.Open(tt.dialect, u) + db, err := common.SqlOpen(tt.dialect, u) require.NoError(t, err) require.NoError(t, db.Ping()) db.Close() @@ -64,7 +64,7 @@ func TestStartUsingTemplate(t *testing.T) { require.NoError(t, c()) // ensure that database is gone after calling c - db, _ = sql.Open(tt.dialect, u) + db, _ = common.SqlOpen(tt.dialect, u) require.Error(t, db.Ping()) } })