From bec3d702fb6a69164877950c314cc50273e006b6 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 25 Sep 2020 17:36:47 -0400 Subject: [PATCH] Add -database-url to dev mode (#459) Create default dev mode login name of "admin" and password of "password" --- internal/cmd/base/servers.go | 38 ++++++++---- internal/cmd/commands/dev/dev.go | 59 +++++++++++++------ internal/db/db.go | 15 +---- internal/oplog/testing.go | 9 +-- .../getting-started/installing/production.mdx | 5 +- .../content/docs/installing/production.mdx | 5 +- 6 files changed, 79 insertions(+), 52 deletions(-) diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index d6902750e4..b20efd3a7b 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -441,23 +441,35 @@ func (b *Server) ConnectToDatabase(dialect string) error { func (b *Server) CreateDevDatabase(dialect string, opt ...Option) error { opts := getOpts(opt...) - c, url, container, err := db.InitDbInDocker(dialect) - // In case of an error, run the cleanup function. If we pass all errors, c should be set to a noop - // function before returning from this method - defer func() { - if !opts.withSkipDatabaseDestruction { - if err := c(); err != nil { - b.Logger.Error("error cleaning up docker container", "error", err) + var container, url string + var err error + var c func() error + + switch b.DatabaseUrl { + case "": + c, url, container, err = db.InitDbInDocker(dialect) + // In case of an error, run the cleanup function. If we pass all errors, c should be set to a noop + // function before returning from this method + defer func() { + if !opts.withSkipDatabaseDestruction { + if err := c(); err != nil { + b.Logger.Error("error cleaning up docker container", "error", err) + } } + }() + + if err != nil { + return fmt.Errorf("unable to start dev database with dialect %s: %w", dialect, err) } - }() - if err != nil { - return fmt.Errorf("unable to start dev database with dialect %s: %w", dialect, err) - } + b.DevDatabaseCleanupFunc = c + b.DatabaseUrl = url - b.DevDatabaseCleanupFunc = c - b.DatabaseUrl = url + default: + if _, err := db.InitStore(dialect, c, b.DatabaseUrl); err != nil { + return fmt.Errorf("error initializing store: %w", err) + } + } b.InfoKeys = append(b.InfoKeys, "dev database url") b.Info["dev database url"] = b.DatabaseUrl diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 169d4a0d8c..c0ed687ace 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -51,6 +51,7 @@ type Command struct { flagWorkerPublicAddr string flagPassthroughDirectory string flagRecoveryKey string + flagDatabaseUrl string flagDisableDatabaseDestruction bool } @@ -102,17 +103,19 @@ func (c *Command) Flags() *base.FlagSets { }) f.StringVar(&base.StringVar{ - Name: "password", - Target: &c.flagPassword, - EnvVar: "BOUNDARY_DEV_PASSWORD", - Usage: "Initial login password.", + Name: "password", + Target: &c.flagPassword, + Default: "password", + EnvVar: "BOUNDARY_DEV_PASSWORD", + Usage: "Initial login password.", }) f.StringVar(&base.StringVar{ - Name: "login-name", - Target: &c.flagLoginName, - EnvVar: "BOUNDARY_DEV_LOGIN_NAME", - Usage: "Initial admin login name.", + Name: "login-name", + Target: &c.flagLoginName, + Default: "admin", + EnvVar: "BOUNDARY_DEV_LOGIN_NAME", + Usage: "Initial admin login name.", }) f.StringVar(&base.StringVar{ @@ -199,6 +202,12 @@ func (c *Command) Flags() *base.FlagSets { Usage: "Specifies the base64'd 256-bit AES key to use for recovery operations", }) + f.StringVar(&base.StringVar{ + Name: "database-url", + Target: &c.flagDatabaseUrl, + Usage: `If set, specifies the URL used to connect to the database for initialization (otherwise a Docker container will be started). This can refer to a file on disk (file://) from which a URL will be read; an env var (env://) from which the URL will be read; or a direct database URL.`, + }) + return set } @@ -358,16 +367,30 @@ func (c *Command) Run(args []string) int { } }() - var opts []base.Option - if c.flagDisableDatabaseDestruction { - opts = append(opts, base.WithSkipDatabaseDestruction()) - } - if err := c.CreateDevDatabase("postgres", opts...); err != nil { - c.UI.Error(fmt.Errorf("Error creating dev database container: %w", err).Error()) - return 1 - } - if !c.flagDisableDatabaseDestruction { - c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase) + switch c.flagDatabaseUrl { + case "": + var opts []base.Option + if c.flagDisableDatabaseDestruction { + opts = append(opts, base.WithSkipDatabaseDestruction()) + } + if err := c.CreateDevDatabase("postgres", opts...); err != nil { + c.UI.Error(fmt.Errorf("Error creating dev database container: %w", err).Error()) + return 1 + } + if !c.flagDisableDatabaseDestruction { + c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase) + } + default: + dbaseUrl, err := config.ParseAddress(c.flagDatabaseUrl) + if err != nil && err != config.ErrNotAUrl { + c.UI.Error(fmt.Errorf("Error parsing database url: %w", err).Error()) + return 1 + } + c.DatabaseUrl = strings.TrimSpace(dbaseUrl) + if err := c.CreateDevDatabase("postgres"); err != nil { + c.UI.Error(fmt.Errorf("Error connecting to database: %w", err).Error()) + return 1 + } } c.PrintInfo(c.UI) diff --git a/internal/db/db.go b/internal/db/db.go index 3ff84775ec..8f07fe65ef 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -60,17 +60,8 @@ func Migrate(connectionUrl string, migrationsDirectory string) error { return nil } -// InitDbInDocker initializes the data store within docker or an existing PG_URL +// InitDbInDocker initializes the data store within docker or an existing func InitDbInDocker(dialect string) (cleanup func() error, retURL, container string, err error) { - switch dialect { - case "postgres": - if os.Getenv("PG_URL") != "" { - if _, err := InitStore(dialect, func() error { return nil }, os.Getenv("PG_URL")); err != nil { - return func() error { return nil }, os.Getenv("PG_URL"), "", fmt.Errorf("error initializing store: %w", err) - } - return func() error { return nil }, os.Getenv("PG_URL"), "", nil - } - } c, url, container, err := StartDbInDocker(dialect) if err != nil { return func() error { return nil }, "", "", fmt.Errorf("could not start docker: %w", err) @@ -99,8 +90,8 @@ func StartDbInDocker(dialect string) (cleanup func() error, retURL, container st var url string switch dialect { case "postgres": - resource, err = pool.Run("postgres", "latest", []string{"POSTGRES_PASSWORD=secret", "POSTGRES_DB=boundary"}) - url = "postgres://postgres:secret@localhost:%s?sslmode=disable" + resource, err = pool.Run("postgres", "12", []string{"POSTGRES_PASSWORD=password", "POSTGRES_DB=boundary"}) + url = "postgres://postgres:password@localhost:%s?sslmode=disable" default: panic(fmt.Sprintf("unknown dialect %q", dialect)) } diff --git a/internal/oplog/testing.go b/internal/oplog/testing.go index 9f962a29b1..7bc4977187 100644 --- a/internal/oplog/testing.go +++ b/internal/oplog/testing.go @@ -4,7 +4,6 @@ import ( "crypto/rand" "database/sql" "fmt" - "os" "strings" "testing" @@ -58,14 +57,10 @@ func testId(t *testing.T) string { // testInitDbInDocker initializes postgres within dockertest for the unit tests func testInitDbInDocker(t *testing.T) (cleanup func(), retURL string, err error) { t.Helper() - if os.Getenv("PG_URL") != "" { - testInitStore(t, func() {}, os.Getenv("PG_URL")) - return func() {}, os.Getenv("PG_URL"), nil - } pool, err := dockertest.NewPool("") require.NoErrorf(t, err, "could not connect to docker: %w", err) - resource, err := pool.Run("postgres", "latest", []string{"POSTGRES_PASSWORD=secret", "POSTGRES_DB=boundary"}) + resource, err := pool.Run("postgres", "12", []string{"POSTGRES_PASSWORD=password", "POSTGRES_DB=boundary"}) require.NoErrorf(t, err, "could not start resource: %w", err) c := func() { @@ -73,7 +68,7 @@ func testInitDbInDocker(t *testing.T) (cleanup func(), retURL string, err error) assert.NoError(t, err) } - url := fmt.Sprintf("postgres://postgres:secret@localhost:%s?sslmode=disable", resource.GetPort("5432/tcp")) + url := fmt.Sprintf("postgres://postgres:password@localhost:%s?sslmode=disable", resource.GetPort("5432/tcp")) err = pool.Retry(func() error { db, err := sql.Open("postgres", url) diff --git a/website/content/docs/getting-started/installing/production.mdx b/website/content/docs/getting-started/installing/production.mdx index 92c6756cc7..c0f2cabda4 100644 --- a/website/content/docs/getting-started/installing/production.mdx +++ b/website/content/docs/getting-started/installing/production.mdx @@ -124,7 +124,10 @@ kms "aead" { key_id = "global_recovery" } -# Database URL for postgres, can be overridden with PG_URL +# Database URL for postgres. This can be a direct "postgres://" +# URL, or it can be "file://" to read the contents of a file to +# supply the url, or "env://" to name an environment variable +# that contains the URL. database { url = "postgresql://boundary:boundarydemo@${aws_db_instance.boundary.endpoint}/boundary" } diff --git a/website/content/docs/installing/production.mdx b/website/content/docs/installing/production.mdx index 637a8731da..1c6930c24b 100644 --- a/website/content/docs/installing/production.mdx +++ b/website/content/docs/installing/production.mdx @@ -126,7 +126,10 @@ kms "aead" { key_id = "global_recovery" } -# Database URL for postgres, can be overridden with PG_URL +# Database URL for postgres. This can be a direct "postgres://" +# URL, or it can be "file://" to read the contents of a file to +# supply the url, or "env://" to name an environment variable +# that contains the URL. database { url = "postgresql://boundary:boundarydemo@${aws_db_instance.boundary.endpoint}/boundary" }