mygithub.libinneed.workers.dev/stackitcloud/stackit-cli
Advanced tools
| ## stackit beta intake runner create | ||
| Creates a new Intake Runner | ||
| ### Synopsis | ||
| Creates a new Intake Runner. | ||
| ``` | ||
| stackit beta intake runner create [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Create a new Intake Runner with a display name and message capacity limits | ||
| $ stackit beta intake runner create --display-name my-runner --max-message-size-kib 1000 --max-messages-per-hour 5000 | ||
| Create a new Intake Runner with a description and labels | ||
| $ stackit beta intake runner create --display-name my-runner --max-message-size-kib 1000 --max-messages-per-hour 5000 --description "Main runner for production" --labels="env=prod,team=billing" | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| --description string Description | ||
| --display-name string Display name | ||
| -h, --help Help for "stackit beta intake runner create" | ||
| --labels stringToString Labels in key=value format, separated by commas. Example: --labels "key1=value1,key2=value2" (default []) | ||
| --max-message-size-kib int Maximum message size in KiB | ||
| --max-messages-per-hour int Maximum number of messages per hour | ||
| ``` | ||
| ### 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 beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners | ||
| ## stackit beta intake runner delete | ||
| Deletes an Intake Runner | ||
| ### Synopsis | ||
| Deletes an Intake Runner. | ||
| ``` | ||
| stackit beta intake runner delete RUNNER_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Delete an Intake Runner with ID "xxx" | ||
| $ stackit beta intake runner delete xxx | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit beta intake runner delete" | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners | ||
| ## stackit beta intake runner describe | ||
| Shows details of an Intake Runner | ||
| ### Synopsis | ||
| Shows details of an Intake Runner. | ||
| ``` | ||
| stackit beta intake runner describe RUNNER_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Get details of an Intake Runner with ID "xxx" | ||
| $ stackit beta intake runner describe xxx | ||
| Get details of an Intake Runner with ID "xxx" in JSON format | ||
| $ stackit beta intake runner describe xxx --output-format json | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit beta intake runner 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 beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners | ||
| ## stackit beta intake runner list | ||
| Lists all Intake Runners | ||
| ### Synopsis | ||
| Lists all Intake Runners for the current project. | ||
| ``` | ||
| stackit beta intake runner list [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List all Intake Runners | ||
| $ stackit beta intake runner list | ||
| List all Intake Runners in JSON format | ||
| $ stackit beta intake runner list --output-format json | ||
| List up to 5 Intake Runners | ||
| $ stackit beta intake runner list --limit 5 | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit beta intake runner list" | ||
| --limit int Maximum number of entries to list | ||
| ``` | ||
| ### 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 beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners | ||
| ## stackit beta intake runner update | ||
| Updates an Intake Runner | ||
| ### Synopsis | ||
| Updates an Intake Runner. Only the specified fields are updated. | ||
| ``` | ||
| stackit beta intake runner update RUNNER_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Update the display name of an Intake Runner with ID "xxx" | ||
| $ stackit beta intake runner update xxx --display-name "new-runner-name" | ||
| Update the message capacity limits for an Intake Runner with ID "xxx" | ||
| $ stackit beta intake runner update xxx --max-message-size-kib 1000 --max-messages-per-hour 10000 | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| --description string Description | ||
| --display-name string Display name | ||
| -h, --help Help for "stackit beta intake runner update" | ||
| --labels stringToString Labels in key=value format, separated by commas. Example: --labels "key1=value1,key2=value2". (default []) | ||
| --max-message-size-kib int Maximum message size in KiB. Note: Overall message capacity cannot be decreased. | ||
| --max-messages-per-hour int Maximum number of messages per hour. Note: Overall message capacity cannot be decreased. | ||
| ``` | ||
| ### 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 beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners | ||
| ## stackit beta intake runner | ||
| Provides functionality for Intake Runners | ||
| ### Synopsis | ||
| Provides functionality for Intake Runners. | ||
| ``` | ||
| stackit beta intake runner [flags] | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit beta intake runner" | ||
| ``` | ||
| ### 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 beta intake](./stackit_beta_intake.md) - Provides functionality for intake | ||
| * [stackit beta intake runner create](./stackit_beta_intake_runner_create.md) - Creates a new Intake Runner | ||
| * [stackit beta intake runner delete](./stackit_beta_intake_runner_delete.md) - Deletes an Intake Runner | ||
| * [stackit beta intake runner describe](./stackit_beta_intake_runner_describe.md) - Shows details of an Intake Runner | ||
| * [stackit beta intake runner list](./stackit_beta_intake_runner_list.md) - Lists all Intake Runners | ||
| * [stackit beta intake runner update](./stackit_beta_intake_runner_update.md) - Updates an Intake Runner | ||
| ## stackit beta intake | ||
| Provides functionality for intake | ||
| ### Synopsis | ||
| Provides functionality for intake. | ||
| ``` | ||
| stackit beta intake [flags] | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit beta intake" | ||
| ``` | ||
| ### 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 beta](./stackit_beta.md) - Contains beta STACKIT CLI commands | ||
| * [stackit beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners | ||
| ## stackit beta kms key describe | ||
| Describe a KMS key | ||
| ### Synopsis | ||
| Describe a KMS key | ||
| ``` | ||
| stackit beta kms key describe KEY_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Describe a KMS key with ID xxx of keyring yyy | ||
| $ stackit beta kms key describe xxx --keyring-id yyy | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit beta kms key describe" | ||
| --keyring-id string Key Ring ID | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --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 beta kms key](./stackit_beta_kms_key.md) - Manage KMS keys | ||
| ## stackit beta kms keyring describe | ||
| Describe a KMS key ring | ||
| ### Synopsis | ||
| Describe a KMS key ring | ||
| ``` | ||
| stackit beta kms keyring describe KEYRING_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Describe a KMS key ring with ID xxx | ||
| $ stackit beta kms keyring describe xxx | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit beta kms keyring 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 beta kms keyring](./stackit_beta_kms_keyring.md) - Manage KMS key rings | ||
| ## stackit beta kms wrapping-key describe | ||
| Describe a KMS wrapping key | ||
| ### Synopsis | ||
| Describe a KMS wrapping key | ||
| ``` | ||
| stackit beta kms wrapping-key describe WRAPPING_KEY_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Describe a KMS wrapping key with ID xxx of keyring yyy | ||
| $ stackit beta kms wrappingkey describe xxx --keyring-id yyy | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit beta kms wrapping-key describe" | ||
| --keyring-id string Key Ring ID | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --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 beta kms wrapping-key](./stackit_beta_kms_wrapping-key.md) - Manage KMS wrapping keys | ||
| package intake | ||
| import ( | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake/runner" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| ) | ||
| // NewCmd creates the 'stackit intake' command | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "intake", | ||
| Short: "Provides functionality for intake", | ||
| Long: "Provides functionality for intake.", | ||
| Args: args.NoArgs, | ||
| Run: utils.CmdHelp, | ||
| } | ||
| addSubcommands(cmd, params) | ||
| return cmd | ||
| } | ||
| func addSubcommands(cmd *cobra.Command, params *params.CmdParams) { | ||
| cmd.AddCommand(runner.NewCmd(params)) | ||
| } |
| package create | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| ) | ||
| // Define a unique key for the context to avoid collisions | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu01" | ||
| testDisplayName = "testrunner" | ||
| testMaxMessageSizeKiB = int64(1024) | ||
| testMaxMessagesPerHour = int64(10000) | ||
| testDescription = "This is a test runner" | ||
| testLabelsString = "env=test,team=dev" | ||
| ) | ||
| var ( | ||
| // testCtx dummy context for testing purposes | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| // testClient mock API client | ||
| testClient = &intake.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testLabels = map[string]string{"env": "test", "team": "dev"} | ||
| ) | ||
| // fixtureFlagValues generates a map of flag values for tests | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| displayNameFlag: testDisplayName, | ||
| maxMessageSizeKiBFlag: "1024", | ||
| maxMessagesPerHourFlag: "10000", | ||
| descriptionFlag: testDescription, | ||
| labelFlag: testLabelsString, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| // fixtureInputModel generates an input model for tests | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| DisplayName: utils.Ptr(testDisplayName), | ||
| MaxMessageSizeKiB: utils.Ptr(testMaxMessageSizeKiB), | ||
| MaxMessagesPerHour: utils.Ptr(testMaxMessagesPerHour), | ||
| Description: utils.Ptr(testDescription), | ||
| Labels: utils.Ptr(testLabels), | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| // fixtureCreatePayload generates a CreateIntakeRunnerPayload for tests | ||
| func fixtureCreatePayload(mods ...func(payload *intake.CreateIntakeRunnerPayload)) intake.CreateIntakeRunnerPayload { | ||
| payload := intake.CreateIntakeRunnerPayload{ | ||
| DisplayName: utils.Ptr(testDisplayName), | ||
| MaxMessageSizeKiB: utils.Ptr(testMaxMessageSizeKiB), | ||
| MaxMessagesPerHour: utils.Ptr(testMaxMessagesPerHour), | ||
| Description: utils.Ptr(testDescription), | ||
| Labels: utils.Ptr(testLabels), | ||
| } | ||
| for _, mod := range mods { | ||
| mod(&payload) | ||
| } | ||
| return payload | ||
| } | ||
| // fixtureRequest generates an API request for tests | ||
| func fixtureRequest(mods ...func(request *intake.ApiCreateIntakeRunnerRequest)) intake.ApiCreateIntakeRunnerRequest { | ||
| request := testClient.CreateIntakeRunner(testCtx, testProjectId, testRegion) | ||
| request = request.CreateIntakeRunnerPayload(fixtureCreatePayload()) | ||
| 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(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "display name missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, displayNameFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "max message size missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, maxMessageSizeKiBFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "max messages per hour missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, maxMessagesPerHourFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "required fields only", | ||
| flagValues: map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| displayNameFlag: testDisplayName, | ||
| maxMessageSizeKiBFlag: "1024", | ||
| maxMessagesPerHourFlag: "10000", | ||
| }, | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Description = nil | ||
| model.Labels = nil | ||
| }), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| parseInputWrapper := func(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| return parseInput(p, cmd) | ||
| } | ||
| testutils.TestParseInput(t, NewCmd, parseInputWrapper, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest intake.ApiCreateIntakeRunnerRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| { | ||
| description: "no optionals", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.Description = nil | ||
| model.Labels = nil | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *intake.ApiCreateIntakeRunnerRequest) { | ||
| *request = (*request).CreateIntakeRunnerPayload(fixtureCreatePayload(func(payload *intake.CreateIntakeRunnerPayload) { | ||
| payload.Description = nil | ||
| payload.Labels = nil | ||
| })) | ||
| }), | ||
| }, | ||
| } | ||
| 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 { | ||
| model *inputModel | ||
| projectLabel string | ||
| resp *intake.IntakeRunnerResponse | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "default output", | ||
| args: args{ | ||
| model: fixtureInputModel(), | ||
| projectLabel: "my-project", | ||
| resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "default output - async", | ||
| args: args{ | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.Async = true | ||
| }), | ||
| projectLabel: "my-project", | ||
| resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "json output", | ||
| args: args{ | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.OutputFormat = print.JSONOutputFormat | ||
| }), | ||
| resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "nil response - default output", | ||
| args: args{ | ||
| model: fixtureInputModel(), | ||
| resp: nil, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "nil response - json output", | ||
| args: args{ | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.OutputFormat = print.JSONOutputFormat | ||
| }), | ||
| resp: nil, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.projectLabel, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package create | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake/wait" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| cliErr "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/intake/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| ) | ||
| const ( | ||
| displayNameFlag = "display-name" | ||
| maxMessageSizeKiBFlag = "max-message-size-kib" | ||
| maxMessagesPerHourFlag = "max-messages-per-hour" | ||
| descriptionFlag = "description" | ||
| labelFlag = "labels" | ||
| ) | ||
| // inputModel struct holds all the input parameters for the command | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| DisplayName *string | ||
| MaxMessageSizeKiB *int64 | ||
| MaxMessagesPerHour *int64 | ||
| Description *string | ||
| Labels *map[string]string | ||
| } | ||
| func NewCmd(p *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "create", | ||
| Short: "Creates a new Intake Runner", | ||
| Long: "Creates a new Intake Runner.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Create a new Intake Runner with a display name and message capacity limits`, | ||
| `$ stackit beta intake runner create --display-name my-runner --max-message-size-kib 1000 --max-messages-per-hour 5000`), | ||
| examples.NewExample( | ||
| `Create a new Intake Runner with a description and labels`, | ||
| `$ stackit beta intake runner create --display-name my-runner --max-message-size-kib 1000 --max-messages-per-hour 5000 --description "Main runner for production" --labels="env=prod,team=billing"`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, _ []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(p.Printer, cmd) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| projectLabel, err := projectname.GetProjectName(ctx, p.Printer, p.CliVersion, cmd) | ||
| if err != nil { | ||
| p.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| projectLabel = model.ProjectId | ||
| } | ||
| if !model.AssumeYes { | ||
| prompt := fmt.Sprintf("Are you sure you want to create an Intake Runner for project %q?", projectLabel) | ||
| err = p.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("create Intake Runner: %w", err) | ||
| } | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(p.Printer) | ||
| s.Start("Creating STACKIT Intake Runner") | ||
| _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, apiClient, model.ProjectId, model.Region, resp.GetId()).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for STACKIT Intake Runner creation: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| return outputResult(p.Printer, model, projectLabel, resp) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().String(displayNameFlag, "", "Display name") | ||
| cmd.Flags().Int64(maxMessageSizeKiBFlag, 0, "Maximum message size in KiB") | ||
| cmd.Flags().Int64(maxMessagesPerHourFlag, 0, "Maximum number of messages per hour") | ||
| cmd.Flags().String(descriptionFlag, "", "Description") | ||
| cmd.Flags().StringToString(labelFlag, nil, "Labels in key=value format, separated by commas. Example: --labels \"key1=value1,key2=value2\"") | ||
| err := flags.MarkFlagsRequired(cmd, displayNameFlag, maxMessageSizeKiBFlag, maxMessagesPerHourFlag) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &cliErr.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| DisplayName: flags.FlagToStringPointer(p, cmd, displayNameFlag), | ||
| MaxMessageSizeKiB: flags.FlagToInt64Pointer(p, cmd, maxMessageSizeKiBFlag), | ||
| MaxMessagesPerHour: flags.FlagToInt64Pointer(p, cmd, maxMessagesPerHourFlag), | ||
| Description: flags.FlagToStringPointer(p, cmd, descriptionFlag), | ||
| Labels: flags.FlagToStringToStringPointer(p, cmd, labelFlag), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *intake.APIClient) intake.ApiCreateIntakeRunnerRequest { | ||
| // Start building the request by calling the base method with path parameters | ||
| req := apiClient.CreateIntakeRunner(ctx, model.ProjectId, model.Region) | ||
| // Create the payload struct with data from the input model | ||
| payload := intake.CreateIntakeRunnerPayload{ | ||
| DisplayName: model.DisplayName, | ||
| MaxMessageSizeKiB: model.MaxMessageSizeKiB, | ||
| MaxMessagesPerHour: model.MaxMessagesPerHour, | ||
| Description: model.Description, | ||
| Labels: model.Labels, | ||
| } | ||
| // Attach the payload to the request builder | ||
| req = req.CreateIntakeRunnerPayload(payload) | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, model *inputModel, projectLabel string, resp *intake.IntakeRunnerResponse) error { | ||
| return p.OutputResult(model.OutputFormat, resp, func() error { | ||
| if resp == nil { | ||
| p.Outputf("Triggered creation of Intake Runner for project %q, but no runner ID was returned.\n", projectLabel) | ||
| return nil | ||
| } | ||
| operationState := "Created" | ||
| if model.Async { | ||
| operationState = "Triggered creation of" | ||
| } | ||
| p.Outputf("%s Intake Runner for project %q. Runner ID: %s\n", operationState, projectLabel, utils.PtrString(resp.Id)) | ||
| return nil | ||
| }) | ||
| } |
| package delete | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| ) | ||
| // Define a unique key for the context to avoid collisions | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu01" | ||
| ) | ||
| var ( | ||
| // testCtx is a dummy context for testing purposes | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| // testClient is a mock API client | ||
| testClient = &intake.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testRunnerId = uuid.NewString() | ||
| ) | ||
| // fixtureArgValues generates a slice of arguments for tests | ||
| func fixtureArgValues(mods ...func(argValues []string)) []string { | ||
| argValues := []string{ | ||
| testRunnerId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| // fixtureFlagValues generates a map of flag values for tests | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| // fixtureInputModel generates an input model for tests | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| RunnerId: testRunnerId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| // fixtureRequest generates an API request for tests | ||
| func fixtureRequest(mods ...func(request *intake.ApiDeleteIntakeRunnerRequest)) intake.ApiDeleteIntakeRunnerRequest { | ||
| request := testClient.DeleteIntakeRunner(testCtx, testProjectId, testRegion, testRunnerId) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no arg values", | ||
| argValues: []string{}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no flag values", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "runner id invalid", | ||
| argValues: []string{"invalid-uuid"}, | ||
| flagValues: fixtureFlagValues(), | ||
| 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 intake.ApiDeleteIntakeRunnerRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildRequest(testCtx, tt.model, testClient) | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmpopts.EquateComparable(testCtx), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("Data does not match: %s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package delete | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| cliErr "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/intake/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake/wait" | ||
| ) | ||
| const ( | ||
| runnerIdArg = "RUNNER_ID" | ||
| ) | ||
| // inputModel struct holds all the input parameters for the command | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| RunnerId string | ||
| } | ||
| // NewCmd creates a new cobra command for deleting an Intake Runner | ||
| func NewCmd(p *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("delete %s", runnerIdArg), | ||
| Short: "Deletes an Intake Runner", | ||
| Long: "Deletes an Intake Runner.", | ||
| Args: args.SingleArg(runnerIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Delete an Intake Runner with ID "xxx"`, | ||
| `$ stackit beta intake runner delete xxx`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(p.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if !model.AssumeYes { | ||
| prompt := fmt.Sprintf("Are you sure you want to delete Intake Runner %q?", model.RunnerId) | ||
| err = p.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| if err = req.Execute(); err != nil { | ||
| return fmt.Errorf("delete Intake Runner: %w", err) | ||
| } | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(p.Printer) | ||
| s.Start("Deleting STACKIT Intake Runner") | ||
| _, err = wait.DeleteIntakeRunnerWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.RunnerId).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for STACKIT Intake Runner deletion: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| operationState := "Deleted" | ||
| if model.Async { | ||
| operationState = "Triggered deletion of" | ||
| } | ||
| p.Printer.Outputf("%s stackit Intake Runner %s \n", operationState, model.RunnerId) | ||
| return nil | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| // parseInput parses the command arguments and flags into a standardized model | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| runnerId := inputArgs[0] | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &cliErr.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| RunnerId: runnerId, | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| // buildRequest creates the API request to delete an Intake Runner | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *intake.APIClient) intake.ApiDeleteIntakeRunnerRequest { | ||
| req := apiClient.DeleteIntakeRunner(ctx, model.ProjectId, model.Region, model.RunnerId) | ||
| return req | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| ) | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu01" | ||
| ) | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &intake.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testRunnerId = uuid.NewString() | ||
| ) | ||
| func fixtureArgValues(mods ...func(argValues []string)) []string { | ||
| argValues := []string{ | ||
| testRunnerId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| RunnerId: testRunnerId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *intake.ApiGetIntakeRunnerRequest)) intake.ApiGetIntakeRunnerRequest { | ||
| request := testClient.GetIntakeRunner(testCtx, testProjectId, testRegion, testRunnerId) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no arg values", | ||
| argValues: []string{}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no flag values", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "runner id invalid", | ||
| argValues: []string{"invalid-uuid"}, | ||
| flagValues: fixtureFlagValues(), | ||
| 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 intake.ApiGetIntakeRunnerRequest | ||
| }{ | ||
| { | ||
| 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 | ||
| runner *intake.IntakeRunnerResponse | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "default output", | ||
| args: args{outputFormat: "default", runner: &intake.IntakeRunnerResponse{}}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "json output", | ||
| args: args{outputFormat: print.JSONOutputFormat, runner: &intake.IntakeRunnerResponse{}}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "yaml output", | ||
| args: args{outputFormat: print.YAMLOutputFormat, runner: &intake.IntakeRunnerResponse{}}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "nil runner", | ||
| args: args{runner: nil}, | ||
| wantErr: true, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.runner); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| cliErr "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/intake/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| ) | ||
| const ( | ||
| runnerIdArg = "RUNNER_ID" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| RunnerId string | ||
| } | ||
| func NewCmd(p *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("describe %s", runnerIdArg), | ||
| Short: "Shows details of an Intake Runner", | ||
| Long: "Shows details of an Intake Runner.", | ||
| Args: args.SingleArg(runnerIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Get details of an Intake Runner with ID "xxx"`, | ||
| `$ stackit beta intake runner describe xxx`), | ||
| examples.NewExample( | ||
| `Get details of an Intake Runner with ID "xxx" in JSON format`, | ||
| `$ stackit beta intake runner describe xxx --output-format json`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(p.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API to get a single runner | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get Intake Runner: %w", err) | ||
| } | ||
| return outputResult(p.Printer, model.OutputFormat, resp) | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| runnerId := inputArgs[0] | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &cliErr.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| RunnerId: runnerId, | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| // buildRequest creates the API request to get a single Intake Runner | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *intake.APIClient) intake.ApiGetIntakeRunnerRequest { | ||
| req := apiClient.GetIntakeRunner(ctx, model.ProjectId, model.Region, model.RunnerId) | ||
| return req | ||
| } | ||
| // outputResult formats the API response and prints it to the console | ||
| func outputResult(p *print.Printer, outputFormat string, runner *intake.IntakeRunnerResponse) error { | ||
| if runner == nil { | ||
| return fmt.Errorf("received nil runner, could not display details") | ||
| } | ||
| return p.OutputResult(outputFormat, runner, func() error { | ||
| table := tables.NewTable() | ||
| table.SetHeader("Attribute", "Value") | ||
| table.AddRow("ID", runner.GetId()) | ||
| table.AddRow("Name", runner.GetDisplayName()) | ||
| table.AddRow("State", runner.GetState()) | ||
| table.AddRow("Created", runner.GetCreateTime()) | ||
| table.AddRow("Labels", runner.GetLabels()) | ||
| table.AddRow("Description", runner.GetDescription()) | ||
| table.AddRow("Max Message Size (KiB)", runner.GetMaxMessageSizeKiB()) | ||
| table.AddRow("Max Messages/Hour", runner.GetMaxMessagesPerHour()) | ||
| table.AddRow("Ingestion URI", runner.GetUri()) | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "strconv" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| ) | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu01" | ||
| testLimit = int64(5) | ||
| ) | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &intake.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *intake.ApiListIntakeRunnersRequest)) intake.ApiListIntakeRunnersRequest { | ||
| request := testClient.ListIntakeRunners(testCtx, testProjectId, testRegion) | ||
| 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(), | ||
| }, | ||
| { | ||
| description: "with limit", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[limitFlag] = strconv.FormatInt(testLimit, 10) | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Limit = utils.Ptr(testLimit) | ||
| }), | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "limit is zero", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[limitFlag] = "0" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "limit is negative", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[limitFlag] = "-1" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, func(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| return parseInput(p, cmd) | ||
| }, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest intake.ApiListIntakeRunnersRequest | ||
| }{ | ||
| { | ||
| 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 | ||
| runners []intake.IntakeRunnerResponse | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "default output", | ||
| args: args{outputFormat: "default", runners: []intake.IntakeRunnerResponse{}}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "json output", | ||
| args: args{outputFormat: print.JSONOutputFormat, runners: []intake.IntakeRunnerResponse{}}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty slice", | ||
| args: args{runners: []intake.IntakeRunnerResponse{}}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "nil slice", | ||
| args: args{runners: nil}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty intake runner in slice", | ||
| args: args{ | ||
| runners: []intake.IntakeRunnerResponse{{}}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, "dummy-projectlabel", tt.args.runners); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/intake/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| ) | ||
| const ( | ||
| limitFlag = "limit" | ||
| ) | ||
| // inputModel struct holds all the input parameters for the command | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| Limit *int64 | ||
| } | ||
| // NewCmd creates a new cobra command for listing Intake Runners | ||
| func NewCmd(p *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "list", | ||
| Short: "Lists all Intake Runners", | ||
| Long: "Lists all Intake Runners for the current project.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List all Intake Runners`, | ||
| `$ stackit beta intake runner list`), | ||
| examples.NewExample( | ||
| `List all Intake Runners in JSON format`, | ||
| `$ stackit beta intake runner list --output-format json`), | ||
| examples.NewExample( | ||
| `List up to 5 Intake Runners`, | ||
| `$ stackit beta intake runner list --limit 5`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, _ []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(p.Printer, cmd) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("list Intake Runners: %w", err) | ||
| } | ||
| runners := resp.GetIntakeRunners() | ||
| // Truncate output | ||
| if model.Limit != nil && len(runners) > int(*model.Limit) { | ||
| runners = runners[:*model.Limit] | ||
| } | ||
| projectLabel := model.ProjectId | ||
| if len(runners) == 0 { | ||
| projectLabel, err = projectname.GetProjectName(ctx, p.Printer, p.CliVersion, cmd) | ||
| if err != nil { | ||
| p.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| } | ||
| } | ||
| return outputResult(p.Printer, model.OutputFormat, projectLabel, runners) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| // configureFlags adds the --limit flag to the command | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list") | ||
| } | ||
| // parseInput parses the command flags into a standardized model | ||
| func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| limit := flags.FlagToInt64Pointer(p, cmd, limitFlag) | ||
| if limit != nil && *limit < 1 { | ||
| return nil, &errors.FlagValidationError{ | ||
| Flag: limitFlag, | ||
| Details: "must be greater than 0", | ||
| } | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| Limit: limit, | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| // buildRequest creates the API request to list Intake Runners | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *intake.APIClient) intake.ApiListIntakeRunnersRequest { | ||
| req := apiClient.ListIntakeRunners(ctx, model.ProjectId, model.Region) | ||
| // Note: we do support API pagination, but for consistency with other services, we fetch all items and apply | ||
| // client-side limit. | ||
| // A more advanced implementation could use the --limit flag to set the API's PageSize. | ||
| return req | ||
| } | ||
| // outputResult formats the API response and prints it to the console | ||
| func outputResult(p *print.Printer, outputFormat, projectLabel string, runners []intake.IntakeRunnerResponse) error { | ||
| return p.OutputResult(outputFormat, runners, func() error { | ||
| if len(runners) == 0 { | ||
| p.Outputf("No intake runners found for project %q\n", projectLabel) | ||
| return nil | ||
| } | ||
| table := tables.NewTable() | ||
| table.SetHeader("ID", "NAME", "STATE") | ||
| for _, runner := range runners { | ||
| table.AddRow( | ||
| runner.GetId(), | ||
| runner.GetDisplayName(), | ||
| runner.GetState(), | ||
| ) | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package runner | ||
| import ( | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake/runner/create" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake/runner/delete" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake/runner/describe" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake/runner/list" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake/runner/update" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| ) | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "runner", | ||
| Short: "Provides functionality for Intake Runners", | ||
| Long: "Provides functionality for Intake Runners.", | ||
| Args: args.NoArgs, | ||
| Run: utils.CmdHelp, | ||
| } | ||
| // Pass the params down to each action command | ||
| cmd.AddCommand(create.NewCmd(params)) | ||
| cmd.AddCommand(delete.NewCmd(params)) | ||
| cmd.AddCommand(describe.NewCmd(params)) | ||
| cmd.AddCommand(list.NewCmd(params)) | ||
| cmd.AddCommand(update.NewCmd(params)) | ||
| return cmd | ||
| } |
| package update | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| ) | ||
| type testCtxKey struct{} | ||
| const ( | ||
| testRegion = "eu01" | ||
| ) | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &intake.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testRunnerId = uuid.NewString() | ||
| ) | ||
| func fixtureArgValues(mods ...func(argValues []string)) []string { | ||
| argValues := []string{ | ||
| testRunnerId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| displayNameFlag: "new-runner-name", | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| RunnerId: testRunnerId, | ||
| DisplayName: utils.Ptr("new-runner-name"), | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *intake.ApiUpdateIntakeRunnerRequest)) intake.ApiUpdateIntakeRunnerRequest { | ||
| request := testClient.UpdateIntakeRunner(testCtx, testProjectId, testRegion, testRunnerId) | ||
| payload := intake.UpdateIntakeRunnerPayload{ | ||
| DisplayName: utils.Ptr("new-runner-name"), | ||
| } | ||
| request = request.UpdateIntakeRunnerPayload(payload) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no update flags provided", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| }, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "update all fields", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[maxMessageSizeKiBFlag] = "2048" | ||
| flagValues[maxMessagesPerHourFlag] = "10000" | ||
| flagValues[descriptionFlag] = "new description" | ||
| flagValues[labelFlag] = "env=prod,team=sre" | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.MaxMessageSizeKiB = utils.Ptr(int64(2048)) | ||
| model.MaxMessagesPerHour = utils.Ptr(int64(10000)) | ||
| model.Description = utils.Ptr("new description") | ||
| model.Labels = utils.Ptr(map[string]string{"env": "prod", "team": "sre"}) | ||
| }), | ||
| }, | ||
| { | ||
| description: "no args", | ||
| argValues: []string{}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| 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 intake.ApiUpdateIntakeRunnerRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| { | ||
| description: "update description and labels", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.DisplayName = nil | ||
| model.Description = utils.Ptr("new-desc") | ||
| model.Labels = utils.Ptr(map[string]string{"key": "value"}) | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *intake.ApiUpdateIntakeRunnerRequest) { | ||
| payload := intake.UpdateIntakeRunnerPayload{ | ||
| Description: utils.Ptr("new-desc"), | ||
| Labels: utils.Ptr(map[string]string{"key": "value"}), | ||
| } | ||
| *request = (*request).UpdateIntakeRunnerPayload(payload) | ||
| }), | ||
| }, | ||
| { | ||
| description: "update all fields", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.DisplayName = utils.Ptr("another-name") | ||
| model.MaxMessageSizeKiB = utils.Ptr(int64(4096)) | ||
| model.MaxMessagesPerHour = utils.Ptr(int64(20000)) | ||
| model.Description = utils.Ptr("final-desc") | ||
| model.Labels = utils.Ptr(map[string]string{"a": "b"}) | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *intake.ApiUpdateIntakeRunnerRequest) { | ||
| payload := intake.UpdateIntakeRunnerPayload{ | ||
| DisplayName: utils.Ptr("another-name"), | ||
| MaxMessageSizeKiB: utils.Ptr(int64(4096)), | ||
| MaxMessagesPerHour: utils.Ptr(int64(20000)), | ||
| Description: utils.Ptr("final-desc"), | ||
| Labels: utils.Ptr(map[string]string{"a": "b"}), | ||
| } | ||
| *request = (*request).UpdateIntakeRunnerPayload(payload) | ||
| }), | ||
| }, | ||
| } | ||
| 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 { | ||
| model *inputModel | ||
| projectLabel string | ||
| resp *intake.IntakeRunnerResponse | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "default output", | ||
| args: args{ | ||
| model: fixtureInputModel(), | ||
| projectLabel: "my-project", | ||
| resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "default output - async", | ||
| args: args{ | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.Async = true | ||
| }), | ||
| projectLabel: "my-project", | ||
| resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "json output", | ||
| args: args{ | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.OutputFormat = print.JSONOutputFormat | ||
| }), | ||
| resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "nil response - default output", | ||
| args: args{ | ||
| model: fixtureInputModel(), | ||
| resp: nil, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "nil response - json output", | ||
| args: args{ | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.OutputFormat = print.JSONOutputFormat | ||
| }), | ||
| resp: nil, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.projectLabel, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package update | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| cliErr "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/intake/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake/wait" | ||
| ) | ||
| const ( | ||
| runnerIdArg = "RUNNER_ID" | ||
| ) | ||
| const ( | ||
| displayNameFlag = "display-name" | ||
| maxMessageSizeKiBFlag = "max-message-size-kib" | ||
| maxMessagesPerHourFlag = "max-messages-per-hour" | ||
| descriptionFlag = "description" | ||
| labelFlag = "labels" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| RunnerId string | ||
| DisplayName *string | ||
| MaxMessageSizeKiB *int64 | ||
| MaxMessagesPerHour *int64 | ||
| Description *string | ||
| Labels *map[string]string | ||
| } | ||
| func NewCmd(p *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("update %s", runnerIdArg), | ||
| Short: "Updates an Intake Runner", | ||
| Long: "Updates an Intake Runner. Only the specified fields are updated.", | ||
| Args: args.SingleArg(runnerIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Update the display name of an Intake Runner with ID "xxx"`, | ||
| `$ stackit beta intake runner update xxx --display-name "new-runner-name"`), | ||
| examples.NewExample( | ||
| `Update the message capacity limits for an Intake Runner with ID "xxx"`, | ||
| `$ stackit beta intake runner update xxx --max-message-size-kib 1000 --max-messages-per-hour 10000`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(p.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| projectLabel, err := projectname.GetProjectName(ctx, p.Printer, p.CliVersion, cmd) | ||
| if err != nil { | ||
| p.Printer.Debug(print.ErrorLevel, "get project name: %v", err) | ||
| projectLabel = model.ProjectId | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("update Intake Runner: %w", err) | ||
| } | ||
| // Wait for async operation, if async mode not enabled | ||
| if !model.Async { | ||
| s := spinner.New(p.Printer) | ||
| s.Start("Updating STACKIT Intake Runner") | ||
| _, err = wait.CreateOrUpdateIntakeRunnerWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.RunnerId).WaitWithContext(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("wait for STACKIT Intake Runner update: %w", err) | ||
| } | ||
| s.Stop() | ||
| } | ||
| return outputResult(p.Printer, model, projectLabel, resp) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().String(displayNameFlag, "", "Display name") | ||
| cmd.Flags().Int64(maxMessageSizeKiBFlag, 0, "Maximum message size in KiB. Note: Overall message capacity cannot be decreased.") | ||
| cmd.Flags().Int64(maxMessagesPerHourFlag, 0, "Maximum number of messages per hour. Note: Overall message capacity cannot be decreased.") | ||
| cmd.Flags().String(descriptionFlag, "", "Description") | ||
| cmd.Flags().StringToString(labelFlag, nil, `Labels in key=value format, separated by commas. Example: --labels "key1=value1,key2=value2".`) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| runnerId := inputArgs[0] | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &cliErr.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| RunnerId: runnerId, | ||
| DisplayName: flags.FlagToStringPointer(p, cmd, displayNameFlag), | ||
| MaxMessageSizeKiB: flags.FlagToInt64Pointer(p, cmd, maxMessageSizeKiBFlag), | ||
| MaxMessagesPerHour: flags.FlagToInt64Pointer(p, cmd, maxMessagesPerHourFlag), | ||
| Description: flags.FlagToStringPointer(p, cmd, descriptionFlag), | ||
| Labels: flags.FlagToStringToStringPointer(p, cmd, labelFlag), | ||
| } | ||
| if model.DisplayName == nil && model.MaxMessageSizeKiB == nil && model.MaxMessagesPerHour == nil && model.Description == nil && model.Labels == nil { | ||
| return nil, &cliErr.EmptyUpdateError{} | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *intake.APIClient) intake.ApiUpdateIntakeRunnerRequest { | ||
| req := apiClient.UpdateIntakeRunner(ctx, model.ProjectId, model.Region, model.RunnerId) | ||
| payload := intake.UpdateIntakeRunnerPayload{} | ||
| if model.DisplayName != nil { | ||
| payload.DisplayName = model.DisplayName | ||
| } | ||
| if model.MaxMessageSizeKiB != nil { | ||
| payload.MaxMessageSizeKiB = model.MaxMessageSizeKiB | ||
| } | ||
| if model.MaxMessagesPerHour != nil { | ||
| payload.MaxMessagesPerHour = model.MaxMessagesPerHour | ||
| } | ||
| if model.Description != nil { | ||
| payload.Description = model.Description | ||
| } | ||
| if model.Labels != nil { | ||
| payload.Labels = model.Labels | ||
| } | ||
| req = req.UpdateIntakeRunnerPayload(payload) | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, model *inputModel, projectLabel string, resp *intake.IntakeRunnerResponse) error { | ||
| return p.OutputResult(model.OutputFormat, resp, func() error { | ||
| if resp == nil { | ||
| p.Outputf("Triggered update of Intake Runner for project %q, but no runner ID was returned.\n", projectLabel) | ||
| return nil | ||
| } | ||
| operationState := "Updated" | ||
| if model.Async { | ||
| operationState = "Triggered update of" | ||
| } | ||
| p.Outputf("%s Intake Runner for project %q. Runner ID: %s\n", operationState, projectLabel, utils.PtrString(resp.Id)) | ||
| return nil | ||
| }) | ||
| } |
| package describe | ||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "fmt" | ||
| "testing" | ||
| "time" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/kms" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &kms.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| var testKeyRingID = uuid.NewString() | ||
| var testKeyID = uuid.NewString() | ||
| var testTime = time.Time{} | ||
| const testRegion = "eu01" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| flagKeyRingID: testKeyRingID, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| KeyID: testKeyID, | ||
| KeyRingID: testKeyRingID, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| argValues: []string{testKeyID}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| argValues: []string{}, | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no arg values", | ||
| argValues: []string{}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no flag values", | ||
| argValues: []string{testKeyID}, | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid key id", | ||
| argValues: []string{"invalid-uuid"}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "missing key ring id", | ||
| argValues: []string{testKeyID}, | ||
| flagValues: fixtureFlagValues(func(m map[string]string) { delete(m, flagKeyRingID) }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid key ring id", | ||
| argValues: []string{testKeyID}, | ||
| flagValues: fixtureFlagValues(func(m map[string]string) { | ||
| m[flagKeyRingID] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "missing project id", | ||
| argValues: []string{testKeyID}, | ||
| flagValues: fixtureFlagValues(func(m map[string]string) { delete(m, globalflags.ProjectIdFlag) }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid project id", | ||
| argValues: []string{testKeyID}, | ||
| flagValues: fixtureFlagValues(func(m map[string]string) { m[globalflags.ProjectIdFlag] = "invalid-uuid" }), | ||
| 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) { | ||
| got := buildRequest(testCtx, fixtureInputModel(), testClient) | ||
| want := testClient.GetKey(testCtx, testProjectId, testRegion, testKeyRingID, testKeyID) | ||
| diff := cmp.Diff(got, want, | ||
| cmp.AllowUnexported(want), | ||
| cmpopts.EquateComparable(testCtx), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("buildRequest() mismatch (-want +got):\n%s", diff) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| outputFmt string | ||
| keyRing *kms.Key | ||
| wantErr bool | ||
| expected string | ||
| }{ | ||
| { | ||
| description: "empty", | ||
| outputFmt: "table", | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| description: "table format", | ||
| outputFmt: "table", | ||
| keyRing: &kms.Key{ | ||
| AccessScope: utils.Ptr(kms.ACCESSSCOPE_PUBLIC), | ||
| Algorithm: utils.Ptr(kms.ALGORITHM_AES_256_GCM), | ||
| CreatedAt: utils.Ptr(testTime), | ||
| DeletionDate: nil, | ||
| Description: utils.Ptr("very secure and secret key"), | ||
| DisplayName: utils.Ptr("Test Key"), | ||
| Id: utils.Ptr(testKeyID), | ||
| ImportOnly: utils.Ptr(true), | ||
| KeyRingId: utils.Ptr(testKeyRingID), | ||
| Protection: utils.Ptr(kms.PROTECTION_SOFTWARE), | ||
| Purpose: utils.Ptr(kms.PURPOSE_SYMMETRIC_ENCRYPT_DECRYPT), | ||
| State: utils.Ptr(kms.KEYSTATE_ACTIVE), | ||
| }, | ||
| expected: fmt.Sprintf(` | ||
| ID │ %-37s | ||
| ───────────────┼────────────────────────────────────── | ||
| DISPLAY NAME │ Test Key | ||
| ───────────────┼────────────────────────────────────── | ||
| CREATED AT │ %-37s | ||
| ───────────────┼────────────────────────────────────── | ||
| STATE │ active | ||
| ───────────────┼────────────────────────────────────── | ||
| DESCRIPTION │ very secure and secret key | ||
| ───────────────┼────────────────────────────────────── | ||
| ACCESS SCOPE │ PUBLIC | ||
| ───────────────┼────────────────────────────────────── | ||
| ALGORITHM │ aes_256_gcm | ||
| ───────────────┼────────────────────────────────────── | ||
| DELETION DATE │ | ||
| ───────────────┼────────────────────────────────────── | ||
| IMPORT ONLY │ true | ||
| ───────────────┼────────────────────────────────────── | ||
| KEYRING ID │ %-37s | ||
| ───────────────┼────────────────────────────────────── | ||
| PROTECTION │ software | ||
| ───────────────┼────────────────────────────────────── | ||
| PURPOSE │ symmetric_encrypt_decrypt | ||
| `, | ||
| testKeyID, | ||
| testTime, | ||
| testKeyRingID, | ||
| ), | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| var buf bytes.Buffer | ||
| p.Cmd.SetOut(&buf) | ||
| if err := outputResult(p, tt.outputFmt, tt.keyRing); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| diff := cmp.Diff(buf.String(), tt.expected) | ||
| if diff != "" { | ||
| t.Fatalf("outputResult() output mismatch (-want +got):\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/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/kms/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/kms" | ||
| ) | ||
| const ( | ||
| argKeyID = "KEY_ID" | ||
| flagKeyRingID = "keyring-id" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| KeyID string | ||
| KeyRingID string | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("describe %s", argKeyID), | ||
| Short: "Describe a KMS key", | ||
| Long: "Describe a KMS key", | ||
| Args: args.SingleArg(argKeyID, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Describe a KMS key with ID xxx of keyring yyy`, | ||
| `$ stackit beta kms key describe xxx --keyring-id yyy`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get key: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, resp) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Var(flags.UUIDFlag(), flagKeyRingID, "Key Ring ID") | ||
| err := flags.MarkFlagsRequired(cmd, flagKeyRingID) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, args []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := &inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| KeyID: args[0], | ||
| KeyRingID: flags.FlagToStringValue(p, cmd, flagKeyRingID), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *kms.APIClient) kms.ApiGetKeyRequest { | ||
| return apiClient.GetKey(ctx, model.ProjectId, model.Region, model.KeyRingID, model.KeyID) | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, key *kms.Key) error { | ||
| if key == nil { | ||
| return fmt.Errorf("key response is empty") | ||
| } | ||
| return p.OutputResult(outputFormat, key, func() error { | ||
| table := tables.NewTable() | ||
| table.AddRow("ID", utils.PtrString(key.Id)) | ||
| table.AddSeparator() | ||
| table.AddRow("DISPLAY NAME", utils.PtrString(key.DisplayName)) | ||
| table.AddSeparator() | ||
| table.AddRow("CREATED AT", utils.PtrString(key.CreatedAt)) | ||
| table.AddSeparator() | ||
| table.AddRow("STATE", utils.PtrString(key.State)) | ||
| table.AddSeparator() | ||
| table.AddRow("DESCRIPTION", utils.PtrString(key.Description)) | ||
| table.AddSeparator() | ||
| table.AddRow("ACCESS SCOPE", utils.PtrString(key.AccessScope)) | ||
| table.AddSeparator() | ||
| table.AddRow("ALGORITHM", utils.PtrString(key.Algorithm)) | ||
| table.AddSeparator() | ||
| table.AddRow("DELETION DATE", utils.PtrString(key.DeletionDate)) | ||
| table.AddSeparator() | ||
| table.AddRow("IMPORT ONLY", utils.PtrString(key.ImportOnly)) | ||
| table.AddSeparator() | ||
| table.AddRow("KEYRING ID", utils.PtrString(key.KeyRingId)) | ||
| table.AddSeparator() | ||
| table.AddRow("PROTECTION", utils.PtrString(key.Protection)) | ||
| table.AddSeparator() | ||
| table.AddRow("PURPOSE", utils.PtrString(key.Purpose)) | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("display table: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package describe | ||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "fmt" | ||
| "testing" | ||
| "time" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/kms" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &kms.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| var testKeyRingID = uuid.NewString() | ||
| var testTime = time.Time{} | ||
| const testRegion = "eu01" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| KeyRingID: testKeyRingID, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| argValues: []string{testKeyRingID}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| argValues: []string{}, | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no arg values", | ||
| argValues: []string{}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no flag values", | ||
| argValues: []string{testKeyRingID}, | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid key ring id", | ||
| argValues: []string{"!invalid-uuid"}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "missing project id", | ||
| argValues: []string{testKeyRingID}, | ||
| flagValues: fixtureFlagValues(func(m map[string]string) { delete(m, globalflags.ProjectIdFlag) }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid project id", | ||
| argValues: []string{testKeyRingID}, | ||
| flagValues: fixtureFlagValues(func(m map[string]string) { m[globalflags.ProjectIdFlag] = "invalid-uuid" }), | ||
| 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) { | ||
| got := buildRequest(testCtx, fixtureInputModel(), testClient) | ||
| want := testClient.GetKeyRing(testCtx, testProjectId, testRegion, testKeyRingID) | ||
| diff := cmp.Diff(got, want, | ||
| cmp.AllowUnexported(want), | ||
| cmpopts.EquateComparable(testCtx), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("buildRequest() mismatch (-want +got):\n%s", diff) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| outputFmt string | ||
| keyRing *kms.KeyRing | ||
| wantErr bool | ||
| expected string | ||
| }{ | ||
| { | ||
| description: "empty", | ||
| outputFmt: "table", | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| description: "table format", | ||
| outputFmt: "table", | ||
| keyRing: &kms.KeyRing{ | ||
| Id: utils.Ptr(testKeyRingID), | ||
| DisplayName: utils.Ptr("Test Key Ring"), | ||
| CreatedAt: utils.Ptr(testTime), | ||
| Description: utils.Ptr("This is a test key ring."), | ||
| State: utils.Ptr(kms.KEYRINGSTATE_ACTIVE), | ||
| }, | ||
| expected: fmt.Sprintf(` | ||
| ID │ %-37s | ||
| ──────────────┼────────────────────────────────────── | ||
| DISPLAY NAME │ Test Key Ring | ||
| ──────────────┼────────────────────────────────────── | ||
| CREATED AT │ %-37s | ||
| ──────────────┼────────────────────────────────────── | ||
| STATE │ active | ||
| ──────────────┼────────────────────────────────────── | ||
| DESCRIPTION │ This is a test key ring. | ||
| `, | ||
| testKeyRingID, | ||
| testTime, | ||
| ), | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| var buf bytes.Buffer | ||
| p.Cmd.SetOut(&buf) | ||
| if err := outputResult(p, tt.outputFmt, tt.keyRing); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| diff := cmp.Diff(buf.String(), tt.expected) | ||
| if diff != "" { | ||
| t.Fatalf("outputResult() output mismatch (-want +got):\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/kms/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/kms" | ||
| ) | ||
| const ( | ||
| argKeyRingID = "KEYRING_ID" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| KeyRingID string | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("describe %s", argKeyRingID), | ||
| Short: "Describe a KMS key ring", | ||
| Long: "Describe a KMS key ring", | ||
| Args: args.SingleArg(argKeyRingID, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Describe a KMS key ring with ID xxx`, | ||
| `$ stackit beta kms keyring describe xxx`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get key ring: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, resp) | ||
| }, | ||
| } | ||
| return cmd | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, args []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := &inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| KeyRingID: args[0], | ||
| } | ||
| p.DebugInputModel(model) | ||
| return model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *kms.APIClient) kms.ApiGetKeyRingRequest { | ||
| return apiClient.GetKeyRing(ctx, model.ProjectId, model.Region, model.KeyRingID) | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, keyRing *kms.KeyRing) error { | ||
| if keyRing == nil { | ||
| return fmt.Errorf("key ring response is empty") | ||
| } | ||
| return p.OutputResult(outputFormat, keyRing, func() error { | ||
| table := tables.NewTable() | ||
| table.AddRow("ID", utils.PtrString(keyRing.Id)) | ||
| table.AddSeparator() | ||
| table.AddRow("DISPLAY NAME", utils.PtrString(keyRing.DisplayName)) | ||
| table.AddSeparator() | ||
| table.AddRow("CREATED AT", utils.PtrString(keyRing.CreatedAt)) | ||
| table.AddSeparator() | ||
| table.AddRow("STATE", utils.PtrString(keyRing.State)) | ||
| table.AddSeparator() | ||
| table.AddRow("DESCRIPTION", utils.PtrString(keyRing.Description)) | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("display table: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package describe | ||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "fmt" | ||
| "testing" | ||
| "time" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/kms" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &kms.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| var testKeyRingID = uuid.NewString() | ||
| var testWrappingKeyID = uuid.NewString() | ||
| var testTime = time.Time{} | ||
| const testRegion = "eu01" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| flagKeyRingID: testKeyRingID, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| }, | ||
| KeyRingID: testKeyRingID, | ||
| WrappingKeyID: testWrappingKeyID, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| argValues: []string{testWrappingKeyID}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| argValues: []string{}, | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no arg values", | ||
| argValues: []string{}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no flag values", | ||
| argValues: []string{testWrappingKeyID}, | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid key ring id", | ||
| argValues: []string{testWrappingKeyID}, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[flagKeyRingID] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "missing project id", | ||
| argValues: []string{testWrappingKeyID}, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid project id", | ||
| argValues: []string{testWrappingKeyID}, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| 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) { | ||
| got := buildRequest(testCtx, fixtureInputModel(), testClient) | ||
| want := testClient.GetWrappingKey(testCtx, testProjectId, testRegion, testKeyRingID, testWrappingKeyID) | ||
| diff := cmp.Diff(got, want, | ||
| cmp.AllowUnexported(want), | ||
| cmpopts.EquateComparable(testCtx), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("buildRequest() mismatch (-want +got):\n%s", diff) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| outputFmt string | ||
| keyRing *kms.WrappingKey | ||
| wantErr bool | ||
| expected string | ||
| }{ | ||
| { | ||
| description: "empty", | ||
| outputFmt: "table", | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| description: "table format", | ||
| outputFmt: "table", | ||
| keyRing: &kms.WrappingKey{ | ||
| Id: utils.Ptr(testWrappingKeyID), | ||
| DisplayName: utils.Ptr("Test Key Ring"), | ||
| CreatedAt: utils.Ptr(testTime), | ||
| Description: utils.Ptr("This is a test key ring."), | ||
| State: utils.Ptr(kms.WRAPPINGKEYSTATE_ACTIVE), | ||
| AccessScope: utils.Ptr(kms.ACCESSSCOPE_PUBLIC), | ||
| Algorithm: utils.Ptr(kms.WRAPPINGALGORITHM__2048_OAEP_SHA256), | ||
| ExpiresAt: utils.Ptr(testTime), | ||
| KeyRingId: utils.Ptr(testKeyRingID), | ||
| Protection: utils.Ptr(kms.PROTECTION_SOFTWARE), | ||
| PublicKey: utils.Ptr("-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQ...\n-----END PUBLIC KEY-----"), | ||
| Purpose: utils.Ptr(kms.WRAPPINGPURPOSE_ASYMMETRIC_KEY), | ||
| }, | ||
| expected: fmt.Sprintf(` | ||
| ID │ %-46s | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| DISPLAY NAME │ Test Key Ring | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| CREATED AT │ %-46s | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| STATE │ active | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| DESCRIPTION │ This is a test key ring. | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| ACCESS SCOPE │ PUBLIC | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| ALGORITHM │ rsa_2048_oaep_sha256 | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| EXPIRES AT │ %-46s | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| KEYRING ID │ %-46s | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| PROTECTION │ software | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| PUBLIC KEY │ -----BEGIN PUBLIC KEY----- | ||
| │ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQ... | ||
| │ -----END PUBLIC KEY----- | ||
| ──────────────┼─────────────────────────────────────────────── | ||
| PURPOSE │ wrap_asymmetric_key | ||
| `, | ||
| testWrappingKeyID, | ||
| testTime, | ||
| testTime, | ||
| testKeyRingID), | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(¶ms.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| var buf bytes.Buffer | ||
| p.Cmd.SetOut(&buf) | ||
| if err := outputResult(p, tt.outputFmt, tt.keyRing); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| diff := cmp.Diff(buf.String(), tt.expected) | ||
| if diff != "" { | ||
| t.Fatalf("outputResult() output mismatch (-want +got):\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/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/kms/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/kms" | ||
| ) | ||
| const ( | ||
| argWrappingKeyID = "WRAPPING_KEY_ID" | ||
| flagKeyRingID = "keyring-id" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| WrappingKeyID string | ||
| KeyRingID string | ||
| } | ||
| func NewCmd(params *params.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("describe %s", argWrappingKeyID), | ||
| Short: "Describe a KMS wrapping key", | ||
| Long: "Describe a KMS wrapping key", | ||
| Args: args.SingleArg(argWrappingKeyID, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Describe a KMS wrapping key with ID xxx of keyring yyy`, | ||
| `$ stackit beta kms wrappingkey describe xxx --keyring-id yyy`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("get wrapping key: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, resp) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Var(flags.UUIDFlag(), flagKeyRingID, "Key Ring ID") | ||
| err := flags.MarkFlagsRequired(cmd, flagKeyRingID) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, args []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &errors.ProjectIdError{} | ||
| } | ||
| model := &inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| WrappingKeyID: args[0], | ||
| KeyRingID: flags.FlagToStringValue(p, cmd, flagKeyRingID), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *kms.APIClient) kms.ApiGetWrappingKeyRequest { | ||
| return apiClient.GetWrappingKey(ctx, model.ProjectId, model.Region, model.KeyRingID, model.WrappingKeyID) | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, wrappingKey *kms.WrappingKey) error { | ||
| if wrappingKey == nil { | ||
| return fmt.Errorf("wrapping key response is empty") | ||
| } | ||
| return p.OutputResult(outputFormat, wrappingKey, func() error { | ||
| table := tables.NewTable() | ||
| table.AddRow("ID", utils.PtrString(wrappingKey.Id)) | ||
| table.AddSeparator() | ||
| table.AddRow("DISPLAY NAME", utils.PtrString(wrappingKey.DisplayName)) | ||
| table.AddSeparator() | ||
| table.AddRow("CREATED AT", utils.PtrString(wrappingKey.CreatedAt)) | ||
| table.AddSeparator() | ||
| table.AddRow("STATE", utils.PtrString(wrappingKey.State)) | ||
| table.AddSeparator() | ||
| table.AddRow("DESCRIPTION", utils.PtrString(wrappingKey.Description)) | ||
| table.AddSeparator() | ||
| table.AddRow("ACCESS SCOPE", utils.PtrString(wrappingKey.AccessScope)) | ||
| table.AddSeparator() | ||
| table.AddRow("ALGORITHM", utils.PtrString(wrappingKey.Algorithm)) | ||
| table.AddSeparator() | ||
| table.AddRow("EXPIRES AT", utils.PtrString(wrappingKey.ExpiresAt)) | ||
| table.AddSeparator() | ||
| table.AddRow("KEYRING ID", utils.PtrString(wrappingKey.KeyRingId)) | ||
| table.AddSeparator() | ||
| table.AddRow("PROTECTION", utils.PtrString(wrappingKey.Protection)) | ||
| table.AddSeparator() | ||
| table.AddRow("PUBLIC KEY", utils.PtrString(wrappingKey.PublicKey)) | ||
| table.AddSeparator() | ||
| table.AddRow("PURPOSE", utils.PtrString(wrappingKey.Purpose)) | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("display table: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package client | ||
| import ( | ||
| "github.com/spf13/viper" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/config" | ||
| genericclient "github.com/stackitcloud/stackit-cli/internal/pkg/generic-client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/intake" | ||
| ) | ||
| // ConfigureClient creates and configures a new Intake API client | ||
| func ConfigureClient(p *print.Printer, cliVersion string) (*intake.APIClient, error) { | ||
| return genericclient.ConfigureClientGeneric(p, cliVersion, viper.GetString(config.IntakeCustomEndpointKey), true, genericclient.CreateApiClient[*intake.APIClient](intake.NewAPIClient)) | ||
| } |
@@ -20,3 +20,3 @@ name: CI | ||
| - name: Checkout | ||
| uses: actions/checkout@v5 | ||
| uses: actions/checkout@v6 | ||
@@ -51,3 +51,3 @@ - name: Install go | ||
| - name: Checkout | ||
| uses: actions/checkout@v5 | ||
| uses: actions/checkout@v6 | ||
@@ -54,0 +54,0 @@ - name: Check GoReleaser |
@@ -26,3 +26,3 @@ # STACKIT CLI release workflow. | ||
| - name: Checkout | ||
| uses: actions/checkout@v5 | ||
| uses: actions/checkout@v6 | ||
| with: | ||
@@ -110,3 +110,3 @@ # Allow goreleaser to access older tag information. | ||
| - name: Checkout | ||
| uses: actions/checkout@v5 | ||
| uses: actions/checkout@v6 | ||
@@ -149,3 +149,3 @@ # use the artifacts from the "goreleaser" job | ||
| - name: Checkout | ||
| uses: actions/checkout@v5 | ||
| uses: actions/checkout@v6 | ||
@@ -152,0 +152,0 @@ - name: Download artifacts from workflow |
@@ -14,7 +14,7 @@ name: Renovate | ||
| - name: Checkout | ||
| uses: actions/checkout@v5 | ||
| uses: actions/checkout@v6 | ||
| - name: Self-hosted Renovate | ||
| uses: renovatebot/github-action@v44.0.3 | ||
| uses: renovatebot/github-action@v44.0.4 | ||
| with: | ||
| configurationFile: .github/renovate.json | ||
| token: ${{ secrets.RENOVATE_TOKEN }} |
@@ -35,2 +35,3 @@ ## stackit beta kms key | ||
| * [stackit beta kms key delete](./stackit_beta_kms_key_delete.md) - Deletes a KMS key | ||
| * [stackit beta kms key describe](./stackit_beta_kms_key_describe.md) - Describe a KMS key | ||
| * [stackit beta kms key import](./stackit_beta_kms_key_import.md) - Import a KMS key | ||
@@ -37,0 +38,0 @@ * [stackit beta kms key list](./stackit_beta_kms_key_list.md) - List all KMS keys |
@@ -35,3 +35,4 @@ ## stackit beta kms keyring | ||
| * [stackit beta kms keyring delete](./stackit_beta_kms_keyring_delete.md) - Deletes a KMS key ring | ||
| * [stackit beta kms keyring describe](./stackit_beta_kms_keyring_describe.md) - Describe a KMS key ring | ||
| * [stackit beta kms keyring list](./stackit_beta_kms_keyring_list.md) - Lists all KMS key rings | ||
@@ -35,3 +35,4 @@ ## stackit beta kms wrapping-key | ||
| * [stackit beta kms wrapping-key delete](./stackit_beta_kms_wrapping-key_delete.md) - Deletes a KMS wrapping key | ||
| * [stackit beta kms wrapping-key describe](./stackit_beta_kms_wrapping-key_describe.md) - Describe a KMS wrapping key | ||
| * [stackit beta kms wrapping-key list](./stackit_beta_kms_wrapping-key_list.md) - Lists all KMS wrapping keys | ||
@@ -45,4 +45,5 @@ ## stackit beta | ||
| * [stackit beta alb](./stackit_beta_alb.md) - Manages application loadbalancers | ||
| * [stackit beta intake](./stackit_beta_intake.md) - Provides functionality for intake | ||
| * [stackit beta kms](./stackit_beta_kms.md) - Provides functionality for KMS | ||
| * [stackit beta sqlserverflex](./stackit_beta_sqlserverflex.md) - Provides functionality for SQLServer Flex | ||
@@ -39,2 +39,3 @@ ## stackit config set | ||
| --identity-provider-custom-well-known-configuration string Identity Provider well-known OpenID configuration URL, used for user authentication | ||
| --intake-custom-endpoint string Intake API base URL, used in calls to this API | ||
| --kms-custom-endpoint string KMS API base URL, used in calls to this API | ||
@@ -41,0 +42,0 @@ --load-balancer-custom-endpoint string Load Balancer API base URL, used in calls to this API |
@@ -37,2 +37,3 @@ ## stackit config unset | ||
| --identity-provider-custom-well-known-configuration Identity Provider well-known OpenID configuration URL. If unset, uses the default identity provider | ||
| --intake-custom-endpoint Intake API base URL. If unset, uses the default base URL | ||
| --kms-custom-endpoint KMS API base URL. If unset, uses the default base URL | ||
@@ -39,0 +40,0 @@ --load-balancer-custom-endpoint Load Balancer API base URL. If unset, uses the default base URL |
+28
-27
@@ -7,3 +7,3 @@ module github.com/stackitcloud/stackit-cli | ||
| github.com/fatih/color v1.18.0 | ||
| github.com/goccy/go-yaml v1.18.0 | ||
| github.com/goccy/go-yaml v1.19.0 | ||
| github.com/golang-jwt/jwt/v5 v5.3.0 | ||
@@ -13,3 +13,3 @@ github.com/google/go-cmp v0.7.0 | ||
| github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf | ||
| github.com/jedib0t/go-pretty/v6 v6.7.2 | ||
| github.com/jedib0t/go-pretty/v6 v6.7.5 | ||
| github.com/lmittmann/tint v1.1.2 | ||
@@ -20,20 +20,21 @@ github.com/mattn/go-colorable v0.1.14 | ||
| github.com/spf13/viper v1.21.0 | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.19.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/git v0.9.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/iaas v1.2.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.5.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.24.1 | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.20.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/authorization v0.10.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/git v0.9.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/iaas v1.2.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/intake v0.4.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.5.3 | ||
| github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.24.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/postgresflex v1.2.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.18.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/runcommand v1.3.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.13.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.3.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.2.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.11.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/ske v1.4.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.18.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/runcommand v1.3.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.13.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serverbackup v1.3.3 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.2.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.11.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.2.3 | ||
| github.com/stackitcloud/stackit-sdk-go/services/ske v1.5.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.2 | ||
| github.com/zalando/go-keyring v0.2.6 | ||
@@ -248,10 +249,10 @@ golang.org/x/mod v0.30.0 | ||
| github.com/spf13/cast v1.10.0 // indirect | ||
| github.com/stackitcloud/stackit-sdk-go/services/kms v1.1.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.4.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/observability v0.15.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.25.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/redis v0.25.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/kms v1.1.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.4.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/observability v0.15.1 | ||
| github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.25.2 | ||
| github.com/stackitcloud/stackit-sdk-go/services/redis v0.25.2 | ||
| github.com/subosito/gotenv v1.6.0 // indirect | ||
@@ -258,0 +259,0 @@ go.uber.org/multierr v1.11.0 // indirect |
+1
-1
@@ -10,3 +10,3 @@ # This file contains all available configuration options | ||
| # timeout for analysis, e.g. 30s, 5m, default is 1m | ||
| timeout: 5m | ||
| timeout: 10m | ||
| linters-settings: | ||
@@ -13,0 +13,0 @@ goimports: |
@@ -8,2 +8,4 @@ package list | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
@@ -23,9 +25,12 @@ "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| var ( | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &alb.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| testRegion = "eu01" | ||
| testLimit int64 = 10 | ||
| testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| testClient = &alb.APIClient{} | ||
| testProjectId = uuid.NewString() | ||
| ) | ||
| const ( | ||
| testRegion = "eu01" | ||
| testLimit int64 = 10 | ||
| ) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
@@ -46,3 +51,3 @@ flagValues := map[string]string{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ProjectId: testProjectId, Region: testRegion, Verbosity: globalflags.VerbosityDefault}, | ||
| Limit: &testLimit, | ||
| Limit: utils.Ptr(testLimit), | ||
| } | ||
@@ -142,2 +147,3 @@ for _, mod := range mods { | ||
| outputFormat string | ||
| projectLabel string | ||
| items []alb.LoadBalancer | ||
@@ -171,3 +177,3 @@ } | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.items); (err != nil) != tt.wantErr { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.projectLabel, tt.args.items); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
@@ -174,0 +180,0 @@ } |
@@ -28,4 +28,3 @@ package list | ||
| const ( | ||
| labelSelectorFlag = "label-selector" | ||
| limitFlag = "limit" | ||
| limitFlag = "limit" | ||
| ) | ||
@@ -77,15 +76,10 @@ | ||
| } | ||
| items := response.GetLoadBalancers() | ||
| if items := response.LoadBalancers; items == nil || len(*items) == 0 { | ||
| params.Printer.Info("No load balancers found for project %q", projectLabel) | ||
| } else { | ||
| if model.Limit != nil && len(*items) > int(*model.Limit) { | ||
| *items = (*items)[:*model.Limit] | ||
| } | ||
| if err := outputResult(params.Printer, model.OutputFormat, *items); err != nil { | ||
| return fmt.Errorf("output loadbalancers: %w", err) | ||
| } | ||
| // Truncate output | ||
| if model.Limit != nil && len(items) > int(*model.Limit) { | ||
| items = items[:*model.Limit] | ||
| } | ||
| return nil | ||
| return outputResult(params.Printer, model.OutputFormat, projectLabel, items) | ||
| }, | ||
@@ -130,4 +124,9 @@ } | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, items []alb.LoadBalancer) error { | ||
| func outputResult(p *print.Printer, outputFormat, projectLabel string, items []alb.LoadBalancer) error { | ||
| return p.OutputResult(outputFormat, items, func() error { | ||
| if len(items) == 0 { | ||
| p.Outputf("No load balancers found for project %q", projectLabel) | ||
| return nil | ||
| } | ||
| table := tables.NewTable() | ||
@@ -134,0 +133,0 @@ table.SetHeader("NAME", "EXTERNAL ADDRESS", "REGION", "STATUS", "VERSION", "ERRORS") |
@@ -71,9 +71,5 @@ package list | ||
| } | ||
| items := resp.GetCredentials() | ||
| if resp.Credentials == nil || len(*resp.Credentials) == 0 { | ||
| params.Printer.Info("No credentials found\n") | ||
| return nil | ||
| } | ||
| items := *resp.Credentials | ||
| // Truncate output | ||
| if model.Limit != nil && len(items) > int(*model.Limit) { | ||
@@ -120,8 +116,8 @@ items = items[:*model.Limit] | ||
| func outputResult(p *print.Printer, outputFormat string, items []alb.CredentialsResponse) error { | ||
| if items == nil { | ||
| p.Outputln("no credentials found") | ||
| return nil | ||
| } | ||
| return p.OutputResult(outputFormat, items, func() error { | ||
| if len(items) == 0 { | ||
| p.Outputf("No credentials found\n") | ||
| return nil | ||
| } | ||
| return p.OutputResult(outputFormat, items, func() error { | ||
| table := tables.NewTable() | ||
@@ -128,0 +124,0 @@ table.SetHeader("CREDENTIAL REF", "DISPLAYNAME", "USERNAME", "REGION") |
@@ -24,5 +24,6 @@ package plans | ||
| testProjectId = uuid.NewString() | ||
| testRegion = "eu01" | ||
| ) | ||
| const testRegion = "eu01" | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
@@ -136,2 +137,3 @@ flagValues := map[string]string{ | ||
| outputFormat string | ||
| projectLabel string | ||
| items []alb.PlanDetails | ||
@@ -165,3 +167,3 @@ } | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.items); (err != nil) != tt.wantErr { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.projectLabel, tt.args.items); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
@@ -168,0 +170,0 @@ } |
@@ -65,12 +65,5 @@ package plans | ||
| } | ||
| items := response.GetValidPlans() | ||
| if items := response.ValidPlans; items == nil || len(*items) == 0 { | ||
| params.Printer.Info("No plans found for project %q", projectLabel) | ||
| } else { | ||
| if err := outputResult(params.Printer, model.OutputFormat, *items); err != nil { | ||
| return fmt.Errorf("output plans: %w", err) | ||
| } | ||
| } | ||
| return nil | ||
| return outputResult(params.Printer, model.OutputFormat, projectLabel, items) | ||
| }, | ||
@@ -102,4 +95,9 @@ } | ||
| func outputResult(p *print.Printer, outputFormat string, items []alb.PlanDetails) error { | ||
| func outputResult(p *print.Printer, outputFormat, projectLabel string, items []alb.PlanDetails) error { | ||
| return p.OutputResult(outputFormat, items, func() error { | ||
| if len(items) == 0 { | ||
| p.Outputf("No plans found for project %q", projectLabel) | ||
| return nil | ||
| } | ||
| table := tables.NewTable() | ||
@@ -106,0 +104,0 @@ table.SetHeader("PLAN ID", "NAME", "FLAVOR", "MAX CONNS", "DESCRIPTION") |
@@ -7,2 +7,3 @@ package beta | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/alb" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms" | ||
@@ -43,3 +44,4 @@ "github.com/stackitcloud/stackit-cli/internal/cmd/beta/sqlserverflex" | ||
| cmd.AddCommand(alb.NewCmd(params)) | ||
| cmd.AddCommand(intake.NewCmd(params)) | ||
| cmd.AddCommand(kms.NewCmd(params)) | ||
| } |
@@ -6,2 +6,3 @@ package key | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/key/delete" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/key/describe" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/key/importKey" | ||
@@ -37,2 +38,3 @@ "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/key/list" | ||
| cmd.AddCommand(rotate.NewCmd(params)) | ||
| cmd.AddCommand(describe.NewCmd(params)) | ||
| } |
@@ -6,2 +6,3 @@ package keyring | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/keyring/delete" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/keyring/describe" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/keyring/list" | ||
@@ -31,2 +32,3 @@ "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| cmd.AddCommand(create.NewCmd(params)) | ||
| cmd.AddCommand(describe.NewCmd(params)) | ||
| } |
@@ -6,2 +6,3 @@ package wrappingkey | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/wrappingkey/delete" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/wrappingkey/describe" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/beta/kms/wrappingkey/list" | ||
@@ -31,2 +32,3 @@ "github.com/stackitcloud/stackit-cli/internal/cmd/params" | ||
| cmd.AddCommand(create.NewCmd(params)) | ||
| cmd.AddCommand(describe.NewCmd(params)) | ||
| } |
@@ -50,2 +50,3 @@ package set | ||
| tokenCustomEndpointFlag = "token-custom-endpoint" | ||
| intakeCustomEndpointFlag = "intake-custom-endpoint" | ||
| ) | ||
@@ -165,2 +166,3 @@ | ||
| cmd.Flags().String(tokenCustomEndpointFlag, "", "Custom token endpoint of the Service Account API, which is used to request access tokens when the service account authentication is activated. Not relevant for user authentication.") | ||
| cmd.Flags().String(intakeCustomEndpointFlag, "", "Intake API base URL, used in calls to this API") | ||
@@ -224,2 +226,4 @@ err := viper.BindPFlag(config.SessionTimeLimitKey, cmd.Flags().Lookup(sessionTimeLimitFlag)) | ||
| cobra.CheckErr(err) | ||
| err = viper.BindPFlag(config.IntakeCustomEndpointKey, cmd.Flags().Lookup(intakeCustomEndpointFlag)) | ||
| cobra.CheckErr(err) | ||
| } | ||
@@ -226,0 +230,0 @@ |
@@ -47,2 +47,3 @@ package unset | ||
| tokenCustomEndpointFlag: true, | ||
| intakeCustomEndpointFlag: true, | ||
| } | ||
@@ -88,2 +89,3 @@ for _, mod := range mods { | ||
| TokenCustomEndpoint: true, | ||
| IntakeCustomEndpoint: true, | ||
| } | ||
@@ -145,2 +147,3 @@ for _, mod := range mods { | ||
| model.TokenCustomEndpoint = false | ||
| model.IntakeCustomEndpoint = false | ||
| }), | ||
@@ -147,0 +150,0 @@ }, |
@@ -54,2 +54,3 @@ package unset | ||
| tokenCustomEndpointFlag = "token-custom-endpoint" | ||
| intakeCustomEndpointFlag = "intake-custom-endpoint" | ||
| ) | ||
@@ -93,2 +94,3 @@ | ||
| TokenCustomEndpoint bool | ||
| IntakeCustomEndpoint bool | ||
| } | ||
@@ -217,2 +219,5 @@ | ||
| } | ||
| if model.IntakeCustomEndpoint { | ||
| viper.Set(config.IntakeCustomEndpointKey, "") | ||
| } | ||
@@ -266,2 +271,3 @@ err := config.Write() | ||
| cmd.Flags().Bool(tokenCustomEndpointFlag, false, "Custom token endpoint of the Service Account API, which is used to request access tokens when the service account authentication is activated. Not relevant for user authentication.") | ||
| cmd.Flags().Bool(intakeCustomEndpointFlag, false, "Intake API base URL. If unset, uses the default base URL") | ||
| } | ||
@@ -306,2 +312,3 @@ | ||
| TokenCustomEndpoint: flags.FlagToBoolValue(p, cmd, tokenCustomEndpointFlag), | ||
| IntakeCustomEndpoint: flags.FlagToBoolValue(p, cmd, intakeCustomEndpointFlag), | ||
| } | ||
@@ -308,0 +315,0 @@ |
@@ -248,4 +248,4 @@ package auth | ||
| // Print the link | ||
| p.Outputln("Your browser has been opened to visit:\n") | ||
| p.Outputf("%s\n\n", authorizationURL) | ||
| p.Info("Your browser has been opened to visit:\n\n") | ||
| p.Info("%s\n\n", authorizationURL) | ||
@@ -252,0 +252,0 @@ // Start the blocking web server loop |
@@ -51,2 +51,3 @@ package config | ||
| GitCustomEndpointKey = "git_custom_endpoint" | ||
| IntakeCustomEndpointKey = "intake_custom_endpoint" | ||
@@ -112,2 +113,3 @@ ProjectNameKey = "project_name" | ||
| GitCustomEndpointKey, | ||
| IntakeCustomEndpointKey, | ||
| AlbCustomEndpoint, | ||
@@ -200,2 +202,3 @@ } | ||
| viper.SetDefault(GitCustomEndpointKey, "") | ||
| viper.SetDefault(IntakeCustomEndpointKey, "") | ||
| viper.SetDefault(AlbCustomEndpoint, "") | ||
@@ -202,0 +205,0 @@ } |
Sorry, the diff of this file is too big to display