diff --git a/internal/cmd/commands/database/flags.go b/internal/cmd/commands/database/flags.go new file mode 100644 index 0000000000..1e4ecc4f2d --- /dev/null +++ b/internal/cmd/commands/database/flags.go @@ -0,0 +1,150 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package database + +import "fmt" + +const ( + flagConfigName = "config" + flagConfigKmsName = "config-kms" + flagLogLevelName = "log-level" + flagLogFormatName = "log-format" + flagSkipInitialLoginRoleName = "skip-initial-login-role-creation" + flagSkipInitialAuthenticatedUserRoleName = "skip-initial-authenticated-user-role-creation" + flagSkipAuthMethodName = "skip-auth-method-creation" + flagSkipScopesName = "skip-scopes-creation" + flagSkipHostResourcesName = "skip-host-resources-creation" + flagSkipTargetName = "skip-target-creation" + flagMigrationUrlName = "migration-url" +) + +// initFlags contains the flags for the database init command, including optional +// flags to control which initialization steps are skipped and the reasons for skipping. +type initFlags struct { + flagConfig []string + flagConfigKms string + flagLogLevel string + flagLogFormat string + flagMigrationUrl string + flagSkipInitialLoginRoleCreation bool + flagSkipInitialAuthenticatedUserRoleCreation bool + flagSkipAuthMethodCreation bool + flagSkipScopesCreation bool + flagSkipHostResourcesCreation bool + flagSkipTargetCreation bool +} + +// SkipInitialLoginRoleCreation checks if the creation of the initial anonymous login role should be skipped. +// It returns a boolean indicating whether to skip and a reason string if skipping is applicable. +// +// The creation of the initial login role is skipped if: +// - The flag for skipping the creation of the initial anonymous user role (`-skip-initial-login-role-creation`) is provided. +// +// Returns: +// - bool: True if the creation should be skipped, false otherwise. +// - string: A reason string explaining why the creation is skipped, or an empty string if not skipped. +func (f *initFlags) SkipInitialLoginRoleCreation() (bool, string) { + if f.flagSkipInitialLoginRoleCreation { + return true, reasonFlagWasSet(flagSkipInitialLoginRoleName) + } + return false, "" +} + +// SkipInitialAuthenticatedUserRoleCreation checks if the creation of the initial authenticated user role should be skipped. +// It returns a boolean indicating whether to skip and a reason string explaining why the creation is skipped. +// +// The creation of the initial authenticated user role is skipped if: +// - The flag for skipping the creation of the initial authenticated user role (`-skip-initial-authenticated-user-role-creation`) is provided. +// +// Returns: +// - bool: True if the creation of the initial authenticated user role should be skipped, false otherwise. +// - string: A reason string explaining why the creation is skipped, or an empty string if not skipped. +func (f *initFlags) SkipInitialAuthenticatedUserRoleCreation() (bool, string) { + if f.flagSkipInitialAuthenticatedUserRoleCreation { + return true, reasonFlagWasSet(flagSkipInitialAuthenticatedUserRoleName) + } + return false, "" +} + +// SkipAuthMethodCreation checks if the creation of authentication methods should be skipped. +// It returns a boolean indicating whether to skip and a reason string explaining why the creation is skipped. +// +// Authentication method creation is skipped if: +// - The flag for skipping authentication method creation (`-skip-auth-method-creation`) is provided. +// +// Returns: +// - bool: True if the creation of authentication methods should be skipped, false otherwise. +// - string: A reason string explaining why the creation is skipped, or an empty string if not skipped. +func (f *initFlags) SkipAuthMethodCreation() (bool, string) { + if f.flagSkipAuthMethodCreation { + return true, reasonFlagWasSet(flagSkipAuthMethodName) + } + return false, "" +} + +// SkipScopesCreation checks if the creation of default scopes should be skipped. +// It returns a boolean indicating whether to skip and a reason string explaining why the creation is skipped. +// +// Scopes creation is skipped if: +// - The flag for skipping scopes creation (`-skip-scopes-creation`) is provided. +// +// Returns: +// - bool: True if the creation of scopes should be skipped, false otherwise. +// - string: A reason string explaining why the creation is skipped, or an empty string if not skipped. +func (f *initFlags) SkipScopesCreation() (bool, string) { + if f.flagSkipScopesCreation { + return true, reasonFlagWasSet(flagSkipScopesName) + } + return false, "" +} + +// SkipHostResourcesCreation checks if the creation of default host resources should be skipped. +// It returns a boolean indicating whether to skip and a reason string explaining why the creation is skipped. +// +// Host resources creation is skipped if: +// - The flag for skipping host resources creation (`-skip-host-resources-creation`) is provided. +// - Any of the dependent resources were skipped. +// +// Dependent resources include: +// - Scopes creation +// +// Returns: +// - bool: True if the creation of host resources should be skipped, false otherwise. +// - string: A reason string explaining why the creation is skipped, or an empty string if not skipped. +func (f *initFlags) SkipHostResourcesCreation() (bool, string) { + if f.flagSkipHostResourcesCreation { + return true, reasonFlagWasSet(flagSkipHostResourcesName) + } + return f.SkipScopesCreation() +} + +// SkipTargetCreation checks if the creation of default targets should be skipped. +// It returns a boolean indicating whether to skip and a reason string explaining why the creation is skipped. +// +// Target creation is skipped if: +// - The flag for skipping target creation (`-skip-target-creation`) is provided. +// - Any of the dependent resources were skipped. +// +// Dependent resources include: +// - Host resources creation +// - Scopes creation +// +// Returns: +// - bool: True if the creation of targets should be skipped, false otherwise. +// - string: A reason string explaining why the creation is skipped, or an empty string if not skipped. +func (f *initFlags) SkipTargetCreation() (bool, string) { + if f.flagSkipTargetCreation { + return true, reasonFlagWasSet(flagSkipTargetName) + } + if skip, reason := f.SkipHostResourcesCreation(); skip { + return true, reason + } + return f.SkipScopesCreation() +} + +// reasonFlagWasSet is a helper function that generates a reason string dynamically +// based on the provided flag name. +func reasonFlagWasSet(fn string) string { + return fmt.Sprintf("flag `-%s` was set", fn) +} diff --git a/internal/cmd/commands/database/flags_test.go b/internal/cmd/commands/database/flags_test.go new file mode 100644 index 0000000000..befa49e735 --- /dev/null +++ b/internal/cmd/commands/database/flags_test.go @@ -0,0 +1,225 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package database + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestInitFlags_SkipInitialLoginRoleCreation(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + flags *initFlags + expectedSkip bool + expectedReason string + }{ + { + name: "SkipInitialLoginRoleCreation is true", + flags: &initFlags{flagSkipInitialLoginRoleCreation: true}, + expectedSkip: true, + expectedReason: "flag `-skip-initial-login-role-creation` was set", + }, + { + name: "SkipInitialLoginRoleCreation is false", + flags: &initFlags{flagSkipInitialLoginRoleCreation: false}, + expectedSkip: false, + expectedReason: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + skip, reason := tt.flags.SkipInitialLoginRoleCreation() + assert.Equal(t, tt.expectedSkip, skip) + assert.Equal(t, tt.expectedReason, reason) + }) + } +} + +func TestInitFlags_SkipInitialAuthenticatedUserRoleCreation(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + flags *initFlags + expectedSkip bool + expectedReason string + }{ + { + name: "SkipInitialAuthenticatedUserRoleCreation is true", + flags: &initFlags{flagSkipInitialAuthenticatedUserRoleCreation: true}, + expectedSkip: true, + expectedReason: "flag `-skip-initial-authenticated-user-role-creation` was set", + }, + { + name: "SkipInitialAuthenticatedUserRoleCreation is false", + flags: &initFlags{flagSkipInitialAuthenticatedUserRoleCreation: false}, + expectedSkip: false, + expectedReason: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + skip, reason := tt.flags.SkipInitialAuthenticatedUserRoleCreation() + assert.Equal(t, tt.expectedSkip, skip) + assert.Equal(t, tt.expectedReason, reason) + }) + } +} + +func TestInitFlags_SkipAuthMethodCreation(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + flags *initFlags + expectedSkip bool + expectedReason string + }{ + { + name: "SkipAuthMethodCreation is true", + flags: &initFlags{flagSkipAuthMethodCreation: true}, + expectedSkip: true, + expectedReason: "flag `-skip-auth-method-creation` was set", + }, + { + name: "SkipAuthMethodCreation is false", + flags: &initFlags{flagSkipAuthMethodCreation: false}, + expectedSkip: false, + expectedReason: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + skip, reason := tt.flags.SkipAuthMethodCreation() + assert.Equal(t, tt.expectedSkip, skip) + assert.Equal(t, tt.expectedReason, reason) + }) + } +} + +func TestInitFlags_SkipScopesCreation(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + flags *initFlags + expectedSkip bool + expectedReason string + }{ + { + name: "SkipScopesCreation is true", + flags: &initFlags{flagSkipScopesCreation: true}, + expectedSkip: true, + expectedReason: "flag `-skip-scopes-creation` was set", + }, + { + name: "SkipScopesCreation is false", + flags: &initFlags{flagSkipScopesCreation: false}, + expectedSkip: false, + expectedReason: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + skip, reason := tt.flags.SkipScopesCreation() + assert.Equal(t, tt.expectedSkip, skip) + assert.Equal(t, tt.expectedReason, reason) + }) + } +} + +func TestInitFlags_SkipHostResourcesCreation(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + flags *initFlags + expectedSkip bool + expectedReason string + }{ + { + name: "SkipHostResourcesCreation is true", + flags: &initFlags{flagSkipHostResourcesCreation: true}, + expectedSkip: true, + expectedReason: "flag `-skip-host-resources-creation` was set", + }, + { + name: "SkipScopesCreation is true", + flags: &initFlags{flagSkipScopesCreation: true}, + expectedSkip: true, + expectedReason: "flag `-skip-scopes-creation` was set", + }, + { + name: "No flags set", + flags: &initFlags{}, + expectedSkip: false, + expectedReason: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + skip, reason := tt.flags.SkipHostResourcesCreation() + assert.Equal(t, tt.expectedSkip, skip) + assert.Equal(t, tt.expectedReason, reason) + }) + } +} + +func TestInitFlags_SkipTargetCreation(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + flags *initFlags + expectedSkip bool + expectedReason string + }{ + { + name: "SkipTargetCreation is true", + flags: &initFlags{flagSkipTargetCreation: true}, + expectedSkip: true, + expectedReason: "flag `-skip-target-creation` was set", + }, + { + name: "SkipHostResourcesCreation is true", + flags: &initFlags{flagSkipHostResourcesCreation: true}, + expectedSkip: true, + expectedReason: "flag `-skip-host-resources-creation` was set", + }, + { + name: "SkipScopesCreation is true", + flags: &initFlags{flagSkipScopesCreation: true}, + expectedSkip: true, + expectedReason: "flag `-skip-scopes-creation` was set", + }, + { + name: "No flags set", + flags: &initFlags{}, + expectedSkip: false, + expectedReason: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + skip, reason := tt.flags.SkipTargetCreation() + assert.Equal(t, tt.expectedSkip, skip) + assert.Equal(t, tt.expectedReason, reason) + }) + } +} diff --git a/internal/cmd/commands/database/init.go b/internal/cmd/commands/database/init.go index 486b918cc6..66f2197840 100644 --- a/internal/cmd/commands/database/init.go +++ b/internal/cmd/commands/database/init.go @@ -32,22 +32,20 @@ type InitCommand struct { Config *config.Config - // This will be intialized, if needed, in ParseFlagsAndConfig when + // This will be initialized, if needed, in ParseFlagsAndConfig when // instantiating a config wrapper, if requested. It's then called as a // deferred function on the Run method. configWrapperCleanupFunc func() error - flagConfig []string - flagConfigKms string - flagLogLevel string - flagLogFormat string - flagMigrationUrl string - flagSkipInitialLoginRoleCreation bool - flagSkipInitialAuthenticatedUserRoleCreation bool - flagSkipAuthMethodCreation bool - flagSkipScopesCreation bool - flagSkipHostResourcesCreation bool - flagSkipTargetCreation bool + // initFlags is a struct that holds initialization flags for various operations. + // + // New flags should be added as fields to this struct rather than creating new + // standalone fields elsewhere. + // + // For optional flags, helper functions should also be added to determine if + // specific configurations should be skipped and why, such as whether a flag + // is enabled or a dependency is skipped. + initFlags initFlags } func (c *InitCommand) Synopsis() string { @@ -85,8 +83,8 @@ func (c *InitCommand) Flags() *base.FlagSets { f := set.NewFlagSet("Command Options") f.StringSliceVar(&base.StringSliceVar{ - Name: "config", - Target: &c.flagConfig, + Name: flagConfigName, + Target: &c.initFlags.flagConfig, Completion: complete.PredictOr( complete.PredictFiles("*.hcl"), complete.PredictFiles("*.json"), @@ -95,8 +93,8 @@ func (c *InitCommand) Flags() *base.FlagSets { }) f.StringVar(&base.StringVar{ - Name: "config-kms", - Target: &c.flagConfigKms, + Name: flagConfigKmsName, + Target: &c.initFlags.flagConfigKms, Completion: complete.PredictOr( complete.PredictFiles("*.hcl"), complete.PredictFiles("*.json"), @@ -105,8 +103,8 @@ func (c *InitCommand) Flags() *base.FlagSets { }) f.StringVar(&base.StringVar{ - Name: "log-level", - Target: &c.flagLogLevel, + Name: flagLogLevelName, + Target: &c.initFlags.flagLogLevel, EnvVar: "BOUNDARY_LOG_LEVEL", Completion: complete.PredictSet("trace", "debug", "info", "warn", "err"), Usage: "Log verbosity level. Supported values (in order of more detail to less) are " + @@ -114,8 +112,8 @@ func (c *InitCommand) Flags() *base.FlagSets { }) f.StringVar(&base.StringVar{ - Name: "log-format", - Target: &c.flagLogFormat, + Name: flagLogFormatName, + Target: &c.initFlags.flagLogFormat, Completion: complete.PredictSet("standard", "json"), Usage: `Log format. Supported values are "standard" and "json".`, }) @@ -123,44 +121,44 @@ func (c *InitCommand) Flags() *base.FlagSets { f = set.NewFlagSet("Init Options") f.BoolVar(&base.BoolVar{ - Name: "skip-initial-login-role-creation", - Target: &c.flagSkipInitialLoginRoleCreation, + Name: flagSkipInitialLoginRoleName, + Target: &c.initFlags.flagSkipInitialLoginRoleCreation, Usage: "If set, a role providing necessary grants for logging in will not be created as part of initialization. If set, the recovery KMS will be needed to perform any actions.", }) f.BoolVar(&base.BoolVar{ - Name: "skip-initial-authenticated-user-role-creation", - Target: &c.flagSkipInitialAuthenticatedUserRoleCreation, + Name: flagSkipInitialAuthenticatedUserRoleName, + Target: &c.initFlags.flagSkipInitialAuthenticatedUserRoleCreation, Usage: "If set, a role providing initial grants for any authenticated user will not be created as part of initialization.", }) f.BoolVar(&base.BoolVar{ - Name: "skip-auth-method-creation", - Target: &c.flagSkipAuthMethodCreation, + Name: flagSkipAuthMethodName, + Target: &c.initFlags.flagSkipAuthMethodCreation, Usage: "If set, an auth method will not be created as part of initialization. If set, the recovery KMS will be needed to perform any actions.", }) f.BoolVar(&base.BoolVar{ - Name: "skip-scopes-creation", - Target: &c.flagSkipScopesCreation, + Name: flagSkipScopesName, + Target: &c.initFlags.flagSkipScopesCreation, Usage: "If set, scopes will not be created as part of initialization.", }) f.BoolVar(&base.BoolVar{ - Name: "skip-host-resources-creation", - Target: &c.flagSkipHostResourcesCreation, + Name: flagSkipHostResourcesName, + Target: &c.initFlags.flagSkipHostResourcesCreation, Usage: "If set, host resources (host catalog, host set, host) will not be created as part of initialization.", }) f.BoolVar(&base.BoolVar{ - Name: "skip-target-creation", - Target: &c.flagSkipTargetCreation, + Name: flagSkipTargetName, + Target: &c.initFlags.flagSkipTargetCreation, Usage: "If set, a target will not be created as part of initialization.", }) f.StringVar(&base.StringVar{ - Name: "migration-url", - Target: &c.flagMigrationUrl, + Name: flagMigrationUrlName, + Target: &c.initFlags.flagMigrationUrl, Usage: `If set, overrides a migration URL set in config, and specifies the URL used to connect to the database for initialization. This can allow different permissions for the user running initialization vs. normal operation. 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.`, }) @@ -196,7 +194,7 @@ func (c *InitCommand) Run(args []string) (retCode int) { dialect := "postgres" - if err := c.SetupLogging(c.flagLogLevel, c.flagLogFormat, c.Config.LogLevel, c.Config.LogFormat); err != nil { + if err := c.SetupLogging(c.initFlags.flagLogLevel, c.initFlags.flagLogFormat, c.Config.LogLevel, c.Config.LogFormat); err != nil { c.UI.Error(err.Error()) return base.CommandCliError } @@ -250,8 +248,8 @@ func (c *InitCommand) Run(args []string) (retCode int) { if c.Config.Controller.Database.MigrationUrl != "" { migrationUrlToParse = c.Config.Controller.Database.MigrationUrl } - if c.flagMigrationUrl != "" { - migrationUrlToParse = c.flagMigrationUrl + if c.initFlags.flagMigrationUrl != "" { + migrationUrlToParse = c.initFlags.flagMigrationUrl } // Fallback to using database URL for everything if migrationUrlToParse == "" { @@ -321,178 +319,176 @@ func (c *InitCommand) Run(args []string) (retCode int) { }() } - if c.flagSkipInitialLoginRoleCreation { - return base.CommandSuccess - } - - role, err := c.CreateInitialLoginRole(c.Context) - if err != nil { - c.UI.Error(fmt.Errorf("Error creating initial global-scoped login role: %w", err).Error()) - return base.CommandCliError - } - - roleInfo := &RoleInfo{ - RoleId: role.PublicId, - Name: role.Name, - } - switch base.Format(c.UI) { - case "table": - c.UI.Output(generateInitialLoginRoleTableOutput(roleInfo)) - case "json": - jsonMap["login_role"] = roleInfo - } - - if c.flagSkipInitialAuthenticatedUserRoleCreation { - return base.CommandSuccess - } - - role, err = c.CreateInitialAuthenticatedUserRole(c.Context) - if err != nil { - c.UI.Error(fmt.Errorf("Error creating initial global-scoped authenticated user role: %w", err).Error()) - return base.CommandCliError - } - - roleInfo = &RoleInfo{ - RoleId: role.PublicId, - Name: role.Name, - } - switch base.Format(c.UI) { - case "table": - c.UI.Output(generateInitialAuthenticatedUserRoleOutput(roleInfo)) - case "json": - jsonMap["authenticated_user_role"] = roleInfo - } - - if c.flagSkipAuthMethodCreation { - return base.CommandSuccess - } - - // Use an easy name, at least - c.DevLoginName = "admin" - am, user, err := c.CreateInitialPasswordAuthMethod(c.Context) - if err != nil { - c.UI.Error(fmt.Errorf("Error creating initial auth method and user: %w", err).Error()) - return base.CommandCliError - } - - authMethodInfo := &AuthInfo{ - AuthMethodId: c.DevPasswordAuthMethodId, - AuthMethodName: am.Name, - LoginName: c.DevLoginName, - Password: c.DevPassword, - ScopeId: scope.Global.String(), - UserId: user.PublicId, - UserName: user.Name, - } - switch base.Format(c.UI) { - case "table": - c.UI.Output(generateInitialAuthTableOutput(authMethodInfo)) - case "json": - jsonMap["auth_method"] = authMethodInfo + if shouldSkip, reason := c.initFlags.SkipInitialLoginRoleCreation(); shouldSkip { + c.UI.Info(fmt.Sprintf("Skipping creation of initial login role: %s", reason)) + } else { + loginRole, err := c.CreateInitialLoginRole(c.Context) + if err != nil { + c.UI.Error(fmt.Errorf("error creating initial login role: %w", err).Error()) + return base.CommandCliError + } + roleInfo := &RoleInfo{ + RoleId: loginRole.PublicId, + Name: loginRole.Name, + } + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateInitialLoginRoleTableOutput(roleInfo)) + case "json": + jsonMap["login_role"] = roleInfo + } } - if c.flagSkipScopesCreation { - return base.CommandSuccess - } + if shouldSkip, reason := c.initFlags.SkipInitialAuthenticatedUserRoleCreation(); shouldSkip { + c.UI.Info(fmt.Sprintf("Skipping creation of initial global-scoped authenticated user role: %s", reason)) + } else { + role, err := c.CreateInitialAuthenticatedUserRole(c.Context) + if err != nil { + c.UI.Error(fmt.Errorf("Error creating initial global-scoped authenticated user role: %w", err).Error()) + return base.CommandCliError + } - orgScope, projScope, err := c.CreateInitialScopes(c.Context) - if err != nil { - c.UI.Error(fmt.Errorf("Error creating initial scopes: %w", err).Error()) - return base.CommandCliError + roleInfo := &RoleInfo{ + RoleId: role.PublicId, + Name: role.Name, + } + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateInitialAuthenticatedUserRoleOutput(roleInfo)) + case "json": + jsonMap["authenticated_user_role"] = roleInfo + } } - orgScopeInfo := &ScopeInfo{ - ScopeId: c.DevOrgId, - Type: scope.Org.String(), - Name: orgScope.Name, - } - switch base.Format(c.UI) { - case "table": - c.UI.Output(generateInitialScopeTableOutput(orgScopeInfo)) - case "json": - jsonMap["org_scope"] = orgScopeInfo + if shouldSkip, reason := c.initFlags.SkipAuthMethodCreation(); shouldSkip { + c.UI.Info(fmt.Sprintf("Skipping creation of initial auth method: %s", reason)) + } else { + // Use an easy name, at least + c.DevLoginName = "admin" + am, user, err := c.CreateInitialPasswordAuthMethod(c.Context) + if err != nil { + c.UI.Error(fmt.Errorf("Error creating initial auth method and user: %w", err).Error()) + return base.CommandCliError + } + authMethodInfo := &AuthInfo{ + AuthMethodId: c.DevPasswordAuthMethodId, + AuthMethodName: am.Name, + LoginName: c.DevLoginName, + Password: c.DevPassword, + ScopeId: scope.Global.String(), + UserId: user.PublicId, + UserName: user.Name, + } + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateInitialAuthTableOutput(authMethodInfo)) + case "json": + jsonMap["auth_method"] = authMethodInfo + } } - projScopeInfo := &ScopeInfo{ - ScopeId: c.DevProjectId, - Type: scope.Project.String(), - Name: projScope.Name, - } - switch base.Format(c.UI) { - case "table": - c.UI.Output(generateInitialScopeTableOutput(projScopeInfo)) - case "json": - jsonMap["project_scope"] = projScopeInfo - } + if shouldSkip, reason := c.initFlags.SkipScopesCreation(); shouldSkip { + c.UI.Info(fmt.Sprintf("Skipping creation of initial scopes: %s", reason)) + } else { + orgScope, projScope, err := c.CreateInitialScopes(c.Context) + if err != nil { + c.UI.Error(fmt.Errorf("Error creating initial scopes: %w", err).Error()) + return base.CommandCliError + } - if c.flagSkipHostResourcesCreation { - return base.CommandSuccess - } + orgScopeInfo := &ScopeInfo{ + ScopeId: c.DevOrgId, + Type: scope.Org.String(), + Name: orgScope.Name, + } + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateInitialScopeTableOutput(orgScopeInfo)) + case "json": + jsonMap["org_scope"] = orgScopeInfo + } - hc, hs, h, err := c.CreateInitialHostResources(c.Context) - if err != nil { - c.UI.Error(fmt.Errorf("Error creating initial host resources: %w", err).Error()) - return base.CommandCliError + projScopeInfo := &ScopeInfo{ + ScopeId: c.DevProjectId, + Type: scope.Project.String(), + Name: projScope.Name, + } + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateInitialScopeTableOutput(projScopeInfo)) + case "json": + jsonMap["project_scope"] = projScopeInfo + } } - hostInfo := &HostInfo{ - HostCatalogId: c.DevHostCatalogId, - HostCatalogName: hc.GetName(), - HostSetId: c.DevHostSetId, - HostSetName: hs.GetName(), - HostId: c.DevHostId, - HostName: h.GetName(), - Type: "static", - ScopeId: c.DevProjectId, - } - switch base.Format(c.UI) { - case "table": - c.UI.Output(generateInitialHostResourcesTableOutput(hostInfo)) - case "json": - jsonMap["host_resources"] = hostInfo - } + if shouldSkip, reason := c.initFlags.SkipHostResourcesCreation(); shouldSkip { + c.UI.Info(fmt.Sprintf("Skipping creation of initial host resources: %s", reason)) + } else { + hc, hs, h, err := c.CreateInitialHostResources(c.Context) + if err != nil { + c.UI.Error(fmt.Errorf("Error creating initial host resources: %w", err).Error()) + return base.CommandCliError + } - if c.flagSkipTargetCreation { - return base.CommandSuccess + hostInfo := &HostInfo{ + HostCatalogId: c.DevHostCatalogId, + HostCatalogName: hc.GetName(), + HostSetId: c.DevHostSetId, + HostSetName: hs.GetName(), + HostId: c.DevHostId, + HostName: h.GetName(), + Type: "static", + ScopeId: c.DevProjectId, + } + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateInitialHostResourcesTableOutput(hostInfo)) + case "json": + jsonMap["host_resources"] = hostInfo + } } - c.DevTargetSessionConnectionLimit = -1 - ta, err := c.CreateInitialTargetWithAddress(c.Context) - if err != nil { - c.UI.Error(fmt.Errorf("Error creating initial target: %w", err).Error()) - return base.CommandCliError - } - taInfo := &TargetInfo{ - TargetId: ta.GetPublicId(), - DefaultPort: ta.GetDefaultPort(), - SessionMaxSeconds: ta.GetSessionMaxSeconds(), - SessionConnectionLimit: ta.GetSessionConnectionLimit(), - Type: string(ta.GetType()), - ScopeId: ta.GetProjectId(), - Name: ta.GetName(), - } + if shouldSkip, reason := c.initFlags.SkipTargetCreation(); shouldSkip { + c.UI.Info(fmt.Sprintf("Skipping creation of initial target: %s", reason)) + } else { + c.DevTargetSessionConnectionLimit = -1 + ta, err := c.CreateInitialTargetWithAddress(c.Context) + if err != nil { + c.UI.Error(fmt.Errorf("Error creating initial target: %w", err).Error()) + return base.CommandCliError + } + taInfo := &TargetInfo{ + TargetId: ta.GetPublicId(), + DefaultPort: ta.GetDefaultPort(), + SessionMaxSeconds: ta.GetSessionMaxSeconds(), + SessionConnectionLimit: ta.GetSessionConnectionLimit(), + Type: string(ta.GetType()), + ScopeId: ta.GetProjectId(), + Name: ta.GetName(), + } - ths, err := c.CreateInitialTargetWithHostSources(c.Context) - if err != nil { - c.UI.Error(fmt.Errorf("Error creating initial secondary target: %w", err).Error()) - return base.CommandCliError - } - thsInfo := &TargetInfo{ - TargetId: ths.GetPublicId(), - DefaultPort: ths.GetDefaultPort(), - SessionMaxSeconds: ths.GetSessionMaxSeconds(), - SessionConnectionLimit: ths.GetSessionConnectionLimit(), - Type: string(ths.GetType()), - ScopeId: ths.GetProjectId(), - Name: ths.GetName(), - } - switch base.Format(c.UI) { - case "table": - c.UI.Output(generateInitialTargetTableOutput(taInfo)) - c.UI.Output(generateInitialTargetTableOutput(thsInfo)) - case "json": - jsonMap["target"] = taInfo - jsonMap["target_secondary"] = thsInfo + ths, err := c.CreateInitialTargetWithHostSources(c.Context) + if err != nil { + c.UI.Error(fmt.Errorf("Error creating initial secondary target: %w", err).Error()) + return base.CommandCliError + } + thsInfo := &TargetInfo{ + TargetId: ths.GetPublicId(), + DefaultPort: ths.GetDefaultPort(), + SessionMaxSeconds: ths.GetSessionMaxSeconds(), + SessionConnectionLimit: ths.GetSessionConnectionLimit(), + Type: string(ths.GetType()), + ScopeId: ths.GetProjectId(), + Name: ths.GetName(), + } + switch base.Format(c.UI) { + case "table": + c.UI.Output(generateInitialTargetTableOutput(taInfo)) + c.UI.Output(generateInitialTargetTableOutput(thsInfo)) + case "json": + jsonMap["target"] = taInfo + jsonMap["target_secondary"] = thsInfo + } } return base.CommandSuccess @@ -510,12 +506,12 @@ func (c *InitCommand) ParseFlagsAndConfig(args []string) int { // Validation switch { - case len(c.flagConfig) == 0: + case len(c.initFlags.flagConfig) == 0: c.UI.Error("Must specify a config file using -config") return base.CommandUserError } - c.Config, err = config.Load(c.Context, c.flagConfig, c.flagConfigKms) + c.Config, err = config.Load(c.Context, c.initFlags.flagConfig, c.initFlags.flagConfigKms) if err != nil { c.UI.Error("Error parsing config: " + err.Error()) return base.CommandUserError