From 8f738682c7f30cb81ed626153b25b461628d2918 Mon Sep 17 00:00:00 2001 From: Marcel <72880145+marceljk@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:31:40 +0100 Subject: [PATCH] Revert "Onboard IaaS Server Service Account Commands (#522)" This reverts commit 0da6d09db4ec5e5b1ff06f32b7b916081bd8e0d5. --- docs/stackit_beta_server.md | 1 - docs/stackit_beta_server_service-account.md | 35 --- ...ckit_beta_server_service-account_attach.md | 40 ---- ...ckit_beta_server_service-account_detach.md | 40 ---- ...tackit_beta_server_service-account_list.md | 47 ---- internal/cmd/beta/server/server.go | 3 - .../server/service-account/attach/attach.go | 145 ----------- .../service-account/attach/attach_test.go | 226 ------------------ .../server/service-account/detach/detach.go | 145 ----------- .../service-account/detach/detach_test.go | 226 ------------------ .../beta/server/service-account/list/list.go | 170 ------------- .../server/service-account/list/list_test.go | 221 ----------------- .../server/service-account/service-account.go | 30 --- 13 files changed, 1329 deletions(-) delete mode 100644 docs/stackit_beta_server_service-account.md delete mode 100644 docs/stackit_beta_server_service-account_attach.md delete mode 100644 docs/stackit_beta_server_service-account_detach.md delete mode 100644 docs/stackit_beta_server_service-account_list.md delete mode 100644 internal/cmd/beta/server/service-account/attach/attach.go delete mode 100644 internal/cmd/beta/server/service-account/attach/attach_test.go delete mode 100644 internal/cmd/beta/server/service-account/detach/detach.go delete mode 100644 internal/cmd/beta/server/service-account/detach/detach_test.go delete mode 100644 internal/cmd/beta/server/service-account/list/list.go delete mode 100644 internal/cmd/beta/server/service-account/list/list_test.go delete mode 100644 internal/cmd/beta/server/service-account/service-account.go diff --git a/docs/stackit_beta_server.md b/docs/stackit_beta_server.md index 35a7fb435..5d2443980 100644 --- a/docs/stackit_beta_server.md +++ b/docs/stackit_beta_server.md @@ -36,7 +36,6 @@ stackit beta server [flags] * [stackit beta server describe](./stackit_beta_server_describe.md) - Shows details of a server * [stackit beta server list](./stackit_beta_server_list.md) - Lists all servers of a project * [stackit beta server public-ip](./stackit_beta_server_public-ip.md) - Allows attaching/detaching public IPs to servers -* [stackit beta server service-account](./stackit_beta_server_service-account.md) - Allows attaching/detaching service accounts to servers * [stackit beta server update](./stackit_beta_server_update.md) - Updates a server * [stackit beta server volume](./stackit_beta_server_volume.md) - Provides functionality for server volumes diff --git a/docs/stackit_beta_server_service-account.md b/docs/stackit_beta_server_service-account.md deleted file mode 100644 index 294f369d9..000000000 --- a/docs/stackit_beta_server_service-account.md +++ /dev/null @@ -1,35 +0,0 @@ -## stackit beta server service-account - -Allows attaching/detaching service accounts to servers - -### Synopsis - -Allows attaching/detaching service accounts to servers - -``` -stackit beta server service-account [flags] -``` - -### Options - -``` - -h, --help Help for "stackit beta server service-account" -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] - -p, --project-id string Project ID - --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") -``` - -### SEE ALSO - -* [stackit beta server](./stackit_beta_server.md) - Provides functionality for servers -* [stackit beta server service-account attach](./stackit_beta_server_service-account_attach.md) - Attach a service account to a server -* [stackit beta server service-account detach](./stackit_beta_server_service-account_detach.md) - Detach a service account from a server -* [stackit beta server service-account list](./stackit_beta_server_service-account_list.md) - List all attached service accounts for a server - diff --git a/docs/stackit_beta_server_service-account_attach.md b/docs/stackit_beta_server_service-account_attach.md deleted file mode 100644 index 5d00495df..000000000 --- a/docs/stackit_beta_server_service-account_attach.md +++ /dev/null @@ -1,40 +0,0 @@ -## stackit beta server service-account attach - -Attach a service account to a server - -### Synopsis - -Attach a service account to a server - -``` -stackit beta server service-account attach [flags] -``` - -### Examples - -``` - Attach a service account with mail "xxx@sa.stackit.cloud" to a server with ID "yyy" - $ stackit beta server service-account attach xxx@sa.stackit.cloud --server-id yyy -``` - -### Options - -``` - -h, --help Help for "stackit beta server service-account attach" - -s, --server-id string Server ID -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] - -p, --project-id string Project ID - --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") -``` - -### SEE ALSO - -* [stackit beta server service-account](./stackit_beta_server_service-account.md) - Allows attaching/detaching service accounts to servers - diff --git a/docs/stackit_beta_server_service-account_detach.md b/docs/stackit_beta_server_service-account_detach.md deleted file mode 100644 index 88276a8ce..000000000 --- a/docs/stackit_beta_server_service-account_detach.md +++ /dev/null @@ -1,40 +0,0 @@ -## stackit beta server service-account detach - -Detach a service account from a server - -### Synopsis - -Detach a service account from a server - -``` -stackit beta server service-account detach [flags] -``` - -### Examples - -``` - Detach a service account with mail "xxx@sa.stackit.cloud" from a server "yyy" - $ stackit beta server service-account detach xxx@sa.stackit.cloud --server-id yyy -``` - -### Options - -``` - -h, --help Help for "stackit beta server service-account detach" - -s, --server-id string Server id -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] - -p, --project-id string Project ID - --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") -``` - -### SEE ALSO - -* [stackit beta server service-account](./stackit_beta_server_service-account.md) - Allows attaching/detaching service accounts to servers - diff --git a/docs/stackit_beta_server_service-account_list.md b/docs/stackit_beta_server_service-account_list.md deleted file mode 100644 index 64f993e2d..000000000 --- a/docs/stackit_beta_server_service-account_list.md +++ /dev/null @@ -1,47 +0,0 @@ -## stackit beta server service-account list - -List all attached service accounts for a server - -### Synopsis - -List all attached service accounts for a server - -``` -stackit beta server service-account list [flags] -``` - -### Examples - -``` - List all attached service accounts for a server with ID "xxx" - $ stackit beta server service-account list --server-id xxx - - List up to 10 attached service accounts for a server with ID "xxx" - $ stackit beta server service-account list --server-id xxx --limit 10 - - List all attached service accounts for a server with ID "xxx" in JSON format - $ stackit beta server service-account list --server-id xxx --output-format json -``` - -### Options - -``` - -h, --help Help for "stackit beta server service-account list" - --limit int Maximum number of entries to list - -s, --server-id string Server ID -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] - -p, --project-id string Project ID - --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") -``` - -### SEE ALSO - -* [stackit beta server service-account](./stackit_beta_server_service-account.md) - Allows attaching/detaching service accounts to servers - diff --git a/internal/cmd/beta/server/server.go b/internal/cmd/beta/server/server.go index 3ca7dca6f..4b8ba0fd4 100644 --- a/internal/cmd/beta/server/server.go +++ b/internal/cmd/beta/server/server.go @@ -8,10 +8,8 @@ import ( "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server/describe" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server/list" publicip "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server/public-ip" - serviceaccount "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server/service-account" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server/update" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server/volume" - "github.com/stackitcloud/stackit-cli/internal/pkg/args" "github.com/stackitcloud/stackit-cli/internal/pkg/print" "github.com/stackitcloud/stackit-cli/internal/pkg/utils" @@ -39,7 +37,6 @@ func addSubcommands(cmd *cobra.Command, p *print.Printer) { cmd.AddCommand(describe.NewCmd(p)) cmd.AddCommand(list.NewCmd(p)) cmd.AddCommand(publicip.NewCmd(p)) - cmd.AddCommand(serviceaccount.NewCmd(p)) cmd.AddCommand(update.NewCmd(p)) cmd.AddCommand(volume.NewCmd(p)) } diff --git a/internal/cmd/beta/server/service-account/attach/attach.go b/internal/cmd/beta/server/service-account/attach/attach.go deleted file mode 100644 index c5cffdbad..000000000 --- a/internal/cmd/beta/server/service-account/attach/attach.go +++ /dev/null @@ -1,145 +0,0 @@ -package attach - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/errors" - "github.com/stackitcloud/stackit-cli/internal/pkg/examples" - "github.com/stackitcloud/stackit-cli/internal/pkg/flags" - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" - iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" - - "github.com/goccy/go-yaml" - "github.com/spf13/cobra" - "github.com/stackitcloud/stackit-sdk-go/services/iaas" -) - -const ( - serviceAccMailArg = "SERVICE_ACCOUNT_EMAIL" - - serverIdFlag = "server-id" -) - -type inputModel struct { - *globalflags.GlobalFlagModel - ServerId *string - ServiceAccMail string -} - -func NewCmd(p *print.Printer) *cobra.Command { - cmd := &cobra.Command{ - Use: "attach", - Short: "Attach a service account to a server", - Long: "Attach a service account to a server", - Args: args.SingleArg(serviceAccMailArg, nil), - Example: examples.Build( - examples.NewExample( - `Attach a service account with mail "xxx@sa.stackit.cloud" to a server with ID "yyy"`, - "$ stackit beta server service-account attach xxx@sa.stackit.cloud --server-id yyy", - ), - ), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - model, err := parseInput(p, cmd, args) - if err != nil { - return err - } - - // Configure API client - apiClient, err := client.ConfigureClient(p) - if err != nil { - return err - } - serverLabel, err := iaasUtils.GetServerName(ctx, apiClient, model.ProjectId, *model.ServerId) - if err != nil { - p.Debug(print.ErrorLevel, "get server name: %v", err) - serverLabel = *model.ServerId - } - - if !model.AssumeYes { - prompt := fmt.Sprintf("Are you sure you want to attach service account %q to server %q?", model.ServiceAccMail, serverLabel) - err = p.PromptForConfirmation(prompt) - if err != nil { - return err - } - } - - // Call API - req := buildRequest(ctx, model, apiClient) - resp, err := req.Execute() - if err != nil { - return fmt.Errorf("attach service account to server: %w", err) - } - - return outputResult(p, model.OutputFormat, model.ServiceAccMail, serverLabel, resp) - }, - } - configureFlags(cmd) - return cmd -} - -func configureFlags(cmd *cobra.Command) { - cmd.Flags().VarP(flags.UUIDFlag(), serverIdFlag, "s", "Server ID") - - err := flags.MarkFlagsRequired(cmd, serverIdFlag) - cobra.CheckErr(err) -} - -func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { - serviceAccMail := inputArgs[0] - globalFlags := globalflags.Parse(p, cmd) - if globalFlags.ProjectId == "" { - return nil, &errors.ProjectIdError{} - } - - model := inputModel{ - GlobalFlagModel: globalFlags, - ServerId: flags.FlagToStringPointer(p, cmd, serverIdFlag), - ServiceAccMail: serviceAccMail, - } - - 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 -} - -func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiAddServiceAccountToServerRequest { - req := apiClient.AddServiceAccountToServer(ctx, model.ProjectId, *model.ServerId, model.ServiceAccMail) - return req -} - -func outputResult(p *print.Printer, outputFormat, serviceAccMail, serverLabel string, serviceAccounts *iaas.ServiceAccountMailListResponse) error { - switch outputFormat { - case print.JSONOutputFormat: - details, err := json.MarshalIndent(serviceAccounts, "", " ") - if err != nil { - return fmt.Errorf("marshal service account: %w", err) - } - p.Outputln(string(details)) - - return nil - case print.YAMLOutputFormat: - details, err := yaml.MarshalWithOptions(serviceAccounts, yaml.IndentSequence(true)) - if err != nil { - return fmt.Errorf("marshal service account: %w", err) - } - p.Outputln(string(details)) - - return nil - default: - p.Outputf("Attached service account %q to server %q\n", serviceAccMail, serverLabel) - return nil - } -} diff --git a/internal/cmd/beta/server/service-account/attach/attach_test.go b/internal/cmd/beta/server/service-account/attach/attach_test.go deleted file mode 100644 index f7854c24b..000000000 --- a/internal/cmd/beta/server/service-account/attach/attach_test.go +++ /dev/null @@ -1,226 +0,0 @@ -package attach - -import ( - "context" - "testing" - - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/utils" - "github.com/stackitcloud/stackit-sdk-go/services/iaas" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/google/uuid" -) - -var projectIdFlag = globalflags.ProjectIdFlag - -type testCtxKey struct{} - -var testCtx = context.WithValue(context.Background(), &testCtxKey{}, "test") -var testClient = &iaas.APIClient{} -var testProjectId = uuid.NewString() -var testServerId = uuid.NewString() -var testServiceAccount = "test@example.com" - -func fixtureArgValues(mods ...func(argValues []string)) []string { - argValues := []string{ - testServiceAccount, - } - for _, mod := range mods { - mod(argValues) - } - return argValues -} - -func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { - flagValues := map[string]string{ - projectIdFlag: testProjectId, - serverIdFlag: testServerId, - } - for _, mod := range mods { - mod(flagValues) - } - return flagValues -} - -func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { - model := &inputModel{ - GlobalFlagModel: &globalflags.GlobalFlagModel{ - Verbosity: globalflags.VerbosityDefault, - ProjectId: testProjectId, - }, - ServerId: utils.Ptr(testServerId), - ServiceAccMail: testServiceAccount, - } - for _, mod := range mods { - mod(model) - } - return model -} - -func fixtureRequest(mods ...func(request *iaas.ApiAddServiceAccountToServerRequest)) iaas.ApiAddServiceAccountToServerRequest { - request := testClient.AddServiceAccountToServer(testCtx, testProjectId, testServerId, testServiceAccount) - for _, mod := range mods { - mod(&request) - } - return request -} - -func TestParseInput(t *testing.T) { - tests := []struct { - description string - argValues []string - flagValues map[string]string - isValid bool - expectedModel *inputModel - }{ - { - description: "base", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(), - isValid: true, - expectedModel: fixtureInputModel(), - }, - { - description: "no values", - argValues: fixtureArgValues(), - flagValues: map[string]string{}, - isValid: false, - }, - { - description: "project id missing", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, projectIdFlag) - }), - isValid: false, - }, - { - description: "project id invalid 1", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "" - }), - isValid: false, - }, - { - description: "project id invalid 2", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "invalid-uuid" - }), - isValid: false, - }, - { - description: "server id missing", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, serverIdFlag) - }), - }, - { - description: "server id invalid 1", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[serverIdFlag] = "" - }), - isValid: false, - }, - { - description: "server id invalid 2", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[serverIdFlag] = "invalid-uuid" - }), - isValid: false, - }, - { - description: "service account argument missing", - argValues: []string{}, - 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 parsing 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) - } - }) - } -} - -func TestBuildRequest(t *testing.T) { - tests := []struct { - description string - model *inputModel - expectedRequest iaas.ApiAddServiceAccountToServerRequest - }{ - { - description: "base", - model: fixtureInputModel(), - 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/beta/server/service-account/detach/detach.go b/internal/cmd/beta/server/service-account/detach/detach.go deleted file mode 100644 index 99e95506c..000000000 --- a/internal/cmd/beta/server/service-account/detach/detach.go +++ /dev/null @@ -1,145 +0,0 @@ -package detach - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/stackitcloud/stackit-cli/internal/pkg/args" - "github.com/stackitcloud/stackit-cli/internal/pkg/errors" - "github.com/stackitcloud/stackit-cli/internal/pkg/examples" - "github.com/stackitcloud/stackit-cli/internal/pkg/flags" - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" - iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" - - "github.com/goccy/go-yaml" - "github.com/spf13/cobra" - "github.com/stackitcloud/stackit-sdk-go/services/iaas" -) - -const ( - serviceAccMailArg = "SERVICE_ACCOUNT_EMAIL" - - serverIdFlag = "server-id" -) - -type inputModel struct { - *globalflags.GlobalFlagModel - ServerId *string - ServiceAccMail string -} - -func NewCmd(p *print.Printer) *cobra.Command { - cmd := &cobra.Command{ - Use: "detach", - Short: "Detach a service account from a server", - Long: "Detach a service account from a server", - Args: args.SingleArg(serviceAccMailArg, nil), - Example: examples.Build( - examples.NewExample( - `Detach a service account with mail "xxx@sa.stackit.cloud" from a server "yyy"`, - "$ stackit beta server service-account detach xxx@sa.stackit.cloud --server-id yyy", - ), - ), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - model, err := parseInput(p, cmd, args) - if err != nil { - return err - } - - // Configure API client - apiClient, err := client.ConfigureClient(p) - if err != nil { - return err - } - serverLabel, err := iaasUtils.GetServerName(ctx, apiClient, model.ProjectId, *model.ServerId) - if err != nil { - p.Debug(print.ErrorLevel, "get server name: %v", err) - serverLabel = *model.ServerId - } - - if !model.AssumeYes { - prompt := fmt.Sprintf("Are your sure you want to detach service account %q from a server %q?", model.ServiceAccMail, serverLabel) - err = p.PromptForConfirmation(prompt) - if err != nil { - return err - } - } - - // Call API - req := buildRequest(ctx, model, apiClient) - resp, err := req.Execute() - if err != nil { - return fmt.Errorf("detach service account request: %w", err) - } - - return outputResult(p, model.OutputFormat, model.ServiceAccMail, serverLabel, resp) - }, - } - configureFlags(cmd) - return cmd -} - -func configureFlags(cmd *cobra.Command) { - cmd.Flags().VarP(flags.UUIDFlag(), serverIdFlag, "s", "Server id") - - err := flags.MarkFlagsRequired(cmd, serverIdFlag) - cobra.CheckErr(err) -} - -func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { - serviceAccMail := inputArgs[0] - globalFlags := globalflags.Parse(p, cmd) - if globalFlags.ProjectId == "" { - return nil, &errors.ProjectIdError{} - } - - model := inputModel{ - GlobalFlagModel: globalFlags, - ServerId: flags.FlagToStringPointer(p, cmd, serverIdFlag), - ServiceAccMail: serviceAccMail, - } - - 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 -} - -func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiRemoveServiceAccountFromServerRequest { - req := apiClient.RemoveServiceAccountFromServer(ctx, model.ProjectId, *model.ServerId, model.ServiceAccMail) - return req -} - -func outputResult(p *print.Printer, outputFormat, serviceAccMail, serverLabel string, service *iaas.ServiceAccountMailListResponse) error { - switch outputFormat { - case print.JSONOutputFormat: - details, err := json.MarshalIndent(service, "", " ") - if err != nil { - return fmt.Errorf("marshal service account: %w", err) - } - p.Outputln(string(details)) - - return nil - case print.YAMLOutputFormat: - details, err := yaml.MarshalWithOptions(service, yaml.IndentSequence(true)) - if err != nil { - return fmt.Errorf("marshal service account: %w", err) - } - p.Outputln(string(details)) - - return nil - default: - p.Outputf("Detached service account %q from server %q\n", serviceAccMail, serverLabel) - return nil - } -} diff --git a/internal/cmd/beta/server/service-account/detach/detach_test.go b/internal/cmd/beta/server/service-account/detach/detach_test.go deleted file mode 100644 index 5250e0a6f..000000000 --- a/internal/cmd/beta/server/service-account/detach/detach_test.go +++ /dev/null @@ -1,226 +0,0 @@ -package detach - -import ( - "context" - "testing" - - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/utils" - "github.com/stackitcloud/stackit-sdk-go/services/iaas" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/google/uuid" -) - -var projectIdFlag = globalflags.ProjectIdFlag - -type testCtxKey struct{} - -var testCtx = context.WithValue(context.Background(), &testCtxKey{}, "test") -var testClient = &iaas.APIClient{} -var testProjectId = uuid.NewString() -var testServerId = uuid.NewString() -var testServiceAccount = "test@example.com" - -func fixtureArgValues(mods ...func(argValues []string)) []string { - argValues := []string{ - testServiceAccount, - } - for _, mod := range mods { - mod(argValues) - } - return argValues -} - -func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { - flagValues := map[string]string{ - projectIdFlag: testProjectId, - serverIdFlag: testServerId, - } - for _, mod := range mods { - mod(flagValues) - } - return flagValues -} - -func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { - model := &inputModel{ - GlobalFlagModel: &globalflags.GlobalFlagModel{ - Verbosity: globalflags.VerbosityDefault, - ProjectId: testProjectId, - }, - ServerId: utils.Ptr(testServerId), - ServiceAccMail: testServiceAccount, - } - for _, mod := range mods { - mod(model) - } - return model -} - -func fixtureRequest(mods ...func(request *iaas.ApiRemoveServiceAccountFromServerRequest)) iaas.ApiRemoveServiceAccountFromServerRequest { - request := testClient.RemoveServiceAccountFromServer(testCtx, testProjectId, testServerId, testServiceAccount) - for _, mod := range mods { - mod(&request) - } - return request -} - -func TestParseInput(t *testing.T) { - tests := []struct { - description string - argValues []string - flagValues map[string]string - isValid bool - expectedModel *inputModel - }{ - { - description: "base", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(), - isValid: true, - expectedModel: fixtureInputModel(), - }, - { - description: "no values", - argValues: fixtureArgValues(), - flagValues: map[string]string{}, - isValid: false, - }, - { - description: "project id missing", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, projectIdFlag) - }), - isValid: false, - }, - { - description: "project id invalid 1", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "" - }), - isValid: false, - }, - { - description: "project id invalid 2", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[projectIdFlag] = "invalid-uuid" - }), - isValid: false, - }, - { - description: "server id missing", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, serverIdFlag) - }), - }, - { - description: "server id invalid 1", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[serverIdFlag] = "" - }), - isValid: false, - }, - { - description: "server id invalid 2", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[serverIdFlag] = "invalid-uuid" - }), - isValid: false, - }, - { - description: "service account argument missing", - argValues: []string{}, - 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 parsing 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) - } - }) - } -} - -func TestBuildRequest(t *testing.T) { - tests := []struct { - description string - model *inputModel - expectedRequest iaas.ApiRemoveServiceAccountFromServerRequest - }{ - { - description: "base", - model: fixtureInputModel(), - 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/beta/server/service-account/list/list.go b/internal/cmd/beta/server/service-account/list/list.go deleted file mode 100644 index c938f2b80..000000000 --- a/internal/cmd/beta/server/service-account/list/list.go +++ /dev/null @@ -1,170 +0,0 @@ -package list - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/stackitcloud/stackit-cli/internal/pkg/errors" - "github.com/stackitcloud/stackit-cli/internal/pkg/examples" - "github.com/stackitcloud/stackit-cli/internal/pkg/flags" - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" - iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" - "github.com/stackitcloud/stackit-cli/internal/pkg/tables" - - "github.com/goccy/go-yaml" - "github.com/spf13/cobra" - "github.com/stackitcloud/stackit-sdk-go/services/iaas" -) - -const ( - serverIdFlag = "server-id" - limitFlag = "limit" -) - -type inputModel struct { - *globalflags.GlobalFlagModel - Limit *int64 - ServerId *string -} - -func NewCmd(p *print.Printer) *cobra.Command { - cmd := &cobra.Command{ - Use: "list", - Short: "List all attached service accounts for a server", - Long: "List all attached service accounts for a server", - Args: cobra.NoArgs, - Example: examples.Build( - examples.NewExample( - `List all attached service accounts for a server with ID "xxx"`, - "$ stackit beta server service-account list --server-id xxx", - ), - examples.NewExample( - `List up to 10 attached service accounts for a server with ID "xxx"`, - "$ stackit beta server service-account list --server-id xxx --limit 10", - ), - examples.NewExample( - `List all attached service accounts for a server with ID "xxx" in JSON format`, - "$ stackit beta server service-account list --server-id xxx --output-format json", - ), - ), - RunE: func(cmd *cobra.Command, _ []string) error { - ctx := context.Background() - model, err := parseInput(p, cmd) - if err != nil { - return err - } - - // Configure API client - apiClient, err := client.ConfigureClient(p) - if err != nil { - return err - } - - serverName, err := iaasUtils.GetServerName(ctx, apiClient, model.ProjectId, *model.ServerId) - if err != nil { - p.Debug(print.ErrorLevel, "get server name: %v", err) - serverName = *model.ServerId - } - - // Call API - req := buildRequest(ctx, model, apiClient) - resp, err := req.Execute() - if err != nil { - return fmt.Errorf("list service accounts: %w", err) - } - serviceAccounts := *resp.Items - if len(serviceAccounts) == 0 { - p.Info("No service accounts found for server %s\n", *model.ServerId) - return nil - } - - if model.Limit != nil && len(serviceAccounts) > int(*model.Limit) { - serviceAccounts = serviceAccounts[:int(*model.Limit)] - } - - return outputResult(p, model.OutputFormat, *model.ServerId, serverName, serviceAccounts) - }, - } - configureFlags(cmd) - return cmd -} - -func configureFlags(cmd *cobra.Command) { - cmd.Flags().VarP(flags.UUIDFlag(), serverIdFlag, "s", "Server ID") - cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list") - - err := flags.MarkFlagsRequired(cmd, serverIdFlag) - cobra.CheckErr(err) -} - -func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { - globalFlags := globalflags.Parse(p, cmd) - if globalFlags.ProjectId == "" { - return nil, &errors.ProjectIdError{} - } - - limit := flags.FlagToInt64Pointer(p, cmd, limitFlag) - if limit != nil && *limit < 1 { - return nil, &errors.FlagValidationError{ - Flag: limitFlag, - Details: "must be greater than 0", - } - } - - model := inputModel{ - GlobalFlagModel: globalFlags, - Limit: limit, - ServerId: flags.FlagToStringPointer(p, cmd, serverIdFlag), - } - - 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 -} - -func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiListServerServiceAccountsRequest { - req := apiClient.ListServerServiceAccounts(ctx, model.ProjectId, *model.ServerId) - return req -} - -func outputResult(p *print.Printer, outputFormat, serverId, serverName string, serviceAccounts []string) error { - switch outputFormat { - case print.JSONOutputFormat: - details, err := json.MarshalIndent(serviceAccounts, "", " ") - if err != nil { - return fmt.Errorf("marshal service accounts list: %w", err) - } - p.Outputln(string(details)) - - return nil - case print.YAMLOutputFormat: - details, err := yaml.MarshalWithOptions(serviceAccounts, yaml.IndentSequence(true)) - if err != nil { - return fmt.Errorf("marshal service accounts list: %w", err) - } - p.Outputln(string(details)) - - return nil - default: - table := tables.NewTable() - table.SetHeader("SERVER ID", "SERVER NAME", "SERVICE ACCOUNT") - for i := range serviceAccounts { - table.AddRow(serverId, serverName, serviceAccounts[i]) - } - err := table.Display(p) - if err != nil { - return fmt.Errorf("rednder table: %w", err) - } - return nil - } -} diff --git a/internal/cmd/beta/server/service-account/list/list_test.go b/internal/cmd/beta/server/service-account/list/list_test.go deleted file mode 100644 index 04ba8c721..000000000 --- a/internal/cmd/beta/server/service-account/list/list_test.go +++ /dev/null @@ -1,221 +0,0 @@ -package list - -import ( - "context" - "strconv" - "testing" - - "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/utils" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/google/uuid" - "github.com/stackitcloud/stackit-sdk-go/services/iaas" -) - -var projectIdFlag = globalflags.ProjectIdFlag - -type testCtxKey struct{} - -var testCtx = context.WithValue(context.Background(), &testCtxKey{}, "test") -var testClient = &iaas.APIClient{} -var testProjectId = uuid.NewString() -var testServerId = uuid.NewString() -var testLimit = int64(10) - -func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { - flagValues := map[string]string{ - projectIdFlag: testProjectId, - serverIdFlag: testServerId, - limitFlag: strconv.FormatInt(testLimit, 10), - } - for _, mod := range mods { - mod(flagValues) - } - return flagValues -} - -func fixtureInputModel(mods ...func(inputModel *inputModel)) *inputModel { - model := &inputModel{ - GlobalFlagModel: &globalflags.GlobalFlagModel{ - Verbosity: globalflags.VerbosityDefault, - ProjectId: testProjectId, - }, - ServerId: utils.Ptr(testServerId), - Limit: utils.Ptr(testLimit), - } - for _, mod := range mods { - mod(model) - } - return model -} - -func fixtureRequest(mods ...func(request *iaas.ApiListServerServiceAccountsRequest)) iaas.ApiListServerServiceAccountsRequest { - request := testClient.ListServerServiceAccounts(testCtx, testProjectId, testServerId) - for _, mod := range mods { - mod(&request) - } - return request -} - -func TestParseInput(t *testing.T) { - tests := []struct { - description string - flagValues map[string]string - isValid bool - expectedModel *inputModel - }{ - { - description: "base", - flagValues: fixtureFlagValues(), - isValid: true, - expectedModel: fixtureInputModel(), - }, - { - 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: "server id missing", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, serverIdFlag) - }), - isValid: false, - }, - { - description: "server id invalid 1", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[serverIdFlag] = "" - }), - isValid: false, - }, - { - description: "server id invalid 2", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[serverIdFlag] = "invalid-uuid" - }), - isValid: false, - }, - { - description: "without limit", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, limitFlag) - }), - isValid: true, - expectedModel: fixtureInputModel(func(model *inputModel) { - model.Limit = nil - }), - }, - { - description: "limit invalid 1", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[limitFlag] = "invalid" - }), - isValid: false, - }, - { - description: "limit invalid 2", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[limitFlag] = "0" - }), - 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.ValidateRequiredFlags() - if err != nil { - if !tt.isValid { - return - } - t.Fatalf("error validating flags: %v", err) - } - - model, err := parseInput(p, cmd) - 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) - } - }) - } -} - -func TestBuildRequest(t *testing.T) { - tests := []struct { - description string - model *inputModel - expectedRequest iaas.ApiListServerServiceAccountsRequest - }{ - { - description: "base", - model: fixtureInputModel(), - 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("Request does not match: %s", diff) - } - }) - } -} diff --git a/internal/cmd/beta/server/service-account/service-account.go b/internal/cmd/beta/server/service-account/service-account.go deleted file mode 100644 index e0907d4f1..000000000 --- a/internal/cmd/beta/server/service-account/service-account.go +++ /dev/null @@ -1,30 +0,0 @@ -package serviceaccount - -import ( - "github.com/spf13/cobra" - - "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server/service-account/attach" - "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server/service-account/detach" - "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server/service-account/list" - - "github.com/stackitcloud/stackit-cli/internal/pkg/print" - "github.com/stackitcloud/stackit-cli/internal/pkg/utils" -) - -func NewCmd(p *print.Printer) *cobra.Command { - cmd := &cobra.Command{ - Use: "service-account", - Short: "Allows attaching/detaching service accounts to servers", - Long: "Allows attaching/detaching service accounts to servers", - Args: cobra.NoArgs, - Run: utils.CmdHelp, - } - addSubcommands(cmd, p) - return cmd -} - -func addSubcommands(cmd *cobra.Command, p *print.Printer) { - cmd.AddCommand(attach.NewCmd(p)) - cmd.AddCommand(detach.NewCmd(p)) - cmd.AddCommand(list.NewCmd(p)) -}