mygithub.libinneed.workers.dev/stackitcloud/stackit-cli
Advanced tools
| ## stackit git flavor list | ||
| Lists instances flavors of STACKIT Git. | ||
| ### Synopsis | ||
| Lists instances flavors of STACKIT Git for the current project. | ||
| ``` | ||
| stackit git flavor list [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List STACKIT Git flavors | ||
| $ stackit git flavor list | ||
| Lists up to 10 STACKIT Git flavors | ||
| $ stackit git flavor list --limit=10 | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git flavor list" | ||
| --limit int Limit the output to the first n elements | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git flavor](./stackit_git_flavor.md) - Provides functionality for STACKIT Git flavors | ||
| ## stackit git flavor | ||
| Provides functionality for STACKIT Git flavors | ||
| ### Synopsis | ||
| Provides functionality for STACKIT Git flavors. | ||
| ``` | ||
| stackit git flavor [flags] | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git flavor" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git](./stackit_git.md) - Provides functionality for STACKIT Git | ||
| * [stackit git flavor list](./stackit_git_flavor_list.md) - Lists instances flavors of STACKIT Git. | ||
| ## stackit git instance create | ||
| Creates STACKIT Git instance | ||
| ### Synopsis | ||
| Create a STACKIT Git instance by name. | ||
| ``` | ||
| stackit git instance create [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Create a instance with name 'my-new-instance' | ||
| $ stackit git instance create --name my-new-instance | ||
| Create a instance with name 'my-new-instance' and flavor | ||
| $ stackit git instance create --name my-new-instance --flavor git-100' | ||
| Create a instance with name 'my-new-instance' and acl | ||
| $ stackit git instance create --name my-new-instance --acl 1.1.1.1/1' | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| --acl strings Acl for the instance. | ||
| --flavor string Flavor of the instance. | ||
| -h, --help Help for "stackit git instance create" | ||
| --name string The name of the instance. | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git instance](./stackit_git_instance.md) - Provides functionality for STACKIT Git instances | ||
| ## stackit git instance delete | ||
| Deletes STACKIT Git instance | ||
| ### Synopsis | ||
| Deletes a STACKIT Git instance by its internal ID. | ||
| ``` | ||
| stackit git instance delete INSTANCE_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Delete a instance with ID "xxx" | ||
| $ stackit git instance delete xxx | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git instance delete" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git instance](./stackit_git_instance.md) - Provides functionality for STACKIT Git instances | ||
| ## stackit git instance describe | ||
| Describes STACKIT Git instance | ||
| ### Synopsis | ||
| Describes a STACKIT Git instance by its internal ID. | ||
| ``` | ||
| stackit git instance describe INSTANCE_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Describe instance "xxx" | ||
| $ stackit git describe xxx | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git instance describe" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git instance](./stackit_git_instance.md) - Provides functionality for STACKIT Git instances | ||
| ## stackit git instance list | ||
| Lists all instances of STACKIT Git. | ||
| ### Synopsis | ||
| Lists all instances of STACKIT Git for the current project. | ||
| ``` | ||
| stackit git instance list [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List all STACKIT Git instances | ||
| $ stackit git instance list | ||
| Lists up to 10 STACKIT Git instances | ||
| $ stackit git instance list --limit=10 | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git instance list" | ||
| --limit int Limit the output to the first n elements | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git instance](./stackit_git_instance.md) - Provides functionality for STACKIT Git instances | ||
| ## stackit git instance | ||
| Provides functionality for STACKIT Git instances | ||
| ### Synopsis | ||
| Provides functionality for STACKIT Git instances. | ||
| ``` | ||
| stackit git instance [flags] | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git instance" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git](./stackit_git.md) - Provides functionality for STACKIT Git | ||
| * [stackit git instance create](./stackit_git_instance_create.md) - Creates STACKIT Git instance | ||
| * [stackit git instance delete](./stackit_git_instance_delete.md) - Deletes STACKIT Git instance | ||
| * [stackit git instance describe](./stackit_git_instance_describe.md) - Describes STACKIT Git instance | ||
| * [stackit git instance list](./stackit_git_instance_list.md) - Lists all instances of STACKIT Git. | ||
| ## stackit ske cluster hibernate | ||
| Trigger hibernate for a SKE cluster | ||
| ### Synopsis | ||
| Trigger hibernate for a STACKIT Kubernetes Engine (SKE) cluster. | ||
| ``` | ||
| stackit ske cluster hibernate CLUSTER_NAME [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Trigger hibernate for a SKE cluster with name "my-cluster" | ||
| $ stackit ske cluster hibernate my-cluster | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit ske cluster hibernate" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit ske cluster](./stackit_ske_cluster.md) - Provides functionality for SKE cluster | ||
| ## stackit ske cluster maintenance | ||
| Trigger maintenance for a SKE cluster | ||
| ### Synopsis | ||
| Trigger maintenance for a STACKIT Kubernetes Engine (SKE) cluster. | ||
| ``` | ||
| stackit ske cluster maintenance CLUSTER_NAME [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Trigger maintenance for a SKE cluster with name "my-cluster" | ||
| $ stackit ske cluster maintenance my-cluster | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit ske cluster maintenance" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit ske cluster](./stackit_ske_cluster.md) - Provides functionality for SKE cluster | ||
| ## stackit ske cluster reconcile | ||
| Trigger reconcile for a SKE cluster | ||
| ### Synopsis | ||
| Trigger reconcile for a STACKIT Kubernetes Engine (SKE) cluster. | ||
| ``` | ||
| stackit ske cluster reconcile CLUSTER_NAME [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Trigger reconcile for a SKE cluster with name "my-cluster" | ||
| $ stackit ske cluster reconcile my-cluster | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit ske cluster reconcile" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit ske cluster](./stackit_ske_cluster.md) - Provides functionality for SKE cluster | ||
| ## stackit ske cluster wakeup | ||
| Trigger wakeup from hibernation for a SKE cluster | ||
| ### Synopsis | ||
| Trigger wakeup from hibernation for a STACKIT Kubernetes Engine (SKE) cluster. | ||
| ``` | ||
| stackit ske cluster wakeup CLUSTER_NAME [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Trigger wakeup from hibernation for a SKE cluster with name "my-cluster" | ||
| $ stackit ske cluster wakeup my-cluster | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit ske cluster wakeup" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit ske cluster](./stackit_ske_cluster.md) - Provides functionality for SKE cluster | ||
| package flavor | ||
| import ( | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/flavor/list" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "flavor", | ||
| Short: "Provides functionality for STACKIT Git flavors", | ||
| Long: "Provides functionality for STACKIT Git flavors.", | ||
| Args: args.NoArgs, | ||
| Run: utils.CmdHelp, | ||
| } | ||
| addSubcommands(cmd, params) | ||
| return cmd | ||
| } | ||
| func addSubcommands(cmd *cobra.Command, params *params.CmdParams) { | ||
| cmd.AddCommand( | ||
| list.NewCmd(params), | ||
| ) | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "strconv" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/git" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &git.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| const ( | ||
| testLimit = 10 | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *git.ApiListFlavorsRequest)) git.ApiListFlavorsRequest { | ||
| request := testClient.ListFlavors(testCtx, testProjectId) | ||
| 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, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "with limit flag", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues["limit"] = strconv.Itoa(testLimit) | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Limit = utils.Ptr(int64(testLimit)) | ||
| }), | ||
| }, | ||
| { | ||
| description: "with limit flag == 0", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues["limit"] = strconv.Itoa(0) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "with limit flag < 0", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues["limit"] = strconv.Itoa(-1) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := NewCmd(¶ms.CmdParams{Printer: 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 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 *inputModel | ||
| expectedRequest git.ApiListFlavorsRequest | ||
| }{ | ||
| { | ||
| 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) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| type args struct { | ||
| outputFormat string | ||
| flavors []git.Flavor | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set empty flavors slice", | ||
| args: args{ | ||
| flavors: []git.Flavor{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set empty flavors in flavors slice", | ||
| args: args{ | ||
| flavors: []git.Flavor{{}}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.flavors); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| Limit *int64 | ||
| } | ||
| const limitFlag = "limit" | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "list", | ||
| Short: "Lists instances flavors of STACKIT Git.", | ||
| Long: "Lists instances flavors of STACKIT Git for the current project.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List STACKIT Git flavors`, | ||
| "$ stackit git flavor list"), | ||
| examples.NewExample( | ||
| "Lists up to 10 STACKIT Git flavors", | ||
| "$ stackit git flavor list --limit=10", | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, _ []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get STACKIT Git flavors: %w", err) | ||
| } | ||
| flavors := *resp.Flavors | ||
| if len(flavors) == 0 { | ||
| projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| projectLabel = model.ProjectId | ||
| } | ||
| params.Printer.Info("No flavors found for project %q\n", projectLabel) | ||
| return nil | ||
| } else if model.Limit != nil && len(flavors) > int(*model.Limit) { | ||
| flavors = (flavors)[:*model.Limit] | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, flavors) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Int64(limitFlag, 0, "Limit the output to the first n elements") | ||
| } | ||
| 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, | ||
| } | ||
| 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 *git.APIClient) git.ApiListFlavorsRequest { | ||
| return apiClient.ListFlavors(ctx, model.ProjectId) | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, flavors []git.Flavor) error { | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(flavors, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal Observability flavor list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(flavors, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal Observability flavor list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| default: | ||
| table := tables.NewTable() | ||
| table.SetHeader("ID", "DESCRIPTION", "DISPLAY_NAME", "AVAILABLE", "SKU") | ||
| for i := range flavors { | ||
| flavor := (flavors)[i] | ||
| table.AddRow( | ||
| utils.PtrString(flavor.Id), | ||
| utils.PtrString(flavor.Description), | ||
| utils.PtrString(flavor.DisplayName), | ||
| utils.PtrString(flavor.Availability), | ||
| utils.PtrString(flavor.Sku), | ||
| ) | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
| } |
| package create | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/git" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &git.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testName = "test-instance" | ||
| testFlavor = "git-100" | ||
| testAcl = []string{"0.0.0.0/0"} | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| nameFlag: testName, | ||
| flavorFlag: testFlavor, | ||
| aclFlag: testAcl[0], | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Verbosity: globalflags.VerbosityDefault}, | ||
| Name: testName, | ||
| Flavor: testFlavor, | ||
| Acl: testAcl, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureCreatePayload(mods ...func(payload *git.CreateInstancePayload)) (payload git.CreateInstancePayload) { | ||
| payload = git.CreateInstancePayload{ | ||
| Name: &testName, | ||
| Flavor: git.CreateInstancePayloadGetFlavorAttributeType(&testFlavor), | ||
| Acl: &testAcl, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(&payload) | ||
| } | ||
| return payload | ||
| } | ||
| func fixtureRequest(mods ...func(request *git.ApiCreateInstanceRequest)) git.ApiCreateInstanceRequest { | ||
| request := testClient.CreateInstance(testCtx, testProjectId) | ||
| request = request.CreateInstancePayload(fixtureCreatePayload()) | ||
| 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, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "name missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, nameFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := NewCmd(¶ms.CmdParams{Printer: p}) | ||
| if err := globalflags.Configure(cmd.Flags()); err != nil { | ||
| t.Errorf("cannot 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) | ||
| } | ||
| } | ||
| if err := cmd.ValidateFlagGroups(); err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| t.Fatalf("error validating flag groups: %v", err) | ||
| } | ||
| if err := cmd.ValidateRequiredFlags(); 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 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 *inputModel | ||
| expectedRequest git.ApiCreateInstanceRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| { | ||
| description: "name flag", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.Name = "new-name" | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *git.ApiCreateInstanceRequest) { | ||
| *request = (*request).CreateInstancePayload(fixtureCreatePayload(func(payload *git.CreateInstancePayload) { | ||
| payload.Name = utils.Ptr("new-name") | ||
| })) | ||
| }), | ||
| }, | ||
| } | ||
| 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), | ||
| cmp.AllowUnexported(git.NullableString{}), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("Data does not match: %s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| type args struct { | ||
| model *inputModel | ||
| resp *git.Instance | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "nil", | ||
| args: args{ | ||
| model: nil, | ||
| resp: nil, | ||
| }, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty input", | ||
| args: args{ | ||
| model: &inputModel{}, | ||
| resp: &git.Instance{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "output json", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| OutputFormat: print.JSONOutputFormat, | ||
| }, | ||
| }, | ||
| resp: nil, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package create | ||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/git/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git/wait" | ||
| ) | ||
| const ( | ||
| nameFlag = "name" | ||
| flavorFlag = "flavor" | ||
| aclFlag = "acl" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| Id *string | ||
| Name string | ||
| Flavor string | ||
| Acl []string | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "create", | ||
| Short: "Creates STACKIT Git instance", | ||
| Long: "Create a STACKIT Git instance by name.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Create a instance with name 'my-new-instance'`, | ||
| `$ stackit git instance create --name my-new-instance`, | ||
| ), | ||
| examples.NewExample( | ||
| `Create a instance with name 'my-new-instance' and flavor`, | ||
| `$ stackit git instance create --name my-new-instance --flavor git-100'`, | ||
| ), | ||
| examples.NewExample( | ||
| `Create a instance with name 'my-new-instance' and acl`, | ||
| `$ stackit git instance create --name my-new-instance --acl 1.1.1.1/1'`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, _ []string) (err error) { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if !model.AssumeYes { | ||
| prompt := fmt.Sprintf("Are you sure you want to create the instance %q?", model.Name) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| // Call API | ||
| request := buildRequest(ctx, model, apiClient) | ||
| result, err := request.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("create stackit git instance: %w", err) | ||
| } | ||
| model.Id = result.Id | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(params.Printer) | ||
| s.Start("Creating stackit git instance") | ||
| _, err = wait.CreateGitInstanceWaitHandler(ctx, apiClient, model.ProjectId, *model.Id).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for stackit git Instance creation: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| return outputResult(params.Printer, model, result) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().String(nameFlag, "", "The name of the instance.") | ||
| cmd.Flags().String(flavorFlag, "", "Flavor of the instance.") | ||
| cmd.Flags().StringSlice(aclFlag, []string{}, "Acl for the instance.") | ||
| if err := flags.MarkFlagsRequired(cmd, nameFlag); err != nil { | ||
| 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{} | ||
| } | ||
| name := flags.FlagToStringValue(p, cmd, nameFlag) | ||
| flavor := flags.FlagToStringValue(p, cmd, flavorFlag) | ||
| acl := flags.FlagToStringSliceValue(p, cmd, aclFlag) | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| Name: name, | ||
| Flavor: flavor, | ||
| Acl: acl, | ||
| } | ||
| 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 *git.APIClient) git.ApiCreateInstanceRequest { | ||
| return apiClient.CreateInstance(ctx, model.ProjectId).CreateInstancePayload(createPayload(model)) | ||
| } | ||
| func createPayload(model *inputModel) git.CreateInstancePayload { | ||
| return git.CreateInstancePayload{ | ||
| Name: &model.Name, | ||
| Flavor: git.CreateInstancePayloadGetFlavorAttributeType(&model.Flavor), | ||
| Acl: &model.Acl, | ||
| } | ||
| } | ||
| func outputResult(p *print.Printer, model *inputModel, resp *git.Instance) error { | ||
| if model == nil { | ||
| return fmt.Errorf("input model is nil") | ||
| } | ||
| var outputFormat string | ||
| if model.GlobalFlagModel != nil { | ||
| outputFormat = model.OutputFormat | ||
| } | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal instance: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal iminstanceage: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| default: | ||
| p.Outputf("Created instance %q with id %s\n", model.Name, utils.PtrString(model.Id)) | ||
| return nil | ||
| } | ||
| } |
| package delete | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &git.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testInstanceId = uuid.NewString() | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Verbosity: globalflags.VerbosityDefault}, | ||
| InstanceId: testInstanceId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *git.ApiDeleteInstanceRequest)) git.ApiDeleteInstanceRequest { | ||
| request := testClient.DeleteInstance(testCtx, testProjectId, testInstanceId) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| flagValues map[string]string | ||
| args []string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{testInstanceId}, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no arguments", | ||
| flagValues: fixtureFlagValues(), | ||
| args: nil, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "multiple arguments", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{"foo", "bar"}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid instance id", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{"foo"}, | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := NewCmd(¶ms.CmdParams{Printer: p}) | ||
| err := globalflags.Configure(cmd.Flags()) | ||
| if err != nil { | ||
| t.Fatalf("configure global flags: %v", err) | ||
| } | ||
| cmd.SetArgs(tt.args) | ||
| 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) | ||
| } | ||
| } | ||
| if err := cmd.ValidateArgs(tt.args); err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| } | ||
| err = cmd.ValidateRequiredFlags() | ||
| if err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| t.Fatalf("error validating flags: %v", err) | ||
| } | ||
| model, err := parseInput(p, cmd, tt.args) | ||
| 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 *inputModel | ||
| expectedRequest git.ApiDeleteInstanceRequest | ||
| }{ | ||
| { | ||
| 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) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package delete | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/spf13/cobra" | ||
| "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/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client" | ||
| gitUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git/wait" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| InstanceId string | ||
| } | ||
| const instanceIdArg = "INSTANCE_ID" | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("delete %s", instanceIdArg), | ||
| Short: "Deletes STACKIT Git instance", | ||
| Long: "Deletes a STACKIT Git instance by its internal ID.", | ||
| Args: args.SingleArg(instanceIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample(`Delete a instance with ID "xxx"`, | ||
| `$ stackit git instance delete xxx`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| projectName, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| projectName = model.ProjectId | ||
| } | ||
| instanceName, err := gitUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get stackit git intance name: %v", err) | ||
| instanceName = model.InstanceId | ||
| } else if instanceName == "" { | ||
| instanceName = model.InstanceId | ||
| } | ||
| if !model.AssumeYes { | ||
| prompt := fmt.Sprintf("Are you sure you want to delete the stackit git instance %q for %q?", instanceName, projectName) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| // Call API | ||
| request := buildRequest(ctx, model, apiClient) | ||
| err = request.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("delete instance: %w", err) | ||
| } | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(params.Printer) | ||
| s.Start("Deleting stackit git instance") | ||
| _, err = wait.DeleteGitInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for stackit git instance deletion: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| operationState := "Deleted" | ||
| if model.Async { | ||
| operationState = "Triggered deletion of" | ||
| } | ||
| params.Printer.Info("%s stackit git instance %s \n", operationState, model.InstanceId) | ||
| return nil | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, cliArgs []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| InstanceId: cliArgs[0], | ||
| } | ||
| 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 *git.APIClient) git.ApiDeleteInstanceRequest { | ||
| return apiClient.DeleteInstance(ctx, model.ProjectId, model.InstanceId) | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &git.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testInstanceId = []string{uuid.NewString()} | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Verbosity: globalflags.VerbosityDefault}, | ||
| InstanceId: testInstanceId[0], | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *git.ApiGetInstanceRequest)) git.ApiGetInstanceRequest { | ||
| request := testClient.GetInstance(testCtx, testProjectId, testInstanceId[0]) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| args []string | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| expectedModel: fixtureInputModel(), | ||
| args: testInstanceId, | ||
| isValid: true, | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| args: testInstanceId, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| args: testInstanceId, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| args: testInstanceId, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| args: testInstanceId, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no instance id passed", | ||
| flagValues: fixtureFlagValues(), | ||
| args: nil, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "multiple instance ids passed", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{uuid.NewString(), uuid.NewString()}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid instance id passed", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{"foobar"}, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := NewCmd(¶ms.CmdParams{Printer: p}) | ||
| if err := globalflags.Configure(cmd.Flags()); err != nil { | ||
| t.Errorf("cannot 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) | ||
| } | ||
| } | ||
| if err := cmd.ValidateRequiredFlags(); err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| t.Fatalf("error validating flags: %v", err) | ||
| } | ||
| if err := cmd.ValidateArgs(tt.args); err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| } | ||
| model, err := parseInput(p, cmd, tt.args) | ||
| 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 *inputModel | ||
| expectedRequest git.ApiGetInstanceRequest | ||
| }{ | ||
| { | ||
| 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) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| type args struct { | ||
| outputFormat string | ||
| resp *git.Instance | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{ | ||
| resp: &git.Instance{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "nil", | ||
| args: args{}, | ||
| wantErr: true, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
| "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/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| InstanceId string | ||
| } | ||
| const instanceIdArg = "INSTANCE_ID" | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("describe %s", instanceIdArg), | ||
| Short: "Describes STACKIT Git instance", | ||
| Long: "Describes a STACKIT Git instance by its internal ID.", | ||
| Args: args.SingleArg(instanceIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample(`Describe instance "xxx"`, `$ stackit git describe xxx`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| request := buildRequest(ctx, model, apiClient) | ||
| instance, err := request.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get instance: %w", err) | ||
| } | ||
| if err := outputResult(params.Printer, model.OutputFormat, instance); err != nil { | ||
| return err | ||
| } | ||
| return nil | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, cliArgs []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| InstanceId: cliArgs[0], | ||
| } | ||
| 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 *git.APIClient) git.ApiGetInstanceRequest { | ||
| return apiClient.GetInstance(ctx, model.ProjectId, model.InstanceId) | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, resp *git.Instance) error { | ||
| if resp == nil { | ||
| return fmt.Errorf("instance not found") | ||
| } | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal instance: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal instance: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| default: | ||
| table := tables.NewTable() | ||
| if id := resp.Id; id != nil { | ||
| table.AddRow("ID", *id) | ||
| table.AddSeparator() | ||
| } | ||
| if name := resp.Name; name != nil { | ||
| table.AddRow("NAME", *name) | ||
| table.AddSeparator() | ||
| } | ||
| if url := resp.Url; url != nil { | ||
| table.AddRow("URL", *url) | ||
| table.AddSeparator() | ||
| } | ||
| if version := resp.Version; version != nil { | ||
| table.AddRow("VERSION", *version) | ||
| table.AddSeparator() | ||
| } | ||
| if state := resp.State; state != nil { | ||
| table.AddRow("STATE", *state) | ||
| table.AddSeparator() | ||
| } | ||
| if created := resp.Created; created != nil { | ||
| table.AddRow("CREATED", *created) | ||
| table.AddSeparator() | ||
| } | ||
| if err := table.Display(p); err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
| } |
| package instance | ||
| import ( | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/instance/create" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/instance/delete" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/instance/describe" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/instance/list" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "instance", | ||
| Short: "Provides functionality for STACKIT Git instances", | ||
| Long: "Provides functionality for STACKIT Git instances.", | ||
| Args: args.NoArgs, | ||
| Run: utils.CmdHelp, | ||
| } | ||
| addSubcommands(cmd, params) | ||
| return cmd | ||
| } | ||
| func addSubcommands(cmd *cobra.Command, params *params.CmdParams) { | ||
| cmd.AddCommand( | ||
| list.NewCmd(params), | ||
| describe.NewCmd(params), | ||
| create.NewCmd(params), | ||
| delete.NewCmd(params), | ||
| ) | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "strconv" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/git" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &git.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| const ( | ||
| testLimit = 10 | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *git.ApiListInstancesRequest)) git.ApiListInstancesRequest { | ||
| request := testClient.ListInstances(testCtx, testProjectId) | ||
| 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, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "with limit flag", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues["limit"] = strconv.Itoa(testLimit) | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Limit = utils.Ptr(int64(testLimit)) | ||
| }), | ||
| }, | ||
| { | ||
| description: "with limit flag == 0", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues["limit"] = strconv.Itoa(0) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "with limit flag < 0", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues["limit"] = strconv.Itoa(-1) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := NewCmd(¶ms.CmdParams{Printer: 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 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 *inputModel | ||
| expectedRequest git.ApiListInstancesRequest | ||
| }{ | ||
| { | ||
| 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) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| type args struct { | ||
| outputFormat string | ||
| instances []git.Instance | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set empty instances slice", | ||
| args: args{ | ||
| instances: []git.Instance{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set empty instances in instances slice", | ||
| args: args{ | ||
| instances: []git.Instance{{}}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.instances); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
| "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/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| Limit *int64 | ||
| } | ||
| const limitFlag = "limit" | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "list", | ||
| Short: "Lists all instances of STACKIT Git.", | ||
| Long: "Lists all instances of STACKIT Git for the current project.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List all STACKIT Git instances`, | ||
| "$ stackit git instance list"), | ||
| examples.NewExample( | ||
| "Lists up to 10 STACKIT Git instances", | ||
| "$ stackit git instance list --limit=10", | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, _ []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get STACKIT Git instances: %w", err) | ||
| } | ||
| instances := *resp.Instances | ||
| if len(instances) == 0 { | ||
| projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| projectLabel = model.ProjectId | ||
| } | ||
| params.Printer.Info("No instances found for project %q\n", projectLabel) | ||
| return nil | ||
| } else if model.Limit != nil && len(instances) > int(*model.Limit) { | ||
| instances = (instances)[:*model.Limit] | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, instances) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Int64(limitFlag, 0, "Limit the output to the first n elements") | ||
| } | ||
| 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, | ||
| } | ||
| 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 *git.APIClient) git.ApiListInstancesRequest { | ||
| return apiClient.ListInstances(ctx, model.ProjectId) | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, instances []git.Instance) error { | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(instances, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal Observability instance list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(instances, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal Observability instance list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| default: | ||
| table := tables.NewTable() | ||
| table.SetHeader("ID", "NAME", "URL", "VERSION", "STATE", "CREATED") | ||
| for i := range instances { | ||
| instance := (instances)[i] | ||
| table.AddRow( | ||
| utils.PtrString(instance.Id), | ||
| utils.PtrString(instance.Name), | ||
| utils.PtrString(instance.Url), | ||
| utils.PtrString(instance.Version), | ||
| utils.PtrString(instance.State), | ||
| utils.PtrString(instance.Created), | ||
| ) | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
| } |
| package hibernate | ||
| 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-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu01" | ||
| testClusterName = "my-cluster" | ||
| ) | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &ske.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| func fixtureArgValues(mods ...func(argValues []string)) []string { | ||
| argValues := []string{ | ||
| testClusterName, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| ClusterName: testClusterName, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *ske.ApiTriggerHibernateRequest)) ske.ApiTriggerHibernateRequest { | ||
| request := testClient.TriggerHibernate(testCtx, testProjectId, testRegion, testClusterName) | ||
| 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: "missing project id", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| delete(fv, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid project id - empty string", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| fv[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid uuid format", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| fv[globalflags.ProjectIdFlag] = "not-a-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := &cobra.Command{} | ||
| 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) | ||
| } | ||
| } | ||
| if len(tt.argValues) == 0 { | ||
| _, err := parseInput(p, cmd, tt.argValues) | ||
| if err == nil && !tt.isValid { | ||
| t.Fatalf("expected error due to missing args") | ||
| } | ||
| return | ||
| } | ||
| model, err := parseInput(p, cmd, tt.argValues) | ||
| 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:\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest ske.ApiTriggerHibernateRequest | ||
| }{ | ||
| { | ||
| 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, | ||
| cmpopts.EquateComparable(testCtx), | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("request mismatch:\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package hibernate | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske/wait" | ||
| ) | ||
| const ( | ||
| clusterNameArg = "CLUSTER_NAME" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| ClusterName string | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("hibernate %s", clusterNameArg), | ||
| Short: "Trigger hibernate for a SKE cluster", | ||
| Long: "Trigger hibernate for a STACKIT Kubernetes Engine (SKE) cluster.", | ||
| Args: args.SingleArg(clusterNameArg, nil), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Trigger hibernate for a SKE cluster with name "my-cluster"`, | ||
| "$ stackit ske cluster hibernate my-cluster"), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| projectLabel = model.ProjectId | ||
| } | ||
| if !model.AssumeYes { | ||
| prompt := fmt.Sprintf("Are you sure you want to trigger hibernate for %q in project %q?", model.ClusterName, projectLabel) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| _, err = req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("hibernate SKE cluster: %w", err) | ||
| } | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(params.Printer) | ||
| s.Start("Hibernating cluster") | ||
| _, err = wait.TriggerClusterHibernationWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.ClusterName).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for SKE cluster hibernation: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| operationState := "Hibernated" | ||
| if model.Async { | ||
| operationState = "Triggered hibernation of" | ||
| } | ||
| params.Printer.Outputf("%s cluster %q\n", operationState, model.ClusterName) | ||
| return nil | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| clusterName := inputArgs[0] | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| ClusterName: clusterName, | ||
| } | ||
| 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 *ske.APIClient) ske.ApiTriggerHibernateRequest { | ||
| req := apiClient.TriggerHibernate(ctx, model.ProjectId, model.Region, model.ClusterName) | ||
| return req | ||
| } |
| package maintenance | ||
| 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-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu01" | ||
| testClusterName = "my-cluster" | ||
| ) | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &ske.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| func fixtureArgValues(mods ...func([]string)) []string { | ||
| argValues := []string{ | ||
| testClusterName, | ||
| } | ||
| for _, m := range mods { | ||
| m(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| func fixtureFlagValues(mods ...func(map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, m := range mods { | ||
| m(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(*inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| ClusterName: testClusterName, | ||
| } | ||
| for _, m := range mods { | ||
| m(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(*ske.ApiTriggerMaintenanceRequest)) ske.ApiTriggerMaintenanceRequest { | ||
| request := testClient.TriggerMaintenance(testCtx, testProjectId, testRegion, testClusterName) | ||
| for _, m := range mods { | ||
| m(&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: "missing project id", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| delete(fv, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid project id - empty string", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| fv[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid uuid format", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| fv[globalflags.ProjectIdFlag] = "not-a-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := &cobra.Command{} | ||
| 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) | ||
| } | ||
| } | ||
| if len(tt.argValues) == 0 { | ||
| _, err := parseInput(p, cmd, tt.argValues) | ||
| if err == nil && !tt.isValid { | ||
| t.Fatalf("expected error due to missing args") | ||
| } | ||
| return | ||
| } | ||
| 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("input model mismatch:\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest ske.ApiTriggerMaintenanceRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| got := buildRequest(testCtx, tt.model, testClient) | ||
| want := tt.expectedRequest | ||
| diff := cmp.Diff(got, want, | ||
| cmpopts.EquateComparable(testCtx), | ||
| cmp.AllowUnexported(want), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("request mismatch:\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package maintenance | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske/wait" | ||
| ) | ||
| const ( | ||
| clusterNameArg = "CLUSTER_NAME" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| ClusterName string | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("maintenance %s", clusterNameArg), | ||
| Short: "Trigger maintenance for a SKE cluster", | ||
| Long: "Trigger maintenance for a STACKIT Kubernetes Engine (SKE) cluster.", | ||
| Args: args.SingleArg(clusterNameArg, nil), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Trigger maintenance for a SKE cluster with name "my-cluster"`, | ||
| "$ stackit ske cluster maintenance my-cluster"), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| projectLabel = model.ProjectId | ||
| } | ||
| if !model.AssumeYes { | ||
| prompt := fmt.Sprintf("Are you sure you want to trigger maintenance for %q in project %q?", model.ClusterName, projectLabel) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| _, err = req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("trigger maintenance SKE cluster: %w", err) | ||
| } | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(params.Printer) | ||
| s.Start("Performing cluster maintenance") | ||
| _, err = wait.TriggerClusterMaintenanceWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.ClusterName).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for SKE cluster maintenance to complete: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| operationState := "Performed maintenance for" | ||
| if model.Async { | ||
| operationState = "Triggered maintenance for" | ||
| } | ||
| params.Printer.Outputf("%s cluster %q\n", operationState, model.ClusterName) | ||
| return nil | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| clusterName := inputArgs[0] | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| ClusterName: clusterName, | ||
| } | ||
| 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 *ske.APIClient) ske.ApiTriggerMaintenanceRequest { | ||
| req := apiClient.TriggerMaintenance(ctx, model.ProjectId, model.Region, model.ClusterName) | ||
| return req | ||
| } |
| package reconcile | ||
| 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-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu01" | ||
| testClusterName = "my-cluster" | ||
| ) | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &ske.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| func fixtureArgValues(mods ...func([]string)) []string { | ||
| argValues := []string{ | ||
| testClusterName, | ||
| } | ||
| for _, m := range mods { | ||
| m(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| func fixtureFlagValues(mods ...func(map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, m := range mods { | ||
| m(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(*inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| ClusterName: testClusterName, | ||
| } | ||
| for _, m := range mods { | ||
| m(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *ske.ApiTriggerReconcileRequest)) ske.ApiTriggerHibernateRequest { | ||
| request := testClient.TriggerReconcile(testCtx, testProjectId, testRegion, testClusterName) | ||
| for _, m := range mods { | ||
| m(&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: "missing project id", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| delete(fv, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid project id - empty string", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| fv[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid uuid format", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| fv[globalflags.ProjectIdFlag] = "not-a-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := &cobra.Command{} | ||
| 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) | ||
| } | ||
| } | ||
| if len(tt.argValues) == 0 { | ||
| _, err := parseInput(p, cmd, tt.argValues) | ||
| if err == nil && !tt.isValid { | ||
| t.Fatalf("expected error due to missing args") | ||
| } | ||
| return | ||
| } | ||
| 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("input model mismatch:\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest ske.ApiTriggerHibernateRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| got := buildRequest(testCtx, tt.model, testClient) | ||
| want := tt.expectedRequest | ||
| diff := cmp.Diff(got, want, | ||
| cmpopts.EquateComparable(testCtx), | ||
| cmp.AllowUnexported(want), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("request mismatch:\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package reconcile | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske/wait" | ||
| ) | ||
| const ( | ||
| clusterNameArg = "CLUSTER_NAME" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| ClusterName string | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("reconcile %s", clusterNameArg), | ||
| Short: "Trigger reconcile for a SKE cluster", | ||
| Long: "Trigger reconcile for a STACKIT Kubernetes Engine (SKE) cluster.", | ||
| Args: args.SingleArg(clusterNameArg, nil), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Trigger reconcile for a SKE cluster with name "my-cluster"`, | ||
| "$ stackit ske cluster reconcile my-cluster"), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| _, err = req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("reconcile SKE cluster: %w", err) | ||
| } | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(params.Printer) | ||
| s.Start("Performing cluster reconciliation") | ||
| _, err = wait.TriggerClusterReconciliationWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.ClusterName).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for SKE cluster reconciliation: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| operationState := "Performed reconciliation for" | ||
| if model.Async { | ||
| operationState = "Triggered reconcile for" | ||
| } | ||
| params.Printer.Outputf("%s cluster %q\n", operationState, model.ClusterName) | ||
| return nil | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| clusterName := inputArgs[0] | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| ClusterName: clusterName, | ||
| } | ||
| 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 *ske.APIClient) ske.ApiTriggerReconcileRequest { | ||
| req := apiClient.TriggerReconcile(ctx, model.ProjectId, model.Region, model.ClusterName) | ||
| return req | ||
| } |
| package wakeup | ||
| 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-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu01" | ||
| testClusterName = "my-cluster" | ||
| ) | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &ske.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| func fixtureArgValues(mods ...func([]string)) []string { | ||
| argValues := []string{testClusterName} | ||
| for _, m := range mods { | ||
| m(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| func fixtureFlagValues(mods ...func(map[string]string)) map[string]string { | ||
| flags := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, m := range mods { | ||
| m(flags) | ||
| } | ||
| return flags | ||
| } | ||
| func fixtureInputModel(mods ...func(*inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| ClusterName: testClusterName, | ||
| } | ||
| for _, m := range mods { | ||
| m(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(*ske.ApiTriggerWakeupRequest)) ske.ApiTriggerWakeupRequest { | ||
| req := testClient.TriggerWakeup(testCtx, testProjectId, testRegion, testClusterName) | ||
| for _, m := range mods { | ||
| m(&req) | ||
| } | ||
| return req | ||
| } | ||
| 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: "missing project id", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| delete(fv, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid project id - empty string", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| fv[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid uuid format", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(fv map[string]string) { | ||
| fv[globalflags.ProjectIdFlag] = "not-a-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := &cobra.Command{} | ||
| 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) | ||
| } | ||
| } | ||
| if len(tt.argValues) == 0 { | ||
| _, err := parseInput(p, cmd, tt.argValues) | ||
| if err == nil && !tt.isValid { | ||
| t.Fatalf("expected failure due to missing args") | ||
| } | ||
| return | ||
| } | ||
| model, err := parseInput(p, cmd, tt.argValues) | ||
| if err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
| if !tt.isValid { | ||
| t.Fatalf("did not fail on invalid input") | ||
| } | ||
| diff := cmp.Diff(model, tt.expectedModel) | ||
| if diff != "" { | ||
| t.Fatalf("input model mismatch:\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest ske.ApiTriggerHibernateRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| got := buildRequest(testCtx, tt.model, testClient) | ||
| want := tt.expectedRequest | ||
| diff := cmp.Diff(got, want, | ||
| cmpopts.EquateComparable(testCtx), | ||
| cmp.AllowUnexported(want), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("request mismatch:\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package wakeup | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/ske/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske/wait" | ||
| ) | ||
| const ( | ||
| clusterNameArg = "CLUSTER_NAME" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| ClusterName string | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("wakeup %s", clusterNameArg), | ||
| Short: "Trigger wakeup from hibernation for a SKE cluster", | ||
| Long: "Trigger wakeup from hibernation for a STACKIT Kubernetes Engine (SKE) cluster.", | ||
| Args: args.SingleArg(clusterNameArg, nil), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Trigger wakeup from hibernation for a SKE cluster with name "my-cluster"`, | ||
| "$ stackit ske cluster wakeup my-cluster"), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| _, err = req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("wakeup SKE cluster: %w", err) | ||
| } | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(params.Printer) | ||
| s.Start("Performing cluster wakeup") | ||
| _, err = wait.TriggerClusterWakeupWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.ClusterName).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for SKE cluster wakeup: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| operationState := "Performed wakeup of" | ||
| if model.Async { | ||
| operationState = "Triggered wakeup of" | ||
| } | ||
| params.Printer.Outputf("%s cluster %q\n", operationState, model.ClusterName) | ||
| return nil | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| clusterName := inputArgs[0] | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| ClusterName: clusterName, | ||
| } | ||
| 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 *ske.APIClient) ske.ApiTriggerWakeupRequest { | ||
| req := apiClient.TriggerWakeup(ctx, model.ProjectId, model.Region, model.ClusterName) | ||
| return req | ||
| } |
| #!/bin/bash | ||
| # This script is used to publish new packages to the CLI RPM repository | ||
| # Usage: ./publish-rpm-packages.sh | ||
| set -eo pipefail | ||
| ROOT_DIR=$(git rev-parse --show-toplevel) | ||
| PACKAGES_BUCKET_URL="https://packages.stackit.cloud" | ||
| RPM_REPO_PATH="rpm/cli" | ||
| RPM_BUCKET_NAME="distribution" | ||
| CUSTOM_KEYRING_FILE="rpm-keyring.gpg" | ||
| GORELEASER_PACKAGES_FOLDER="dist/" | ||
| TEMP_DIR=$(mktemp -d) | ||
| # We need to disable the key database daemon (keyboxd) | ||
| # This can be done by removing "use-keyboxd" from ~/.gnupg/common.conf (see https://github.com/gpg/gnupg/blob/master/README) | ||
| echo -n >~/.gnupg/common.conf | ||
| # Create a local mirror of the current state of the remote RPM repository | ||
| printf ">>> Creating mirror \n" | ||
| curl ${PACKAGES_BUCKET_URL}/${RPM_REPO_PATH}/repodata/repomd.xml >${TEMP_DIR}/repomd.xml || echo "No existing repository found, creating new one" | ||
| # Create RPM repository structure | ||
| mkdir -p ${TEMP_DIR}/rpm-repo/RPMS | ||
| # Copy existing RPMs from remote repository (if any) | ||
| printf "\n>>> Downloading existing RPMs \n" | ||
| aws s3 sync s3://${RPM_BUCKET_NAME}/${RPM_REPO_PATH}/RPMS/ ${TEMP_DIR}/rpm-repo/RPMS/ --endpoint-url https://object.storage.eu01.onstackit.cloud || echo "No existing RPMs found" | ||
| # Copy new generated .rpm packages to the local repo | ||
| # Note: GoReleaser already signs these RPM packages with embedded signatures | ||
| printf "\n>>> Adding new packages to local repo \n" | ||
| cp ${GORELEASER_PACKAGES_FOLDER}/*.rpm ${TEMP_DIR}/rpm-repo/RPMS/ | ||
| # Create RPM repository metadata using createrepo_c | ||
| printf "\n>>> Creating RPM repository metadata \n" | ||
| docker run --rm \ | ||
| -v "${TEMP_DIR}/rpm-repo:/repo" \ | ||
| fedora:latest \ | ||
| bash -c " | ||
| # Install createrepo_c | ||
| dnf install -y createrepo_c | ||
| # Create repository metadata | ||
| createrepo_c /repo | ||
| " | ||
| # Sign the repository metadata using the same GPG key as APT | ||
| if [ -n "$GPG_PRIVATE_KEY_FINGERPRINT" ] && [ -n "$GPG_PASSPHRASE" ]; then | ||
| printf "\n>>> Signing repository metadata \n" | ||
| gpg --batch --yes --pinentry-mode loopback --local-user="${GPG_PRIVATE_KEY_FINGERPRINT}" --passphrase="${GPG_PASSPHRASE}" --detach-sign --armor ${TEMP_DIR}/rpm-repo/repodata/repomd.xml | ||
| else | ||
| echo ">>> Skipping repository metadata signing (GPG environment variables not set)" | ||
| fi | ||
| # Upload to S3 | ||
| printf "\n>>> Uploading to S3 \n" | ||
| aws s3 sync ${TEMP_DIR}/rpm-repo/ s3://${RPM_BUCKET_NAME}/${RPM_REPO_PATH}/ --endpoint-url https://object.storage.eu01.onstackit.cloud | ||
| # Clean up | ||
| rm -rf ${TEMP_DIR} | ||
| printf "\n>>> RPM repository published successfully to ${PACKAGES_BUCKET_URL}/${RPM_REPO_PATH} \n" |
@@ -67,5 +67,7 @@ name: CI | ||
| steps: | ||
| - uses: fgrosse/go-coverage-report@v1.2.0 | ||
| - name: Check new code coverage | ||
| uses: fgrosse/go-coverage-report@v1.2.0 | ||
| continue-on-error: true # Add this line to prevent pipeline failures in forks | ||
| with: | ||
| coverage-artifact-name: ${{ env.CODE_COVERAGE_ARTIFACT_NAME }} | ||
| coverage-file-name: ${{ env.CODE_COVERAGE_FILE_NAME }} | ||
| coverage-file-name: ${{ env.CODE_COVERAGE_FILE_NAME }} |
@@ -78,2 +78,4 @@ # STACKIT CLI release workflow. | ||
| GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} | ||
| - name: Install createrepo_c | ||
| run: brew install createrepo_c | ||
| - name: Publish packages to APT repo | ||
@@ -85,1 +87,7 @@ if: contains(github.ref_name, '-') == false | ||
| run: ./scripts/publish-apt-packages.sh | ||
| - name: Publish packages to RPM repo | ||
| if: contains(github.ref_name, '-') == false | ||
| env: | ||
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | ||
| GPG_PRIVATE_KEY_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} | ||
| run: ./scripts/publish-rpm-packages.sh |
@@ -16,5 +16,5 @@ name: Renovate | ||
| - name: Self-hosted Renovate | ||
| uses: renovatebot/github-action@v43.0.2 | ||
| uses: renovatebot/github-action@v43.0.5 | ||
| with: | ||
| configurationFile: .github/renovate.json | ||
| token: ${{ secrets.RENOVATE_TOKEN }} |
+1
-2
@@ -168,4 +168,3 @@ version: 2 | ||
| # if the tag has a prerelease indicator (e.g. v0.0.1-alpha1) | ||
| # Temporarily not skipping prereleases to test integration with Winget | ||
| # skip_upload: auto | ||
| skip_upload: auto | ||
| repository: | ||
@@ -172,0 +171,0 @@ owner: stackitcloud |
@@ -33,6 +33,4 @@ ## stackit git | ||
| * [stackit](./stackit.md) - Manage STACKIT resources using the command line | ||
| * [stackit git create](./stackit_git_create.md) - Creates STACKIT Git instance | ||
| * [stackit git delete](./stackit_git_delete.md) - Deletes STACKIT Git instance | ||
| * [stackit git describe](./stackit_git_describe.md) - Describes STACKIT Git instance | ||
| * [stackit git list](./stackit_git_list.md) - Lists all instances of STACKIT Git. | ||
| * [stackit git flavor](./stackit_git_flavor.md) - Provides functionality for STACKIT Git flavors | ||
| * [stackit git instance](./stackit_git_instance.md) - Provides functionality for STACKIT Git instances | ||
@@ -32,3 +32,3 @@ ## stackit mongodbflex user create | ||
| --instance-id string ID of the instance | ||
| --role strings Roles of the user, possible values are ["read" "readWrite"] (default [read]) | ||
| --role strings Roles of the user, possible values are ["read" "readWrite" "readWriteAnyDatabase"] (default [read]) | ||
| --username string Username of the user. If not specified, a random username will be assigned | ||
@@ -35,0 +35,0 @@ ``` |
@@ -26,3 +26,3 @@ ## stackit mongodbflex user update | ||
| --instance-id string ID of the instance | ||
| --role strings Roles of the user, possible values are ["read" "readWrite"] (default []) | ||
| --role strings Roles of the user, possible values are ["read" "readWrite" "readWriteAnyDatabase"] (default []) | ||
| ``` | ||
@@ -29,0 +29,0 @@ |
| ## stackit ske cluster create | ||
| Creates an SKE cluster | ||
| Creates a SKE cluster | ||
@@ -18,9 +18,9 @@ ### Synopsis | ||
| ``` | ||
| Create an SKE cluster using default configuration | ||
| Create a SKE cluster using default configuration | ||
| $ stackit ske cluster create my-cluster | ||
| Create an SKE cluster using an API payload sourced from the file "./payload.json" | ||
| Create a SKE cluster using an API payload sourced from the file "./payload.json" | ||
| $ stackit ske cluster create my-cluster --payload @./payload.json | ||
| Create an SKE cluster using an API payload provided as a JSON string | ||
| Create a SKE cluster using an API payload provided as a JSON string | ||
| $ stackit ske cluster create my-cluster --payload "{...}" | ||
@@ -27,0 +27,0 @@ |
@@ -16,3 +16,3 @@ ## stackit ske cluster delete | ||
| ``` | ||
| Delete an SKE cluster with name "my-cluster" | ||
| Delete a SKE cluster with name "my-cluster" | ||
| $ stackit ske cluster delete my-cluster | ||
@@ -19,0 +19,0 @@ ``` |
| ## stackit ske cluster describe | ||
| Shows details of a SKE cluster | ||
| Shows details of a SKE cluster | ||
| ### Synopsis | ||
| Shows details of a STACKIT Kubernetes Engine (SKE) cluster. | ||
| Shows details of a STACKIT Kubernetes Engine (SKE) cluster. | ||
@@ -16,6 +16,6 @@ ``` | ||
| ``` | ||
| Get details of an SKE cluster with name "my-cluster" | ||
| Get details of a SKE cluster with name "my-cluster" | ||
| $ stackit ske cluster describe my-cluster | ||
| Get details of an SKE cluster with name "my-cluster" in JSON format | ||
| Get details of a SKE cluster with name "my-cluster" in JSON format | ||
| $ stackit ske cluster describe my-cluster --output-format json | ||
@@ -22,0 +22,0 @@ ``` |
| ## stackit ske cluster update | ||
| Updates an SKE cluster | ||
| Updates a SKE cluster | ||
@@ -18,6 +18,6 @@ ### Synopsis | ||
| ``` | ||
| Update an SKE cluster using an API payload sourced from the file "./payload.json" | ||
| Update a SKE cluster using an API payload sourced from the file "./payload.json" | ||
| $ stackit ske cluster update my-cluster --payload @./payload.json | ||
| Update an SKE cluster using an API payload provided as a JSON string | ||
| Update a SKE cluster using an API payload provided as a JSON string | ||
| $ stackit ske cluster update my-cluster --payload "{...}" | ||
@@ -24,0 +24,0 @@ |
@@ -33,8 +33,12 @@ ## stackit ske cluster | ||
| * [stackit ske](./stackit_ske.md) - Provides functionality for SKE | ||
| * [stackit ske cluster create](./stackit_ske_cluster_create.md) - Creates an SKE cluster | ||
| * [stackit ske cluster create](./stackit_ske_cluster_create.md) - Creates a SKE cluster | ||
| * [stackit ske cluster delete](./stackit_ske_cluster_delete.md) - Deletes a SKE cluster | ||
| * [stackit ske cluster describe](./stackit_ske_cluster_describe.md) - Shows details of a SKE cluster | ||
| * [stackit ske cluster describe](./stackit_ske_cluster_describe.md) - Shows details of a SKE cluster | ||
| * [stackit ske cluster generate-payload](./stackit_ske_cluster_generate-payload.md) - Generates a payload to create/update SKE clusters | ||
| * [stackit ske cluster hibernate](./stackit_ske_cluster_hibernate.md) - Trigger hibernate for a SKE cluster | ||
| * [stackit ske cluster list](./stackit_ske_cluster_list.md) - Lists all SKE clusters | ||
| * [stackit ske cluster update](./stackit_ske_cluster_update.md) - Updates an SKE cluster | ||
| * [stackit ske cluster maintenance](./stackit_ske_cluster_maintenance.md) - Trigger maintenance for a SKE cluster | ||
| * [stackit ske cluster reconcile](./stackit_ske_cluster_reconcile.md) - Trigger reconcile for a SKE cluster | ||
| * [stackit ske cluster update](./stackit_ske_cluster_update.md) - Updates a SKE cluster | ||
| * [stackit ske cluster wakeup](./stackit_ske_cluster_wakeup.md) - Trigger wakeup from hibernation for a SKE cluster | ||
| ## stackit ske kubeconfig create | ||
| Creates or update a kubeconfig for an SKE cluster | ||
| Creates or update a kubeconfig for a SKE cluster | ||
| ### Synopsis | ||
| Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exits in the kubeconfig file the information will be updated. | ||
| Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exists in the kubeconfig file the information will be updated. | ||
@@ -9,0 +9,0 @@ By default, the kubeconfig information of the SKE cluster is merged into the default kubeconfig file of the current user. If the kubeconfig file doesn't exist, a new one will be created. |
@@ -33,4 +33,4 @@ ## stackit ske kubeconfig | ||
| * [stackit ske](./stackit_ske.md) - Provides functionality for SKE | ||
| * [stackit ske kubeconfig create](./stackit_ske_kubeconfig_create.md) - Creates or update a kubeconfig for an SKE cluster | ||
| * [stackit ske kubeconfig create](./stackit_ske_kubeconfig_create.md) - Creates or update a kubeconfig for a SKE cluster | ||
| * [stackit ske kubeconfig login](./stackit_ske_kubeconfig_login.md) - Login plugin for kubernetes clients | ||
+37
-35
@@ -8,35 +8,35 @@ module github.com/stackitcloud/stackit-cli | ||
| github.com/goccy/go-yaml v1.18.0 | ||
| github.com/golang-jwt/jwt/v5 v5.2.2 | ||
| github.com/golang-jwt/jwt/v5 v5.2.3 | ||
| github.com/google/go-cmp v0.7.0 | ||
| github.com/google/uuid v1.6.0 | ||
| github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf | ||
| github.com/jedib0t/go-pretty/v6 v6.6.7 | ||
| github.com/jedib0t/go-pretty/v6 v6.6.8 | ||
| github.com/lmittmann/tint v1.1.2 | ||
| github.com/mattn/go-colorable v0.1.14 | ||
| github.com/spf13/cobra v1.9.1 | ||
| github.com/spf13/pflag v1.0.6 | ||
| github.com/spf13/pflag v1.0.7 | ||
| github.com/spf13/viper v1.20.1 | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.17.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/alb v0.5.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/authorization v0.8.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/git v0.7.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/iaas v0.26.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.3.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.24.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/postgresflex v1.2.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.17.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/runcommand v1.3.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.13.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.3.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.2.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.9.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/ske v1.0.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.0 | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.17.3 | ||
| github.com/stackitcloud/stackit-sdk-go/services/alb v0.6.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/authorization v0.8.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/iaas v0.27.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.5.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.24.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/postgresflex v1.2.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.17.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/runcommand v1.3.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.13.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.3.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.2.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.9.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/ske v1.3.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.1 | ||
| github.com/zalando/go-keyring v0.2.6 | ||
| golang.org/x/mod v0.25.0 | ||
| golang.org/x/mod v0.26.0 | ||
| golang.org/x/oauth2 v0.30.0 | ||
| golang.org/x/term v0.32.0 | ||
| golang.org/x/text v0.26.0 | ||
| golang.org/x/term v0.33.0 | ||
| golang.org/x/text v0.27.0 | ||
| k8s.io/apimachinery v0.32.3 | ||
@@ -47,3 +47,3 @@ k8s.io/client-go v0.32.3 | ||
| require ( | ||
| golang.org/x/net v0.41.0 // indirect | ||
| golang.org/x/net v0.42.0 // indirect | ||
| golang.org/x/time v0.11.0 // indirect | ||
@@ -211,4 +211,6 @@ gopkg.in/inf.v0 v0.9.1 // indirect | ||
| golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect | ||
| golang.org/x/sync v0.15.0 // indirect | ||
| golang.org/x/tools v0.34.0 // indirect | ||
| golang.org/x/sync v0.16.0 // indirect | ||
| golang.org/x/tools v0.35.0 // indirect | ||
| golang.org/x/tools/go/expect v0.1.1-deprecated // indirect | ||
| golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect | ||
| google.golang.org/protobuf v1.36.6 // indirect | ||
@@ -242,12 +244,12 @@ gopkg.in/yaml.v2 v2.4.0 // indirect | ||
| github.com/spf13/cast v1.7.1 // indirect | ||
| github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.4.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.3.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/observability v0.8.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.25.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/redis v0.25.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.3.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/observability v0.9.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.25.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/redis v0.25.1 | ||
| github.com/subosito/gotenv v1.6.0 // indirect | ||
| go.uber.org/multierr v1.11.0 // indirect | ||
| golang.org/x/sys v0.33.0 // indirect | ||
| golang.org/x/sys v0.34.0 // indirect | ||
| gopkg.in/yaml.v3 v3.0.1 // indirect | ||
@@ -254,0 +256,0 @@ k8s.io/api v0.32.3 // indirect |
+41
-1
@@ -20,5 +20,45 @@ # Installation | ||
| ```shell | ||
| brew install stackit | ||
| brew install --cask stackit | ||
| ``` | ||
| #### Formula deprecated | ||
| The homebrew formula is deprecated, will no longer be updated and will be removed after 2026-01-22. | ||
| You need to install the STACKIT CLI as cask. | ||
| Therefor you need to uninstall the formula and reinstall it as cask. | ||
| Your profiles should normally remain. To ensure that nothing will be gone, you should backup them. | ||
| 1. Export your existing profiles. This will create a json file in your current directory. | ||
| ```shell | ||
| stackit config profile export default | ||
| ``` | ||
| 2. If you have multiple profiles, then execute the export command for each of them. You can find your profiles via: | ||
| ```shell | ||
| stackit config profile list | ||
| stackit config profile export <profile-name> | ||
| ``` | ||
| 3. Uninstall the formula. | ||
| ```shell | ||
| brew uninstall stackit | ||
| ``` | ||
| 4. Install the STACKIT CLI as cask. | ||
| ```shell | ||
| brew install --cask stackit | ||
| ``` | ||
| 5. Check if your configs are still stored. | ||
| ```shell | ||
| stackit config profile list | ||
| ``` | ||
| 6. In case the profiles are gone, import your profiles via: | ||
| ```shell | ||
| $ stackit config profile import -c @default.json --name myProfile | ||
| ``` | ||
| ### Linux | ||
@@ -25,0 +65,0 @@ |
| package getaccesstoken | ||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -10,4 +14,10 @@ "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
@@ -24,3 +34,8 @@ cmd := &cobra.Command{ | ||
| ), | ||
| RunE: func(_ *cobra.Command, _ []string) error { | ||
| RunE: func(cmd *cobra.Command, _ []string) error { | ||
| model, err := parseInput(params.Printer, cmd) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| userSessionExpired, err := auth.UserSessionExpired() | ||
@@ -34,3 +49,4 @@ if err != nil { | ||
| accessToken, err := auth.GetAccessToken() | ||
| // Try to get a valid access token, refreshing if necessary | ||
| accessToken, err := auth.RefreshAccessToken(params.Printer) | ||
| if err != nil { | ||
@@ -40,12 +56,28 @@ return err | ||
| accessTokenExpired, err := auth.TokenExpired(accessToken) | ||
| if err != nil { | ||
| return err | ||
| switch model.OutputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(map[string]string{ | ||
| "access_token": accessToken, | ||
| }, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal image list: %w", err) | ||
| } | ||
| params.Printer.Outputln(string(details)) | ||
| return nil | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(map[string]string{ | ||
| "access_token": accessToken, | ||
| }, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal image list: %w", err) | ||
| } | ||
| params.Printer.Outputln(string(details)) | ||
| return nil | ||
| default: | ||
| params.Printer.Outputln(accessToken) | ||
| return nil | ||
| } | ||
| if accessTokenExpired { | ||
| return &cliErr.AccessTokenExpiredError{} | ||
| } | ||
| params.Printer.Outputf("%s\n", accessToken) | ||
| return nil | ||
| }, | ||
@@ -55,1 +87,20 @@ } | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| } | ||
| 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 | ||
| } |
| package git | ||
| import ( | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/create" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/delete" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/describe" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/list" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/flavor" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/git/instance" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
@@ -29,7 +27,5 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| cmd.AddCommand( | ||
| list.NewCmd(params), | ||
| describe.NewCmd(params), | ||
| create.NewCmd(params), | ||
| delete.NewCmd(params), | ||
| instance.NewCmd(params), | ||
| flavor.NewCmd(params), | ||
| ) | ||
| } |
@@ -17,3 +17,6 @@ package describe | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| testBackupId = "backupID" | ||
| ) | ||
@@ -26,3 +29,2 @@ type testCtxKey struct{} | ||
| var testInstanceId = uuid.NewString() | ||
| var testBackupId = "backupID" | ||
@@ -41,4 +43,5 @@ func fixtureArgValues(mods ...func(argValues []string)) []string { | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| } | ||
@@ -56,2 +59,3 @@ for _, mod := range mods { | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| Region: testRegion, | ||
| }, | ||
@@ -68,3 +72,3 @@ InstanceId: testInstanceId, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiGetBackupRequest)) mongodbflex.ApiGetBackupRequest { | ||
| request := testClient.GetBackup(testCtx, testProjectId, testInstanceId, testBackupId) | ||
| request := testClient.GetBackup(testCtx, testProjectId, testInstanceId, testBackupId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -113,3 +117,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -122,3 +126,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -131,3 +135,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -134,0 +138,0 @@ isValid: false, |
@@ -64,3 +64,3 @@ package describe | ||
| instanceLabel, err := mongoUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| instanceLabel, err := mongoUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -79,3 +79,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| restoreJobs, err := apiClient.ListRestoreJobs(ctx, model.ProjectId, model.InstanceId).Execute() | ||
| restoreJobs, err := apiClient.ListRestoreJobs(ctx, model.ProjectId, model.InstanceId, model.Region).Execute() | ||
| if err != nil { | ||
@@ -127,3 +127,3 @@ return fmt.Errorf("get restore jobs for MongoDB Flex instance %q: %w", instanceLabel, err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiGetBackupRequest { | ||
| req := apiClient.GetBackup(ctx, model.ProjectId, model.InstanceId, model.BackupId) | ||
| req := apiClient.GetBackup(ctx, model.ProjectId, model.InstanceId, model.BackupId, model.Region) | ||
| return req | ||
@@ -130,0 +130,0 @@ } |
@@ -18,3 +18,5 @@ package list | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -30,5 +32,6 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| limitFlag: "10", | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| limitFlag: "10", | ||
| } | ||
@@ -46,2 +49,3 @@ for _, mod := range mods { | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| Region: testRegion, | ||
| }, | ||
@@ -58,3 +62,3 @@ InstanceId: utils.Ptr(testInstanceId), | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiListBackupsRequest)) mongodbflex.ApiListBackupsRequest { | ||
| request := testClient.ListBackups(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.ListBackups(testCtx, testProjectId, testInstanceId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -87,3 +91,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -95,3 +99,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -103,3 +107,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -106,0 +110,0 @@ isValid: false, |
@@ -67,3 +67,3 @@ package list | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, *model.InstanceId) | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, *model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -86,3 +86,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| restoreJobs, err := apiClient.ListRestoreJobs(ctx, model.ProjectId, *model.InstanceId).Execute() | ||
| restoreJobs, err := apiClient.ListRestoreJobs(ctx, model.ProjectId, *model.InstanceId, model.Region).Execute() | ||
| if err != nil { | ||
@@ -146,3 +146,3 @@ return fmt.Errorf("get restore jobs for MongoDB Flex instance %q: %w", instanceLabel, err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiListBackupsRequest { | ||
| req := apiClient.ListBackups(ctx, model.ProjectId, *model.InstanceId) | ||
| req := apiClient.ListBackups(ctx, model.ProjectId, *model.InstanceId, model.Region) | ||
| return req | ||
@@ -149,0 +149,0 @@ } |
@@ -18,3 +18,5 @@ package restorejobs | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -30,5 +32,6 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| limitFlag: "10", | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| limitFlag: "10", | ||
| } | ||
@@ -45,2 +48,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -58,3 +62,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiListRestoreJobsRequest)) mongodbflex.ApiListRestoreJobsRequest { | ||
| request := testClient.ListRestoreJobs(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.ListRestoreJobs(testCtx, testProjectId, testInstanceId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -87,3 +91,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -95,3 +99,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -103,3 +107,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -106,0 +110,0 @@ isValid: false, |
@@ -66,3 +66,3 @@ package restorejobs | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, *model.InstanceId) | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, *model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -139,3 +139,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiListRestoreJobsRequest { | ||
| req := apiClient.ListRestoreJobs(ctx, model.ProjectId, *model.InstanceId) | ||
| req := apiClient.ListRestoreJobs(ctx, model.ProjectId, *model.InstanceId, model.Region) | ||
| return req | ||
@@ -142,0 +142,0 @@ } |
@@ -18,7 +18,6 @@ package restore | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu02" | ||
| testBackupId = "backupID" | ||
@@ -37,6 +36,7 @@ testTimestamp = "2021-01-01T00:00:00Z" | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| backupIdFlag: testBackupId, | ||
| backupInstanceIdFlag: testBackupInstanceId, | ||
| instanceIdFlag: testInstanceId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| backupIdFlag: testBackupId, | ||
| backupInstanceIdFlag: testBackupInstanceId, | ||
| instanceIdFlag: testInstanceId, | ||
| } | ||
@@ -53,2 +53,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -67,3 +68,3 @@ }, | ||
| func fixtureRestoreRequest(mods ...func(request mongodbflex.ApiRestoreInstanceRequest)) mongodbflex.ApiRestoreInstanceRequest { | ||
| request := testClient.RestoreInstance(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.RestoreInstance(testCtx, testProjectId, testInstanceId, testRegion) | ||
| request = request.RestoreInstancePayload(mongodbflex.RestoreInstancePayload{ | ||
@@ -80,3 +81,3 @@ BackupId: utils.Ptr(testBackupId), | ||
| func fixtureCloneRequest(mods ...func(request mongodbflex.ApiCloneInstanceRequest)) mongodbflex.ApiCloneInstanceRequest { | ||
| request := testClient.CloneInstance(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.CloneInstance(testCtx, testProjectId, testInstanceId, testRegion) | ||
| request = request.CloneInstancePayload(mongodbflex.CloneInstancePayload{ | ||
@@ -114,3 +115,3 @@ Timestamp: utils.Ptr(testTimestamp), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -122,3 +123,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -130,3 +131,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -133,0 +134,0 @@ isValid: false, |
@@ -73,3 +73,3 @@ package restore | ||
| instanceLabel, err := mongodbUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| instanceLabel, err := mongodbUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -106,3 +106,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| s.Start("Restoring instance") | ||
| _, err = wait.RestoreInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId, model.BackupId).WaitWithContext(ctx) | ||
| _, err = wait.RestoreInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId, model.BackupId, model.Region).WaitWithContext(ctx) | ||
| if err != nil { | ||
@@ -128,3 +128,3 @@ return fmt.Errorf("wait for MongoDB Flex instance restoration: %w", err) | ||
| s.Start("Cloning instance") | ||
| _, err = wait.CloneInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId).WaitWithContext(ctx) | ||
| _, err = wait.CloneInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region).WaitWithContext(ctx) | ||
| if err != nil { | ||
@@ -190,3 +190,3 @@ return fmt.Errorf("wait for MongoDB Flex instance cloning: %w", err) | ||
| func buildRestoreRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiRestoreInstanceRequest { | ||
| req := apiClient.RestoreInstance(ctx, model.ProjectId, model.InstanceId) | ||
| req := apiClient.RestoreInstance(ctx, model.ProjectId, model.InstanceId, model.Region) | ||
| req = req.RestoreInstancePayload(mongodbflex.RestoreInstancePayload{ | ||
@@ -200,3 +200,3 @@ BackupId: &model.BackupId, | ||
| func buildCloneRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiCloneInstanceRequest { | ||
| req := apiClient.CloneInstance(ctx, model.ProjectId, model.InstanceId) | ||
| req := apiClient.CloneInstance(ctx, model.ProjectId, model.InstanceId, model.Region) | ||
| req = req.CloneInstancePayload(mongodbflex.CloneInstancePayload{ | ||
@@ -203,0 +203,0 @@ Timestamp: &model.Timestamp, |
@@ -18,3 +18,5 @@ package schedule | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -30,4 +32,5 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| } | ||
@@ -44,2 +47,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -56,3 +60,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiGetInstanceRequest)) mongodbflex.ApiGetInstanceRequest { | ||
| request := testClient.GetInstance(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.GetInstance(testCtx, testProjectId, testInstanceId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -85,3 +89,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -93,3 +97,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -101,3 +105,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -104,0 +108,0 @@ isValid: false, |
@@ -103,3 +103,3 @@ package schedule | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiGetInstanceRequest { | ||
| req := apiClient.GetInstance(ctx, model.ProjectId, model.InstanceId) | ||
| req := apiClient.GetInstance(ctx, model.ProjectId, model.InstanceId, model.Region) | ||
| return req | ||
@@ -106,0 +106,0 @@ } |
@@ -16,3 +16,6 @@ package updateschedule | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| testSchedule = "0 0/6 * * *" | ||
| ) | ||
@@ -25,9 +28,9 @@ type testCtxKey struct{} | ||
| var testInstanceId = uuid.NewString() | ||
| var testSchedule = "0 0/6 * * *" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| scheduleFlag: testSchedule, | ||
| instanceIdFlag: testInstanceId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| scheduleFlag: testSchedule, | ||
| instanceIdFlag: testInstanceId, | ||
| } | ||
@@ -44,6 +47,7 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| InstanceId: utils.Ptr(testInstanceId), | ||
| BackupSchedule: &testSchedule, | ||
| BackupSchedule: utils.Ptr(testSchedule), | ||
| } | ||
@@ -72,3 +76,3 @@ for _, mod := range mods { | ||
| func fixtureUpdateBackupScheduleRequest(mods ...func(request *mongodbflex.ApiUpdateBackupScheduleRequest)) mongodbflex.ApiUpdateBackupScheduleRequest { | ||
| request := testClient.UpdateBackupSchedule(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.UpdateBackupSchedule(testCtx, testProjectId, testInstanceId, testRegion) | ||
| request = request.UpdateBackupSchedulePayload(fixturePayload()) | ||
@@ -82,3 +86,3 @@ for _, mod := range mods { | ||
| func fixtureGetInstanceRequest(mods ...func(request *mongodbflex.ApiGetInstanceRequest)) mongodbflex.ApiGetInstanceRequest { | ||
| request := testClient.GetInstance(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.GetInstance(testCtx, testProjectId, testInstanceId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -92,3 +96,3 @@ mod(&request) | ||
| instance := mongodbflex.Instance{ | ||
| BackupSchedule: &testSchedule, | ||
| BackupSchedule: utils.Ptr(testSchedule), | ||
| Options: &map[string]string{ | ||
@@ -130,3 +134,3 @@ "dailySnapshotRetentionDays": "0", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -138,3 +142,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -146,3 +150,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -272,2 +276,3 @@ isValid: false, | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| }, | ||
@@ -289,2 +294,3 @@ InstanceId: utils.Ptr(testInstanceId), | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| }, | ||
@@ -314,2 +320,3 @@ InstanceId: utils.Ptr(testInstanceId), | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| }, | ||
@@ -316,0 +323,0 @@ InstanceId: utils.Ptr(testInstanceId), |
@@ -84,3 +84,3 @@ package updateschedule | ||
| instanceLabel, err := mongoDBflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, *model.InstanceId) | ||
| instanceLabel, err := mongoDBflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, *model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -163,3 +163,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| func buildUpdateBackupScheduleRequest(ctx context.Context, model *inputModel, instance *mongodbflex.Instance, apiClient *mongodbflex.APIClient) mongodbflex.ApiUpdateBackupScheduleRequest { | ||
| req := apiClient.UpdateBackupSchedule(ctx, model.ProjectId, *model.InstanceId) | ||
| req := apiClient.UpdateBackupSchedule(ctx, model.ProjectId, *model.InstanceId, model.Region) | ||
@@ -234,4 +234,4 @@ payload := getUpdateBackupSchedulePayload(instance) | ||
| func buildGetInstanceRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiGetInstanceRequest { | ||
| req := apiClient.GetInstance(ctx, model.ProjectId, *model.InstanceId) | ||
| req := apiClient.GetInstance(ctx, model.ProjectId, *model.InstanceId, model.Region) | ||
| return req | ||
| } |
@@ -19,3 +19,5 @@ package create | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -34,7 +36,7 @@ type testCtxKey struct{} | ||
| func (c *mongoDBFlexClientMocked) CreateInstance(ctx context.Context, projectId string) mongodbflex.ApiCreateInstanceRequest { | ||
| return testClient.CreateInstance(ctx, projectId) | ||
| func (c *mongoDBFlexClientMocked) CreateInstance(ctx context.Context, projectId, region string) mongodbflex.ApiCreateInstanceRequest { | ||
| return testClient.CreateInstance(ctx, projectId, region) | ||
| } | ||
| func (c *mongoDBFlexClientMocked) ListStoragesExecute(_ context.Context, _, _ string) (*mongodbflex.ListStoragesResponse, error) { | ||
| func (c *mongoDBFlexClientMocked) ListStoragesExecute(_ context.Context, _, _, _ string) (*mongodbflex.ListStoragesResponse, error) { | ||
| if c.listFlavorsFails { | ||
@@ -46,3 +48,3 @@ return nil, fmt.Errorf("list storages failed") | ||
| func (c *mongoDBFlexClientMocked) ListFlavorsExecute(_ context.Context, _ string) (*mongodbflex.ListFlavorsResponse, error) { | ||
| func (c *mongoDBFlexClientMocked) ListFlavorsExecute(_ context.Context, _, _ string) (*mongodbflex.ListFlavorsResponse, error) { | ||
| if c.listFlavorsFails { | ||
@@ -59,11 +61,12 @@ return nil, fmt.Errorf("list flavors failed") | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceNameFlag: "example-name", | ||
| aclFlag: "0.0.0.0/0", | ||
| backupScheduleFlag: "0 0/6 * * *", | ||
| flavorIdFlag: testFlavorId, | ||
| storageClassFlag: "premium-perf4-mongodb", // Non-default | ||
| storageSizeFlag: "10", | ||
| versionFlag: "6.0", | ||
| typeFlag: "Replica", | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceNameFlag: "example-name", | ||
| aclFlag: "0.0.0.0/0", | ||
| backupScheduleFlag: "0 0/6 * * *", | ||
| flavorIdFlag: testFlavorId, | ||
| storageClassFlag: "premium-perf4-mongodb", // Non-default | ||
| storageSizeFlag: "10", | ||
| versionFlag: "6.0", | ||
| typeFlag: "Replica", | ||
| } | ||
@@ -80,2 +83,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -99,3 +103,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiCreateInstanceRequest)) mongodbflex.ApiCreateInstanceRequest { | ||
| request := testClient.CreateInstance(testCtx, testProjectId) | ||
| request := testClient.CreateInstance(testCtx, testProjectId, testRegion) | ||
| request = request.CreateInstancePayload(fixturePayload()) | ||
@@ -111,3 +115,3 @@ for _, mod := range mods { | ||
| Name: utils.Ptr("example-name"), | ||
| Acl: &mongodbflex.ACL{Items: utils.Ptr([]string{"0.0.0.0/0"})}, | ||
| Acl: &mongodbflex.CreateInstancePayloadAcl{Items: utils.Ptr([]string{"0.0.0.0/0"})}, | ||
| BackupSchedule: utils.Ptr("0 0/6 * * *"), | ||
@@ -176,3 +180,3 @@ FlavorId: utils.Ptr(testFlavorId), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -184,3 +188,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -192,3 +196,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -334,3 +338,3 @@ isValid: false, | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -363,3 +367,3 @@ Id: utils.Ptr(testFlavorId), | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -394,3 +398,3 @@ Id: utils.Ptr(testFlavorId), | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -420,3 +424,3 @@ Id: utils.Ptr(testFlavorId), | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -459,3 +463,3 @@ Id: utils.Ptr(testFlavorId), | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -495,3 +499,3 @@ Id: utils.Ptr(testFlavorId), | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -521,3 +525,3 @@ Id: utils.Ptr(testFlavorId), | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -524,0 +528,0 @@ Id: utils.Ptr(testFlavorId), |
@@ -107,3 +107,3 @@ package create | ||
| if model.Version == nil { | ||
| version, err := mongodbflexUtils.GetLatestMongoDBVersion(ctx, apiClient, model.ProjectId) | ||
| version, err := mongodbflexUtils.GetLatestMongoDBVersion(ctx, apiClient, model.ProjectId, model.Region) | ||
| if err != nil { | ||
@@ -130,3 +130,3 @@ return fmt.Errorf("get latest MongoDB version: %w", err) | ||
| s.Start("Creating instance") | ||
| _, err = wait.CreateInstanceWaitHandler(ctx, apiClient, model.ProjectId, instanceId).WaitWithContext(ctx) | ||
| _, err = wait.CreateInstanceWaitHandler(ctx, apiClient, model.ProjectId, instanceId, model.Region).WaitWithContext(ctx) | ||
| if err != nil { | ||
@@ -213,9 +213,9 @@ return fmt.Errorf("wait for MongoDB Flex instance creation: %w", err) | ||
| type MongoDBFlexClient interface { | ||
| CreateInstance(ctx context.Context, projectId string) mongodbflex.ApiCreateInstanceRequest | ||
| ListFlavorsExecute(ctx context.Context, projectId string) (*mongodbflex.ListFlavorsResponse, error) | ||
| ListStoragesExecute(ctx context.Context, projectId, flavorId string) (*mongodbflex.ListStoragesResponse, error) | ||
| CreateInstance(ctx context.Context, projectId, region string) mongodbflex.ApiCreateInstanceRequest | ||
| ListFlavorsExecute(ctx context.Context, projectId, region string) (*mongodbflex.ListFlavorsResponse, error) | ||
| ListStoragesExecute(ctx context.Context, projectId, flavorId, region string) (*mongodbflex.ListStoragesResponse, error) | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient MongoDBFlexClient) (mongodbflex.ApiCreateInstanceRequest, error) { | ||
| req := apiClient.CreateInstance(ctx, model.ProjectId) | ||
| req := apiClient.CreateInstance(ctx, model.ProjectId, model.Region) | ||
@@ -225,3 +225,3 @@ var flavorId *string | ||
| flavors, err := apiClient.ListFlavorsExecute(ctx, model.ProjectId) | ||
| flavors, err := apiClient.ListFlavorsExecute(ctx, model.ProjectId, model.Region) | ||
| if err != nil { | ||
@@ -248,3 +248,3 @@ return req, fmt.Errorf("get MongoDB Flex flavors: %w", err) | ||
| storages, err := apiClient.ListStoragesExecute(ctx, model.ProjectId, *flavorId) | ||
| storages, err := apiClient.ListStoragesExecute(ctx, model.ProjectId, *flavorId, model.Region) | ||
| if err != nil { | ||
@@ -265,3 +265,3 @@ return req, fmt.Errorf("get MongoDB Flex storages: %w", err) | ||
| Name: model.InstanceName, | ||
| Acl: &mongodbflex.ACL{Items: model.ACL}, | ||
| Acl: &mongodbflex.CreateInstancePayloadAcl{Items: model.ACL}, | ||
| BackupSchedule: model.BackupSchedule, | ||
@@ -268,0 +268,0 @@ FlavorId: flavorId, |
@@ -17,3 +17,5 @@ package delete | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -39,3 +41,4 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
@@ -52,2 +55,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -64,3 +68,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiDeleteInstanceRequest)) mongodbflex.ApiDeleteInstanceRequest { | ||
| request := testClient.DeleteInstance(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.DeleteInstance(testCtx, testProjectId, testInstanceId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -109,3 +113,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -118,3 +122,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -127,3 +131,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -130,0 +134,0 @@ isValid: false, |
@@ -56,3 +56,3 @@ package delete | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -82,3 +82,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| s.Start("Deleting instance") | ||
| _, err = wait.DeleteInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId).WaitWithContext(ctx) | ||
| _, err = wait.DeleteInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region).WaitWithContext(ctx) | ||
| if err != nil { | ||
@@ -127,4 +127,4 @@ return fmt.Errorf("wait for MongoDB Flex instance deletion: %w", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiDeleteInstanceRequest { | ||
| req := apiClient.DeleteInstance(ctx, model.ProjectId, model.InstanceId) | ||
| req := apiClient.DeleteInstance(ctx, model.ProjectId, model.InstanceId, model.Region) | ||
| return req | ||
| } |
@@ -17,3 +17,5 @@ package describe | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -39,3 +41,4 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
@@ -52,2 +55,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -64,3 +68,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiGetInstanceRequest)) mongodbflex.ApiGetInstanceRequest { | ||
| request := testClient.GetInstance(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.GetInstance(testCtx, testProjectId, testInstanceId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -109,3 +113,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -118,3 +122,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -127,3 +131,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -130,0 +134,0 @@ isValid: false, |
@@ -99,3 +99,3 @@ package describe | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiGetInstanceRequest { | ||
| req := apiClient.GetInstance(ctx, model.ProjectId, model.InstanceId) | ||
| req := apiClient.GetInstance(ctx, model.ProjectId, model.InstanceId, model.Region) | ||
| return req | ||
@@ -102,0 +102,0 @@ } |
@@ -19,3 +19,5 @@ package list | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -30,4 +32,5 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| limitFlag: "10", | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| limitFlag: "10", | ||
| } | ||
@@ -44,2 +47,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -56,3 +60,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiListInstancesRequest)) mongodbflex.ApiListInstancesRequest { | ||
| request := testClient.ListInstances(testCtx, testProjectId).Tag("") | ||
| request := testClient.ListInstances(testCtx, testProjectId, testRegion).Tag("") | ||
| for _, mod := range mods { | ||
@@ -85,3 +89,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -93,3 +97,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -101,3 +105,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -104,0 +108,0 @@ isValid: false, |
@@ -130,3 +130,3 @@ package list | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiListInstancesRequest { | ||
| req := apiClient.ListInstances(ctx, model.ProjectId).Tag("") | ||
| req := apiClient.ListInstances(ctx, model.ProjectId, model.Region).Tag("") | ||
| return req | ||
@@ -133,0 +133,0 @@ } |
@@ -19,3 +19,5 @@ package update | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -33,10 +35,10 @@ type testCtxKey struct{} | ||
| getInstanceFails bool | ||
| getInstanceResp *mongodbflex.GetInstanceResponse | ||
| getInstanceResp *mongodbflex.InstanceResponse | ||
| } | ||
| func (c *mongoDBFlexClientMocked) PartialUpdateInstance(ctx context.Context, projectId, instanceId string) mongodbflex.ApiPartialUpdateInstanceRequest { | ||
| return testClient.PartialUpdateInstance(ctx, projectId, instanceId) | ||
| func (c *mongoDBFlexClientMocked) PartialUpdateInstance(ctx context.Context, projectId, instanceId, region string) mongodbflex.ApiPartialUpdateInstanceRequest { | ||
| return testClient.PartialUpdateInstance(ctx, projectId, instanceId, region) | ||
| } | ||
| func (c *mongoDBFlexClientMocked) GetInstanceExecute(_ context.Context, _, _ string) (*mongodbflex.GetInstanceResponse, error) { | ||
| func (c *mongoDBFlexClientMocked) GetInstanceExecute(_ context.Context, _, _, _ string) (*mongodbflex.InstanceResponse, error) { | ||
| if c.getInstanceFails { | ||
@@ -48,3 +50,3 @@ return nil, fmt.Errorf("get instance failed") | ||
| func (c *mongoDBFlexClientMocked) ListStoragesExecute(_ context.Context, _, _ string) (*mongodbflex.ListStoragesResponse, error) { | ||
| func (c *mongoDBFlexClientMocked) ListStoragesExecute(_ context.Context, _, _, _ string) (*mongodbflex.ListStoragesResponse, error) { | ||
| if c.listFlavorsFails { | ||
@@ -56,3 +58,3 @@ return nil, fmt.Errorf("list storages failed") | ||
| func (c *mongoDBFlexClientMocked) ListFlavorsExecute(_ context.Context, _ string) (*mongodbflex.ListFlavorsResponse, error) { | ||
| func (c *mongoDBFlexClientMocked) ListFlavorsExecute(_ context.Context, _, _ string) (*mongodbflex.ListFlavorsResponse, error) { | ||
| if c.listFlavorsFails { | ||
@@ -80,3 +82,4 @@ return nil, fmt.Errorf("list flavors failed") | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
@@ -91,11 +94,12 @@ for _, mod := range mods { | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| flavorIdFlag: testFlavorId, | ||
| instanceNameFlag: "example-name", | ||
| aclFlag: "0.0.0.0/0", | ||
| backupScheduleFlag: "0 0 * * *", | ||
| storageClassFlag: "class", | ||
| storageSizeFlag: "10", | ||
| versionFlag: "5.0", | ||
| typeFlag: "Single", | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| flavorIdFlag: testFlavorId, | ||
| instanceNameFlag: "example-name", | ||
| aclFlag: "0.0.0.0/0", | ||
| backupScheduleFlag: "0 0 * * *", | ||
| storageClassFlag: "class", | ||
| storageSizeFlag: "10", | ||
| versionFlag: "5.0", | ||
| typeFlag: "Single", | ||
| } | ||
@@ -112,2 +116,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -127,2 +132,3 @@ }, | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -147,3 +153,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiPartialUpdateInstanceRequest)) mongodbflex.ApiPartialUpdateInstanceRequest { | ||
| request := testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId, testRegion) | ||
| request = request.PartialUpdateInstancePayload(mongodbflex.PartialUpdateInstancePayload{}) | ||
@@ -216,3 +222,3 @@ for _, mod := range mods { | ||
| flagValues: fixtureRequiredFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -225,3 +231,3 @@ isValid: false, | ||
| flagValues: fixtureRequiredFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -234,3 +240,3 @@ isValid: false, | ||
| flagValues: fixtureRequiredFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -363,3 +369,3 @@ isValid: false, | ||
| getInstanceFails bool | ||
| getInstanceResp *mongodbflex.GetInstanceResponse | ||
| getInstanceResp *mongodbflex.InstanceResponse | ||
| listFlavorsFails bool | ||
@@ -384,3 +390,3 @@ listFlavorsResp *mongodbflex.ListFlavorsResponse | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -393,3 +399,3 @@ Id: utils.Ptr(testFlavorId), | ||
| }, | ||
| expectedRequest: testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId). | ||
| expectedRequest: testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId, testRegion). | ||
| PartialUpdateInstancePayload(mongodbflex.PartialUpdateInstancePayload{ | ||
@@ -407,3 +413,3 @@ FlavorId: utils.Ptr(testFlavorId), | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -416,3 +422,3 @@ Id: utils.Ptr(testFlavorId), | ||
| }, | ||
| expectedRequest: testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId). | ||
| expectedRequest: testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId, testRegion). | ||
| PartialUpdateInstancePayload(mongodbflex.PartialUpdateInstancePayload{ | ||
@@ -428,3 +434,3 @@ FlavorId: utils.Ptr(testFlavorId), | ||
| isValid: true, | ||
| getInstanceResp: &mongodbflex.GetInstanceResponse{ | ||
| getInstanceResp: &mongodbflex.InstanceResponse{ | ||
| Item: &mongodbflex.Instance{ | ||
@@ -443,3 +449,3 @@ Flavor: &mongodbflex.Flavor{ | ||
| }, | ||
| expectedRequest: testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId). | ||
| expectedRequest: testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId, testRegion). | ||
| PartialUpdateInstancePayload(mongodbflex.PartialUpdateInstancePayload{ | ||
@@ -458,3 +464,3 @@ Storage: &mongodbflex.Storage{ | ||
| isValid: true, | ||
| getInstanceResp: &mongodbflex.GetInstanceResponse{ | ||
| getInstanceResp: &mongodbflex.InstanceResponse{ | ||
| Item: &mongodbflex.Instance{ | ||
@@ -473,3 +479,3 @@ Flavor: &mongodbflex.Flavor{ | ||
| }, | ||
| expectedRequest: testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId). | ||
| expectedRequest: testClient.PartialUpdateInstance(testCtx, testProjectId, testInstanceId, testRegion). | ||
| PartialUpdateInstancePayload(mongodbflex.PartialUpdateInstancePayload{ | ||
@@ -502,3 +508,3 @@ Storage: &mongodbflex.Storage{ | ||
| listFlavorsResp: &mongodbflex.ListFlavorsResponse{ | ||
| Flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| Flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -547,3 +553,3 @@ Id: utils.Ptr(testFlavorId), | ||
| ), | ||
| getInstanceResp: &mongodbflex.GetInstanceResponse{ | ||
| getInstanceResp: &mongodbflex.InstanceResponse{ | ||
| Item: &mongodbflex.Instance{ | ||
@@ -571,3 +577,3 @@ Flavor: &mongodbflex.Flavor{ | ||
| ), | ||
| getInstanceResp: &mongodbflex.GetInstanceResponse{ | ||
| getInstanceResp: &mongodbflex.InstanceResponse{ | ||
| Item: &mongodbflex.Instance{ | ||
@@ -574,0 +580,0 @@ Flavor: &mongodbflex.Flavor{ |
@@ -86,3 +86,3 @@ package update | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -116,3 +116,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| s.Start("Updating instance") | ||
| _, err = wait.PartialUpdateInstanceWaitHandler(ctx, apiClient, model.ProjectId, instanceId).WaitWithContext(ctx) | ||
| _, err = wait.PartialUpdateInstanceWaitHandler(ctx, apiClient, model.ProjectId, instanceId, model.Region).WaitWithContext(ctx) | ||
| if err != nil { | ||
@@ -205,10 +205,10 @@ return fmt.Errorf("wait for MongoDB Flex instance update: %w", err) | ||
| type MongoDBFlexClient interface { | ||
| PartialUpdateInstance(ctx context.Context, projectId, instanceId string) mongodbflex.ApiPartialUpdateInstanceRequest | ||
| GetInstanceExecute(ctx context.Context, projectId, instanceId string) (*mongodbflex.GetInstanceResponse, error) | ||
| ListFlavorsExecute(ctx context.Context, projectId string) (*mongodbflex.ListFlavorsResponse, error) | ||
| ListStoragesExecute(ctx context.Context, projectId, flavorId string) (*mongodbflex.ListStoragesResponse, error) | ||
| PartialUpdateInstance(ctx context.Context, projectId, instanceId, region string) mongodbflex.ApiPartialUpdateInstanceRequest | ||
| GetInstanceExecute(ctx context.Context, projectId, instanceId, region string) (*mongodbflex.InstanceResponse, error) | ||
| ListFlavorsExecute(ctx context.Context, projectId, region string) (*mongodbflex.ListFlavorsResponse, error) | ||
| ListStoragesExecute(ctx context.Context, projectId, flavorId, region string) (*mongodbflex.ListStoragesResponse, error) | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient MongoDBFlexClient) (mongodbflex.ApiPartialUpdateInstanceRequest, error) { | ||
| req := apiClient.PartialUpdateInstance(ctx, model.ProjectId, model.InstanceId) | ||
| req := apiClient.PartialUpdateInstance(ctx, model.ProjectId, model.InstanceId, model.Region) | ||
@@ -218,3 +218,3 @@ var flavorId *string | ||
| flavors, err := apiClient.ListFlavorsExecute(ctx, model.ProjectId) | ||
| flavors, err := apiClient.ListFlavorsExecute(ctx, model.ProjectId, model.Region) | ||
| if err != nil { | ||
@@ -228,3 +228,3 @@ return req, fmt.Errorf("get MongoDB Flex flavors: %w", err) | ||
| if model.RAM == nil || model.CPU == nil { | ||
| currentInstance, err := apiClient.GetInstanceExecute(ctx, model.ProjectId, model.InstanceId) | ||
| currentInstance, err := apiClient.GetInstanceExecute(ctx, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -260,3 +260,3 @@ return req, fmt.Errorf("get MongoDB Flex instance: %w", err) | ||
| if validationFlavorId == nil { | ||
| currentInstance, err := apiClient.GetInstanceExecute(ctx, model.ProjectId, model.InstanceId) | ||
| currentInstance, err := apiClient.GetInstanceExecute(ctx, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -267,3 +267,3 @@ return req, fmt.Errorf("get MongoDB Flex instance: %w", err) | ||
| } | ||
| storages, err = apiClient.ListStoragesExecute(ctx, model.ProjectId, *validationFlavorId) | ||
| storages, err = apiClient.ListStoragesExecute(ctx, model.ProjectId, *validationFlavorId, model.Region) | ||
| if err != nil { | ||
@@ -270,0 +270,0 @@ return req, fmt.Errorf("get MongoDB Flex storages: %w", err) |
@@ -31,3 +31,3 @@ package options | ||
| func (c *mongoDBFlexClientMocked) ListFlavorsExecute(_ context.Context, _ string) (*mongodbflex.ListFlavorsResponse, error) { | ||
| func (c *mongoDBFlexClientMocked) ListFlavorsExecute(_ context.Context, _, _ string) (*mongodbflex.ListFlavorsResponse, error) { | ||
| c.listFlavorsCalled = true | ||
@@ -38,7 +38,7 @@ if c.listFlavorsFails { | ||
| return utils.Ptr(mongodbflex.ListFlavorsResponse{ | ||
| Flavors: utils.Ptr([]mongodbflex.HandlersInfraFlavor{}), | ||
| Flavors: utils.Ptr([]mongodbflex.InstanceFlavor{}), | ||
| }), nil | ||
| } | ||
| func (c *mongoDBFlexClientMocked) ListVersionsExecute(_ context.Context, _ string) (*mongodbflex.ListVersionsResponse, error) { | ||
| func (c *mongoDBFlexClientMocked) ListVersionsExecute(_ context.Context, _, _ string) (*mongodbflex.ListVersionsResponse, error) { | ||
| c.listVersionsCalled = true | ||
@@ -53,3 +53,3 @@ if c.listVersionsFails { | ||
| func (c *mongoDBFlexClientMocked) ListStoragesExecute(_ context.Context, _, _ string) (*mongodbflex.ListStoragesResponse, error) { | ||
| func (c *mongoDBFlexClientMocked) ListStoragesExecute(_ context.Context, _, _, _ string) (*mongodbflex.ListStoragesResponse, error) { | ||
| c.listStoragesCalled = true | ||
@@ -56,0 +56,0 @@ if c.listStoragesFails { |
@@ -40,5 +40,5 @@ package options | ||
| type options struct { | ||
| Flavors *[]mongodbflex.HandlersInfraFlavor `json:"flavors,omitempty"` | ||
| Versions *[]string `json:"versions,omitempty"` | ||
| Storages *flavorStorages `json:"flavorStorages,omitempty"` | ||
| Flavors *[]mongodbflex.InstanceFlavor `json:"flavors,omitempty"` | ||
| Versions *[]string `json:"versions,omitempty"` | ||
| Storages *flavorStorages `json:"flavorStorages,omitempty"` | ||
| } | ||
@@ -142,5 +142,5 @@ | ||
| type mongoDBFlexOptionsClient interface { | ||
| ListFlavorsExecute(ctx context.Context, projectId string) (*mongodbflex.ListFlavorsResponse, error) | ||
| ListVersionsExecute(ctx context.Context, projectId string) (*mongodbflex.ListVersionsResponse, error) | ||
| ListStoragesExecute(ctx context.Context, projectId, flavorId string) (*mongodbflex.ListStoragesResponse, error) | ||
| ListFlavorsExecute(ctx context.Context, projectId, region string) (*mongodbflex.ListFlavorsResponse, error) | ||
| ListVersionsExecute(ctx context.Context, projectId, region string) (*mongodbflex.ListVersionsResponse, error) | ||
| ListStoragesExecute(ctx context.Context, projectId, flavorId, region string) (*mongodbflex.ListStoragesResponse, error) | ||
| } | ||
@@ -155,3 +155,3 @@ | ||
| if model.Flavors { | ||
| flavors, err = apiClient.ListFlavorsExecute(ctx, model.ProjectId) | ||
| flavors, err = apiClient.ListFlavorsExecute(ctx, model.ProjectId, model.Region) | ||
| if err != nil { | ||
@@ -162,3 +162,3 @@ return fmt.Errorf("get MongoDB Flex flavors: %w", err) | ||
| if model.Versions { | ||
| versions, err = apiClient.ListVersionsExecute(ctx, model.ProjectId) | ||
| versions, err = apiClient.ListVersionsExecute(ctx, model.ProjectId, model.Region) | ||
| if err != nil { | ||
@@ -169,3 +169,3 @@ return fmt.Errorf("get MongoDB Flex versions: %w", err) | ||
| if model.Storages { | ||
| storages, err = apiClient.ListStoragesExecute(ctx, model.ProjectId, *model.FlavorId) | ||
| storages, err = apiClient.ListStoragesExecute(ctx, model.ProjectId, *model.FlavorId, model.Region) | ||
| if err != nil { | ||
@@ -245,3 +245,3 @@ return fmt.Errorf("get MongoDB Flex storages: %w", err) | ||
| func buildFlavorsTable(flavors []mongodbflex.HandlersInfraFlavor) tables.Table { | ||
| func buildFlavorsTable(flavors []mongodbflex.InstanceFlavor) tables.Table { | ||
| table := tables.NewTable() | ||
@@ -248,0 +248,0 @@ table.SetTitle("Flavors") |
@@ -19,3 +19,5 @@ package create | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -31,7 +33,8 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| usernameFlag: "johndoe", | ||
| databaseFlag: "default", | ||
| roleFlag: "read", | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| usernameFlag: "johndoe", | ||
| databaseFlag: "default", | ||
| roleFlag: "read", | ||
| } | ||
@@ -48,2 +51,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -63,3 +67,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiCreateUserRequest)) mongodbflex.ApiCreateUserRequest { | ||
| request := testClient.CreateUser(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.CreateUser(testCtx, testProjectId, testInstanceId, testRegion) | ||
| request = request.CreateUserPayload(mongodbflex.CreateUserPayload{ | ||
@@ -109,3 +113,3 @@ Username: utils.Ptr("johndoe"), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -117,3 +121,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -125,3 +129,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -128,0 +132,0 @@ isValid: false, |
@@ -75,3 +75,3 @@ package create | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -107,3 +107,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| func configureFlags(cmd *cobra.Command) { | ||
| roleOptions := []string{"read", "readWrite"} | ||
| roleOptions := []string{"read", "readWrite", "readWriteAnyDatabase"} | ||
@@ -146,3 +146,3 @@ cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "ID of the instance") | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiCreateUserRequest { | ||
| req := apiClient.CreateUser(ctx, model.ProjectId, model.InstanceId) | ||
| req := apiClient.CreateUser(ctx, model.ProjectId, model.InstanceId, model.Region) | ||
| req = req.CreateUserPayload(mongodbflex.CreateUserPayload{ | ||
@@ -149,0 +149,0 @@ Username: model.Username, |
@@ -17,3 +17,5 @@ package delete | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -40,4 +42,5 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| } | ||
@@ -54,2 +57,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -67,3 +71,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiDeleteUserRequest)) mongodbflex.ApiDeleteUserRequest { | ||
| request := testClient.DeleteUser(testCtx, testProjectId, testInstanceId, testUserId) | ||
| request := testClient.DeleteUser(testCtx, testProjectId, testInstanceId, testUserId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -112,3 +116,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -121,3 +125,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -130,3 +134,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -133,0 +137,0 @@ isValid: false, |
@@ -62,3 +62,3 @@ package delete | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -69,3 +69,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| userLabel, err := mongodbflexUtils.GetUserName(ctx, apiClient, model.ProjectId, model.InstanceId, model.UserId) | ||
| userLabel, err := mongodbflexUtils.GetUserName(ctx, apiClient, model.ProjectId, model.InstanceId, model.UserId, model.Region) | ||
| if err != nil { | ||
@@ -133,4 +133,4 @@ params.Printer.Debug(print.ErrorLevel, "get user name: %v", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiDeleteUserRequest { | ||
| req := apiClient.DeleteUser(ctx, model.ProjectId, model.InstanceId, model.UserId) | ||
| req := apiClient.DeleteUser(ctx, model.ProjectId, model.InstanceId, model.UserId, model.Region) | ||
| return req | ||
| } |
@@ -17,3 +17,5 @@ package describe | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -40,4 +42,5 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| } | ||
@@ -54,2 +57,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -67,3 +71,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiGetUserRequest)) mongodbflex.ApiGetUserRequest { | ||
| request := testClient.GetUser(testCtx, testProjectId, testInstanceId, testUserId) | ||
| request := testClient.GetUser(testCtx, testProjectId, testInstanceId, testUserId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -112,3 +116,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -121,3 +125,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -130,3 +134,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -133,0 +137,0 @@ isValid: false, |
@@ -117,3 +117,3 @@ package describe | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiGetUserRequest { | ||
| req := apiClient.GetUser(ctx, model.ProjectId, model.InstanceId, model.UserId) | ||
| req := apiClient.GetUser(ctx, model.ProjectId, model.InstanceId, model.UserId, model.Region) | ||
| return req | ||
@@ -120,0 +120,0 @@ } |
@@ -19,3 +19,5 @@ package list | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -31,5 +33,6 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| limitFlag: "10", | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| limitFlag: "10", | ||
| } | ||
@@ -46,2 +49,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -59,3 +63,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiListUsersRequest)) mongodbflex.ApiListUsersRequest { | ||
| request := testClient.ListUsers(testCtx, testProjectId, testInstanceId) | ||
| request := testClient.ListUsers(testCtx, testProjectId, testInstanceId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -88,3 +92,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -96,3 +100,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -104,3 +108,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -107,0 +111,0 @@ isValid: false, |
@@ -74,3 +74,3 @@ package list | ||
| if resp.Items == nil || len(*resp.Items) == 0 { | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, *model.InstanceId) | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, *model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -139,3 +139,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiListUsersRequest { | ||
| req := apiClient.ListUsers(ctx, model.ProjectId, *model.InstanceId) | ||
| req := apiClient.ListUsers(ctx, model.ProjectId, *model.InstanceId, model.Region) | ||
| return req | ||
@@ -142,0 +142,0 @@ } |
@@ -17,3 +17,5 @@ package resetpassword | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -40,4 +42,5 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| } | ||
@@ -54,2 +57,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -67,3 +71,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiResetUserRequest)) mongodbflex.ApiResetUserRequest { | ||
| request := testClient.ResetUser(testCtx, testProjectId, testInstanceId, testUserId) | ||
| request := testClient.ResetUser(testCtx, testProjectId, testInstanceId, testUserId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -112,3 +116,3 @@ mod(&request) | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -121,3 +125,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -130,3 +134,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -133,0 +137,0 @@ isValid: false, |
@@ -64,3 +64,3 @@ package resetpassword | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -71,3 +71,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| userLabel, err := mongodbflexUtils.GetUserName(ctx, apiClient, model.ProjectId, model.InstanceId, model.UserId) | ||
| userLabel, err := mongodbflexUtils.GetUserName(ctx, apiClient, model.ProjectId, model.InstanceId, model.UserId, model.Region) | ||
| if err != nil { | ||
@@ -135,3 +135,3 @@ params.Printer.Debug(print.ErrorLevel, "get user name: %v", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiResetUserRequest { | ||
| req := apiClient.ResetUser(ctx, model.ProjectId, model.InstanceId, model.UserId) | ||
| req := apiClient.ResetUser(ctx, model.ProjectId, model.InstanceId, model.UserId, model.Region) | ||
| return req | ||
@@ -138,0 +138,0 @@ } |
@@ -18,3 +18,5 @@ package update | ||
| var projectIdFlag = globalflags.ProjectIdFlag | ||
| const ( | ||
| testRegion = "eu02" | ||
| ) | ||
@@ -41,5 +43,6 @@ type testCtxKey struct{} | ||
| flagValues := map[string]string{ | ||
| projectIdFlag: testProjectId, | ||
| instanceIdFlag: testInstanceId, | ||
| databaseFlag: "default", | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| instanceIdFlag: testInstanceId, | ||
| databaseFlag: "default", | ||
| } | ||
@@ -56,2 +59,3 @@ for _, mod := range mods { | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
@@ -70,3 +74,3 @@ }, | ||
| func fixtureRequest(mods ...func(request *mongodbflex.ApiPartialUpdateUserRequest)) mongodbflex.ApiPartialUpdateUserRequest { | ||
| request := testClient.PartialUpdateUser(testCtx, testProjectId, testInstanceId, testUserId) | ||
| request := testClient.PartialUpdateUser(testCtx, testProjectId, testInstanceId, testUserId, testRegion) | ||
| request = request.PartialUpdateUserPayload(mongodbflex.PartialUpdateUserPayload{ | ||
@@ -135,3 +139,3 @@ Database: utils.Ptr("default"), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, projectIdFlag) | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
@@ -144,3 +148,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "" | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
@@ -153,3 +157,3 @@ isValid: false, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[projectIdFlag] = "invalid-uuid" | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
@@ -156,0 +160,0 @@ isValid: false, |
@@ -63,3 +63,3 @@ package update | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| instanceLabel, err := mongodbflexUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId, model.Region) | ||
| if err != nil { | ||
@@ -70,3 +70,3 @@ params.Printer.Debug(print.ErrorLevel, "get instance name: %v", err) | ||
| userLabel, err := mongodbflexUtils.GetUserName(ctx, apiClient, model.ProjectId, model.InstanceId, model.UserId) | ||
| userLabel, err := mongodbflexUtils.GetUserName(ctx, apiClient, model.ProjectId, model.InstanceId, model.UserId, model.Region) | ||
| if err != nil { | ||
@@ -102,3 +102,3 @@ params.Printer.Debug(print.ErrorLevel, "get user name: %v", err) | ||
| func configureFlags(cmd *cobra.Command) { | ||
| roleOptions := []string{"read", "readWrite"} | ||
| roleOptions := []string{"read", "readWrite", "readWriteAnyDatabase"} | ||
@@ -149,3 +149,3 @@ cmd.Flags().Var(flags.UUIDFlag(), instanceIdFlag, "ID of the instance") | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *mongodbflex.APIClient) mongodbflex.ApiPartialUpdateUserRequest { | ||
| req := apiClient.PartialUpdateUser(ctx, model.ProjectId, model.InstanceId, model.UserId) | ||
| req := apiClient.PartialUpdateUser(ctx, model.ProjectId, model.InstanceId, model.UserId, model.Region) | ||
| req = req.PartialUpdateUserPayload(mongodbflex.PartialUpdateUserPayload{ | ||
@@ -152,0 +152,0 @@ Database: model.Database, |
@@ -159,5 +159,5 @@ package create | ||
| default: | ||
| p.Outputf("Created security group %q\n", name) | ||
| p.Outputf("Created security group %q.\nSecurity Group ID %s\n", name, utils.PtrString(resp.Id)) | ||
| return nil | ||
| } | ||
| } |
@@ -9,4 +9,8 @@ package cluster | ||
| generatepayload "github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/generate-payload" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/hibernate" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/list" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/maintenance" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/reconcile" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/update" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/cluster/wakeup" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
@@ -37,2 +41,6 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| cmd.AddCommand(update.NewCmd(params)) | ||
| cmd.AddCommand(hibernate.NewCmd(params)) | ||
| cmd.AddCommand(maintenance.NewCmd(params)) | ||
| cmd.AddCommand(reconcile.NewCmd(params)) | ||
| cmd.AddCommand(wakeup.NewCmd(params)) | ||
| } |
@@ -43,3 +43,3 @@ package create | ||
| Use: fmt.Sprintf("create %s", clusterNameArg), | ||
| Short: "Creates an SKE cluster", | ||
| Short: "Creates a SKE cluster", | ||
| Long: fmt.Sprintf("%s\n%s\n%s", | ||
@@ -53,9 +53,9 @@ "Creates a STACKIT Kubernetes Engine (SKE) cluster.", | ||
| examples.NewExample( | ||
| `Create an SKE cluster using default configuration`, | ||
| `Create a SKE cluster using default configuration`, | ||
| "$ stackit ske cluster create my-cluster"), | ||
| examples.NewExample( | ||
| `Create an SKE cluster using an API payload sourced from the file "./payload.json"`, | ||
| `Create a SKE cluster using an API payload sourced from the file "./payload.json"`, | ||
| "$ stackit ske cluster create my-cluster --payload @./payload.json"), | ||
| examples.NewExample( | ||
| `Create an SKE cluster using an API payload provided as a JSON string`, | ||
| `Create a SKE cluster using an API payload provided as a JSON string`, | ||
| `$ stackit ske cluster create my-cluster --payload "{...}"`), | ||
@@ -62,0 +62,0 @@ examples.NewExample( |
@@ -38,3 +38,3 @@ package delete | ||
| examples.NewExample( | ||
| `Delete an SKE cluster with name "my-cluster"`, | ||
| `Delete a SKE cluster with name "my-cluster"`, | ||
| "$ stackit ske cluster delete my-cluster"), | ||
@@ -41,0 +41,0 @@ ), |
@@ -34,11 +34,11 @@ package describe | ||
| Use: fmt.Sprintf("describe %s", clusterNameArg), | ||
| Short: "Shows details of a SKE cluster", | ||
| Long: "Shows details of a STACKIT Kubernetes Engine (SKE) cluster.", | ||
| Short: "Shows details of a SKE cluster", | ||
| Long: "Shows details of a STACKIT Kubernetes Engine (SKE) cluster.", | ||
| Args: args.SingleArg(clusterNameArg, nil), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Get details of an SKE cluster with name "my-cluster"`, | ||
| `Get details of a SKE cluster with name "my-cluster"`, | ||
| "$ stackit ske cluster describe my-cluster"), | ||
| examples.NewExample( | ||
| `Get details of an SKE cluster with name "my-cluster" in JSON format`, | ||
| `Get details of a SKE cluster with name "my-cluster" in JSON format`, | ||
| "$ stackit ske cluster describe my-cluster --output-format json"), | ||
@@ -45,0 +45,0 @@ ), |
@@ -40,3 +40,3 @@ package update | ||
| Use: fmt.Sprintf("update %s", clusterNameArg), | ||
| Short: "Updates an SKE cluster", | ||
| Short: "Updates a SKE cluster", | ||
| Long: fmt.Sprintf("%s\n%s\n%s", | ||
@@ -50,6 +50,6 @@ "Updates a STACKIT Kubernetes Engine (SKE) cluster.", | ||
| examples.NewExample( | ||
| `Update an SKE cluster using an API payload sourced from the file "./payload.json"`, | ||
| `Update a SKE cluster using an API payload sourced from the file "./payload.json"`, | ||
| "$ stackit ske cluster update my-cluster --payload @./payload.json"), | ||
| examples.NewExample( | ||
| `Update an SKE cluster using an API payload provided as a JSON string`, | ||
| `Update a SKE cluster using an API payload provided as a JSON string`, | ||
| `$ stackit ske cluster update my-cluster --payload "{...}"`), | ||
@@ -56,0 +56,0 @@ examples.NewExample( |
@@ -47,5 +47,5 @@ package create | ||
| Use: fmt.Sprintf("create %s", clusterNameArg), | ||
| Short: "Creates or update a kubeconfig for an SKE cluster", | ||
| Short: "Creates or update a kubeconfig for a SKE cluster", | ||
| Long: fmt.Sprintf("%s\n\n%s\n%s\n%s\n%s", | ||
| "Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exits in the kubeconfig file the information will be updated.", | ||
| "Creates a kubeconfig for a STACKIT Kubernetes Engine (SKE) cluster, if the config exists in the kubeconfig file the information will be updated.", | ||
| "By default, the kubeconfig information of the SKE cluster is merged into the default kubeconfig file of the current user. If the kubeconfig file doesn't exist, a new one will be created.", | ||
@@ -52,0 +52,0 @@ "You can override this behavior by specifying a custom filepath with the --filepath flag.\n", |
@@ -31,9 +31,9 @@ package ske | ||
| func addSubcommands(cmd *cobra.Command, params *params.CmdParams) { | ||
| cmd.AddCommand(cluster.NewCmd(params)) | ||
| cmd.AddCommand(credentials.NewCmd(params)) | ||
| cmd.AddCommand(describe.NewCmd(params)) | ||
| cmd.AddCommand(disable.NewCmd(params)) | ||
| cmd.AddCommand(enable.NewCmd(params)) | ||
| cmd.AddCommand(kubeconfig.NewCmd(params)) | ||
| cmd.AddCommand(disable.NewCmd(params)) | ||
| cmd.AddCommand(cluster.NewCmd(params)) | ||
| cmd.AddCommand(credentials.NewCmd(params)) | ||
| cmd.AddCommand(options.NewCmd(params)) | ||
| } |
@@ -5,2 +5,3 @@ package auth | ||
| "fmt" | ||
| "net/http" | ||
| "os" | ||
@@ -136,1 +137,69 @@ "strconv" | ||
| } | ||
| // RefreshAccessToken refreshes the access token if it's expired for the user token flow. | ||
| // It returns the new access token or an error if the refresh fails. | ||
| func RefreshAccessToken(p *print.Printer) (string, error) { | ||
| flow, err := GetAuthFlow() | ||
| if err != nil { | ||
| return "", fmt.Errorf("get authentication flow: %w", err) | ||
| } | ||
| if flow != AUTH_FLOW_USER_TOKEN { | ||
| return "", fmt.Errorf("token refresh is only supported for user token flow, current flow: %s", flow) | ||
| } | ||
| // Load tokens from storage | ||
| authFields := map[authFieldKey]string{ | ||
| ACCESS_TOKEN: "", | ||
| REFRESH_TOKEN: "", | ||
| IDP_TOKEN_ENDPOINT: "", | ||
| } | ||
| err = GetAuthFieldMap(authFields) | ||
| if err != nil { | ||
| return "", fmt.Errorf("get tokens from auth storage: %w", err) | ||
| } | ||
| accessToken := authFields[ACCESS_TOKEN] | ||
| refreshToken := authFields[REFRESH_TOKEN] | ||
| tokenEndpoint := authFields[IDP_TOKEN_ENDPOINT] | ||
| if accessToken == "" { | ||
| return "", fmt.Errorf("access token not set") | ||
| } | ||
| if refreshToken == "" { | ||
| return "", fmt.Errorf("refresh token not set") | ||
| } | ||
| if tokenEndpoint == "" { | ||
| return "", fmt.Errorf("token endpoint not set") | ||
| } | ||
| // Check if access token is expired | ||
| accessTokenExpired, err := TokenExpired(accessToken) | ||
| if err != nil { | ||
| return "", fmt.Errorf("check if access token has expired: %w", err) | ||
| } | ||
| if !accessTokenExpired { | ||
| // Token is still valid, return it | ||
| return accessToken, nil | ||
| } | ||
| p.Debug(print.DebugLevel, "access token expired, refreshing...") | ||
| // Create a temporary userTokenFlow to reuse the refresh logic | ||
| utf := &userTokenFlow{ | ||
| printer: p, | ||
| client: &http.Client{}, | ||
| authFlow: flow, | ||
| accessToken: accessToken, | ||
| refreshToken: refreshToken, | ||
| tokenEndpoint: tokenEndpoint, | ||
| } | ||
| // Refresh the tokens | ||
| err = refreshTokens(utf) | ||
| if err != nil { | ||
| return "", fmt.Errorf("refresh access token: %w", err) | ||
| } | ||
| // Return the new access token | ||
| return utf.accessToken, nil | ||
| } |
@@ -21,6 +21,4 @@ package client | ||
| } | ||
| region := viper.GetString(config.RegionKey) | ||
| cfgOptions := []sdkConfig.ConfigurationOption{ | ||
| utils.UserAgentConfigOption(cliVersion), | ||
| sdkConfig.WithRegion(region), | ||
| authCfgOption, | ||
@@ -30,3 +28,2 @@ } | ||
| customEndpoint := viper.GetString(config.MongoDBFlexCustomEndpointKey) | ||
| if customEndpoint != "" { | ||
@@ -33,0 +30,0 @@ cfgOptions = append(cfgOptions, sdkConfig.WithEndpoint(customEndpoint)) |
@@ -23,2 +23,3 @@ package utils | ||
| const ( | ||
| testRegion = "eu02" | ||
| testInstanceName = "instance" | ||
@@ -32,3 +33,3 @@ testUserName = "user" | ||
| getInstanceFails bool | ||
| getInstanceResp *mongodbflex.GetInstanceResponse | ||
| getInstanceResp *mongodbflex.InstanceResponse | ||
| getUserFails bool | ||
@@ -40,3 +41,3 @@ getUserResp *mongodbflex.GetUserResponse | ||
| func (m *mongoDBFlexClientMocked) ListVersionsExecute(_ context.Context, _ string) (*mongodbflex.ListVersionsResponse, error) { | ||
| func (m *mongoDBFlexClientMocked) ListVersionsExecute(_ context.Context, _, _ string) (*mongodbflex.ListVersionsResponse, error) { | ||
| if m.listVersionsFails { | ||
@@ -48,3 +49,3 @@ return nil, fmt.Errorf("could not list versions") | ||
| func (m *mongoDBFlexClientMocked) ListRestoreJobsExecute(_ context.Context, _, _ string) (*mongodbflex.ListRestoreJobsResponse, error) { | ||
| func (m *mongoDBFlexClientMocked) ListRestoreJobsExecute(_ context.Context, _, _, _ string) (*mongodbflex.ListRestoreJobsResponse, error) { | ||
| if m.listRestoreJobsFails { | ||
@@ -56,3 +57,3 @@ return nil, fmt.Errorf("could not list versions") | ||
| func (m *mongoDBFlexClientMocked) GetInstanceExecute(_ context.Context, _, _ string) (*mongodbflex.GetInstanceResponse, error) { | ||
| func (m *mongoDBFlexClientMocked) GetInstanceExecute(_ context.Context, _, _, _ string) (*mongodbflex.InstanceResponse, error) { | ||
| if m.getInstanceFails { | ||
@@ -64,3 +65,3 @@ return nil, fmt.Errorf("could not get instance") | ||
| func (m *mongoDBFlexClientMocked) GetUserExecute(_ context.Context, _, _, _ string) (*mongodbflex.GetUserResponse, error) { | ||
| func (m *mongoDBFlexClientMocked) GetUserExecute(_ context.Context, _, _, _, _ string) (*mongodbflex.GetUserResponse, error) { | ||
| if m.getUserFails { | ||
@@ -184,3 +185,3 @@ return nil, fmt.Errorf("could not get user") | ||
| flavorId string | ||
| flavors *[]mongodbflex.HandlersInfraFlavor | ||
| flavors *[]mongodbflex.InstanceFlavor | ||
| isValid bool | ||
@@ -191,3 +192,3 @@ }{ | ||
| flavorId: "foo", | ||
| flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| flavors: &[]mongodbflex.InstanceFlavor{ | ||
| {Id: utils.Ptr("bar-1")}, | ||
@@ -208,3 +209,3 @@ {Id: utils.Ptr("bar-2")}, | ||
| flavorId: "foo", | ||
| flavors: &[]mongodbflex.HandlersInfraFlavor{}, | ||
| flavors: &[]mongodbflex.InstanceFlavor{}, | ||
| isValid: false, | ||
@@ -215,3 +216,3 @@ }, | ||
| flavorId: "foo", | ||
| flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| flavors: &[]mongodbflex.InstanceFlavor{ | ||
| {Id: utils.Ptr("bar-1")}, | ||
@@ -226,3 +227,3 @@ {Id: nil}, | ||
| flavorId: "foo", | ||
| flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| flavors: &[]mongodbflex.InstanceFlavor{ | ||
| {Id: utils.Ptr("bar-1")}, | ||
@@ -254,3 +255,3 @@ {Id: utils.Ptr("bar-2")}, | ||
| ram int64 | ||
| flavors *[]mongodbflex.HandlersInfraFlavor | ||
| flavors *[]mongodbflex.InstanceFlavor | ||
| isValid bool | ||
@@ -263,3 +264,3 @@ expectedOutput *string | ||
| ram: 4, | ||
| flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -295,3 +296,3 @@ Id: utils.Ptr("bar-1"), | ||
| ram: 4, | ||
| flavors: &[]mongodbflex.HandlersInfraFlavor{}, | ||
| flavors: &[]mongodbflex.InstanceFlavor{}, | ||
| isValid: false, | ||
@@ -303,3 +304,3 @@ }, | ||
| ram: 4, | ||
| flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -328,3 +329,3 @@ Id: utils.Ptr("bar-1"), | ||
| ram: 4, | ||
| flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -352,3 +353,3 @@ Id: utils.Ptr("bar-1"), | ||
| ram: 4, | ||
| flavors: &[]mongodbflex.HandlersInfraFlavor{ | ||
| flavors: &[]mongodbflex.InstanceFlavor{ | ||
| { | ||
@@ -431,3 +432,3 @@ Id: utils.Ptr("bar-1"), | ||
| output, err := GetLatestMongoDBVersion(context.Background(), client, testProjectId) | ||
| output, err := GetLatestMongoDBVersion(context.Background(), client, testProjectId, testRegion) | ||
@@ -454,3 +455,3 @@ if tt.isValid && err != nil { | ||
| getInstanceFails bool | ||
| getInstanceResp *mongodbflex.GetInstanceResponse | ||
| getInstanceResp *mongodbflex.InstanceResponse | ||
| isValid bool | ||
@@ -461,3 +462,3 @@ expectedOutput string | ||
| description: "base", | ||
| getInstanceResp: &mongodbflex.GetInstanceResponse{ | ||
| getInstanceResp: &mongodbflex.InstanceResponse{ | ||
| Item: &mongodbflex.Instance{ | ||
@@ -484,3 +485,3 @@ Name: utils.Ptr(testInstanceName), | ||
| output, err := GetInstanceName(context.Background(), client, testProjectId, testInstanceId) | ||
| output, err := GetInstanceName(context.Background(), client, testProjectId, testInstanceId, testRegion) | ||
@@ -535,3 +536,3 @@ if tt.isValid && err != nil { | ||
| output, err := GetUserName(context.Background(), client, testProjectId, testInstanceId, testUserId) | ||
| output, err := GetUserName(context.Background(), client, testProjectId, testInstanceId, testUserId, testRegion) | ||
@@ -538,0 +539,0 @@ if tt.isValid && err != nil { |
@@ -24,6 +24,6 @@ package utils | ||
| type MongoDBFlexClient interface { | ||
| ListVersionsExecute(ctx context.Context, projectId string) (*mongodbflex.ListVersionsResponse, error) | ||
| GetInstanceExecute(ctx context.Context, projectId, instanceId string) (*mongodbflex.GetInstanceResponse, error) | ||
| GetUserExecute(ctx context.Context, projectId, instanceId, userId string) (*mongodbflex.GetUserResponse, error) | ||
| ListRestoreJobsExecute(ctx context.Context, projectId string, instanceId string) (*mongodbflex.ListRestoreJobsResponse, error) | ||
| ListVersionsExecute(ctx context.Context, projectId, region string) (*mongodbflex.ListVersionsResponse, error) | ||
| GetInstanceExecute(ctx context.Context, projectId, instanceId, region string) (*mongodbflex.InstanceResponse, error) | ||
| GetUserExecute(ctx context.Context, projectId, instanceId, userId, region string) (*mongodbflex.GetUserResponse, error) | ||
| ListRestoreJobsExecute(ctx context.Context, projectId string, instanceId, region string) (*mongodbflex.ListRestoreJobsResponse, error) | ||
| } | ||
@@ -61,3 +61,3 @@ | ||
| func ValidateFlavorId(flavorId string, flavors *[]mongodbflex.HandlersInfraFlavor) error { | ||
| func ValidateFlavorId(flavorId string, flavors *[]mongodbflex.InstanceFlavor) error { | ||
| if flavors == nil { | ||
@@ -106,3 +106,3 @@ return fmt.Errorf("nil flavors") | ||
| func LoadFlavorId(cpu, ram int64, flavors *[]mongodbflex.HandlersInfraFlavor) (*string, error) { | ||
| func LoadFlavorId(cpu, ram int64, flavors *[]mongodbflex.InstanceFlavor) (*string, error) { | ||
| if flavors == nil { | ||
@@ -128,4 +128,4 @@ return nil, fmt.Errorf("nil flavors") | ||
| func GetLatestMongoDBVersion(ctx context.Context, apiClient MongoDBFlexClient, projectId string) (string, error) { | ||
| resp, err := apiClient.ListVersionsExecute(ctx, projectId) | ||
| func GetLatestMongoDBVersion(ctx context.Context, apiClient MongoDBFlexClient, projectId, region string) (string, error) { | ||
| resp, err := apiClient.ListVersionsExecute(ctx, projectId, region) | ||
| if err != nil { | ||
@@ -151,4 +151,4 @@ return "", fmt.Errorf("get MongoDB versions: %w", err) | ||
| func GetInstanceName(ctx context.Context, apiClient MongoDBFlexClient, projectId, instanceId string) (string, error) { | ||
| resp, err := apiClient.GetInstanceExecute(ctx, projectId, instanceId) | ||
| func GetInstanceName(ctx context.Context, apiClient MongoDBFlexClient, projectId, instanceId, region string) (string, error) { | ||
| resp, err := apiClient.GetInstanceExecute(ctx, projectId, instanceId, region) | ||
| if err != nil { | ||
@@ -160,4 +160,4 @@ return "", fmt.Errorf("get MongoDB Flex instance: %w", err) | ||
| func GetUserName(ctx context.Context, apiClient MongoDBFlexClient, projectId, instanceId, userId string) (string, error) { | ||
| resp, err := apiClient.GetUserExecute(ctx, projectId, instanceId, userId) | ||
| func GetUserName(ctx context.Context, apiClient MongoDBFlexClient, projectId, instanceId, userId, region string) (string, error) { | ||
| resp, err := apiClient.GetUserExecute(ctx, projectId, instanceId, userId, region) | ||
| if err != nil { | ||
@@ -164,0 +164,0 @@ return "", fmt.Errorf("get MongoDB Flex user: %w", err) |
| ## stackit git create | ||
| Creates STACKIT Git instance | ||
| ### Synopsis | ||
| Create a STACKIT Git instance by name. | ||
| ``` | ||
| stackit git create [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Create a instance with name 'my-new-instance' | ||
| $ stackit git create --name my-new-instance | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git create" | ||
| --name string The name of the instance. | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git](./stackit_git.md) - Provides functionality for STACKIT Git | ||
| ## stackit git delete | ||
| Deletes STACKIT Git instance | ||
| ### Synopsis | ||
| Deletes a STACKIT Git instance by its internal ID. | ||
| ``` | ||
| stackit git delete INSTANCE_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Delete a instance with ID "xxx" | ||
| $ stackit git delete xxx | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git delete" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git](./stackit_git.md) - Provides functionality for STACKIT Git | ||
| ## stackit git describe | ||
| Describes STACKIT Git instance | ||
| ### Synopsis | ||
| Describes a STACKIT Git instance by its internal ID. | ||
| ``` | ||
| stackit git describe INSTANCE_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Describe instance "xxx" | ||
| $ stackit git describe xxx | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git describe" | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git](./stackit_git.md) - Provides functionality for STACKIT Git | ||
| ## stackit git list | ||
| Lists all instances of STACKIT Git. | ||
| ### Synopsis | ||
| Lists all instances of STACKIT Git for the current project. | ||
| ``` | ||
| stackit git list [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List all STACKIT Git instances | ||
| $ stackit git instance list | ||
| Lists up to 10 STACKIT Git instances | ||
| $ stackit git instance list --limit=10 | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit git list" | ||
| --limit int Limit the output to the first n elements | ||
| ``` | ||
| ### 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 | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit git](./stackit_git.md) - Provides functionality for STACKIT Git | ||
| package create | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/git" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &git.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testName = "test-instance" | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| nameFlag: testName, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Verbosity: globalflags.VerbosityDefault}, | ||
| Name: testName, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureCreatePayload(mods ...func(payload *git.CreateInstancePayload)) (payload git.CreateInstancePayload) { | ||
| payload = git.CreateInstancePayload{ | ||
| Name: &testName, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(&payload) | ||
| } | ||
| return payload | ||
| } | ||
| func fixtureRequest(mods ...func(request *git.ApiCreateInstanceRequest)) git.ApiCreateInstanceRequest { | ||
| request := testClient.CreateInstance(testCtx, testProjectId) | ||
| request = request.CreateInstancePayload(fixtureCreatePayload()) | ||
| 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, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "name missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, nameFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := NewCmd(¶ms.CmdParams{Printer: p}) | ||
| if err := globalflags.Configure(cmd.Flags()); err != nil { | ||
| t.Errorf("cannot 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) | ||
| } | ||
| } | ||
| if err := cmd.ValidateFlagGroups(); err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| t.Fatalf("error validating flag groups: %v", err) | ||
| } | ||
| if err := cmd.ValidateRequiredFlags(); 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 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 *inputModel | ||
| expectedRequest git.ApiCreateInstanceRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| { | ||
| description: "name flag", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.Name = "new-name" | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *git.ApiCreateInstanceRequest) { | ||
| *request = (*request).CreateInstancePayload(fixtureCreatePayload(func(payload *git.CreateInstancePayload) { | ||
| payload.Name = utils.Ptr("new-name") | ||
| })) | ||
| }), | ||
| }, | ||
| } | ||
| 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), | ||
| cmp.AllowUnexported(git.NullableString{}), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("Data does not match: %s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| type args struct { | ||
| model *inputModel | ||
| resp *git.Instance | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "nil", | ||
| args: args{ | ||
| model: nil, | ||
| resp: nil, | ||
| }, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty input", | ||
| args: args{ | ||
| model: &inputModel{}, | ||
| resp: &git.Instance{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "output json", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| OutputFormat: print.JSONOutputFormat, | ||
| }, | ||
| }, | ||
| resp: nil, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package create | ||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
| "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/git/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git/wait" | ||
| ) | ||
| const ( | ||
| nameFlag = "name" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| Id *string | ||
| Name string | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "create", | ||
| Short: "Creates STACKIT Git instance", | ||
| Long: "Create a STACKIT Git instance by name.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Create a instance with name 'my-new-instance'`, | ||
| `$ stackit git create --name my-new-instance`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, _ []string) (err error) { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if !model.AssumeYes { | ||
| prompt := fmt.Sprintf("Are you sure you want to create the instance %q?", model.Name) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| // Call API | ||
| request := buildRequest(ctx, model, apiClient) | ||
| result, err := request.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("create stackit git instance: %w", err) | ||
| } | ||
| model.Id = result.Id | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(params.Printer) | ||
| s.Start("Creating stackit git instance") | ||
| _, err = wait.CreateGitInstanceWaitHandler(ctx, apiClient, model.ProjectId, *model.Id).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for stackit git Instance creation: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| return outputResult(params.Printer, model, result) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().String(nameFlag, "", "The name of the instance.") | ||
| if err := flags.MarkFlagsRequired(cmd, nameFlag); err != nil { | ||
| 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{} | ||
| } | ||
| name := flags.FlagToStringValue(p, cmd, nameFlag) | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| Name: name, | ||
| } | ||
| 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 *git.APIClient) git.ApiCreateInstanceRequest { | ||
| return apiClient.CreateInstance(ctx, model.ProjectId).CreateInstancePayload(createPayload(model)) | ||
| } | ||
| func createPayload(model *inputModel) git.CreateInstancePayload { | ||
| return git.CreateInstancePayload{ | ||
| Name: &model.Name, | ||
| } | ||
| } | ||
| func outputResult(p *print.Printer, model *inputModel, resp *git.Instance) error { | ||
| if model == nil { | ||
| return fmt.Errorf("input model is nil") | ||
| } | ||
| var outputFormat string | ||
| if model.GlobalFlagModel != nil { | ||
| outputFormat = model.OutputFormat | ||
| } | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal instance: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal iminstanceage: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| default: | ||
| p.Outputf("Created instance %q with id %s\n", model.Name, utils.PtrString(model.Id)) | ||
| return nil | ||
| } | ||
| } |
| package delete | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &git.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testInstanceId = uuid.NewString() | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Verbosity: globalflags.VerbosityDefault}, | ||
| InstanceId: testInstanceId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *git.ApiDeleteInstanceRequest)) git.ApiDeleteInstanceRequest { | ||
| request := testClient.DeleteInstance(testCtx, testProjectId, testInstanceId) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| flagValues map[string]string | ||
| args []string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{testInstanceId}, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no arguments", | ||
| flagValues: fixtureFlagValues(), | ||
| args: nil, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "multiple arguments", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{"foo", "bar"}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid instance id", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{"foo"}, | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := NewCmd(¶ms.CmdParams{Printer: p}) | ||
| err := globalflags.Configure(cmd.Flags()) | ||
| if err != nil { | ||
| t.Fatalf("configure global flags: %v", err) | ||
| } | ||
| cmd.SetArgs(tt.args) | ||
| 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) | ||
| } | ||
| } | ||
| if err := cmd.ValidateArgs(tt.args); err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| } | ||
| err = cmd.ValidateRequiredFlags() | ||
| if err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| t.Fatalf("error validating flags: %v", err) | ||
| } | ||
| model, err := parseInput(p, cmd, tt.args) | ||
| 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 *inputModel | ||
| expectedRequest git.ApiDeleteInstanceRequest | ||
| }{ | ||
| { | ||
| 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) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package delete | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/spf13/cobra" | ||
| "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/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client" | ||
| gitUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git/wait" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| InstanceId string | ||
| } | ||
| const instanceIdArg = "INSTANCE_ID" | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("delete %s", instanceIdArg), | ||
| Short: "Deletes STACKIT Git instance", | ||
| Long: "Deletes a STACKIT Git instance by its internal ID.", | ||
| Args: args.SingleArg(instanceIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample(`Delete a instance with ID "xxx"`, `$ stackit git delete xxx`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| projectName, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| projectName = model.ProjectId | ||
| } | ||
| instanceName, err := gitUtils.GetInstanceName(ctx, apiClient, model.ProjectId, model.InstanceId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get stackit git intance name: %v", err) | ||
| instanceName = model.InstanceId | ||
| } else if instanceName == "" { | ||
| instanceName = model.InstanceId | ||
| } | ||
| if !model.AssumeYes { | ||
| prompt := fmt.Sprintf("Are you sure you want to delete the stackit git instance %q for %q?", instanceName, projectName) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| // Call API | ||
| request := buildRequest(ctx, model, apiClient) | ||
| err = request.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("delete instance: %w", err) | ||
| } | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(params.Printer) | ||
| s.Start("Deleting stackit git instance") | ||
| _, err = wait.DeleteGitInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for stackit git instance deletion: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| operationState := "Deleted" | ||
| if model.Async { | ||
| operationState = "Triggered deletion of" | ||
| } | ||
| params.Printer.Info("%s stackit git instance %s \n", operationState, model.InstanceId) | ||
| return nil | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, cliArgs []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| InstanceId: cliArgs[0], | ||
| } | ||
| 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 *git.APIClient) git.ApiDeleteInstanceRequest { | ||
| return apiClient.DeleteInstance(ctx, model.ProjectId, model.InstanceId) | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &git.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testInstanceId = []string{uuid.NewString()} | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Verbosity: globalflags.VerbosityDefault}, | ||
| InstanceId: testInstanceId[0], | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *git.ApiGetInstanceRequest)) git.ApiGetInstanceRequest { | ||
| request := testClient.GetInstance(testCtx, testProjectId, testInstanceId[0]) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| args []string | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| expectedModel: fixtureInputModel(), | ||
| args: testInstanceId, | ||
| isValid: true, | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| args: testInstanceId, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| args: testInstanceId, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| args: testInstanceId, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| args: testInstanceId, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no instance id passed", | ||
| flagValues: fixtureFlagValues(), | ||
| args: nil, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "multiple instance ids passed", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{uuid.NewString(), uuid.NewString()}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid instance id passed", | ||
| flagValues: fixtureFlagValues(), | ||
| args: []string{"foobar"}, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := NewCmd(¶ms.CmdParams{Printer: p}) | ||
| if err := globalflags.Configure(cmd.Flags()); err != nil { | ||
| t.Errorf("cannot 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) | ||
| } | ||
| } | ||
| if err := cmd.ValidateRequiredFlags(); err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| t.Fatalf("error validating flags: %v", err) | ||
| } | ||
| if err := cmd.ValidateArgs(tt.args); err != nil { | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
| } | ||
| model, err := parseInput(p, cmd, tt.args) | ||
| 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 *inputModel | ||
| expectedRequest git.ApiGetInstanceRequest | ||
| }{ | ||
| { | ||
| 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) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| type args struct { | ||
| outputFormat string | ||
| resp *git.Instance | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{ | ||
| resp: &git.Instance{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "nil", | ||
| args: args{}, | ||
| wantErr: true, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
| "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/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| InstanceId string | ||
| } | ||
| const instanceIdArg = "INSTANCE_ID" | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("describe %s", instanceIdArg), | ||
| Short: "Describes STACKIT Git instance", | ||
| Long: "Describes a STACKIT Git instance by its internal ID.", | ||
| Args: args.SingleArg(instanceIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample(`Describe instance "xxx"`, `$ stackit git describe xxx`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| request := buildRequest(ctx, model, apiClient) | ||
| instance, err := request.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get instance: %w", err) | ||
| } | ||
| if err := outputResult(params.Printer, model.OutputFormat, instance); err != nil { | ||
| return err | ||
| } | ||
| return nil | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, cliArgs []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| InstanceId: cliArgs[0], | ||
| } | ||
| 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 *git.APIClient) git.ApiGetInstanceRequest { | ||
| return apiClient.GetInstance(ctx, model.ProjectId, model.InstanceId) | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, resp *git.Instance) error { | ||
| if resp == nil { | ||
| return fmt.Errorf("instance not found") | ||
| } | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal instance: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal instance: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| default: | ||
| table := tables.NewTable() | ||
| if id := resp.Id; id != nil { | ||
| table.AddRow("ID", *id) | ||
| table.AddSeparator() | ||
| } | ||
| if name := resp.Name; name != nil { | ||
| table.AddRow("NAME", *name) | ||
| table.AddSeparator() | ||
| } | ||
| if url := resp.Url; url != nil { | ||
| table.AddRow("URL", *url) | ||
| table.AddSeparator() | ||
| } | ||
| if version := resp.Version; version != nil { | ||
| table.AddRow("VERSION", *version) | ||
| table.AddSeparator() | ||
| } | ||
| if state := resp.State; state != nil { | ||
| table.AddRow("STATE", *state) | ||
| table.AddSeparator() | ||
| } | ||
| if created := resp.Created; created != nil { | ||
| table.AddRow("CREATED", *created) | ||
| table.AddSeparator() | ||
| } | ||
| if err := table.Display(p); err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "strconv" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "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/git" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &git.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| const ( | ||
| testLimit = 10 | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *git.ApiListInstancesRequest)) git.ApiListInstancesRequest { | ||
| request := testClient.ListInstances(testCtx, testProjectId) | ||
| 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, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "with limit flag", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues["limit"] = strconv.Itoa(testLimit) | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Limit = utils.Ptr(int64(testLimit)) | ||
| }), | ||
| }, | ||
| { | ||
| description: "with limit flag == 0", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues["limit"] = strconv.Itoa(0) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "with limit flag < 0", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues["limit"] = strconv.Itoa(-1) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| p := print.NewPrinter() | ||
| cmd := NewCmd(¶ms.CmdParams{Printer: 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 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 *inputModel | ||
| expectedRequest git.ApiListInstancesRequest | ||
| }{ | ||
| { | ||
| 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) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| type args struct { | ||
| outputFormat string | ||
| instances []git.Instance | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set empty instances slice", | ||
| args: args{ | ||
| instances: []git.Instance{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set empty instances in instances slice", | ||
| args: args{ | ||
| instances: []git.Instance{{}}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.instances); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
| "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/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/git/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| Limit *int64 | ||
| } | ||
| const limitFlag = "limit" | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "list", | ||
| Short: "Lists all instances of STACKIT Git.", | ||
| Long: "Lists all instances of STACKIT Git for the current project.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List all STACKIT Git instances`, | ||
| "$ stackit git instance list"), | ||
| examples.NewExample( | ||
| "Lists up to 10 STACKIT Git instances", | ||
| "$ stackit git instance list --limit=10", | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, _ []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get STACKIT Git instances: %w", err) | ||
| } | ||
| instances := *resp.Instances | ||
| if len(instances) == 0 { | ||
| projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| projectLabel = model.ProjectId | ||
| } | ||
| params.Printer.Info("No instances found for project %q\n", projectLabel) | ||
| return nil | ||
| } else if model.Limit != nil && len(instances) > int(*model.Limit) { | ||
| instances = (instances)[:*model.Limit] | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, instances) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Int64(limitFlag, 0, "Limit the output to the first n elements") | ||
| } | ||
| 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, | ||
| } | ||
| 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 *git.APIClient) git.ApiListInstancesRequest { | ||
| return apiClient.ListInstances(ctx, model.ProjectId) | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, instances []git.Instance) error { | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(instances, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal Observability instance list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(instances, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal Observability instance list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| return nil | ||
| default: | ||
| table := tables.NewTable() | ||
| table.SetHeader("ID", "NAME", "URL", "VERSION", "STATE", "CREATED") | ||
| for i := range instances { | ||
| instance := (instances)[i] | ||
| table.AddRow( | ||
| utils.PtrString(instance.Id), | ||
| utils.PtrString(instance.Name), | ||
| utils.PtrString(instance.Url), | ||
| utils.PtrString(instance.Version), | ||
| utils.PtrString(instance.State), | ||
| utils.PtrString(instance.Created), | ||
| ) | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| } | ||
| } |
Sorry, the diff of this file is too big to display