From 8f8263a3bafd4f3f485de8f80894bb28df77eb83 Mon Sep 17 00:00:00 2001 From: Jonathan Hess Date: Wed, 17 Jul 2024 14:43:58 -0600 Subject: [PATCH] test: Add e2e test to access instance using DNS SRV record. part of #842 WIP: e2e test changes --- e2e_mysql_test.go | 56 +++++++++++++++++++++++++++-------- e2e_postgres_test.go | 70 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 13 deletions(-) diff --git a/e2e_mysql_test.go b/e2e_mysql_test.go index 91f197ca..bfc9bdfd 100644 --- a/e2e_mysql_test.go +++ b/e2e_mysql_test.go @@ -15,12 +15,15 @@ package cloudsqlconn_test import ( + "context" "database/sql" + "fmt" "os" "testing" "time" "cloud.google.com/go/cloudsqlconn" + "cloud.google.com/go/cloudsqlconn/instance" "cloud.google.com/go/cloudsqlconn/mysql/mysql" gomysql "github.com/go-sql-driver/mysql" ) @@ -54,25 +57,52 @@ func requireMySQLVars(t *testing.T) { } } +type mockResolver struct { +} + +func (r *mockResolver) Resolve(_ context.Context, name string) (instanceName instance.ConnName, err error) { + if name == "mysql.example.com" { + return instance.ParseConnNameWithDomainName(mysqlConnName, "mysql.example.com") + } + return instance.ConnName{}, fmt.Errorf("no resolution for %v", name) +} + func TestMySQLDriver(t *testing.T) { if testing.Short() { t.Skip("skipping MySQL integration tests") } tcs := []struct { - desc string - driverName string - opts []cloudsqlconn.Option + desc string + driverName string + instanceName string + user string + password string + opts []cloudsqlconn.Option }{ { - desc: "default options", - driverName: "cloudsql-mysql", - opts: nil, + desc: "default options", + driverName: "cloudsql-mysql", + opts: nil, + instanceName: mysqlConnName, + user: mysqlUser, + password: mysqlPass, + }, + { + desc: "auto IAM authn", + driverName: "cloudsql-mysql-iam", + opts: []cloudsqlconn.Option{cloudsqlconn.WithIAMAuthN()}, + instanceName: mysqlIAMConnName, + user: mysqlIAMUser, + password: "password", }, { - desc: "auto IAM authn", - driverName: "cloudsql-mysql-iam", - opts: []cloudsqlconn.Option{cloudsqlconn.WithIAMAuthN()}, + desc: "with dns", + driverName: "cloudsql-mysql-dns", + opts: []cloudsqlconn.Option{cloudsqlconn.WithResolver(&mockResolver{})}, + instanceName: "mysql.example.com", + user: mysqlUser, + password: mysqlPass, }, } @@ -85,18 +115,18 @@ func TestMySQLDriver(t *testing.T) { } t.Log(now) } - cleanup, err := mysql.RegisterDriver(tc.driverName) + cleanup, err := mysql.RegisterDriver(tc.driverName, tc.opts...) if err != nil { t.Fatalf("failed to register driver: %v", err) } defer cleanup() cfg := gomysql.NewConfig() cfg.CheckConnLiveness = true - cfg.User = mysqlUser - cfg.Passwd = mysqlPass + cfg.User = tc.user + cfg.Passwd = tc.password cfg.DBName = mysqlDB cfg.Net = tc.driverName - cfg.Addr = mysqlConnName + cfg.Addr = tc.instanceName cfg.Params = map[string]string{"parseTime": "true"} db, err := sql.Open("mysql", cfg.FormatDSN()) diff --git a/e2e_postgres_test.go b/e2e_postgres_test.go index 48a59656..e0e806cf 100644 --- a/e2e_postgres_test.go +++ b/e2e_postgres_test.go @@ -28,6 +28,7 @@ import ( "time" "cloud.google.com/go/cloudsqlconn" + "cloud.google.com/go/cloudsqlconn/instance" "github.com/jackc/pgx/v5/pgxpool" "golang.org/x/oauth2" "golang.org/x/oauth2/google" @@ -107,6 +108,65 @@ func TestPostgresPgxPoolConnect(t *testing.T) { t.Log(now) } +type pgMockResolver struct { +} + +func (r *pgMockResolver) Resolve(_ context.Context, name string) (instanceName instance.ConnName, err error) { + if name == "pg.example.com" { + return instance.ParseConnNameWithDomainName(postgresConnName, "pg.example.com") + } + return instance.ConnName{}, fmt.Errorf("no resolution for %v", name) +} + +func TestPostgresPgxPoolConnectDomainName(t *testing.T) { + if testing.Short() { + t.Skip("skipping Postgres integration tests") + } + requirePostgresVars(t) + pgxv5.RegisterDriver("pgxpool-connect") + + ctx := context.Background() + + // Configure the driver to connect to the database + dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", postgresUser, postgresPass, postgresDB) + config, err := pgxpool.ParseConfig(dsn) + if err != nil { + t.Fatalf("failed to parse pgx config: %v", err) + } + + // Create a new dialer with any options + d, err := cloudsqlconn.NewDialer(ctx, cloudsqlconn.WithResolver(&pgMockResolver{})) + if err != nil { + t.Fatalf("failed to init Dialer: %v", err) + } + + // call cleanup when you're done with the database connection to close dialer + cleanup := func() error { return d.Close() } + + // Tell the driver to use the Cloud SQL Go Connector to create connections + // postgresConnName takes the form of 'project:region:instance'. + config.ConnConfig.DialFunc = func(ctx context.Context, _ string, addr string) (net.Conn, error) { + return d.Dial(ctx, "pg.example.com") + } + + // Interact with the driver directly as you normally would + pool, err := pgxpool.NewWithConfig(ctx, config) + if err != nil { + t.Fatalf("failed to create pool: %s", err) + } + // ... etc + + defer cleanup() + defer pool.Close() + + var now time.Time + err = pool.QueryRow(context.Background(), "SELECT NOW()").Scan(&now) + if err != nil { + t.Fatalf("QueryRow failed: %s", err) + } + t.Log(now) +} + func TestPostgresConnectWithIAMUser(t *testing.T) { if testing.Short() { t.Skip("skipping Postgres integration tests") @@ -210,6 +270,7 @@ func TestPostgresV5Hook(t *testing.T) { driver string source string IAMAuthN bool + resolver bool }{ { driver: "cloudsql-postgres-v5", @@ -223,6 +284,13 @@ func TestPostgresV5Hook(t *testing.T) { postgresConnName, postgresUserIAM, postgresDB), IAMAuthN: true, }, + { + driver: "cloudsql-postgres-v5-dns", + source: fmt.Sprintf("host=%s user=%s password=%s dbname=%s sslmode=disable", + "pg.example.com", postgresUser, postgresPass, postgresDB), + IAMAuthN: false, + resolver: true, + }, } if testing.Short() { @@ -239,6 +307,8 @@ func TestPostgresV5Hook(t *testing.T) { for _, tc := range tests { if tc.IAMAuthN { pgxv5.RegisterDriver(tc.driver, cloudsqlconn.WithIAMAuthN()) + } else if tc.resolver { + pgxv5.RegisterDriver(tc.driver, cloudsqlconn.WithResolver(&pgMockResolver{})) } else { pgxv5.RegisterDriver(tc.driver) }