Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions libs/go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,28 @@ factory, close, err := postgres.Open(ctx, readerURL, writerURL, authenticator,
postgres.WithExternalIdentity(declaredIssuer))
```

## One authenticated pool, for a service that owns its own tenancy

`OpenPool(ctx, connection, profile, options...)` opens one pool for one
capability and nothing else: the profile decides the transport and the pinned
authority, the options decide the credential, and the caller gets a pool whose
first connection has already succeeded. It grants no role, installs no session
policy and judges no privilege.

It exists because every other constructor here is opinionated. `Open` splits a
service across a reader and a writer and installs the scoped RLS factory;
`OpenMaintenance` selects an application role and refuses a privileged login. A
service with one runtime principal and its own authority over whose tenant a
call concerns fits neither, and so called `pgxpool.New` itself — which means it
also re-decided the transport, the trust anchor and the credential, each of
which this package already decides. This is the primitive underneath the other
two, exposed, so adopting the library is one call rather than a redesign.

`pgxpool` connects lazily, so `OpenPool` pings before returning: a wrong
credential, an unverifiable server or an unreachable address fails at boot,
where it is a deployment fault, rather than at the first query, where it reads
as a runtime one.

## External identity: the consuming service names no cloud

`WithExternalIdentity(declaration)` authenticates every pooled connection as the
Expand Down
70 changes: 70 additions & 0 deletions libs/go/pool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package postgres

import (
"context"
"errors"
"fmt"
"sync"

"github.com/jackc/pgx/v5/pgxpool"
)

// OpenPool opens one authenticated pool for one capability, and nothing else.
//
// It exists because every other constructor here is opinionated: Open splits a
// service across a reader and a writer and installs the scoped RLS factory,
// OpenMaintenance selects an application role and refuses a privileged login.
// A service that owns its own tenancy model — one runtime principal, its own
// authority on whose tenant a call is about — fits neither, and so wrote
// pgxpool.New itself. Four services in this fleet did, which is four copies of
// the transport and credential decisions this package already makes.
//
// So this is the primitive under the others, exposed: the profile decides the
// transport and the pinned authority, the options decide the credential, and the
// caller gets a pool whose first connection has already succeeded. It grants no
// role, installs no session policy and judges no privilege — a caller wanting
// those wants Open or OpenMaintenance.
//
// The returned closer is safe to call more than once.
func OpenPool(ctx context.Context, connection string, profile ConnectionProfile, options ...Option) (*pgxpool.Pool, func(), error) {
if ctx == nil {
return nil, nil, errors.New("Postgres context is required")
}
if connection == "" {
return nil, nil, errors.New("Postgres connection is required")
}
configuration, err := configured(options...)
if err != nil {
return nil, nil, err
}
// Resolved before the configuration is parsed, because whether a credential
// provider is installed decides whether a password in the URL is legal.
if err := resolveExternalIdentity(ctx, &configuration); err != nil {
return nil, nil, err
}
if configuration.operationTimeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, configuration.operationTimeout)
defer cancel()
}
poolConfig, err := ParseConnection(connection, profile, configuration.accessTokenProvider != nil)
if err != nil {
return nil, nil, fmt.Errorf("parse Postgres capability: %w", err)
}
if configuration.accessTokenProvider != nil {
installAccessTokenProvider(poolConfig, configuration.accessTokenProvider)
}
pool, err := pgxpool.NewWithConfig(ctx, poolConfig)
if err != nil {
return nil, nil, profileConnectionError(profile, fmt.Errorf("open Postgres capability: %w", err))
}
// pgxpool connects lazily, so without this the first failure of a wrong
// credential, an unverifiable server or an unreachable address would surface
// at the first query rather than at boot, where it belongs.
if err := pool.Ping(ctx); err != nil {
pool.Close()
return nil, nil, profileConnectionError(profile, fmt.Errorf("ping Postgres capability: %w", err))
}
var once sync.Once
return pool, func() { once.Do(pool.Close) }, nil
}
109 changes: 109 additions & 0 deletions libs/go/pool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package postgres

import (
"context"
"errors"
"strings"
"testing"
"time"
)

const poolTestHost = "store.identity.internal.example.com"

func TestOpenPoolRefusesAnIncompleteCall(t *testing.T) {
cleanProfileEnvironment(t)
dsn := "postgresql://writer@" + poolTestHost + "/records?sslmode=verify-full"
//nolint:staticcheck // a nil context is exactly what this asserts is refused
if _, _, err := OpenPool(nil, dsn, ConnectionProfile{Transport: VerifiedTLS}); err == nil {
t.Fatal("a nil context was accepted")
}
if _, _, err := OpenPool(t.Context(), "", ConnectionProfile{Transport: VerifiedTLS}); err == nil {
t.Fatal("an empty connection was accepted")
}
}

// Every refusal the opinionated constructors make about a capability's
// transport and credential must hold here too: this is the same decision, not a
// relaxed version of it reached by a shorter route.
func TestOpenPoolKeepsEveryTransportAndCredentialRefusal(t *testing.T) {
cleanProfileEnvironment(t)
anchorPEM, _, _ := authority(t, "Cloud SQL Server CA", time.Now().Add(time.Hour))
provider := func(context.Context, string) (string, error) { return "token", nil }

for name, call := range map[string]func() error{
"two credentials for one pool": func() error {
_, _, err := OpenPool(t.Context(), "postgresql://writer@"+poolTestHost+"/records?sslmode=verify-ca",
ConnectionProfile{Transport: PinnedAuthorityTLS, ServerTrustAnchorPEM: anchorPEM},
WithExternalIdentity(IssuerGCPCloudSQL), WithAccessTokenProvider(provider))
return err
},
"a password beside a credential provider": func() error {
_, _, err := OpenPool(t.Context(), "postgresql://writer:seeded@"+poolTestHost+"/records?sslmode=verify-full",
ConnectionProfile{Transport: VerifiedTLS}, WithAccessTokenProvider(provider))
return err
},
"a pinned transport with no anchor": func() error {
_, _, err := OpenPool(t.Context(), "postgresql://writer@"+poolTestHost+"/records?sslmode=verify-ca",
ConnectionProfile{Transport: PinnedAuthorityTLS})
return err
},
"an anchor on a transport that verifies nothing": func() error {
_, _, err := OpenPool(t.Context(), "postgresql://writer@"+poolTestHost+"/records?sslmode=disable",
ConnectionProfile{Transport: MeshProtected, ServerTrustAnchorPEM: anchorPEM})
return err
},
"an undeclared external identity": func() error {
_, _, err := OpenPool(t.Context(), "postgresql://writer@"+poolTestHost+"/records?sslmode=verify-full",
ConnectionProfile{Transport: VerifiedTLS}, WithExternalIdentity(""))
return err
},
"verify-full on a pinned transport": func() error {
_, _, err := OpenPool(t.Context(), "postgresql://writer@"+poolTestHost+"/records?sslmode=verify-full",
ConnectionProfile{Transport: PinnedAuthorityTLS, ServerTrustAnchorPEM: anchorPEM})
return err
},
} {
t.Run(name, func(t *testing.T) {
if err := call(); err == nil {
t.Fatal("accepted")
}
})
}
}

// pgxpool connects lazily. A pool handed back before its first connection
// succeeded moves a wrong credential, an unverifiable server or an unreachable
// address from boot to the first query, where it reads as a runtime fault.
func TestOpenPoolFailsAtBootRatherThanAtFirstQuery(t *testing.T) {
cleanProfileEnvironment(t)
pool, close, err := OpenPool(t.Context(),
"postgresql://writer@unresolvable.invalid/records?sslmode=verify-full",
ConnectionProfile{Transport: VerifiedTLS},
WithOperationTimeout(3*time.Second))
if err == nil {
close()
t.Fatal("a pool whose server does not resolve was returned as usable")
}
if pool != nil || close != nil {
t.Fatal("a failed open returned a pool or a closer")
}
// An explicit profile reports the capability as unavailable rather than
// handing back the driver's own text, which carries the binding.
if !errors.Is(err, ErrConnectionUnavailable) && !strings.Contains(err.Error(), "ping") {
t.Fatalf("unexpected error shape: %v", err)
}
}

// The legacy zero profile stays available for a caller not yet declaring a
// transport, so adoption is one call rather than one call plus a posture change.
func TestOpenPoolAcceptsTheLegacyZeroProfile(t *testing.T) {
cleanProfileEnvironment(t)
_, _, err := OpenPool(t.Context(), "postgresql://writer:local@127.0.0.1:1/records?sslmode=disable",
ConnectionProfile{}, WithOperationTimeout(3*time.Second))
if err == nil {
t.Fatal("a pool to a closed port was returned as usable")
}
if errors.Is(err, ErrConnectionProfile) {
t.Fatalf("the zero profile was rejected as a profile error: %v", err)
}
}
Loading