diff --git a/docstore/mongodocstore/go.mod b/docstore/mongodocstore/go.mod index f8c417c6d8..b59f16b5d5 100644 --- a/docstore/mongodocstore/go.mod +++ b/docstore/mongodocstore/go.mod @@ -14,7 +14,7 @@ module gocloud.dev/docstore/mongodocstore -go 1.25.0 +go 1.25.7 require ( github.com/google/go-cmp v0.7.0 diff --git a/gcp/cloudsql/cloudsql.go b/gcp/cloudsql/cloudsql.go index b5b3e39f7d..b2a24efc0b 100644 --- a/gcp/cloudsql/cloudsql.go +++ b/gcp/cloudsql/cloudsql.go @@ -17,6 +17,9 @@ package cloudsql // import "gocloud.dev/gcp/cloudsql" import ( + "context" + + "cloud.google.com/go/cloudsqlconn" "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/certs" "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy" "github.com/google/wire" @@ -26,18 +29,86 @@ import ( // CertSourceSet is a Wire provider set that binds a Cloud SQL proxy // certificate source from an GCP-authenticated HTTP client. +// +// Deprecated: Use DialerSet instead, which supports PSC and explicit IP type +// selection via URLOpener.IPType or the "ip_type" URL query parameter. var CertSourceSet = wire.NewSet( NewCertSource, wire.Bind(new(proxy.CertSource), new(*certs.RemoteCertSource))) // NewCertSource creates a local certificate source that uses the given // HTTP client. The client is assumed to make authenticated requests. +// +// Deprecated: Use NewDialer instead. func NewCertSource(c *gcp.HTTPClient) *certs.RemoteCertSource { return certs.NewCertSourceOpts(&c.Client, certs.RemoteOpts{}) } -// NewCertSourceWithIAM creates a local certificate source, including Token source for token information used in -// cert creation, that uses the given HTTP client. The client is assumed to make authenticated requests. +// NewCertSourceWithIAM creates a local certificate source, including Token +// source for token information used in cert creation, that uses the given HTTP +// client. The client is assumed to make authenticated requests. +// +// Deprecated: Use NewDialerWithIAM instead. func NewCertSourceWithIAM(c *gcp.HTTPClient, t oauth2.TokenSource) *certs.RemoteCertSource { return certs.NewCertSourceOpts(&c.Client, certs.RemoteOpts{EnableIAMLogin: true, TokenSource: t}) } + +// DialerSet is a Wire provider set that binds a Cloud SQL Dialer from a GCP +// token source. Use this instead of CertSourceSet when PSC or explicit IP type +// selection is required. +var DialerSet = wire.NewSet(NewDialer) + +// IPType specifies the type of IP address to use when connecting to a Cloud SQL instance. +type IPType int + +const ( + // IPTypeAuto uses a public IP if one is assigned to the instance; otherwise + // falls back to private IP. This is the default and matches the behavior of + // the legacy cloudsql-proxy v1 (equivalent to --auto-ip). + IPTypeAuto IPType = iota + // IPTypePublic uses the instance's public IP address. + IPTypePublic + // IPTypePrivate uses the instance's private IP address (requires VPC peering). + IPTypePrivate + // IPTypePSC uses a Private Service Connect endpoint. + IPTypePSC +) + +// DialOptions returns the cloudsqlconn.DialOption(s) for this IPType. +func (t IPType) DialOptions() []cloudsqlconn.DialOption { + switch t { + case IPTypePublic: + return []cloudsqlconn.DialOption{cloudsqlconn.WithPublicIP()} + case IPTypePrivate: + return []cloudsqlconn.DialOption{cloudsqlconn.WithPrivateIP()} + case IPTypePSC: + return []cloudsqlconn.DialOption{cloudsqlconn.WithPSC()} + default: // IPTypeAuto + return []cloudsqlconn.DialOption{cloudsqlconn.WithAutoIP()} + } +} + +// NewDialer creates a Cloud SQL Dialer using the given OAuth2 token source. +// The returned cleanup function must be called when the dialer is no longer needed. +func NewDialer(ctx context.Context, ts oauth2.TokenSource) (*cloudsqlconn.Dialer, func(), error) { + d, err := cloudsqlconn.NewDialer(ctx, cloudsqlconn.WithTokenSource(ts)) + if err != nil { + return nil, func() {}, err + } + return d, func() { d.Close() }, nil +} + +// NewDialerWithIAM creates a Cloud SQL Dialer with IAM authentication enabled, +// using the given OAuth2 token source. IAM authentication allows passwordless +// connections to Cloud SQL instances. +// The returned cleanup function must be called when the dialer is no longer needed. +func NewDialerWithIAM(ctx context.Context, ts oauth2.TokenSource) (*cloudsqlconn.Dialer, func(), error) { + d, err := cloudsqlconn.NewDialer(ctx, + cloudsqlconn.WithIAMAuthNTokenSources(ts, ts), + cloudsqlconn.WithIAMAuthN(), + ) + if err != nil { + return nil, func() {}, err + } + return d, func() { d.Close() }, nil +} diff --git a/go.mod b/go.mod index 3a88ab2c47..732319d10c 100644 --- a/go.mod +++ b/go.mod @@ -14,9 +14,10 @@ module gocloud.dev -go 1.25.0 +go 1.25.7 require ( + cloud.google.com/go/cloudsqlconn v1.20.2 cloud.google.com/go/compute/metadata v0.9.0 cloud.google.com/go/firestore v1.21.0 cloud.google.com/go/iam v1.5.3 @@ -124,6 +125,7 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/martian/v3 v3.3.3 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect diff --git a/go.sum b/go.sum index 64011c71d2..2ccfa9b08b 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/cloudsqlconn v1.20.2 h1:r1BFbgxKA7h0jY13pGk8wBueUeLhqF27e5Hyaxl8Ua8= +cloud.google.com/go/cloudsqlconn v1.20.2/go.mod h1:cGBrxU+pKs1NppBkecFC+rKn9B5GnEdlz7XrHbuwn7E= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -244,12 +246,16 @@ github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1 github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -340,6 +346,26 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= +github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= +github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= +github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -358,6 +384,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo= github.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/microsoft/go-mssqldb v1.9.8 h1:d4IFMvF/o+HdpXUqbBfzHvn/NlFA75YGcfHUUvDFJEM= +github.com/microsoft/go-mssqldb v1.9.8/go.mod h1:eGSRSGAW4hKMy5YcAenhCDjIRm2rhqIdmmwgciMzLus= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= @@ -367,6 +395,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/internal/testing/alldeps b/internal/testing/alldeps index 7b3a94d892..b041ac811b 100644 --- a/internal/testing/alldeps +++ b/internal/testing/alldeps @@ -2,6 +2,7 @@ cel.dev/expr cloud.google.com/go cloud.google.com/go/auth cloud.google.com/go/auth/oauth2adapt +cloud.google.com/go/cloudsqlconn cloud.google.com/go/compute/metadata cloud.google.com/go/firestore cloud.google.com/go/iam @@ -84,6 +85,7 @@ github.com/go-logr/stdr github.com/go-sql-driver/mysql github.com/gogo/protobuf github.com/golang-jwt/jwt/v5 +github.com/golang/groupcache github.com/golang/protobuf github.com/golang/snappy github.com/google/go-cmp @@ -118,6 +120,7 @@ github.com/jmespath/go-jmespath github.com/klauspost/compress github.com/kylelemons/godebug github.com/lib/pq +github.com/mitchellh/go-homedir github.com/mitchellh/mapstructure github.com/montanaflynn/stats github.com/munnerz/goautoneg @@ -130,7 +133,6 @@ github.com/prometheus/client_golang github.com/prometheus/client_model github.com/prometheus/common github.com/prometheus/otlptranslator -github.com/prometheus/procfs github.com/rabbitmq/amqp091-go github.com/rcrowley/go-metrics github.com/ryanuber/go-glob diff --git a/internal/website/go.mod b/internal/website/go.mod index 47f148b2c1..39f7e85c50 100644 --- a/internal/website/go.mod +++ b/internal/website/go.mod @@ -1,6 +1,6 @@ module gocloud.dev/internal/website -go 1.25.0 +go 1.25.7 require ( github.com/google/go-cmp v0.6.0 diff --git a/mysql/gcpmysql/gcpmysql.go b/mysql/gcpmysql/gcpmysql.go index 87588804c5..01d9e62eb2 100644 --- a/mysql/gcpmysql/gcpmysql.go +++ b/mysql/gcpmysql/gcpmysql.go @@ -24,6 +24,17 @@ // To customize the URL opener, or for more details on the URL format, // see URLOpener. // +// # IP Type +// +// By default, connections use auto-IP selection (public IP if available, +// otherwise private IP), matching the behavior of the legacy cloudsql-proxy. +// To use a specific IP type, set URLOpener.IPType or pass the "ip_type" query +// parameter in the URL (requires URLOpener.Dialer to be set): +// +// gcpmysql://user:pass@project/region/instance/dbname?ip_type=psc +// +// Valid values for ip_type: auto, public, private, psc. +// // See https://gocloud.dev/concepts/urls/ for background information. package gcpmysql // import "gocloud.dev/mysql/gcpmysql" @@ -37,6 +48,7 @@ import ( "strings" "sync" + "cloud.google.com/go/cloudsqlconn" "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy" "github.com/XSAM/otelsql" "github.com/go-sql-driver/mysql" @@ -68,13 +80,13 @@ func (o *lazyCredsOpener) OpenMySQLURL(ctx context.Context, u *url.URL) (*sql.DB o.err = err return } - client, err := gcp.NewHTTPClient(gcp.DefaultTransport(), creds.TokenSource) + // Ignore cleanup: the dialer lives for the process lifetime in this global opener. + d, _, err := cloudsql.NewDialer(ctx, creds.TokenSource) if err != nil { o.err = err return } - certSource := cloudsql.NewCertSource(client) - o.opener = &URLOpener{CertSource: certSource} + o.opener = &URLOpener{Dialer: d} }) if o.err != nil { return nil, fmt.Errorf("gcpmysql open %v: %v", u, o.err) @@ -85,8 +97,15 @@ func (o *lazyCredsOpener) OpenMySQLURL(ctx context.Context, u *url.URL) (*sql.DB // URLOpener opens Cloud MySQL URLs like // "gcpmysql://user:password@project/region/instance/dbname". type URLOpener struct { + // Dialer creates Cloud SQL connections using the Cloud SQL Go Connector. + // Supports PSC and explicit IP type selection via the "ip_type" URL query + // parameter. If both Dialer and CertSource are set, Dialer takes precedence. + Dialer *cloudsqlconn.Dialer + // CertSource specifies how the opener will obtain authentication information. - // CertSource must not be nil. + // + // Deprecated: Use Dialer instead. CertSource does not support PSC or + // explicit IP type selection. Ignored if Dialer is also set. CertSource proxy.CertSource // TraceOpts contains options for OpenTelemetry. @@ -95,25 +114,48 @@ type URLOpener struct { // OpenMySQLURL opens a new GCP database connection wrapped with OpenTelemetry instrumentation. func (uo *URLOpener) OpenMySQLURL(ctx context.Context, u *url.URL) (*sql.DB, error) { - if uo.CertSource == nil { - return nil, fmt.Errorf("gcpmysql: URLOpener CertSource is nil") + if uo.Dialer == nil && uo.CertSource == nil { + return nil, fmt.Errorf("gcpmysql: URLOpener Dialer and CertSource are both nil") } - var ( - client = &proxy.Client{Certs: uo.CertSource, Port: 3307} - cfg, err = configFromURL(u) - ) + + cfg, err := configFromURL(u) if err != nil { return nil, fmt.Errorf("gcpmysql: open config %v", err) } - cfg.DialFunc = func(ctx context.Context, _, addr string) (net.Conn, error) { - // MySQL driver's addr is in the form "[host]:3306" after normalized. - // https://github.com/go-sql-driver/mysql/blob/76c00e35a8d48f8f70f0e7dffe584692bd3fa612/dsn.go#L193-L195 - instance, _, err := net.SplitHostPort(addr) - if err != nil { - return nil, err + + if uo.Dialer != nil { + // New path: Cloud SQL Go Connector with IP type and PSC support. + ipType := cloudsql.IPTypeAuto + if s := u.Query().Get("ip_type"); s != "" { + parsed, err := parseIPType(s) + if err != nil { + return nil, fmt.Errorf("gcpmysql: open: %v", err) + } + ipType = parsed + } + dialOpts := ipType.DialOptions() + d := uo.Dialer + cfg.DialFunc = func(ctx context.Context, _, addr string) (net.Conn, error) { + // MySQL driver's addr is in the form "[host]:3306" after normalized. + // https://github.com/go-sql-driver/mysql/blob/76c00e35a8d48f8f70f0e7dffe584692bd3fa612/dsn.go#L193-L195 + instance, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + return d.Dial(ctx, instance, dialOpts...) + } + } else { + // Legacy path: cloudsql-proxy v1 (no PSC or IP type selection). + client := &proxy.Client{Certs: uo.CertSource, Port: 3307} + cfg.DialFunc = func(ctx context.Context, _, addr string) (net.Conn, error) { + instance, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + return client.DialContext(ctx, instance) } - return client.DialContext(ctx, instance) } + c, err := mysql.NewConnector(cfg) if err != nil { return nil, fmt.Errorf("gcpmysql: open connector %v", err) @@ -127,10 +169,14 @@ func configFromURL(u *url.URL) (*mysql.Config, error) { return nil, err } + // Strip ip_type from query before passing to MySQL DSN parser. + query := u.Query() + query.Del("ip_type") + var cfg *mysql.Config switch { - case len(u.RawQuery) > 0: - optDsn := fmt.Sprintf("/%s?%s", dbName, u.RawQuery) + case len(query) > 0: + optDsn := fmt.Sprintf("/%s?%s", dbName, query.Encode()) if cfg, err = mysql.ParseDSN(optDsn); err != nil { return nil, err } @@ -161,3 +207,18 @@ func instanceFromURL(u *url.URL) (instance, db string, _ error) { } return parts[0] + ":" + parts[1] + ":" + parts[2], parts[3], nil } + +func parseIPType(s string) (cloudsql.IPType, error) { + switch strings.ToLower(s) { + case "auto", "": + return cloudsql.IPTypeAuto, nil + case "public": + return cloudsql.IPTypePublic, nil + case "private": + return cloudsql.IPTypePrivate, nil + case "psc": + return cloudsql.IPTypePSC, nil + default: + return 0, fmt.Errorf("unknown ip_type %q (valid: auto, public, private, psc)", s) + } +} diff --git a/mysql/gcpmysql/gcpmysql_test.go b/mysql/gcpmysql/gcpmysql_test.go index db41a15b2b..806e5f0132 100644 --- a/mysql/gcpmysql/gcpmysql_test.go +++ b/mysql/gcpmysql/gcpmysql_test.go @@ -16,14 +16,19 @@ package gcpmysql import ( "context" + "crypto/tls" + "crypto/x509" "fmt" "net/url" "reflect" "testing" + "cloud.google.com/go/cloudsqlconn" drvr "github.com/go-sql-driver/mysql" + "gocloud.dev/gcp/cloudsql" "gocloud.dev/internal/testing/terraform" "gocloud.dev/mysql" + "golang.org/x/oauth2" ) func TestOpen(t *testing.T) { @@ -62,6 +67,153 @@ func TestOpen(t *testing.T) { } } +// mockCertSource is a no-op proxy.CertSource for unit tests. +type mockCertSource struct{} + +func (mockCertSource) Local(instance string) (tls.Certificate, error) { + return tls.Certificate{}, nil +} +func (mockCertSource) Remote(instance string) (*x509.Certificate, string, string, string, error) { + return nil, "", "", "", nil +} + +func mustParseURL(raw string) *url.URL { + u, err := url.Parse(raw) + if err != nil { + panic(err) + } + return u +} + +func TestParseIPType(t *testing.T) { + tests := []struct { + input string + want cloudsql.IPType + wantErr bool + }{ + {"auto", cloudsql.IPTypeAuto, false}, + {"public", cloudsql.IPTypePublic, false}, + {"private", cloudsql.IPTypePrivate, false}, + {"psc", cloudsql.IPTypePSC, false}, + {"", cloudsql.IPTypeAuto, false}, // empty string defaults to auto + {"PUBLIC", cloudsql.IPTypePublic, false}, // case-insensitive + {"invalid", 0, true}, + {"public ", 0, true}, // whitespace is not trimmed + } + for _, tc := range tests { + got, err := parseIPType(tc.input) + if (err != nil) != tc.wantErr { + t.Errorf("parseIPType(%q) error = %v, wantErr %v", tc.input, err, tc.wantErr) + continue + } + if !tc.wantErr && got != tc.want { + t.Errorf("parseIPType(%q) = %v, want %v", tc.input, got, tc.want) + } + } +} + +func TestOpenMySQLURL_Errors(t *testing.T) { + ctx := context.Background() + base := "gcpmysql://user:pass@project/us-central1/instance/db" + + fakeTS := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "fake"}) + d, err := cloudsqlconn.NewDialer(ctx, cloudsqlconn.WithTokenSource(fakeTS)) + if err != nil { + t.Fatalf("NewDialer: %v", err) + } + defer d.Close() + + tests := []struct { + name string + uo *URLOpener + url string + }{ + { + name: "NilDialerAndCertSource", + uo: &URLOpener{}, + url: base, + }, + { + // ip_type validation only runs on the Dialer path; legacy silently ignores it. + name: "InvalidIPType", + uo: &URLOpener{Dialer: d}, + url: base + "?ip_type=bogus", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + db, err := tc.uo.OpenMySQLURL(ctx, mustParseURL(tc.url)) + if err == nil { + db.Close() + t.Fatal("expected error, got nil") + } + }) + } +} + +func TestOpenMySQLURL_DialerPath(t *testing.T) { + ctx := context.Background() + fakeTS := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "fake"}) + d, err := cloudsqlconn.NewDialer(ctx, cloudsqlconn.WithTokenSource(fakeTS)) + if err != nil { + t.Fatalf("NewDialer: %v", err) + } + defer d.Close() + + tests := []struct { + name string + url string + }{ + {"DefaultIPType", "gcpmysql://user:pass@project/us-central1/instance/db"}, + {"ExplicitAutoIPType", "gcpmysql://user:pass@project/us-central1/instance/db?ip_type=auto"}, + {"PublicIPType", "gcpmysql://user:pass@project/us-central1/instance/db?ip_type=public"}, + {"PrivateIPType", "gcpmysql://user:pass@project/us-central1/instance/db?ip_type=private"}, + {"PSCIPType", "gcpmysql://user:pass@project/us-central1/instance/db?ip_type=psc"}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + uo := &URLOpener{Dialer: d} + db, err := uo.OpenMySQLURL(ctx, mustParseURL(tc.url)) + if err != nil { + t.Fatalf("OpenMySQLURL(%q): %v", tc.url, err) + } + db.Close() + }) + } +} + +func TestOpenMySQLURL_LegacyPath(t *testing.T) { + ctx := context.Background() + uo := &URLOpener{CertSource: mockCertSource{}} + db, err := uo.OpenMySQLURL(ctx, mustParseURL("gcpmysql://user:pass@project/us-central1/instance/db")) + if err != nil { + t.Fatalf("OpenMySQLURL: %v", err) + } + db.Close() +} + +// TestOpenMySQLURL_DialerTakesPrecedence verifies that when both Dialer and +// CertSource are set, the Dialer path is taken. The two paths differ in how they +// handle ip_type: the Dialer path validates and returns an error for unknown values; +// the legacy CertSource path silently ignores ip_type. Passing ip_type=bogus +// therefore errors only if the Dialer path ran. +func TestOpenMySQLURL_DialerTakesPrecedence(t *testing.T) { + ctx := context.Background() + fakeTS := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "fake"}) + d, err := cloudsqlconn.NewDialer(ctx, cloudsqlconn.WithTokenSource(fakeTS)) + if err != nil { + t.Fatalf("NewDialer: %v", err) + } + defer d.Close() + + uo := &URLOpener{Dialer: d, CertSource: mockCertSource{}} + db, err := uo.OpenMySQLURL(ctx, mustParseURL("gcpmysql://user:pass@project/us-central1/instance/db?ip_type=bogus")) + if err == nil { + db.Close() + t.Fatal("expected ip_type validation error from Dialer path; CertSource path would have silently ignored ip_type") + } +} + func TestInstanceFromURL(t *testing.T) { tests := []struct { name string diff --git a/postgres/gcppostgres/gcppostgres.go b/postgres/gcppostgres/gcppostgres.go index 90c4dbbe92..4e5ae8cdf1 100644 --- a/postgres/gcppostgres/gcppostgres.go +++ b/postgres/gcppostgres/gcppostgres.go @@ -24,6 +24,17 @@ // To customize the URL opener, or for more details on the URL format, // see URLOpener. // +// # IP Type +// +// By default, connections use auto-IP selection (public IP if available, +// otherwise private IP), matching the behavior of the legacy cloudsql-proxy. +// To use a specific IP type, set URLOpener.IPType or pass the "ip_type" query +// parameter in the URL (requires URLOpener.Dialer to be set): +// +// gcppostgres://user:pass@project/region/instance/dbname?ip_type=psc +// +// Valid values for ip_type: auto, public, private, psc. +// // See https://gocloud.dev/concepts/urls/ for background information. package gcppostgres // import "gocloud.dev/postgres/gcppostgres" @@ -40,6 +51,7 @@ import ( "sync" "time" + "cloud.google.com/go/cloudsqlconn" "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy" "github.com/XSAM/otelsql" "github.com/lib/pq" @@ -71,13 +83,13 @@ func (o *lazyCredsOpener) OpenPostgresURL(ctx context.Context, u *url.URL) (*sql o.err = err return } - client, err := gcp.NewHTTPClient(gcp.DefaultTransport(), creds.TokenSource) + // Ignore cleanup: the dialer lives for the process lifetime in this global opener. + d, _, err := cloudsql.NewDialerWithIAM(ctx, creds.TokenSource) if err != nil { o.err = err return } - certSource := cloudsql.NewCertSourceWithIAM(client, creds.TokenSource) - o.opener = &URLOpener{CertSource: certSource} + o.opener = &URLOpener{Dialer: d} }) if o.err != nil { return nil, fmt.Errorf("gcppostgres open %v: %v", u, o.err) @@ -88,8 +100,15 @@ func (o *lazyCredsOpener) OpenPostgresURL(ctx context.Context, u *url.URL) (*sql // URLOpener opens GCP PostgreSQL URLs // like "gcppostgres://user:password@myproject/us-central1/instanceId/mydb". type URLOpener struct { + // Dialer creates Cloud SQL connections using the Cloud SQL Go Connector. + // Supports PSC and explicit IP type selection via the "ip_type" URL query + // parameter. If both Dialer and CertSource are set, Dialer takes precedence. + Dialer *cloudsqlconn.Dialer + // CertSource specifies how the opener will obtain authentication information. - // CertSource must not be nil. + // + // Deprecated: Use Dialer instead. CertSource does not support PSC or + // explicit IP type selection. Ignored if Dialer is also set. CertSource proxy.CertSource // TraceOpts contains options for OpenTelemetry. @@ -98,8 +117,8 @@ type URLOpener struct { // OpenPostgresURL opens a new GCP database connection wrapped with OpenTelemetry instrumentation. func (uo *URLOpener) OpenPostgresURL(ctx context.Context, u *url.URL) (*sql.DB, error) { - if uo.CertSource == nil { - return nil, fmt.Errorf("gcppostgres: URLOpener CertSource is nil") + if uo.Dialer == nil && uo.CertSource == nil { + return nil, fmt.Errorf("gcppostgres: URLOpener Dialer and CertSource are both nil") } instance, dbName, err := instanceFromURL(u) if err != nil { @@ -120,8 +139,32 @@ func (uo *URLOpener) OpenPostgresURL(ctx context.Context, u *url.URL) (*sql.DB, u2.Scheme = "postgres" u2.Host = "cloudsql" u2.Path = "/" + dbName + + if uo.Dialer != nil { + // New path: Cloud SQL Go Connector with IP type and PSC support. + ipType := cloudsql.IPTypeAuto + if s := query.Get("ip_type"); s != "" { + parsed, err := parseIPType(s) + if err != nil { + return nil, fmt.Errorf("gcppostgres: open: %v", err) + } + ipType = parsed + query.Del("ip_type") + } + u2.RawQuery = query.Encode() + return sql.OpenDB(connector{ + dialer: uo.Dialer, + instance: instance, + dialOpts: ipType.DialOptions(), + pqConn: u2.String(), + traceOpts: append([]otelsql.Option(nil), uo.TraceOpts...), + }), nil + } + + // Legacy path: cloudsql-proxy v1 (no PSC or IP type selection). + query.Del("ip_type") // strip even if set, silently ignore for legacy path u2.RawQuery = query.Encode() - db := sql.OpenDB(connector{ + return sql.OpenDB(legacyConnector{ client: &proxy.Client{ Port: 3307, Certs: uo.CertSource, @@ -129,8 +172,7 @@ func (uo *URLOpener) OpenPostgresURL(ctx context.Context, u *url.URL) (*sql.DB, instance: instance, pqConn: u2.String(), traceOpts: append([]otelsql.Option(nil), uo.TraceOpts...), - }) - return db, nil + }), nil } func instanceFromURL(u *url.URL) (instance, db string, _ error) { @@ -145,9 +187,27 @@ func instanceFromURL(u *url.URL) (instance, db string, _ error) { return parts[0] + ":" + parts[1] + ":" + parts[2], parts[3], nil } +func parseIPType(s string) (cloudsql.IPType, error) { + switch strings.ToLower(s) { + case "auto", "": + return cloudsql.IPTypeAuto, nil + case "public": + return cloudsql.IPTypePublic, nil + case "private": + return cloudsql.IPTypePrivate, nil + case "psc": + return cloudsql.IPTypePSC, nil + default: + return 0, fmt.Errorf("unknown ip_type %q (valid: auto, public, private, psc)", s) + } +} + +// --- New path: Cloud SQL Go Connector --- + type pqDriver struct { - client *proxy.Client + dialer *cloudsqlconn.Dialer instance string + dialOpts []cloudsqlconn.DialOption traceOpts []otelsql.Option } @@ -157,18 +217,19 @@ func (d pqDriver) Open(name string) (driver.Conn, error) { } func (d pqDriver) OpenConnector(name string) (driver.Connector, error) { - return connector{d.client, d.instance, name, d.traceOpts}, nil + return connector{d.dialer, d.instance, d.dialOpts, name, d.traceOpts}, nil } type connector struct { - client *proxy.Client + dialer *cloudsqlconn.Dialer instance string + dialOpts []cloudsqlconn.DialOption pqConn string traceOpts []otelsql.Option } func (c connector) Connect(context.Context) (driver.Conn, error) { - conn, err := pq.DialOpen(dialer{c.client, c.instance}, c.pqConn) + conn, err := pq.DialOpen(dialer{c.dialer, c.instance, c.dialOpts}, c.pqConn) if err != nil { return nil, err } @@ -176,18 +237,68 @@ func (c connector) Connect(context.Context) (driver.Conn, error) { } func (c connector) Driver() driver.Driver { - return otelsql.WrapDriver(pqDriver{c.client, c.instance, c.traceOpts}, c.traceOpts...) + return otelsql.WrapDriver(pqDriver{c.dialer, c.instance, c.dialOpts, c.traceOpts}, c.traceOpts...) } type dialer struct { - client *proxy.Client + d *cloudsqlconn.Dialer instance string + dialOpts []cloudsqlconn.DialOption } func (d dialer) Dial(network, address string) (net.Conn, error) { - return d.client.Dial(d.instance) + return d.d.Dial(context.Background(), d.instance, d.dialOpts...) } func (d dialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) { return nil, errors.New("gcppostgres: DialTimeout not supported") } + +// --- Legacy path: cloudsql-proxy v1 --- + +type legacyPqDriver struct { + client *proxy.Client + instance string + traceOpts []otelsql.Option +} + +func (d legacyPqDriver) Open(name string) (driver.Conn, error) { + c, _ := d.OpenConnector(name) + return c.Connect(context.Background()) +} + +func (d legacyPqDriver) OpenConnector(name string) (driver.Connector, error) { + return legacyConnector{d.client, d.instance, name, d.traceOpts}, nil +} + +type legacyConnector struct { + client *proxy.Client + instance string + pqConn string + traceOpts []otelsql.Option +} + +func (c legacyConnector) Connect(context.Context) (driver.Conn, error) { + conn, err := pq.DialOpen(legacyDialer{c.client, c.instance}, c.pqConn) + if err != nil { + return nil, err + } + return conn, nil +} + +func (c legacyConnector) Driver() driver.Driver { + return otelsql.WrapDriver(legacyPqDriver{c.client, c.instance, c.traceOpts}, c.traceOpts...) +} + +type legacyDialer struct { + client *proxy.Client + instance string +} + +func (d legacyDialer) Dial(network, address string) (net.Conn, error) { + return d.client.Dial(d.instance) +} + +func (d legacyDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) { + return nil, errors.New("gcppostgres: DialTimeout not supported") +} diff --git a/postgres/gcppostgres/gcppostgres_test.go b/postgres/gcppostgres/gcppostgres_test.go index e707d50d25..1f86b978ee 100644 --- a/postgres/gcppostgres/gcppostgres_test.go +++ b/postgres/gcppostgres/gcppostgres_test.go @@ -16,12 +16,17 @@ package gcppostgres import ( "context" + "crypto/tls" + "crypto/x509" "fmt" "net/url" "testing" + "cloud.google.com/go/cloudsqlconn" + "gocloud.dev/gcp/cloudsql" "gocloud.dev/internal/testing/terraform" "gocloud.dev/postgres" + "golang.org/x/oauth2" ) func TestURLOpener(t *testing.T) { @@ -90,6 +95,163 @@ func TestURLOpener(t *testing.T) { } } +// mockCertSource is a no-op proxy.CertSource for unit tests. +type mockCertSource struct{} + +func (mockCertSource) Local(instance string) (tls.Certificate, error) { + return tls.Certificate{}, nil +} +func (mockCertSource) Remote(instance string) (*x509.Certificate, string, string, string, error) { + return nil, "", "", "", nil +} + +func mustParseURL(raw string) *url.URL { + u, err := url.Parse(raw) + if err != nil { + panic(err) + } + return u +} + +func TestParseIPType(t *testing.T) { + tests := []struct { + input string + want cloudsql.IPType + wantErr bool + }{ + {"auto", cloudsql.IPTypeAuto, false}, + {"public", cloudsql.IPTypePublic, false}, + {"private", cloudsql.IPTypePrivate, false}, + {"psc", cloudsql.IPTypePSC, false}, + {"", cloudsql.IPTypeAuto, false}, // empty string defaults to auto + {"PUBLIC", cloudsql.IPTypePublic, false}, // case-insensitive + {"invalid", 0, true}, + {"public ", 0, true}, // whitespace is not trimmed + } + for _, tc := range tests { + got, err := parseIPType(tc.input) + if (err != nil) != tc.wantErr { + t.Errorf("parseIPType(%q) error = %v, wantErr %v", tc.input, err, tc.wantErr) + continue + } + if !tc.wantErr && got != tc.want { + t.Errorf("parseIPType(%q) = %v, want %v", tc.input, got, tc.want) + } + } +} + +func TestOpenPostgresURL_Errors(t *testing.T) { + ctx := context.Background() + base := "gcppostgres://user:pass@project/us-central1/instance/db" + + fakeTS := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "fake"}) + d, err := cloudsqlconn.NewDialer(ctx, cloudsqlconn.WithTokenSource(fakeTS)) + if err != nil { + t.Fatalf("NewDialer: %v", err) + } + defer d.Close() + + tests := []struct { + name string + uo *URLOpener + url string + }{ + { + name: "NilDialerAndCertSource", + uo: &URLOpener{}, + url: base, + }, + { + name: "ForbiddenSSLMode", + uo: &URLOpener{CertSource: mockCertSource{}}, + url: base + "?sslmode=require", + }, + { + name: "ForbiddenSSLCert", + uo: &URLOpener{CertSource: mockCertSource{}}, + url: base + "?sslcert=/tmp/cert", + }, + { + // ip_type validation only runs on the Dialer path; legacy silently ignores it. + name: "InvalidIPType", + uo: &URLOpener{Dialer: d}, + url: base + "?ip_type=bogus", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + db, err := tc.uo.OpenPostgresURL(ctx, mustParseURL(tc.url)) + if err == nil { + db.Close() + t.Fatal("expected error, got nil") + } + }) + } +} + +func TestOpenPostgresURL_DialerPath(t *testing.T) { + ctx := context.Background() + fakeTS := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "fake"}) + d, err := cloudsqlconn.NewDialer(ctx, cloudsqlconn.WithTokenSource(fakeTS)) + if err != nil { + t.Fatalf("NewDialer: %v", err) + } + defer d.Close() + + tests := []struct { + name string + url string + }{ + {"DefaultIPType", "gcppostgres://user:pass@project/us-central1/instance/db"}, + {"ExplicitAutoIPType", "gcppostgres://user:pass@project/us-central1/instance/db?ip_type=auto"}, + {"PublicIPType", "gcppostgres://user:pass@project/us-central1/instance/db?ip_type=public"}, + {"PrivateIPType", "gcppostgres://user:pass@project/us-central1/instance/db?ip_type=private"}, + {"PSCIPType", "gcppostgres://user:pass@project/us-central1/instance/db?ip_type=psc"}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + uo := &URLOpener{Dialer: d} + db, err := uo.OpenPostgresURL(ctx, mustParseURL(tc.url)) + if err != nil { + t.Fatalf("OpenPostgresURL(%q): %v", tc.url, err) + } + db.Close() + }) + } +} + +func TestOpenPostgresURL_LegacyPath(t *testing.T) { + ctx := context.Background() + uo := &URLOpener{CertSource: mockCertSource{}} + db, err := uo.OpenPostgresURL(ctx, mustParseURL("gcppostgres://user:pass@project/us-central1/instance/db")) + if err != nil { + t.Fatalf("OpenPostgresURL: %v", err) + } + db.Close() +} + +// TestOpenPostgresURL_DialerTakesPrecedence verifies that when both Dialer and +// CertSource are set, the Dialer path is taken. The two paths differ in how they +// handle ip_type: the Dialer path validates and returns an error for unknown values; +// the legacy CertSource path silently ignores ip_type. Passing ip_type=bogus +// therefore errors only if the Dialer path ran. +func TestOpenPostgresURL_DialerTakesPrecedence(t *testing.T) { + ctx := context.Background() + fakeTS := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: "fake"}) + d, err := cloudsqlconn.NewDialer(ctx, cloudsqlconn.WithTokenSource(fakeTS)) + if err != nil { + t.Fatalf("NewDialer: %v", err) + } + defer d.Close() + + uo := &URLOpener{Dialer: d, CertSource: mockCertSource{}} + db, err := uo.OpenPostgresURL(ctx, mustParseURL("gcppostgres://user:pass@project/us-central1/instance/db?ip_type=bogus")) + if err == nil { + db.Close() + t.Fatal("expected ip_type validation error from Dialer path; CertSource path would have silently ignored ip_type") + } +} + func TestInstanceFromURL(t *testing.T) { tests := []struct { name string diff --git a/pubsub/kafkapubsub/go.mod b/pubsub/kafkapubsub/go.mod index 87a4342cb8..11ef66dc3a 100644 --- a/pubsub/kafkapubsub/go.mod +++ b/pubsub/kafkapubsub/go.mod @@ -14,7 +14,7 @@ module gocloud.dev/pubsub/kafkapubsub -go 1.25.0 +go 1.25.7 require ( github.com/IBM/sarama v1.47.0 diff --git a/pubsub/natspubsub/go.mod b/pubsub/natspubsub/go.mod index 2e0d371f43..b765131c88 100644 --- a/pubsub/natspubsub/go.mod +++ b/pubsub/natspubsub/go.mod @@ -14,7 +14,7 @@ module gocloud.dev/pubsub/natspubsub -go 1.25.0 +go 1.25.7 require ( github.com/google/go-cmp v0.7.0 diff --git a/pubsub/rabbitpubsub/go.mod b/pubsub/rabbitpubsub/go.mod index 47ca0b5ea9..4a4d5e2dd2 100644 --- a/pubsub/rabbitpubsub/go.mod +++ b/pubsub/rabbitpubsub/go.mod @@ -14,7 +14,7 @@ module gocloud.dev/pubsub/rabbitpubsub -go 1.25.0 +go 1.25.7 require ( github.com/rabbitmq/amqp091-go v1.10.0 diff --git a/runtimevar/etcdvar/go.mod b/runtimevar/etcdvar/go.mod index 33fc12ae0e..31d623fce4 100644 --- a/runtimevar/etcdvar/go.mod +++ b/runtimevar/etcdvar/go.mod @@ -14,7 +14,7 @@ module gocloud.dev/runtimevar/etcdvar -go 1.25.0 +go 1.25.7 require ( github.com/google/go-cmp v0.7.0 diff --git a/runtimevar/hashivault/go.mod b/runtimevar/hashivault/go.mod index aa6ad2a7d9..d470882d32 100644 --- a/runtimevar/hashivault/go.mod +++ b/runtimevar/hashivault/go.mod @@ -14,7 +14,7 @@ module gocloud.dev/runtimevar/hashivault -go 1.25.0 +go 1.25.7 require ( github.com/hashicorp/vault/api v1.22.0 diff --git a/samples/go.mod b/samples/go.mod index 9d13264acf..7787de67d2 100644 --- a/samples/go.mod +++ b/samples/go.mod @@ -14,7 +14,7 @@ module gocloud.dev/samples -go 1.25.0 +go 1.25.7 require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 @@ -53,6 +53,7 @@ require ( cloud.google.com/go v0.123.0 // indirect cloud.google.com/go/auth v0.18.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/cloudsqlconn v1.20.2 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/firestore v1.21.0 // indirect cloud.google.com/go/iam v1.5.3 // indirect @@ -126,6 +127,7 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/google/go-replayers/grpcreplay v1.3.0 // indirect github.com/google/go-replayers/httpreplay v1.2.0 // indirect @@ -178,6 +180,7 @@ require ( github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.mongodb.org/mongo-driver v1.17.9 // indirect + go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/bridges/prometheus v0.67.0 // indirect go.opentelemetry.io/contrib/detectors/aws/ec2 v1.38.0 // indirect diff --git a/samples/go.sum b/samples/go.sum index 435b81e8a3..105b743cb1 100644 --- a/samples/go.sum +++ b/samples/go.sum @@ -33,6 +33,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/cloudsqlconn v1.20.2 h1:r1BFbgxKA7h0jY13pGk8wBueUeLhqF27e5Hyaxl8Ua8= +cloud.google.com/go/cloudsqlconn v1.20.2/go.mod h1:cGBrxU+pKs1NppBkecFC+rKn9B5GnEdlz7XrHbuwn7E= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -256,10 +258,16 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -387,6 +395,26 @@ github.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicH github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= +github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= +github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= +github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -425,6 +453,8 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/microsoft/go-mssqldb v1.9.8 h1:d4IFMvF/o+HdpXUqbBfzHvn/NlFA75YGcfHUUvDFJEM= +github.com/microsoft/go-mssqldb v1.9.8/go.mod h1:eGSRSGAW4hKMy5YcAenhCDjIRm2rhqIdmmwgciMzLus= github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76 h1:KGuD/pM2JpL9FAYvBrnBBeENKZNh6eNtjqytV6TYjnk= github.com/minio/highwayhash v1.0.4-0.20251030100505-070ab1a87a76/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -474,6 +504,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= @@ -513,6 +545,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/bridges/prometheus v0.67.0 h1:dkBzNEAIKADEaFnuESzcXvpd09vxvDZsOjx11gjUqLk= diff --git a/secrets/hashivault/go.mod b/secrets/hashivault/go.mod index 4e844ca71a..1fc1708273 100644 --- a/secrets/hashivault/go.mod +++ b/secrets/hashivault/go.mod @@ -14,7 +14,7 @@ module gocloud.dev/secrets/hashivault -go 1.25.0 +go 1.25.7 require ( github.com/hashicorp/vault/api v1.22.0