diff --git a/cmd/scw/testdata/test-all-usage-vpc-rule-edit-usage.golden b/cmd/scw/testdata/test-all-usage-vpc-rule-edit-usage.golden new file mode 100644 index 0000000000..bcb62be09c --- /dev/null +++ b/cmd/scw/testdata/test-all-usage-vpc-rule-edit-usage.golden @@ -0,0 +1,23 @@ +🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲 +πŸŸ₯πŸŸ₯πŸŸ₯ STDERR️️ πŸŸ₯πŸŸ₯πŸŸ₯️ +This command starts your default editor to edit a marshaled version of your resource +Default editor will be taken from $VISUAL, then $EDITOR or an editor based on your system + +USAGE: + scw vpc rule edit [arg=value ...] + +ARGS: + vpc-id ID of the Network ACL's VPC + [is-ipv6] Defines whether this set of ACL rules is for IPv6 (false = IPv4). Each Network ACL can have rules for only one IP type + [default-policy] Action to take for packets which do not match any rules + [mode=yaml] marshaling used when editing data (yaml | json) + [region=fr-par] Region to target. If none is passed will use default region from the config + +FLAGS: + -h, --help help for edit + +GLOBAL FLAGS: + -c, --config string The path to the config file + -D, --debug Enable debug mode + -o, --output string Output format: json or human, see 'scw help output' for more info (default "human") + -p, --profile string The config profile to use diff --git a/cmd/scw/testdata/test-all-usage-vpc-rule-usage.golden b/cmd/scw/testdata/test-all-usage-vpc-rule-usage.golden index 5daacfa28c..a5c62b954f 100644 --- a/cmd/scw/testdata/test-all-usage-vpc-rule-usage.golden +++ b/cmd/scw/testdata/test-all-usage-vpc-rule-usage.golden @@ -6,6 +6,7 @@ USAGE: scw vpc rule AVAILABLE COMMANDS: + edit Edit all ACL rules of a VPC get Get ACL Rules for VPC set Set VPC ACL rules diff --git a/docs/commands/vpc.md b/docs/commands/vpc.md index e6bbfd8856..ac08dea044 100644 --- a/docs/commands/vpc.md +++ b/docs/commands/vpc.md @@ -17,6 +17,7 @@ This API allows you to manage your Virtual Private Clouds (VPCs) and Private Net - [Return routes with associated next hop data](#return-routes-with-associated-next-hop-data) - [Update Route](#update-route) - [Rule management command](#rule-management-command) + - [Edit all ACL rules of a VPC](#edit-all-acl-rules-of-a-vpc) - [Get ACL Rules for VPC](#get-acl-rules-for-vpc) - [Set VPC ACL rules](#set-vpc-acl-rules) - [Subnet management command](#subnet-management-command) @@ -316,6 +317,30 @@ scw vpc route update [arg=value ...] ACL Rules. +### Edit all ACL rules of a VPC + +This command starts your default editor to edit a marshaled version of your resource +Default editor will be taken from $VISUAL, then $EDITOR or an editor based on your system + +**Usage:** + +``` +scw vpc rule edit [arg=value ...] +``` + + +**Args:** + +| Name | | Description | +|------|---|-------------| +| vpc-id | Required | ID of the Network ACL's VPC | +| is-ipv6 | | Defines whether this set of ACL rules is for IPv6 (false = IPv4). Each Network ACL can have rules for only one IP type | +| default-policy | | Action to take for packets which do not match any rules | +| mode | Default: `yaml`
One of: `yaml`, `json` | marshaling used when editing data | +| region | Default: `fr-par` | Region to target. If none is passed will use default region from the config | + + + ### Get ACL Rules for VPC Retrieve a list of ACL rules for a VPC, specified by its VPC ID. diff --git a/internal/namespaces/vpc/v2/custom.go b/internal/namespaces/vpc/v2/custom.go index 59803fcacb..6102defb5f 100644 --- a/internal/namespaces/vpc/v2/custom.go +++ b/internal/namespaces/vpc/v2/custom.go @@ -15,5 +15,9 @@ func GetCommands() *core.Commands { cmds.MustFind("vpc", "private-network", "get").Override(privateNetworkGetBuilder) human.RegisterMarshalerFunc(vpc.PrivateNetwork{}, privateNetworkMarshalerFunc) + cmds.Merge(core.NewCommands( + vpcACLEditCommand(), + )) + return cmds } diff --git a/internal/namespaces/vpc/v2/custom_acl.go b/internal/namespaces/vpc/v2/custom_acl.go new file mode 100644 index 0000000000..d1d5d63bcd --- /dev/null +++ b/internal/namespaces/vpc/v2/custom_acl.go @@ -0,0 +1,117 @@ +package vpc + +import ( + "context" + "fmt" + "reflect" + + "github.com/scaleway/scaleway-cli/v2/core" + "github.com/scaleway/scaleway-cli/v2/internal/editor" + "github.com/scaleway/scaleway-sdk-go/api/vpc/v2" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +var vpcACLEditYamlExample = `default_policy: drop +is_ipv6: false +rules: [ +- protocol: TCP + src_port_low: 0 + src_port_high: 0 + dst_port_low: 80 + dst_port_high: 80 + source: 0.0.0.0/0 + destination: 0.0.0.0/0 + description: Allow HTTP traffic from any source + action: accept +- protocol: TCP + src_port_low: 0 + src_port_high: 0 + dst_port_low: 443 + dst_port_high: 443 + source: 0.0.0.0/0 + destination: 0.0.0.0/0 + description: Allow HTTPS traffic from any source + action: accept +] +` + +type vpcACLEditArgs struct { + Region scw.Region + VpcID string + IsIPv6 bool + DefaultPolicy vpc.Action + Mode editor.MarshalMode +} + +func vpcACLEditCommand() *core.Command { + return &core.Command{ + Short: "Edit all ACL rules of a VPC", + Long: editor.LongDescription, + Namespace: "vpc", + Resource: "rule", + Verb: "edit", + ArgsType: reflect.TypeOf(vpcACLEditArgs{}), + ArgSpecs: core.ArgSpecs{ + { + Name: "vpc-id", + Short: "ID of the Network ACL's VPC", + Required: true, + Positional: false, + }, + { + Name: "is-ipv6", + Short: "Defines whether this set of ACL rules is for IPv6 (false = IPv4). Each Network ACL can have rules for only one IP type", + Required: false, + Positional: false, + }, + { + Name: "default-policy", + Short: "Action to take for packets which do not match any rules", + Required: false, + Positional: false, + }, + editor.MarshalModeArgSpec(), + core.RegionArgSpec(), + }, + Run: func(ctx context.Context, argsI interface{}) (i interface{}, e error) { + args := argsI.(*vpcACLEditArgs) + + client := core.ExtractClient(ctx) + api := vpc.NewAPI(client) + + setRequest := &vpc.SetACLRequest{ + Region: args.Region, + VpcID: args.VpcID, + IsIPv6: args.IsIPv6, + DefaultPolicy: args.DefaultPolicy, + } + + rules, err := api.GetACL(&vpc.GetACLRequest{ + Region: args.Region, + VpcID: args.VpcID, + IsIPv6: args.IsIPv6, + }, scw.WithContext(ctx)) + if err != nil { + return nil, fmt.Errorf("failed to list ACL rules: %w", err) + } + + editedSetRequest, err := editor.UpdateResourceEditor(rules, setRequest, &editor.Config{ + PutRequest: true, + MarshalMode: args.Mode, + Template: vpcACLEditYamlExample, + }) + if err != nil { + return nil, err + } + + setRequest = editedSetRequest.(*vpc.SetACLRequest) + + resp, err := api.SetACL(setRequest, scw.WithContext(ctx)) + if err != nil { + return nil, fmt.Errorf("failed to set ACL rules: %w", err) + } + + return resp.Rules, nil + }, + } +}