From ccada87e8c82986778841e7475f69b485ba6ca45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Palet?= Date: Fri, 10 May 2024 11:45:57 +0100 Subject: [PATCH 1/4] Implement config profile set --- internal/cmd/config/config.go | 2 + internal/cmd/config/profile/profile.go | 33 +++++ internal/cmd/config/profile/set/set.go | 83 ++++++++++++ internal/cmd/config/profile/set/set_test.go | 132 ++++++++++++++++++++ internal/pkg/config/config.go | 5 +- internal/pkg/config/profiles.go | 38 +++++- internal/pkg/config/profiles_test.go | 2 +- internal/pkg/errors/errors.go | 12 ++ main.go | 2 +- 9 files changed, 301 insertions(+), 8 deletions(-) create mode 100644 internal/cmd/config/profile/profile.go create mode 100644 internal/cmd/config/profile/set/set.go create mode 100644 internal/cmd/config/profile/set/set_test.go diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index 7c3fea56f..c4fffbf7a 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/stackitcloud/stackit-cli/internal/cmd/config/list" + "github.com/stackitcloud/stackit-cli/internal/cmd/config/profile" "github.com/stackitcloud/stackit-cli/internal/cmd/config/set" "github.com/stackitcloud/stackit-cli/internal/cmd/config/unset" "github.com/stackitcloud/stackit-cli/internal/pkg/args" @@ -35,4 +36,5 @@ func addSubcommands(cmd *cobra.Command, p *print.Printer) { cmd.AddCommand(list.NewCmd(p)) cmd.AddCommand(set.NewCmd(p)) cmd.AddCommand(unset.NewCmd(p)) + cmd.AddCommand(profile.NewCmd(p)) } diff --git a/internal/cmd/config/profile/profile.go b/internal/cmd/config/profile/profile.go new file mode 100644 index 000000000..196707e73 --- /dev/null +++ b/internal/cmd/config/profile/profile.go @@ -0,0 +1,33 @@ +package profile + +import ( + "fmt" + + "github.com/stackitcloud/stackit-cli/internal/cmd/config/profile/set" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + + "github.com/spf13/cobra" +) + +func NewCmd(p *print.Printer) *cobra.Command { + cmd := &cobra.Command{ + Use: "profile", + Short: "Manage the CLI configurations profiles", + Long: fmt.Sprintf("%s\n%s\n%s\n%s", + "Manage the CLI configurations profiles.", + `The profile to be used can be managed via the STACKIT_CLI_PROFILE environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands.`, + "The environment variable takes precedence over what is set via the commands.", + "When no profile is set, the default profile is used.", + ), + Args: args.NoArgs, + Run: utils.CmdHelp, + } + addSubcommands(cmd, p) + return cmd +} + +func addSubcommands(cmd *cobra.Command, p *print.Printer) { + cmd.AddCommand(set.NewCmd(p)) +} diff --git a/internal/cmd/config/profile/set/set.go b/internal/cmd/config/profile/set/set.go new file mode 100644 index 000000000..1ab1cac0e --- /dev/null +++ b/internal/cmd/config/profile/set/set.go @@ -0,0 +1,83 @@ +package set + +import ( + "fmt" + + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/config" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + + "github.com/spf13/cobra" +) + +const ( + profileArg = "PROFILE" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + Profile string +} + +func NewCmd(p *print.Printer) *cobra.Command { + cmd := &cobra.Command{ + Use: fmt.Sprintf("set %s", profileArg), + Short: "Set a CLI configuration profile", + Long: fmt.Sprintf("%s\n%s\n%s\n%s", + "Set a CLI configuration profile as the active profile.", + `The profile to be used can be managed via the STACKIT_CLI_PROFILE environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands.`, + "The environment variable takes precedence over what is set via the commands.", + "When no profile is set, the default profile is used.", + ), + Args: args.SingleArg(profileArg, nil), + Example: examples.Build( + examples.NewExample( + `Set the configuration profile "my-profile" as the active profile`, + "$ stackit config profile set my-profile"), + ), + RunE: func(cmd *cobra.Command, args []string) error { + model, err := parseInput(p, cmd, args) + if err != nil { + return err + } + + err = config.SetProfile(model.Profile) + if err != nil { + return fmt.Errorf("set profile: %w", err) + } + + p.Info("Profile %q set successfully as the active profile\n", model.Profile) + return nil + }, + } + return cmd +} + +func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { + profile := inputArgs[0] + + err := config.ValidateProfile(profile) + if err != nil { + return nil, err + } + + globalFlags := globalflags.Parse(p, cmd) + + model := inputModel{ + GlobalFlagModel: globalFlags, + Profile: profile, + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} diff --git a/internal/cmd/config/profile/set/set_test.go b/internal/cmd/config/profile/set/set_test.go new file mode 100644 index 000000000..47f56ca0b --- /dev/null +++ b/internal/cmd/config/profile/set/set_test.go @@ -0,0 +1,132 @@ +package set + +import ( + "testing" + + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + + "github.com/google/go-cmp/cmp" +) + +const testProfile = "test-profile" + +func fixtureArgValues(mods ...func(argValues []string)) []string { + argValues := []string{ + testProfile, + } + for _, mod := range mods { + mod(argValues) + } + return argValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + Verbosity: globalflags.VerbosityDefault, + }, + Profile: testProfile, + } + for _, mod := range mods { + mod(model) + } + return model +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + argValues []string + flagValues map[string]string + isValid bool + expectedModel *inputModel + }{ + { + description: "base", + argValues: fixtureArgValues(), + isValid: true, + expectedModel: fixtureInputModel(), + }, + { + description: "no values", + argValues: []string{}, + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "no arg values", + argValues: []string{}, + isValid: false, + }, + { + description: "some global flag", + argValues: fixtureArgValues(), + flagValues: map[string]string{ + globalflags.VerbosityFlag: globalflags.DebugVerbosity, + }, + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.GlobalFlagModel.Verbosity = globalflags.DebugVerbosity + }), + }, + { + description: "invalid profile", + argValues: []string{"invalid-profile-&"}, + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + p := print.NewPrinter() + cmd := NewCmd(p) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateArgs(tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating args: %v", err) + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(p, cmd, tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing input: %v", err) + } + + if !tt.isValid { + t.Fatalf("did not fail on invalid input") + } + diff := cmp.Diff(model, tt.expectedModel) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 8070bebc5..c28ca73fd 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -82,12 +82,13 @@ func InitConfig() { configDir, err := os.UserConfigDir() cobra.CheckErr(err) + configFolderPath = filepath.Join(configDir, configFolder) // Default config folder + configProfile, err := GetProfile() cobra.CheckErr(err) - configFolderPath = filepath.Join(configDir, configFolder) if configProfile != "" { - configFolderPath = filepath.Join(configFolderPath, configProfile) + configFolderPath = filepath.Join(configFolderPath, configProfile) // If a profile is set, use the profile config folder } configFilePath := filepath.Join(configFolderPath, fmt.Sprintf("%s.%s", configFileName, configFileExtension)) diff --git a/internal/pkg/config/profiles.go b/internal/pkg/config/profiles.go index 5f21099d9..4cdc90143 100644 --- a/internal/pkg/config/profiles.go +++ b/internal/pkg/config/profiles.go @@ -5,6 +5,8 @@ import ( "os" "path/filepath" "regexp" + + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" ) // GetProfile returns the current profile to be used by the CLI. @@ -29,23 +31,51 @@ func GetProfile() (string, error) { profile = contents } - err := validateProfile(profile) + err := ValidateProfile(profile) if err != nil { return "", fmt.Errorf("validate profile: %w", err) } return profile, nil } -// validateProfile validates the profile name. +// SetProfile sets the profile to be used by the CLI. +func SetProfile(profile string) error { + err := ValidateProfile(profile) + if err != nil { + return fmt.Errorf("validate profile: %w", err) + } + + profileFilePath := filepath.Join(configFolderPath, fmt.Sprintf("%s.%s", profileFileName, profileFileExtension)) + err = os.WriteFile(profileFilePath, []byte(profile), os.ModePerm) + if err != nil { + return fmt.Errorf("write profile to file: %w", err) + } + return nil +} + +// UnsetProfile removes the profile file. +// If the profile file does not exist, it does nothing. +func UnsetProfile() error { + profileFilePath := filepath.Join(configFolderPath, fmt.Sprintf("%s.%s", profileFileName, profileFileExtension)) + err := os.Remove(profileFilePath) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("remove profile file: %w", err) + } + return nil +} + +// ValidateProfile validates the profile name. // It can only use letters, numbers, or "-" and cannot be empty. // If the profile is invalid, it returns an error. -func validateProfile(profile string) error { +func ValidateProfile(profile string) error { match, err := regexp.MatchString("^[a-zA-Z0-9-]+$", profile) if err != nil { return fmt.Errorf("match string regex: %w", err) } if !match { - return fmt.Errorf("profile name can only contain letters, numbers, and \"-\" and cannot be empty") + return &errors.InvalidProfileError{ + Profile: profile, + } } return nil } diff --git a/internal/pkg/config/profiles_test.go b/internal/pkg/config/profiles_test.go index 8e96a6e88..e97451aeb 100644 --- a/internal/pkg/config/profiles_test.go +++ b/internal/pkg/config/profiles_test.go @@ -47,7 +47,7 @@ func TestValidateProfile(t *testing.T) { for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - err := validateProfile(tt.profile) + err := ValidateProfile(tt.profile) if tt.isValid && err != nil { t.Errorf("expected profile to be valid but got error: %v", err) } diff --git a/internal/pkg/errors/errors.go b/internal/pkg/errors/errors.go index d421aa1b7..0d285276e 100644 --- a/internal/pkg/errors/errors.go +++ b/internal/pkg/errors/errors.go @@ -113,6 +113,10 @@ For more details on the available storages for the configured flavor (%[3]s), ru SUBCOMMAND_MISSING = `missing subcommand` + INVALID_PROFILE = `the profile name %q is invalid. + +The profile name can only contain letters, numbers, and "-" and cannot be empty.` + USAGE_TIP = `For usage help, run: $ %s --help` ) @@ -304,3 +308,11 @@ func AppendUsageTip(err error, cmd *cobra.Command) error { tip := fmt.Sprintf(USAGE_TIP, cmd.CommandPath()) return fmt.Errorf("%w.\n\n%s", err, tip) } + +type InvalidProfileError struct { + Profile string +} + +func (e *InvalidProfileError) Error() string { + return fmt.Sprintf(INVALID_PROFILE, e.Profile) +} diff --git a/main.go b/main.go index 37689d889..5286783ac 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ import ( "github.com/stackitcloud/stackit-cli/internal/pkg/config" ) -// These values are dynamically overridden by GoReleaser +// These values are overwritten by GoReleaser at build time var ( version = "DEV" date = "UNKNOWN" From ac61053d48e88082411cddd2e045ee6cedcd99e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Palet?= Date: Fri, 10 May 2024 12:12:01 +0100 Subject: [PATCH 2/4] Implement config profile unset --- internal/cmd/config/profile/profile.go | 2 ++ internal/cmd/config/profile/unset/unset.go | 39 ++++++++++++++++++++++ internal/pkg/config/config.go | 4 ++- internal/pkg/config/profiles.go | 2 -- 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 internal/cmd/config/profile/unset/unset.go diff --git a/internal/cmd/config/profile/profile.go b/internal/cmd/config/profile/profile.go index 196707e73..17f1e2976 100644 --- a/internal/cmd/config/profile/profile.go +++ b/internal/cmd/config/profile/profile.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/stackitcloud/stackit-cli/internal/cmd/config/profile/set" + "github.com/stackitcloud/stackit-cli/internal/cmd/config/profile/unset" "github.com/stackitcloud/stackit-cli/internal/pkg/args" "github.com/stackitcloud/stackit-cli/internal/pkg/print" "github.com/stackitcloud/stackit-cli/internal/pkg/utils" @@ -30,4 +31,5 @@ func NewCmd(p *print.Printer) *cobra.Command { func addSubcommands(cmd *cobra.Command, p *print.Printer) { cmd.AddCommand(set.NewCmd(p)) + cmd.AddCommand(unset.NewCmd(p)) } diff --git a/internal/cmd/config/profile/unset/unset.go b/internal/cmd/config/profile/unset/unset.go new file mode 100644 index 000000000..61ea77607 --- /dev/null +++ b/internal/cmd/config/profile/unset/unset.go @@ -0,0 +1,39 @@ +package unset + +import ( + "fmt" + + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/config" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + + "github.com/spf13/cobra" +) + +func NewCmd(p *print.Printer) *cobra.Command { + cmd := &cobra.Command{ + Use: "unset", + Short: "Unset the current active CLI configuration profile", + Long: fmt.Sprintf("%s\n%s", + "Unset the current active CLI configuration profile.", + "When no profile is set, the default profile will be used.", + ), + Args: args.NoArgs, + Example: examples.Build( + examples.NewExample( + `Unset the currently active configuration profile. The default profile will be used.`, + "$ stackit config profile unset"), + ), + RunE: func(cmd *cobra.Command, args []string) error { + err := config.UnsetProfile() + if err != nil { + return fmt.Errorf("unset profile: %w", err) + } + + p.Info("Profile unset successfully. The default profile will be used.\n") + return nil + }, + } + return cmd +} diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index c28ca73fd..209710e08 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -77,12 +77,14 @@ var ConfigKeys = []string{ } var configFolderPath string +var profileFilePath string func InitConfig() { configDir, err := os.UserConfigDir() cobra.CheckErr(err) - configFolderPath = filepath.Join(configDir, configFolder) // Default config folder + configFolderPath = filepath.Join(configDir, configFolder) // Default config folder + profileFilePath = filepath.Join(configFolderPath, fmt.Sprintf("%s.%s", profileFileName, profileFileExtension)) // Profile file path is in the default config folder configProfile, err := GetProfile() cobra.CheckErr(err) diff --git a/internal/pkg/config/profiles.go b/internal/pkg/config/profiles.go index 4cdc90143..303fcbe36 100644 --- a/internal/pkg/config/profiles.go +++ b/internal/pkg/config/profiles.go @@ -20,7 +20,6 @@ import ( func GetProfile() (string, error) { profile, profileSet := os.LookupEnv("STACKIT_CLI_PROFILE") if !profileSet { - profileFilePath := filepath.Join(configFolderPath, fmt.Sprintf("%s.%s", profileFileName, profileFileExtension)) contents, exists, err := readFileIfExists(profileFilePath) if err != nil { return "", fmt.Errorf("read profile from file: %w", err) @@ -56,7 +55,6 @@ func SetProfile(profile string) error { // UnsetProfile removes the profile file. // If the profile file does not exist, it does nothing. func UnsetProfile() error { - profileFilePath := filepath.Join(configFolderPath, fmt.Sprintf("%s.%s", profileFileName, profileFileExtension)) err := os.Remove(profileFilePath) if err != nil && !os.IsNotExist(err) { return fmt.Errorf("remove profile file: %w", err) From e79b6b74013834ba2f97ddd17840ecc15ecf4114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Palet?= Date: Fri, 10 May 2024 12:12:29 +0100 Subject: [PATCH 3/4] Extend config list to show active profile --- internal/cmd/config/list/list.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/internal/cmd/config/list/list.go b/internal/cmd/config/list/list.go index 3da8eb0a1..8b890aa4a 100644 --- a/internal/cmd/config/list/list.go +++ b/internal/cmd/config/list/list.go @@ -50,7 +50,13 @@ func NewCmd(p *print.Printer) *cobra.Command { configData := viper.AllSettings() model := parseInput(p, cmd) - return outputResult(p, model.OutputFormat, configData) + + activeProfile, err := config.GetProfile() + if err != nil { + return fmt.Errorf("get profile: %w", err) + } + + return outputResult(p, model.OutputFormat, configData, activeProfile) }, } return cmd @@ -64,9 +70,12 @@ func parseInput(p *print.Printer, cmd *cobra.Command) *inputModel { } } -func outputResult(p *print.Printer, outputFormat string, configData map[string]any) error { +func outputResult(p *print.Printer, outputFormat string, configData map[string]any, activeProfile string) error { switch outputFormat { case print.JSONOutputFormat: + if activeProfile != "" { + configData["active_profile"] = activeProfile + } details, err := json.MarshalIndent(configData, "", " ") if err != nil { return fmt.Errorf("marshal config list: %w", err) @@ -74,6 +83,10 @@ func outputResult(p *print.Printer, outputFormat string, configData map[string]a p.Outputln(string(details)) return nil default: + if activeProfile != "" { + p.Outputf("\n ACTIVE PROFILE: %s\n", activeProfile) + } + // Sort the config options by key configKeys := make([]string, 0, len(configData)) for k := range configData { From 263653e9fe5c8f0d148c890d29e4592d19666f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Palet?= Date: Fri, 10 May 2024 15:17:33 +0100 Subject: [PATCH 4/4] Adjustments after review --- internal/cmd/config/config.go | 12 ++++++------ internal/cmd/config/profile/profile.go | 6 +++--- internal/cmd/root.go | 18 +++++++++++++++--- internal/pkg/config/profiles.go | 2 +- internal/pkg/errors/errors.go | 8 ++++---- 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/internal/cmd/config/config.go b/internal/cmd/config/config.go index c4fffbf7a..a96355a54 100644 --- a/internal/cmd/config/config.go +++ b/internal/cmd/config/config.go @@ -18,12 +18,12 @@ func NewCmd(p *print.Printer) *cobra.Command { cmd := &cobra.Command{ Use: "config", Short: "Provides functionality for CLI configuration options", - Long: fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", "Provides functionality for CLI configuration options", - "The configuration is stored in a file in the user's config directory, which is OS dependent.", - "Windows: %APPDATA%\\stackit", - "Linux: $XDG_CONFIG_HOME/stackit", - "macOS: $HOME/Library/Application Support/stackit", - "The configuration file is named `cli-config.json` and is created automatically in your first CLI run.", + Long: fmt.Sprintf("%s\n%s\n\n%s\n%s\n%s", + "Provides functionality for CLI configuration options.", + `You can set and unset different configuration options via the "stackit config set" and "stackit config unset" commands.`, + "Additionally, you can configure the CLI to use different profiles, each with its own configuration.", + `Additional profiles can be configured via the "STACKIT_CLI_PROFILE" environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands.`, + "The environment variable takes precedence over what is set via the commands.", ), Args: args.NoArgs, Run: utils.CmdHelp, diff --git a/internal/cmd/config/profile/profile.go b/internal/cmd/config/profile/profile.go index 17f1e2976..42bea8e58 100644 --- a/internal/cmd/config/profile/profile.go +++ b/internal/cmd/config/profile/profile.go @@ -15,10 +15,10 @@ import ( func NewCmd(p *print.Printer) *cobra.Command { cmd := &cobra.Command{ Use: "profile", - Short: "Manage the CLI configurations profiles", + Short: "Manage the CLI configuration profiles", Long: fmt.Sprintf("%s\n%s\n%s\n%s", - "Manage the CLI configurations profiles.", - `The profile to be used can be managed via the STACKIT_CLI_PROFILE environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands.`, + "Manage the CLI configuration profiles.", + `The profile to be used can be managed via the "STACKIT_CLI_PROFILE" environment variable or using the "stackit config profile set PROFILE" and "stackit config profile unset" commands.`, "The environment variable takes precedence over what is set via the commands.", "When no profile is set, the default profile is used.", ), diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 5845d0c80..93b88ad1e 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -8,7 +8,7 @@ import ( "github.com/stackitcloud/stackit-cli/internal/cmd/argus" "github.com/stackitcloud/stackit-cli/internal/cmd/auth" - "github.com/stackitcloud/stackit-cli/internal/cmd/config" + configCmd "github.com/stackitcloud/stackit-cli/internal/cmd/config" "github.com/stackitcloud/stackit-cli/internal/cmd/curl" "github.com/stackitcloud/stackit-cli/internal/cmd/dns" loadbalancer "github.com/stackitcloud/stackit-cli/internal/cmd/load-balancer" @@ -26,6 +26,7 @@ import ( serviceaccount "github.com/stackitcloud/stackit-cli/internal/cmd/service-account" "github.com/stackitcloud/stackit-cli/internal/cmd/ske" "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/config" "github.com/stackitcloud/stackit-cli/internal/pkg/errors" "github.com/stackitcloud/stackit-cli/internal/pkg/flags" "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" @@ -44,7 +45,7 @@ func NewRootCmd(version, date string, p *print.Printer) *cobra.Command { SilenceErrors: true, // Error is beautified in a custom way before being printed SilenceUsage: true, DisableAutoGenTag: true, - PersistentPreRun: func(cmd *cobra.Command, args []string) { + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { p.Cmd = cmd p.Verbosity = print.Level(globalflags.Parse(p, cmd).Verbosity) @@ -54,9 +55,20 @@ func NewRootCmd(version, date string, p *print.Printer) *cobra.Command { configFilePath := viper.ConfigFileUsed() p.Debug(print.DebugLevel, "using config file: %s", configFilePath) + activeProfile, err := config.GetProfile() + if err != nil { + return fmt.Errorf("get profile: %w", err) + } + if activeProfile == "" { + activeProfile = "(no active profile, the default profile configuration will be used)" + } + p.Debug(print.DebugLevel, "active config profile: %s", activeProfile) + configKeys := viper.AllSettings() configKeysStr := print.BuildDebugStrFromMap(configKeys) p.Debug(print.DebugLevel, "config keys: %s", configKeysStr) + + return nil }, RunE: func(cmd *cobra.Command, args []string) error { if flags.FlagToBoolValue(p, cmd, "version") { @@ -104,7 +116,7 @@ func configureFlags(cmd *cobra.Command) error { func addSubcommands(cmd *cobra.Command, p *print.Printer) { cmd.AddCommand(argus.NewCmd(p)) cmd.AddCommand(auth.NewCmd(p)) - cmd.AddCommand(config.NewCmd(p)) + cmd.AddCommand(configCmd.NewCmd(p)) cmd.AddCommand(curl.NewCmd(p)) cmd.AddCommand(dns.NewCmd(p)) cmd.AddCommand(loadbalancer.NewCmd(p)) diff --git a/internal/pkg/config/profiles.go b/internal/pkg/config/profiles.go index 303fcbe36..c581b95a1 100644 --- a/internal/pkg/config/profiles.go +++ b/internal/pkg/config/profiles.go @@ -71,7 +71,7 @@ func ValidateProfile(profile string) error { return fmt.Errorf("match string regex: %w", err) } if !match { - return &errors.InvalidProfileError{ + return &errors.InvalidProfileNameError{ Profile: profile, } } diff --git a/internal/pkg/errors/errors.go b/internal/pkg/errors/errors.go index 0d285276e..0bb41a4ab 100644 --- a/internal/pkg/errors/errors.go +++ b/internal/pkg/errors/errors.go @@ -113,7 +113,7 @@ For more details on the available storages for the configured flavor (%[3]s), ru SUBCOMMAND_MISSING = `missing subcommand` - INVALID_PROFILE = `the profile name %q is invalid. + INVALID_PROFILE_NAME = `the profile name %q is invalid. The profile name can only contain letters, numbers, and "-" and cannot be empty.` @@ -309,10 +309,10 @@ func AppendUsageTip(err error, cmd *cobra.Command) error { return fmt.Errorf("%w.\n\n%s", err, tip) } -type InvalidProfileError struct { +type InvalidProfileNameError struct { Profile string } -func (e *InvalidProfileError) Error() string { - return fmt.Sprintf(INVALID_PROFILE, e.Profile) +func (e *InvalidProfileNameError) Error() string { + return fmt.Sprintf(INVALID_PROFILE_NAME, e.Profile) }