mygithub.libinneed.workers.dev/stackitcloud/stackit-cli
Advanced tools
| ## stackit organization describe | ||
| Show an organization | ||
| ### Synopsis | ||
| Show an organization. | ||
| ``` | ||
| stackit organization describe [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Describe the organization with the organization uuid "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" | ||
| $ stackit organization describe xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ||
| Describe the organization with the container id "foo-bar-organization" | ||
| $ stackit organization describe foo-bar-organization | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit organization 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 organization](./stackit_organization.md) - Manages organizations | ||
| ## stackit organization list | ||
| Lists all organizations | ||
| ### Synopsis | ||
| Lists all organizations. | ||
| ``` | ||
| stackit organization list [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Lists organizations for your user | ||
| $ stackit organization list | ||
| Lists the first 10 organizations | ||
| $ stackit organization list --limit 10 | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit organization list" | ||
| --limit int Maximum number of entries to list (default 50) | ||
| ``` | ||
| ### 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 organization](./stackit_organization.md) - Manages organizations | ||
| ## stackit ske options availability-zones | ||
| Lists SKE provider options for availability-zones | ||
| ### Synopsis | ||
| Lists STACKIT Kubernetes Engine (SKE) provider options for availability-zones. | ||
| ``` | ||
| stackit ske options availability-zones [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List SKE options for availability-zones | ||
| $ stackit ske options availability-zones | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit ske options availability-zones" | ||
| ``` | ||
| ### 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 options](./stackit_ske_options.md) - Lists SKE provider options | ||
| ## stackit ske options kubernetes-versions | ||
| Lists SKE provider options for kubernetes-versions | ||
| ### Synopsis | ||
| Lists STACKIT Kubernetes Engine (SKE) provider options for kubernetes-versions. | ||
| ``` | ||
| stackit ske options kubernetes-versions [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List SKE options for kubernetes-versions | ||
| $ stackit ske options kubernetes-versions | ||
| List SKE options for supported kubernetes-versions | ||
| $ stackit ske options kubernetes-versions --supported | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit ske options kubernetes-versions" | ||
| --supported List supported versions only | ||
| ``` | ||
| ### 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 options](./stackit_ske_options.md) - Lists SKE provider options | ||
| ## stackit ske options machine-images | ||
| Lists SKE provider options for machine-images | ||
| ### Synopsis | ||
| Lists STACKIT Kubernetes Engine (SKE) provider options for machine-images. | ||
| ``` | ||
| stackit ske options machine-images [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List SKE options for machine-images | ||
| $ stackit ske options machine-images | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit ske options machine-images" | ||
| ``` | ||
| ### 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 options](./stackit_ske_options.md) - Lists SKE provider options | ||
| ## stackit ske options machine-types | ||
| Lists SKE provider options for machine-types | ||
| ### Synopsis | ||
| Lists STACKIT Kubernetes Engine (SKE) provider options for machine-types. | ||
| ``` | ||
| stackit ske options machine-types [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List SKE options for machine-types | ||
| $ stackit ske options machine-types | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit ske options machine-types" | ||
| ``` | ||
| ### 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 options](./stackit_ske_options.md) - Lists SKE provider options | ||
| ## stackit ske options volume-types | ||
| Lists SKE provider options for volume-types | ||
| ### Synopsis | ||
| Lists STACKIT Kubernetes Engine (SKE) provider options for volume-types. | ||
| ``` | ||
| stackit ske options volume-types [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List SKE options for volume-types | ||
| $ stackit ske options volume-types | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit ske options volume-types" | ||
| ``` | ||
| ### 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 options](./stackit_ske_options.md) - Lists SKE provider options | ||
| package describe | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "time" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/resourcemanager" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &resourcemanager.APIClient{} | ||
| var ( | ||
| testOrganizationId = uuid.NewString() | ||
| ) | ||
| func fixtureArgValues(mods ...func(argValues []string)) []string { | ||
| argValues := []string{ | ||
| testOrganizationId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| OrganizationId: testOrganizationId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *resourcemanager.ApiGetOrganizationRequest)) resourcemanager.ApiGetOrganizationRequest { | ||
| request := testClient.GetOrganization(testCtx, testOrganizationId) | ||
| 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(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "uuid as example for an organization id", | ||
| argValues: []string{"12345678-90ab-cdef-1234-1234567890ab"}, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.OrganizationId = "12345678-90ab-cdef-1234-1234567890ab" | ||
| }), | ||
| }, | ||
| { | ||
| description: "non uuid string as example for a container id", | ||
| argValues: []string{"foo-bar-organization"}, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.OrganizationId = "foo-bar-organization" | ||
| }), | ||
| }, | ||
| { | ||
| description: "no args", | ||
| argValues: []string{}, | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest resourcemanager.ApiGetOrganizationRequest | ||
| }{ | ||
| { | ||
| 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 | ||
| organization *resourcemanager.OrganizationResponse | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "nil pointer as organization", | ||
| args: args{ | ||
| organization: nil, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty organization", | ||
| args: args{ | ||
| organization: utils.Ptr(resourcemanager.OrganizationResponse{}), | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "full response", | ||
| args: args{ | ||
| organization: utils.Ptr(resourcemanager.OrganizationResponse{ | ||
| OrganizationId: utils.Ptr(uuid.NewString()), | ||
| Name: utils.Ptr("foo bar"), | ||
| LifecycleState: utils.Ptr(resourcemanager.LIFECYCLESTATE_ACTIVE), | ||
| ContainerId: utils.Ptr("foo-bar-organization"), | ||
| CreationTime: utils.Ptr(time.Now()), | ||
| UpdateTime: utils.Ptr(time.Now()), | ||
| Labels: utils.Ptr(map[string]string{ | ||
| "foo": "true", | ||
| "bar": "false", | ||
| }), | ||
| }), | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(&types.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.organization); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "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/resourcemanager/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/resourcemanager" | ||
| ) | ||
| const ( | ||
| organizationIdArg = "ORGANIZATION_ID" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| OrganizationId string | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "describe", | ||
| Short: "Show an organization", | ||
| Long: "Show an organization.", | ||
| // the arg can be the organization uuid or the container id, which is not a uuid, so no validation needed | ||
| Args: args.SingleArg(organizationIdArg, nil), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Describe the organization with the organization uuid "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"`, | ||
| "$ stackit organization describe xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", | ||
| ), | ||
| examples.NewExample( | ||
| `Describe the organization with the container id "foo-bar-organization"`, | ||
| "$ stackit organization describe foo-bar-organization", | ||
| ), | ||
| ), | ||
| 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) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, resp) | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| organizationId := inputArgs[0] | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| OrganizationId: organizationId, | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *resourcemanager.APIClient) resourcemanager.ApiGetOrganizationRequest { | ||
| req := apiClient.GetOrganization(ctx, model.OrganizationId) | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, organization *resourcemanager.OrganizationResponse) error { | ||
| return p.OutputResult(outputFormat, organization, func() error { | ||
| if organization == nil { | ||
| p.Outputln("show organization: empty response") | ||
| return nil | ||
| } | ||
| table := tables.NewTable() | ||
| table.AddRow("ORGANIZATION ID", utils.PtrString(organization.OrganizationId)) | ||
| table.AddSeparator() | ||
| table.AddRow("NAME", utils.PtrString(organization.Name)) | ||
| table.AddSeparator() | ||
| table.AddRow("CONTAINER ID", utils.PtrString(organization.ContainerId)) | ||
| table.AddSeparator() | ||
| table.AddRow("STATUS", utils.PtrString(organization.LifecycleState)) | ||
| table.AddSeparator() | ||
| table.AddRow("CREATION TIME", utils.PtrString(organization.CreationTime)) | ||
| table.AddSeparator() | ||
| table.AddRow("UPDATE TIME", utils.PtrString(organization.UpdateTime)) | ||
| table.AddSeparator() | ||
| table.AddRow("LABELS", utils.JoinStringMap(utils.PtrValue(organization.Labels), ": ", ", ")) | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "strconv" | ||
| "testing" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/resourcemanager" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &resourcemanager.APIClient{} | ||
| const ( | ||
| testEmail = "foo@bar" | ||
| testLimit = 10 | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| limitFlag: strconv.Itoa(int(testLimit)), | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| Limit: utils.Ptr(int64(testLimit)), | ||
| Member: testEmail, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *resourcemanager.ApiListOrganizationsRequest)) resourcemanager.ApiListOrganizationsRequest { | ||
| request := testClient.ListOrganizations(testCtx) | ||
| request = request.Limit(testLimit) | ||
| request = request.Member(testEmail) | ||
| 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", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| // model.Member is set by the Run function afterwards | ||
| model.Member = "" | ||
| }), | ||
| }, | ||
| { | ||
| description: "no limit", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, limitFlag) | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| // model.Member is set by the Run function afterwards | ||
| model.Member = "" | ||
| model.Limit = nil | ||
| }), | ||
| }, | ||
| { | ||
| description: "limit invalid", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[limitFlag] = "invalid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "limit invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[limitFlag] = "0" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest resourcemanager.ApiListOrganizationsRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| { | ||
| description: "empty input model", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.Member = "" | ||
| model.Limit = nil | ||
| }), | ||
| expectedRequest: testClient.ListOrganizations(testCtx).Member(""), | ||
| }, | ||
| } | ||
| 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 | ||
| organizations []resourcemanager.ListOrganizationsResponseItemsInner | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty organizations slice", | ||
| args: args{ | ||
| organizations: []resourcemanager.ListOrganizationsResponseItemsInner{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty organization in organizations slice", | ||
| args: args{ | ||
| organizations: []resourcemanager.ListOrganizationsResponseItemsInner{{}}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(&types.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.organizations); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/auth" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/config" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "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/resourcemanager/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/resourcemanager" | ||
| ) | ||
| const ( | ||
| limitFlag = "limit" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| Limit *int64 | ||
| Member string | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "list", | ||
| Short: "Lists all organizations", | ||
| Long: "Lists all organizations.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Lists organizations for your user`, | ||
| "$ stackit organization list", | ||
| ), | ||
| examples.NewExample( | ||
| `Lists the first 10 organizations`, | ||
| "$ stackit organization list --limit 10", | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| activeProfile, err := config.GetProfile() | ||
| if err != nil { | ||
| return fmt.Errorf("get profile: %w", err) | ||
| } | ||
| model.Member = auth.GetProfileEmail(activeProfile) | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if resp == nil { | ||
| return fmt.Errorf("list organizations: empty response") | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, utils.PtrValue(resp.Items)) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list (default 50)") | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| limit := flags.FlagToInt64Pointer(p, cmd, limitFlag) | ||
| if limit != nil && (*limit < 1 || *limit > 100) { | ||
| return nil, &errors.FlagValidationError{ | ||
| Flag: limitFlag, | ||
| Details: "must be between 0 and 100", | ||
| } | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| Limit: limit, | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *resourcemanager.APIClient) resourcemanager.ApiListOrganizationsRequest { | ||
| req := apiClient.ListOrganizations(ctx) | ||
| req = req.Member(model.Member) | ||
| if model.Limit != nil { | ||
| req = req.Limit(float32(*model.Limit)) | ||
| } | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, organizations []resourcemanager.ListOrganizationsResponseItemsInner) error { | ||
| return p.OutputResult(outputFormat, organizations, func() error { | ||
| if len(organizations) == 0 { | ||
| p.Outputln("No organizations found") | ||
| return nil | ||
| } | ||
| table := tables.NewTable() | ||
| table.SetHeader("ID", "NAME", "CONTAINER ID") | ||
| for _, organization := range organizations { | ||
| table.AddRow( | ||
| utils.PtrString(organization.OrganizationId), | ||
| utils.PtrString(organization.Name), | ||
| utils.PtrString(organization.ContainerId), | ||
| ) | ||
| table.AddSeparator() | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package availability_zones | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &ske.APIClient{} | ||
| const testRegion = "eu01" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{ | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Region = "" | ||
| }), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| inputModel *inputModel | ||
| expectedRequest ske.ApiListProviderOptionsRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| inputModel: fixtureInputModel(), | ||
| expectedRequest: testClient.ListProviderOptions(testCtx, testRegion), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildRequest(testCtx, testClient, tt.inputModel) | ||
| 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 { | ||
| model *inputModel | ||
| options *ske.ProviderOptions | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "missing options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| }, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty input model", | ||
| args: args{ | ||
| model: &inputModel{}, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set model and options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| AvailabilityZones: &[]ske.AvailabilityZone{}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty value in values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| AvailabilityZones: &[]ske.AvailabilityZone{{}}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "valid values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| AvailabilityZones: &[]ske.AvailabilityZone{ | ||
| { | ||
| Name: utils.Ptr("zone1"), | ||
| }, | ||
| { | ||
| Name: utils.Ptr("zone2"), | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(&types.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.options); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package availability_zones | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "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/tables" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type inputModel struct { | ||
| globalflags.GlobalFlagModel | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "availability-zones", | ||
| Short: "Lists SKE provider options for availability-zones", | ||
| Long: "Lists STACKIT Kubernetes Engine (SKE) provider options for availability-zones.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List SKE options for availability-zones`, | ||
| "$ stackit ske options availability-zones"), | ||
| ), | ||
| 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, apiClient, model) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get SKE provider options: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model, resp) | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| model := inputModel{ | ||
| GlobalFlagModel: utils.PtrValue(globalFlags), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, apiClient *ske.APIClient, model *inputModel) ske.ApiListProviderOptionsRequest { | ||
| req := apiClient.ListProviderOptions(ctx, model.Region) | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, model *inputModel, options *ske.ProviderOptions) error { | ||
| if options == nil { | ||
| return fmt.Errorf("options is nil") | ||
| } | ||
| options.KubernetesVersions = nil | ||
| options.MachineImages = nil | ||
| options.MachineTypes = nil | ||
| options.VolumeTypes = nil | ||
| return p.OutputResult(model.OutputFormat, options, func() error { | ||
| zones := utils.PtrValue(options.AvailabilityZones) | ||
| table := tables.NewTable() | ||
| table.SetHeader("ZONE") | ||
| for i := range zones { | ||
| z := zones[i] | ||
| table.AddRow(utils.PtrValue(z.Name)) | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("display output: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package kubernetes_versions | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "time" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &ske.APIClient{} | ||
| const testRegion = "eu01" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.RegionFlag: testRegion, | ||
| supportedFlag: "false", | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{ | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| Supported: false, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Region = "" | ||
| }), | ||
| }, | ||
| { | ||
| description: "supported only", | ||
| flagValues: map[string]string{ | ||
| supportedFlag: "true", | ||
| }, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Supported = true | ||
| model.Region = "" | ||
| }), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| inputModel *inputModel | ||
| expectedRequest ske.ApiListProviderOptionsRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| inputModel: fixtureInputModel(), | ||
| expectedRequest: testClient.ListProviderOptions(testCtx, testRegion), | ||
| }, | ||
| { | ||
| description: "base", | ||
| inputModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Supported = true | ||
| }), | ||
| expectedRequest: testClient.ListProviderOptions(testCtx, testRegion).VersionState("SUPPORTED"), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildRequest(testCtx, testClient, tt.inputModel) | ||
| 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 { | ||
| model *inputModel | ||
| options *ske.ProviderOptions | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "missing options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| }, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty input model", | ||
| args: args{ | ||
| model: &inputModel{}, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set model and options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| KubernetesVersions: &[]ske.KubernetesVersion{}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty value in values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| KubernetesVersions: &[]ske.KubernetesVersion{{}}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "valid values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| KubernetesVersions: &[]ske.KubernetesVersion{ | ||
| { | ||
| FeatureGates: &map[string]string{ | ||
| "featureGate1": "foo", | ||
| "featureGate2": "bar", | ||
| }, | ||
| State: utils.Ptr("supported"), | ||
| Version: utils.Ptr("0.00.0"), | ||
| }, | ||
| { | ||
| ExpirationDate: utils.Ptr(time.Now()), | ||
| State: utils.Ptr("deprecated"), | ||
| Version: utils.Ptr("0.00.0"), | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(&types.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.options); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package kubernetes_versions | ||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "time" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "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/tables" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| const ( | ||
| supportedFlag = "supported" | ||
| ) | ||
| type inputModel struct { | ||
| globalflags.GlobalFlagModel | ||
| Supported bool | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "kubernetes-versions", | ||
| Short: "Lists SKE provider options for kubernetes-versions", | ||
| Long: "Lists STACKIT Kubernetes Engine (SKE) provider options for kubernetes-versions.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List SKE options for kubernetes-versions`, | ||
| "$ stackit ske options kubernetes-versions"), | ||
| examples.NewExample( | ||
| `List SKE options for supported kubernetes-versions`, | ||
| "$ stackit ske options kubernetes-versions --supported"), | ||
| ), | ||
| 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, apiClient, model) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get SKE provider options: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model, resp) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Bool(supportedFlag, false, "List supported versions only") | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| model := inputModel{ | ||
| GlobalFlagModel: utils.PtrValue(globalFlags), | ||
| Supported: flags.FlagToBoolValue(p, cmd, supportedFlag), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, apiClient *ske.APIClient, model *inputModel) ske.ApiListProviderOptionsRequest { | ||
| req := apiClient.ListProviderOptions(ctx, model.Region) | ||
| if model.Supported { | ||
| req = req.VersionState("SUPPORTED") | ||
| } | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, model *inputModel, options *ske.ProviderOptions) error { | ||
| if options == nil { | ||
| return fmt.Errorf("options is nil") | ||
| } | ||
| options.AvailabilityZones = nil | ||
| options.MachineImages = nil | ||
| options.MachineTypes = nil | ||
| options.VolumeTypes = nil | ||
| return p.OutputResult(model.OutputFormat, options, func() error { | ||
| versions := utils.PtrValue(options.KubernetesVersions) | ||
| table := tables.NewTable() | ||
| table.SetHeader("VERSION", "STATE", "EXPIRATION DATE", "FEATURE GATES") | ||
| for i := range versions { | ||
| v := versions[i] | ||
| featureGate, err := json.Marshal(utils.PtrValue(v.FeatureGates)) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal featureGates of Kubernetes version %q: %w", utils.PtrValue(v.Version), err) | ||
| } | ||
| expirationDate := "" | ||
| if v.ExpirationDate != nil { | ||
| expirationDate = v.ExpirationDate.Format(time.RFC3339) | ||
| } | ||
| table.AddRow( | ||
| utils.PtrString(v.Version), | ||
| utils.PtrString(v.State), | ||
| expirationDate, | ||
| string(featureGate)) | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("display output: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package machine_images | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "time" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &ske.APIClient{} | ||
| const testRegion = "eu01" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{ | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Region = "" | ||
| }), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| inputModel *inputModel | ||
| expectedRequest ske.ApiListProviderOptionsRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| inputModel: fixtureInputModel(), | ||
| expectedRequest: testClient.ListProviderOptions(testCtx, testRegion), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildRequest(testCtx, testClient, tt.inputModel) | ||
| 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 { | ||
| model *inputModel | ||
| options *ske.ProviderOptions | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "missing options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| }, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty input model", | ||
| args: args{ | ||
| model: &inputModel{}, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set model and options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| MachineImages: &[]ske.MachineImage{}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty value in values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| MachineImages: &[]ske.MachineImage{{}}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "valid values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| MachineImages: &[]ske.MachineImage{ | ||
| { | ||
| Name: utils.Ptr("image1"), | ||
| Versions: &[]ske.MachineImageVersion{ | ||
| { | ||
| Cri: &[]ske.CRI{ | ||
| { | ||
| Name: ske.CRINAME_CONTAINERD.Ptr(), | ||
| }, | ||
| }, | ||
| ExpirationDate: utils.Ptr(time.Now()), | ||
| State: utils.Ptr("supported"), | ||
| Version: utils.Ptr("0.00.0"), | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| Name: utils.Ptr("zone2"), | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(&types.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.options); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package machine_images | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "strings" | ||
| "time" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "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/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type inputModel struct { | ||
| globalflags.GlobalFlagModel | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "machine-images", | ||
| Short: "Lists SKE provider options for machine-images", | ||
| Long: "Lists STACKIT Kubernetes Engine (SKE) provider options for machine-images.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List SKE options for machine-images`, | ||
| "$ stackit ske options machine-images"), | ||
| ), | ||
| 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, apiClient, model) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get SKE provider options: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model, resp) | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| model := inputModel{ | ||
| GlobalFlagModel: utils.PtrValue(globalFlags), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, apiClient *ske.APIClient, model *inputModel) ske.ApiListProviderOptionsRequest { | ||
| req := apiClient.ListProviderOptions(ctx, model.Region) | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, model *inputModel, options *ske.ProviderOptions) error { | ||
| if options == nil { | ||
| return fmt.Errorf("options is nil") | ||
| } | ||
| options.AvailabilityZones = nil | ||
| options.KubernetesVersions = nil | ||
| options.MachineTypes = nil | ||
| options.VolumeTypes = nil | ||
| return p.OutputResult(model.OutputFormat, options, func() error { | ||
| images := utils.PtrValue(options.MachineImages) | ||
| table := tables.NewTable() | ||
| table.SetHeader("NAME", "VERSION", "STATE", "EXPIRATION DATE", "SUPPORTED CRI") | ||
| for i := range images { | ||
| image := images[i] | ||
| versions := utils.PtrValue(image.Versions) | ||
| for j := range versions { | ||
| version := versions[j] | ||
| criNames := make([]string, 0) | ||
| for i := range utils.PtrValue(version.Cri) { | ||
| cri := utils.PtrValue(version.Cri)[i] | ||
| criNames = append(criNames, utils.PtrString(cri.Name)) | ||
| } | ||
| criNamesString := strings.Join(criNames, ", ") | ||
| expirationDate := "-" | ||
| if version.ExpirationDate != nil { | ||
| expirationDate = version.ExpirationDate.Format(time.RFC3339) | ||
| } | ||
| table.AddRow( | ||
| utils.PtrString(image.Name), | ||
| utils.PtrString(version.Version), | ||
| utils.PtrString(version.State), | ||
| expirationDate, | ||
| criNamesString, | ||
| ) | ||
| } | ||
| } | ||
| table.EnableAutoMergeOnColumns(1) | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("display output: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package machine_types | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &ske.APIClient{} | ||
| const testRegion = "eu01" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{ | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Region = "" | ||
| }), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| inputModel *inputModel | ||
| expectedRequest ske.ApiListProviderOptionsRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| inputModel: fixtureInputModel(), | ||
| expectedRequest: testClient.ListProviderOptions(testCtx, testRegion), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildRequest(testCtx, testClient, tt.inputModel) | ||
| 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 { | ||
| model *inputModel | ||
| options *ske.ProviderOptions | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "missing options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| }, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty input model", | ||
| args: args{ | ||
| model: &inputModel{}, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set model and options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| MachineTypes: &[]ske.MachineType{}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty value in values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| MachineTypes: &[]ske.MachineType{{}}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "valid values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| MachineTypes: &[]ske.MachineType{ | ||
| { | ||
| Architecture: utils.Ptr("amd64"), | ||
| Cpu: utils.Ptr(int64(2)), | ||
| Gpu: utils.Ptr(int64(0)), | ||
| Memory: utils.Ptr(int64(16)), | ||
| Name: utils.Ptr("type1"), | ||
| }, | ||
| { | ||
| Architecture: utils.Ptr("amd64"), | ||
| Cpu: utils.Ptr(int64(2)), | ||
| Gpu: utils.Ptr(int64(0)), | ||
| Memory: utils.Ptr(int64(16)), | ||
| Name: utils.Ptr("type2"), | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(&types.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.options); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package machine_types | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "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/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type inputModel struct { | ||
| globalflags.GlobalFlagModel | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "machine-types", | ||
| Short: "Lists SKE provider options for machine-types", | ||
| Long: "Lists STACKIT Kubernetes Engine (SKE) provider options for machine-types.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List SKE options for machine-types`, | ||
| "$ stackit ske options machine-types"), | ||
| ), | ||
| 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, apiClient, model) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get SKE provider options: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model, resp) | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| model := inputModel{ | ||
| GlobalFlagModel: utils.PtrValue(globalFlags), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, apiClient *ske.APIClient, model *inputModel) ske.ApiListProviderOptionsRequest { | ||
| req := apiClient.ListProviderOptions(ctx, model.Region) | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, model *inputModel, options *ske.ProviderOptions) error { | ||
| if options == nil { | ||
| return fmt.Errorf("options is nil") | ||
| } | ||
| options.AvailabilityZones = nil | ||
| options.KubernetesVersions = nil | ||
| options.MachineImages = nil | ||
| options.VolumeTypes = nil | ||
| return p.OutputResult(model.OutputFormat, options, func() error { | ||
| machineTypes := utils.PtrValue(options.MachineTypes) | ||
| table := tables.NewTable() | ||
| table.SetHeader("TYPE", "CPU", "MEMORY") | ||
| for i := range machineTypes { | ||
| t := machineTypes[i] | ||
| table.AddRow( | ||
| utils.PtrString(t.Name), | ||
| utils.PtrString(t.Cpu), | ||
| utils.PtrString(t.Memory), | ||
| ) | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("display output: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package volume_types | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &ske.APIClient{} | ||
| const testRegion = "eu01" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{ | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Region = "" | ||
| }), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| inputModel *inputModel | ||
| expectedRequest ske.ApiListProviderOptionsRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| inputModel: fixtureInputModel(), | ||
| expectedRequest: testClient.ListProviderOptions(testCtx, testRegion), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildRequest(testCtx, testClient, tt.inputModel) | ||
| 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 { | ||
| model *inputModel | ||
| options *ske.ProviderOptions | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "missing options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| }, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty input model", | ||
| args: args{ | ||
| model: &inputModel{}, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "set model and options", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| VolumeTypes: &[]ske.VolumeType{}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty value in values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| VolumeTypes: &[]ske.VolumeType{{}}, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "valid values", | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
| options: &ske.ProviderOptions{ | ||
| VolumeTypes: &[]ske.VolumeType{ | ||
| { | ||
| Name: utils.Ptr("type1"), | ||
| }, | ||
| { | ||
| Name: utils.Ptr("type2"), | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(&types.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.options); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package volume_types | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "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/tables" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/ske" | ||
| ) | ||
| type inputModel struct { | ||
| globalflags.GlobalFlagModel | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "volume-types", | ||
| Short: "Lists SKE provider options for volume-types", | ||
| Long: "Lists STACKIT Kubernetes Engine (SKE) provider options for volume-types.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List SKE options for volume-types`, | ||
| "$ stackit ske options volume-types"), | ||
| ), | ||
| 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, apiClient, model) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get SKE provider options: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model, resp) | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| model := inputModel{ | ||
| GlobalFlagModel: utils.PtrValue(globalFlags), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, apiClient *ske.APIClient, model *inputModel) ske.ApiListProviderOptionsRequest { | ||
| req := apiClient.ListProviderOptions(ctx, model.Region) | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, model *inputModel, options *ske.ProviderOptions) error { | ||
| if options == nil { | ||
| return fmt.Errorf("options is nil") | ||
| } | ||
| options.AvailabilityZones = nil | ||
| options.KubernetesVersions = nil | ||
| options.MachineImages = nil | ||
| options.MachineTypes = nil | ||
| return p.OutputResult(model.OutputFormat, options, func() error { | ||
| volumeTypes := utils.PtrValue(options.VolumeTypes) | ||
| table := tables.NewTable() | ||
| table.SetHeader("TYPE") | ||
| for i := range volumeTypes { | ||
| z := volumeTypes[i] | ||
| table.AddRow(utils.PtrString(z.Name)) | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("display output: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
@@ -53,3 +53,3 @@ name: CI | ||
| - name: Check GoReleaser | ||
| uses: goreleaser/goreleaser-action@v6 | ||
| uses: goreleaser/goreleaser-action@v7 | ||
| with: | ||
@@ -56,0 +56,0 @@ args: check |
@@ -77,3 +77,3 @@ # STACKIT CLI release workflow. | ||
| - name: Run GoReleaser | ||
| uses: goreleaser/goreleaser-action@v6 | ||
| uses: goreleaser/goreleaser-action@v7 | ||
| with: | ||
@@ -80,0 +80,0 @@ args: release --clean |
+18
-19
@@ -140,21 +140,20 @@ version: 2 | ||
| # Temporary disable snap release, see https://jira.schwarz/browse/STACKITCLI-330 | ||
| # snapcrafts: | ||
| # # IDs of the builds for which to create packages for | ||
| # - ids: | ||
| # - linux-builds | ||
| # # The name of the snap | ||
| # name: stackit | ||
| # # The canonical title of the application, displayed in the software | ||
| # # centre graphical frontends | ||
| # title: STACKIT CLI | ||
| # summary: A command-line interface to manage STACKIT resources. | ||
| # description: "A command-line interface to manage STACKIT resources." | ||
| # license: Apache-2.0 | ||
| # confinement: classic | ||
| # # Grade "devel" will only release to `edge` and `beta` channels | ||
| # # Grade "stable" will also release to the `candidate` and `stable` channels | ||
| # grade: stable | ||
| # # Whether to publish the Snap to the store | ||
| # publish: true | ||
| snapcrafts: | ||
| # IDs of the builds for which to create packages for | ||
| - ids: | ||
| - linux-builds | ||
| # The name of the snap | ||
| name: stackit | ||
| # The canonical title of the application, displayed in the software | ||
| # centre graphical frontends | ||
| title: STACKIT CLI | ||
| summary: A command-line interface to manage STACKIT resources. | ||
| description: "A command-line interface to manage STACKIT resources." | ||
| license: Apache-2.0 | ||
| confinement: classic | ||
| # Grade "devel" will only release to `edge` and `beta` channels | ||
| # Grade "stable" will also release to the `candidate` and `stable` channels | ||
| grade: stable | ||
| # Whether to publish the Snap to the store | ||
| publish: true | ||
@@ -161,0 +160,0 @@ winget: |
@@ -34,4 +34,6 @@ ## stackit organization | ||
| * [stackit](./stackit.md) - Manage STACKIT resources using the command line | ||
| * [stackit organization describe](./stackit_organization_describe.md) - Show an organization | ||
| * [stackit organization list](./stackit_organization_list.md) - Lists all organizations | ||
| * [stackit organization member](./stackit_organization_member.md) - Manages organization members | ||
| * [stackit organization role](./stackit_organization_role.md) - Manages organization roles | ||
@@ -7,2 +7,3 @@ ## stackit ske options | ||
| Command "options" is deprecated, use the subcommands instead. | ||
| Lists STACKIT Kubernetes Engine (SKE) provider options (availability zones, Kubernetes versions, machine images and types, volume types). | ||
@@ -15,24 +16,6 @@ Pass one or more flags to filter what categories are shown. | ||
| ### Examples | ||
| ``` | ||
| List SKE options for all categories | ||
| $ stackit ske options | ||
| List SKE options regarding Kubernetes versions only | ||
| $ stackit ske options --kubernetes-versions | ||
| List SKE options regarding Kubernetes versions and machine images | ||
| $ stackit ske options --kubernetes-versions --machine-images | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| --availability-zones Lists availability zones | ||
| -h, --help Help for "stackit ske options" | ||
| --kubernetes-versions Lists supported kubernetes versions | ||
| --machine-images Lists supported machine images | ||
| --machine-types Lists supported machine types | ||
| --volume-types Lists supported volume types | ||
| -h, --help Help for "stackit ske options" | ||
| ``` | ||
@@ -54,2 +37,7 @@ | ||
| * [stackit ske](./stackit_ske.md) - Provides functionality for SKE | ||
| * [stackit ske options availability-zones](./stackit_ske_options_availability-zones.md) - Lists SKE provider options for availability-zones | ||
| * [stackit ske options kubernetes-versions](./stackit_ske_options_kubernetes-versions.md) - Lists SKE provider options for kubernetes-versions | ||
| * [stackit ske options machine-images](./stackit_ske_options_machine-images.md) - Lists SKE provider options for machine-images | ||
| * [stackit ske options machine-types](./stackit_ske_options_machine-types.md) - Lists SKE provider options for machine-types | ||
| * [stackit ske options volume-types](./stackit_ske_options_volume-types.md) - Lists SKE provider options for volume-types | ||
+19
-19
| module github.com/stackitcloud/stackit-cli | ||
| go 1.24.0 | ||
| go 1.25.0 | ||
@@ -18,6 +18,6 @@ require ( | ||
| github.com/spf13/viper v1.21.0 | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.21.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/alb v0.9.3 | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.22.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/alb v0.10.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/authorization v0.12.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/cdn v1.9.4 | ||
| github.com/stackitcloud/stackit-sdk-go/services/cdn v1.10.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.6 | ||
@@ -37,3 +37,3 @@ github.com/stackitcloud/stackit-sdk-go/services/edge v0.4.3 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.2.6 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.11.6 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.12.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.7 | ||
@@ -43,8 +43,8 @@ github.com/stackitcloud/stackit-sdk-go/services/ske v1.7.0 | ||
| github.com/zalando/go-keyring v0.2.6 | ||
| golang.org/x/mod v0.32.0 | ||
| golang.org/x/mod v0.33.0 | ||
| golang.org/x/oauth2 v0.35.0 | ||
| golang.org/x/term v0.39.0 | ||
| golang.org/x/text v0.33.0 | ||
| k8s.io/apimachinery v0.34.2 | ||
| k8s.io/client-go v0.34.2 | ||
| golang.org/x/term v0.40.0 | ||
| golang.org/x/text v0.34.0 | ||
| k8s.io/apimachinery v0.35.2 | ||
| k8s.io/client-go v0.35.1 | ||
| ) | ||
@@ -237,3 +237,3 @@ | ||
| go.uber.org/zap v1.27.0 // indirect | ||
| go.yaml.in/yaml/v2 v2.4.2 // indirect | ||
| go.yaml.in/yaml/v2 v2.4.3 // indirect | ||
| go.yaml.in/yaml/v3 v3.0.4 // indirect | ||
@@ -246,2 +246,3 @@ golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 // indirect | ||
| honnef.co/go/tools v0.6.1 // indirect | ||
| k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect | ||
| mvdan.cc/gofumpt v0.9.2 // indirect | ||
@@ -260,3 +261,2 @@ mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect | ||
| github.com/godbus/dbus/v5 v5.1.0 // indirect | ||
| github.com/gogo/protobuf v1.3.2 // indirect | ||
| github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||
@@ -275,17 +275,17 @@ github.com/json-iterator/go v1.1.12 // indirect | ||
| github.com/stackitcloud/stackit-sdk-go/services/kms v1.3.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.7.3 | ||
| github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.8.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.6 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.6 | ||
| github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.4.5 | ||
| github.com/stackitcloud/stackit-sdk-go/services/observability v0.16.3 | ||
| github.com/stackitcloud/stackit-sdk-go/services/observability v0.17.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.26.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/redis v0.25.6 | ||
| github.com/stackitcloud/stackit-sdk-go/services/sfs v0.3.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/sfs v0.4.0 | ||
| github.com/subosito/gotenv v1.6.0 // indirect | ||
| golang.org/x/sys v0.40.0 // indirect | ||
| golang.org/x/sys v0.41.0 // indirect | ||
| gopkg.in/yaml.v3 v3.0.1 // indirect | ||
| k8s.io/api v0.34.2 // indirect | ||
| k8s.io/api v0.35.1 // indirect | ||
| k8s.io/klog/v2 v2.130.1 // indirect | ||
| k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 | ||
| sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect | ||
| k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 | ||
| sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect | ||
| sigs.k8s.io/yaml v1.6.0 // indirect | ||
@@ -292,0 +292,0 @@ ) |
+16
-1
@@ -191,4 +191,19 @@ # Installation | ||
| > We are currently working on distributing the CLI on a package manager for Windows. For the moment, please refer to one of the installation methods below. | ||
| #### Scoop | ||
| The STACKIT CLI can be installed through the [Scoop](https://scoop.sh/) package manager. | ||
| 1. Install Scoop (if not already installed): | ||
| ```powershell | ||
| Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser | ||
| Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression | ||
| ``` | ||
| 2. Install the CLI: | ||
| ```powershell | ||
| scoop install stackit | ||
| ``` | ||
| ## Manual installation | ||
@@ -195,0 +210,0 @@ |
@@ -8,2 +8,3 @@ package describe | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
@@ -229,2 +230,29 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| }, | ||
| { | ||
| name: "valid value", | ||
| args: args{ | ||
| resp: &iaas.Image{ | ||
| Id: utils.Ptr(uuid.NewString()), | ||
| Name: utils.Ptr("Image"), | ||
| Status: utils.Ptr("STATUS"), | ||
| DiskFormat: utils.Ptr("format"), | ||
| MinDiskSize: utils.Ptr(int64(0)), | ||
| MinRam: utils.Ptr(int64(0)), | ||
| Config: &iaas.ImageConfig{ | ||
| Architecture: utils.Ptr("architecture"), | ||
| OperatingSystem: utils.Ptr("os"), | ||
| OperatingSystemDistro: iaas.NewNullableString(utils.Ptr("os distro")), | ||
| OperatingSystemVersion: iaas.NewNullableString(utils.Ptr("0.00.0")), | ||
| Uefi: utils.Ptr(true), | ||
| }, | ||
| Labels: utils.Ptr(map[string]any{ | ||
| "label1": true, | ||
| "label2": false, | ||
| "label3": 42, | ||
| "foo": "bar", | ||
| }), | ||
| }, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
@@ -231,0 +259,0 @@ p := print.NewPrinter() |
@@ -99,5 +99,4 @@ package describe | ||
| table.AddRow("ID", *id) | ||
| table.AddSeparator() | ||
| } | ||
| table.AddSeparator() | ||
| if name := resp.Name; name != nil { | ||
@@ -107,2 +106,6 @@ table.AddRow("NAME", *name) | ||
| } | ||
| if status := resp.Status; status != nil { | ||
| table.AddRow("STATUS", *status) | ||
| table.AddSeparator() | ||
| } | ||
| if format := resp.DiskFormat; format != nil { | ||
@@ -109,0 +112,0 @@ table.AddRow("FORMAT", *format) |
@@ -29,3 +29,3 @@ package create | ||
| var testPayload = &observability.CreateScrapeConfigPayload{ | ||
| BasicAuth: &observability.CreateScrapeConfigPayloadBasicAuth{ | ||
| BasicAuth: &observability.PartialUpdateScrapeConfigsRequestInnerBasicAuth{ | ||
| Username: utils.Ptr("username"), | ||
@@ -39,5 +39,5 @@ Password: utils.Ptr("password"), | ||
| JobName: utils.Ptr("default-name"), | ||
| MetricsRelabelConfigs: &[]observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInner{ | ||
| MetricsRelabelConfigs: &[]observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInner{ | ||
| { | ||
| Action: observability.CREATESCRAPECONFIGPAYLOADMETRICSRELABELCONFIGSINNERACTION_REPLACE.Ptr(), | ||
| Action: observability.PARTIALUPDATESCRAPECONFIGSREQUESTINNERMETRICSRELABELCONFIGSINNERACTION_REPLACE.Ptr(), | ||
| Modulus: utils.Ptr(1.0), | ||
@@ -59,3 +59,3 @@ Regex: utils.Ptr("regex"), | ||
| ScrapeTimeout: utils.Ptr("timeout"), | ||
| StaticConfigs: &[]observability.CreateScrapeConfigPayloadStaticConfigsInner{ | ||
| StaticConfigs: &[]observability.PartialUpdateScrapeConfigsRequestInnerStaticConfigsInner{ | ||
| { | ||
@@ -69,3 +69,3 @@ Labels: &map[string]interface{}{ | ||
| }, | ||
| TlsConfig: &observability.CreateScrapeConfigPayloadHttpSdConfigsInnerOauth2TlsConfig{ | ||
| TlsConfig: &observability.PartialUpdateScrapeConfigsRequestInnerHttpSdConfigsInnerOauth2TlsConfig{ | ||
| InsecureSkipVerify: utils.Ptr(true), | ||
@@ -72,0 +72,0 @@ }, |
@@ -27,3 +27,3 @@ package update | ||
| var testPayload = observability.UpdateScrapeConfigPayload{ | ||
| BasicAuth: &observability.CreateScrapeConfigPayloadBasicAuth{ | ||
| BasicAuth: &observability.PartialUpdateScrapeConfigsRequestInnerBasicAuth{ | ||
| Username: utils.Ptr("username"), | ||
@@ -36,5 +36,5 @@ Password: utils.Ptr("password"), | ||
| MetricsPath: utils.Ptr("/metrics"), | ||
| MetricsRelabelConfigs: &[]observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInner{ | ||
| MetricsRelabelConfigs: &[]observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInner{ | ||
| { | ||
| Action: observability.CREATESCRAPECONFIGPAYLOADMETRICSRELABELCONFIGSINNERACTION_REPLACE.Ptr(), | ||
| Action: observability.PARTIALUPDATESCRAPECONFIGSREQUESTINNERMETRICSRELABELCONFIGSINNERACTION_REPLACE.Ptr(), | ||
| Modulus: utils.Ptr(1.0), | ||
@@ -41,0 +41,0 @@ Regex: utils.Ptr("regex"), |
@@ -8,2 +8,4 @@ package organization | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/organization/describe" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/organization/list" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/organization/member" | ||
@@ -35,2 +37,4 @@ "github.com/stackitcloud/stackit-cli/internal/cmd/organization/role" | ||
| cmd.AddCommand(role.NewCmd(params)) | ||
| cmd.AddCommand(list.NewCmd(params)) | ||
| cmd.AddCommand(describe.NewCmd(params)) | ||
| } |
@@ -109,2 +109,12 @@ package create | ||
| { | ||
| description: "params flag missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, paramsFlag) | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Params = nil | ||
| }), | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
@@ -111,0 +121,0 @@ flagValues: fixtureFlagValues(func(flagValues map[string]string) { |
@@ -122,3 +122,5 @@ package create | ||
| } | ||
| parsedParams, err := runcommandUtils.ParseScriptParams(*model.Params) | ||
| var err error | ||
| model.Params, err = runcommandUtils.ParseScriptParams(model.Params) | ||
| if err != nil { | ||
@@ -130,3 +132,2 @@ return nil, &cliErr.FlagValidationError{ | ||
| } | ||
| model.Params = &parsedParams | ||
@@ -133,0 +134,0 @@ p.DebugInputModel(model) |
@@ -42,3 +42,3 @@ package options | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{Region: testRegion, Verbosity: globalflags.VerbosityDefault}, | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{Region: testRegion, Verbosity: globalflags.VerbosityDefault}, | ||
| AvailabilityZones: false, | ||
@@ -58,3 +58,3 @@ KubernetesVersions: false, | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{Region: testRegion, Verbosity: globalflags.VerbosityDefault}, | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{Region: testRegion, Verbosity: globalflags.VerbosityDefault}, | ||
| AvailabilityZones: true, | ||
@@ -192,3 +192,3 @@ KubernetesVersions: true, | ||
| model: &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{}, | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
@@ -199,3 +199,3 @@ }, | ||
| { | ||
| name: "missing global flags in model", | ||
| name: "empty input model", | ||
| args: args{ | ||
@@ -205,3 +205,3 @@ model: &inputModel{}, | ||
| }, | ||
| wantErr: true, | ||
| wantErr: false, | ||
| }, | ||
@@ -212,3 +212,3 @@ { | ||
| model: &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{}, | ||
| GlobalFlagModel: globalflags.GlobalFlagModel{}, | ||
| }, | ||
@@ -215,0 +215,0 @@ options: &ske.ProviderOptions{}, |
@@ -10,2 +10,7 @@ package options | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/options/availability_zones" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/options/kubernetes_versions" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/options/machine_images" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/options/machine_types" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/ske/options/volume_types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
@@ -15,3 +20,2 @@ | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
@@ -35,3 +39,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| globalflags.GlobalFlagModel | ||
| AvailabilityZones bool | ||
@@ -48,3 +52,4 @@ KubernetesVersions bool | ||
| Short: "Lists SKE provider options", | ||
| Long: fmt.Sprintf("%s\n%s", | ||
| Long: fmt.Sprintf("%s\n%s\n%s", | ||
| "Command \"options\" is deprecated, use the subcommands instead.", | ||
| "Lists STACKIT Kubernetes Engine (SKE) provider options (availability zones, Kubernetes versions, machine images and types, volume types).", | ||
@@ -54,14 +59,5 @@ "Pass one or more flags to filter what categories are shown.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List SKE options for all categories`, | ||
| "$ stackit ske options"), | ||
| examples.NewExample( | ||
| `List SKE options regarding Kubernetes versions only`, | ||
| "$ stackit ske options --kubernetes-versions"), | ||
| examples.NewExample( | ||
| `List SKE options regarding Kubernetes versions and machine images`, | ||
| "$ stackit ske options --kubernetes-versions --machine-images"), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| params.Printer.Info("Command \"options\" is deprecated, use the subcommands instead.\n") | ||
| ctx := context.Background() | ||
@@ -90,5 +86,14 @@ model, err := parseInput(params.Printer, cmd, args) | ||
| configureFlags(cmd) | ||
| addSubcommands(cmd, params) | ||
| return cmd | ||
| } | ||
| func addSubcommands(cmd *cobra.Command, params *types.CmdParams) { | ||
| cmd.AddCommand(availability_zones.NewCmd(params)) | ||
| cmd.AddCommand(kubernetes_versions.NewCmd(params)) | ||
| cmd.AddCommand(machine_images.NewCmd(params)) | ||
| cmd.AddCommand(machine_types.NewCmd(params)) | ||
| cmd.AddCommand(volume_types.NewCmd(params)) | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
@@ -100,2 +105,8 @@ cmd.Flags().Bool(availabilityZonesFlag, false, "Lists availability zones") | ||
| cmd.Flags().Bool(volumeTypesFlag, false, "Lists supported volume types") | ||
| cobra.CheckErr(cmd.Flags().MarkDeprecated(availabilityZonesFlag, "This flag is deprecated and will be removed on 2026-09-26. Use the availability-zone subcommand instead.")) | ||
| cobra.CheckErr(cmd.Flags().MarkDeprecated(kubernetesVersionsFlag, "This flag is deprecated and will be removed on 2026-09-26. Use the kubernetes-versions subcommand instead.")) | ||
| cobra.CheckErr(cmd.Flags().MarkDeprecated(machineImagesFlag, "This flag is deprecated and will be removed on 2026-09-26. Use the machine-images subcommand instead.")) | ||
| cobra.CheckErr(cmd.Flags().MarkDeprecated(machineTypesFlag, "This flag is deprecated and will be removed on 2026-09-26. Use the machine-types subcommand instead.")) | ||
| cobra.CheckErr(cmd.Flags().MarkDeprecated(volumeTypesFlag, "This flag is deprecated and will be removed on 2026-09-26. Use the volume-types subcommand instead.")) | ||
| } | ||
@@ -121,3 +132,3 @@ | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| GlobalFlagModel: utils.PtrValue(globalFlags), | ||
| AvailabilityZones: availabilityZones, | ||
@@ -140,3 +151,3 @@ KubernetesVersions: kubernetesVersions, | ||
| func outputResult(p *print.Printer, model *inputModel, options *ske.ProviderOptions) error { | ||
| if model == nil || model.GlobalFlagModel == nil { | ||
| if model == nil { | ||
| return fmt.Errorf("model is nil") | ||
@@ -143,0 +154,0 @@ } else if options == nil { |
@@ -10,2 +10,3 @@ package config | ||
| "github.com/spf13/viper" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
@@ -438,3 +439,9 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/fileutils" | ||
| err = fileutils.CopyFile(configFile, exportPath) | ||
| _, err = os.Stat(configFile) | ||
| if os.IsNotExist(err) { | ||
| // viper.SafeWriteConfigAs would not overwrite the target, so we use WriteConfigAs for the same behavior as CopyFile | ||
| err = viper.WriteConfigAs(exportPath) | ||
| } else { | ||
| err = fileutils.CopyFile(configFile, exportPath) | ||
| } | ||
| if err != nil { | ||
@@ -441,0 +448,0 @@ return fmt.Errorf("export config file to %q: %w", exportPath, err) |
@@ -92,3 +92,3 @@ package utils | ||
| payload := &observability.UpdateScrapeConfigPayload{ | ||
| BasicAuth: &observability.CreateScrapeConfigPayloadBasicAuth{ | ||
| BasicAuth: &observability.PartialUpdateScrapeConfigsRequestInnerBasicAuth{ | ||
| Username: utils.Ptr("username"), | ||
@@ -101,5 +101,5 @@ Password: utils.Ptr("password"), | ||
| MetricsPath: utils.Ptr("/metrics"), | ||
| MetricsRelabelConfigs: &[]observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInner{ | ||
| MetricsRelabelConfigs: &[]observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInner{ | ||
| { | ||
| Action: utils.Ptr(observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInnerAction("replace")), | ||
| Action: observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInnerGetActionAttributeType(utils.Ptr("replace")), | ||
| Modulus: utils.Ptr(1.0), | ||
@@ -130,3 +130,3 @@ Regex: utils.Ptr("regex"), | ||
| }, | ||
| TlsConfig: &observability.CreateScrapeConfigPayloadHttpSdConfigsInnerOauth2TlsConfig{ | ||
| TlsConfig: &observability.PartialUpdateScrapeConfigsRequestInnerHttpSdConfigsInnerOauth2TlsConfig{ | ||
| InsecureSkipVerify: utils.Ptr(true), | ||
@@ -432,3 +432,3 @@ }, | ||
| config *[]observability.MetricsRelabelConfig | ||
| expected *[]observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInner | ||
| expected *[]observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInner | ||
| }{ | ||
@@ -448,5 +448,5 @@ { | ||
| }, | ||
| expected: &[]observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInner{ | ||
| expected: &[]observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInner{ | ||
| { | ||
| Action: observability.CREATESCRAPECONFIGPAYLOADMETRICSRELABELCONFIGSINNERACTION_REPLACE.Ptr(), | ||
| Action: observability.PARTIALUPDATESCRAPECONFIGSREQUESTINNERMETRICSRELABELCONFIGSINNERACTION_REPLACE.Ptr(), | ||
| Modulus: utils.Float64Ptr(1.0), | ||
@@ -548,3 +548,3 @@ Regex: utils.Ptr("regex"), | ||
| auth *observability.BasicAuth | ||
| expected *observability.CreateScrapeConfigPayloadBasicAuth | ||
| expected *observability.PartialUpdateScrapeConfigsRequestInnerBasicAuth | ||
| }{ | ||
@@ -557,3 +557,3 @@ { | ||
| }, | ||
| expected: &observability.CreateScrapeConfigPayloadBasicAuth{ | ||
| expected: &observability.PartialUpdateScrapeConfigsRequestInnerBasicAuth{ | ||
| Username: utils.Ptr("username"), | ||
@@ -566,3 +566,3 @@ Password: utils.Ptr("password"), | ||
| auth: &observability.BasicAuth{}, | ||
| expected: &observability.CreateScrapeConfigPayloadBasicAuth{}, | ||
| expected: &observability.PartialUpdateScrapeConfigsRequestInnerBasicAuth{}, | ||
| }, | ||
@@ -596,3 +596,3 @@ { | ||
| config *observability.TLSConfig | ||
| expected *observability.CreateScrapeConfigPayloadHttpSdConfigsInnerOauth2TlsConfig | ||
| expected *observability.PartialUpdateScrapeConfigsRequestInnerHttpSdConfigsInnerOauth2TlsConfig | ||
| }{ | ||
@@ -604,3 +604,3 @@ { | ||
| }, | ||
| expected: &observability.CreateScrapeConfigPayloadHttpSdConfigsInnerOauth2TlsConfig{ | ||
| expected: &observability.PartialUpdateScrapeConfigsRequestInnerHttpSdConfigsInnerOauth2TlsConfig{ | ||
| InsecureSkipVerify: utils.Ptr(true), | ||
@@ -612,3 +612,3 @@ }, | ||
| config: &observability.TLSConfig{}, | ||
| expected: &observability.CreateScrapeConfigPayloadHttpSdConfigsInnerOauth2TlsConfig{}, | ||
| expected: &observability.PartialUpdateScrapeConfigsRequestInnerHttpSdConfigsInnerOauth2TlsConfig{}, | ||
| }, | ||
@@ -615,0 +615,0 @@ { |
@@ -24,3 +24,3 @@ package utils | ||
| var ( | ||
| defaultStaticConfigs = []observability.CreateScrapeConfigPayloadStaticConfigsInner{ | ||
| defaultStaticConfigs = []observability.PartialUpdateScrapeConfigsRequestInnerStaticConfigsInner{ | ||
| { | ||
@@ -124,10 +124,10 @@ Targets: utils.Ptr([]string{ | ||
| func mapMetricsRelabelConfig(metricsRelabelConfigs *[]observability.MetricsRelabelConfig) *[]observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInner { | ||
| func mapMetricsRelabelConfig(metricsRelabelConfigs *[]observability.MetricsRelabelConfig) *[]observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInner { | ||
| if metricsRelabelConfigs == nil { | ||
| return nil | ||
| } | ||
| var mappedConfigs []observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInner | ||
| var mappedConfigs []observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInner | ||
| for _, config := range *metricsRelabelConfigs { | ||
| mappedConfig := observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInner{ | ||
| Action: observability.CreateScrapeConfigPayloadMetricsRelabelConfigsInnerGetActionAttributeType(config.Action), | ||
| mappedConfig := observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInner{ | ||
| Action: observability.PartialUpdateScrapeConfigsRequestInnerMetricsRelabelConfigsInnerGetActionAttributeType(config.Action), | ||
| Modulus: utils.ConvertInt64PToFloat64P(config.Modulus), | ||
@@ -165,3 +165,3 @@ Regex: config.Regex, | ||
| func mapBasicAuth(basicAuth *observability.BasicAuth) *observability.CreateScrapeConfigPayloadBasicAuth { | ||
| func mapBasicAuth(basicAuth *observability.BasicAuth) *observability.PartialUpdateScrapeConfigsRequestInnerBasicAuth { | ||
| if basicAuth == nil { | ||
@@ -171,3 +171,3 @@ return nil | ||
| return &observability.CreateScrapeConfigPayloadBasicAuth{ | ||
| return &observability.PartialUpdateScrapeConfigsRequestInnerBasicAuth{ | ||
| Password: basicAuth.Password, | ||
@@ -178,3 +178,3 @@ Username: basicAuth.Username, | ||
| func mapTlsConfig(tlsConfig *observability.TLSConfig) *observability.CreateScrapeConfigPayloadHttpSdConfigsInnerOauth2TlsConfig { | ||
| func mapTlsConfig(tlsConfig *observability.TLSConfig) *observability.PartialUpdateScrapeConfigsRequestInnerHttpSdConfigsInnerOauth2TlsConfig { | ||
| if tlsConfig == nil { | ||
@@ -184,3 +184,3 @@ return nil | ||
| return &observability.CreateScrapeConfigPayloadHttpSdConfigsInnerOauth2TlsConfig{ | ||
| return &observability.PartialUpdateScrapeConfigsRequestInnerHttpSdConfigsInnerOauth2TlsConfig{ | ||
| InsecureSkipVerify: tlsConfig.InsecureSkipVerify, | ||
@@ -187,0 +187,0 @@ } |
@@ -5,2 +5,4 @@ package utils | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| ) | ||
@@ -11,18 +13,24 @@ | ||
| description string | ||
| input map[string]string | ||
| expectedOutput map[string]string | ||
| input *map[string]string | ||
| expectedOutput *map[string]string | ||
| isValid bool | ||
| }{ | ||
| { | ||
| "base-ok", | ||
| map[string]string{"script": "ls /"}, | ||
| map[string]string{"script": "ls /"}, | ||
| true, | ||
| description: "base-ok", | ||
| input: &map[string]string{"script": "ls /"}, | ||
| expectedOutput: &map[string]string{"script": "ls /"}, | ||
| isValid: true, | ||
| }, | ||
| { | ||
| "not-ok-nonexistant-file-specified-for-script", | ||
| map[string]string{"script": "@{/some/file/which/does/not/exist/and/thus/fails}"}, | ||
| nil, | ||
| false, | ||
| description: "nil input", | ||
| input: nil, | ||
| expectedOutput: nil, | ||
| isValid: true, | ||
| }, | ||
| { | ||
| description: "not-ok-nonexistant-file-specified-for-script", | ||
| input: &map[string]string{"script": "@{/some/file/which/does/not/exist/and/thus/fails}"}, | ||
| expectedOutput: nil, | ||
| isValid: false, | ||
| }, | ||
| } | ||
@@ -43,4 +51,5 @@ | ||
| } | ||
| if output["script"] != tt.expectedOutput["script"] { | ||
| t.Errorf("expected output to be %s, got %s", tt.expectedOutput["script"], output["script"]) | ||
| diff := cmp.Diff(output, tt.expectedOutput) | ||
| if diff != "" { | ||
| t.Fatalf("ParseScriptParams() output mismatch (-want +got):\n%s", diff) | ||
| } | ||
@@ -47,0 +56,0 @@ }) |
@@ -8,9 +8,11 @@ package utils | ||
| func ParseScriptParams(params map[string]string) (map[string]string, error) { | ||
| func ParseScriptParams(params *map[string]string) (*map[string]string, error) { //nolint:gocritic // flag value is a map pointer | ||
| if params == nil { | ||
| return nil, nil | ||
| } | ||
| parsed := map[string]string{} | ||
| for k, v := range params { | ||
| for k, v := range *params { | ||
| parsed[k] = v | ||
| if k == "script" && strings.HasPrefix(v, "@{") && strings.HasSuffix(v, "}") { | ||
@@ -25,3 +27,4 @@ // Check if a script file path was specified, like: --params script=@{/tmp/test.sh} | ||
| } | ||
| return parsed, nil | ||
| return &parsed, nil | ||
| } |
Sorry, the diff of this file is too big to display