From df5b453710298c4dac3c27e6c6f37cbc381da5b3 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Wed, 22 Nov 2023 13:30:45 +0000 Subject: [PATCH 1/7] Implement `ske credential describe` --- internal/cmd/ske/credential/credential.go | 22 +++ .../cmd/ske/credential/describe/describe.go | 92 +++++++++ .../ske/credential/describe/describe_test.go | 185 ++++++++++++++++++ internal/cmd/ske/ske.go | 2 + 4 files changed, 301 insertions(+) create mode 100644 internal/cmd/ske/credential/credential.go create mode 100644 internal/cmd/ske/credential/describe/describe.go create mode 100644 internal/cmd/ske/credential/describe/describe_test.go diff --git a/internal/cmd/ske/credential/credential.go b/internal/cmd/ske/credential/credential.go new file mode 100644 index 00000000..a4f51f5b --- /dev/null +++ b/internal/cmd/ske/credential/credential.go @@ -0,0 +1,22 @@ +package credential + +import ( + "github.com/stackitcloud/stackit-cli/internal/cmd/ske/credential/describe" + + "github.com/spf13/cobra" +) + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "credential", + Short: "Provides functionality for SKE credentials", + Long: "Provides functionality for SKE credentials", + Example: describe.NewCmd().Example, + } + addSubcommands(cmd) + return cmd +} + +func addSubcommands(cmd *cobra.Command) { + cmd.AddCommand(describe.NewCmd()) +} diff --git a/internal/cmd/ske/credential/describe/describe.go b/internal/cmd/ske/credential/describe/describe.go new file mode 100644 index 00000000..b96d106b --- /dev/null +++ b/internal/cmd/ske/credential/describe/describe.go @@ -0,0 +1,92 @@ +package describe + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/ske" +) + +const ( + clusterNameFlag = "cluster-name" +) + +type flagModel struct { + *globalflags.GlobalFlagModel + ClusterName string +} + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "describe", + Short: "Get details of the credential associated to a SKE cluster", + Long: "Get details of the credential associated to a SKE cluster", + Example: `$ stackit ske credential describe --project-id xxx --cluster-name xxx`, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseFlags(cmd) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(cmd) + if err != nil { + return fmt.Errorf("authentication failed, please run \"stackit auth login\" or \"stackit auth activate-service-account\"") + } + + // Call API + req := buildRequest(ctx, model, apiClient) + resp, err := req.Execute() + if err != nil { + return fmt.Errorf("describe SKE credential: %w", err) + } + + // Show details + details, err := json.MarshalIndent(resp, "", " ") + if err != nil { + return fmt.Errorf("marshal SKE credential: %w", err) + } + cmd.Println(string(details)) + + return nil + }, + } + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().String(clusterNameFlag, "", "Cluster name") + + err := utils.MarkFlagsRequired(cmd, clusterNameFlag) + cobra.CheckErr(err) +} + +func parseFlags(cmd *cobra.Command) (*flagModel, error) { + globalFlags := globalflags.Parse() + if globalFlags.ProjectId == "" { + return nil, fmt.Errorf("project ID not set") + } + + clusterName := utils.FlagToStringValue(cmd, clusterNameFlag) + if clusterName == "" { + return nil, fmt.Errorf("cluster name can't be empty") + } + + return &flagModel{ + GlobalFlagModel: globalFlags, + ClusterName: clusterName, + }, nil +} + +func buildRequest(ctx context.Context, model *flagModel, apiClient *ske.APIClient) ske.ApiGetCredentialsRequest { + req := apiClient.GetCredentials(ctx, model.ProjectId, model.ClusterName) + return req +} diff --git a/internal/cmd/ske/credential/describe/describe_test.go b/internal/cmd/ske/credential/describe/describe_test.go new file mode 100644 index 00000000..cec706bd --- /dev/null +++ b/internal/cmd/ske/credential/describe/describe_test.go @@ -0,0 +1,185 @@ +package describe + +import ( + "context" + "testing" + + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/ske" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &ske.APIClient{} +var testProjectId = uuid.NewString() + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + clusterNameFlag: "cluster", + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureFlagModel(mods ...func(model *flagModel)) *flagModel { + model := &flagModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + }, + ClusterName: "cluster", + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *ske.ApiGetCredentialsRequest)) ske.ApiGetCredentialsRequest { + request := testClient.GetCredentials(testCtx, testProjectId, "cluster") + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseFlags(t *testing.T) { + tests := []struct { + description string + flagValues map[string]string + isValid bool + expectedModel *flagModel + }{ + { + description: "base", + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureFlagModel(), + }, + { + description: "no values", + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "cluster name missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, clusterNameFlag) + }), + isValid: false, + }, + { + description: "cluster name invalid", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[clusterNameFlag] = "" + }), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + cmd := &cobra.Command{} + configureFlags(cmd) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + configureFlags(cmd) + + 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.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseFlags(cmd) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %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) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *flagModel + expectedRequest ske.ApiGetCredentialsRequest + }{ + { + description: "base", + model: fixtureFlagModel(), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/ske/ske.go b/internal/cmd/ske/ske.go index 3d9205bf..5c60a408 100644 --- a/internal/cmd/ske/ske.go +++ b/internal/cmd/ske/ske.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster" + "github.com/stackitcloud/stackit-cli/internal/cmd/ske/credential" "github.com/stackitcloud/stackit-cli/internal/cmd/ske/describe" "github.com/stackitcloud/stackit-cli/internal/cmd/ske/disable" "github.com/stackitcloud/stackit-cli/internal/cmd/ske/enable" @@ -27,4 +28,5 @@ func addSubcommands(cmd *cobra.Command) { cmd.AddCommand(enable.NewCmd()) cmd.AddCommand(disable.NewCmd()) cmd.AddCommand(cluster.NewCmd()) + cmd.AddCommand(credential.NewCmd()) } From 30842ee7b0edc9585dfe3d6e30cfc66d11106557 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Wed, 22 Nov 2023 13:38:07 +0000 Subject: [PATCH 2/7] Implement `ske credential rotate` --- internal/cmd/ske/credential/credential.go | 6 +- internal/cmd/ske/credential/rotate/rotate.go | 84 ++++++++ .../cmd/ske/credential/rotate/rotate_test.go | 182 ++++++++++++++++++ 3 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 internal/cmd/ske/credential/rotate/rotate.go create mode 100644 internal/cmd/ske/credential/rotate/rotate_test.go diff --git a/internal/cmd/ske/credential/credential.go b/internal/cmd/ske/credential/credential.go index a4f51f5b..0cfeaff9 100644 --- a/internal/cmd/ske/credential/credential.go +++ b/internal/cmd/ske/credential/credential.go @@ -1,7 +1,10 @@ package credential import ( + "fmt" + "github.com/stackitcloud/stackit-cli/internal/cmd/ske/credential/describe" + "github.com/stackitcloud/stackit-cli/internal/cmd/ske/credential/rotate" "github.com/spf13/cobra" ) @@ -11,7 +14,7 @@ func NewCmd() *cobra.Command { Use: "credential", Short: "Provides functionality for SKE credentials", Long: "Provides functionality for SKE credentials", - Example: describe.NewCmd().Example, + Example: fmt.Sprintf("%s\n%s", describe.NewCmd().Example, rotate.NewCmd().Example), } addSubcommands(cmd) return cmd @@ -19,4 +22,5 @@ func NewCmd() *cobra.Command { func addSubcommands(cmd *cobra.Command) { cmd.AddCommand(describe.NewCmd()) + cmd.AddCommand(rotate.NewCmd()) } diff --git a/internal/cmd/ske/credential/rotate/rotate.go b/internal/cmd/ske/credential/rotate/rotate.go new file mode 100644 index 00000000..60446259 --- /dev/null +++ b/internal/cmd/ske/credential/rotate/rotate.go @@ -0,0 +1,84 @@ +package rotate + +import ( + "context" + "fmt" + + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-sdk-go/services/ske" +) + +const ( + clusterNameFlag = "cluster-name" +) + +type flagModel struct { + *globalflags.GlobalFlagModel + ClusterName string +} + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "rotate", + Short: "Rotate credential associated to a SKE cluster", + Long: "Rotate credential associated to a SKE cluster. The old credential will be invalid after the operation", + Example: `$ stackit postgresql credential create --project-id xxx --instance-id xxx`, + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseFlags(cmd) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(cmd) + if err != nil { + return fmt.Errorf("authentication failed, please run \"stackit auth login\" or \"stackit auth activate-service-account\"") + } + + // Call API + req := buildRequest(ctx, model, apiClient) + _, err = req.Execute() + if err != nil { + return fmt.Errorf("rotate SKE credential: %w", err) + } + + return nil + }, + } + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().String(clusterNameFlag, "", "Cluster name") + + err := utils.MarkFlagsRequired(cmd, clusterNameFlag) + cobra.CheckErr(err) +} + +func parseFlags(cmd *cobra.Command) (*flagModel, error) { + globalFlags := globalflags.Parse() + if globalFlags.ProjectId == "" { + return nil, fmt.Errorf("project ID not set") + } + + clusterName := utils.FlagToStringValue(cmd, clusterNameFlag) + if clusterName == "" { + return nil, fmt.Errorf("cluster name can't be empty") + } + + return &flagModel{ + GlobalFlagModel: globalFlags, + ClusterName: clusterName, + }, nil +} + +func buildRequest(ctx context.Context, model *flagModel, apiClient *ske.APIClient) ske.ApiTriggerRotateCredentialsRequest { + req := apiClient.TriggerRotateCredentials(ctx, model.ProjectId, model.ClusterName) + return req +} diff --git a/internal/cmd/ske/credential/rotate/rotate_test.go b/internal/cmd/ske/credential/rotate/rotate_test.go new file mode 100644 index 00000000..874a5dc8 --- /dev/null +++ b/internal/cmd/ske/credential/rotate/rotate_test.go @@ -0,0 +1,182 @@ +package rotate + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-sdk-go/services/ske" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &ske.APIClient{} +var testProjectId = uuid.NewString() + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + clusterNameFlag: "cluster", + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureFlagModel(mods ...func(model *flagModel)) *flagModel { + model := &flagModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + }, + ClusterName: "cluster", + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *ske.ApiTriggerRotateCredentialsRequest)) ske.ApiTriggerRotateCredentialsRequest { + request := testClient.TriggerRotateCredentials(testCtx, testProjectId, "cluster") + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseFlags(t *testing.T) { + tests := []struct { + description string + flagValues map[string]string + isValid bool + expectedModel *flagModel + }{ + { + description: "base", + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureFlagModel(), + }, + { + description: "no values", + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "cluster name missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, clusterNameFlag) + }), + isValid: false, + }, + { + description: "cluster name invalid", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[clusterNameFlag] = "" + }), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + cmd := &cobra.Command{} + configureFlags(cmd) + 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.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseFlags(cmd) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %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) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *flagModel + expectedRequest ske.ApiTriggerRotateCredentialsRequest + }{ + { + description: "base", + model: fixtureFlagModel(), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} From 51c3549a4e383837b617c4af1ed810eefd587457 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 24 Nov 2023 11:58:12 +0000 Subject: [PATCH 3/7] Fix duplicate configuration --- internal/cmd/ske/credential/describe/describe_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/cmd/ske/credential/describe/describe_test.go b/internal/cmd/ske/credential/describe/describe_test.go index cec706bd..9c129631 100644 --- a/internal/cmd/ske/credential/describe/describe_test.go +++ b/internal/cmd/ske/credential/describe/describe_test.go @@ -117,8 +117,6 @@ func TestParseFlags(t *testing.T) { t.Fatalf("configure global flags: %v", err) } - configureFlags(cmd) - for flag, value := range tt.flagValues { err := cmd.Flags().Set(flag, value) if err != nil { From 2108f879a7e333d01309d511220369f89a0807b6 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 24 Nov 2023 12:02:17 +0000 Subject: [PATCH 4/7] Fix example, add message at the end --- internal/cmd/ske/credential/rotate/rotate.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/cmd/ske/credential/rotate/rotate.go b/internal/cmd/ske/credential/rotate/rotate.go index 60446259..b1d312cb 100644 --- a/internal/cmd/ske/credential/rotate/rotate.go +++ b/internal/cmd/ske/credential/rotate/rotate.go @@ -26,7 +26,7 @@ func NewCmd() *cobra.Command { Use: "rotate", Short: "Rotate credential associated to a SKE cluster", Long: "Rotate credential associated to a SKE cluster. The old credential will be invalid after the operation", - Example: `$ stackit postgresql credential create --project-id xxx --instance-id xxx`, + Example: `$ stackit ske credential rotate --project-id xxx --cluster-name xxx`, RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() model, err := parseFlags(cmd) @@ -47,6 +47,7 @@ func NewCmd() *cobra.Command { return fmt.Errorf("rotate SKE credential: %w", err) } + cmd.Println("Credentials rotated") return nil }, } From 0b04538a6edfdb0a9b3abbe0eefa30b90c726024 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 24 Nov 2023 16:49:23 +0000 Subject: [PATCH 5/7] Implement output format --- .../cmd/ske/credential/describe/describe.go | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/internal/cmd/ske/credential/describe/describe.go b/internal/cmd/ske/credential/describe/describe.go index b96d106b..0ebcdf41 100644 --- a/internal/cmd/ske/credential/describe/describe.go +++ b/internal/cmd/ske/credential/describe/describe.go @@ -7,6 +7,7 @@ import ( "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" "github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client" + "github.com/stackitcloud/stackit-cli/internal/pkg/tables" "github.com/stackitcloud/stackit-cli/internal/pkg/utils" "github.com/spf13/cobra" @@ -48,14 +49,7 @@ func NewCmd() *cobra.Command { return fmt.Errorf("describe SKE credential: %w", err) } - // Show details - details, err := json.MarshalIndent(resp, "", " ") - if err != nil { - return fmt.Errorf("marshal SKE credential: %w", err) - } - cmd.Println(string(details)) - - return nil + return outputResult(cmd, model.OutputFormat, resp) }, } configureFlags(cmd) @@ -90,3 +84,26 @@ func buildRequest(ctx context.Context, model *flagModel, apiClient *ske.APIClien req := apiClient.GetCredentials(ctx, model.ProjectId, model.ClusterName) return req } + +func outputResult(cmd *cobra.Command, outputFormat string, credential *ske.CredentialsResponse) error { + switch outputFormat { + case globalflags.TableOutputFormat: + table := tables.NewTable() + table.SetHeader("SERVER", "TOKEN") + table.AddRow(*credential.Server, *credential.Token) + err := table.Render(cmd) + if err != nil { + return fmt.Errorf("render table: %w", err) + } + + return nil + default: + details, err := json.MarshalIndent(credential, "", " ") + if err != nil { + return fmt.Errorf("marshal PostgreSQL credential: %w", err) + } + cmd.Println(string(details)) + + return nil + } +} From 45ecc391dd9bfb8b98300eb3429761fd0a9d1ffa Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 24 Nov 2023 16:53:52 +0000 Subject: [PATCH 6/7] Merge changes --- internal/cmd/ske/credential/describe/describe.go | 2 +- internal/cmd/ske/credential/rotate/rotate.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cmd/ske/credential/describe/describe.go b/internal/cmd/ske/credential/describe/describe.go index 0ebcdf41..d116a9ea 100644 --- a/internal/cmd/ske/credential/describe/describe.go +++ b/internal/cmd/ske/credential/describe/describe.go @@ -64,7 +64,7 @@ func configureFlags(cmd *cobra.Command) { } func parseFlags(cmd *cobra.Command) (*flagModel, error) { - globalFlags := globalflags.Parse() + globalFlags := globalflags.Parse(cmd) if globalFlags.ProjectId == "" { return nil, fmt.Errorf("project ID not set") } diff --git a/internal/cmd/ske/credential/rotate/rotate.go b/internal/cmd/ske/credential/rotate/rotate.go index b1d312cb..580273b9 100644 --- a/internal/cmd/ske/credential/rotate/rotate.go +++ b/internal/cmd/ske/credential/rotate/rotate.go @@ -63,7 +63,7 @@ func configureFlags(cmd *cobra.Command) { } func parseFlags(cmd *cobra.Command) (*flagModel, error) { - globalFlags := globalflags.Parse() + globalFlags := globalflags.Parse(cmd) if globalFlags.ProjectId == "" { return nil, fmt.Errorf("project ID not set") } From 7c1c787e2fcb5551c69117575dd3e73240adf057 Mon Sep 17 00:00:00 2001 From: Henrique Santos Date: Fri, 24 Nov 2023 16:55:35 +0000 Subject: [PATCH 7/7] Add confirmation prompt --- internal/cmd/ske/credential/rotate/rotate.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/cmd/ske/credential/rotate/rotate.go b/internal/cmd/ske/credential/rotate/rotate.go index 580273b9..0495c385 100644 --- a/internal/cmd/ske/credential/rotate/rotate.go +++ b/internal/cmd/ske/credential/rotate/rotate.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/stackitcloud/stackit-cli/internal/pkg/confirm" "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" "github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client" "github.com/stackitcloud/stackit-cli/internal/pkg/utils" @@ -34,6 +35,14 @@ func NewCmd() *cobra.Command { return err } + if !model.AssumeYes { + prompt := fmt.Sprintf("Are you sure you want to rotate credentials for project %s? (The old credentials will be invalid after this operation)", model.ProjectId) + err = confirm.PromptForConfirmation(cmd, prompt) + if err != nil { + return err + } + } + // Configure API client apiClient, err := client.ConfigureClient(cmd) if err != nil {