Create dev org in database (#41)

These changes create a dev org in the database on startup.

They also plumb through a dev-org-id flag that allows specifying a chosen org ID in dev mode.

The logic will allow us to let people specify a default org ID in the config file too, although currently that's not enabled.

Several other improvements were made as well:

*    Consolidation of some code from across the server commands
*    Plumbing shutdownch through so we can use it for canceling contexts during startup
This commit is contained in:
Jeff Mitchell authored and GitHub committed 2020-05-11 17:03:23 -04:00
1 parent 670abaed88
commit 062ce6079f
7 files changed
+150 -63

No files matched your search

+72 -4
View File
@@ -25,7 +25,9 @@ import (
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/watchtower/globals"
"github.com/hashicorp/watchtower/internal/db"
"github.com/hashicorp/watchtower/internal/iam"
"github.com/hashicorp/watchtower/version"
"github.com/jinzhu/gorm"
"github.com/mitchellh/cli"
"golang.org/x/net/http/httpproxy"
"google.golang.org/grpc/grpclog"
@@ -34,6 +36,8 @@ import (
)
type Server struct {
*Command
InfoKeys []string
Info map[string]string
@@ -57,12 +61,17 @@ type Server struct {
Listeners []*ServerListener
DefaultOrgId string
DevDatabaseUrl string
DevDatabaseCleanupFunc func() error
Database *gorm.DB
}
func NewServer() *Server {
func NewServer(cmd *Command) *Server {
return &Server{
Command: cmd,
InfoKeys: make([]string, 0, 20),
Info: make(map[string]string),
SecureRandomReader: rand.Reader,
@@ -355,11 +364,11 @@ func (b *Server) RunShutdownFuncs(ui cli.Ui) {
}
}
func (b *Server) CreateDevDatabase() error {
c, url, err := db.InitDbInDocker("postgres")
func (b *Server) CreateDevDatabase(dialect string) error {
c, url, err := db.InitDbInDocker(dialect)
if err != nil {
c()
return fmt.Errorf("unable to start dev database: %w", err)
return fmt.Errorf("unable to start dev database with dialect %s: %w", dialect, err)
}
b.DevDatabaseCleanupFunc = c
@@ -367,10 +376,69 @@ func (b *Server) CreateDevDatabase() error {
b.InfoKeys = append(b.InfoKeys, "dev database url")
b.Info["dev database url"] = b.DevDatabaseUrl
dbase, err := gorm.Open(dialect, url)
if err != nil {
c()
return fmt.Errorf("unable to create db object with dialect %s: %w", dialect, err)
}
b.Database = dbase
rw := &db.Db{Tx: b.Database}
repo, err := iam.NewRepository(rw, rw, b.ControllerKMS)
if err != nil {
c()
return fmt.Errorf("unable to create repo for org id: %w", err)
}
ctx, cancel := context.WithCancel(context.Background())
go func() {
<-b.ShutdownCh
cancel()
}()
var scope *iam.Scope
if b.DefaultOrgId != "" {
scope, err = repo.LookupScope(ctx, iam.WithPublicId(b.DefaultOrgId))
if err != nil {
c()
return fmt.Errorf("error looking up existing scope with org ID %q: %w", b.DefaultOrgId, err)
}
if scope != nil {
goto INFO
}
}
scope, err = iam.NewOrganization(iam.WithPublicId(b.DefaultOrgId))
if err != nil {
c()
return fmt.Errorf("error creating new org scope: %w", err)
}
scope, err = repo.CreateScope(ctx, scope)
if err != nil {
c()
return fmt.Errorf("error persisting new org scope: %w", err)
}
if b.DefaultOrgId != "" {
if scope.GetPublicId() != b.DefaultOrgId {
c()
return fmt.Errorf("expected org ID %q, got %q after persisting", b.DefaultOrgId, scope.GetPublicId())
}
} else {
b.DefaultOrgId = scope.GetPublicId()
}
INFO:
b.InfoKeys = append(b.InfoKeys, "dev org id")
b.Info["dev org id"] = b.DefaultOrgId
return nil
}
func (b *Server) DestroyDevDatabase() error {
if b.Database != nil {
b.Database.Close()
}
if b.DevDatabaseCleanupFunc != nil {
return b.DevDatabaseCleanupFunc()
}
+6 -6
View File
@@ -37,30 +37,30 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
Commands = map[string]cli.CommandFactory{
"controller": func() (cli.Command, error) {
return &controller.Command{
Command: &base.Command{
Server: base.NewServer(&base.Command{
UI: serverCmdUi,
ShutdownCh: MakeShutdownCh(),
},
}),
SighupCh: MakeSighupCh(),
SigUSR2Ch: MakeSigUSR2Ch(),
}, nil
},
"worker": func() (cli.Command, error) {
return &worker.Command{
Command: &base.Command{
Server: base.NewServer(&base.Command{
UI: serverCmdUi,
ShutdownCh: MakeShutdownCh(),
},
}),
SighupCh: MakeSighupCh(),
SigUSR2Ch: MakeSigUSR2Ch(),
}, nil
},
"dev": func() (cli.Command, error) {
return &dev.Command{
Command: &base.Command{
Server: base.NewServer(&base.Command{
UI: serverCmdUi,
ShutdownCh: MakeShutdownCh(),
},
}),
SighupCh: MakeSighupCh(),
SigUSR2Ch: MakeSigUSR2Ch(),
}, nil
+28 -4
View File
@@ -22,7 +22,6 @@ var _ cli.Command = (*Command)(nil)
var _ cli.CommandAutocomplete = (*Command)(nil)
type Command struct {
*base.Command
*base.Server
ExtShutdownCh chan struct{}
@@ -42,6 +41,7 @@ type Command struct {
flagDevAdminPassword string
flagDevControllerAPIListenAddr string
flagDevControllerClusterListenAddr string
flagDevOrgId string
flagCombineLogs bool
}
@@ -105,6 +105,15 @@ func (c *Command) Flags() *base.FlagSets {
"production.",
})
f.StringVar(&base.StringVar{
Name: "dev-org-id",
Target: &c.flagDevOrgId,
Default: "",
EnvVar: "WATCHTWER_DEV_ORG_ID",
Usage: "Auto-created organization ID. This only applies when running in \"dev\" " +
"mode.",
})
f.StringVar(&base.StringVar{
Name: "dev-admin-password",
Target: &c.flagDevAdminPassword,
@@ -147,7 +156,6 @@ func (c *Command) AutocompleteFlags() complete.Flags {
}
func (c *Command) Run(args []string) int {
c.Server = base.NewServer()
c.CombineLogs = c.flagCombineLogs
if result := c.ParseFlagsAndConfig(args); result > 0 {
@@ -244,7 +252,7 @@ func (c *Command) Run(args []string) int {
}
if c.flagDev {
if err := c.CreateDevDatabase(); err != nil {
if err := c.CreateDevDatabase("postgres"); err != nil {
c.UI.Error(fmt.Errorf("Error creating dev database container: %s", err.Error()).Error())
return 1
}
@@ -296,10 +304,14 @@ func (c *Command) ParseFlagsAndConfig(args []string) int {
} else {
c.Config, err = config.DevController()
if err != nil {
c.UI.Error(fmt.Errorf("Error creating dev config: %s", err).Error())
c.UI.Error(fmt.Errorf("Error creating dev config: %w", err).Error())
return 1
}
if c.flagDevOrgId != "" {
c.Config.DefaultOrgId = c.flagDevOrgId
}
for _, l := range c.Config.Listeners {
if len(l.Purpose) != 1 {
continue
@@ -318,6 +330,18 @@ func (c *Command) ParseFlagsAndConfig(args []string) int {
}
}
if c.Config.DefaultOrgId != "" {
if !strings.HasPrefix(c.Config.DefaultOrgId, "o_") {
c.UI.Error(fmt.Sprintf("Invalid default org ID, must start with %q", "o_"))
return 1
}
if len(c.Config.DefaultOrgId) != 12 {
c.UI.Error(fmt.Sprintf("Invalid default org ID, must be 10 base62 characters after %q", "o_"))
return 1
}
c.DefaultOrgId = c.Config.DefaultOrgId
}
return 0
}
+27 -42
View File
@@ -18,7 +18,6 @@ var _ cli.Command = (*Command)(nil)
var _ cli.CommandAutocomplete = (*Command)(nil)
type Command struct {
*base.Command
*base.Server
SighupCh chan struct{}
@@ -33,6 +32,7 @@ type Command struct {
flagLogFormat string
flagDev bool
flagDevAdminPassword string
flagDevOrgId string
flagDevControllerAPIListenAddr string
flagDevControllerClusterListenAddr string
flagCombineLogs bool
@@ -81,6 +81,15 @@ func (c *Command) Flags() *base.FlagSets {
f = set.NewFlagSet("Dev Options")
f.StringVar(&base.StringVar{
Name: "dev-org-id",
Target: &c.flagDevOrgId,
Default: "",
EnvVar: "WATCHTWER_DEV_ORG_ID",
Usage: "Auto-created organization ID. This only applies when running in \"dev\" " +
"mode.",
})
f.StringVar(&base.StringVar{
Name: "dev-admin-password",
Target: &c.flagDevAdminPassword,
@@ -123,7 +132,6 @@ func (c *Command) AutocompleteFlags() complete.Flags {
}
func (c *Command) Run(args []string) int {
c.Server = base.NewServer()
c.CombineLogs = c.flagCombineLogs
var err error
@@ -139,9 +147,12 @@ func (c *Command) Run(args []string) int {
devConfig, err := config.DevController()
if err != nil {
c.UI.Error(fmt.Errorf("Error creating controller dev config: %s", err).Error())
c.UI.Error(fmt.Errorf("Error creating controller dev config: %w", err).Error())
return 1
}
if c.flagDevOrgId != "" {
devConfig.DefaultOrgId = c.flagDevOrgId
}
for _, l := range devConfig.Listeners {
if len(l.Purpose) != 1 {
@@ -160,6 +171,18 @@ func (c *Command) Run(args []string) int {
}
}
if devConfig.DefaultOrgId != "" {
if !strings.HasPrefix(devConfig.DefaultOrgId, "o_") {
c.UI.Error(fmt.Sprintf("Invalid default org ID, must start with %q", "o_"))
return 1
}
if len(devConfig.DefaultOrgId) != 12 {
c.UI.Error(fmt.Sprintf("Invalid default org ID, must be 10 base62 characters after %q", "o_"))
return 1
}
c.DefaultOrgId = devConfig.DefaultOrgId
}
if err := c.SetupLogging(c.flagLogLevel, c.flagLogFormat, "", ""); err != nil {
c.UI.Error(err.Error())
return 1
@@ -197,45 +220,9 @@ func (c *Command) Run(args []string) int {
return 1
}
/*
// If we're in Dev mode, then initialize the core
if c.flagDev && !c.flagDevSkipInit {
init, err := c.enableDev(core, coreConfig)
if err != nil {
c.UI.Error(fmt.Sprintf("Error initializing Dev mode: %w", err))
return 1
}
// Print the big dev mode warning!
c.UI.Warn(base.WrapAtLength(
"WARNING! dev mode is enabled! In this mode, Watchtower runs entirely " +
"in-memory and all state is lost upon shutdown."))
c.UI.Warn("")
c.UI.Warn("You may need to set the following environment variable:")
c.UI.Warn("")
endpointURL := "http://" + config.Listeners[0].Config["address"].(string)
if runtime.GOOS == "windows" {
c.UI.Warn("PowerShell:")
c.UI.Warn(fmt.Sprintf(" $env:WATCHTOWER_ADDR=\"%s\"", endpointURL))
c.UI.Warn("cmd.exe:")
c.UI.Warn(fmt.Sprintf(" set WATCHTOWER_ADDR=%s", endpointURL))
} else {
c.UI.Warn(fmt.Sprintf(" $ export VAULT_ADDR='%s'", endpointURL))
}
c.UI.Warn(fmt.Sprintf("Root Token: %s", init.RootToken))
c.UI.Warn("")
c.UI.Warn(base.WrapAtLength(
"Development mode should NOT be used in production installations!"))
c.UI.Warn("")
}
*/
defer c.RunShutdownFuncs(c.UI)
if err := c.CreateDevDatabase(); err != nil {
if err := c.CreateDevDatabase("postgres"); err != nil {
c.UI.Error(fmt.Errorf("Error creating dev database container: %w", err).Error())
return 1
}
@@ -251,7 +238,6 @@ func (c *Command) Run(args []string) int {
c.childSighupCh = append(c.childSighupCh, controllerSighupCh)
devController := &controllercmd.Command{
Command: c.Command,
Server: c.Server,
ExtShutdownCh: childShutdownCh,
SighupCh: controllerSighupCh,
@@ -265,7 +251,6 @@ func (c *Command) Run(args []string) int {
workerSighupCh := make(chan struct{})
c.childSighupCh = append(c.childSighupCh, workerSighupCh)
devWorker := &workercmd.Command{
Command: c.Command,
Server: c.Server,
ExtShutdownCh: childShutdownCh,
SighupCh: workerSighupCh,
-2
View File
@@ -20,7 +20,6 @@ var _ cli.Command = (*Command)(nil)
var _ cli.CommandAutocomplete = (*Command)(nil)
type Command struct {
*base.Command
*base.Server
ExtShutdownCh chan struct{}
@@ -127,7 +126,6 @@ func (c *Command) AutocompleteFlags() complete.Flags {
}
func (c *Command) Run(args []string) int {
c.Server = base.NewServer()
c.CombineLogs = c.flagCombineLogs
if result := c.ParseFlagsAndConfig(args); result > 0 {
+2 -1
View File
@@ -63,7 +63,8 @@ listener "tcp" {
type Config struct {
*configutil.SharedConfig `hcl:"-"`
DevController bool `hcl:"-"`
DevController bool `hcl:"-"`
DefaultOrgId string `hcl:"default_org_id"`
}
// DevWorker is a Config that is used for dev mode of Watchtower
+15 -4
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"strings"
"github.com/hashicorp/watchtower/internal/db"
"github.com/hashicorp/watchtower/internal/iam/store"
@@ -62,6 +63,7 @@ func NewProject(organizationPublicId string, opt ...Option) (*Scope, error) {
// WithScope specifies the Scope's parent
func newScope(scopeType ScopeType, opt ...Option) (*Scope, error) {
opts := getOpts(opt...)
withPublicId := opts.withPublicId
withName := opts.withName
withScope := opts.withScope
withDescription := opts.withDescription
@@ -81,11 +83,20 @@ func newScope(scopeType ScopeType, opt ...Option) (*Scope, error) {
if scopeType == OrganizationScope {
prefix = "o"
}
publicId, err := db.NewPublicId(prefix)
if err != nil {
return nil, fmt.Errorf("error generating public id %w for new scope", err)
var publicId string
if withPublicId != "" {
if !strings.HasPrefix(withPublicId, prefix+"_") {
return nil, errors.New("passed-in public ID has wrong prefix for type")
}
publicId = withPublicId
} else {
var err error
publicId, err = db.NewPublicId(prefix)
if err != nil {
return nil, fmt.Errorf("error generating public id %w for new scope", err)
}
}
s := &Scope{
Scope: &store.Scope{
PublicId: publicId,