Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: add support for reading db connection string from a file#20910

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
rowansmithau wants to merge9 commits intomain
base:main
Choose a base branch
Loading
fromrowansmithau/feat/db-read-creds-from-file
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
9 commits
Select commitHold shift + click to select a range
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
26 changes: 16 additions & 10 deletionscli/resetpassword.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,7 +9,6 @@ import (

"cdr.dev/slog"
"cdr.dev/slog/sloggers/sloghuman"
"github.com/coder/coder/v2/coderd/database/awsiamrds"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/pretty"
"github.com/coder/serpent"
Expand All@@ -21,8 +20,9 @@ import (

func (*RootCmd) resetPassword() *serpent.Command {
var (
postgresURL string
postgresAuth string
postgresURL string
postgresURLFile string
postgresAuth string
)

root := &serpent.Command{
Expand All@@ -37,13 +37,13 @@ func (*RootCmd) resetPassword() *serpent.Command {
logger = logger.Leveled(slog.LevelDebug)
}

sqlDriver:="postgres"
if codersdk.PostgresAuth(postgresAuth) == codersdk.PostgresAuthAWSIAMRDS {
var err error
sqlDriver, err = awsiamrds.Register(inv.Context(), sqlDriver)
if err != nil {
return xerrors.Errorf("register aws rds iam auth: %w", err)
}
sqlDriver, postgresURL, err:=ResolvePostgresParams(inv.Context(), PostgresParams{
URL: postgresURL,
URLFile: postgresURLFile,
Auth: postgresAuth,
}, "postgres")
if err != nil {
return err
}

sqlDB, err := ConnectToPostgres(inv.Context(), logger, sqlDriver, postgresURL, nil)
Expand DownExpand Up@@ -106,6 +106,12 @@ func (*RootCmd) resetPassword() *serpent.Command {
Env: "CODER_PG_CONNECTION_URL",
Value: serpent.StringOf(&postgresURL),
},
{
Flag: "postgres-url-file",
Description: "Path to a file containing the URL of a PostgreSQL database. The file contents will be read and used as the connection URL. This is an alternative to --postgres-url for cases where the URL is stored in a file, such as a Docker or Kubernetes secret.",
Env: "CODER_PG_CONNECTION_URL_FILE",
Value: serpent.StringOf(&postgresURLFile),
},
serpent.Option{
Name: "Postgres Connection Auth",
Description: "Type of auth to use when connecting to postgres.",
Expand Down
78 changes: 78 additions & 0 deletionscli/server.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,6 +76,7 @@ import (
"github.com/coder/coder/v2/coderd/database/dbmetrics"
"github.com/coder/coder/v2/coderd/database/dbpurge"
"github.com/coder/coder/v2/coderd/database/migrations"
"github.com/coder/coder/v2/coderd/database/pgfileurl"
"github.com/coder/coder/v2/coderd/database/pubsub"
"github.com/coder/coder/v2/coderd/devtunnel"
"github.com/coder/coder/v2/coderd/entitlements"
Expand DownExpand Up@@ -433,6 +434,31 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
}
config := r.createConfig()

// If a postgres URL file is specified, register a driver that
// re-reads the file on each connection attempt. This supports
// credential rotation without restarting the server.
if vals.PostgresURLFile != "" {
if vals.PostgresURL != "" {
return xerrors.Errorf("cannot specify both --postgres-url and --postgres-url-file flags")
}
// Read the URL once for initial validation and migrations.
logger.Debug(ctx, "coderd: database connection URL sourced from file")
postgresURL, err := ReadPostgresURLFromFile(vals.PostgresURLFile.String())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

We need some sort of go sql driver BS so we can get this to be read on every single connection attempt. Reading it once on startup won't resolve the customer's problem.

if err != nil {
return err
}
if err := vals.PostgresURL.Set(postgresURL); err != nil {
return xerrors.Errorf("set database URL from file: %w", err)
}
// Register a driver that re-reads the file on each new connection.
sqlDriver, err = pgfileurl.Register(sqlDriver, vals.PostgresURLFile.String(), logger)
if err != nil {
return xerrors.Errorf("register database file URL driver: %w", err)
}
} else if vals.PostgresURL != "" {
logger.Debug(ctx, "database connection URL sourced from environment variable")
}

builtinPostgres := false
// Only use built-in if PostgreSQL URL isn't specified!
if vals.PostgresURL == "" {
Expand DownExpand Up@@ -2820,6 +2846,58 @@ func signalNotifyContext(ctx context.Context, inv *serpent.Invocation, sig ...os
return inv.SignalNotifyContext(ctx, sig...)
}

// ReadPostgresURLFromFile reads a PostgreSQL connection URL from a file. The
// file contents are trimmed of whitespace. This is useful for reading secrets
// from files, such as Docker or Kubernetes secrets.
func ReadPostgresURLFromFile(filePath string) (string, error) {
content, err := os.ReadFile(filePath)
if err != nil {
return "", xerrors.Errorf("read postgres URL file %q: %w", filePath, err)
}
return strings.TrimSpace(string(content)), nil
}

// PostgresParams holds parameters for resolving a postgres connection.
type PostgresParams struct {
// URL is the direct connection URL (from --postgres-url or CODER_PG_CONNECTION_URL).
URL string
// URLFile is the path to a file containing the connection URL
// (from --postgres-url-file or CODER_PG_CONNECTION_URL_FILE).
URLFile string
// Auth is the authentication method (e.g., "password" or "awsiamrds").
Auth string
}

// ResolvePostgresParams resolves the postgres connection URL and SQL driver.
// It handles mutual exclusion between URL and URLFile, reads the URL from file
// if specified, and registers the appropriate SQL driver based on the auth type.
// Returns the driver name and the resolved URL.
func ResolvePostgresParams(ctx context.Context, params PostgresParams, baseDriver string) (driver string, dbURL string, err error) {
dbURL = params.URL

// Handle mutual exclusion and file reading.
if params.URLFile != "" {
if params.URL != "" {
return "", "", xerrors.Errorf("cannot specify both --postgres-url and --postgres-url-file")
}
dbURL, err = ReadPostgresURLFromFile(params.URLFile)
if err != nil {
return "", "", err
}
}

// Register the appropriate driver.
driver = baseDriver
if codersdk.PostgresAuth(params.Auth) == codersdk.PostgresAuthAWSIAMRDS {
driver, err = awsiamrds.Register(ctx, driver)
if err != nil {
return "", "", xerrors.Errorf("register aws rds iam auth: %w", err)
}
}

return driver, dbURL, nil
}

func getAndMigratePostgresDB(ctx context.Context, logger slog.Logger, postgresURL string, auth codersdk.PostgresAuth, sqlDriver string) (*sql.DB, string, error) {
dbURL, err := escapePostgresURLUserInfo(postgresURL)
if err != nil {
Expand Down
25 changes: 16 additions & 9 deletionscli/server_createadminuser.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,6 @@ import (
"cdr.dev/slog/sloggers/sloghuman"
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/awsiamrds"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/gitsshkey"
"github.com/coder/coder/v2/coderd/httpapi"
Expand All@@ -26,6 +25,7 @@ import (
func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
var (
newUserDBURL string
newUserDBURLFile string
newUserPgAuth string
newUserSSHKeygenAlgorithm string
newUserUsername string
Expand All@@ -52,6 +52,15 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
ctx, cancel := inv.SignalNotifyContext(ctx, StopSignals...)
defer cancel()

sqlDriver, newUserDBURL, err := ResolvePostgresParams(ctx, PostgresParams{
URL: newUserDBURL,
URLFile: newUserDBURLFile,
Auth: newUserPgAuth,
}, "postgres")
if err != nil {
return err
}

if newUserDBURL == "" {
cliui.Infof(inv.Stdout, "Using built-in PostgreSQL (%s)", cfg.PostgresPath())
url, closePg, err := startBuiltinPostgres(ctx, cfg, logger, "")
Expand All@@ -64,14 +73,6 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
newUserDBURL = url
}

sqlDriver := "postgres"
if codersdk.PostgresAuth(newUserPgAuth) == codersdk.PostgresAuthAWSIAMRDS {
sqlDriver, err = awsiamrds.Register(inv.Context(), sqlDriver)
if err != nil {
return xerrors.Errorf("register aws rds iam auth: %w", err)
}
}

sqlDB, err := ConnectToPostgres(ctx, logger, sqlDriver, newUserDBURL, nil)
if err != nil {
return xerrors.Errorf("connect to postgres: %w", err)
Expand DownExpand Up@@ -257,6 +258,12 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
Description: "URL of a PostgreSQL database. If empty, the built-in PostgreSQL deployment will be used (Coder must not be already running in this case).",
Value: serpent.StringOf(&newUserDBURL),
},
serpent.Option{
Env: "CODER_PG_CONNECTION_URL_FILE",
Flag: "postgres-url-file",
Description: "Path to a file containing the URL of a PostgreSQL database. The file contents will be read and used as the connection URL. This is an alternative to --postgres-url for cases where the URL is stored in a file, such as a Docker or Kubernetes secret.",
Value: serpent.StringOf(&newUserDBURLFile),
},
serpent.Option{
Name: "Postgres Connection Auth",
Description: "Type of auth to use when connecting to postgres.",
Expand Down
40 changes: 40 additions & 0 deletionscli/server_internal_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,8 @@ import (
"bytes"
"context"
"crypto/tls"
"os"
"path/filepath"
"testing"

"github.com/spf13/pflag"
Expand DownExpand Up@@ -372,3 +374,41 @@ func TestEscapePostgresURLUserInfo(t *testing.T) {
})
}
}

func TestReadPostgresURLFromFile(t *testing.T) {
t.Parallel()

t.Run("ReadsFile", func(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
filePath := filepath.Join(tmpDir, "pg_url")
expectedURL := "postgres://user:pass@localhost:5432/db"
err := os.WriteFile(filePath, []byte(expectedURL), 0o600)
require.NoError(t, err)

url, err := ReadPostgresURLFromFile(filePath)
require.NoError(t, err)
require.Equal(t, expectedURL, url)
})

t.Run("TrimsWhitespace", func(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
filePath := filepath.Join(tmpDir, "pg_url")
expectedURL := "postgres://user:pass@localhost:5432/db"
// Write with leading/trailing whitespace and newlines
err := os.WriteFile(filePath, []byte(" \n"+expectedURL+"\n \n"), 0o600)
require.NoError(t, err)

url, err := ReadPostgresURLFromFile(filePath)
require.NoError(t, err)
require.Equal(t, expectedURL, url)
})

t.Run("FileNotFound", func(t *testing.T) {
t.Parallel()
_, err := ReadPostgresURLFromFile("/nonexistent/path/to/file")
require.Error(t, err)
require.Contains(t, err.Error(), "read postgres URL file")
})
}
30 changes: 18 additions & 12 deletionscli/server_regenerate_vapid_keypair.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,16 +12,16 @@ import (

"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/awsiamrds"
"github.com/coder/coder/v2/coderd/webpush"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)

func (r *RootCmd) newRegenerateVapidKeypairCommand() *serpent.Command {
var (
regenVapidKeypairDBURL string
regenVapidKeypairPgAuth string
regenVapidKeypairDBURL string
regenVapidKeypairDBURLFile string
regenVapidKeypairPgAuth string
)
regenerateVapidKeypairCommand := &serpent.Command{
Use: "regenerate-vapid-keypair",
Expand All@@ -39,6 +39,15 @@ func (r *RootCmd) newRegenerateVapidKeypairCommand() *serpent.Command {

defer cancel()

sqlDriver, regenVapidKeypairDBURL, err := ResolvePostgresParams(ctx, PostgresParams{
URL: regenVapidKeypairDBURL,
URLFile: regenVapidKeypairDBURLFile,
Auth: regenVapidKeypairPgAuth,
}, "postgres")
if err != nil {
return err
}

if regenVapidKeypairDBURL == "" {
cliui.Infof(inv.Stdout, "Using built-in PostgreSQL (%s)", cfg.PostgresPath())
url, closePg, err := startBuiltinPostgres(ctx, cfg, logger, "")
Expand All@@ -51,15 +60,6 @@ func (r *RootCmd) newRegenerateVapidKeypairCommand() *serpent.Command {
regenVapidKeypairDBURL = url
}

sqlDriver := "postgres"
var err error
if codersdk.PostgresAuth(regenVapidKeypairPgAuth) == codersdk.PostgresAuthAWSIAMRDS {
sqlDriver, err = awsiamrds.Register(inv.Context(), sqlDriver)
if err != nil {
return xerrors.Errorf("register aws rds iam auth: %w", err)
}
}

sqlDB, err := ConnectToPostgres(ctx, logger, sqlDriver, regenVapidKeypairDBURL, nil)
if err != nil {
return xerrors.Errorf("connect to postgres: %w", err)
Expand DownExpand Up@@ -98,6 +98,12 @@ func (r *RootCmd) newRegenerateVapidKeypairCommand() *serpent.Command {
Description: "URL of a PostgreSQL database. If empty, the built-in PostgreSQL deployment will be used (Coder must not be already running in this case).",
Value: serpent.StringOf(&regenVapidKeypairDBURL),
},
serpent.Option{
Env: "CODER_PG_CONNECTION_URL_FILE",
Flag: "postgres-url-file",
Description: "Path to a file containing the URL of a PostgreSQL database. The file contents will be read and used as the connection URL. This is an alternative to --postgres-url for cases where the URL is stored in a file, such as a Docker or Kubernetes secret.",
Value: serpent.StringOf(&regenVapidKeypairDBURLFile),
},
serpent.Option{
Name: "Postgres Connection Auth",
Description: "Type of auth to use when connecting to postgres.",
Expand Down
6 changes: 6 additions & 0 deletionscli/testdata/coder_reset-password_--help.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,5 +12,11 @@ OPTIONS:
--postgres-url string, $CODER_PG_CONNECTION_URL
URL of a PostgreSQL database to connect to.

--postgres-url-file string, $CODER_PG_CONNECTION_URL_FILE
Path to a file containing the URL of a PostgreSQL database. The file
contents will be read and used as the connection URL. This is an
alternative to --postgres-url for cases where the URL is stored in a
file, such as a Docker or Kubernetes secret.

———
Run `coder --help` for a list of global options.
6 changes: 6 additions & 0 deletionscli/testdata/coder_server_--help.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,12 @@ OPTIONS:
server postgres-builtin-url". Note that any special characters in the
URL must be URL-encoded.

--postgres-url-file string, $CODER_PG_CONNECTION_URL_FILE
Path to a file containing the URL of a PostgreSQL database. The file
contents will be read and used as the connection URL. This is an
alternative to --postgres-url for cases where the URL is stored in a
file, such as a Docker or Kubernetes secret.

--ssh-keygen-algorithm string, $CODER_SSH_KEYGEN_ALGORITHM (default: ed25519)
The algorithm to use for generating ssh keys. Accepted values are
"ed25519", "ecdsa", or "rsa4096".
Expand Down
6 changes: 6 additions & 0 deletionscli/testdata/coder_server_create-admin-user_--help.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,12 @@ OPTIONS:
deployment will be used (Coder must not be already running in this
case).

--postgres-url-file string, $CODER_PG_CONNECTION_URL_FILE
Path to a file containing the URL of a PostgreSQL database. The file
contents will be read and used as the connection URL. This is an
alternative to --postgres-url for cases where the URL is stored in a
file, such as a Docker or Kubernetes secret.

--raw-url bool
Output the raw connection URL instead of a psql command.

Expand Down
6 changes: 6 additions & 0 deletionscli/testdata/server-config.yaml.golden
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -470,6 +470,12 @@ cacheDir: [cache dir]
# temporary directory and deleted when the server is stopped.
# (default: <unset>, type: bool)
ephemeralDeployment: false
# Path to a file containing the URL of a PostgreSQL database. The file contents
# will be read and used as the connection URL. This is an alternative to
# --postgres-url for cases where the URL is stored in a file, such as a Docker or
# Kubernetes secret.
# (default: <unset>, type: string)
pgConnectionURLFile: ""
# Type of auth to use when connecting to postgres. For AWS RDS, using IAM
# authentication (awsiamrds) is recommended.
# (default: password, type: enum[password\|awsiamrds])
Expand Down
3 changes: 3 additions & 0 deletionscoderd/apidoc/docs.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

3 changes: 3 additions & 0 deletionscoderd/apidoc/swagger.json
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp