From bb2ed83f9552f862dfb54dcdb1b1b6448b414309 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Tue, 2 Sep 2025 10:43:21 +0200 Subject: [PATCH 01/22] Onboard Intake (1/3): introduce intake runner command --- docs/stackit.md | 1 + docs/stackit_intake.md | 41 +++ docs/stackit_intake_runner.md | 38 +++ docs/stackit_intake_runner_create.md | 48 +++ docs/stackit_intake_runner_delete.md | 40 +++ docs/stackit_intake_runner_describe.md | 43 +++ docs/stackit_intake_runner_list.md | 47 +++ docs/stackit_intake_runner_update.md | 51 +++ go.mod | 1 + internal/cmd/intake/common/util.go | 27 ++ internal/cmd/intake/intake.go | 31 ++ internal/cmd/intake/runner/create/create.go | 175 +++++++++++ .../cmd/intake/runner/create/create_test.go | 297 ++++++++++++++++++ internal/cmd/intake/runner/delete/delete.go | 105 +++++++ .../cmd/intake/runner/delete/delete_test.go | 200 ++++++++++++ .../cmd/intake/runner/describe/describe.go | 142 +++++++++ .../intake/runner/describe/describe_test.go | 235 ++++++++++++++ internal/cmd/intake/runner/list/list.go | 175 +++++++++++ internal/cmd/intake/runner/list/list_test.go | 221 +++++++++++++ internal/cmd/intake/runner/runner.go | 31 ++ internal/cmd/intake/runner/update/update.go | 172 ++++++++++ .../cmd/intake/runner/update/update_test.go | 260 +++++++++++++++ internal/cmd/root.go | 2 + internal/pkg/config/config.go | 3 + internal/pkg/services/intake/client/client.go | 46 +++ 25 files changed, 2432 insertions(+) create mode 100644 docs/stackit_intake.md create mode 100644 docs/stackit_intake_runner.md create mode 100644 docs/stackit_intake_runner_create.md create mode 100644 docs/stackit_intake_runner_delete.md create mode 100644 docs/stackit_intake_runner_describe.md create mode 100644 docs/stackit_intake_runner_list.md create mode 100644 docs/stackit_intake_runner_update.md create mode 100644 internal/cmd/intake/common/util.go create mode 100644 internal/cmd/intake/intake.go create mode 100644 internal/cmd/intake/runner/create/create.go create mode 100644 internal/cmd/intake/runner/create/create_test.go create mode 100644 internal/cmd/intake/runner/delete/delete.go create mode 100644 internal/cmd/intake/runner/delete/delete_test.go create mode 100644 internal/cmd/intake/runner/describe/describe.go create mode 100644 internal/cmd/intake/runner/describe/describe_test.go create mode 100644 internal/cmd/intake/runner/list/list.go create mode 100644 internal/cmd/intake/runner/list/list_test.go create mode 100644 internal/cmd/intake/runner/runner.go create mode 100644 internal/cmd/intake/runner/update/update.go create mode 100644 internal/cmd/intake/runner/update/update_test.go create mode 100644 internal/pkg/services/intake/client/client.go diff --git a/docs/stackit.md b/docs/stackit.md index d0ddc4554..c4cace3ef 100644 --- a/docs/stackit.md +++ b/docs/stackit.md @@ -34,6 +34,7 @@ stackit [flags] * [stackit dns](./stackit_dns.md) - Provides functionality for DNS * [stackit git](./stackit_git.md) - Provides functionality for STACKIT Git * [stackit image](./stackit_image.md) - Manage server images +* [stackit intake](./stackit_intake.md) - Provides functionality for STACKIT Intake * [stackit key-pair](./stackit_key-pair.md) - Provides functionality for SSH key pairs * [stackit load-balancer](./stackit_load-balancer.md) - Provides functionality for Load Balancer * [stackit logme](./stackit_logme.md) - Provides functionality for LogMe diff --git a/docs/stackit_intake.md b/docs/stackit_intake.md new file mode 100644 index 000000000..1a92caf16 --- /dev/null +++ b/docs/stackit_intake.md @@ -0,0 +1,41 @@ +## stackit intake + +Provides functionality for STACKIT Intake + +### Synopsis + +Provides functionality for STACKIT Intake. + +``` +stackit intake [flags] +``` + +### Examples + +``` + + $ stackit intake +``` + +### Options + +``` + -h, --help Help for "stackit 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](./stackit.md) - Manage STACKIT resources using the command line +* [stackit intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners + diff --git a/docs/stackit_intake_runner.md b/docs/stackit_intake_runner.md new file mode 100644 index 000000000..7acd9dcda --- /dev/null +++ b/docs/stackit_intake_runner.md @@ -0,0 +1,38 @@ +## stackit intake runner + +Provides functionality for Intake Runners + +### Synopsis + +Provides functionality for Intake Runners. + +``` +stackit intake runner [flags] +``` + +### Options + +``` + -h, --help Help for "stackit 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 intake](./stackit_intake.md) - Provides functionality for STACKIT Intake +* [stackit intake runner create](./stackit_intake_runner_create.md) - Creates a new Intake Runner +* [stackit intake runner delete](./stackit_intake_runner_delete.md) - Deletes an Intake Runner +* [stackit intake runner describe](./stackit_intake_runner_describe.md) - Shows details of an Intake Runner +* [stackit intake runner list](./stackit_intake_runner_list.md) - Lists all Intake Runners +* [stackit intake runner update](./stackit_intake_runner_update.md) - Updates an Intake Runner + diff --git a/docs/stackit_intake_runner_create.md b/docs/stackit_intake_runner_create.md new file mode 100644 index 000000000..607afe062 --- /dev/null +++ b/docs/stackit_intake_runner_create.md @@ -0,0 +1,48 @@ +## stackit intake runner create + +Creates a new Intake Runner + +### Synopsis + +Creates a new Intake Runner. + +``` +stackit intake runner create [flags] +``` + +### Examples + +``` + Create a new Intake Runner with a display name and message capacity limits + $ stackit 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 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 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 intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners + diff --git a/docs/stackit_intake_runner_delete.md b/docs/stackit_intake_runner_delete.md new file mode 100644 index 000000000..98ddfd193 --- /dev/null +++ b/docs/stackit_intake_runner_delete.md @@ -0,0 +1,40 @@ +## stackit intake runner delete + +Deletes an Intake Runner + +### Synopsis + +Deletes an Intake Runner. + +``` +stackit intake runner delete RUNNER_ID [flags] +``` + +### Examples + +``` + Delete an Intake Runner with ID "xxx" + $ stackit intake runner delete xxx +``` + +### Options + +``` + -h, --help Help for "stackit 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 intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners + diff --git a/docs/stackit_intake_runner_describe.md b/docs/stackit_intake_runner_describe.md new file mode 100644 index 000000000..f15b5a674 --- /dev/null +++ b/docs/stackit_intake_runner_describe.md @@ -0,0 +1,43 @@ +## stackit intake runner describe + +Shows details of an Intake Runner + +### Synopsis + +Shows details of an Intake Runner. + +``` +stackit intake runner describe RUNNER_ID [flags] +``` + +### Examples + +``` + Get details of an Intake Runner with ID "xxx" + $ stackit intake runner describe xxx + + Get details of an Intake Runner with ID "xxx" in JSON format + $ stackit intake runner describe xxx --output-format json +``` + +### Options + +``` + -h, --help Help for "stackit 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 intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners + diff --git a/docs/stackit_intake_runner_list.md b/docs/stackit_intake_runner_list.md new file mode 100644 index 000000000..1a6ee9cf5 --- /dev/null +++ b/docs/stackit_intake_runner_list.md @@ -0,0 +1,47 @@ +## stackit intake runner list + +Lists all Intake Runners + +### Synopsis + +Lists all Intake Runners for the current project. + +``` +stackit intake runner list [flags] +``` + +### Examples + +``` + List all Intake Runners + $ stackit intake runner list + + List all Intake Runners in JSON format + $ stackit intake runner list --output-format json + + List up to 5 Intake Runners + $ stackit intake runner list --limit 5 +``` + +### Options + +``` + -h, --help Help for "stackit 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 intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners + diff --git a/docs/stackit_intake_runner_update.md b/docs/stackit_intake_runner_update.md new file mode 100644 index 000000000..6ed3f9cc8 --- /dev/null +++ b/docs/stackit_intake_runner_update.md @@ -0,0 +1,51 @@ +## stackit intake runner update + +Updates an Intake Runner + +### Synopsis + +Updates an Intake Runner. Only the specified fields are updated. + +``` +stackit intake runner update RUNNER_ID [flags] +``` + +### Examples + +``` + Update the display name of an Intake Runner with ID "xxx" + $ stackit intake runner update xxx --display-name "new-runner-name" + + Update the message capacity limits for an Intake Runner with ID "xxx" + $ stackit intake runner update xxx --max-message-size-kib 2000 --max-messages-per-hour 10000 + + Clear the labels of an Intake Runner with ID "xxx" by providing an empty value + $ stackit intake runner update xxx --labels "" +``` + +### Options + +``` + --description string Description + --display-name string Display name + -h, --help Help for "stackit intake runner update" + --labels string Labels in key=value format. To clear all labels, provide an empty string, e.g. --labels "" + --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 intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners + diff --git a/go.mod b/go.mod index e95d8cf67..0e0a3815a 100644 --- a/go.mod +++ b/go.mod @@ -181,6 +181,7 @@ require ( github.com/sonatard/noctx v0.1.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect + github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.0 // indirect github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.11.1 // indirect diff --git a/internal/cmd/intake/common/util.go b/internal/cmd/intake/common/util.go new file mode 100644 index 000000000..8d4ec77b2 --- /dev/null +++ b/internal/cmd/intake/common/util.go @@ -0,0 +1,27 @@ +package common + +import ( + "fmt" + "strings" +) + +// ParseLabels parses the labels flag value into a map. +// An empty string clears the labels, returning a pointer to an empty map. +func ParseLabels(labelsVal string) (map[string]string, error) { + if labelsVal == "" { + // User wants to clear labels + return map[string]string{}, nil + } + + // User provided labels, parse them + parsedLabels := make(map[string]string) + pairs := strings.Split(labelsVal, ",") + for _, pair := range pairs { + kv := strings.SplitN(pair, "=", 2) + if len(kv) != 2 || kv[0] == "" { + return nil, fmt.Errorf("invalid label format, expected key=value: %q", pair) + } + parsedLabels[kv[0]] = kv[1] + } + return parsedLabels, nil +} diff --git a/internal/cmd/intake/intake.go b/internal/cmd/intake/intake.go new file mode 100644 index 000000000..b9db32826 --- /dev/null +++ b/internal/cmd/intake/intake.go @@ -0,0 +1,31 @@ +package intake + +import ( + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-cli/internal/cmd/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/examples" + "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 STACKIT Intake", + Long: "Provides functionality for STACKIT Intake.", + Args: args.NoArgs, + Example: examples.Build( + examples.NewExample( + ``, + "$ stackit intake"), + ), + Run: utils.CmdHelp, + } + + // Sub-commands + cmd.AddCommand(runner.NewCmd(params)) + + return cmd +} diff --git a/internal/cmd/intake/runner/create/create.go b/internal/cmd/intake/runner/create/create.go new file mode 100644 index 000000000..47a737b33 --- /dev/null +++ b/internal/cmd/intake/runner/create/create.go @@ -0,0 +1,175 @@ +package create + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/goccy/go-yaml" + "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/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" +) + +const ( + displayNameFlag = "display-name" + maxMessageSizeKiBFlag = "max-message-size-kib" + maxMessagesPerHourFlag = "max-messages-per-hour" + descriptionFlag = "description" + labelsFlag = "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 NewCreateCmd(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 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 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) + 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) + } + runnerId := *resp.Id + + return outputResult(p.Printer, model.OutputFormat, projectLabel, runnerId, 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(labelsFlag, 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, labelsFlag), + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *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, outputFormat, projectLabel, runnerId string, resp *intake.IntakeRunnerResponse) error { + switch outputFormat { + case print.JSONOutputFormat: + details, err := json.MarshalIndent(resp, "", " ") + if err != nil { + return fmt.Errorf("marshal instance: %w", err) + } + p.Outputln(string(details)) + + return nil + case print.YAMLOutputFormat: + details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) + if err != nil { + return fmt.Errorf("marshal instance: %w", err) + } + p.Outputln(string(details)) + + return nil + default: + p.Outputf("Created Intake Runner for project %q. Runner ID: %s\n", projectLabel, runnerId) + return nil + } +} diff --git a/internal/cmd/intake/runner/create/create_test.go b/internal/cmd/intake/runner/create/create_test.go new file mode 100644 index 000000000..161b39169 --- /dev/null +++ b/internal/cmd/intake/runner/create/create_test.go @@ -0,0 +1,297 @@ +package create + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-cli/internal/cmd/params" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + "github.com/stackitcloud/stackit-sdk-go/services/intake" +) + +// Define a unique key for the context to avoid collisions +type testCtxKey struct{} + +var ( + // testCtx dummy context for testing purposes + testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") + // testClient mock API client + testClient = &intake.APIClient{} + testProjectId = uuid.NewString() + testRegion = "eu01" + + // Define test values for flags + testDisplayName = "testrunner" + testMaxMessageSizeKiB = int64(1024) + testMaxMessagesPerHour = int64(10000) + testDescription = "This is a test runner" + testLabels = map[string]string{"env": "test", "team": "dev"} + testLabelsString = "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, + labelsFlag: 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 + 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) { + p := print.NewPrinter() + cmd := NewCreateCmd(¶ms.CmdParams{Printer: p}) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(p, cmd) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %v", err) + } + + if !tt.isValid { + t.Fatalf("did not fail on invalid input") + } + diff := cmp.Diff(model, tt.expectedModel) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest 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 { + outputFormat string + projectLabel string + runnerId string + resp *intake.IntakeRunnerResponse + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "default output", + args: args{outputFormat: "default", projectLabel: "my-project", runnerId: "runner-id-123", resp: &intake.IntakeRunnerResponse{}}, + wantErr: false, + }, + { + name: "json output", + args: args{outputFormat: print.JSONOutputFormat, resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}}, + wantErr: false, + }, + { + name: "yaml output", + args: args{outputFormat: print.YAMLOutputFormat, resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}}, + wantErr: false, + }, + { + name: "nil response", + args: args{outputFormat: print.JSONOutputFormat, resp: nil}, + wantErr: false, + }, + } + p := print.NewPrinter() + p.Cmd = NewCreateCmd(¶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.projectLabel, tt.args.runnerId, tt.args.resp); (err != nil) != tt.wantErr { + t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/internal/cmd/intake/runner/delete/delete.go b/internal/cmd/intake/runner/delete/delete.go new file mode 100644 index 000000000..c80b6dfba --- /dev/null +++ b/internal/cmd/intake/runner/delete/delete.go @@ -0,0 +1,105 @@ +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/utils" + "github.com/stackitcloud/stackit-sdk-go/services/intake" +) + +const ( + runnerIdArg = "RUNNER_ID" +) + +// inputModel struct holds all the input parameters for the command +type inputModel struct { + *globalflags.GlobalFlagModel + RunnerId string +} + +// NewDeleteCmd creates a new cobra command for deleting an Intake Runner +func NewDeleteCmd(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 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) + 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) + } + + p.Printer.Info("Deletion request for Intake Runner %q sent successfully.\n", 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, + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} + +// 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 +} diff --git a/internal/cmd/intake/runner/delete/delete_test.go b/internal/cmd/intake/runner/delete/delete_test.go new file mode 100644 index 000000000..c1d7c6d72 --- /dev/null +++ b/internal/cmd/intake/runner/delete/delete_test.go @@ -0,0 +1,200 @@ +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/cmd/params" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-sdk-go/services/intake" +) + +// Define a unique key for the context to avoid collisions +type testCtxKey struct{} + +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() + testRegion = "eu01" +) + +// 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) { + p := print.NewPrinter() + cmd := NewDeleteCmd(¶ms.CmdParams{Printer: p}) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateArgs(tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating args: %v", err) + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(p, cmd, tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing input: %v", err) + } + + if !tt.isValid { + t.Fatalf("did not fail on invalid input") + } + diff := cmp.Diff(model, tt.expectedModel) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest 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) + } + }) + } +} diff --git a/internal/cmd/intake/runner/describe/describe.go b/internal/cmd/intake/runner/describe/describe.go new file mode 100644 index 000000000..d2cb1ecdb --- /dev/null +++ b/internal/cmd/intake/runner/describe/describe.go @@ -0,0 +1,142 @@ +package describe + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/goccy/go-yaml" + "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 NewDescribeCmd(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 intake runner describe xxx`), + examples.NewExample( + `Get details of an Intake Runner with ID "xxx" in JSON format`, + `$ stackit 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) + 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, + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} + +// 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") + } + + switch outputFormat { + case print.JSONOutputFormat: + details, err := json.MarshalIndent(runner, "", " ") + if err != nil { + return fmt.Errorf("marshal Intake Runner: %w", err) + } + p.Outputln(string(details)) + return nil + + case print.YAMLOutputFormat: + details, err := yaml.MarshalWithOptions(runner, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) + if err != nil { + return fmt.Errorf("marshal Intake Runner: %w", err) + } + p.Outputln(string(details)) + return nil + + default: + table := tables.NewTable() + table.SetHeader("Attribute", "Value") + table.AddRow("ID", runner.GetId()) + table.AddRow("Name", runner.GetDisplayName()) + 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 + } +} diff --git a/internal/cmd/intake/runner/describe/describe_test.go b/internal/cmd/intake/runner/describe/describe_test.go new file mode 100644 index 000000000..54553b2b1 --- /dev/null +++ b/internal/cmd/intake/runner/describe/describe_test.go @@ -0,0 +1,235 @@ +package describe + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-cli/internal/cmd/params" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-sdk-go/services/intake" +) + +type testCtxKey struct{} + +var ( + testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") + testClient = &intake.APIClient{} + testProjectId = uuid.NewString() + testRunnerId = uuid.NewString() + testRegion = "eu01" +) + +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) { + p := print.NewPrinter() + cmd := NewDescribeCmd(¶ms.CmdParams{Printer: p}) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateArgs(tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating args: %v", err) + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(p, cmd, tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing input: %v", err) + } + + if !tt.isValid { + t.Fatalf("did not fail on invalid input") + } + diff := cmp.Diff(model, tt.expectedModel) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest 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 = NewDescribeCmd(¶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) + } + }) + } +} diff --git a/internal/cmd/intake/runner/list/list.go b/internal/cmd/intake/runner/list/list.go new file mode 100644 index 000000000..791bc3cc4 --- /dev/null +++ b/internal/cmd/intake/runner/list/list.go @@ -0,0 +1,175 @@ +package list + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/goccy/go-yaml" + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-cli/internal/cmd/params" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/flags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/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 +} + +// NewListCmd creates a new cobra command for listing Intake Runners +func NewListCmd(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 intake runner list`), + examples.NewExample( + `List all Intake Runners in JSON format`, + `$ stackit intake runner list --output-format json`), + examples.NewExample( + `List up to 5 Intake Runners`, + `$ stackit 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) + 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() + 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) + projectLabel = model.ProjectId + } + p.Printer.Info("No Intake Runners found for project %q\n", projectLabel) + return nil + } + + // Truncate output + if model.Limit != nil && len(runners) > int(*model.Limit) { + runners = runners[:*model.Limit] + } + + return outputResult(p.Printer, model.OutputFormat, 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, + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} + +// 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 string, runners []intake.IntakeRunnerResponse) error { + switch outputFormat { + case print.JSONOutputFormat: + details, err := json.MarshalIndent(runners, "", " ") + if err != nil { + return fmt.Errorf("marshal Intake Runner list: %w", err) + } + p.Outputln(string(details)) + return nil + + case print.YAMLOutputFormat: + details, err := yaml.MarshalWithOptions(runners, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) + if err != nil { + return fmt.Errorf("marshal Intake Runner list: %w", err) + } + p.Outputln(string(details)) + return nil + + default: + table := tables.NewTable() + + table.SetHeader("ID", "NAME") + for i := range runners { + runner := runners[i] + table.AddRow( + runner.GetId(), + runner.GetDisplayName(), + ) + } + err := table.Display(p) + if err != nil { + return fmt.Errorf("render table: %w", err) + } + return nil + } +} diff --git a/internal/cmd/intake/runner/list/list_test.go b/internal/cmd/intake/runner/list/list_test.go new file mode 100644 index 000000000..f291d283f --- /dev/null +++ b/internal/cmd/intake/runner/list/list_test.go @@ -0,0 +1,221 @@ +package list + +import ( + "context" + "strconv" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-cli/internal/cmd/params" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + "github.com/stackitcloud/stackit-sdk-go/services/intake" +) + +type testCtxKey struct{} + +var ( + testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") + testClient = &intake.APIClient{} + testProjectId = uuid.NewString() + testRegion = "eu01" + testLimit = int64(5) +) + +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 + 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) { + p := print.NewPrinter() + cmd := NewListCmd(¶ms.CmdParams{Printer: p}) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(p, cmd) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing flags: %v", err) + } + + if !tt.isValid { + t.Fatalf("did not fail on invalid input") + } + diff := cmp.Diff(model, tt.expectedModel) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest 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, + }, + } + p := print.NewPrinter() + p.Cmd = NewListCmd(¶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.runners); (err != nil) != tt.wantErr { + t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/internal/cmd/intake/runner/runner.go b/internal/cmd/intake/runner/runner.go new file mode 100644 index 000000000..33d1e4861 --- /dev/null +++ b/internal/cmd/intake/runner/runner.go @@ -0,0 +1,31 @@ +package runner + +import ( + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner/create" + "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner/delete" + "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner/describe" + "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner/list" + "github.com/stackitcloud/stackit-cli/internal/cmd/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.NewCreateCmd(params)) + cmd.AddCommand(delete.NewDeleteCmd(params)) + cmd.AddCommand(describe.NewDescribeCmd(params)) + cmd.AddCommand(list.NewListCmd(params)) + cmd.AddCommand(update.NewUpdateCmd(params)) + + return cmd +} diff --git a/internal/cmd/intake/runner/update/update.go b/internal/cmd/intake/runner/update/update.go new file mode 100644 index 000000000..68fa53529 --- /dev/null +++ b/internal/cmd/intake/runner/update/update.go @@ -0,0 +1,172 @@ +package update + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/stackitcloud/stackit-cli/internal/cmd/intake/common" + "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/services/intake/client" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + "github.com/stackitcloud/stackit-sdk-go/services/intake" +) + +const ( + runnerIdArg = "RUNNER_ID" +) + +const ( + displayNameFlag = "display-name" + maxMessageSizeKiBFlag = "max-message-size-kib" + maxMessagesPerHourFlag = "max-messages-per-hour" + descriptionFlag = "description" + labelsFlag = "labels" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + RunnerId string + DisplayName *string + MaxMessageSizeKiB *int64 + MaxMessagesPerHour *int64 + Description *string + Labels *map[string]string +} + +func NewUpdateCmd(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 intake runner update xxx --display-name "new-runner-name"`), + examples.NewExample( + `Update the message capacity limits for an Intake Runner with ID "xxx"`, + `$ stackit intake runner update xxx --max-message-size-kib 2000 --max-messages-per-hour 10000`), + examples.NewExample( + `Clear the labels of an Intake Runner with ID "xxx" by providing an empty value`, + `$ stackit intake runner update xxx --labels ""`), + ), + 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) + if err != nil { + return err + } + + // Call API + req := buildRequest(ctx, model, apiClient) + if err := req.Execute(); err != nil { + return fmt.Errorf("update Intake Runner: %w", err) + } + + p.Printer.Info("Update request for Intake Runner %q sent successfully.\n", model.RunnerId) + return nil + }, + } + 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().String(labelsFlag, "", "Labels in key=value format. To clear all labels, provide an empty string, e.g. --labels \"\"") +} + +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{} + } + + var labels *map[string]string + if cmd.Flags().Changed(labelsFlag) { + labelsVal, err := cmd.Flags().GetString(labelsFlag) + if err != nil { + return nil, fmt.Errorf("could not parse --%s: %w", labelsFlag, err) + } + if labelsVal == "" { + // User wants to clear labels + labels = &map[string]string{} + } else { + // User provided labels, parse them + parsedLabels, err := common.ParseLabels(labelsVal) + if err != nil { + return nil, err + } + + labels = &parsedLabels + } + } + + 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: labels, + } + + if model.DisplayName == nil && model.MaxMessageSizeKiB == nil && model.MaxMessagesPerHour == nil && model.Description == nil && model.Labels == nil { + return nil, &cliErr.EmptyUpdateError{} + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *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 +} diff --git a/internal/cmd/intake/runner/update/update_test.go b/internal/cmd/intake/runner/update/update_test.go new file mode 100644 index 000000000..f17bf12ff --- /dev/null +++ b/internal/cmd/intake/runner/update/update_test.go @@ -0,0 +1,260 @@ +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/utils" + "github.com/stackitcloud/stackit-sdk-go/services/intake" +) + +type testCtxKey struct{} + +var ( + testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") + testClient = &intake.APIClient{} + testProjectId = uuid.NewString() + testRunnerId = uuid.NewString() + testRegion = "eu01" +) + +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[labelsFlag] = "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: "clear labels", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[labelsFlag] = "" + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.Labels = utils.Ptr(map[string]string{}) + }), + }, + { + 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) { + p := print.NewPrinter() + cmd := NewUpdateCmd(¶ms.CmdParams{Printer: p}) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateArgs(tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating args: %v", err) + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(p, cmd, tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing input: %v", err) + } + + if !tt.isValid { + t.Fatalf("did not fail on invalid input") + } + diff := cmp.Diff(model, tt.expectedModel) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest 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) + } + }) + } +} diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 4e0ac6ea9..9d2566c06 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -14,6 +14,7 @@ import ( "github.com/stackitcloud/stackit-cli/internal/cmd/dns" "github.com/stackitcloud/stackit-cli/internal/cmd/git" "github.com/stackitcloud/stackit-cli/internal/cmd/image" + "github.com/stackitcloud/stackit-cli/internal/cmd/intake" keypair "github.com/stackitcloud/stackit-cli/internal/cmd/key-pair" loadbalancer "github.com/stackitcloud/stackit-cli/internal/cmd/load-balancer" "github.com/stackitcloud/stackit-cli/internal/cmd/logme" @@ -192,6 +193,7 @@ func addSubcommands(cmd *cobra.Command, params *params.CmdParams) { cmd.AddCommand(quota.NewCmd(params)) cmd.AddCommand(affinityGroups.NewCmd(params)) cmd.AddCommand(git.NewCmd(params)) + cmd.AddCommand(intake.NewCmd(params)) } // traverseCommands calls f for c and all of its children. diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 957d7c475..d5e1b38dd 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -46,6 +46,7 @@ const ( IaaSCustomEndpointKey = "iaas_custom_endpoint" TokenCustomEndpointKey = "token_custom_endpoint" GitCustomEndpointKey = "git_custom_endpoint" + IntakeCustomEndpointKey = "intake_custom_endpoint" ProjectNameKey = "project_name" DefaultProfileName = "default" @@ -105,6 +106,7 @@ var ConfigKeys = []string{ IaaSCustomEndpointKey, TokenCustomEndpointKey, GitCustomEndpointKey, + IntakeCustomEndpointKey, } var defaultConfigFolderPath string @@ -190,6 +192,7 @@ func setConfigDefaults() { viper.SetDefault(IaaSCustomEndpointKey, "") viper.SetDefault(TokenCustomEndpointKey, "") viper.SetDefault(GitCustomEndpointKey, "") + viper.SetDefault(IntakeCustomEndpointKey, "") } func getConfigFilePath(configFolder string) string { diff --git a/internal/pkg/services/intake/client/client.go b/internal/pkg/services/intake/client/client.go new file mode 100644 index 000000000..9f4e014cc --- /dev/null +++ b/internal/pkg/services/intake/client/client.go @@ -0,0 +1,46 @@ +package client + +import ( + "github.com/spf13/viper" + "github.com/stackitcloud/stackit-cli/internal/pkg/auth" + "github.com/stackitcloud/stackit-cli/internal/pkg/config" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" + "github.com/stackitcloud/stackit-sdk-go/services/intake" +) + +// ConfigureClient creates and configures a new Intake API client +func ConfigureClient(p *print.Printer) (*intake.APIClient, error) { + authCfgOption, err := auth.AuthenticationConfig(p, auth.AuthorizeUser) + if err != nil { + p.Debug(print.ErrorLevel, "configure authentication: %v", err) + return nil, &errors.AuthError{} + } + + region := viper.GetString(config.RegionKey) + cfgOptions := []sdkConfig.ConfigurationOption{ + sdkConfig.WithRegion(region), + authCfgOption, + } + + customEndpoint := viper.GetString(config.IntakeCustomEndpointKey) + + if customEndpoint != "" { + cfgOptions = append(cfgOptions, sdkConfig.WithEndpoint(customEndpoint)) + } + + if p.IsVerbosityDebug() { + cfgOptions = append(cfgOptions, + sdkConfig.WithMiddleware(print.RequestResponseCapturer(p, nil)), + ) + } + + apiClient, err := intake.NewAPIClient(cfgOptions...) + if err != nil { + p.Debug(print.ErrorLevel, "create new API client: %v", err) + return nil, &errors.AuthError{} + } + + return apiClient, nil +} From a04878f4ef4c3a1ddcfd88d126d3aecfc5e9d1e7 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Fri, 5 Sep 2025 16:56:33 +0200 Subject: [PATCH 02/22] address review comments --- internal/cmd/intake/common/util_test.go | 95 +++++++++++++++++++ internal/cmd/intake/intake.go | 19 ++-- internal/cmd/intake/runner/create/create.go | 8 +- .../cmd/intake/runner/create/create_test.go | 2 +- internal/cmd/intake/runner/delete/delete.go | 2 +- internal/cmd/intake/runner/list/list.go | 2 +- internal/cmd/intake/runner/list/list_test.go | 7 ++ 7 files changed, 116 insertions(+), 19 deletions(-) create mode 100644 internal/cmd/intake/common/util_test.go diff --git a/internal/cmd/intake/common/util_test.go b/internal/cmd/intake/common/util_test.go new file mode 100644 index 000000000..e8c30dc2b --- /dev/null +++ b/internal/cmd/intake/common/util_test.go @@ -0,0 +1,95 @@ +package common + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestParseLabels(t *testing.T) { + tests := []struct { + description string + input string + expectedMap map[string]string + expectError bool + }{ + { + description: "single label", + input: "key1=val1", + expectedMap: map[string]string{"key1": "val1"}, + expectError: false, + }, + { + description: "multiple labels", + input: "key1=val1,key2=val2", + expectedMap: map[string]string{"key1": "val1", "key2": "val2"}, + expectError: false, + }, + { + description: "empty value", + input: "key1=", + expectedMap: map[string]string{"key1": ""}, + expectError: false, + }, + { + description: "value with equals sign", + input: "key1=value=with=equals", + expectedMap: map[string]string{"key1": "value=with=equals"}, + expectError: false, + }, + { + description: "special case: empty string to clear labels", + input: "", + expectedMap: map[string]string{}, // Should be an empty map, not nil + expectError: false, + }, + { + description: "invalid format - no equals", + input: "key1val1", + expectedMap: nil, + expectError: true, + }, + { + description: "invalid format - empty key", + input: "=val1", + expectedMap: nil, + expectError: true, + }, + { + description: "mixed valid and invalid pair", + input: "key1=val1,key2", + expectedMap: nil, + expectError: true, + }, + { + description: "invalid format - leading comma", + input: ",key1=val1", + expectedMap: nil, + expectError: true, + }, + { + description: "invalid format - trailing comma", + input: "key1=val1,", + expectedMap: nil, + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + parsedMap, err := ParseLabels(tt.input) + + if !tt.expectError && err != nil { + t.Fatalf("did not expect an error, but got: %v", err) + } + + if tt.expectError && err == nil { + t.Fatalf("expected an error, but got nil") + } + + if diff := cmp.Diff(tt.expectedMap, parsedMap); diff != "" { + t.Errorf("map mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/internal/cmd/intake/intake.go b/internal/cmd/intake/intake.go index b9db32826..fc5c7e4d1 100644 --- a/internal/cmd/intake/intake.go +++ b/internal/cmd/intake/intake.go @@ -5,7 +5,6 @@ import ( "github.com/stackitcloud/stackit-cli/internal/cmd/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/examples" "github.com/stackitcloud/stackit-cli/internal/pkg/utils" ) @@ -13,19 +12,15 @@ import ( func NewCmd(params *params.CmdParams) *cobra.Command { cmd := &cobra.Command{ Use: "intake", - Short: "Provides functionality for STACKIT Intake", - Long: "Provides functionality for STACKIT Intake.", + Short: "Provides functionality for intake", + Long: "Provides functionality for intake.", Args: args.NoArgs, - Example: examples.Build( - examples.NewExample( - ``, - "$ stackit intake"), - ), - Run: utils.CmdHelp, + Run: utils.CmdHelp, } + addSubcommands(cmd, params) + return cmd +} - // Sub-commands +func addSubcommands(cmd *cobra.Command, params *params.CmdParams) { cmd.AddCommand(runner.NewCmd(params)) - - return cmd } diff --git a/internal/cmd/intake/runner/create/create.go b/internal/cmd/intake/runner/create/create.go index 47a737b33..59caf001c 100644 --- a/internal/cmd/intake/runner/create/create.go +++ b/internal/cmd/intake/runner/create/create.go @@ -18,6 +18,7 @@ import ( "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/utils" ) const ( @@ -85,9 +86,8 @@ func NewCreateCmd(p *params.CmdParams) *cobra.Command { if err != nil { return fmt.Errorf("create Intake Runner: %w", err) } - runnerId := *resp.Id - return outputResult(p.Printer, model.OutputFormat, projectLabel, runnerId, resp) + return outputResult(p.Printer, model.OutputFormat, projectLabel, resp) }, } configureFlags(cmd) @@ -150,7 +150,7 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *intake.APIC return req } -func outputResult(p *print.Printer, outputFormat, projectLabel, runnerId string, resp *intake.IntakeRunnerResponse) error { +func outputResult(p *print.Printer, outputFormat, projectLabel string, resp *intake.IntakeRunnerResponse) error { switch outputFormat { case print.JSONOutputFormat: details, err := json.MarshalIndent(resp, "", " ") @@ -169,7 +169,7 @@ func outputResult(p *print.Printer, outputFormat, projectLabel, runnerId string, return nil default: - p.Outputf("Created Intake Runner for project %q. Runner ID: %s\n", projectLabel, runnerId) + p.Outputf("Created Intake Runner for project %q. Runner ID: %s\n", projectLabel, utils.PtrString(resp.Id)) return nil } } diff --git a/internal/cmd/intake/runner/create/create_test.go b/internal/cmd/intake/runner/create/create_test.go index 161b39169..a140db7ec 100644 --- a/internal/cmd/intake/runner/create/create_test.go +++ b/internal/cmd/intake/runner/create/create_test.go @@ -289,7 +289,7 @@ func TestOutputResult(t *testing.T) { p.Cmd = NewCreateCmd(¶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.projectLabel, tt.args.runnerId, tt.args.resp); (err != nil) != tt.wantErr { + if err := outputResult(p, tt.args.outputFormat, tt.args.projectLabel, tt.args.resp); (err != nil) != tt.wantErr { t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/internal/cmd/intake/runner/delete/delete.go b/internal/cmd/intake/runner/delete/delete.go index c80b6dfba..26743fdbd 100644 --- a/internal/cmd/intake/runner/delete/delete.go +++ b/internal/cmd/intake/runner/delete/delete.go @@ -65,7 +65,7 @@ func NewDeleteCmd(p *params.CmdParams) *cobra.Command { return fmt.Errorf("delete Intake Runner: %w", err) } - p.Printer.Info("Deletion request for Intake Runner %q sent successfully.\n", model.RunnerId) + p.Printer.Outputf("Deletion request for Intake Runner %q sent successfully.\n", model.RunnerId) return nil }, } diff --git a/internal/cmd/intake/runner/list/list.go b/internal/cmd/intake/runner/list/list.go index 791bc3cc4..8e9a0199f 100644 --- a/internal/cmd/intake/runner/list/list.go +++ b/internal/cmd/intake/runner/list/list.go @@ -74,7 +74,7 @@ func NewListCmd(p *params.CmdParams) *cobra.Command { p.Printer.Debug(print.ErrorLevel, "get project name: %v", err) projectLabel = model.ProjectId } - p.Printer.Info("No Intake Runners found for project %q\n", projectLabel) + p.Printer.Outputf("No Intake Runners found for project %q\n", projectLabel) return nil } diff --git a/internal/cmd/intake/runner/list/list_test.go b/internal/cmd/intake/runner/list/list_test.go index f291d283f..86545c0ee 100644 --- a/internal/cmd/intake/runner/list/list_test.go +++ b/internal/cmd/intake/runner/list/list_test.go @@ -208,6 +208,13 @@ func TestOutputResult(t *testing.T) { args: args{runners: nil}, wantErr: false, }, + { + name: "empty intake runner in slice", + args: args{ + runners: []intake.IntakeRunnerResponse{{}}, + }, + wantErr: false, + }, } p := print.NewPrinter() p.Cmd = NewListCmd(¶ms.CmdParams{Printer: p}) From 244a94fb4eb5f439fd2b362fe362b428a2607343 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 8 Sep 2025 11:07:47 +0200 Subject: [PATCH 03/22] address review comments: fix listing being empty --- internal/cmd/intake/runner/list/list.go | 26 +++++++++++--------- internal/cmd/intake/runner/list/list_test.go | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/internal/cmd/intake/runner/list/list.go b/internal/cmd/intake/runner/list/list.go index 8e9a0199f..c572d6e65 100644 --- a/internal/cmd/intake/runner/list/list.go +++ b/internal/cmd/intake/runner/list/list.go @@ -68,22 +68,21 @@ func NewListCmd(p *params.CmdParams) *cobra.Command { return fmt.Errorf("list Intake Runners: %w", err) } runners := resp.GetIntakeRunners() - 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) - projectLabel = model.ProjectId - } - p.Printer.Outputf("No Intake Runners found for project %q\n", projectLabel) - return nil - } // Truncate output if model.Limit != nil && len(runners) > int(*model.Limit) { runners = runners[:*model.Limit] } - return outputResult(p.Printer, model.OutputFormat, runners) + 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) @@ -137,7 +136,7 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *intake.APIC } // outputResult formats the API response and prints it to the console -func outputResult(p *print.Printer, outputFormat string, runners []intake.IntakeRunnerResponse) error { +func outputResult(p *print.Printer, outputFormat, projectLabel string, runners []intake.IntakeRunnerResponse) error { switch outputFormat { case print.JSONOutputFormat: details, err := json.MarshalIndent(runners, "", " ") @@ -156,6 +155,11 @@ func outputResult(p *print.Printer, outputFormat string, runners []intake.Intake return nil default: + if len(runners) == 0 { + p.Outputf("No intake runners found for project %q\n", projectLabel) + return nil + } + table := tables.NewTable() table.SetHeader("ID", "NAME") diff --git a/internal/cmd/intake/runner/list/list_test.go b/internal/cmd/intake/runner/list/list_test.go index 86545c0ee..0b2b92007 100644 --- a/internal/cmd/intake/runner/list/list_test.go +++ b/internal/cmd/intake/runner/list/list_test.go @@ -220,7 +220,7 @@ func TestOutputResult(t *testing.T) { p.Cmd = NewListCmd(¶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.runners); (err != nil) != tt.wantErr { + 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) } }) From 6be700c62a4e6b3b992748180f571a47377aeefa Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 8 Sep 2025 12:18:10 +0200 Subject: [PATCH 04/22] address review comments: be able to configure custom endpoints --- internal/cmd/config/set/set.go | 4 ++++ internal/cmd/config/unset/unset.go | 7 +++++++ internal/cmd/config/unset/unset_test.go | 3 +++ 3 files changed, 14 insertions(+) diff --git a/internal/cmd/config/set/set.go b/internal/cmd/config/set/set.go index 54f9f527d..eb3248ee3 100644 --- a/internal/cmd/config/set/set.go +++ b/internal/cmd/config/set/set.go @@ -46,6 +46,7 @@ const ( sqlServerFlexCustomEndpointFlag = "sqlserverflex-custom-endpoint" iaasCustomEndpointFlag = "iaas-custom-endpoint" tokenCustomEndpointFlag = "token-custom-endpoint" + intakeCustomEndpointFlag = "intake-custom-endpoint" ) type inputModel struct { @@ -159,6 +160,7 @@ func configureFlags(cmd *cobra.Command) { cmd.Flags().String(sqlServerFlexCustomEndpointFlag, "", "SQLServer Flex API base URL, used in calls to this API") cmd.Flags().String(iaasCustomEndpointFlag, "", "IaaS API base URL, used in calls to this API") 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") err := viper.BindPFlag(config.SessionTimeLimitKey, cmd.Flags().Lookup(sessionTimeLimitFlag)) cobra.CheckErr(err) @@ -215,6 +217,8 @@ func configureFlags(cmd *cobra.Command) { cobra.CheckErr(err) err = viper.BindPFlag(config.TokenCustomEndpointKey, cmd.Flags().Lookup(tokenCustomEndpointFlag)) cobra.CheckErr(err) + err = viper.BindPFlag(config.IntakeCustomEndpointKey, cmd.Flags().Lookup(intakeCustomEndpointFlag)) + cobra.CheckErr(err) } func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { diff --git a/internal/cmd/config/unset/unset.go b/internal/cmd/config/unset/unset.go index a288be6a3..1f56b25e8 100644 --- a/internal/cmd/config/unset/unset.go +++ b/internal/cmd/config/unset/unset.go @@ -50,6 +50,7 @@ const ( sqlServerFlexCustomEndpointFlag = "sqlserverflex-custom-endpoint" iaasCustomEndpointFlag = "iaas-custom-endpoint" tokenCustomEndpointFlag = "token-custom-endpoint" + intakeCustomEndpointFlag = "intake-custom-endpoint" ) type inputModel struct { @@ -87,6 +88,7 @@ type inputModel struct { SQLServerFlexCustomEndpoint bool IaaSCustomEndpoint bool TokenCustomEndpoint bool + IntakeCustomEndpoint bool } func NewCmd(params *params.CmdParams) *cobra.Command { @@ -207,6 +209,9 @@ func NewCmd(params *params.CmdParams) *cobra.Command { if model.TokenCustomEndpoint { viper.Set(config.TokenCustomEndpointKey, "") } + if model.IntakeCustomEndpoint { + viper.Set(config.IntakeCustomEndpointKey, "") + } err := config.Write() if err != nil { @@ -254,6 +259,7 @@ func configureFlags(cmd *cobra.Command) { cmd.Flags().Bool(sqlServerFlexCustomEndpointFlag, false, "SQLServer Flex API base URL. If unset, uses the default base URL") cmd.Flags().Bool(iaasCustomEndpointFlag, false, "IaaS API base URL. If unset, uses the default base URL") 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") } func parseInput(p *print.Printer, cmd *cobra.Command) *inputModel { @@ -292,6 +298,7 @@ func parseInput(p *print.Printer, cmd *cobra.Command) *inputModel { SQLServerFlexCustomEndpoint: flags.FlagToBoolValue(p, cmd, sqlServerFlexCustomEndpointFlag), IaaSCustomEndpoint: flags.FlagToBoolValue(p, cmd, iaasCustomEndpointFlag), TokenCustomEndpoint: flags.FlagToBoolValue(p, cmd, tokenCustomEndpointFlag), + IntakeCustomEndpoint: flags.FlagToBoolValue(p, cmd, intakeCustomEndpointFlag), } if p.IsVerbosityDebug() { diff --git a/internal/cmd/config/unset/unset_test.go b/internal/cmd/config/unset/unset_test.go index 246696f15..af15ebd32 100644 --- a/internal/cmd/config/unset/unset_test.go +++ b/internal/cmd/config/unset/unset_test.go @@ -43,6 +43,7 @@ func fixtureFlagValues(mods ...func(flagValues map[string]bool)) map[string]bool sqlServerFlexCustomEndpointFlag: true, iaasCustomEndpointFlag: true, tokenCustomEndpointFlag: true, + intakeCustomEndpointFlag: true, } for _, mod := range mods { mod(flagValues) @@ -82,6 +83,7 @@ func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { SQLServerFlexCustomEndpoint: true, IaaSCustomEndpoint: true, TokenCustomEndpoint: true, + IntakeCustomEndpoint: true, } for _, mod := range mods { mod(model) @@ -137,6 +139,7 @@ func TestParseInput(t *testing.T) { model.SQLServerFlexCustomEndpoint = false model.IaaSCustomEndpoint = false model.TokenCustomEndpoint = false + model.IntakeCustomEndpoint = false }), }, { From ef3f64190fc6fd38e512c072d2eaf7da33fc3292 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Wed, 10 Sep 2025 12:01:14 +0200 Subject: [PATCH 05/22] update docs --- docs/stackit.md | 2 +- docs/stackit_config_set.md | 1 + docs/stackit_config_unset.md | 1 + docs/stackit_intake.md | 11 ++--------- docs/stackit_intake_runner.md | 2 +- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/docs/stackit.md b/docs/stackit.md index c4cace3ef..2c0e0b0fa 100644 --- a/docs/stackit.md +++ b/docs/stackit.md @@ -34,7 +34,7 @@ stackit [flags] * [stackit dns](./stackit_dns.md) - Provides functionality for DNS * [stackit git](./stackit_git.md) - Provides functionality for STACKIT Git * [stackit image](./stackit_image.md) - Manage server images -* [stackit intake](./stackit_intake.md) - Provides functionality for STACKIT Intake +* [stackit intake](./stackit_intake.md) - Provides functionality for intake * [stackit key-pair](./stackit_key-pair.md) - Provides functionality for SSH key pairs * [stackit load-balancer](./stackit_load-balancer.md) - Provides functionality for Load Balancer * [stackit logme](./stackit_logme.md) - Provides functionality for LogMe diff --git a/docs/stackit_config_set.md b/docs/stackit_config_set.md index b1abf5662..880184af2 100644 --- a/docs/stackit_config_set.md +++ b/docs/stackit_config_set.md @@ -36,6 +36,7 @@ stackit config set [flags] --iaas-custom-endpoint string IaaS API base URL, used in calls to this API --identity-provider-custom-client-id string Identity Provider client ID, used for user authentication --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 --load-balancer-custom-endpoint string Load Balancer API base URL, used in calls to this API --logme-custom-endpoint string LogMe API base URL, used in calls to this API --mariadb-custom-endpoint string MariaDB API base URL, used in calls to this API diff --git a/docs/stackit_config_unset.md b/docs/stackit_config_unset.md index 4a48b759e..65ef2fd97 100644 --- a/docs/stackit_config_unset.md +++ b/docs/stackit_config_unset.md @@ -34,6 +34,7 @@ stackit config unset [flags] --iaas-custom-endpoint IaaS API base URL. If unset, uses the default base URL --identity-provider-custom-client-id Identity Provider client ID, used for user authentication --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 --load-balancer-custom-endpoint Load Balancer API base URL. If unset, uses the default base URL --logme-custom-endpoint LogMe API base URL. If unset, uses the default base URL --mariadb-custom-endpoint MariaDB API base URL. If unset, uses the default base URL diff --git a/docs/stackit_intake.md b/docs/stackit_intake.md index 1a92caf16..16cbad26e 100644 --- a/docs/stackit_intake.md +++ b/docs/stackit_intake.md @@ -1,22 +1,15 @@ ## stackit intake -Provides functionality for STACKIT Intake +Provides functionality for intake ### Synopsis -Provides functionality for STACKIT Intake. +Provides functionality for intake. ``` stackit intake [flags] ``` -### Examples - -``` - - $ stackit intake -``` - ### Options ``` diff --git a/docs/stackit_intake_runner.md b/docs/stackit_intake_runner.md index 7acd9dcda..90ca7b0e8 100644 --- a/docs/stackit_intake_runner.md +++ b/docs/stackit_intake_runner.md @@ -29,7 +29,7 @@ stackit intake runner [flags] ### SEE ALSO -* [stackit intake](./stackit_intake.md) - Provides functionality for STACKIT Intake +* [stackit intake](./stackit_intake.md) - Provides functionality for intake * [stackit intake runner create](./stackit_intake_runner_create.md) - Creates a new Intake Runner * [stackit intake runner delete](./stackit_intake_runner_delete.md) - Deletes an Intake Runner * [stackit intake runner describe](./stackit_intake_runner_describe.md) - Shows details of an Intake Runner From 535dfb6bc7d85d8ffcd8dfde53f8ac451df1d397 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 15 Sep 2025 16:46:28 +0200 Subject: [PATCH 06/22] remove option for clearing labels # Conflicts: # go.mod # go.sum --- docs/stackit_intake_runner_update.md | 2 +- go.mod | 50 +++++----- go.sum | 25 +++-- internal/cmd/intake/common/util.go | 27 ------ internal/cmd/intake/common/util_test.go | 95 ------------------- internal/cmd/intake/runner/create/create.go | 6 +- .../cmd/intake/runner/create/create_test.go | 2 +- internal/cmd/intake/runner/update/update.go | 27 +----- .../cmd/intake/runner/update/update_test.go | 13 +-- 9 files changed, 50 insertions(+), 197 deletions(-) delete mode 100644 internal/cmd/intake/common/util.go delete mode 100644 internal/cmd/intake/common/util_test.go diff --git a/docs/stackit_intake_runner_update.md b/docs/stackit_intake_runner_update.md index 6ed3f9cc8..5cdef4ba2 100644 --- a/docs/stackit_intake_runner_update.md +++ b/docs/stackit_intake_runner_update.md @@ -29,7 +29,7 @@ stackit intake runner update RUNNER_ID [flags] --description string Description --display-name string Display name -h, --help Help for "stackit intake runner update" - --labels string Labels in key=value format. To clear all labels, provide an empty string, e.g. --labels "" + --labels stringToString Labels in key=value format. To clear all labels, provide an empty string, e.g. --labels "" (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. ``` diff --git a/go.mod b/go.mod index 0e0a3815a..9e6ec25b3 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/stackitcloud/stackit-cli -go 1.24.0 +go 1.24 require ( github.com/fatih/color v1.18.0 @@ -16,11 +16,11 @@ require ( github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stackitcloud/stackit-sdk-go/core v0.17.3 - github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.0 - github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0 + github.com/stackitcloud/stackit-sdk-go/services/alb v0.6.1 + github.com/stackitcloud/stackit-sdk-go/services/authorization v0.8.1 github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1 - github.com/stackitcloud/stackit-sdk-go/services/git v0.8.0 - github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0 + github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1 + github.com/stackitcloud/stackit-sdk-go/services/iaas v0.30.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/services/postgresflex v1.2.1 @@ -34,16 +34,16 @@ require ( github.com/stackitcloud/stackit-sdk-go/services/ske v1.4.0 github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.3.1 github.com/zalando/go-keyring v0.2.6 - golang.org/x/mod v0.28.0 - golang.org/x/oauth2 v0.31.0 - golang.org/x/term v0.35.0 - golang.org/x/text v0.29.0 - k8s.io/apimachinery v0.34.1 - k8s.io/client-go v0.34.1 + golang.org/x/mod v0.27.0 + golang.org/x/oauth2 v0.30.0 + golang.org/x/term v0.34.0 + golang.org/x/text v0.28.0 + k8s.io/apimachinery v0.32.3 + k8s.io/client-go v0.32.3 ) require ( - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.43.0 // indirect golang.org/x/time v0.11.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect ) @@ -91,7 +91,7 @@ require ( github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/firefart/nonamedreturns v1.0.6 // indirect - github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect github.com/ghostiam/protogetter v0.3.15 // indirect github.com/go-critic/go-critic v0.13.0 // indirect @@ -206,12 +206,10 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 // indirect - golang.org/x/tools v0.37.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/tools v0.36.0 // indirect golang.org/x/tools/go/expect v0.1.1-deprecated // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect google.golang.org/protobuf v1.36.6 // indirect @@ -219,8 +217,6 @@ require ( honnef.co/go/tools v0.6.1 // indirect mvdan.cc/gofumpt v0.8.0 // indirect mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect - sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect ) require ( @@ -231,11 +227,12 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gofuzz v1.2.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -243,7 +240,7 @@ require ( github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect - github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0 + github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1 github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1 github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.1 github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.4.0 @@ -252,13 +249,14 @@ require ( github.com/stackitcloud/stackit-sdk-go/services/redis v0.25.1 github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.36.0 // indirect + golang.org/x/sys v0.35.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.34.1 // indirect + k8s.io/api v0.32.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect - sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) tool ( diff --git a/go.sum b/go.sum index 61d5cc4f2..4106d0769 100644 --- a/go.sum +++ b/go.sum @@ -429,6 +429,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= @@ -575,6 +576,16 @@ github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0 h1:dnEjyapuv8WwRN5v github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0/go.mod h1:854gnLR92NvAbJAA1xZEumrtNh1DoBP1FXTMvhwYA6w= github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0 h1:q33ZaCBVEBUsnMDxYyuJKtJvGcE5nKgvuPed3s8zXNI= github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0/go.mod h1:20QOZ3rBC9wTGgzXzLz9M6YheX0VaxWE0/JI+s8On7k= +github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1 h1:hkFixFnBcQzU4BSIZFITc8N0gK0pUYk7mk0wdUu5Ki8= +github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1/go.mod h1:Ng1EzrRndG3iGXGH90AZJz//wfK+2YOyDwTnTLwX3a4= +github.com/stackitcloud/stackit-sdk-go/services/iaas v0.30.0 h1:01+noyCSadNH3ALHufcVXxNs0hBsetzJkOMN1Fe0VLc= +github.com/stackitcloud/stackit-sdk-go/services/iaas v0.30.0/go.mod h1:854gnLR92NvAbJAA1xZEumrtNh1DoBP1FXTMvhwYA6w= +github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.0 h1:IhswZoEHqkBW60wEmRaeoSrW68PGLQKrWVmMPMzwYrY= +github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.0/go.mod h1:vRnT3zxWJ1k7wbAk8JmO0xFmPhmeos5HTIWdsVAAoKU= +github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.1 h1:qKAGtRfnB89vXom5mIwctMHFeznMQWXJd3cqQBURIK8= +github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.1/go.mod h1:jOArPjNRkwv4487+9ab3dRG+lM09leu5FiRohbQs9Z4= +github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1 h1:OdJEs8eOfrzn9tCBDLxIyP8hX50zPfcXNYnRoQX+chs= +github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1/go.mod h1:11uzaOPCF9SeDHXEGOPMlHDD3J5r2TnvCGUwW9Igq9c= github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1 h1:hv5WrRU9rN6Jx4OwdOGJRyaQrfA9p1tzEoQK6/CDyoA= github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1/go.mod h1:ivt8lvnAoBZsde2jSAuicyn6RgTmHvvNAJ3whaUbAD4= github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.1 h1:Db/ebOL2vbpIeh5XB2Ews2B9Lj5DJlMWIEJh60FfZ4Y= @@ -875,10 +886,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 h1:dHQOQddU4YHS5gY33/6klKjq7Gp3WwMyOXGNp5nzRj8= -golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -887,8 +896,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -901,8 +910,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/internal/cmd/intake/common/util.go b/internal/cmd/intake/common/util.go deleted file mode 100644 index 8d4ec77b2..000000000 --- a/internal/cmd/intake/common/util.go +++ /dev/null @@ -1,27 +0,0 @@ -package common - -import ( - "fmt" - "strings" -) - -// ParseLabels parses the labels flag value into a map. -// An empty string clears the labels, returning a pointer to an empty map. -func ParseLabels(labelsVal string) (map[string]string, error) { - if labelsVal == "" { - // User wants to clear labels - return map[string]string{}, nil - } - - // User provided labels, parse them - parsedLabels := make(map[string]string) - pairs := strings.Split(labelsVal, ",") - for _, pair := range pairs { - kv := strings.SplitN(pair, "=", 2) - if len(kv) != 2 || kv[0] == "" { - return nil, fmt.Errorf("invalid label format, expected key=value: %q", pair) - } - parsedLabels[kv[0]] = kv[1] - } - return parsedLabels, nil -} diff --git a/internal/cmd/intake/common/util_test.go b/internal/cmd/intake/common/util_test.go deleted file mode 100644 index e8c30dc2b..000000000 --- a/internal/cmd/intake/common/util_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package common - -import ( - "testing" - - "github.com/google/go-cmp/cmp" -) - -func TestParseLabels(t *testing.T) { - tests := []struct { - description string - input string - expectedMap map[string]string - expectError bool - }{ - { - description: "single label", - input: "key1=val1", - expectedMap: map[string]string{"key1": "val1"}, - expectError: false, - }, - { - description: "multiple labels", - input: "key1=val1,key2=val2", - expectedMap: map[string]string{"key1": "val1", "key2": "val2"}, - expectError: false, - }, - { - description: "empty value", - input: "key1=", - expectedMap: map[string]string{"key1": ""}, - expectError: false, - }, - { - description: "value with equals sign", - input: "key1=value=with=equals", - expectedMap: map[string]string{"key1": "value=with=equals"}, - expectError: false, - }, - { - description: "special case: empty string to clear labels", - input: "", - expectedMap: map[string]string{}, // Should be an empty map, not nil - expectError: false, - }, - { - description: "invalid format - no equals", - input: "key1val1", - expectedMap: nil, - expectError: true, - }, - { - description: "invalid format - empty key", - input: "=val1", - expectedMap: nil, - expectError: true, - }, - { - description: "mixed valid and invalid pair", - input: "key1=val1,key2", - expectedMap: nil, - expectError: true, - }, - { - description: "invalid format - leading comma", - input: ",key1=val1", - expectedMap: nil, - expectError: true, - }, - { - description: "invalid format - trailing comma", - input: "key1=val1,", - expectedMap: nil, - expectError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - parsedMap, err := ParseLabels(tt.input) - - if !tt.expectError && err != nil { - t.Fatalf("did not expect an error, but got: %v", err) - } - - if tt.expectError && err == nil { - t.Fatalf("expected an error, but got nil") - } - - if diff := cmp.Diff(tt.expectedMap, parsedMap); diff != "" { - t.Errorf("map mismatch (-want +got):\n%s", diff) - } - }) - } -} diff --git a/internal/cmd/intake/runner/create/create.go b/internal/cmd/intake/runner/create/create.go index 59caf001c..b6baf0d2e 100644 --- a/internal/cmd/intake/runner/create/create.go +++ b/internal/cmd/intake/runner/create/create.go @@ -26,7 +26,7 @@ const ( maxMessageSizeKiBFlag = "max-message-size-kib" maxMessagesPerHourFlag = "max-messages-per-hour" descriptionFlag = "description" - labelsFlag = "labels" + labelFlag = "labels" ) // inputModel struct holds all the input parameters for the command @@ -99,7 +99,7 @@ func configureFlags(cmd *cobra.Command) { 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(labelsFlag, nil, "Labels in key=value format, separated by commas. Example: --labels \"key1=value1,key2=value2\"") + 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) @@ -117,7 +117,7 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { MaxMessageSizeKiB: flags.FlagToInt64Pointer(p, cmd, maxMessageSizeKiBFlag), MaxMessagesPerHour: flags.FlagToInt64Pointer(p, cmd, maxMessagesPerHourFlag), Description: flags.FlagToStringPointer(p, cmd, descriptionFlag), - Labels: flags.FlagToStringToStringPointer(p, cmd, labelsFlag), + Labels: flags.FlagToStringToStringPointer(p, cmd, labelFlag), } if p.IsVerbosityDebug() { diff --git a/internal/cmd/intake/runner/create/create_test.go b/internal/cmd/intake/runner/create/create_test.go index a140db7ec..f0be33c41 100644 --- a/internal/cmd/intake/runner/create/create_test.go +++ b/internal/cmd/intake/runner/create/create_test.go @@ -43,7 +43,7 @@ func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]st maxMessageSizeKiBFlag: "1024", maxMessagesPerHourFlag: "10000", descriptionFlag: testDescription, - labelsFlag: testLabelsString, + labelFlag: testLabelsString, } for _, mod := range mods { mod(flagValues) diff --git a/internal/cmd/intake/runner/update/update.go b/internal/cmd/intake/runner/update/update.go index 68fa53529..67940d071 100644 --- a/internal/cmd/intake/runner/update/update.go +++ b/internal/cmd/intake/runner/update/update.go @@ -6,7 +6,6 @@ import ( "github.com/spf13/cobra" - "github.com/stackitcloud/stackit-cli/internal/cmd/intake/common" "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" @@ -28,7 +27,7 @@ const ( maxMessageSizeKiBFlag = "max-message-size-kib" maxMessagesPerHourFlag = "max-messages-per-hour" descriptionFlag = "description" - labelsFlag = "labels" + labelFlag = "labels" ) type inputModel struct { @@ -90,7 +89,7 @@ func configureFlags(cmd *cobra.Command) { 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().String(labelsFlag, "", "Labels in key=value format. To clear all labels, provide an empty string, e.g. --labels \"\"") + cmd.Flags().StringToString(labelFlag, nil, "Labels in key=value format. To clear all labels, provide an empty string, e.g. --labels \"\"") } func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { @@ -101,26 +100,6 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu return nil, &cliErr.ProjectIdError{} } - var labels *map[string]string - if cmd.Flags().Changed(labelsFlag) { - labelsVal, err := cmd.Flags().GetString(labelsFlag) - if err != nil { - return nil, fmt.Errorf("could not parse --%s: %w", labelsFlag, err) - } - if labelsVal == "" { - // User wants to clear labels - labels = &map[string]string{} - } else { - // User provided labels, parse them - parsedLabels, err := common.ParseLabels(labelsVal) - if err != nil { - return nil, err - } - - labels = &parsedLabels - } - } - model := inputModel{ GlobalFlagModel: globalFlags, RunnerId: runnerId, @@ -128,7 +107,7 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu MaxMessageSizeKiB: flags.FlagToInt64Pointer(p, cmd, maxMessageSizeKiBFlag), MaxMessagesPerHour: flags.FlagToInt64Pointer(p, cmd, maxMessagesPerHourFlag), Description: flags.FlagToStringPointer(p, cmd, descriptionFlag), - Labels: labels, + Labels: flags.FlagToStringToStringPointer(p, cmd, labelFlag), } if model.DisplayName == nil && model.MaxMessageSizeKiB == nil && model.MaxMessagesPerHour == nil && model.Description == nil && model.Labels == nil { diff --git a/internal/cmd/intake/runner/update/update_test.go b/internal/cmd/intake/runner/update/update_test.go index f17bf12ff..bb2f88cf2 100644 --- a/internal/cmd/intake/runner/update/update_test.go +++ b/internal/cmd/intake/runner/update/update_test.go @@ -105,7 +105,7 @@ func TestParseInput(t *testing.T) { flagValues[maxMessageSizeKiBFlag] = "2048" flagValues[maxMessagesPerHourFlag] = "10000" flagValues[descriptionFlag] = "new description" - flagValues[labelsFlag] = "env=prod,team=sre" + flagValues[labelFlag] = "env=prod,team=sre" }), isValid: true, expectedModel: fixtureInputModel(func(model *inputModel) { @@ -115,17 +115,6 @@ func TestParseInput(t *testing.T) { model.Labels = utils.Ptr(map[string]string{"env": "prod", "team": "sre"}) }), }, - { - description: "clear labels", - argValues: fixtureArgValues(), - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[labelsFlag] = "" - }), - isValid: true, - expectedModel: fixtureInputModel(func(model *inputModel) { - model.Labels = utils.Ptr(map[string]string{}) - }), - }, { description: "no args", argValues: []string{}, From 1abf8da09bb6a856d5950da986fc91872fcb43d6 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 25 Sep 2025 14:44:42 +0200 Subject: [PATCH 07/22] Bump Intake OpenAPI to 0.2.0 and address update fixes --- go.sum | 2 + internal/cmd/intake/runner/update/update.go | 39 ++++++++++++++-- .../cmd/intake/runner/update/update_test.go | 44 +++++++++++++++++++ 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/go.sum b/go.sum index 4106d0769..7e37cf819 100644 --- a/go.sum +++ b/go.sum @@ -584,6 +584,8 @@ github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.0 h1:IhswZoEHqkBW60w github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.0/go.mod h1:vRnT3zxWJ1k7wbAk8JmO0xFmPhmeos5HTIWdsVAAoKU= github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.1 h1:qKAGtRfnB89vXom5mIwctMHFeznMQWXJd3cqQBURIK8= github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.1/go.mod h1:jOArPjNRkwv4487+9ab3dRG+lM09leu5FiRohbQs9Z4= +github.com/stackitcloud/stackit-sdk-go/services/intake v0.2.0 h1:p/zi4VPoCQWk7/2ubi3hxsqiaye41x/Pl3GXYbPkYOY= +github.com/stackitcloud/stackit-sdk-go/services/intake v0.2.0/go.mod h1:jOArPjNRkwv4487+9ab3dRG+lM09leu5FiRohbQs9Z4= github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1 h1:OdJEs8eOfrzn9tCBDLxIyP8hX50zPfcXNYnRoQX+chs= github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1/go.mod h1:11uzaOPCF9SeDHXEGOPMlHDD3J5r2TnvCGUwW9Igq9c= github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1 h1:hv5WrRU9rN6Jx4OwdOGJRyaQrfA9p1tzEoQK6/CDyoA= diff --git a/internal/cmd/intake/runner/update/update.go b/internal/cmd/intake/runner/update/update.go index 67940d071..ec97d01d9 100644 --- a/internal/cmd/intake/runner/update/update.go +++ b/internal/cmd/intake/runner/update/update.go @@ -2,7 +2,10 @@ package update import ( "context" + "encoding/json" "fmt" + "github.com/goccy/go-yaml" + "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" "github.com/spf13/cobra" @@ -70,14 +73,20 @@ func NewUpdateCmd(p *params.CmdParams) *cobra.Command { 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) - if err := req.Execute(); err != nil { + resp, err := req.Execute() + if err != nil { return fmt.Errorf("update Intake Runner: %w", err) } - p.Printer.Info("Update request for Intake Runner %q sent successfully.\n", model.RunnerId) - return nil + return outputResult(p.Printer, model.OutputFormat, projectLabel, resp) }, } configureFlags(cmd) @@ -149,3 +158,27 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *intake.APIC req = req.UpdateIntakeRunnerPayload(payload) return req } + +func outputResult(p *print.Printer, outputFormat, projectLabel string, resp *intake.IntakeRunnerResponse) error { + switch outputFormat { + case print.JSONOutputFormat: + details, err := json.MarshalIndent(resp, "", " ") + if err != nil { + return fmt.Errorf("marshal instance: %w", err) + } + p.Outputln(string(details)) + + return nil + case print.YAMLOutputFormat: + details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) + if err != nil { + return fmt.Errorf("marshal instance: %w", err) + } + p.Outputln(string(details)) + + return nil + default: + p.Outputf("Updated Intake Runner for project %q. Runner ID: %s\n", projectLabel, utils.PtrString(resp.Id)) + return nil + } +} diff --git a/internal/cmd/intake/runner/update/update_test.go b/internal/cmd/intake/runner/update/update_test.go index bb2f88cf2..ca1f63060 100644 --- a/internal/cmd/intake/runner/update/update_test.go +++ b/internal/cmd/intake/runner/update/update_test.go @@ -247,3 +247,47 @@ func TestBuildRequest(t *testing.T) { }) } } + +func TestOutputResult(t *testing.T) { + type args struct { + outputFormat string + projectLabel string + runnerId string + resp *intake.IntakeRunnerResponse + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "default output", + args: args{outputFormat: "default", projectLabel: "my-project", runnerId: "runner-id-123", resp: &intake.IntakeRunnerResponse{}}, + wantErr: false, + }, + { + name: "json output", + args: args{outputFormat: print.JSONOutputFormat, resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}}, + wantErr: false, + }, + { + name: "yaml output", + args: args{outputFormat: print.YAMLOutputFormat, resp: &intake.IntakeRunnerResponse{Id: utils.Ptr("runner-id-123")}}, + wantErr: false, + }, + { + name: "nil response", + args: args{outputFormat: print.JSONOutputFormat, resp: nil}, + wantErr: false, + }, + } + p := print.NewPrinter() + p.Cmd = NewUpdateCmd(¶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.projectLabel, tt.args.resp); (err != nil) != tt.wantErr { + t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} From 8cc9fe11d3d7a4cd0915fd7209b4deeab45e26d0 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 25 Sep 2025 15:46:55 +0200 Subject: [PATCH 08/22] Address review: make json and yaml valid for update and delete + remove example --- internal/cmd/intake/runner/delete/delete.go | 1 - internal/cmd/intake/runner/update/update.go | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/cmd/intake/runner/delete/delete.go b/internal/cmd/intake/runner/delete/delete.go index 26743fdbd..32a4d36a8 100644 --- a/internal/cmd/intake/runner/delete/delete.go +++ b/internal/cmd/intake/runner/delete/delete.go @@ -65,7 +65,6 @@ func NewDeleteCmd(p *params.CmdParams) *cobra.Command { return fmt.Errorf("delete Intake Runner: %w", err) } - p.Printer.Outputf("Deletion request for Intake Runner %q sent successfully.\n", model.RunnerId) return nil }, } diff --git a/internal/cmd/intake/runner/update/update.go b/internal/cmd/intake/runner/update/update.go index ec97d01d9..fc2e49976 100644 --- a/internal/cmd/intake/runner/update/update.go +++ b/internal/cmd/intake/runner/update/update.go @@ -56,9 +56,6 @@ func NewUpdateCmd(p *params.CmdParams) *cobra.Command { examples.NewExample( `Update the message capacity limits for an Intake Runner with ID "xxx"`, `$ stackit intake runner update xxx --max-message-size-kib 2000 --max-messages-per-hour 10000`), - examples.NewExample( - `Clear the labels of an Intake Runner with ID "xxx" by providing an empty value`, - `$ stackit intake runner update xxx --labels ""`), ), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() @@ -98,7 +95,7 @@ func configureFlags(cmd *cobra.Command) { 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. To clear all labels, provide an empty string, e.g. --labels \"\"") + 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) { From fcb7cb08e8e87bc01680a7c1de2433c0a5a9e59e Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 25 Sep 2025 16:28:14 +0200 Subject: [PATCH 09/22] Address review: adjust cli version for client --- internal/cmd/intake/runner/create/create.go | 2 +- internal/cmd/intake/runner/delete/delete.go | 2 +- internal/cmd/intake/runner/describe/describe.go | 2 +- internal/cmd/intake/runner/list/list.go | 2 +- internal/cmd/intake/runner/update/update.go | 2 +- internal/pkg/services/intake/client/client.go | 4 +++- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/cmd/intake/runner/create/create.go b/internal/cmd/intake/runner/create/create.go index b6baf0d2e..90ea735a2 100644 --- a/internal/cmd/intake/runner/create/create.go +++ b/internal/cmd/intake/runner/create/create.go @@ -61,7 +61,7 @@ func NewCreateCmd(p *params.CmdParams) *cobra.Command { } // Configure API client - apiClient, err := client.ConfigureClient(p.Printer) + apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) if err != nil { return err } diff --git a/internal/cmd/intake/runner/delete/delete.go b/internal/cmd/intake/runner/delete/delete.go index 32a4d36a8..747d602e9 100644 --- a/internal/cmd/intake/runner/delete/delete.go +++ b/internal/cmd/intake/runner/delete/delete.go @@ -46,7 +46,7 @@ func NewDeleteCmd(p *params.CmdParams) *cobra.Command { } // Configure API client - apiClient, err := client.ConfigureClient(p.Printer) + apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) if err != nil { return err } diff --git a/internal/cmd/intake/runner/describe/describe.go b/internal/cmd/intake/runner/describe/describe.go index d2cb1ecdb..68e06a07f 100644 --- a/internal/cmd/intake/runner/describe/describe.go +++ b/internal/cmd/intake/runner/describe/describe.go @@ -51,7 +51,7 @@ func NewDescribeCmd(p *params.CmdParams) *cobra.Command { } // Configure API client - apiClient, err := client.ConfigureClient(p.Printer) + apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) if err != nil { return err } diff --git a/internal/cmd/intake/runner/list/list.go b/internal/cmd/intake/runner/list/list.go index c572d6e65..d9918937e 100644 --- a/internal/cmd/intake/runner/list/list.go +++ b/internal/cmd/intake/runner/list/list.go @@ -56,7 +56,7 @@ func NewListCmd(p *params.CmdParams) *cobra.Command { } // Configure API client - apiClient, err := client.ConfigureClient(p.Printer) + apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) if err != nil { return err } diff --git a/internal/cmd/intake/runner/update/update.go b/internal/cmd/intake/runner/update/update.go index fc2e49976..6cfed530d 100644 --- a/internal/cmd/intake/runner/update/update.go +++ b/internal/cmd/intake/runner/update/update.go @@ -65,7 +65,7 @@ func NewUpdateCmd(p *params.CmdParams) *cobra.Command { } // Configure API client - apiClient, err := client.ConfigureClient(p.Printer) + apiClient, err := client.ConfigureClient(p.Printer, p.CliVersion) if err != nil { return err } diff --git a/internal/pkg/services/intake/client/client.go b/internal/pkg/services/intake/client/client.go index 9f4e014cc..750f01b7f 100644 --- a/internal/pkg/services/intake/client/client.go +++ b/internal/pkg/services/intake/client/client.go @@ -6,12 +6,13 @@ import ( "github.com/stackitcloud/stackit-cli/internal/pkg/config" "github.com/stackitcloud/stackit-cli/internal/pkg/errors" "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" "github.com/stackitcloud/stackit-sdk-go/services/intake" ) // ConfigureClient creates and configures a new Intake API client -func ConfigureClient(p *print.Printer) (*intake.APIClient, error) { +func ConfigureClient(p *print.Printer, cliVersion string) (*intake.APIClient, error) { authCfgOption, err := auth.AuthenticationConfig(p, auth.AuthorizeUser) if err != nil { p.Debug(print.ErrorLevel, "configure authentication: %v", err) @@ -20,6 +21,7 @@ func ConfigureClient(p *print.Printer) (*intake.APIClient, error) { region := viper.GetString(config.RegionKey) cfgOptions := []sdkConfig.ConfigurationOption{ + utils.UserAgentConfigOption(cliVersion), sdkConfig.WithRegion(region), authCfgOption, } From 511d144842d5b702905c30ad1405515f81842ced Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 25 Sep 2025 16:33:34 +0200 Subject: [PATCH 10/22] Address review: decrease max message size from example --- internal/cmd/intake/runner/update/update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/intake/runner/update/update.go b/internal/cmd/intake/runner/update/update.go index 6cfed530d..cb0d0c36f 100644 --- a/internal/cmd/intake/runner/update/update.go +++ b/internal/cmd/intake/runner/update/update.go @@ -55,7 +55,7 @@ func NewUpdateCmd(p *params.CmdParams) *cobra.Command { `$ stackit intake runner update xxx --display-name "new-runner-name"`), examples.NewExample( `Update the message capacity limits for an Intake Runner with ID "xxx"`, - `$ stackit intake runner update xxx --max-message-size-kib 2000 --max-messages-per-hour 10000`), + `$ stackit 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() From aa3b3c08174703a54e277d0a4d731ce76dcece80 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 25 Sep 2025 16:35:14 +0200 Subject: [PATCH 11/22] lint --- internal/cmd/intake/runner/update/update.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cmd/intake/runner/update/update.go b/internal/cmd/intake/runner/update/update.go index cb0d0c36f..ccd9e8e06 100644 --- a/internal/cmd/intake/runner/update/update.go +++ b/internal/cmd/intake/runner/update/update.go @@ -4,9 +4,8 @@ import ( "context" "encoding/json" "fmt" - "github.com/goccy/go-yaml" - "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" + "github.com/goccy/go-yaml" "github.com/spf13/cobra" "github.com/stackitcloud/stackit-cli/internal/cmd/params" @@ -16,6 +15,7 @@ import ( "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/utils" "github.com/stackitcloud/stackit-sdk-go/services/intake" From 5f446949e026dff1f0a94bfdebbf821097a0e84d Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Mon, 6 Oct 2025 16:07:50 +0200 Subject: [PATCH 12/22] go.sum --- go.sum | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/go.sum b/go.sum index 7e37cf819..dc005ff5f 100644 --- a/go.sum +++ b/go.sum @@ -158,6 +158,7 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= @@ -285,9 +286,11 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -564,22 +567,22 @@ github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YE github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stackitcloud/stackit-sdk-go/core v0.17.3 h1:GsZGmRRc/3GJLmCUnsZswirr5wfLRrwavbnL/renOqg= github.com/stackitcloud/stackit-sdk-go/core v0.17.3/go.mod h1:HBCXJGPgdRulplDzhrmwC+Dak9B/x0nzNtmOpu+1Ahg= +github.com/stackitcloud/stackit-sdk-go/services/alb v0.6.1/go.mod h1:UyN4hlkdiK5beDi5j9xHMENxRE9A9dlIWSXO/unhQW8= github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.0 h1:UM/HvewAgELEvZ4zk++Ykd6jUTSL+asRqtK+DszDOzE= github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.0/go.mod h1:mOmGoNLPXLJ5tKLnhOnhKW7wL9Giy/4DowiDZNLlQCY= +github.com/stackitcloud/stackit-sdk-go/services/authorization v0.8.1/go.mod h1:OwQ+fYpON4WQpEinvI9lCTuuwj9UBCnPPJcnDpK803U= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0 h1:7ZKd3b+E/R4TEVShLTXxx5FrsuDuJBOyuVOuKTMa4mo= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0/go.mod h1:/FoXa6hF77Gv8brrvLBCKa5ie1Xy9xn39yfHwaln9Tw= github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1 h1:CnhAMLql0MNmAeq4roQKN8OpSKX4FSgTU6Eu6detB4I= github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1/go.mod h1:7Bx85knfNSBxulPdJUFuBePXNee3cO+sOTYnUG6M+iQ= -github.com/stackitcloud/stackit-sdk-go/services/git v0.8.0 h1:/weT7P5Uwy1Qlhw0NidqtQBlbbb/dQehweDV/I9ShXg= -github.com/stackitcloud/stackit-sdk-go/services/git v0.8.0/go.mod h1:AXFfYBJZIW1o0W0zZEb/proQMhMsb3Nn5E1htS8NDPE= -github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0 h1:dnEjyapuv8WwRN5vE2z6+4/+ZqQTBx+bX27x2nOF7Jw= -github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0/go.mod h1:854gnLR92NvAbJAA1xZEumrtNh1DoBP1FXTMvhwYA6w= -github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0 h1:q33ZaCBVEBUsnMDxYyuJKtJvGcE5nKgvuPed3s8zXNI= -github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0/go.mod h1:20QOZ3rBC9wTGgzXzLz9M6YheX0VaxWE0/JI+s8On7k= github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1 h1:hkFixFnBcQzU4BSIZFITc8N0gK0pUYk7mk0wdUu5Ki8= github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1/go.mod h1:Ng1EzrRndG3iGXGH90AZJz//wfK+2YOyDwTnTLwX3a4= +github.com/stackitcloud/stackit-sdk-go/services/git v0.8.0 h1:/weT7P5Uwy1Qlhw0NidqtQBlbbb/dQehweDV/I9ShXg= +github.com/stackitcloud/stackit-sdk-go/services/git v0.8.0/go.mod h1:AXFfYBJZIW1o0W0zZEb/proQMhMsb3Nn5E1htS8NDPE= github.com/stackitcloud/stackit-sdk-go/services/iaas v0.30.0 h1:01+noyCSadNH3ALHufcVXxNs0hBsetzJkOMN1Fe0VLc= github.com/stackitcloud/stackit-sdk-go/services/iaas v0.30.0/go.mod h1:854gnLR92NvAbJAA1xZEumrtNh1DoBP1FXTMvhwYA6w= +github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0 h1:dnEjyapuv8WwRN5vE2z6+4/+ZqQTBx+bX27x2nOF7Jw= +github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0/go.mod h1:854gnLR92NvAbJAA1xZEumrtNh1DoBP1FXTMvhwYA6w= github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.0 h1:IhswZoEHqkBW60wEmRaeoSrW68PGLQKrWVmMPMzwYrY= github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.0/go.mod h1:vRnT3zxWJ1k7wbAk8JmO0xFmPhmeos5HTIWdsVAAoKU= github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.1 h1:qKAGtRfnB89vXom5mIwctMHFeznMQWXJd3cqQBURIK8= @@ -588,6 +591,8 @@ github.com/stackitcloud/stackit-sdk-go/services/intake v0.2.0 h1:p/zi4VPoCQWk7/2 github.com/stackitcloud/stackit-sdk-go/services/intake v0.2.0/go.mod h1:jOArPjNRkwv4487+9ab3dRG+lM09leu5FiRohbQs9Z4= github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1 h1:OdJEs8eOfrzn9tCBDLxIyP8hX50zPfcXNYnRoQX+chs= github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.5.1/go.mod h1:11uzaOPCF9SeDHXEGOPMlHDD3J5r2TnvCGUwW9Igq9c= +github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0 h1:q33ZaCBVEBUsnMDxYyuJKtJvGcE5nKgvuPed3s8zXNI= +github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0/go.mod h1:20QOZ3rBC9wTGgzXzLz9M6YheX0VaxWE0/JI+s8On7k= github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1 h1:hv5WrRU9rN6Jx4OwdOGJRyaQrfA9p1tzEoQK6/CDyoA= github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1/go.mod h1:ivt8lvnAoBZsde2jSAuicyn6RgTmHvvNAJ3whaUbAD4= github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.1 h1:Db/ebOL2vbpIeh5XB2Ews2B9Lj5DJlMWIEJh60FfZ4Y= @@ -768,6 +773,7 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -810,6 +816,7 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -818,6 +825,7 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -835,6 +843,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -977,6 +986,7 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= @@ -1094,16 +1104,20 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= +k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= +k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= mvdan.cc/gofumpt v0.8.0 h1:nZUCeC2ViFaerTcYKstMmfysj6uhQrA2vJe+2vwGU6k= @@ -1113,11 +1127,14 @@ mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5gin rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= From b74ab8943ac7a29abbad33fc1fd68ce044c1aaaa Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 11:24:31 +0200 Subject: [PATCH 13/22] Address review: prevent nil pointer due to nil response --- go.mod | 2 +- go.sum | 14 ++++++++++++++ internal/cmd/intake/runner/create/create.go | 4 ++++ internal/cmd/intake/runner/create/create_test.go | 5 +++++ internal/cmd/intake/runner/update/update.go | 4 ++++ internal/cmd/intake/runner/update/update_test.go | 5 +++++ 6 files changed, 33 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9e6ec25b3..2fbd2a5d3 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1 github.com/stackitcloud/stackit-sdk-go/services/git v0.7.1 github.com/stackitcloud/stackit-sdk-go/services/iaas v0.30.0 + github.com/stackitcloud/stackit-sdk-go/services/intake v0.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/services/postgresflex v1.2.1 @@ -181,7 +182,6 @@ require ( github.com/sonatard/noctx v0.1.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect - github.com/stackitcloud/stackit-sdk-go/services/intake v0.1.0 // indirect github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.11.1 // indirect diff --git a/go.sum b/go.sum index dc005ff5f..9f25cd9f9 100644 --- a/go.sum +++ b/go.sum @@ -158,6 +158,7 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= @@ -290,6 +291,7 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -567,9 +569,11 @@ github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YE github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stackitcloud/stackit-sdk-go/core v0.17.3 h1:GsZGmRRc/3GJLmCUnsZswirr5wfLRrwavbnL/renOqg= github.com/stackitcloud/stackit-sdk-go/core v0.17.3/go.mod h1:HBCXJGPgdRulplDzhrmwC+Dak9B/x0nzNtmOpu+1Ahg= +github.com/stackitcloud/stackit-sdk-go/services/alb v0.6.1 h1:wJC/7mkIppHTBU0awGdLEFcmnjasp43MM9gX6/gdWvA= github.com/stackitcloud/stackit-sdk-go/services/alb v0.6.1/go.mod h1:UyN4hlkdiK5beDi5j9xHMENxRE9A9dlIWSXO/unhQW8= github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.0 h1:UM/HvewAgELEvZ4zk++Ykd6jUTSL+asRqtK+DszDOzE= github.com/stackitcloud/stackit-sdk-go/services/alb v0.7.0/go.mod h1:mOmGoNLPXLJ5tKLnhOnhKW7wL9Giy/4DowiDZNLlQCY= +github.com/stackitcloud/stackit-sdk-go/services/authorization v0.8.1 h1:Kzr1G4g9PHI8ePFnHrHZEX06XtEJQYBK9JExje0aXl0= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.8.1/go.mod h1:OwQ+fYpON4WQpEinvI9lCTuuwj9UBCnPPJcnDpK803U= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0 h1:7ZKd3b+E/R4TEVShLTXxx5FrsuDuJBOyuVOuKTMa4mo= github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0/go.mod h1:/FoXa6hF77Gv8brrvLBCKa5ie1Xy9xn39yfHwaln9Tw= @@ -773,6 +777,7 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= @@ -816,6 +821,7 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= @@ -825,6 +831,7 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= @@ -1104,12 +1111,15 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= +k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= +k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= @@ -1117,6 +1127,7 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= @@ -1127,14 +1138,17 @@ mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5gin rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/internal/cmd/intake/runner/create/create.go b/internal/cmd/intake/runner/create/create.go index 90ea735a2..a5bdaa134 100644 --- a/internal/cmd/intake/runner/create/create.go +++ b/internal/cmd/intake/runner/create/create.go @@ -169,6 +169,10 @@ func outputResult(p *print.Printer, outputFormat, projectLabel string, resp *int return nil default: + if resp == nil { + p.Outputf("Created Intake Runner for project %q, but no runner ID was returned.\n", projectLabel) + return nil + } p.Outputf("Created Intake Runner for project %q. Runner ID: %s\n", projectLabel, utils.PtrString(resp.Id)) return nil } diff --git a/internal/cmd/intake/runner/create/create_test.go b/internal/cmd/intake/runner/create/create_test.go index f0be33c41..d28a4d77c 100644 --- a/internal/cmd/intake/runner/create/create_test.go +++ b/internal/cmd/intake/runner/create/create_test.go @@ -284,6 +284,11 @@ func TestOutputResult(t *testing.T) { args: args{outputFormat: print.JSONOutputFormat, resp: nil}, wantErr: false, }, + { + name: "nil response - default output", + args: args{outputFormat: "default", resp: nil}, + wantErr: false, + }, } p := print.NewPrinter() p.Cmd = NewCreateCmd(¶ms.CmdParams{Printer: p}) diff --git a/internal/cmd/intake/runner/update/update.go b/internal/cmd/intake/runner/update/update.go index ccd9e8e06..40c0d8b4b 100644 --- a/internal/cmd/intake/runner/update/update.go +++ b/internal/cmd/intake/runner/update/update.go @@ -175,6 +175,10 @@ func outputResult(p *print.Printer, outputFormat, projectLabel string, resp *int return nil default: + if resp == nil { + p.Outputf("Updated Intake Runner for project %q, but no runner ID was returned.\n", projectLabel) + return nil + } p.Outputf("Updated Intake Runner for project %q. Runner ID: %s\n", projectLabel, utils.PtrString(resp.Id)) return nil } diff --git a/internal/cmd/intake/runner/update/update_test.go b/internal/cmd/intake/runner/update/update_test.go index ca1f63060..989226971 100644 --- a/internal/cmd/intake/runner/update/update_test.go +++ b/internal/cmd/intake/runner/update/update_test.go @@ -280,6 +280,11 @@ func TestOutputResult(t *testing.T) { args: args{outputFormat: print.JSONOutputFormat, resp: nil}, wantErr: false, }, + { + name: "nil response - default output", + args: args{outputFormat: "default", resp: nil}, + wantErr: false, + }, } p := print.NewPrinter() p.Cmd = NewUpdateCmd(¶ms.CmdParams{Printer: p}) From a8c658ceec52fb3a897346c75e24dcac4fd1c67e Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 11:29:45 +0200 Subject: [PATCH 14/22] Address review: adjust range to avoid using index --- internal/cmd/intake/runner/list/list.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/cmd/intake/runner/list/list.go b/internal/cmd/intake/runner/list/list.go index d9918937e..8b90d1b5b 100644 --- a/internal/cmd/intake/runner/list/list.go +++ b/internal/cmd/intake/runner/list/list.go @@ -163,8 +163,7 @@ func outputResult(p *print.Printer, outputFormat, projectLabel string, runners [ table := tables.NewTable() table.SetHeader("ID", "NAME") - for i := range runners { - runner := runners[i] + for _, runner := range runners { table.AddRow( runner.GetId(), runner.GetDisplayName(), From 0928e271175eb0669e09cdc5c6332d54433907d4 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 12:07:55 +0200 Subject: [PATCH 15/22] Address review: move some vars into constants in tests --- .../cmd/intake/runner/create/create_test.go | 18 ++++++++++-------- .../cmd/intake/runner/delete/delete_test.go | 5 ++++- .../intake/runner/describe/describe_test.go | 5 ++++- internal/cmd/intake/runner/list/list_test.go | 7 +++++-- .../cmd/intake/runner/update/update_test.go | 5 ++++- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/internal/cmd/intake/runner/create/create_test.go b/internal/cmd/intake/runner/create/create_test.go index d28a4d77c..ca5052918 100644 --- a/internal/cmd/intake/runner/create/create_test.go +++ b/internal/cmd/intake/runner/create/create_test.go @@ -17,21 +17,23 @@ import ( // 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() - testRegion = "eu01" - // Define test values for flags - testDisplayName = "testrunner" - testMaxMessageSizeKiB = int64(1024) - testMaxMessagesPerHour = int64(10000) - testDescription = "This is a test runner" - testLabels = map[string]string{"env": "test", "team": "dev"} - testLabelsString = "env=test,team=dev" + testLabels = map[string]string{"env": "test", "team": "dev"} ) // fixtureFlagValues generates a map of flag values for tests diff --git a/internal/cmd/intake/runner/delete/delete_test.go b/internal/cmd/intake/runner/delete/delete_test.go index c1d7c6d72..0ce29136a 100644 --- a/internal/cmd/intake/runner/delete/delete_test.go +++ b/internal/cmd/intake/runner/delete/delete_test.go @@ -16,6 +16,10 @@ import ( // 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") @@ -23,7 +27,6 @@ var ( testClient = &intake.APIClient{} testProjectId = uuid.NewString() testRunnerId = uuid.NewString() - testRegion = "eu01" ) // fixtureArgValues generates a slice of arguments for tests diff --git a/internal/cmd/intake/runner/describe/describe_test.go b/internal/cmd/intake/runner/describe/describe_test.go index 54553b2b1..9439fa385 100644 --- a/internal/cmd/intake/runner/describe/describe_test.go +++ b/internal/cmd/intake/runner/describe/describe_test.go @@ -15,12 +15,15 @@ import ( type testCtxKey struct{} +const ( + testRegion = "eu01" +) + var ( testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") testClient = &intake.APIClient{} testProjectId = uuid.NewString() testRunnerId = uuid.NewString() - testRegion = "eu01" ) func fixtureArgValues(mods ...func(argValues []string)) []string { diff --git a/internal/cmd/intake/runner/list/list_test.go b/internal/cmd/intake/runner/list/list_test.go index 0b2b92007..ac47cbe45 100644 --- a/internal/cmd/intake/runner/list/list_test.go +++ b/internal/cmd/intake/runner/list/list_test.go @@ -17,12 +17,15 @@ import ( type testCtxKey struct{} +const ( + testRegion = "eu01" + testLimit = int64(5) +) + var ( testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") testClient = &intake.APIClient{} testProjectId = uuid.NewString() - testRegion = "eu01" - testLimit = int64(5) ) func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { diff --git a/internal/cmd/intake/runner/update/update_test.go b/internal/cmd/intake/runner/update/update_test.go index 989226971..c8e0a071d 100644 --- a/internal/cmd/intake/runner/update/update_test.go +++ b/internal/cmd/intake/runner/update/update_test.go @@ -16,12 +16,15 @@ import ( type testCtxKey struct{} +const ( + testRegion = "eu01" +) + var ( testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") testClient = &intake.APIClient{} testProjectId = uuid.NewString() testRunnerId = uuid.NewString() - testRegion = "eu01" ) func fixtureArgValues(mods ...func(argValues []string)) []string { From 124868a97338ae0a2d8ecd51756d54917f29f341 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 12:21:38 +0200 Subject: [PATCH 16/22] Address review: move command to beta --- internal/cmd/beta/beta.go | 2 ++ internal/cmd/{ => beta}/intake/intake.go | 2 +- .../cmd/{ => beta}/intake/runner/create/create.go | 4 ++-- .../{ => beta}/intake/runner/create/create_test.go | 0 .../cmd/{ => beta}/intake/runner/delete/delete.go | 2 +- .../{ => beta}/intake/runner/delete/delete_test.go | 0 .../{ => beta}/intake/runner/describe/describe.go | 4 ++-- .../intake/runner/describe/describe_test.go | 0 internal/cmd/{ => beta}/intake/runner/list/list.go | 6 +++--- .../cmd/{ => beta}/intake/runner/list/list_test.go | 0 internal/cmd/{ => beta}/intake/runner/runner.go | 12 ++++++------ .../cmd/{ => beta}/intake/runner/update/update.go | 4 ++-- .../{ => beta}/intake/runner/update/update_test.go | 0 internal/cmd/root.go | 2 -- 14 files changed, 19 insertions(+), 19 deletions(-) rename internal/cmd/{ => beta}/intake/intake.go (90%) rename internal/cmd/{ => beta}/intake/runner/create/create.go (94%) rename internal/cmd/{ => beta}/intake/runner/create/create_test.go (100%) rename internal/cmd/{ => beta}/intake/runner/delete/delete.go (98%) rename internal/cmd/{ => beta}/intake/runner/delete/delete_test.go (100%) rename internal/cmd/{ => beta}/intake/runner/describe/describe.go (97%) rename internal/cmd/{ => beta}/intake/runner/describe/describe_test.go (100%) rename internal/cmd/{ => beta}/intake/runner/list/list.go (96%) rename internal/cmd/{ => beta}/intake/runner/list/list_test.go (100%) rename internal/cmd/{ => beta}/intake/runner/runner.go (62%) rename internal/cmd/{ => beta}/intake/runner/update/update.go (96%) rename internal/cmd/{ => beta}/intake/runner/update/update_test.go (100%) diff --git a/internal/cmd/beta/beta.go b/internal/cmd/beta/beta.go index 5a007b87e..85acbf950 100644 --- a/internal/cmd/beta/beta.go +++ b/internal/cmd/beta/beta.go @@ -2,6 +2,7 @@ package beta import ( "fmt" + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/alb" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/sqlserverflex" @@ -38,4 +39,5 @@ func NewCmd(params *params.CmdParams) *cobra.Command { func addSubcommands(cmd *cobra.Command, params *params.CmdParams) { cmd.AddCommand(sqlserverflex.NewCmd(params)) cmd.AddCommand(alb.NewCmd(params)) + cmd.AddCommand(intake.NewCmd(params)) } diff --git a/internal/cmd/intake/intake.go b/internal/cmd/beta/intake/intake.go similarity index 90% rename from internal/cmd/intake/intake.go rename to internal/cmd/beta/intake/intake.go index fc5c7e4d1..96533f29c 100644 --- a/internal/cmd/intake/intake.go +++ b/internal/cmd/beta/intake/intake.go @@ -2,7 +2,7 @@ package intake import ( "github.com/spf13/cobra" - "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner" + "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" diff --git a/internal/cmd/intake/runner/create/create.go b/internal/cmd/beta/intake/runner/create/create.go similarity index 94% rename from internal/cmd/intake/runner/create/create.go rename to internal/cmd/beta/intake/runner/create/create.go index a5bdaa134..6305b1913 100644 --- a/internal/cmd/intake/runner/create/create.go +++ b/internal/cmd/beta/intake/runner/create/create.go @@ -48,10 +48,10 @@ func NewCreateCmd(p *params.CmdParams) *cobra.Command { Example: examples.Build( examples.NewExample( `Create a new Intake Runner with a display name and message capacity limits`, - `$ stackit intake runner create --display-name my-runner --max-message-size-kib 1000 --max-messages-per-hour 5000`), + `$ 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 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"`), + `$ 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() diff --git a/internal/cmd/intake/runner/create/create_test.go b/internal/cmd/beta/intake/runner/create/create_test.go similarity index 100% rename from internal/cmd/intake/runner/create/create_test.go rename to internal/cmd/beta/intake/runner/create/create_test.go diff --git a/internal/cmd/intake/runner/delete/delete.go b/internal/cmd/beta/intake/runner/delete/delete.go similarity index 98% rename from internal/cmd/intake/runner/delete/delete.go rename to internal/cmd/beta/intake/runner/delete/delete.go index 747d602e9..31c5b731a 100644 --- a/internal/cmd/intake/runner/delete/delete.go +++ b/internal/cmd/beta/intake/runner/delete/delete.go @@ -36,7 +36,7 @@ func NewDeleteCmd(p *params.CmdParams) *cobra.Command { Example: examples.Build( examples.NewExample( `Delete an Intake Runner with ID "xxx"`, - `$ stackit intake runner delete xxx`), + `$ stackit beta intake runner delete xxx`), ), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() diff --git a/internal/cmd/intake/runner/delete/delete_test.go b/internal/cmd/beta/intake/runner/delete/delete_test.go similarity index 100% rename from internal/cmd/intake/runner/delete/delete_test.go rename to internal/cmd/beta/intake/runner/delete/delete_test.go diff --git a/internal/cmd/intake/runner/describe/describe.go b/internal/cmd/beta/intake/runner/describe/describe.go similarity index 97% rename from internal/cmd/intake/runner/describe/describe.go rename to internal/cmd/beta/intake/runner/describe/describe.go index 68e06a07f..3418315ac 100644 --- a/internal/cmd/intake/runner/describe/describe.go +++ b/internal/cmd/beta/intake/runner/describe/describe.go @@ -38,10 +38,10 @@ func NewDescribeCmd(p *params.CmdParams) *cobra.Command { Example: examples.Build( examples.NewExample( `Get details of an Intake Runner with ID "xxx"`, - `$ stackit intake runner describe xxx`), + `$ stackit beta intake runner describe xxx`), examples.NewExample( `Get details of an Intake Runner with ID "xxx" in JSON format`, - `$ stackit intake runner describe xxx --output-format json`), + `$ stackit beta intake runner describe xxx --output-format json`), ), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() diff --git a/internal/cmd/intake/runner/describe/describe_test.go b/internal/cmd/beta/intake/runner/describe/describe_test.go similarity index 100% rename from internal/cmd/intake/runner/describe/describe_test.go rename to internal/cmd/beta/intake/runner/describe/describe_test.go diff --git a/internal/cmd/intake/runner/list/list.go b/internal/cmd/beta/intake/runner/list/list.go similarity index 96% rename from internal/cmd/intake/runner/list/list.go rename to internal/cmd/beta/intake/runner/list/list.go index 8b90d1b5b..ebb328fb1 100644 --- a/internal/cmd/intake/runner/list/list.go +++ b/internal/cmd/beta/intake/runner/list/list.go @@ -40,13 +40,13 @@ func NewListCmd(p *params.CmdParams) *cobra.Command { Example: examples.Build( examples.NewExample( `List all Intake Runners`, - `$ stackit intake runner list`), + `$ stackit beta intake runner list`), examples.NewExample( `List all Intake Runners in JSON format`, - `$ stackit intake runner list --output-format json`), + `$ stackit beta intake runner list --output-format json`), examples.NewExample( `List up to 5 Intake Runners`, - `$ stackit intake runner list --limit 5`), + `$ stackit beta intake runner list --limit 5`), ), RunE: func(cmd *cobra.Command, _ []string) error { ctx := context.Background() diff --git a/internal/cmd/intake/runner/list/list_test.go b/internal/cmd/beta/intake/runner/list/list_test.go similarity index 100% rename from internal/cmd/intake/runner/list/list_test.go rename to internal/cmd/beta/intake/runner/list/list_test.go diff --git a/internal/cmd/intake/runner/runner.go b/internal/cmd/beta/intake/runner/runner.go similarity index 62% rename from internal/cmd/intake/runner/runner.go rename to internal/cmd/beta/intake/runner/runner.go index 33d1e4861..42e3bae4b 100644 --- a/internal/cmd/intake/runner/runner.go +++ b/internal/cmd/beta/intake/runner/runner.go @@ -2,11 +2,11 @@ package runner import ( "github.com/spf13/cobra" - "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner/create" - "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner/delete" - "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner/describe" - "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner/list" - "github.com/stackitcloud/stackit-cli/internal/cmd/intake/runner/update" + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake/runner/create" + delete2 "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" @@ -22,7 +22,7 @@ func NewCmd(params *params.CmdParams) *cobra.Command { } // Pass the params down to each action command cmd.AddCommand(create.NewCreateCmd(params)) - cmd.AddCommand(delete.NewDeleteCmd(params)) + cmd.AddCommand(delete2.NewDeleteCmd(params)) cmd.AddCommand(describe.NewDescribeCmd(params)) cmd.AddCommand(list.NewListCmd(params)) cmd.AddCommand(update.NewUpdateCmd(params)) diff --git a/internal/cmd/intake/runner/update/update.go b/internal/cmd/beta/intake/runner/update/update.go similarity index 96% rename from internal/cmd/intake/runner/update/update.go rename to internal/cmd/beta/intake/runner/update/update.go index 40c0d8b4b..f67634cf9 100644 --- a/internal/cmd/intake/runner/update/update.go +++ b/internal/cmd/beta/intake/runner/update/update.go @@ -52,10 +52,10 @@ func NewUpdateCmd(p *params.CmdParams) *cobra.Command { Example: examples.Build( examples.NewExample( `Update the display name of an Intake Runner with ID "xxx"`, - `$ stackit intake runner update xxx --display-name "new-runner-name"`), + `$ 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 intake runner update xxx --max-message-size-kib 1000 --max-messages-per-hour 10000`), + `$ 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() diff --git a/internal/cmd/intake/runner/update/update_test.go b/internal/cmd/beta/intake/runner/update/update_test.go similarity index 100% rename from internal/cmd/intake/runner/update/update_test.go rename to internal/cmd/beta/intake/runner/update/update_test.go diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 9d2566c06..4e0ac6ea9 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -14,7 +14,6 @@ import ( "github.com/stackitcloud/stackit-cli/internal/cmd/dns" "github.com/stackitcloud/stackit-cli/internal/cmd/git" "github.com/stackitcloud/stackit-cli/internal/cmd/image" - "github.com/stackitcloud/stackit-cli/internal/cmd/intake" keypair "github.com/stackitcloud/stackit-cli/internal/cmd/key-pair" loadbalancer "github.com/stackitcloud/stackit-cli/internal/cmd/load-balancer" "github.com/stackitcloud/stackit-cli/internal/cmd/logme" @@ -193,7 +192,6 @@ func addSubcommands(cmd *cobra.Command, params *params.CmdParams) { cmd.AddCommand(quota.NewCmd(params)) cmd.AddCommand(affinityGroups.NewCmd(params)) cmd.AddCommand(git.NewCmd(params)) - cmd.AddCommand(intake.NewCmd(params)) } // traverseCommands calls f for c and all of its children. From 97fde4079fb9739c02bb050704b8eb8c03708be5 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 13:43:14 +0200 Subject: [PATCH 17/22] Address review: include state, creation time and labels --- internal/cmd/beta/intake/runner/describe/describe.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/cmd/beta/intake/runner/describe/describe.go b/internal/cmd/beta/intake/runner/describe/describe.go index 3418315ac..acd0d8a13 100644 --- a/internal/cmd/beta/intake/runner/describe/describe.go +++ b/internal/cmd/beta/intake/runner/describe/describe.go @@ -128,6 +128,9 @@ func outputResult(p *print.Printer, outputFormat string, runner *intake.IntakeRu 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()) From 12aa33e44fcfcb1593713675d326ff4bf2312d86 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 13:49:26 +0200 Subject: [PATCH 18/22] Address review: add message for delete operation --- internal/cmd/beta/intake/runner/delete/delete.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/cmd/beta/intake/runner/delete/delete.go b/internal/cmd/beta/intake/runner/delete/delete.go index 31c5b731a..ea7b0d4d8 100644 --- a/internal/cmd/beta/intake/runner/delete/delete.go +++ b/internal/cmd/beta/intake/runner/delete/delete.go @@ -64,6 +64,7 @@ func NewDeleteCmd(p *params.CmdParams) *cobra.Command { if err = req.Execute(); err != nil { return fmt.Errorf("delete Intake Runner: %w", err) } + p.Printer.Info("Deleted instance %s\n", model.RunnerId) return nil }, From 9f351343c1e49d7144a31658cb04102825a6671a Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 13:51:44 +0200 Subject: [PATCH 19/22] Address review: add message for delete operation --- internal/cmd/beta/intake/runner/delete/delete.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/beta/intake/runner/delete/delete.go b/internal/cmd/beta/intake/runner/delete/delete.go index ea7b0d4d8..2c1c3c3fc 100644 --- a/internal/cmd/beta/intake/runner/delete/delete.go +++ b/internal/cmd/beta/intake/runner/delete/delete.go @@ -64,7 +64,7 @@ func NewDeleteCmd(p *params.CmdParams) *cobra.Command { if err = req.Execute(); err != nil { return fmt.Errorf("delete Intake Runner: %w", err) } - p.Printer.Info("Deleted instance %s\n", model.RunnerId) + p.Printer.Info("Deleted runner %s\n", model.RunnerId) return nil }, From 247c065911cb66e4cd12decf2a4fe49eb79ade64 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 13:57:06 +0200 Subject: [PATCH 20/22] Re-generate docs --- docs/stackit.md | 1 - docs/stackit_beta.md | 1 + ...ackit_intake.md => stackit_beta_intake.md} | 10 ++--- docs/stackit_beta_intake_runner.md | 38 +++++++++++++++++++ ...d => stackit_beta_intake_runner_create.md} | 12 +++--- ...d => stackit_beta_intake_runner_delete.md} | 10 ++--- ...=> stackit_beta_intake_runner_describe.md} | 12 +++--- ....md => stackit_beta_intake_runner_list.md} | 14 +++---- ...d => stackit_beta_intake_runner_update.md} | 17 ++++----- docs/stackit_intake_runner.md | 38 ------------------- 10 files changed, 75 insertions(+), 78 deletions(-) rename docs/{stackit_intake.md => stackit_beta_intake.md} (69%) create mode 100644 docs/stackit_beta_intake_runner.md rename docs/{stackit_intake_runner_create.md => stackit_beta_intake_runner_create.md} (66%) rename docs/{stackit_intake_runner_delete.md => stackit_beta_intake_runner_delete.md} (69%) rename docs/{stackit_intake_runner_describe.md => stackit_beta_intake_runner_describe.md} (67%) rename docs/{stackit_intake_runner_list.md => stackit_beta_intake_runner_list.md} (68%) rename docs/{stackit_intake_runner_update.md => stackit_beta_intake_runner_update.md} (62%) delete mode 100644 docs/stackit_intake_runner.md diff --git a/docs/stackit.md b/docs/stackit.md index 2c0e0b0fa..d0ddc4554 100644 --- a/docs/stackit.md +++ b/docs/stackit.md @@ -34,7 +34,6 @@ stackit [flags] * [stackit dns](./stackit_dns.md) - Provides functionality for DNS * [stackit git](./stackit_git.md) - Provides functionality for STACKIT Git * [stackit image](./stackit_image.md) - Manage server images -* [stackit intake](./stackit_intake.md) - Provides functionality for intake * [stackit key-pair](./stackit_key-pair.md) - Provides functionality for SSH key pairs * [stackit load-balancer](./stackit_load-balancer.md) - Provides functionality for Load Balancer * [stackit logme](./stackit_logme.md) - Provides functionality for LogMe diff --git a/docs/stackit_beta.md b/docs/stackit_beta.md index b58eb067a..248bf091d 100644 --- a/docs/stackit_beta.md +++ b/docs/stackit_beta.md @@ -42,5 +42,6 @@ stackit beta [flags] * [stackit](./stackit.md) - Manage STACKIT resources using the command line * [stackit beta alb](./stackit_beta_alb.md) - Manages application loadbalancers +* [stackit beta intake](./stackit_beta_intake.md) - Provides functionality for intake * [stackit beta sqlserverflex](./stackit_beta_sqlserverflex.md) - Provides functionality for SQLServer Flex diff --git a/docs/stackit_intake.md b/docs/stackit_beta_intake.md similarity index 69% rename from docs/stackit_intake.md rename to docs/stackit_beta_intake.md index 16cbad26e..f44d3c12d 100644 --- a/docs/stackit_intake.md +++ b/docs/stackit_beta_intake.md @@ -1,4 +1,4 @@ -## stackit intake +## stackit beta intake Provides functionality for intake @@ -7,13 +7,13 @@ Provides functionality for intake Provides functionality for intake. ``` -stackit intake [flags] +stackit beta intake [flags] ``` ### Options ``` - -h, --help Help for "stackit intake" + -h, --help Help for "stackit beta intake" ``` ### Options inherited from parent commands @@ -29,6 +29,6 @@ stackit intake [flags] ### SEE ALSO -* [stackit](./stackit.md) - Manage STACKIT resources using the command line -* [stackit intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners +* [stackit beta](./stackit_beta.md) - Contains beta STACKIT CLI commands +* [stackit beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners diff --git a/docs/stackit_beta_intake_runner.md b/docs/stackit_beta_intake_runner.md new file mode 100644 index 000000000..7d5c60ff3 --- /dev/null +++ b/docs/stackit_beta_intake_runner.md @@ -0,0 +1,38 @@ +## 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 + diff --git a/docs/stackit_intake_runner_create.md b/docs/stackit_beta_intake_runner_create.md similarity index 66% rename from docs/stackit_intake_runner_create.md rename to docs/stackit_beta_intake_runner_create.md index 607afe062..8903cef9d 100644 --- a/docs/stackit_intake_runner_create.md +++ b/docs/stackit_beta_intake_runner_create.md @@ -1,4 +1,4 @@ -## stackit intake runner create +## stackit beta intake runner create Creates a new Intake Runner @@ -7,17 +7,17 @@ Creates a new Intake Runner Creates a new Intake Runner. ``` -stackit intake runner create [flags] +stackit beta intake runner create [flags] ``` ### Examples ``` Create a new Intake Runner with a display name and message capacity limits - $ stackit intake runner create --display-name my-runner --max-message-size-kib 1000 --max-messages-per-hour 5000 + $ 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 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" + $ 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 @@ -25,7 +25,7 @@ stackit intake runner create [flags] ``` --description string Description --display-name string Display name - -h, --help Help for "stackit intake runner create" + -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 @@ -44,5 +44,5 @@ stackit intake runner create [flags] ### SEE ALSO -* [stackit intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners +* [stackit beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners diff --git a/docs/stackit_intake_runner_delete.md b/docs/stackit_beta_intake_runner_delete.md similarity index 69% rename from docs/stackit_intake_runner_delete.md rename to docs/stackit_beta_intake_runner_delete.md index 98ddfd193..0fa94ae5f 100644 --- a/docs/stackit_intake_runner_delete.md +++ b/docs/stackit_beta_intake_runner_delete.md @@ -1,4 +1,4 @@ -## stackit intake runner delete +## stackit beta intake runner delete Deletes an Intake Runner @@ -7,20 +7,20 @@ Deletes an Intake Runner Deletes an Intake Runner. ``` -stackit intake runner delete RUNNER_ID [flags] +stackit beta intake runner delete RUNNER_ID [flags] ``` ### Examples ``` Delete an Intake Runner with ID "xxx" - $ stackit intake runner delete xxx + $ stackit beta intake runner delete xxx ``` ### Options ``` - -h, --help Help for "stackit intake runner delete" + -h, --help Help for "stackit beta intake runner delete" ``` ### Options inherited from parent commands @@ -36,5 +36,5 @@ stackit intake runner delete RUNNER_ID [flags] ### SEE ALSO -* [stackit intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners +* [stackit beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners diff --git a/docs/stackit_intake_runner_describe.md b/docs/stackit_beta_intake_runner_describe.md similarity index 67% rename from docs/stackit_intake_runner_describe.md rename to docs/stackit_beta_intake_runner_describe.md index f15b5a674..11814b10d 100644 --- a/docs/stackit_intake_runner_describe.md +++ b/docs/stackit_beta_intake_runner_describe.md @@ -1,4 +1,4 @@ -## stackit intake runner describe +## stackit beta intake runner describe Shows details of an Intake Runner @@ -7,23 +7,23 @@ Shows details of an Intake Runner Shows details of an Intake Runner. ``` -stackit intake runner describe RUNNER_ID [flags] +stackit beta intake runner describe RUNNER_ID [flags] ``` ### Examples ``` Get details of an Intake Runner with ID "xxx" - $ stackit intake runner describe xxx + $ stackit beta intake runner describe xxx Get details of an Intake Runner with ID "xxx" in JSON format - $ stackit intake runner describe xxx --output-format json + $ stackit beta intake runner describe xxx --output-format json ``` ### Options ``` - -h, --help Help for "stackit intake runner describe" + -h, --help Help for "stackit beta intake runner describe" ``` ### Options inherited from parent commands @@ -39,5 +39,5 @@ stackit intake runner describe RUNNER_ID [flags] ### SEE ALSO -* [stackit intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners +* [stackit beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners diff --git a/docs/stackit_intake_runner_list.md b/docs/stackit_beta_intake_runner_list.md similarity index 68% rename from docs/stackit_intake_runner_list.md rename to docs/stackit_beta_intake_runner_list.md index 1a6ee9cf5..aaf5c9e59 100644 --- a/docs/stackit_intake_runner_list.md +++ b/docs/stackit_beta_intake_runner_list.md @@ -1,4 +1,4 @@ -## stackit intake runner list +## stackit beta intake runner list Lists all Intake Runners @@ -7,26 +7,26 @@ Lists all Intake Runners Lists all Intake Runners for the current project. ``` -stackit intake runner list [flags] +stackit beta intake runner list [flags] ``` ### Examples ``` List all Intake Runners - $ stackit intake runner list + $ stackit beta intake runner list List all Intake Runners in JSON format - $ stackit intake runner list --output-format json + $ stackit beta intake runner list --output-format json List up to 5 Intake Runners - $ stackit intake runner list --limit 5 + $ stackit beta intake runner list --limit 5 ``` ### Options ``` - -h, --help Help for "stackit intake runner list" + -h, --help Help for "stackit beta intake runner list" --limit int Maximum number of entries to list ``` @@ -43,5 +43,5 @@ stackit intake runner list [flags] ### SEE ALSO -* [stackit intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners +* [stackit beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners diff --git a/docs/stackit_intake_runner_update.md b/docs/stackit_beta_intake_runner_update.md similarity index 62% rename from docs/stackit_intake_runner_update.md rename to docs/stackit_beta_intake_runner_update.md index 5cdef4ba2..d02cb7c84 100644 --- a/docs/stackit_intake_runner_update.md +++ b/docs/stackit_beta_intake_runner_update.md @@ -1,4 +1,4 @@ -## stackit intake runner update +## stackit beta intake runner update Updates an Intake Runner @@ -7,20 +7,17 @@ Updates an Intake Runner Updates an Intake Runner. Only the specified fields are updated. ``` -stackit intake runner update RUNNER_ID [flags] +stackit beta intake runner update RUNNER_ID [flags] ``` ### Examples ``` Update the display name of an Intake Runner with ID "xxx" - $ stackit intake runner update xxx --display-name "new-runner-name" + $ stackit beta intake runner update xxx --display-name "new-runner-name" Update the message capacity limits for an Intake Runner with ID "xxx" - $ stackit intake runner update xxx --max-message-size-kib 2000 --max-messages-per-hour 10000 - - Clear the labels of an Intake Runner with ID "xxx" by providing an empty value - $ stackit intake runner update xxx --labels "" + $ stackit beta intake runner update xxx --max-message-size-kib 1000 --max-messages-per-hour 10000 ``` ### Options @@ -28,8 +25,8 @@ stackit intake runner update RUNNER_ID [flags] ``` --description string Description --display-name string Display name - -h, --help Help for "stackit intake runner update" - --labels stringToString Labels in key=value format. To clear all labels, provide an empty string, e.g. --labels "" (default []) + -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. ``` @@ -47,5 +44,5 @@ stackit intake runner update RUNNER_ID [flags] ### SEE ALSO -* [stackit intake runner](./stackit_intake_runner.md) - Provides functionality for Intake Runners +* [stackit beta intake runner](./stackit_beta_intake_runner.md) - Provides functionality for Intake Runners diff --git a/docs/stackit_intake_runner.md b/docs/stackit_intake_runner.md deleted file mode 100644 index 90ca7b0e8..000000000 --- a/docs/stackit_intake_runner.md +++ /dev/null @@ -1,38 +0,0 @@ -## stackit intake runner - -Provides functionality for Intake Runners - -### Synopsis - -Provides functionality for Intake Runners. - -``` -stackit intake runner [flags] -``` - -### Options - -``` - -h, --help Help for "stackit 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 intake](./stackit_intake.md) - Provides functionality for intake -* [stackit intake runner create](./stackit_intake_runner_create.md) - Creates a new Intake Runner -* [stackit intake runner delete](./stackit_intake_runner_delete.md) - Deletes an Intake Runner -* [stackit intake runner describe](./stackit_intake_runner_describe.md) - Shows details of an Intake Runner -* [stackit intake runner list](./stackit_intake_runner_list.md) - Lists all Intake Runners -* [stackit intake runner update](./stackit_intake_runner_update.md) - Updates an Intake Runner - From 52bd9225d3f495692d0522db6e728225288a9a4f Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 13:57:47 +0200 Subject: [PATCH 21/22] lint --- internal/cmd/beta/beta.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/beta/beta.go b/internal/cmd/beta/beta.go index 85acbf950..e6d111215 100644 --- a/internal/cmd/beta/beta.go +++ b/internal/cmd/beta/beta.go @@ -2,9 +2,9 @@ package beta import ( "fmt" - "github.com/stackitcloud/stackit-cli/internal/cmd/beta/intake" "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/sqlserverflex" "github.com/stackitcloud/stackit-cli/internal/cmd/params" "github.com/stackitcloud/stackit-cli/internal/pkg/args" From 90c2584c721c2d7b556adad795ff267f46243eb8 Mon Sep 17 00:00:00 2001 From: Yago Carlos Fernandez Gou Date: Thu, 9 Oct 2025 13:58:13 +0200 Subject: [PATCH 22/22] install dependencies required by new linter version --- go.sum | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.sum b/go.sum index 9f25cd9f9..09e805656 100644 --- a/go.sum +++ b/go.sum @@ -850,6 +850,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= @@ -993,6 +994,7 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=