From 09846ac1e9dc6ec6d8e7dc6c52fd04b96f124e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Thu, 5 Dec 2024 16:25:43 +0100 Subject: [PATCH 01/19] onboard security group rule commands --- README.md | 44 +-- docs/stackit_beta.md | 1 + docs/stackit_beta_security-group-rules.md | 36 ++ ...tackit_beta_security-group-rules_create.md | 60 +++ ...tackit_beta_security-group-rules_delete.md | 42 +++ ...ckit_beta_security-group-rules_describe.md | 43 +++ .../stackit_beta_security-group-rules_list.md | 47 +++ internal/cmd/beta/beta.go | 2 + .../beta/security-group-rule/create/create.go | 262 ++++++++++++++ .../security-group-rule/create/create_test.go | 342 ++++++++++++++++++ .../beta/security-group-rule/delete/delete.go | 130 +++++++ .../security-group-rule/delete/delete_test.go | 232 ++++++++++++ .../security-group-rule/describe/describe.go | 160 ++++++++ .../describe/describe_test.go | 243 +++++++++++++ .../cmd/beta/security-group-rule/list/list.go | 179 +++++++++ .../security-group-rule/list/list_test.go | 214 +++++++++++ .../security-group-rule.go | 32 ++ internal/pkg/errors/errors.go | 20 + internal/pkg/services/iaas/utils/utils.go | 19 + .../pkg/services/iaas/utils/utils_test.go | 139 ++++++- 20 files changed, 2211 insertions(+), 36 deletions(-) create mode 100644 docs/stackit_beta_security-group-rules.md create mode 100644 docs/stackit_beta_security-group-rules_create.md create mode 100644 docs/stackit_beta_security-group-rules_delete.md create mode 100644 docs/stackit_beta_security-group-rules_describe.md create mode 100644 docs/stackit_beta_security-group-rules_list.md create mode 100644 internal/cmd/beta/security-group-rule/create/create.go create mode 100644 internal/cmd/beta/security-group-rule/create/create_test.go create mode 100644 internal/cmd/beta/security-group-rule/delete/delete.go create mode 100644 internal/cmd/beta/security-group-rule/delete/delete_test.go create mode 100644 internal/cmd/beta/security-group-rule/describe/describe.go create mode 100644 internal/cmd/beta/security-group-rule/describe/describe_test.go create mode 100644 internal/cmd/beta/security-group-rule/list/list.go create mode 100644 internal/cmd/beta/security-group-rule/list/list_test.go create mode 100644 internal/cmd/beta/security-group-rule/security-group-rule.go diff --git a/README.md b/README.md index 888f2e03d..be934f333 100644 --- a/README.md +++ b/README.md @@ -65,28 +65,28 @@ Help is available for any command by specifying the special flag `--help` (or si Below you can find a list of the STACKIT services already available in the CLI (along with their respective command names) and the ones that are currently planned to be integrated. -| Service | CLI Commands | Status | -| ---------------------------------- |----------------------------------------------------------------------------------------------------------------------| ------------------------- | -| Observability | `observability` | :white_check_mark: | -| Infrastructure as a Service (IaaS) | `beta network-area`
`beta network`
`beta volume`
`beta network-interface`
`beta public-ip` | :white_check_mark: (beta) | -| Authorization | `project`, `organization` | :white_check_mark: | -| DNS | `dns` | :white_check_mark: | -| Kubernetes Engine (SKE) | `ske` | :white_check_mark: | -| Load Balancer | `load-balancer` | :white_check_mark: | -| LogMe | `logme` | :white_check_mark: | -| MariaDB | `mariadb` | :white_check_mark: | -| MongoDB Flex | `mongodbflex` | :white_check_mark: | -| Object Storage | `object-storage` | :white_check_mark: | -| OpenSearch | `opensearch` | :white_check_mark: | -| PostgreSQL Flex | `postgresflex` | :white_check_mark: | -| RabbitMQ | `rabbitmq` | :white_check_mark: | -| Redis | `redis` | :white_check_mark: | -| Resource Manager | `project` | :white_check_mark: | -| Secrets Manager | `secrets-manager` | :white_check_mark: | -| Server Backup Management | `beta server backup` | :white_check_mark: (beta) | -| Server Command (Run Command) | `beta server command` | :white_check_mark: (beta) | -| Service Account | `service-account` | :white_check_mark: | -| SQLServer Flex | `beta sqlserverflex` | :white_check_mark: (beta) | +| Service | CLI Commands | Status | +| ---------------------------------- |-------------------------------------------------------------------------------------------------------------------------------------------------------| ------------------------- | +| Observability | `observability` | :white_check_mark: | +| Infrastructure as a Service (IaaS) | `beta network-area`
`beta network`
`beta volume`
`beta network-interface`
`beta public-ip`
`beta security-group-rule` | :white_check_mark: (beta) | +| Authorization | `project`, `organization` | :white_check_mark: | +| DNS | `dns` | :white_check_mark: | +| Kubernetes Engine (SKE) | `ske` | :white_check_mark: | +| Load Balancer | `load-balancer` | :white_check_mark: | +| LogMe | `logme` | :white_check_mark: | +| MariaDB | `mariadb` | :white_check_mark: | +| MongoDB Flex | `mongodbflex` | :white_check_mark: | +| Object Storage | `object-storage` | :white_check_mark: | +| OpenSearch | `opensearch` | :white_check_mark: | +| PostgreSQL Flex | `postgresflex` | :white_check_mark: | +| RabbitMQ | `rabbitmq` | :white_check_mark: | +| Redis | `redis` | :white_check_mark: | +| Resource Manager | `project` | :white_check_mark: | +| Secrets Manager | `secrets-manager` | :white_check_mark: | +| Server Backup Management | `beta server backup` | :white_check_mark: (beta) | +| Server Command (Run Command) | `beta server command` | :white_check_mark: (beta) | +| Service Account | `service-account` | :white_check_mark: | +| SQLServer Flex | `beta sqlserverflex` | :white_check_mark: (beta) | ## Authentication diff --git a/docs/stackit_beta.md b/docs/stackit_beta.md index abc498479..dd4c22c40 100644 --- a/docs/stackit_beta.md +++ b/docs/stackit_beta.md @@ -44,6 +44,7 @@ stackit beta [flags] * [stackit beta network-area](./stackit_beta_network-area.md) - Provides functionality for STACKIT Network Area (SNA) * [stackit beta network-interface](./stackit_beta_network-interface.md) - Provides functionality for network interfaces * [stackit beta public-ip](./stackit_beta_public-ip.md) - Provides functionality for public IPs +* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules * [stackit beta server](./stackit_beta_server.md) - Provides functionality for servers * [stackit beta sqlserverflex](./stackit_beta_sqlserverflex.md) - Provides functionality for SQLServer Flex * [stackit beta volume](./stackit_beta_volume.md) - Provides functionality for volumes diff --git a/docs/stackit_beta_security-group-rules.md b/docs/stackit_beta_security-group-rules.md new file mode 100644 index 000000000..478fde37c --- /dev/null +++ b/docs/stackit_beta_security-group-rules.md @@ -0,0 +1,36 @@ +## stackit beta security-group-rules + +Provides functionality for security group rules + +### Synopsis + +Provides functionality for security group rules. + +``` +stackit beta security-group-rules [flags] +``` + +### Options + +``` + -h, --help Help for "stackit beta security-group-rules" +``` + +### Options inherited from parent commands + +``` + -y, --assume-yes If set, skips all confirmation prompts + --async If set, runs the command asynchronously + -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] + -p, --project-id string Project ID + --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") +``` + +### SEE ALSO + +* [stackit beta](./stackit_beta.md) - Contains beta STACKIT CLI commands +* [stackit beta security-group-rules create](./stackit_beta_security-group-rules_create.md) - Creates a security group rule +* [stackit beta security-group-rules delete](./stackit_beta_security-group-rules_delete.md) - Deletes a security group rule +* [stackit beta security-group-rules describe](./stackit_beta_security-group-rules_describe.md) - Shows details of a security group rule +* [stackit beta security-group-rules list](./stackit_beta_security-group-rules_list.md) - Lists all security group rules in a security group of a project + diff --git a/docs/stackit_beta_security-group-rules_create.md b/docs/stackit_beta_security-group-rules_create.md new file mode 100644 index 000000000..b8ddcce05 --- /dev/null +++ b/docs/stackit_beta_security-group-rules_create.md @@ -0,0 +1,60 @@ +## stackit beta security-group-rules create + +Creates a security group rule + +### Synopsis + +Creates a security group rule. + +``` +stackit beta security-group-rules create [flags] +``` + +### Examples + +``` + Create a security group rule for security group with ID "xxx" with direction "ingress" + $ stackit beta security-group-rule create --security-group-id xxx --direction ingress + + Create a security group rule for security group with ID "xxx" with direction "egress", protocol "icmp" and icmp parameters + $ stackit beta security-group-rule create --security-group-id xxx --direction egress --protocol icmp --icmp-parameter-code 0 --icmp-parameter-type 8 + + Create a security group rule for security group with ID "xxx" with direction "ingress" and port range values + $ stackit beta security-group-rule create --security-group-id xxx --direction ingress --port-range-max 24 --port-range-min 22 + + Create a security group rule for security group with ID "xxx" with direction "ingress" and protocol number 1 + $ stackit beta security-group-rule create --security-group-id xxx --direction ingress --protocol-number 1 +``` + +### Options + +``` + --description string The rule description + --direction ingress The direction of the traffic which the rule should match. Some of the possible values are: ingress, `egress` + --ether-type string The ethertype which the rule should match + -h, --help Help for "stackit beta security-group-rules create" + --icmp-parameter-code int ICMP code. Can be set if the protocol is ICMP + --icmp-parameter-type int ICMP type. Can be set if the protocol is ICMP + --ip-range string The remote IP range which the rule should match + --port-range-max int The maximum port number. Should be greater or equal to the minimum. This should only be provided if the protocol is not ICMP + --port-range-min int The minimum port number. Should be less or equal to the maximum. This should only be provided if the protocol is not ICMP + --protocol-name name The protocol name which the rule should match. Either name or `number` must be provided + --protocol-number name The protocol number which the rule should match. Either name or `number` must be provided + --remote-security-group-id string The remote security group which the rule should match + --security-group-id string The security group ID +``` + +### Options inherited from parent commands + +``` + -y, --assume-yes If set, skips all confirmation prompts + --async If set, runs the command asynchronously + -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] + -p, --project-id string Project ID + --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") +``` + +### SEE ALSO + +* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules + diff --git a/docs/stackit_beta_security-group-rules_delete.md b/docs/stackit_beta_security-group-rules_delete.md new file mode 100644 index 000000000..9e31a9612 --- /dev/null +++ b/docs/stackit_beta_security-group-rules_delete.md @@ -0,0 +1,42 @@ +## stackit beta security-group-rules delete + +Deletes a security group rule + +### Synopsis + +Deletes a security group rule. +If the security group rule is still in use, the deletion will fail + + +``` +stackit beta security-group-rules delete [flags] +``` + +### Examples + +``` + Delete security group rule with ID "xxx" in security group with ID "yyy" + $ stackit beta security-group-rule delete xxx --security-group-id yyy +``` + +### Options + +``` + -h, --help Help for "stackit beta security-group-rules delete" + --security-group-id string The security group ID +``` + +### Options inherited from parent commands + +``` + -y, --assume-yes If set, skips all confirmation prompts + --async If set, runs the command asynchronously + -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] + -p, --project-id string Project ID + --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") +``` + +### SEE ALSO + +* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules + diff --git a/docs/stackit_beta_security-group-rules_describe.md b/docs/stackit_beta_security-group-rules_describe.md new file mode 100644 index 000000000..5bbaaa4b8 --- /dev/null +++ b/docs/stackit_beta_security-group-rules_describe.md @@ -0,0 +1,43 @@ +## stackit beta security-group-rules describe + +Shows details of a security group rule + +### Synopsis + +Shows details of a security group rule. + +``` +stackit beta security-group-rules describe [flags] +``` + +### Examples + +``` + Show details of a security group rule with ID "xxx" in security group with ID "yyy" + $ stackit beta security-group-rule describe xxx --security-group-id yyy + + Show details of a security group rule with ID "xxx" in security group with ID "yyy" in JSON format + $ stackit beta security-group-rule describe xxx --security-group-id yyy --output-format json +``` + +### Options + +``` + -h, --help Help for "stackit beta security-group-rules describe" + --security-group-id string The security group ID +``` + +### Options inherited from parent commands + +``` + -y, --assume-yes If set, skips all confirmation prompts + --async If set, runs the command asynchronously + -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] + -p, --project-id string Project ID + --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") +``` + +### SEE ALSO + +* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules + diff --git a/docs/stackit_beta_security-group-rules_list.md b/docs/stackit_beta_security-group-rules_list.md new file mode 100644 index 000000000..3d93959bd --- /dev/null +++ b/docs/stackit_beta_security-group-rules_list.md @@ -0,0 +1,47 @@ +## stackit beta security-group-rules list + +Lists all security group rules in a security group of a project + +### Synopsis + +Lists all security group rules in a security group of a project + +``` +stackit beta security-group-rules list [flags] +``` + +### Examples + +``` + Lists all security group rules in security group with ID "xxx" + $ stackit beta security-group-rule list --security-group-id xxx + + Lists all security group rules in security group with ID "xxx" in JSON format + $ stackit beta security-group-rule list --security-group-id xxx --output-format json + + Lists up to 10 security group rules in security group with ID "xxx" + $ stackit beta security-group-rule list --security-group-id xxx --limit 10 +``` + +### Options + +``` + -h, --help Help for "stackit beta security-group-rules list" + --limit int Maximum number of entries to list + --security-group-id string The security group ID +``` + +### Options inherited from parent commands + +``` + -y, --assume-yes If set, skips all confirmation prompts + --async If set, runs the command asynchronously + -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] + -p, --project-id string Project ID + --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") +``` + +### SEE ALSO + +* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules + diff --git a/internal/cmd/beta/beta.go b/internal/cmd/beta/beta.go index 389c0d614..6cf525cb7 100644 --- a/internal/cmd/beta/beta.go +++ b/internal/cmd/beta/beta.go @@ -7,6 +7,7 @@ import ( networkArea "github.com/stackitcloud/stackit-cli/internal/cmd/beta/network-area" networkinterface "github.com/stackitcloud/stackit-cli/internal/cmd/beta/network-interface" publicip "github.com/stackitcloud/stackit-cli/internal/cmd/beta/public-ip" + securitygrouprule "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/sqlserverflex" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/volume" @@ -48,4 +49,5 @@ func addSubcommands(cmd *cobra.Command, p *print.Printer) { cmd.AddCommand(volume.NewCmd(p)) cmd.AddCommand(networkinterface.NewCmd(p)) cmd.AddCommand(publicip.NewCmd(p)) + cmd.AddCommand(securitygrouprule.NewCmd(p)) } diff --git a/internal/cmd/beta/security-group-rule/create/create.go b/internal/cmd/beta/security-group-rule/create/create.go new file mode 100644 index 000000000..8b6fd77e7 --- /dev/null +++ b/internal/cmd/beta/security-group-rule/create/create.go @@ -0,0 +1,262 @@ +package create + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/goccy/go-yaml" + "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/iaas/client" + iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" + + "github.com/spf13/cobra" +) + +const ( + securityGroupIdFlag = "security-group-id" + directionFlag = "direction" + descriptionFlag = "description" + etherTypeFlag = "ether-type" + icmpParameterCodeFlag = "icmp-parameter-code" + icmpParameterTypeFlag = "icmp-parameter-type" + ipRangeFlag = "ip-range" + portRangeMaxFlag = "port-range-max" + portRangeMinFlag = "port-range-min" + remoteSecurityGroupIdFlag = "remote-security-group-id" + protocolNumberFlag = "protocol-number" + protocolNameFlag = "protocol-name" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + SecurityGroupId string + Direction *string + Description *string + EtherType *string + IcmpParameterCode *int64 + IcmpParameterType *int64 + IpRange *string + PortRangeMax *int64 + PortRangeMin *int64 + RemoteSecurityGroupId *string + ProtocolNumber *int64 + ProtocolName *string +} + +func NewCmd(p *print.Printer) *cobra.Command { + cmd := &cobra.Command{ + Use: "create", + Short: "Creates a security group rule", + Long: "Creates a security group rule.", + Args: args.NoArgs, + Example: examples.Build( + examples.NewExample( + `Create a security group rule for security group with ID "xxx" with direction "ingress"`, + `$ stackit beta security-group-rule create --security-group-id xxx --direction ingress`, + ), + examples.NewExample( + `Create a security group rule for security group with ID "xxx" with direction "egress", protocol "icmp" and icmp parameters`, + `$ stackit beta security-group-rule create --security-group-id xxx --direction egress --protocol icmp --icmp-parameter-code 0 --icmp-parameter-type 8`, + ), + examples.NewExample( + `Create a security group rule for security group with ID "xxx" with direction "ingress" and port range values`, + `$ stackit beta security-group-rule create --security-group-id xxx --direction ingress --port-range-max 24 --port-range-min 22`, + ), + examples.NewExample( + `Create a security group rule for security group with ID "xxx" with direction "ingress" and protocol number 1 `, + `$ stackit beta security-group-rule create --security-group-id xxx --direction ingress --protocol-number 1`, + ), + ), + RunE: func(cmd *cobra.Command, _ []string) error { + ctx := context.Background() + model, err := parseInput(p, cmd) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(p) + if err != nil { + return err + } + + projectLabel, err := projectname.GetProjectName(ctx, p, cmd) + if err != nil { + p.Debug(print.ErrorLevel, "get project name: %v", err) + projectLabel = model.ProjectId + } + + securityGroupLabel, err := iaasUtils.GetSecurityGroupName(ctx, apiClient, model.ProjectId, model.SecurityGroupId) + if err != nil { + p.Debug(print.ErrorLevel, "get security group name: %v", err) + securityGroupLabel = model.SecurityGroupId + } + + if !model.AssumeYes { + prompt := fmt.Sprintf("Are you sure you want to create a security group rule for security group %q for project %q?", securityGroupLabel, projectLabel) + err = p.PromptForConfirmation(prompt) + if err != nil { + return err + } + } + + // Call API + req := buildRequest(ctx, model, apiClient) + resp, err := req.Execute() + if err != nil { + return fmt.Errorf("create security group rule : %w", err) + } + + return outputResult(p, model, projectLabel, securityGroupLabel, resp) + }, + } + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().String(securityGroupIdFlag, "", "The security group ID") + cmd.Flags().String(directionFlag, "", "The direction of the traffic which the rule should match. Some of the possible values are: `ingress`, `egress`") + cmd.Flags().String(descriptionFlag, "", "The rule description") + cmd.Flags().String(etherTypeFlag, "", "The ethertype which the rule should match") + cmd.Flags().Int64(icmpParameterCodeFlag, 0, "ICMP code. Can be set if the protocol is ICMP") + cmd.Flags().Int64(icmpParameterTypeFlag, 0, "ICMP type. Can be set if the protocol is ICMP") + cmd.Flags().String(ipRangeFlag, "", "The remote IP range which the rule should match") + cmd.Flags().Int64(portRangeMaxFlag, 0, "The maximum port number. Should be greater or equal to the minimum. This should only be provided if the protocol is not ICMP") + cmd.Flags().Int64(portRangeMinFlag, 0, "The minimum port number. Should be less or equal to the maximum. This should only be provided if the protocol is not ICMP") + cmd.Flags().Var(flags.UUIDFlag(), remoteSecurityGroupIdFlag, "The remote security group which the rule should match") + cmd.Flags().Int64(protocolNumberFlag, 0, "The protocol number which the rule should match. Either `name` or `number` must be provided") + cmd.Flags().String(protocolNameFlag, "", "The protocol name which the rule should match. Either `name` or `number` must be provided") + + err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag, directionFlag) + cmd.MarkFlagsMutuallyExclusive(protocolNumberFlag, protocolNameFlag) + 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, + SecurityGroupId: flags.FlagToStringValue(p, cmd, securityGroupIdFlag), + Direction: flags.FlagToStringPointer(p, cmd, directionFlag), + Description: flags.FlagToStringPointer(p, cmd, descriptionFlag), + EtherType: flags.FlagToStringPointer(p, cmd, etherTypeFlag), + IcmpParameterCode: flags.FlagToInt64Pointer(p, cmd, icmpParameterCodeFlag), + IcmpParameterType: flags.FlagToInt64Pointer(p, cmd, icmpParameterTypeFlag), + IpRange: flags.FlagToStringPointer(p, cmd, ipRangeFlag), + PortRangeMax: flags.FlagToInt64Pointer(p, cmd, portRangeMaxFlag), + PortRangeMin: flags.FlagToInt64Pointer(p, cmd, portRangeMinFlag), + RemoteSecurityGroupId: flags.FlagToStringPointer(p, cmd, remoteSecurityGroupIdFlag), + ProtocolNumber: flags.FlagToInt64Pointer(p, cmd, protocolNumberFlag), + ProtocolName: flags.FlagToStringPointer(p, cmd, protocolNameFlag), + } + + if model.ProtocolName != nil { + if *model.ProtocolName == "icmp" || *model.ProtocolName == "ipv6-icmp" { + if model.PortRangeMin != nil || model.PortRangeMax != nil { + return nil, &cliErr.SecurityGroupRuleProtocolPortRangeConflictError{ + Cmd: cmd, + } + } + } else { + if model.IcmpParameterCode != nil || model.IcmpParameterType != nil { + return nil, &cliErr.SecurityGroupRuleProtocolParametersConflictError{ + Cmd: cmd, + } + } + } + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiCreateSecurityGroupRuleRequest { + req := apiClient.CreateSecurityGroupRule(ctx, model.ProjectId, model.SecurityGroupId) + icmpParameters := &iaas.ICMPParameters{} + portRange := &iaas.PortRange{} + protocol := &iaas.CreateProtocol{} + + payload := iaas.CreateSecurityGroupRulePayload{ + Direction: model.Direction, + Description: model.Description, + Ethertype: model.EtherType, + IpRange: model.IpRange, + RemoteSecurityGroupId: model.RemoteSecurityGroupId, + } + + if model.IcmpParameterCode != nil || model.IcmpParameterType != nil { + icmpParameters.Code = model.IcmpParameterCode + icmpParameters.Type = model.IcmpParameterType + + payload.IcmpParameters = icmpParameters + } + + if model.PortRangeMax != nil || model.PortRangeMin != nil { + portRange.Max = model.PortRangeMax + portRange.Min = model.PortRangeMin + + payload.PortRange = portRange + } + + if model.ProtocolNumber != nil || model.ProtocolName != nil { + protocol.Int64 = model.ProtocolNumber + protocol.String = model.ProtocolName + + payload.Protocol = protocol + } + + if model.RemoteSecurityGroupId == nil { + payload.RemoteSecurityGroupId = nil + } + + return req.CreateSecurityGroupRulePayload(payload) +} + +func outputResult(p *print.Printer, model *inputModel, projectLabel, securityGroupName string, securityGroupRule *iaas.SecurityGroupRule) error { + switch model.OutputFormat { + case print.JSONOutputFormat: + details, err := json.MarshalIndent(securityGroupRule, "", " ") + if err != nil { + return fmt.Errorf("marshal security group rule: %w", err) + } + p.Outputln(string(details)) + + return nil + case print.YAMLOutputFormat: + details, err := yaml.MarshalWithOptions(securityGroupRule, yaml.IndentSequence(true)) + if err != nil { + return fmt.Errorf("marshal security group rule: %w", err) + } + p.Outputln(string(details)) + + return nil + default: + operationState := "Created" + if model.Async { + operationState = "Triggered creation of" + } + p.Outputf("%s security group rule for security group %q in project %q.\nSecurity group rule ID: %s\n", operationState, projectLabel, securityGroupName, *securityGroupRule.SecurityGroupId) + return nil + } +} diff --git a/internal/cmd/beta/security-group-rule/create/create_test.go b/internal/cmd/beta/security-group-rule/create/create_test.go new file mode 100644 index 000000000..abc999f98 --- /dev/null +++ b/internal/cmd/beta/security-group-rule/create/create_test.go @@ -0,0 +1,342 @@ +package create + +import ( + "context" + "testing" + + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &iaas.APIClient{} + +var testProjectId = uuid.NewString() +var testSecurityGroupId = uuid.NewString() +var testRemoteSecurityGroupId = uuid.NewString() + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + securityGroupIdFlag: testSecurityGroupId, + directionFlag: "ingress", + descriptionFlag: "example-description", + etherTypeFlag: "ether", + icmpParameterCodeFlag: "0", + icmpParameterTypeFlag: "8", + ipRangeFlag: "10.1.2.3", + portRangeMaxFlag: "24", + portRangeMinFlag: "22", + remoteSecurityGroupIdFlag: testRemoteSecurityGroupId, + protocolNumberFlag: "1", + protocolNameFlag: "icmp", + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + Verbosity: globalflags.VerbosityDefault, + }, + SecurityGroupId: testSecurityGroupId, + Direction: utils.Ptr("ingress"), + Description: utils.Ptr("example-description"), + EtherType: utils.Ptr("ether"), + IcmpParameterCode: utils.Ptr(int64(0)), + IcmpParameterType: utils.Ptr(int64(8)), + IpRange: utils.Ptr("10.1.2.3"), + PortRangeMax: utils.Ptr(int64(24)), + PortRangeMin: utils.Ptr(int64(22)), + RemoteSecurityGroupId: utils.Ptr(testRemoteSecurityGroupId), + ProtocolNumber: utils.Ptr(int64(1)), + ProtocolName: utils.Ptr("icmp"), + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *iaas.ApiCreateSecurityGroupRuleRequest)) iaas.ApiCreateSecurityGroupRuleRequest { + request := testClient.CreateSecurityGroupRule(testCtx, testProjectId, testSecurityGroupId) + request = request.CreateSecurityGroupRulePayload(fixturePayload()) + for _, mod := range mods { + mod(&request) + } + return request +} + +func fixtureRequiredRequest(mods ...func(request *iaas.ApiCreateSecurityGroupRuleRequest)) iaas.ApiCreateSecurityGroupRuleRequest { + request := testClient.CreateSecurityGroupRule(testCtx, testProjectId, testSecurityGroupId) + request = request.CreateSecurityGroupRulePayload(iaas.CreateSecurityGroupRulePayload{ + Direction: utils.Ptr("ingress"), + }) + for _, mod := range mods { + mod(&request) + } + return request +} + +func fixturePayload(mods ...func(payload *iaas.CreateSecurityGroupRulePayload)) iaas.CreateSecurityGroupRulePayload { + payload := iaas.CreateSecurityGroupRulePayload{ + Direction: utils.Ptr("ingress"), + Description: utils.Ptr("example-description"), + Ethertype: utils.Ptr("ether"), + IcmpParameters: &iaas.ICMPParameters{ + Code: utils.Ptr(int64(0)), + Type: utils.Ptr(int64(8)), + }, + IpRange: utils.Ptr("10.1.2.3"), + PortRange: &iaas.PortRange{ + Max: utils.Ptr(int64(24)), + Min: utils.Ptr(int64(22)), + }, + Protocol: &iaas.CreateProtocol{ + Int64: utils.Ptr(int64(1)), + String: utils.Ptr("icmp"), + }, + RemoteSecurityGroupId: utils.Ptr(testRemoteSecurityGroupId), + } + for _, mod := range mods { + mod(&payload) + } + return payload +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + flagValues map[string]string + isValid bool + expectedModel *inputModel + }{ + { + description: "base", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, portRangeMaxFlag) + delete(flagValues, portRangeMinFlag) + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.PortRangeMax = nil + model.PortRangeMin = nil + }), + }, + { + description: "required only", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, descriptionFlag) + delete(flagValues, etherTypeFlag) + delete(flagValues, icmpParameterCodeFlag) + delete(flagValues, icmpParameterTypeFlag) + delete(flagValues, ipRangeFlag) + delete(flagValues, portRangeMaxFlag) + delete(flagValues, portRangeMinFlag) + delete(flagValues, remoteSecurityGroupIdFlag) + delete(flagValues, protocolNumberFlag) + delete(flagValues, protocolNameFlag) + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.Description = nil + model.EtherType = nil + model.IcmpParameterCode = nil + model.IcmpParameterType = nil + model.IpRange = nil + model.PortRangeMax = nil + model.PortRangeMin = nil + model.RemoteSecurityGroupId = nil + model.ProtocolNumber = nil + model.ProtocolName = nil + }), + }, + { + description: "direction missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, directionFlag) + }), + isValid: false, + }, + { + description: "protocol is icmp and parameters are missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, icmpParameterCodeFlag) + delete(flagValues, icmpParameterTypeFlag) + }), + isValid: false, + }, + { + description: "protocol is icmp and port range values are provided", + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "protocol is not icmp and port range values are provided", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[protocolNameFlag] = "not-icmp" + delete(flagValues, icmpParameterCodeFlag) + delete(flagValues, icmpParameterTypeFlag) + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.IcmpParameterCode = nil + model.IcmpParameterType = nil + model.ProtocolName = utils.Ptr("not-icmp") + }), + }, + { + description: "protocol is not icmp and icmp parameters are provided", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[protocolNameFlag] = "not-icmp" + }), + isValid: false, + }, + { + description: "no values", + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "security group id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, securityGroupIdFlag) + }), + isValid: false, + }, + { + description: "security group id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[securityGroupIdFlag] = "" + }), + isValid: false, + }, + { + description: "security group id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[securityGroupIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + p := print.NewPrinter() + cmd := NewCmd(p) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(p, cmd) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing 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) { + var tests = []struct { + description string + model *inputModel + expectedRequest iaas.ApiCreateSecurityGroupRuleRequest + }{ + { + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), + }, + { + description: "only direction and security group id in payload", + model: &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + Verbosity: globalflags.VerbosityDefault, + }, + Direction: utils.Ptr("ingress"), + SecurityGroupId: testSecurityGroupId, + }, + expectedRequest: fixtureRequiredRequest(), + }, + } + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + cmp.AllowUnexported(iaas.NullableString{}), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/beta/security-group-rule/delete/delete.go b/internal/cmd/beta/security-group-rule/delete/delete.go new file mode 100644 index 000000000..5a6d63a71 --- /dev/null +++ b/internal/cmd/beta/security-group-rule/delete/delete.go @@ -0,0 +1,130 @@ +package delete + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/flags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" + iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" +) + +const ( + securityGroupRuleIdArg = "SECURITY_GROUP_RULE_ID" + + securityGroupIdFlag = "security-group-id" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + SecurityGroupRuleId string + SecurityGroupId *string +} + +func NewCmd(p *print.Printer) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete", + Short: "Deletes a security group rule", + Long: fmt.Sprintf("%s\n%s\n", + "Deletes a security group rule.", + "If the security group rule is still in use, the deletion will fail", + ), + Args: args.SingleArg(securityGroupRuleIdArg, utils.ValidateUUID), + Example: examples.Build( + examples.NewExample( + `Delete security group rule with ID "xxx" in security group with ID "yyy"`, + "$ stackit beta security-group-rule delete xxx --security-group-id yyy", + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseInput(p, cmd, args) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(p) + if err != nil { + return err + } + + securityGroupLabel, err := iaasUtils.GetSecurityGroupName(ctx, apiClient, model.ProjectId, *model.SecurityGroupId) + if err != nil { + p.Debug(print.ErrorLevel, "get security group name: %v", err) + securityGroupLabel = *model.SecurityGroupId + } + + securityGroupRuleLabel, err := iaasUtils.GetSecurityGroupRuleName(ctx, apiClient, model.ProjectId, model.SecurityGroupRuleId, *model.SecurityGroupId) + if err != nil { + p.Debug(print.ErrorLevel, "get security group rule name: %v", err) + securityGroupRuleLabel = model.SecurityGroupRuleId + } + + if !model.AssumeYes { + prompt := fmt.Sprintf("Are you sure you want to delete security group rule %q from security group %q?", securityGroupRuleLabel, securityGroupLabel) + err = p.PromptForConfirmation(prompt) + if err != nil { + return err + } + } + + // Call API + req := buildRequest(ctx, model, apiClient) + err = req.Execute() + if err != nil { + return fmt.Errorf("delete security group rule: %w", err) + } + + p.Info("Deleted security group rule %q from security group %q\n", securityGroupRuleLabel, securityGroupLabel) + return nil + }, + } + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "The security group ID") + + err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag) + cobra.CheckErr(err) +} + +func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { + securityGroupRuleId := inputArgs[0] + + globalFlags := globalflags.Parse(p, cmd) + if globalFlags.ProjectId == "" { + return nil, &errors.ProjectIdError{} + } + + model := inputModel{ + GlobalFlagModel: globalFlags, + SecurityGroupRuleId: securityGroupRuleId, + SecurityGroupId: flags.FlagToStringPointer(p, cmd, securityGroupIdFlag), + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiDeleteSecurityGroupRuleRequest { + return apiClient.DeleteSecurityGroupRule(ctx, model.ProjectId, *model.SecurityGroupId, model.SecurityGroupRuleId) +} diff --git a/internal/cmd/beta/security-group-rule/delete/delete_test.go b/internal/cmd/beta/security-group-rule/delete/delete_test.go new file mode 100644 index 000000000..00129c49b --- /dev/null +++ b/internal/cmd/beta/security-group-rule/delete/delete_test.go @@ -0,0 +1,232 @@ +package delete + +import ( + "context" + "testing" + + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &iaas.APIClient{} + +var testProjectId = uuid.NewString() +var testSecurityGroupId = uuid.NewString() +var testSecurityGroupRuleId = uuid.NewString() + +func fixtureArgValues(mods ...func(argValues []string)) []string { + argValues := []string{ + testSecurityGroupRuleId, + } + for _, mod := range mods { + mod(argValues) + } + return argValues +} + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + securityGroupIdFlag: testSecurityGroupId, + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + Verbosity: globalflags.VerbosityDefault, + }, + SecurityGroupId: utils.Ptr(testSecurityGroupId), + SecurityGroupRuleId: testSecurityGroupRuleId, + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *iaas.ApiDeleteSecurityGroupRuleRequest)) iaas.ApiDeleteSecurityGroupRuleRequest { + request := testClient.DeleteSecurityGroupRule(testCtx, testProjectId, testSecurityGroupId, testSecurityGroupRuleId) + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + argValues []string + flagValues map[string]string + aclValues []string + isValid bool + expectedModel *inputModel + }{ + { + description: "base", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureInputModel(), + }, + { + description: "no values", + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "security group id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, securityGroupIdFlag) + }), + isValid: false, + }, + { + description: "security group id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[securityGroupIdFlag] = "" + }), + isValid: false, + }, + { + description: "security group id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[securityGroupIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "security group rule id invalid 1", + argValues: []string{""}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "security group rule id invalid 2", + argValues: []string{"invalid-uuid"}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + p := print.NewPrinter() + cmd := NewCmd(p) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateArgs(tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing args: %v", err) + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(p, cmd, tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing 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 iaas.ApiDeleteSecurityGroupRuleRequest + }{ + { + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/beta/security-group-rule/describe/describe.go b/internal/cmd/beta/security-group-rule/describe/describe.go new file mode 100644 index 000000000..dc13df7ac --- /dev/null +++ b/internal/cmd/beta/security-group-rule/describe/describe.go @@ -0,0 +1,160 @@ +package describe + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/goccy/go-yaml" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/errors" + "github.com/stackitcloud/stackit-cli/internal/pkg/examples" + "github.com/stackitcloud/stackit-cli/internal/pkg/flags" + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" + "github.com/stackitcloud/stackit-cli/internal/pkg/tables" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" + + "github.com/spf13/cobra" +) + +const ( + securityGroupRuleIdArg = "SECURITY_GROUP_RULE_ID" + + securityGroupIdFlag = "security-group-id" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + SecurityGroupRuleId string + SecurityGroupId *string +} + +func NewCmd(p *print.Printer) *cobra.Command { + cmd := &cobra.Command{ + Use: "describe", + Short: "Shows details of a security group rule", + Long: "Shows details of a security group rule.", + Args: args.SingleArg(securityGroupRuleIdArg, utils.ValidateUUID), + Example: examples.Build( + examples.NewExample( + `Show details of a security group rule with ID "xxx" in security group with ID "yyy"`, + "$ stackit beta security-group-rule describe xxx --security-group-id yyy", + ), + examples.NewExample( + `Show details of a security group rule with ID "xxx" in security group with ID "yyy" in JSON format`, + "$ stackit beta security-group-rule describe xxx --security-group-id yyy --output-format json", + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := context.Background() + model, err := parseInput(p, cmd, args) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(p) + if err != nil { + return err + } + + // Call API + req := buildRequest(ctx, model, apiClient) + resp, err := req.Execute() + if err != nil { + return fmt.Errorf("read security group rule: %w", err) + } + + return outputResult(p, model.OutputFormat, resp) + }, + } + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "The security group ID") + + err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag) + cobra.CheckErr(err) +} + +func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { + securityGroupRuleId := inputArgs[0] + + globalFlags := globalflags.Parse(p, cmd) + if globalFlags.ProjectId == "" { + return nil, &errors.ProjectIdError{} + } + + model := inputModel{ + GlobalFlagModel: globalFlags, + SecurityGroupRuleId: securityGroupRuleId, + SecurityGroupId: flags.FlagToStringPointer(p, cmd, securityGroupIdFlag), + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiGetSecurityGroupRuleRequest { + return apiClient.GetSecurityGroupRule(ctx, model.ProjectId, *model.SecurityGroupId, model.SecurityGroupRuleId) +} + +func outputResult(p *print.Printer, outputFormat string, securityGroupRule *iaas.SecurityGroupRule) error { + switch outputFormat { + case print.JSONOutputFormat: + details, err := json.MarshalIndent(securityGroupRule, "", " ") + if err != nil { + return fmt.Errorf("marshal security group rule: %w", err) + } + p.Outputln(string(details)) + + return nil + case print.YAMLOutputFormat: + details, err := yaml.MarshalWithOptions(securityGroupRule, yaml.IndentSequence(true)) + if err != nil { + return fmt.Errorf("marshal security group rule: %w", err) + } + p.Outputln(string(details)) + + return nil + default: + table := tables.NewTable() + table.AddRow("ID", *securityGroupRule.Id) + table.AddSeparator() + table.AddRow("PROTOCOL NAME", *securityGroupRule.Protocol.Name) + table.AddSeparator() + table.AddRow("PROTOCOL NUMBER", *securityGroupRule.Protocol.Number) + table.AddSeparator() + table.AddRow("DIRECTION", *securityGroupRule.Direction) + table.AddSeparator() + table.AddRow("START PORT", *securityGroupRule.PortRange.Min) + table.AddSeparator() + table.AddRow("END PORT", *securityGroupRule.PortRange.Max) + table.AddSeparator() + table.AddRow("ETHER TYPE", *securityGroupRule.Ethertype) + table.AddSeparator() + table.AddRow("IP RANGE", *securityGroupRule.IpRange) + table.AddSeparator() + table.AddRow("REMOTE SECURITY GROUP", *securityGroupRule.RemoteSecurityGroupId) + table.AddSeparator() + + err := table.Display(p) + if err != nil { + return fmt.Errorf("render table: %w", err) + } + return nil + } +} diff --git a/internal/cmd/beta/security-group-rule/describe/describe_test.go b/internal/cmd/beta/security-group-rule/describe/describe_test.go new file mode 100644 index 000000000..f88107554 --- /dev/null +++ b/internal/cmd/beta/security-group-rule/describe/describe_test.go @@ -0,0 +1,243 @@ +package describe + +import ( + "context" + "testing" + + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &iaas.APIClient{} +var testProjectId = uuid.NewString() +var testSecurityGroupId = uuid.NewString() +var testSecurityGroupRuleId = uuid.NewString() + +func fixtureArgValues(mods ...func(argValues []string)) []string { + argValues := []string{ + testSecurityGroupRuleId, + } + for _, mod := range mods { + mod(argValues) + } + return argValues +} + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + securityGroupIdFlag: testSecurityGroupId, + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + Verbosity: globalflags.VerbosityDefault, + }, + SecurityGroupId: utils.Ptr(testSecurityGroupId), + SecurityGroupRuleId: testSecurityGroupRuleId, + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *iaas.ApiGetSecurityGroupRuleRequest)) iaas.ApiGetSecurityGroupRuleRequest { + request := testClient.GetSecurityGroupRule(testCtx, testProjectId, testSecurityGroupId, testSecurityGroupRuleId) + for _, mod := range mods { + mod(&request) + } + return request +} + +func TestParseInput(t *testing.T) { + tests := []struct { + description string + argValues []string + flagValues map[string]string + isValid bool + expectedModel *inputModel + }{ + { + description: "base", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(), + isValid: true, + expectedModel: fixtureInputModel(), + }, + { + description: "no values", + argValues: []string{}, + flagValues: map[string]string{}, + isValid: false, + }, + { + 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, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "security group id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, securityGroupIdFlag) + }), + isValid: false, + }, + { + description: "security group id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[securityGroupIdFlag] = "" + }), + isValid: false, + }, + { + description: "security group id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[securityGroupIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "security group rule id invalid 1", + argValues: []string{""}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + { + description: "security group rule id invalid 2", + argValues: []string{"invalid-uuid"}, + flagValues: fixtureFlagValues(), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + p := print.NewPrinter() + cmd := NewCmd(p) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateArgs(tt.argValues) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error 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 iaas.ApiGetSecurityGroupRuleRequest + }{ + { + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/beta/security-group-rule/list/list.go b/internal/cmd/beta/security-group-rule/list/list.go new file mode 100644 index 000000000..18259512d --- /dev/null +++ b/internal/cmd/beta/security-group-rule/list/list.go @@ -0,0 +1,179 @@ +package list + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/goccy/go-yaml" + "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/iaas/client" + iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" + "github.com/stackitcloud/stackit-cli/internal/pkg/tables" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" + + "github.com/spf13/cobra" +) + +const ( + limitFlag = "limit" + + securityGroupIdFlag = "security-group-id" +) + +type inputModel struct { + *globalflags.GlobalFlagModel + Limit *int64 + SecurityGroupId *string +} + +func NewCmd(p *print.Printer) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "Lists all security group rules in a security group of a project", + Long: "Lists all security group rules in a security group of a project", + Args: args.NoArgs, + Example: examples.Build( + examples.NewExample( + `Lists all security group rules in security group with ID "xxx"`, + "$ stackit beta security-group-rule list --security-group-id xxx", + ), + examples.NewExample( + `Lists all security group rules in security group with ID "xxx" in JSON format`, + "$ stackit beta security-group-rule list --security-group-id xxx --output-format json", + ), + examples.NewExample( + `Lists up to 10 security group rules in security group with ID "xxx"`, + "$ stackit beta security-group-rule list --security-group-id xxx --limit 10", + ), + ), + RunE: func(cmd *cobra.Command, _ []string) error { + ctx := context.Background() + model, err := parseInput(p, cmd) + if err != nil { + return err + } + + // Configure API client + apiClient, err := client.ConfigureClient(p) + if err != nil { + return err + } + + // Call API + req := buildRequest(ctx, model, apiClient) + resp, err := req.Execute() + if err != nil { + return fmt.Errorf("list security group rules: %w", err) + } + + if resp.Items == nil || len(*resp.Items) == 0 { + securityGroupLabel, err := iaasUtils.GetSecurityGroupName(ctx, apiClient, model.ProjectId, *model.SecurityGroupId) + if err != nil { + p.Debug(print.ErrorLevel, "get security group name: %v", err) + securityGroupLabel = *model.SecurityGroupId + } + + projectLabel, err := projectname.GetProjectName(ctx, p, cmd) + if err != nil { + p.Debug(print.ErrorLevel, "get project name: %v", err) + projectLabel = model.ProjectId + } + p.Info("No rules found in security group %q for project %q\n", securityGroupLabel, projectLabel) + return nil + } + + // Truncate output + items := *resp.Items + if model.Limit != nil && len(items) > int(*model.Limit) { + items = items[:*model.Limit] + } + + return outputResult(p, model.OutputFormat, items) + }, + } + configureFlags(cmd) + return cmd +} + +func configureFlags(cmd *cobra.Command) { + cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list") + cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "The security group ID") + + err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag) + cobra.CheckErr(err) +} + +func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { + globalFlags := globalflags.Parse(p, cmd) + if globalFlags.ProjectId == "" { + return nil, &errors.ProjectIdError{} + } + + limit := flags.FlagToInt64Pointer(p, cmd, limitFlag) + if limit != nil && *limit < 1 { + return nil, &errors.FlagValidationError{ + Flag: limitFlag, + Details: "must be greater than 0", + } + } + + model := inputModel{ + GlobalFlagModel: globalFlags, + Limit: limit, + SecurityGroupId: flags.FlagToStringPointer(p, cmd, securityGroupIdFlag), + } + + if p.IsVerbosityDebug() { + modelStr, err := print.BuildDebugStrFromInputModel(model) + if err != nil { + p.Debug(print.ErrorLevel, "convert model to string for debugging: %v", err) + } else { + p.Debug(print.DebugLevel, "parsed input values: %s", modelStr) + } + } + + return &model, nil +} + +func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiListSecurityGroupRulesRequest { + return apiClient.ListSecurityGroupRules(ctx, model.ProjectId, *model.SecurityGroupId) +} + +func outputResult(p *print.Printer, outputFormat string, securityGroupRules []iaas.SecurityGroupRule) error { + switch outputFormat { + case print.JSONOutputFormat: + details, err := json.MarshalIndent(securityGroupRules, "", " ") + if err != nil { + return fmt.Errorf("marshal security group rules: %w", err) + } + p.Outputln(string(details)) + + return nil + case print.YAMLOutputFormat: + details, err := yaml.MarshalWithOptions(securityGroupRules, yaml.IndentSequence(true)) + if err != nil { + return fmt.Errorf("marshal security group rules: %w", err) + } + p.Outputln(string(details)) + + return nil + default: + table := tables.NewTable() + table.SetHeader("ID", "ETHER TYPE", "DIRECTION", "PROTOCOL") + + for _, securityGroupRule := range securityGroupRules { + table.AddRow(*securityGroupRule.Id, *securityGroupRule.Ethertype, *securityGroupRule.Direction, *securityGroupRule.Protocol.Name) + table.AddSeparator() + } + + p.Outputln(table.Render()) + return nil + } +} diff --git a/internal/cmd/beta/security-group-rule/list/list_test.go b/internal/cmd/beta/security-group-rule/list/list_test.go new file mode 100644 index 000000000..016039ed7 --- /dev/null +++ b/internal/cmd/beta/security-group-rule/list/list_test.go @@ -0,0 +1,214 @@ +package list + +import ( + "context" + "testing" + + "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" +) + +var projectIdFlag = globalflags.ProjectIdFlag + +type testCtxKey struct{} + +var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") +var testClient = &iaas.APIClient{} +var testProjectId = uuid.NewString() +var testSecurityGroupId = uuid.NewString() + +func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { + flagValues := map[string]string{ + projectIdFlag: testProjectId, + limitFlag: "10", + securityGroupIdFlag: testSecurityGroupId, + } + for _, mod := range mods { + mod(flagValues) + } + return flagValues +} + +func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { + model := &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + Verbosity: globalflags.VerbosityDefault, + ProjectId: testProjectId, + }, + Limit: utils.Ptr(int64(10)), + SecurityGroupId: utils.Ptr(testSecurityGroupId), + } + for _, mod := range mods { + mod(model) + } + return model +} + +func fixtureRequest(mods ...func(request *iaas.ApiListSecurityGroupRulesRequest)) iaas.ApiListSecurityGroupRulesRequest { + request := testClient.ListSecurityGroupRules(testCtx, testProjectId, testSecurityGroupId) + 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: "no flag values", + flagValues: map[string]string{}, + isValid: false, + }, + { + description: "project id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, projectIdFlag) + }), + isValid: false, + }, + { + description: "project id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "" + }), + isValid: false, + }, + { + description: "project id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[projectIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "security group id missing", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + delete(flagValues, securityGroupIdFlag) + }), + isValid: false, + }, + { + description: "security group id invalid 1", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[securityGroupIdFlag] = "" + }), + isValid: false, + }, + { + description: "security group id invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[securityGroupIdFlag] = "invalid-uuid" + }), + isValid: false, + }, + { + description: "limit invalid", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[limitFlag] = "invalid" + }), + isValid: false, + }, + { + description: "limit invalid 2", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[limitFlag] = "0" + }), + isValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + p := print.NewPrinter() + cmd := NewCmd(p) + err := globalflags.Configure(cmd.Flags()) + if err != nil { + t.Fatalf("configure global flags: %v", err) + } + + for flag, value := range tt.flagValues { + err := cmd.Flags().Set(flag, value) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("setting flag --%s=%s: %v", flag, value, err) + } + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flags: %v", err) + } + + model, err := parseInput(p, cmd) + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error parsing input: %v", err) + } + + if !tt.isValid { + t.Fatalf("did not fail on invalid input") + } + diff := cmp.Diff(model, tt.expectedModel) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} + +func TestBuildRequest(t *testing.T) { + tests := []struct { + description string + model *inputModel + expectedRequest iaas.ApiListSecurityGroupRulesRequest + }{ + { + description: "base", + model: fixtureInputModel(), + expectedRequest: fixtureRequest(), + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + request := buildRequest(testCtx, tt.model, testClient) + + diff := cmp.Diff(request, tt.expectedRequest, + cmp.AllowUnexported(tt.expectedRequest), + cmpopts.EquateComparable(testCtx), + ) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + }) + } +} diff --git a/internal/cmd/beta/security-group-rule/security-group-rule.go b/internal/cmd/beta/security-group-rule/security-group-rule.go new file mode 100644 index 000000000..a12f1dc09 --- /dev/null +++ b/internal/cmd/beta/security-group-rule/security-group-rule.go @@ -0,0 +1,32 @@ +package securitygrouprule + +import ( + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule/create" + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule/delete" + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule/describe" + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule/list" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + + "github.com/spf13/cobra" +) + +func NewCmd(p *print.Printer) *cobra.Command { + cmd := &cobra.Command{ + Use: "security-group-rules", + Short: "Provides functionality for security group rules", + Long: "Provides functionality for security group rules.", + Args: args.NoArgs, + Run: utils.CmdHelp, + } + addSubcommands(cmd, p) + return cmd +} + +func addSubcommands(cmd *cobra.Command, p *print.Printer) { + cmd.AddCommand(create.NewCmd(p)) + cmd.AddCommand(delete.NewCmd(p)) + cmd.AddCommand(describe.NewCmd(p)) + cmd.AddCommand(list.NewCmd(p)) +} diff --git a/internal/pkg/errors/errors.go b/internal/pkg/errors/errors.go index 38aaa0835..e5e7438b5 100644 --- a/internal/pkg/errors/errors.go +++ b/internal/pkg/errors/errors.go @@ -146,8 +146,28 @@ To enable it, run: IAAS_SERVER_MISSING_VOLUME_TYPE = `The "boot-volume-source-type" flag must be provided together with "boot-volume-source-id" flag.` IAAS_SERVER_MISSING_IMAGE_OR_VOLUME_FLAGS = `Either "image-id" or "boot-volume-source-type" and "boot-volume-source-id" flags must be provided.` + + IAAS_SECURITY_GROUP_RULE_PROTOCOL_PORT_RANGE_CONFLICT = `"port-range-min" and "port-range-max" attributes can't be provided if "protocol-name" is set to "icmp" or "ipv6-icmp"` + + IAAS_SECURITY_GROUP_RULE_PROTOCOL_PARAMETERS_CONFLICT = `"icmp-parameter-code" and "icmp-parameter-type" attributes can't be provided if "protocol-name" is not "icmp" or "ipv6-icmp"` ) +type SecurityGroupRuleProtocolParametersConflictError struct { + Cmd *cobra.Command +} + +func (e *SecurityGroupRuleProtocolParametersConflictError) Error() string { + return IAAS_SECURITY_GROUP_RULE_PROTOCOL_PARAMETERS_CONFLICT +} + +type SecurityGroupRuleProtocolPortRangeConflictError struct { + Cmd *cobra.Command +} + +func (e *SecurityGroupRuleProtocolPortRangeConflictError) Error() string { + return IAAS_SECURITY_GROUP_RULE_PROTOCOL_PORT_RANGE_CONFLICT +} + type ServerCreateMissingVolumeIdError struct { Cmd *cobra.Command } diff --git a/internal/pkg/services/iaas/utils/utils.go b/internal/pkg/services/iaas/utils/utils.go index fecc0872c..e7a455688 100644 --- a/internal/pkg/services/iaas/utils/utils.go +++ b/internal/pkg/services/iaas/utils/utils.go @@ -8,6 +8,8 @@ import ( ) type IaaSClient interface { + GetSecurityGroupRuleExecute(ctx context.Context, projectId, securityGroupRuleId, securityGroupId string) (*iaas.SecurityGroupRule, error) + GetSecurityGroupExecute(ctx context.Context, projectId, securityGroupId string) (*iaas.SecurityGroup, error) GetPublicIPExecute(ctx context.Context, projectId, publicIpId string) (*iaas.PublicIp, error) GetServerExecute(ctx context.Context, projectId, serverId string) (*iaas.Server, error) GetVolumeExecute(ctx context.Context, projectId, volumeId string) (*iaas.Volume, error) @@ -17,6 +19,23 @@ type IaaSClient interface { GetNetworkAreaRangeExecute(ctx context.Context, organizationId, areaId, networkRangeId string) (*iaas.NetworkRange, error) } +func GetSecurityGroupRuleName(ctx context.Context, apiClient IaaSClient, projectId, securityGroupRuleId, securityGroupId string) (string, error) { + resp, err := apiClient.GetSecurityGroupRuleExecute(ctx, projectId, securityGroupRuleId, securityGroupId) + if err != nil { + return "", fmt.Errorf("get security group rule: %w", err) + } + securityGroupRuleName := *resp.Ethertype + ", " + *resp.Direction + return securityGroupRuleName, nil +} + +func GetSecurityGroupName(ctx context.Context, apiClient IaaSClient, projectId, securityGroupId string) (string, error) { + resp, err := apiClient.GetSecurityGroupExecute(ctx, projectId, securityGroupId) + if err != nil { + return "", fmt.Errorf("get security group: %w", err) + } + return *resp.Name, nil +} + func GetPublicIP(ctx context.Context, apiClient IaaSClient, projectId, publicIpId string) (ip, associatedResource string, err error) { resp, err := apiClient.GetPublicIPExecute(ctx, projectId, publicIpId) if err != nil { diff --git a/internal/pkg/services/iaas/utils/utils_test.go b/internal/pkg/services/iaas/utils/utils_test.go index bc0d94299..c7e75a683 100644 --- a/internal/pkg/services/iaas/utils/utils_test.go +++ b/internal/pkg/services/iaas/utils/utils_test.go @@ -11,20 +11,38 @@ import ( ) type IaaSClientMocked struct { - GetPublicIpFails bool - GetPublicIpResp *iaas.PublicIp - GetServerFails bool - GetServerResp *iaas.Server - GetVolumeFails bool - GetVolumeResp *iaas.Volume - GetNetworkFails bool - GetNetworkResp *iaas.Network - GetNetworkAreaFails bool - GetNetworkAreaResp *iaas.NetworkArea - GetAttachedProjectsFails bool - GetAttachedProjectsResp *iaas.ProjectListResponse - GetNetworkAreaRangeFails bool - GetNetworkAreaRangeResp *iaas.NetworkRange + GetSecurityGroupRuleFails bool + GetSecurityGroupRuleResp *iaas.SecurityGroupRule + GetSecurityGroupFails bool + GetSecurityGroupResp *iaas.SecurityGroup + GetPublicIpFails bool + GetPublicIpResp *iaas.PublicIp + GetServerFails bool + GetServerResp *iaas.Server + GetVolumeFails bool + GetVolumeResp *iaas.Volume + GetNetworkFails bool + GetNetworkResp *iaas.Network + GetNetworkAreaFails bool + GetNetworkAreaResp *iaas.NetworkArea + GetAttachedProjectsFails bool + GetAttachedProjectsResp *iaas.ProjectListResponse + GetNetworkAreaRangeFails bool + GetNetworkAreaRangeResp *iaas.NetworkRange +} + +func (m *IaaSClientMocked) GetSecurityGroupRuleExecute(_ context.Context, _, _, _ string) (*iaas.SecurityGroupRule, error) { + if m.GetSecurityGroupRuleFails { + return nil, fmt.Errorf("could not get security group rule") + } + return m.GetSecurityGroupRuleResp, nil +} + +func (m *IaaSClientMocked) GetSecurityGroupExecute(_ context.Context, _, _ string) (*iaas.SecurityGroup, error) { + if m.GetSecurityGroupFails { + return nil, fmt.Errorf("could not get security group") + } + return m.GetSecurityGroupResp, nil } func (m *IaaSClientMocked) GetPublicIPExecute(_ context.Context, _, _ string) (*iaas.PublicIp, error) { @@ -76,6 +94,99 @@ func (m *IaaSClientMocked) GetNetworkAreaRangeExecute(_ context.Context, _, _, _ return m.GetNetworkAreaRangeResp, nil } +func TestGetSecurityGroupRuleName(t *testing.T) { + type args struct { + getInstanceFails bool + getInstanceResp *iaas.SecurityGroupRule + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "base", + args: args{ + getInstanceResp: &iaas.SecurityGroupRule{ + Ethertype: utils.Ptr("IPv6"), + Direction: utils.Ptr("ingress"), + }, + }, + want: "IPv6, ingress", + }, + { + name: "get security group rule fails", + args: args{ + getInstanceFails: true, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &IaaSClientMocked{ + GetSecurityGroupRuleFails: tt.args.getInstanceFails, + GetSecurityGroupRuleResp: tt.args.getInstanceResp, + } + got, err := GetSecurityGroupRuleName(context.Background(), m, "", "", "") + if (err != nil) != tt.wantErr { + t.Errorf("GetSecurityGroupRuleName() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GetSecurityGroupRuleName() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetSecurityGroupName(t *testing.T) { + type args struct { + getInstanceFails bool + getInstanceResp *iaas.SecurityGroup + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "base", + args: args{ + getInstanceResp: &iaas.SecurityGroup{ + Name: utils.Ptr("test"), + }, + }, + want: "test", + }, + { + name: "get security group fails", + args: args{ + getInstanceFails: true, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &IaaSClientMocked{ + GetSecurityGroupFails: tt.args.getInstanceFails, + GetSecurityGroupResp: tt.args.getInstanceResp, + } + got, err := GetSecurityGroupName(context.Background(), m, "", "") + if (err != nil) != tt.wantErr { + t.Errorf("GetSecurityGroupName() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GetSecurityGroupName() = %v, want %v", got, tt.want) + } + }) + } +} + func TestGetPublicIp(t *testing.T) { type args struct { getPublicIpFails bool From 2e64f2089530484debc84738e502281ef40496ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Thu, 5 Dec 2024 16:40:56 +0100 Subject: [PATCH 02/19] fix NPE --- .../security-group-rule/describe/describe.go | 54 ++++++++++++++----- .../cmd/beta/security-group-rule/list/list.go | 14 ++++- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/internal/cmd/beta/security-group-rule/describe/describe.go b/internal/cmd/beta/security-group-rule/describe/describe.go index dc13df7ac..a3b940cf3 100644 --- a/internal/cmd/beta/security-group-rule/describe/describe.go +++ b/internal/cmd/beta/security-group-rule/describe/describe.go @@ -134,22 +134,48 @@ func outputResult(p *print.Printer, outputFormat string, securityGroupRule *iaas table := tables.NewTable() table.AddRow("ID", *securityGroupRule.Id) table.AddSeparator() - table.AddRow("PROTOCOL NAME", *securityGroupRule.Protocol.Name) - table.AddSeparator() - table.AddRow("PROTOCOL NUMBER", *securityGroupRule.Protocol.Number) - table.AddSeparator() + + if securityGroupRule.Protocol != nil { + if securityGroupRule.Protocol.Name != nil { + table.AddRow("PROTOCOL NAME", *securityGroupRule.Protocol.Name) + table.AddSeparator() + } + + if securityGroupRule.Protocol.Number != nil { + table.AddRow("PROTOCOL NUMBER", *securityGroupRule.Protocol.Number) + table.AddSeparator() + } + } + table.AddRow("DIRECTION", *securityGroupRule.Direction) table.AddSeparator() - table.AddRow("START PORT", *securityGroupRule.PortRange.Min) - table.AddSeparator() - table.AddRow("END PORT", *securityGroupRule.PortRange.Max) - table.AddSeparator() - table.AddRow("ETHER TYPE", *securityGroupRule.Ethertype) - table.AddSeparator() - table.AddRow("IP RANGE", *securityGroupRule.IpRange) - table.AddSeparator() - table.AddRow("REMOTE SECURITY GROUP", *securityGroupRule.RemoteSecurityGroupId) - table.AddSeparator() + + if securityGroupRule.PortRange != nil { + if securityGroupRule.PortRange.Min != nil { + table.AddRow("START PORT", *securityGroupRule.PortRange.Min) + table.AddSeparator() + } + + if securityGroupRule.PortRange.Max != nil { + table.AddRow("END PORT", *securityGroupRule.PortRange.Max) + table.AddSeparator() + } + } + + if securityGroupRule.Ethertype != nil { + table.AddRow("ETHER TYPE", *securityGroupRule.Ethertype) + table.AddSeparator() + } + + if securityGroupRule.IpRange != nil { + table.AddRow("IP RANGE", *securityGroupRule.IpRange) + table.AddSeparator() + } + + if securityGroupRule.RemoteSecurityGroupId != nil { + table.AddRow("REMOTE SECURITY GROUP", *securityGroupRule.RemoteSecurityGroupId) + table.AddSeparator() + } err := table.Display(p) if err != nil { diff --git a/internal/cmd/beta/security-group-rule/list/list.go b/internal/cmd/beta/security-group-rule/list/list.go index 18259512d..120934b4e 100644 --- a/internal/cmd/beta/security-group-rule/list/list.go +++ b/internal/cmd/beta/security-group-rule/list/list.go @@ -169,7 +169,19 @@ func outputResult(p *print.Printer, outputFormat string, securityGroupRules []ia table.SetHeader("ID", "ETHER TYPE", "DIRECTION", "PROTOCOL") for _, securityGroupRule := range securityGroupRules { - table.AddRow(*securityGroupRule.Id, *securityGroupRule.Ethertype, *securityGroupRule.Direction, *securityGroupRule.Protocol.Name) + etherType := "" + if securityGroupRule.Ethertype != nil { + etherType = *securityGroupRule.Ethertype + } + + protocolName := "" + if securityGroupRule.Protocol != nil { + if securityGroupRule.Protocol.Name != nil { + protocolName = *securityGroupRule.Protocol.Name + } + } + + table.AddRow(*securityGroupRule.Id, etherType, *securityGroupRule.Direction, protocolName) table.AddSeparator() } From ac8fba6a215ee4438b048fa1accaa1faea6ec42b Mon Sep 17 00:00:00 2001 From: GokceGK <161626272+GokceGK@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:46:25 +0100 Subject: [PATCH 03/19] Update internal/cmd/beta/security-group-rule/create/create.go Co-authored-by: Alexander Dahmen --- internal/cmd/beta/security-group-rule/create/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/beta/security-group-rule/create/create.go b/internal/cmd/beta/security-group-rule/create/create.go index 8b6fd77e7..377255754 100644 --- a/internal/cmd/beta/security-group-rule/create/create.go +++ b/internal/cmd/beta/security-group-rule/create/create.go @@ -124,7 +124,7 @@ func NewCmd(p *print.Printer) *cobra.Command { func configureFlags(cmd *cobra.Command) { cmd.Flags().String(securityGroupIdFlag, "", "The security group ID") - cmd.Flags().String(directionFlag, "", "The direction of the traffic which the rule should match. Some of the possible values are: `ingress`, `egress`") + cmd.Flags().String(directionFlag, "", "The direction of the traffic which the rule should match. The possible values are: `ingress`, `egress`") cmd.Flags().String(descriptionFlag, "", "The rule description") cmd.Flags().String(etherTypeFlag, "", "The ethertype which the rule should match") cmd.Flags().Int64(icmpParameterCodeFlag, 0, "ICMP code. Can be set if the protocol is ICMP") From 963d7407dd1051f6b4c836d521d60e15554cd2bd Mon Sep 17 00:00:00 2001 From: GokceGK <161626272+GokceGK@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:48:43 +0100 Subject: [PATCH 04/19] Update internal/cmd/beta/security-group-rule/create/create.go Co-authored-by: Alexander Dahmen --- internal/cmd/beta/security-group-rule/create/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/beta/security-group-rule/create/create.go b/internal/cmd/beta/security-group-rule/create/create.go index 377255754..fa3d8d97e 100644 --- a/internal/cmd/beta/security-group-rule/create/create.go +++ b/internal/cmd/beta/security-group-rule/create/create.go @@ -256,7 +256,7 @@ func outputResult(p *print.Printer, model *inputModel, projectLabel, securityGro if model.Async { operationState = "Triggered creation of" } - p.Outputf("%s security group rule for security group %q in project %q.\nSecurity group rule ID: %s\n", operationState, projectLabel, securityGroupName, *securityGroupRule.SecurityGroupId) + p.Outputf("%s security group rule for security group %q in project %q.\nSecurity group rule ID: %s\n", operationState, securityGroupName, projectLabel, *securityGroupRule.Id) return nil } } From a9e1dd89da8510b06ae9808a67e67dc65e6a5c71 Mon Sep 17 00:00:00 2001 From: GokceGK <161626272+GokceGK@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:50:20 +0100 Subject: [PATCH 05/19] Update internal/cmd/beta/security-group-rule/list/list.go Co-authored-by: Alexander Dahmen --- internal/cmd/beta/security-group-rule/list/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/beta/security-group-rule/list/list.go b/internal/cmd/beta/security-group-rule/list/list.go index 120934b4e..e4eaf3044 100644 --- a/internal/cmd/beta/security-group-rule/list/list.go +++ b/internal/cmd/beta/security-group-rule/list/list.go @@ -37,7 +37,7 @@ func NewCmd(p *print.Printer) *cobra.Command { cmd := &cobra.Command{ Use: "list", Short: "Lists all security group rules in a security group of a project", - Long: "Lists all security group rules in a security group of a project", + Long: "Lists all security group rules in a security group of a project.", Args: args.NoArgs, Example: examples.Build( examples.NewExample( From 1104f08e1a3c93cbbab6fa96a384c93799198db4 Mon Sep 17 00:00:00 2001 From: GokceGK <161626272+GokceGK@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:53:31 +0100 Subject: [PATCH 06/19] Update internal/cmd/beta/security-group-rule/create/create_test.go Co-authored-by: Alexander Dahmen --- .../cmd/beta/security-group-rule/create/create_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/cmd/beta/security-group-rule/create/create_test.go b/internal/cmd/beta/security-group-rule/create/create_test.go index abc999f98..b9b1810ed 100644 --- a/internal/cmd/beta/security-group-rule/create/create_test.go +++ b/internal/cmd/beta/security-group-rule/create/create_test.go @@ -274,6 +274,13 @@ func TestParseInput(t *testing.T) { } } + err = cmd.ValidateFlagGroups() + if err != nil { + if !tt.isValid { + return + } + t.Fatalf("error validating flag groups: %v", err) + } err = cmd.ValidateRequiredFlags() if err != nil { if !tt.isValid { From 5ff0eff8c511111848559dd3a3e156a8f623d983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Tue, 10 Dec 2024 08:59:12 +0100 Subject: [PATCH 07/19] fix unit tests --- internal/cmd/beta/security-group-rule/create/create_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/cmd/beta/security-group-rule/create/create_test.go b/internal/cmd/beta/security-group-rule/create/create_test.go index b9b1810ed..24296d514 100644 --- a/internal/cmd/beta/security-group-rule/create/create_test.go +++ b/internal/cmd/beta/security-group-rule/create/create_test.go @@ -130,11 +130,13 @@ func TestParseInput(t *testing.T) { flagValues: fixtureFlagValues(func(flagValues map[string]string) { delete(flagValues, portRangeMaxFlag) delete(flagValues, portRangeMinFlag) + delete(flagValues, protocolNumberFlag) }), isValid: true, expectedModel: fixtureInputModel(func(model *inputModel) { model.PortRangeMax = nil model.PortRangeMin = nil + model.ProtocolNumber = nil }), }, { @@ -191,12 +193,14 @@ func TestParseInput(t *testing.T) { flagValues[protocolNameFlag] = "not-icmp" delete(flagValues, icmpParameterCodeFlag) delete(flagValues, icmpParameterTypeFlag) + delete(flagValues, protocolNumberFlag) }), isValid: true, expectedModel: fixtureInputModel(func(model *inputModel) { model.IcmpParameterCode = nil model.IcmpParameterType = nil model.ProtocolName = utils.Ptr("not-icmp") + model.ProtocolNumber = nil }), }, { From 281a844be1de030e7435615816198db3dfa7a430 Mon Sep 17 00:00:00 2001 From: GokceGK <161626272+GokceGK@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:59:49 +0100 Subject: [PATCH 08/19] Update internal/cmd/beta/security-group-rule/delete/delete_test.go Co-authored-by: Alexander Dahmen --- internal/cmd/beta/security-group-rule/delete/delete_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/cmd/beta/security-group-rule/delete/delete_test.go b/internal/cmd/beta/security-group-rule/delete/delete_test.go index 00129c49b..86264a4f8 100644 --- a/internal/cmd/beta/security-group-rule/delete/delete_test.go +++ b/internal/cmd/beta/security-group-rule/delete/delete_test.go @@ -116,6 +116,7 @@ func TestParseInput(t *testing.T) { }, { description: "security group id missing", + argValues: fixtureArgValues(), flagValues: fixtureFlagValues(func(flagValues map[string]string) { delete(flagValues, securityGroupIdFlag) }), From 93efa2b65307d17297986b1d0f091b96481b2b17 Mon Sep 17 00:00:00 2001 From: GokceGK <161626272+GokceGK@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:59:54 +0100 Subject: [PATCH 09/19] Update internal/cmd/beta/security-group-rule/delete/delete_test.go Co-authored-by: Alexander Dahmen --- internal/cmd/beta/security-group-rule/delete/delete_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/cmd/beta/security-group-rule/delete/delete_test.go b/internal/cmd/beta/security-group-rule/delete/delete_test.go index 86264a4f8..1fa698331 100644 --- a/internal/cmd/beta/security-group-rule/delete/delete_test.go +++ b/internal/cmd/beta/security-group-rule/delete/delete_test.go @@ -124,6 +124,7 @@ func TestParseInput(t *testing.T) { }, { description: "security group id invalid 1", + argValues: fixtureArgValues(), flagValues: fixtureFlagValues(func(flagValues map[string]string) { flagValues[securityGroupIdFlag] = "" }), From 0ba756304d17fede6424247a150f0fe3fd6aeecd Mon Sep 17 00:00:00 2001 From: GokceGK <161626272+GokceGK@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:00:02 +0100 Subject: [PATCH 10/19] Update internal/cmd/beta/security-group-rule/delete/delete_test.go Co-authored-by: Alexander Dahmen --- internal/cmd/beta/security-group-rule/delete/delete_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/cmd/beta/security-group-rule/delete/delete_test.go b/internal/cmd/beta/security-group-rule/delete/delete_test.go index 1fa698331..e8d36d7f6 100644 --- a/internal/cmd/beta/security-group-rule/delete/delete_test.go +++ b/internal/cmd/beta/security-group-rule/delete/delete_test.go @@ -132,6 +132,7 @@ func TestParseInput(t *testing.T) { }, { description: "security group id invalid 2", + argValues: fixtureArgValues(), flagValues: fixtureFlagValues(func(flagValues map[string]string) { flagValues[securityGroupIdFlag] = "invalid-uuid" }), From 3d3f1a207b1f181948f9eb6d5aa3332407796c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Tue, 10 Dec 2024 09:03:14 +0100 Subject: [PATCH 11/19] fix unit tests --- .../cmd/beta/security-group-rule/describe/describe_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/cmd/beta/security-group-rule/describe/describe_test.go b/internal/cmd/beta/security-group-rule/describe/describe_test.go index f88107554..6463ef1fc 100644 --- a/internal/cmd/beta/security-group-rule/describe/describe_test.go +++ b/internal/cmd/beta/security-group-rule/describe/describe_test.go @@ -127,6 +127,7 @@ func TestParseInput(t *testing.T) { }, { description: "security group id missing", + argValues: fixtureArgValues(), flagValues: fixtureFlagValues(func(flagValues map[string]string) { delete(flagValues, securityGroupIdFlag) }), @@ -134,6 +135,7 @@ func TestParseInput(t *testing.T) { }, { description: "security group id invalid 1", + argValues: fixtureArgValues(), flagValues: fixtureFlagValues(func(flagValues map[string]string) { flagValues[securityGroupIdFlag] = "" }), @@ -141,6 +143,7 @@ func TestParseInput(t *testing.T) { }, { description: "security group id invalid 2", + argValues: fixtureArgValues(), flagValues: fixtureFlagValues(func(flagValues map[string]string) { flagValues[securityGroupIdFlag] = "invalid-uuid" }), From 057dcd3b39176c67a1dd1f879cd62cc8628592cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Tue, 10 Dec 2024 09:06:39 +0100 Subject: [PATCH 12/19] update docs --- docs/stackit_beta_security-group-rules_create.md | 2 +- docs/stackit_beta_security-group-rules_list.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/stackit_beta_security-group-rules_create.md b/docs/stackit_beta_security-group-rules_create.md index b8ddcce05..78a884ff7 100644 --- a/docs/stackit_beta_security-group-rules_create.md +++ b/docs/stackit_beta_security-group-rules_create.md @@ -30,7 +30,7 @@ stackit beta security-group-rules create [flags] ``` --description string The rule description - --direction ingress The direction of the traffic which the rule should match. Some of the possible values are: ingress, `egress` + --direction ingress The direction of the traffic which the rule should match. The possible values are: ingress, `egress` --ether-type string The ethertype which the rule should match -h, --help Help for "stackit beta security-group-rules create" --icmp-parameter-code int ICMP code. Can be set if the protocol is ICMP diff --git a/docs/stackit_beta_security-group-rules_list.md b/docs/stackit_beta_security-group-rules_list.md index 3d93959bd..fa441b2c2 100644 --- a/docs/stackit_beta_security-group-rules_list.md +++ b/docs/stackit_beta_security-group-rules_list.md @@ -4,7 +4,7 @@ Lists all security group rules in a security group of a project ### Synopsis -Lists all security group rules in a security group of a project +Lists all security group rules in a security group of a project. ``` stackit beta security-group-rules list [flags] From 19d1ee929d3f32ef41a4fd93a8389153122b2eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Tue, 10 Dec 2024 09:20:11 +0100 Subject: [PATCH 13/19] fix linter issues --- internal/pkg/errors/errors.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/pkg/errors/errors.go b/internal/pkg/errors/errors.go index 290ef2f6a..9390e33a7 100644 --- a/internal/pkg/errors/errors.go +++ b/internal/pkg/errors/errors.go @@ -150,8 +150,8 @@ To enable it, run: IAAS_SECURITY_GROUP_RULE_PROTOCOL_PORT_RANGE_CONFLICT = `"port-range-min" and "port-range-max" attributes can't be provided if "protocol-name" is set to "icmp" or "ipv6-icmp"` IAAS_SECURITY_GROUP_RULE_PROTOCOL_PARAMETERS_CONFLICT = `"icmp-parameter-code" and "icmp-parameter-type" attributes can't be provided if "protocol-name" is not "icmp" or "ipv6-icmp"` - - IAAS_SERVER_NIC_ATTACH_MISSING_NIC_ID = `The "network-interface-id" flag must be provided if the "create" flag is not provided.` + + IAAS_SERVER_NIC_ATTACH_MISSING_NIC_ID = `The "network-interface-id" flag must be provided if the "create" flag is not provided.` IAAS_SERVER_NIC_DETACH_MISSING_NIC_ID = `The "network-interface-id" flag must be provided if the "delete" flag is not provided.` ) @@ -170,7 +170,7 @@ type SecurityGroupRuleProtocolPortRangeConflictError struct { func (e *SecurityGroupRuleProtocolPortRangeConflictError) Error() string { return IAAS_SECURITY_GROUP_RULE_PROTOCOL_PORT_RANGE_CONFLICT -) +} type ServerNicAttachMissingNicIdError struct { Cmd *cobra.Command From d86c41250f693b20dab9bb15cf57737381ffd18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Tue, 10 Dec 2024 14:11:42 +0100 Subject: [PATCH 14/19] fix unit tests --- .../beta/security-group-rule/create/create_test.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/internal/cmd/beta/security-group-rule/create/create_test.go b/internal/cmd/beta/security-group-rule/create/create_test.go index 24296d514..c8d7ee332 100644 --- a/internal/cmd/beta/security-group-rule/create/create_test.go +++ b/internal/cmd/beta/security-group-rule/create/create_test.go @@ -171,22 +171,18 @@ func TestParseInput(t *testing.T) { description: "direction missing", flagValues: fixtureFlagValues(func(flagValues map[string]string) { delete(flagValues, directionFlag) + delete(flagValues, protocolNumberFlag) + delete(flagValues, protocolNameFlag) }), isValid: false, }, { - description: "protocol is icmp and parameters are missing", + description: "protocol is icmp and port range values are provided", flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, icmpParameterCodeFlag) - delete(flagValues, icmpParameterTypeFlag) + delete(flagValues, protocolNumberFlag) }), isValid: false, }, - { - description: "protocol is icmp and port range values are provided", - flagValues: fixtureFlagValues(), - isValid: false, - }, { description: "protocol is not icmp and port range values are provided", flagValues: fixtureFlagValues(func(flagValues map[string]string) { @@ -207,6 +203,7 @@ func TestParseInput(t *testing.T) { description: "protocol is not icmp and icmp parameters are provided", flagValues: fixtureFlagValues(func(flagValues map[string]string) { flagValues[protocolNameFlag] = "not-icmp" + delete(flagValues, protocolNumberFlag) }), isValid: false, }, From 2eafba07cfb909ea628994e492483d01eee2fd8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Wed, 11 Dec 2024 09:11:44 +0100 Subject: [PATCH 15/19] apply acceptance feedbacks --- README.md | 44 +++++++++---------- docs/stackit_beta.md | 2 +- docs/stackit_beta_security-group-rules.md | 36 --------------- docs/stackit_beta_security-group.md | 33 ++++++++++++++ docs/stackit_beta_security-group_rule.md | 36 +++++++++++++++ ...tackit_beta_security-group_rule_create.md} | 22 +++++----- ...tackit_beta_security-group_rule_delete.md} | 10 ++--- ...ckit_beta_security-group_rule_describe.md} | 12 ++--- ... stackit_beta_security-group_rule_list.md} | 14 +++--- internal/cmd/beta/beta.go | 4 +- .../rule}/create/create.go | 16 +++---- .../rule}/create/create_test.go | 0 .../rule}/delete/delete.go | 2 +- .../rule}/delete/delete_test.go | 0 .../rule}/describe/describe.go | 4 +- .../rule}/describe/describe_test.go | 0 .../rule}/list/list.go | 6 +-- .../rule}/list/list_test.go | 0 .../rule/security_group_rule.go} | 12 ++--- .../cmd/beta/security-group/security_group.go | 26 +++++++++++ 20 files changed, 169 insertions(+), 110 deletions(-) delete mode 100644 docs/stackit_beta_security-group-rules.md create mode 100644 docs/stackit_beta_security-group.md create mode 100644 docs/stackit_beta_security-group_rule.md rename docs/{stackit_beta_security-group-rules_create.md => stackit_beta_security-group_rule_create.md} (70%) rename docs/{stackit_beta_security-group-rules_delete.md => stackit_beta_security-group_rule_delete.md} (73%) rename docs/{stackit_beta_security-group-rules_describe.md => stackit_beta_security-group_rule_describe.md} (71%) rename docs/{stackit_beta_security-group-rules_list.md => stackit_beta_security-group_rule_list.md} (71%) rename internal/cmd/beta/{security-group-rule => security-group/rule}/create/create.go (92%) rename internal/cmd/beta/{security-group-rule => security-group/rule}/create/create_test.go (100%) rename internal/cmd/beta/{security-group-rule => security-group/rule}/delete/delete.go (98%) rename internal/cmd/beta/{security-group-rule => security-group/rule}/delete/delete_test.go (100%) rename internal/cmd/beta/{security-group-rule => security-group/rule}/describe/describe.go (97%) rename internal/cmd/beta/{security-group-rule => security-group/rule}/describe/describe_test.go (100%) rename internal/cmd/beta/{security-group-rule => security-group/rule}/list/list.go (96%) rename internal/cmd/beta/{security-group-rule => security-group/rule}/list/list_test.go (100%) rename internal/cmd/beta/{security-group-rule/security-group-rule.go => security-group/rule/security_group_rule.go} (87%) create mode 100644 internal/cmd/beta/security-group/security_group.go diff --git a/README.md b/README.md index be934f333..6fa47fec2 100644 --- a/README.md +++ b/README.md @@ -65,28 +65,28 @@ Help is available for any command by specifying the special flag `--help` (or si Below you can find a list of the STACKIT services already available in the CLI (along with their respective command names) and the ones that are currently planned to be integrated. -| Service | CLI Commands | Status | -| ---------------------------------- |-------------------------------------------------------------------------------------------------------------------------------------------------------| ------------------------- | -| Observability | `observability` | :white_check_mark: | -| Infrastructure as a Service (IaaS) | `beta network-area`
`beta network`
`beta volume`
`beta network-interface`
`beta public-ip`
`beta security-group-rule` | :white_check_mark: (beta) | -| Authorization | `project`, `organization` | :white_check_mark: | -| DNS | `dns` | :white_check_mark: | -| Kubernetes Engine (SKE) | `ske` | :white_check_mark: | -| Load Balancer | `load-balancer` | :white_check_mark: | -| LogMe | `logme` | :white_check_mark: | -| MariaDB | `mariadb` | :white_check_mark: | -| MongoDB Flex | `mongodbflex` | :white_check_mark: | -| Object Storage | `object-storage` | :white_check_mark: | -| OpenSearch | `opensearch` | :white_check_mark: | -| PostgreSQL Flex | `postgresflex` | :white_check_mark: | -| RabbitMQ | `rabbitmq` | :white_check_mark: | -| Redis | `redis` | :white_check_mark: | -| Resource Manager | `project` | :white_check_mark: | -| Secrets Manager | `secrets-manager` | :white_check_mark: | -| Server Backup Management | `beta server backup` | :white_check_mark: (beta) | -| Server Command (Run Command) | `beta server command` | :white_check_mark: (beta) | -| Service Account | `service-account` | :white_check_mark: | -| SQLServer Flex | `beta sqlserverflex` | :white_check_mark: (beta) | +| Service | CLI Commands | Status | +| ---------------------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------| ------------------------- | +| Observability | `observability` | :white_check_mark: | +| Infrastructure as a Service (IaaS) | `beta network-area`
`beta network`
`beta volume`
`beta network-interface`
`beta public-ip`
`beta security-group` | :white_check_mark: (beta) | +| Authorization | `project`, `organization` | :white_check_mark: | +| DNS | `dns` | :white_check_mark: | +| Kubernetes Engine (SKE) | `ske` | :white_check_mark: | +| Load Balancer | `load-balancer` | :white_check_mark: | +| LogMe | `logme` | :white_check_mark: | +| MariaDB | `mariadb` | :white_check_mark: | +| MongoDB Flex | `mongodbflex` | :white_check_mark: | +| Object Storage | `object-storage` | :white_check_mark: | +| OpenSearch | `opensearch` | :white_check_mark: | +| PostgreSQL Flex | `postgresflex` | :white_check_mark: | +| RabbitMQ | `rabbitmq` | :white_check_mark: | +| Redis | `redis` | :white_check_mark: | +| Resource Manager | `project` | :white_check_mark: | +| Secrets Manager | `secrets-manager` | :white_check_mark: | +| Server Backup Management | `beta server backup` | :white_check_mark: (beta) | +| Server Command (Run Command) | `beta server command` | :white_check_mark: (beta) | +| Service Account | `service-account` | :white_check_mark: | +| SQLServer Flex | `beta sqlserverflex` | :white_check_mark: (beta) | ## Authentication diff --git a/docs/stackit_beta.md b/docs/stackit_beta.md index dd4c22c40..075d7b9c4 100644 --- a/docs/stackit_beta.md +++ b/docs/stackit_beta.md @@ -44,7 +44,7 @@ stackit beta [flags] * [stackit beta network-area](./stackit_beta_network-area.md) - Provides functionality for STACKIT Network Area (SNA) * [stackit beta network-interface](./stackit_beta_network-interface.md) - Provides functionality for network interfaces * [stackit beta public-ip](./stackit_beta_public-ip.md) - Provides functionality for public IPs -* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules +* [stackit beta security-group](./stackit_beta_security-group.md) - Provides functionality for security groups * [stackit beta server](./stackit_beta_server.md) - Provides functionality for servers * [stackit beta sqlserverflex](./stackit_beta_sqlserverflex.md) - Provides functionality for SQLServer Flex * [stackit beta volume](./stackit_beta_volume.md) - Provides functionality for volumes diff --git a/docs/stackit_beta_security-group-rules.md b/docs/stackit_beta_security-group-rules.md deleted file mode 100644 index 478fde37c..000000000 --- a/docs/stackit_beta_security-group-rules.md +++ /dev/null @@ -1,36 +0,0 @@ -## stackit beta security-group-rules - -Provides functionality for security group rules - -### Synopsis - -Provides functionality for security group rules. - -``` -stackit beta security-group-rules [flags] -``` - -### Options - -``` - -h, --help Help for "stackit beta security-group-rules" -``` - -### Options inherited from parent commands - -``` - -y, --assume-yes If set, skips all confirmation prompts - --async If set, runs the command asynchronously - -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] - -p, --project-id string Project ID - --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") -``` - -### SEE ALSO - -* [stackit beta](./stackit_beta.md) - Contains beta STACKIT CLI commands -* [stackit beta security-group-rules create](./stackit_beta_security-group-rules_create.md) - Creates a security group rule -* [stackit beta security-group-rules delete](./stackit_beta_security-group-rules_delete.md) - Deletes a security group rule -* [stackit beta security-group-rules describe](./stackit_beta_security-group-rules_describe.md) - Shows details of a security group rule -* [stackit beta security-group-rules list](./stackit_beta_security-group-rules_list.md) - Lists all security group rules in a security group of a project - diff --git a/docs/stackit_beta_security-group.md b/docs/stackit_beta_security-group.md new file mode 100644 index 000000000..fcc28bdd7 --- /dev/null +++ b/docs/stackit_beta_security-group.md @@ -0,0 +1,33 @@ +## stackit beta security-group + +Provides functionality for security groups + +### Synopsis + +Provides functionality for security groups. + +``` +stackit beta security-group [flags] +``` + +### Options + +``` + -h, --help Help for "stackit beta security-group" +``` + +### Options inherited from parent commands + +``` + -y, --assume-yes If set, skips all confirmation prompts + --async If set, runs the command asynchronously + -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] + -p, --project-id string Project ID + --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") +``` + +### SEE ALSO + +* [stackit beta](./stackit_beta.md) - Contains beta STACKIT CLI commands +* [stackit beta security-group rule](./stackit_beta_security-group_rule.md) - Provides functionality for security group rules + diff --git a/docs/stackit_beta_security-group_rule.md b/docs/stackit_beta_security-group_rule.md new file mode 100644 index 000000000..a680f5bfc --- /dev/null +++ b/docs/stackit_beta_security-group_rule.md @@ -0,0 +1,36 @@ +## stackit beta security-group rule + +Provides functionality for security group rules + +### Synopsis + +Provides functionality for security group rules. + +``` +stackit beta security-group rule [flags] +``` + +### Options + +``` + -h, --help Help for "stackit beta security-group rule" +``` + +### Options inherited from parent commands + +``` + -y, --assume-yes If set, skips all confirmation prompts + --async If set, runs the command asynchronously + -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] + -p, --project-id string Project ID + --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") +``` + +### SEE ALSO + +* [stackit beta security-group](./stackit_beta_security-group.md) - Provides functionality for security groups +* [stackit beta security-group rule create](./stackit_beta_security-group_rule_create.md) - Creates a security group rule +* [stackit beta security-group rule delete](./stackit_beta_security-group_rule_delete.md) - Deletes a security group rule +* [stackit beta security-group rule describe](./stackit_beta_security-group_rule_describe.md) - Shows details of a security group rule +* [stackit beta security-group rule list](./stackit_beta_security-group_rule_list.md) - Lists all security group rules in a security group of a project + diff --git a/docs/stackit_beta_security-group-rules_create.md b/docs/stackit_beta_security-group_rule_create.md similarity index 70% rename from docs/stackit_beta_security-group-rules_create.md rename to docs/stackit_beta_security-group_rule_create.md index 78a884ff7..12c59bc68 100644 --- a/docs/stackit_beta_security-group-rules_create.md +++ b/docs/stackit_beta_security-group_rule_create.md @@ -1,4 +1,4 @@ -## stackit beta security-group-rules create +## stackit beta security-group rule create Creates a security group rule @@ -7,23 +7,23 @@ Creates a security group rule Creates a security group rule. ``` -stackit beta security-group-rules create [flags] +stackit beta security-group rule create [flags] ``` ### Examples ``` Create a security group rule for security group with ID "xxx" with direction "ingress" - $ stackit beta security-group-rule create --security-group-id xxx --direction ingress + $ stackit beta security-group rule create --security-group-id xxx --direction ingress Create a security group rule for security group with ID "xxx" with direction "egress", protocol "icmp" and icmp parameters - $ stackit beta security-group-rule create --security-group-id xxx --direction egress --protocol icmp --icmp-parameter-code 0 --icmp-parameter-type 8 + $ stackit beta security-group rule create --security-group-id xxx --direction egress --protocol-name icmp --icmp-parameter-code 0 --icmp-parameter-type 8 - Create a security group rule for security group with ID "xxx" with direction "ingress" and port range values - $ stackit beta security-group-rule create --security-group-id xxx --direction ingress --port-range-max 24 --port-range-min 22 + Create a security group rule for security group with ID "xxx" with direction "ingress", protocol "tcp" and port range values + $ stackit beta security-group rule create --security-group-id xxx --direction ingress --protocol-name tcp --port-range-max 24 --port-range-min 22 Create a security group rule for security group with ID "xxx" with direction "ingress" and protocol number 1 - $ stackit beta security-group-rule create --security-group-id xxx --direction ingress --protocol-number 1 + $ stackit beta security-group rule create --security-group-id xxx --direction ingress --protocol-number 1 ``` ### Options @@ -32,14 +32,14 @@ stackit beta security-group-rules create [flags] --description string The rule description --direction ingress The direction of the traffic which the rule should match. The possible values are: ingress, `egress` --ether-type string The ethertype which the rule should match - -h, --help Help for "stackit beta security-group-rules create" + -h, --help Help for "stackit beta security-group rule create" --icmp-parameter-code int ICMP code. Can be set if the protocol is ICMP --icmp-parameter-type int ICMP type. Can be set if the protocol is ICMP --ip-range string The remote IP range which the rule should match --port-range-max int The maximum port number. Should be greater or equal to the minimum. This should only be provided if the protocol is not ICMP --port-range-min int The minimum port number. Should be less or equal to the maximum. This should only be provided if the protocol is not ICMP - --protocol-name name The protocol name which the rule should match. Either name or `number` must be provided - --protocol-number name The protocol number which the rule should match. Either name or `number` must be provided + --protocol-name name The protocol name which the rule should match. If a protocol is to be defined, either name or `number` must be provided + --protocol-number name The protocol number which the rule should match. If a protocol is to be defined, either name or `number` must be provided --remote-security-group-id string The remote security group which the rule should match --security-group-id string The security group ID ``` @@ -56,5 +56,5 @@ stackit beta security-group-rules create [flags] ### SEE ALSO -* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules +* [stackit beta security-group rule](./stackit_beta_security-group_rule.md) - Provides functionality for security group rules diff --git a/docs/stackit_beta_security-group-rules_delete.md b/docs/stackit_beta_security-group_rule_delete.md similarity index 73% rename from docs/stackit_beta_security-group-rules_delete.md rename to docs/stackit_beta_security-group_rule_delete.md index 9e31a9612..fd56a81d6 100644 --- a/docs/stackit_beta_security-group-rules_delete.md +++ b/docs/stackit_beta_security-group_rule_delete.md @@ -1,4 +1,4 @@ -## stackit beta security-group-rules delete +## stackit beta security-group rule delete Deletes a security group rule @@ -9,20 +9,20 @@ If the security group rule is still in use, the deletion will fail ``` -stackit beta security-group-rules delete [flags] +stackit beta security-group rule delete [flags] ``` ### Examples ``` Delete security group rule with ID "xxx" in security group with ID "yyy" - $ stackit beta security-group-rule delete xxx --security-group-id yyy + $ stackit beta security-group rule delete xxx --security-group-id yyy ``` ### Options ``` - -h, --help Help for "stackit beta security-group-rules delete" + -h, --help Help for "stackit beta security-group rule delete" --security-group-id string The security group ID ``` @@ -38,5 +38,5 @@ stackit beta security-group-rules delete [flags] ### SEE ALSO -* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules +* [stackit beta security-group rule](./stackit_beta_security-group_rule.md) - Provides functionality for security group rules diff --git a/docs/stackit_beta_security-group-rules_describe.md b/docs/stackit_beta_security-group_rule_describe.md similarity index 71% rename from docs/stackit_beta_security-group-rules_describe.md rename to docs/stackit_beta_security-group_rule_describe.md index 5bbaaa4b8..eaa29cc08 100644 --- a/docs/stackit_beta_security-group-rules_describe.md +++ b/docs/stackit_beta_security-group_rule_describe.md @@ -1,4 +1,4 @@ -## stackit beta security-group-rules describe +## stackit beta security-group rule describe Shows details of a security group rule @@ -7,23 +7,23 @@ Shows details of a security group rule Shows details of a security group rule. ``` -stackit beta security-group-rules describe [flags] +stackit beta security-group rule describe [flags] ``` ### Examples ``` Show details of a security group rule with ID "xxx" in security group with ID "yyy" - $ stackit beta security-group-rule describe xxx --security-group-id yyy + $ stackit beta security-group rule describe xxx --security-group-id yyy Show details of a security group rule with ID "xxx" in security group with ID "yyy" in JSON format - $ stackit beta security-group-rule describe xxx --security-group-id yyy --output-format json + $ stackit beta security-group rule describe xxx --security-group-id yyy --output-format json ``` ### Options ``` - -h, --help Help for "stackit beta security-group-rules describe" + -h, --help Help for "stackit beta security-group rule describe" --security-group-id string The security group ID ``` @@ -39,5 +39,5 @@ stackit beta security-group-rules describe [flags] ### SEE ALSO -* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules +* [stackit beta security-group rule](./stackit_beta_security-group_rule.md) - Provides functionality for security group rules diff --git a/docs/stackit_beta_security-group-rules_list.md b/docs/stackit_beta_security-group_rule_list.md similarity index 71% rename from docs/stackit_beta_security-group-rules_list.md rename to docs/stackit_beta_security-group_rule_list.md index fa441b2c2..02fef7466 100644 --- a/docs/stackit_beta_security-group-rules_list.md +++ b/docs/stackit_beta_security-group_rule_list.md @@ -1,4 +1,4 @@ -## stackit beta security-group-rules list +## stackit beta security-group rule list Lists all security group rules in a security group of a project @@ -7,26 +7,26 @@ Lists all security group rules in a security group of a project Lists all security group rules in a security group of a project. ``` -stackit beta security-group-rules list [flags] +stackit beta security-group rule list [flags] ``` ### Examples ``` Lists all security group rules in security group with ID "xxx" - $ stackit beta security-group-rule list --security-group-id xxx + $ stackit beta security-group rule list --security-group-id xxx Lists all security group rules in security group with ID "xxx" in JSON format - $ stackit beta security-group-rule list --security-group-id xxx --output-format json + $ stackit beta security-group rule list --security-group-id xxx --output-format json Lists up to 10 security group rules in security group with ID "xxx" - $ stackit beta security-group-rule list --security-group-id xxx --limit 10 + $ stackit beta security-group rule list --security-group-id xxx --limit 10 ``` ### Options ``` - -h, --help Help for "stackit beta security-group-rules list" + -h, --help Help for "stackit beta security-group rule list" --limit int Maximum number of entries to list --security-group-id string The security group ID ``` @@ -43,5 +43,5 @@ stackit beta security-group-rules list [flags] ### SEE ALSO -* [stackit beta security-group-rules](./stackit_beta_security-group-rules.md) - Provides functionality for security group rules +* [stackit beta security-group rule](./stackit_beta_security-group_rule.md) - Provides functionality for security group rules diff --git a/internal/cmd/beta/beta.go b/internal/cmd/beta/beta.go index 6cf525cb7..219b5e35c 100644 --- a/internal/cmd/beta/beta.go +++ b/internal/cmd/beta/beta.go @@ -7,7 +7,7 @@ import ( networkArea "github.com/stackitcloud/stackit-cli/internal/cmd/beta/network-area" networkinterface "github.com/stackitcloud/stackit-cli/internal/cmd/beta/network-interface" publicip "github.com/stackitcloud/stackit-cli/internal/cmd/beta/public-ip" - securitygrouprule "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule" + securitygroup "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/server" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/sqlserverflex" "github.com/stackitcloud/stackit-cli/internal/cmd/beta/volume" @@ -49,5 +49,5 @@ func addSubcommands(cmd *cobra.Command, p *print.Printer) { cmd.AddCommand(volume.NewCmd(p)) cmd.AddCommand(networkinterface.NewCmd(p)) cmd.AddCommand(publicip.NewCmd(p)) - cmd.AddCommand(securitygrouprule.NewCmd(p)) + cmd.AddCommand(securitygroup.NewCmd(p)) } diff --git a/internal/cmd/beta/security-group-rule/create/create.go b/internal/cmd/beta/security-group/rule/create/create.go similarity index 92% rename from internal/cmd/beta/security-group-rule/create/create.go rename to internal/cmd/beta/security-group/rule/create/create.go index fa3d8d97e..7a334b188 100644 --- a/internal/cmd/beta/security-group-rule/create/create.go +++ b/internal/cmd/beta/security-group/rule/create/create.go @@ -60,19 +60,19 @@ func NewCmd(p *print.Printer) *cobra.Command { Example: examples.Build( examples.NewExample( `Create a security group rule for security group with ID "xxx" with direction "ingress"`, - `$ stackit beta security-group-rule create --security-group-id xxx --direction ingress`, + `$ stackit beta security-group rule create --security-group-id xxx --direction ingress`, ), examples.NewExample( `Create a security group rule for security group with ID "xxx" with direction "egress", protocol "icmp" and icmp parameters`, - `$ stackit beta security-group-rule create --security-group-id xxx --direction egress --protocol icmp --icmp-parameter-code 0 --icmp-parameter-type 8`, + `$ stackit beta security-group rule create --security-group-id xxx --direction egress --protocol-name icmp --icmp-parameter-code 0 --icmp-parameter-type 8`, ), examples.NewExample( - `Create a security group rule for security group with ID "xxx" with direction "ingress" and port range values`, - `$ stackit beta security-group-rule create --security-group-id xxx --direction ingress --port-range-max 24 --port-range-min 22`, + `Create a security group rule for security group with ID "xxx" with direction "ingress", protocol "tcp" and port range values`, + `$ stackit beta security-group rule create --security-group-id xxx --direction ingress --protocol-name tcp --port-range-max 24 --port-range-min 22`, ), examples.NewExample( `Create a security group rule for security group with ID "xxx" with direction "ingress" and protocol number 1 `, - `$ stackit beta security-group-rule create --security-group-id xxx --direction ingress --protocol-number 1`, + `$ stackit beta security-group rule create --security-group-id xxx --direction ingress --protocol-number 1`, ), ), RunE: func(cmd *cobra.Command, _ []string) error { @@ -123,7 +123,7 @@ func NewCmd(p *print.Printer) *cobra.Command { } func configureFlags(cmd *cobra.Command) { - cmd.Flags().String(securityGroupIdFlag, "", "The security group ID") + cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "The security group ID") cmd.Flags().String(directionFlag, "", "The direction of the traffic which the rule should match. The possible values are: `ingress`, `egress`") cmd.Flags().String(descriptionFlag, "", "The rule description") cmd.Flags().String(etherTypeFlag, "", "The ethertype which the rule should match") @@ -133,8 +133,8 @@ func configureFlags(cmd *cobra.Command) { cmd.Flags().Int64(portRangeMaxFlag, 0, "The maximum port number. Should be greater or equal to the minimum. This should only be provided if the protocol is not ICMP") cmd.Flags().Int64(portRangeMinFlag, 0, "The minimum port number. Should be less or equal to the maximum. This should only be provided if the protocol is not ICMP") cmd.Flags().Var(flags.UUIDFlag(), remoteSecurityGroupIdFlag, "The remote security group which the rule should match") - cmd.Flags().Int64(protocolNumberFlag, 0, "The protocol number which the rule should match. Either `name` or `number` must be provided") - cmd.Flags().String(protocolNameFlag, "", "The protocol name which the rule should match. Either `name` or `number` must be provided") + cmd.Flags().Int64(protocolNumberFlag, 0, "The protocol number which the rule should match. If a protocol is to be defined, either `name` or `number` must be provided") + cmd.Flags().String(protocolNameFlag, "", "The protocol name which the rule should match. If a protocol is to be defined, either `name` or `number` must be provided") err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag, directionFlag) cmd.MarkFlagsMutuallyExclusive(protocolNumberFlag, protocolNameFlag) diff --git a/internal/cmd/beta/security-group-rule/create/create_test.go b/internal/cmd/beta/security-group/rule/create/create_test.go similarity index 100% rename from internal/cmd/beta/security-group-rule/create/create_test.go rename to internal/cmd/beta/security-group/rule/create/create_test.go diff --git a/internal/cmd/beta/security-group-rule/delete/delete.go b/internal/cmd/beta/security-group/rule/delete/delete.go similarity index 98% rename from internal/cmd/beta/security-group-rule/delete/delete.go rename to internal/cmd/beta/security-group/rule/delete/delete.go index 5a6d63a71..760b3c8bd 100644 --- a/internal/cmd/beta/security-group-rule/delete/delete.go +++ b/internal/cmd/beta/security-group/rule/delete/delete.go @@ -41,7 +41,7 @@ func NewCmd(p *print.Printer) *cobra.Command { Example: examples.Build( examples.NewExample( `Delete security group rule with ID "xxx" in security group with ID "yyy"`, - "$ stackit beta security-group-rule delete xxx --security-group-id yyy", + "$ stackit beta security-group rule delete xxx --security-group-id yyy", ), ), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/internal/cmd/beta/security-group-rule/delete/delete_test.go b/internal/cmd/beta/security-group/rule/delete/delete_test.go similarity index 100% rename from internal/cmd/beta/security-group-rule/delete/delete_test.go rename to internal/cmd/beta/security-group/rule/delete/delete_test.go diff --git a/internal/cmd/beta/security-group-rule/describe/describe.go b/internal/cmd/beta/security-group/rule/describe/describe.go similarity index 97% rename from internal/cmd/beta/security-group-rule/describe/describe.go rename to internal/cmd/beta/security-group/rule/describe/describe.go index a3b940cf3..76ef8fb66 100644 --- a/internal/cmd/beta/security-group-rule/describe/describe.go +++ b/internal/cmd/beta/security-group/rule/describe/describe.go @@ -41,11 +41,11 @@ func NewCmd(p *print.Printer) *cobra.Command { Example: examples.Build( examples.NewExample( `Show details of a security group rule with ID "xxx" in security group with ID "yyy"`, - "$ stackit beta security-group-rule describe xxx --security-group-id yyy", + "$ stackit beta security-group rule describe xxx --security-group-id yyy", ), examples.NewExample( `Show details of a security group rule with ID "xxx" in security group with ID "yyy" in JSON format`, - "$ stackit beta security-group-rule describe xxx --security-group-id yyy --output-format json", + "$ stackit beta security-group rule describe xxx --security-group-id yyy --output-format json", ), ), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/internal/cmd/beta/security-group-rule/describe/describe_test.go b/internal/cmd/beta/security-group/rule/describe/describe_test.go similarity index 100% rename from internal/cmd/beta/security-group-rule/describe/describe_test.go rename to internal/cmd/beta/security-group/rule/describe/describe_test.go diff --git a/internal/cmd/beta/security-group-rule/list/list.go b/internal/cmd/beta/security-group/rule/list/list.go similarity index 96% rename from internal/cmd/beta/security-group-rule/list/list.go rename to internal/cmd/beta/security-group/rule/list/list.go index e4eaf3044..9f884fb30 100644 --- a/internal/cmd/beta/security-group-rule/list/list.go +++ b/internal/cmd/beta/security-group/rule/list/list.go @@ -42,15 +42,15 @@ func NewCmd(p *print.Printer) *cobra.Command { Example: examples.Build( examples.NewExample( `Lists all security group rules in security group with ID "xxx"`, - "$ stackit beta security-group-rule list --security-group-id xxx", + "$ stackit beta security-group rule list --security-group-id xxx", ), examples.NewExample( `Lists all security group rules in security group with ID "xxx" in JSON format`, - "$ stackit beta security-group-rule list --security-group-id xxx --output-format json", + "$ stackit beta security-group rule list --security-group-id xxx --output-format json", ), examples.NewExample( `Lists up to 10 security group rules in security group with ID "xxx"`, - "$ stackit beta security-group-rule list --security-group-id xxx --limit 10", + "$ stackit beta security-group rule list --security-group-id xxx --limit 10", ), ), RunE: func(cmd *cobra.Command, _ []string) error { diff --git a/internal/cmd/beta/security-group-rule/list/list_test.go b/internal/cmd/beta/security-group/rule/list/list_test.go similarity index 100% rename from internal/cmd/beta/security-group-rule/list/list_test.go rename to internal/cmd/beta/security-group/rule/list/list_test.go diff --git a/internal/cmd/beta/security-group-rule/security-group-rule.go b/internal/cmd/beta/security-group/rule/security_group_rule.go similarity index 87% rename from internal/cmd/beta/security-group-rule/security-group-rule.go rename to internal/cmd/beta/security-group/rule/security_group_rule.go index a12f1dc09..26b3443f5 100644 --- a/internal/cmd/beta/security-group-rule/security-group-rule.go +++ b/internal/cmd/beta/security-group/rule/security_group_rule.go @@ -1,10 +1,10 @@ -package securitygrouprule +package rule import ( - "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule/create" - "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule/delete" - "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule/describe" - "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group-rule/list" + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group/rule/create" + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group/rule/delete" + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group/rule/describe" + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group/rule/list" "github.com/stackitcloud/stackit-cli/internal/pkg/args" "github.com/stackitcloud/stackit-cli/internal/pkg/print" "github.com/stackitcloud/stackit-cli/internal/pkg/utils" @@ -14,7 +14,7 @@ import ( func NewCmd(p *print.Printer) *cobra.Command { cmd := &cobra.Command{ - Use: "security-group-rules", + Use: "rule", Short: "Provides functionality for security group rules", Long: "Provides functionality for security group rules.", Args: args.NoArgs, diff --git a/internal/cmd/beta/security-group/security_group.go b/internal/cmd/beta/security-group/security_group.go new file mode 100644 index 000000000..53f380d90 --- /dev/null +++ b/internal/cmd/beta/security-group/security_group.go @@ -0,0 +1,26 @@ +package securitygroup + +import ( + "github.com/stackitcloud/stackit-cli/internal/cmd/beta/security-group/rule" + "github.com/stackitcloud/stackit-cli/internal/pkg/args" + "github.com/stackitcloud/stackit-cli/internal/pkg/print" + "github.com/stackitcloud/stackit-cli/internal/pkg/utils" + + "github.com/spf13/cobra" +) + +func NewCmd(p *print.Printer) *cobra.Command { + cmd := &cobra.Command{ + Use: "security-group", + Short: "Provides functionality for security groups", + Long: "Provides functionality for security groups.", + Args: args.NoArgs, + Run: utils.CmdHelp, + } + addSubcommands(cmd, p) + return cmd +} + +func addSubcommands(cmd *cobra.Command, p *print.Printer) { + cmd.AddCommand(rule.NewCmd(p)) +} From d95dfcf536d5ea3770859ba36ea95bd1e0c276ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Wed, 11 Dec 2024 09:26:57 +0100 Subject: [PATCH 16/19] add check for protocol if port range info is provided --- internal/cmd/beta/security-group/rule/create/create.go | 6 ++++++ internal/pkg/errors/errors.go | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/internal/cmd/beta/security-group/rule/create/create.go b/internal/cmd/beta/security-group/rule/create/create.go index 7a334b188..dab2a4137 100644 --- a/internal/cmd/beta/security-group/rule/create/create.go +++ b/internal/cmd/beta/security-group/rule/create/create.go @@ -179,6 +179,12 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { } } + if (model.PortRangeMin != nil || model.PortRangeMax != nil) && (model.ProtocolNumber == nil && model.ProtocolName == nil) { + return nil, &cliErr.SecurityGroupRuleProtocolMissingForPortRangeError{ + Cmd: cmd, + } + } + if p.IsVerbosityDebug() { modelStr, err := print.BuildDebugStrFromInputModel(model) if err != nil { diff --git a/internal/pkg/errors/errors.go b/internal/pkg/errors/errors.go index 9390e33a7..a68ccd212 100644 --- a/internal/pkg/errors/errors.go +++ b/internal/pkg/errors/errors.go @@ -151,11 +151,21 @@ To enable it, run: IAAS_SECURITY_GROUP_RULE_PROTOCOL_PARAMETERS_CONFLICT = `"icmp-parameter-code" and "icmp-parameter-type" attributes can't be provided if "protocol-name" is not "icmp" or "ipv6-icmp"` + IAAS_SECURITY_GROUP_RULE_PROTOCOL_MISSING_FOR_PORT_RANGE = `"protocol-name" or "protocol-number" must be provided if "port-range-min" and "port-range-max" attributes are given` + IAAS_SERVER_NIC_ATTACH_MISSING_NIC_ID = `The "network-interface-id" flag must be provided if the "create" flag is not provided.` IAAS_SERVER_NIC_DETACH_MISSING_NIC_ID = `The "network-interface-id" flag must be provided if the "delete" flag is not provided.` ) +type SecurityGroupRuleProtocolMissingForPortRangeError struct { + Cmd *cobra.Command +} + +func (e *SecurityGroupRuleProtocolMissingForPortRangeError) Error() string { + return IAAS_SECURITY_GROUP_RULE_PROTOCOL_MISSING_FOR_PORT_RANGE +} + type SecurityGroupRuleProtocolParametersConflictError struct { Cmd *cobra.Command } From 961bf2f66de78d4c0b2341119723dd948240fdd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Wed, 11 Dec 2024 15:48:04 +0100 Subject: [PATCH 17/19] changed the flag description --- docs/stackit_beta_security-group_rule_create.md | 4 ++-- internal/cmd/beta/security-group/rule/create/create.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/stackit_beta_security-group_rule_create.md b/docs/stackit_beta_security-group_rule_create.md index 12c59bc68..7975c731f 100644 --- a/docs/stackit_beta_security-group_rule_create.md +++ b/docs/stackit_beta_security-group_rule_create.md @@ -38,8 +38,8 @@ stackit beta security-group rule create [flags] --ip-range string The remote IP range which the rule should match --port-range-max int The maximum port number. Should be greater or equal to the minimum. This should only be provided if the protocol is not ICMP --port-range-min int The minimum port number. Should be less or equal to the maximum. This should only be provided if the protocol is not ICMP - --protocol-name name The protocol name which the rule should match. If a protocol is to be defined, either name or `number` must be provided - --protocol-number name The protocol number which the rule should match. If a protocol is to be defined, either name or `number` must be provided + --protocol-name protocol-name The protocol name which the rule should match. If a protocol is to be defined, either protocol-name or `protocol-number` must be provided + --protocol-number protocol-name The protocol number which the rule should match. If a protocol is to be defined, either protocol-name or `protocol-number` must be provided --remote-security-group-id string The remote security group which the rule should match --security-group-id string The security group ID ``` diff --git a/internal/cmd/beta/security-group/rule/create/create.go b/internal/cmd/beta/security-group/rule/create/create.go index dab2a4137..3db24a658 100644 --- a/internal/cmd/beta/security-group/rule/create/create.go +++ b/internal/cmd/beta/security-group/rule/create/create.go @@ -133,8 +133,8 @@ func configureFlags(cmd *cobra.Command) { cmd.Flags().Int64(portRangeMaxFlag, 0, "The maximum port number. Should be greater or equal to the minimum. This should only be provided if the protocol is not ICMP") cmd.Flags().Int64(portRangeMinFlag, 0, "The minimum port number. Should be less or equal to the maximum. This should only be provided if the protocol is not ICMP") cmd.Flags().Var(flags.UUIDFlag(), remoteSecurityGroupIdFlag, "The remote security group which the rule should match") - cmd.Flags().Int64(protocolNumberFlag, 0, "The protocol number which the rule should match. If a protocol is to be defined, either `name` or `number` must be provided") - cmd.Flags().String(protocolNameFlag, "", "The protocol name which the rule should match. If a protocol is to be defined, either `name` or `number` must be provided") + cmd.Flags().Int64(protocolNumberFlag, 0, "The protocol number which the rule should match. If a protocol is to be defined, either `protocol-name` or `protocol-number` must be provided") + cmd.Flags().String(protocolNameFlag, "", "The protocol name which the rule should match. If a protocol is to be defined, either `protocol-name` or `protocol-number` must be provided") err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag, directionFlag) cmd.MarkFlagsMutuallyExclusive(protocolNumberFlag, protocolNameFlag) From cbbd8ab3205d6c3777a4bb9a32cd099d61ae6b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Wed, 11 Dec 2024 17:08:00 +0100 Subject: [PATCH 18/19] remove the flag control (api errors are sufficient) --- .../beta/security-group/rule/create/create.go | 22 -------------- .../security-group/rule/create/create_test.go | 15 ---------- internal/pkg/errors/errors.go | 30 ------------------- 3 files changed, 67 deletions(-) diff --git a/internal/cmd/beta/security-group/rule/create/create.go b/internal/cmd/beta/security-group/rule/create/create.go index 3db24a658..28169384f 100644 --- a/internal/cmd/beta/security-group/rule/create/create.go +++ b/internal/cmd/beta/security-group/rule/create/create.go @@ -163,28 +163,6 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { ProtocolName: flags.FlagToStringPointer(p, cmd, protocolNameFlag), } - if model.ProtocolName != nil { - if *model.ProtocolName == "icmp" || *model.ProtocolName == "ipv6-icmp" { - if model.PortRangeMin != nil || model.PortRangeMax != nil { - return nil, &cliErr.SecurityGroupRuleProtocolPortRangeConflictError{ - Cmd: cmd, - } - } - } else { - if model.IcmpParameterCode != nil || model.IcmpParameterType != nil { - return nil, &cliErr.SecurityGroupRuleProtocolParametersConflictError{ - Cmd: cmd, - } - } - } - } - - if (model.PortRangeMin != nil || model.PortRangeMax != nil) && (model.ProtocolNumber == nil && model.ProtocolName == nil) { - return nil, &cliErr.SecurityGroupRuleProtocolMissingForPortRangeError{ - Cmd: cmd, - } - } - if p.IsVerbosityDebug() { modelStr, err := print.BuildDebugStrFromInputModel(model) if err != nil { diff --git a/internal/cmd/beta/security-group/rule/create/create_test.go b/internal/cmd/beta/security-group/rule/create/create_test.go index c8d7ee332..5d0fd2098 100644 --- a/internal/cmd/beta/security-group/rule/create/create_test.go +++ b/internal/cmd/beta/security-group/rule/create/create_test.go @@ -176,13 +176,6 @@ func TestParseInput(t *testing.T) { }), isValid: false, }, - { - description: "protocol is icmp and port range values are provided", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - delete(flagValues, protocolNumberFlag) - }), - isValid: false, - }, { description: "protocol is not icmp and port range values are provided", flagValues: fixtureFlagValues(func(flagValues map[string]string) { @@ -199,14 +192,6 @@ func TestParseInput(t *testing.T) { model.ProtocolNumber = nil }), }, - { - description: "protocol is not icmp and icmp parameters are provided", - flagValues: fixtureFlagValues(func(flagValues map[string]string) { - flagValues[protocolNameFlag] = "not-icmp" - delete(flagValues, protocolNumberFlag) - }), - isValid: false, - }, { description: "no values", flagValues: map[string]string{}, diff --git a/internal/pkg/errors/errors.go b/internal/pkg/errors/errors.go index a68ccd212..324abbadf 100644 --- a/internal/pkg/errors/errors.go +++ b/internal/pkg/errors/errors.go @@ -147,41 +147,11 @@ To enable it, run: IAAS_SERVER_MISSING_IMAGE_OR_VOLUME_FLAGS = `Either "image-id" or "boot-volume-source-type" and "boot-volume-source-id" flags must be provided.` - IAAS_SECURITY_GROUP_RULE_PROTOCOL_PORT_RANGE_CONFLICT = `"port-range-min" and "port-range-max" attributes can't be provided if "protocol-name" is set to "icmp" or "ipv6-icmp"` - - IAAS_SECURITY_GROUP_RULE_PROTOCOL_PARAMETERS_CONFLICT = `"icmp-parameter-code" and "icmp-parameter-type" attributes can't be provided if "protocol-name" is not "icmp" or "ipv6-icmp"` - - IAAS_SECURITY_GROUP_RULE_PROTOCOL_MISSING_FOR_PORT_RANGE = `"protocol-name" or "protocol-number" must be provided if "port-range-min" and "port-range-max" attributes are given` - IAAS_SERVER_NIC_ATTACH_MISSING_NIC_ID = `The "network-interface-id" flag must be provided if the "create" flag is not provided.` IAAS_SERVER_NIC_DETACH_MISSING_NIC_ID = `The "network-interface-id" flag must be provided if the "delete" flag is not provided.` ) -type SecurityGroupRuleProtocolMissingForPortRangeError struct { - Cmd *cobra.Command -} - -func (e *SecurityGroupRuleProtocolMissingForPortRangeError) Error() string { - return IAAS_SECURITY_GROUP_RULE_PROTOCOL_MISSING_FOR_PORT_RANGE -} - -type SecurityGroupRuleProtocolParametersConflictError struct { - Cmd *cobra.Command -} - -func (e *SecurityGroupRuleProtocolParametersConflictError) Error() string { - return IAAS_SECURITY_GROUP_RULE_PROTOCOL_PARAMETERS_CONFLICT -} - -type SecurityGroupRuleProtocolPortRangeConflictError struct { - Cmd *cobra.Command -} - -func (e *SecurityGroupRuleProtocolPortRangeConflictError) Error() string { - return IAAS_SECURITY_GROUP_RULE_PROTOCOL_PORT_RANGE_CONFLICT -} - type ServerNicAttachMissingNicIdError struct { Cmd *cobra.Command } From 4251b97993f3ff0d4afdab551b9311ff364f5add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Thu, 12 Dec 2024 08:38:42 +0100 Subject: [PATCH 19/19] edit flag descriptions --- ...stackit_beta_security-group_rule_create.md | 6 ++--- .../beta/security-group/rule/create/create.go | 24 +++++++++---------- .../beta/security-group/rule/delete/delete.go | 2 +- .../security-group/rule/describe/describe.go | 2 +- .../cmd/beta/security-group/rule/list/list.go | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/stackit_beta_security-group_rule_create.md b/docs/stackit_beta_security-group_rule_create.md index 7975c731f..7fba6fe31 100644 --- a/docs/stackit_beta_security-group_rule_create.md +++ b/docs/stackit_beta_security-group_rule_create.md @@ -30,7 +30,7 @@ stackit beta security-group rule create [flags] ``` --description string The rule description - --direction ingress The direction of the traffic which the rule should match. The possible values are: ingress, `egress` + --direction string The direction of the traffic which the rule should match. The possible values are: "ingress", "egress" --ether-type string The ethertype which the rule should match -h, --help Help for "stackit beta security-group rule create" --icmp-parameter-code int ICMP code. Can be set if the protocol is ICMP @@ -38,8 +38,8 @@ stackit beta security-group rule create [flags] --ip-range string The remote IP range which the rule should match --port-range-max int The maximum port number. Should be greater or equal to the minimum. This should only be provided if the protocol is not ICMP --port-range-min int The minimum port number. Should be less or equal to the maximum. This should only be provided if the protocol is not ICMP - --protocol-name protocol-name The protocol name which the rule should match. If a protocol is to be defined, either protocol-name or `protocol-number` must be provided - --protocol-number protocol-name The protocol number which the rule should match. If a protocol is to be defined, either protocol-name or `protocol-number` must be provided + --protocol-name string The protocol name which the rule should match. If a protocol is to be defined, either "protocol-name" or "protocol-number" must be provided + --protocol-number int The protocol number which the rule should match. If a protocol is to be defined, either "protocol-name" or "protocol-number" must be provided --remote-security-group-id string The remote security group which the rule should match --security-group-id string The security group ID ``` diff --git a/internal/cmd/beta/security-group/rule/create/create.go b/internal/cmd/beta/security-group/rule/create/create.go index 28169384f..04b49745e 100644 --- a/internal/cmd/beta/security-group/rule/create/create.go +++ b/internal/cmd/beta/security-group/rule/create/create.go @@ -123,18 +123,18 @@ func NewCmd(p *print.Printer) *cobra.Command { } func configureFlags(cmd *cobra.Command) { - cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "The security group ID") - cmd.Flags().String(directionFlag, "", "The direction of the traffic which the rule should match. The possible values are: `ingress`, `egress`") - cmd.Flags().String(descriptionFlag, "", "The rule description") - cmd.Flags().String(etherTypeFlag, "", "The ethertype which the rule should match") - cmd.Flags().Int64(icmpParameterCodeFlag, 0, "ICMP code. Can be set if the protocol is ICMP") - cmd.Flags().Int64(icmpParameterTypeFlag, 0, "ICMP type. Can be set if the protocol is ICMP") - cmd.Flags().String(ipRangeFlag, "", "The remote IP range which the rule should match") - cmd.Flags().Int64(portRangeMaxFlag, 0, "The maximum port number. Should be greater or equal to the minimum. This should only be provided if the protocol is not ICMP") - cmd.Flags().Int64(portRangeMinFlag, 0, "The minimum port number. Should be less or equal to the maximum. This should only be provided if the protocol is not ICMP") - cmd.Flags().Var(flags.UUIDFlag(), remoteSecurityGroupIdFlag, "The remote security group which the rule should match") - cmd.Flags().Int64(protocolNumberFlag, 0, "The protocol number which the rule should match. If a protocol is to be defined, either `protocol-name` or `protocol-number` must be provided") - cmd.Flags().String(protocolNameFlag, "", "The protocol name which the rule should match. If a protocol is to be defined, either `protocol-name` or `protocol-number` must be provided") + cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, `The security group ID`) + cmd.Flags().String(directionFlag, "", `The direction of the traffic which the rule should match. The possible values are: "ingress", "egress"`) + cmd.Flags().String(descriptionFlag, "", `The rule description`) + cmd.Flags().String(etherTypeFlag, "", `The ethertype which the rule should match`) + cmd.Flags().Int64(icmpParameterCodeFlag, 0, `ICMP code. Can be set if the protocol is ICMP`) + cmd.Flags().Int64(icmpParameterTypeFlag, 0, `ICMP type. Can be set if the protocol is ICMP`) + cmd.Flags().String(ipRangeFlag, "", `The remote IP range which the rule should match`) + cmd.Flags().Int64(portRangeMaxFlag, 0, `The maximum port number. Should be greater or equal to the minimum. This should only be provided if the protocol is not ICMP`) + cmd.Flags().Int64(portRangeMinFlag, 0, `The minimum port number. Should be less or equal to the maximum. This should only be provided if the protocol is not ICMP`) + cmd.Flags().Var(flags.UUIDFlag(), remoteSecurityGroupIdFlag, `The remote security group which the rule should match`) + cmd.Flags().Int64(protocolNumberFlag, 0, `The protocol number which the rule should match. If a protocol is to be defined, either "protocol-name" or "protocol-number" must be provided`) + cmd.Flags().String(protocolNameFlag, "", `The protocol name which the rule should match. If a protocol is to be defined, either "protocol-name" or "protocol-number" must be provided`) err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag, directionFlag) cmd.MarkFlagsMutuallyExclusive(protocolNumberFlag, protocolNameFlag) diff --git a/internal/cmd/beta/security-group/rule/delete/delete.go b/internal/cmd/beta/security-group/rule/delete/delete.go index 760b3c8bd..b28ade08e 100644 --- a/internal/cmd/beta/security-group/rule/delete/delete.go +++ b/internal/cmd/beta/security-group/rule/delete/delete.go @@ -93,7 +93,7 @@ func NewCmd(p *print.Printer) *cobra.Command { } func configureFlags(cmd *cobra.Command) { - cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "The security group ID") + cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, `The security group ID`) err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag) cobra.CheckErr(err) diff --git a/internal/cmd/beta/security-group/rule/describe/describe.go b/internal/cmd/beta/security-group/rule/describe/describe.go index 76ef8fb66..93f7df60c 100644 --- a/internal/cmd/beta/security-group/rule/describe/describe.go +++ b/internal/cmd/beta/security-group/rule/describe/describe.go @@ -76,7 +76,7 @@ func NewCmd(p *print.Printer) *cobra.Command { } func configureFlags(cmd *cobra.Command) { - cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "The security group ID") + cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, `The security group ID`) err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag) cobra.CheckErr(err) diff --git a/internal/cmd/beta/security-group/rule/list/list.go b/internal/cmd/beta/security-group/rule/list/list.go index 9f884fb30..3675dec91 100644 --- a/internal/cmd/beta/security-group/rule/list/list.go +++ b/internal/cmd/beta/security-group/rule/list/list.go @@ -103,8 +103,8 @@ func NewCmd(p *print.Printer) *cobra.Command { } func configureFlags(cmd *cobra.Command) { - cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list") - cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "The security group ID") + cmd.Flags().Int64(limitFlag, 0, `Maximum number of entries to list`) + cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, `The security group ID`) err := flags.MarkFlagsRequired(cmd, securityGroupIdFlag) cobra.CheckErr(err)