Add database URL. (#365)

* Add database URL.

This allows specification of either a file://, env://, or other string.
If it starts with file:// or env:// the actual value will be read from
those resources.

* Tidy up the KMS output

* Also run migrations in test mode when specifying the URL
pull/366/head
Jeff Mitchell 6 years ago committed by GitHub
parent c2b49dbc7d
commit c4e2b88022
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -349,7 +349,10 @@ func (b *Server) SetupKMSes(ui cli.Ui, config *config.Config) error {
kmsLogger := b.Logger.ResetNamed(fmt.Sprintf("kms-%s-%s", purpose, kms.Type)) kmsLogger := b.Logger.ResetNamed(fmt.Sprintf("kms-%s-%s", purpose, kms.Type))
origPurpose := kms.Purpose
kms.Purpose = []string{purpose}
wrapper, wrapperConfigError := configutil.ConfigureWrapper(kms, &b.InfoKeys, &b.Info, kmsLogger) wrapper, wrapperConfigError := configutil.ConfigureWrapper(kms, &b.InfoKeys, &b.Info, kmsLogger)
kms.Purpose = origPurpose
if wrapperConfigError != nil { if wrapperConfigError != nil {
if !errwrap.ContainsType(wrapperConfigError, new(logical.KeyNotFoundError)) { if !errwrap.ContainsType(wrapperConfigError, new(logical.KeyNotFoundError)) {
return fmt.Errorf( return fmt.Errorf(

@ -9,6 +9,7 @@ import (
"github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/cmd/config"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/servers/controller" "github.com/hashicorp/boundary/internal/servers/controller"
"github.com/hashicorp/boundary/sdk/strutil" "github.com/hashicorp/boundary/sdk/strutil"
"github.com/hashicorp/boundary/sdk/wrapper" "github.com/hashicorp/boundary/sdk/wrapper"
@ -304,10 +305,29 @@ func (c *Command) Run(args []string) int {
} }
} }
if err := c.CreateDevDatabase("postgres", opts...); err != nil { if err := c.CreateDevDatabase("postgres", opts...); err != nil {
c.UI.Error(fmt.Errorf("Error creating dev database container: %s", err.Error()).Error()) c.UI.Error(fmt.Errorf("Error creating dev database container: %w", err).Error())
return 1 return 1
} }
c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase) c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase)
} else {
if c.Config.Database == nil || c.Config.Database.Url == "" {
c.UI.Error(`"url" not specified in "database" config block"`)
return 1
}
dbaseUrl, err := config.ParseAddress(c.Config.Database.Url)
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 := db.InitStore("postgres", nil, c.DatabaseUrl); err != nil {
c.UI.Error(fmt.Errorf("Error running database migrations: %w", err).Error())
return 1
}
if err := c.ConnectToDatabase("postgres"); err != nil {
c.UI.Error(fmt.Errorf("Error connecting to database: %w", err).Error())
return 1
}
} }
defer func() { defer func() {

@ -4,9 +4,13 @@ import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"encoding/base64" "encoding/base64"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/url"
"os"
"strings"
wrapping "github.com/hashicorp/go-kms-wrapping" wrapping "github.com/hashicorp/go-kms-wrapping"
"github.com/hashicorp/hcl" "github.com/hashicorp/hcl"
@ -91,6 +95,7 @@ type Config struct {
PassthroughDirectory string `hcl:"-"` PassthroughDirectory string `hcl:"-"`
Worker *Worker `hcl:"worker"` Worker *Worker `hcl:"worker"`
Controller *Controller `hcl:"controller"` Controller *Controller `hcl:"controller"`
Database *Database `hcl:"database"`
} }
type Controller struct { type Controller struct {
@ -107,6 +112,10 @@ type Worker struct {
Controllers []string `hcl:"controllers"` Controllers []string `hcl:"controllers"`
} }
type Database struct {
Url string `hcl:"url"`
}
// DevWorker is a Config that is used for dev mode of Boundary // DevWorker is a Config that is used for dev mode of Boundary
// workers // workers
func DevWorker() (*Config, error) { func DevWorker() (*Config, error) {
@ -235,3 +244,37 @@ func (c *Config) Sanitized() map[string]interface{} {
return result return result
} }
var ErrNotAUrl = errors.New("not a url")
// ParseAddress parses a URL with schemes file://, env://, or any other.
// Depending on the scheme it will return specific types of data:
//
// * file:// will return a string with the file's contents * env:// will return
// a string with the env var's contents * anything else will return the string
// as it was
//
// On error, we return the original string along with the error. The caller can
// switch on ErrNotAUrl to understand whether it was the parsing step that
// errored or something else. This is useful to attempt to read a non-URL string
// from some resource, but where the original input may simply be a valid string
// of that type.
func ParseAddress(addr string) (string, error) {
addr = strings.TrimSpace(addr)
parsed, err := url.Parse(addr)
if err != nil {
return addr, ErrNotAUrl
}
switch parsed.Scheme {
case "file":
contents, err := ioutil.ReadFile(strings.TrimPrefix(addr, "file://"))
if err != nil {
return addr, fmt.Errorf("error reading file at %s: %w", addr, err)
}
return string(contents), nil
case "env":
return os.Getenv(strings.TrimPrefix(addr, "env://")), nil
}
return addr, nil
}

@ -139,24 +139,30 @@ func InitStore(dialect string, cleanup func() error, url string) error {
source, err := migrations.NewMigrationSource(dialect) source, err := migrations.NewMigrationSource(dialect)
if err != nil { if err != nil {
mErr = multierror.Append(mErr, fmt.Errorf("error creating migration driver: %w", err)) mErr = multierror.Append(mErr, fmt.Errorf("error creating migration driver: %w", err))
if err := cleanup(); err != nil { if cleanup != nil {
mErr = multierror.Append(mErr, fmt.Errorf("error cleaning up from creating driver: %w", err)) if err := cleanup(); err != nil {
mErr = multierror.Append(mErr, fmt.Errorf("error cleaning up from creating driver: %w", err))
}
} }
return mErr.ErrorOrNil() return mErr.ErrorOrNil()
} }
m, err := migrate.NewWithSourceInstance("httpfs", source, url) m, err := migrate.NewWithSourceInstance("httpfs", source, url)
if err != nil { if err != nil {
mErr = multierror.Append(mErr, fmt.Errorf("error creating migrations: %w", err)) mErr = multierror.Append(mErr, fmt.Errorf("error creating migrations: %w", err))
if err := cleanup(); err != nil { if cleanup != nil {
mErr = multierror.Append(mErr, fmt.Errorf("error cleaning up from creating migrations: %w", err)) if err := cleanup(); err != nil {
mErr = multierror.Append(mErr, fmt.Errorf("error cleaning up from creating migrations: %w", err))
}
} }
return mErr.ErrorOrNil() return mErr.ErrorOrNil()
} }
if err := m.Up(); err != nil && err != migrate.ErrNoChange { if err := m.Up(); err != nil && err != migrate.ErrNoChange {
mErr = multierror.Append(mErr, fmt.Errorf("error running migrations: %w", err)) mErr = multierror.Append(mErr, fmt.Errorf("error running migrations: %w", err))
if err := cleanup(); err != nil { if cleanup != nil {
mErr = multierror.Append(mErr, fmt.Errorf("error cleaning up from running migrations: %w", err)) if err := cleanup(); err != nil {
mErr = multierror.Append(mErr, fmt.Errorf("error cleaning up from running migrations: %w", err))
}
} }
return mErr.ErrorOrNil() return mErr.ErrorOrNil()
} }

@ -11,6 +11,7 @@ import (
"github.com/hashicorp/boundary/api/authtokens" "github.com/hashicorp/boundary/api/authtokens"
"github.com/hashicorp/boundary/internal/cmd/base" "github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/cmd/config"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/servers"
"github.com/hashicorp/go-hclog" "github.com/hashicorp/go-hclog"
@ -343,6 +344,9 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController {
if opts.DatabaseUrl != "" { if opts.DatabaseUrl != "" {
tc.b.DatabaseUrl = opts.DatabaseUrl tc.b.DatabaseUrl = opts.DatabaseUrl
if err := db.InitStore("postgres", nil, tc.b.DatabaseUrl); err != nil {
t.Fatal(err)
}
if err := tc.b.ConnectToDatabase("postgres"); err != nil { if err := tc.b.ConnectToDatabase("postgres"); err != nil {
t.Fatal(err) t.Fatal(err)
} }

Loading…
Cancel
Save