sourcegraph/lib/managedservicesplatform/cloudsql/cloudsql.go
Robert Lin b252e2d68a
msp/runtime: export Cloud SQL conection internals for direct usage (#62524)
Part of https://linear.app/sourcegraph/issue/CORE-96 - we want to be able to use the same Cloud SQL connection mechanism we use for MSP-provisioned databases to be able to connect to a database in another project (in this case, the database of interest is the Sourcegraph.com database).

Also see #62525 

## Test plan

CI - this is a refactor only
2024-05-09 14:42:06 -07:00

94 lines
2.8 KiB
Go

package cloudsql
import (
"context"
"database/sql"
"fmt"
"net"
"cloud.google.com/go/cloudsqlconn"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/stdlib"
"github.com/sourcegraph/sourcegraph/lib/errors"
)
type ConnConfig struct {
// ConnectionName is the CloudSQL connection name,
// e.g. '${project}:${region}:${instance}'
ConnectionName *string
// User is the Cloud SQL user to connect as, e.g. 'test-sa@test-project.iam'
User *string
// Database to connect to.
Database string
// DialOptions are any additional options to pass to the underlying
// cloud-sql-proxy driver.
DialOptions []cloudsqlconn.DialOption
}
// Open opens a *sql.DB connection to the Cloud SQL instance specified by the
// ConnConfig.
//
// 🔔 If you are connecting to a MSP-provisioned Cloud SQL instance,
// DO NOT use this - instead, use runtime.Contract.PostgreSQL.OpenDatabase
// instead.
func Open(
ctx context.Context,
cfg ConnConfig,
) (*sql.DB, error) {
config, err := getCloudSQLConnConfig(ctx, cfg)
if err != nil {
return nil, errors.Wrap(err, "get CloudSQL connection config")
}
return sql.Open("pgx", stdlib.RegisterConnConfig(config))
}
// Connect opens a *pgx.Conn connection to the CloudSQL instance specified by
// the ConnConfig.
//
// 🔔 If you are connecting to a MSP-provisioned Cloud SQL instance,
// DO NOT use this - instead, use runtime.Contract.PostgreSQL.OpenDatabase
// instead.
func Connect(
ctx context.Context,
cfg ConnConfig,
) (*pgx.Conn, error) {
config, err := getCloudSQLConnConfig(ctx, cfg)
if err != nil {
return nil, errors.Wrap(err, "get CloudSQL connection config")
}
return pgx.ConnectConfig(ctx, config)
}
// getCloudSQLConnConfig generates a pgx connection configuration for using
// a Cloud SQL instance using IAM auth.
func getCloudSQLConnConfig(
ctx context.Context,
cfg ConnConfig,
) (*pgx.ConnConfig, error) {
if cfg.ConnectionName == nil || cfg.User == nil {
return nil, errors.New("missing required PostgreSQL configuration")
}
// https://github.com/GoogleCloudPlatform/cloud-sql-go-connector?tab=readme-ov-file#automatic-iam-database-authentication
dsn := fmt.Sprintf("user=%s dbname=%s", *cfg.User, cfg.Database)
config, err := pgx.ParseConfig(dsn)
if err != nil {
return nil, errors.Wrap(err, "pgx.ParseConfig")
}
customDialer, err := cloudsqlconn.NewDialer(ctx,
// always the case when using Cloud SQL in MSP
cloudsqlconn.WithIAMAuthN(),
// allow passthrough of additional dial options
cloudsqlconn.WithDefaultDialOptions(cfg.DialOptions...))
if err != nil {
return nil, errors.Wrap(err, "cloudsqlconn.NewDialer")
}
// Use the Cloud SQL connector to handle connecting to the instance.
// This approach does *NOT* require the Cloud SQL proxy.
config.DialFunc = func(ctx context.Context, _, _ string) (net.Conn, error) {
return customDialer.Dial(ctx, *cfg.ConnectionName)
}
return config, nil
}