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:
1 parent
670abaed88
commit
062ce6079f
7 files changed
+150
-63
No files matched your search
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user