Allow not destroying dev databases (#366)
This also shifts some logic around to better prepare for non-dev-mode workloads, although it does not actually call the KMS creation functions and initial auth method creation from non-dev commands yet.
This commit is contained in:
1 parent
c4e2b88022
commit
37e9fed2e3
5 files changed
+124
-50
No files matched your search
@@ -461,6 +461,28 @@ func (b *Server) CreateDevDatabase(dialect string, opt ...Option) error {
|
||||
|
||||
b.Database.LogMode(true)
|
||||
|
||||
if err := b.CreateGlobalKmsKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if opts.withSkipAuthMethodCreation {
|
||||
// now that we have passed all the error cases, reset c to be a noop so the
|
||||
// defer doesn't do anything.
|
||||
c = func() error { return nil }
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := b.CreateInitialAuthMethod(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// now that we have passed all the error cases, reset c to be a noop so the
|
||||
// defer doesn't do anything.
|
||||
c = func() error { return nil }
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Server) CreateGlobalKmsKeys() error {
|
||||
rw := db.New(b.Database)
|
||||
|
||||
kmsRepo, err := kms.NewRepository(rw, rw)
|
||||
@@ -488,11 +510,24 @@ func (b *Server) CreateDevDatabase(dialect string, opt ...Option) error {
|
||||
return fmt.Errorf("error creating global scope kms keys: %w", err)
|
||||
}
|
||||
|
||||
if opts.withSkipAuthMethodCreation {
|
||||
// now that we have passed all the error cases, reset c to be a noop so the
|
||||
// defer doesn't do anything.
|
||||
c = func() error { return nil }
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Server) CreateInitialAuthMethod() error {
|
||||
rw := db.New(b.Database)
|
||||
|
||||
kmsRepo, err := kms.NewRepository(rw, rw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating kms repository: %w", err)
|
||||
}
|
||||
kmsCache, err := kms.NewKms(kmsRepo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating kms cache: %w", err)
|
||||
}
|
||||
if err := kmsCache.AddExternalWrappers(
|
||||
kms.WithRootWrapper(b.RootKms),
|
||||
); err != nil {
|
||||
return fmt.Errorf("error adding config keys to kms: %w", err)
|
||||
}
|
||||
|
||||
// Create the dev auth method
|
||||
@@ -504,46 +539,53 @@ func (b *Server) CreateDevDatabase(dialect string, opt ...Option) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating new in memory auth method: %w", err)
|
||||
}
|
||||
amId := b.DevAuthMethodId
|
||||
if amId == "" {
|
||||
amId = "ampw_1234567890"
|
||||
if b.DevAuthMethodId == "" {
|
||||
b.DevAuthMethodId, err = db.NewPublicId(password.AuthMethodPrefix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating dev auth method id: %w", err)
|
||||
}
|
||||
}
|
||||
_, err = pwRepo.CreateAuthMethod(ctx, authMethod, password.WithPublicId(amId))
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
<-b.ShutdownCh
|
||||
cancel()
|
||||
}()
|
||||
|
||||
_, err = pwRepo.CreateAuthMethod(ctx, authMethod, password.WithPublicId(b.DevAuthMethodId))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error saving auth method to the db: %w", err)
|
||||
}
|
||||
b.InfoKeys = append(b.InfoKeys, "dev auth method id")
|
||||
b.Info["dev auth method id"] = amId
|
||||
b.InfoKeys = append(b.InfoKeys, "generated auth method id")
|
||||
b.Info["generated auth method id"] = b.DevAuthMethodId
|
||||
|
||||
// Create the dev user
|
||||
acctLoginName := b.DevLoginName
|
||||
if acctLoginName == "" {
|
||||
acctLoginName, err = base62.Random(10)
|
||||
if b.DevLoginName == "" {
|
||||
b.DevLoginName, err = base62.Random(10)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate dev login name: %w", err)
|
||||
return fmt.Errorf("unable to generate login name: %w", err)
|
||||
}
|
||||
acctLoginName = strings.ToLower(acctLoginName)
|
||||
b.DevLoginName = strings.ToLower(b.DevLoginName)
|
||||
}
|
||||
pw := b.DevPassword
|
||||
if pw == "" {
|
||||
pw, err = base62.Random(20)
|
||||
if b.DevPassword == "" {
|
||||
b.DevPassword, err = base62.Random(20)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate dev password: %w", err)
|
||||
return fmt.Errorf("unable to generate password: %w", err)
|
||||
}
|
||||
}
|
||||
b.InfoKeys = append(b.InfoKeys, "dev password")
|
||||
b.Info["dev password"] = pw
|
||||
b.InfoKeys = append(b.InfoKeys, "generated password")
|
||||
b.Info["generated password"] = b.DevPassword
|
||||
|
||||
acct, err := password.NewAccount(amId, password.WithLoginName(acctLoginName))
|
||||
acct, err := password.NewAccount(b.DevAuthMethodId, password.WithLoginName(b.DevLoginName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating new in memory auth account: %w", err)
|
||||
}
|
||||
acct, err = pwRepo.CreateAccount(ctx, scope.Global.String(), acct, password.WithPassword(pw))
|
||||
acct, err = pwRepo.CreateAccount(ctx, scope.Global.String(), acct, password.WithPassword(b.DevPassword))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error saving auth account to the db: %w", err)
|
||||
}
|
||||
b.InfoKeys = append(b.InfoKeys, "dev login name")
|
||||
b.Info["dev login name"] = acct.GetLoginName()
|
||||
b.InfoKeys = append(b.InfoKeys, "generated login name")
|
||||
b.Info["generated login name"] = acct.GetLoginName()
|
||||
|
||||
// Create a role tying them together
|
||||
iamRepo, err := iam.NewRepository(rw, rw, kmsCache, iam.WithRandomReader(b.SecureRandomReader))
|
||||
@@ -552,24 +594,21 @@ func (b *Server) CreateDevDatabase(dialect string, opt ...Option) error {
|
||||
}
|
||||
pr, err := iam.NewRole(scope.Global.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating in memory role for default dev grants: %w", err)
|
||||
return fmt.Errorf("error creating in memory role for generated grants: %w", err)
|
||||
}
|
||||
pr.Name = "Dev Mode Global Scope Admin Role"
|
||||
pr.Name = "Generated Global Scope Admin Role"
|
||||
pr.Description = `Provides admin grants to all authenticated users within the "global" scope`
|
||||
defPermsRole, err := iamRepo.CreateRole(ctx, pr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating role for default dev grants: %w", err)
|
||||
return fmt.Errorf("error creating role for default generated grants: %w", err)
|
||||
}
|
||||
if _, err := iamRepo.AddRoleGrants(ctx, defPermsRole.PublicId, defPermsRole.Version, []string{"id=*;actions=*"}); err != nil {
|
||||
return fmt.Errorf("error creating grant for default dev grants: %w", err)
|
||||
return fmt.Errorf("error creating grant for default generated grants: %w", err)
|
||||
}
|
||||
if _, err := iamRepo.AddPrincipalRoles(ctx, defPermsRole.PublicId, defPermsRole.Version+1, []string{"u_auth"}, nil); err != nil {
|
||||
return fmt.Errorf("error adding principal to role for default dev grants: %w", err)
|
||||
return fmt.Errorf("error adding principal to role for default generated grants: %w", err)
|
||||
}
|
||||
|
||||
// now that we have passed all the error cases, reset c to be a noop so the
|
||||
// defer doesn't do anything.
|
||||
c = func() error { return nil }
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ type Command struct {
|
||||
flagDevControllerAPIListenAddr string
|
||||
flagDevControllerClusterListenAddr string
|
||||
flagDevSkipAuthMethodCreation bool
|
||||
flagDevDisableDatabaseDestruction bool
|
||||
}
|
||||
|
||||
func (c *Command) Synopsis() string {
|
||||
@@ -124,6 +125,12 @@ func (c *Command) Flags() *base.FlagSets {
|
||||
Usage: "If set, an auth method will not be created as part of the dev instance. The recovery KMS will be needed to perform any actions.",
|
||||
})
|
||||
|
||||
f.BoolVar(&base.BoolVar{
|
||||
Name: "dev-disable-database-destruction",
|
||||
Target: &c.flagDevDisableDatabaseDestruction,
|
||||
Usage: "If set, if a database is created automatically in Docker, it will not be removed when the dev server is shut down.",
|
||||
})
|
||||
|
||||
f.BoolVar(&base.BoolVar{
|
||||
Name: "combine-logs",
|
||||
Target: &c.flagCombineLogs,
|
||||
@@ -266,7 +273,9 @@ func (c *Command) Run(args []string) int {
|
||||
c.UI.Error(fmt.Errorf("Error creating dev database container: %w", err).Error())
|
||||
return 1
|
||||
}
|
||||
c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase)
|
||||
if !c.flagDevDisableDatabaseDestruction {
|
||||
c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase)
|
||||
}
|
||||
|
||||
c.PrintInfo(c.UI, "dev mode")
|
||||
c.ReleaseLogGate()
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
func TestAuthenticationHandler(t *testing.T) {
|
||||
c := NewTestController(t, &TestControllerOpts{
|
||||
DisableAuthorizationFailures: true,
|
||||
DefaultAuthMethodId: "ampw_1234567890",
|
||||
DefaultLoginName: "admin",
|
||||
DefaultPassword: "password123",
|
||||
})
|
||||
|
||||
@@ -194,9 +194,11 @@ func (tc *TestController) Shutdown() {
|
||||
if err := tc.b.RunShutdownFuncs(); err != nil {
|
||||
tc.t.Error(err)
|
||||
}
|
||||
if tc.b.DestroyDevDatabase() != nil {
|
||||
if err := tc.b.DestroyDevDatabase(); err != nil {
|
||||
tc.t.Error(err)
|
||||
if !tc.opts.DisableDatabaseDestruction {
|
||||
if tc.b.DestroyDevDatabase() != nil {
|
||||
if err := tc.b.DestroyDevDatabase(); err != nil {
|
||||
tc.t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,6 +225,10 @@ type TestControllerOpts struct {
|
||||
// database
|
||||
DisableDatabaseCreation bool
|
||||
|
||||
// DisableDatabaseDestruction can be set true to allow a database to be
|
||||
// created but examined after-the-fact
|
||||
DisableDatabaseDestruction bool
|
||||
|
||||
// If set, instead of creating a dev database, it will connect to an
|
||||
// existing database given the url
|
||||
DatabaseUrl string
|
||||
@@ -350,6 +356,14 @@ func NewTestController(t *testing.T, opts *TestControllerOpts) *TestController {
|
||||
if err := tc.b.ConnectToDatabase("postgres"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := tc.b.CreateGlobalKmsKeys(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !opts.DisableAuthMethodCreation {
|
||||
if err := tc.b.CreateInitialAuthMethod(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
} else if !opts.DisableDatabaseCreation {
|
||||
var createOpts []base.Option
|
||||
if opts.DisableAuthMethodCreation {
|
||||
|
||||
@@ -39,18 +39,19 @@ func getOpts(opt ...Option) (*controller.TestControllerOpts, error) {
|
||||
}
|
||||
|
||||
type option struct {
|
||||
tcOptions *controller.TestControllerOpts
|
||||
setWithConfigFile bool
|
||||
setWithConfigText bool
|
||||
setDisableAuthMethodCreation bool
|
||||
setDisableDatabaseCreation bool
|
||||
setDefaultAuthMethodId bool
|
||||
setDefaultLoginName bool
|
||||
setDefaultPassword bool
|
||||
setRootKms bool
|
||||
setWorkerAuthKms bool
|
||||
setRecoveryKms bool
|
||||
setDatabaseUrl bool
|
||||
tcOptions *controller.TestControllerOpts
|
||||
setWithConfigFile bool
|
||||
setWithConfigText bool
|
||||
setDisableAuthMethodCreation bool
|
||||
setDisableDatabaseCreation bool
|
||||
setDisableDatabaseDesctruction bool
|
||||
setDefaultAuthMethodId bool
|
||||
setDefaultLoginName bool
|
||||
setDefaultPassword bool
|
||||
setRootKms bool
|
||||
setWorkerAuthKms bool
|
||||
setRecoveryKms bool
|
||||
setDatabaseUrl bool
|
||||
}
|
||||
|
||||
type Option func(*option) error
|
||||
@@ -108,6 +109,16 @@ func DisableDatabaseCreation() Option {
|
||||
}
|
||||
}
|
||||
|
||||
// DisableDatabaseCreation skips creating a database in docker and allows one to
|
||||
// be provided through a tcOptions.
|
||||
func DisableDatabaseDestruction() Option {
|
||||
return func(c *option) error {
|
||||
c.setDisableDatabaseDestruction = true
|
||||
c.tcOptions.DisableDatabaseDestruction = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDefaultAuthMethodId(id string) Option {
|
||||
return func(c *option) error {
|
||||
c.setDefaultAuthMethodId = true
|
||||
|
||||
Reference in new issue
Block a user