diff --git a/cmd/scw/testdata/test-all-usage-edge-services-route-rules-edit-usage.golden b/cmd/scw/testdata/test-all-usage-edge-services-route-rules-edit-usage.golden new file mode 100644 index 0000000000..8619e2258f --- /dev/null +++ b/cmd/scw/testdata/test-all-usage-edge-services-route-rules-edit-usage.golden @@ -0,0 +1,24 @@ +🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲 +πŸŸ₯πŸŸ₯πŸŸ₯ STDERR️️ πŸŸ₯πŸŸ₯πŸŸ₯️ +Edit all route rules of a route stage. + +If backend-stage-id is provided, the editor will only show rule_http_match fields and the specified backend will be applied to all rules automatically. +Otherwise, opens the editor with full rules including backend_stage_id for each rule. + +USAGE: + scw edge-services route-rules edit [arg=value ...] + +ARGS: + route-stage-id ID of the route stage to edit + [backend-stage-id] ID of the backend stage to apply to all rules (simplifies editing when using a single backend) + [mode=yaml] marshaling used when editing data (yaml | json) + +FLAGS: + -h, --help help for edit + --list-sub-commands List all subcommands + +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-edge-services-route-rules-usage.cassette.yaml b/cmd/scw/testdata/test-all-usage-edge-services-route-rules-usage.cassette.yaml new file mode 100644 index 0000000000..e06356e01d --- /dev/null +++ b/cmd/scw/testdata/test-all-usage-edge-services-route-rules-usage.cassette.yaml @@ -0,0 +1,44 @@ +--- +version: 1 +interactions: +- request: + body: '{"access_key":"SCW7DJXHG04SGMT342RA", "secret_key":null, "description":"Generated + by the Scaleway CLI", "created_at":"2025-06-27T14:00:16.810892Z", "updated_at":"2025-06-27T14:00:16.810892Z", + "expires_at":null, "default_project_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5", + "editable":true, "deletable":true, "managed":false, "creation_ip":"130.180.219.188", + "user_id":"4fcf3b56-3e90-4461-8198-dcf85c2699d6"}' + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.35.0.20251209111654-44f4a1dfc432 (go1.25.3; darwin; + arm64) cli-e2e-test + url: https://api.scaleway.com/iam/v1alpha1/api-keys/SCW7DJXHG04SGMT342RA + method: GET + response: + body: '{"access_key":"SCW7DJXHG04SGMT342RA", "secret_key":null, "description":"Generated + by the Scaleway CLI", "created_at":"2025-06-27T14:00:16.810892Z", "updated_at":"2025-06-27T14:00:16.810892Z", + "expires_at":null, "default_project_id":"564aa517-68b0-4fd7-8c8c-d21c4bcdcbd5", + "editable":true, "deletable":true, "managed":false, "creation_ip":"130.180.219.188", + "user_id":"4fcf3b56-3e90-4461-8198-dcf85c2699d6"}' + headers: + Content-Length: + - "406" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 16 Dec 2025 08:05:40 GMT + Server: + - Scaleway API Gateway (fr-par-2;edge01) + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - ba6fd727-2477-4988-adf1-6e57f8878a8c + status: 200 OK + code: 200 + duration: "" diff --git a/cmd/scw/testdata/test-all-usage-edge-services-route-rules-usage.golden b/cmd/scw/testdata/test-all-usage-edge-services-route-rules-usage.golden index 3dbf3887a5..46c6f31daa 100644 --- a/cmd/scw/testdata/test-all-usage-edge-services-route-rules-usage.golden +++ b/cmd/scw/testdata/test-all-usage-edge-services-route-rules-usage.golden @@ -7,6 +7,7 @@ USAGE: AVAILABLE COMMANDS: add Add route rules + edit Edit all route rules of a route stage list List route rules set Set route rules diff --git a/docs/commands/edge-services.md b/docs/commands/edge-services.md index 9905e63f7f..37aecbab90 100644 --- a/docs/commands/edge-services.md +++ b/docs/commands/edge-services.md @@ -34,6 +34,7 @@ Edge Services API - [List purge requests](#list-purge-requests) - [Route-rules management commands](#route-rules-management-commands) - [Add route rules](#add-route-rules) + - [Edit all route rules of a route stage](#edit-all-route-rules-of-a-route-stage) - [List route rules](#list-route-rules) - [Set route rules](#set-route-rules) - [Route-stage management commands](#route-stage-management-commands) @@ -647,6 +648,30 @@ scw edge-services route-rules add [arg=value ...] +### Edit all route rules of a route stage + +Edit all route rules of a route stage. + +If backend-stage-id is provided, the editor will only show rule_http_match fields and the specified backend will be applied to all rules automatically. +Otherwise, opens the editor with full rules including backend_stage_id for each rule. + +**Usage:** + +``` +scw edge-services route-rules edit [arg=value ...] +``` + + +**Args:** + +| Name | | Description | +|------|---|-------------| +| route-stage-id | Required | ID of the route stage to edit | +| backend-stage-id | | ID of the backend stage to apply to all rules (simplifies editing when using a single backend) | +| mode | Default: `yaml`
One of: `yaml`, `json` | marshaling used when editing data | + + + ### List route rules List all route rules of an existing route stage, specified by its `route_stage_id`. diff --git a/internal/namespaces/edge_services/v1beta1/custom.go b/internal/namespaces/edge_services/v1beta1/custom.go index 9fa7f7d58c..38287409f4 100644 --- a/internal/namespaces/edge_services/v1beta1/custom.go +++ b/internal/namespaces/edge_services/v1beta1/custom.go @@ -7,5 +7,9 @@ func GetCommands() *core.Commands { cmds.MustFind("edge-services").Groups = []string{"network"} + cmds.Merge(core.NewCommands( + edgeServicesRouteRulesEditCommand(), + )) + return cmds } diff --git a/internal/namespaces/edge_services/v1beta1/custom_route_rules.go b/internal/namespaces/edge_services/v1beta1/custom_route_rules.go new file mode 100644 index 0000000000..5ef3426077 --- /dev/null +++ b/internal/namespaces/edge_services/v1beta1/custom_route_rules.go @@ -0,0 +1,129 @@ +package edge_services + +import ( + "context" + "fmt" + "reflect" + + "github.com/scaleway/scaleway-cli/v2/core" + "github.com/scaleway/scaleway-cli/v2/internal/editor" + edgeservices "github.com/scaleway/scaleway-sdk-go/api/edge_services/v1beta1" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +var edgeServicesRouteRulesEditYamlExample = `route_rules: +- rule_http_match: + method_filters: + - get + - post + path_filter: + path_filter_type: regex + value: ^/api/.* + backend_stage_id: 11111111-1111-1111-1111-111111111111 +- rule_http_match: + method_filters: + - get + path_filter: + path_filter_type: regex + value: ^/static/.* + backend_stage_id: 11111111-1111-1111-1111-111111111111 +` + +var edgeServicesRouteRulesEditYamlExampleSimple = `route_rules: +- rule_http_match: + method_filters: + - get + - post + path_filter: + path_filter_type: regex + value: ^/api/.* +- rule_http_match: + method_filters: + - get + path_filter: + path_filter_type: regex + value: ^/static/.* +` + +type edgeServicesRouteRulesEditArgs struct { + RouteStageID string + BackendStageID *string + Mode editor.MarshalMode +} + +func edgeServicesRouteRulesEditCommand() *core.Command { + return &core.Command{ + Short: "Edit all route rules of a route stage", + Long: `Edit all route rules of a route stage. + +If backend-stage-id is provided, the editor will only show rule_http_match fields and the specified backend will be applied to all rules automatically. +Otherwise, opens the editor with full rules including backend_stage_id for each rule.`, + Namespace: "edge-services", + Resource: "route-rules", + Verb: "edit", + ArgsType: reflect.TypeOf(edgeServicesRouteRulesEditArgs{}), + ArgSpecs: core.ArgSpecs{ + { + Name: "route-stage-id", + Short: "ID of the route stage to edit", + Required: true, + Positional: true, + }, + { + Name: "backend-stage-id", + Short: "ID of the backend stage to apply to all rules (simplifies editing when using a single backend)", + Required: false, + }, + editor.MarshalModeArgSpec(), + }, + Run: func(ctx context.Context, argsI any) (i any, e error) { + args := argsI.(*edgeServicesRouteRulesEditArgs) + + client := core.ExtractClient(ctx) + api := edgeservices.NewAPI(client) + + rules, err := api.ListRouteRules(&edgeservices.ListRouteRulesRequest{ + RouteStageID: args.RouteStageID, + }, scw.WithContext(ctx)) + if err != nil { + return nil, fmt.Errorf("failed to list route rules: %w", err) + } + + setRequest := &edgeservices.SetRouteRulesRequest{ + RouteStageID: args.RouteStageID, + } + + template := edgeServicesRouteRulesEditYamlExample + var ignoreFields []string + if args.BackendStageID != nil { + template = edgeServicesRouteRulesEditYamlExampleSimple + ignoreFields = []string{"backend_stage_id"} + } + + editedSetRequest, err := editor.UpdateResourceEditor(rules, setRequest, &editor.Config{ + PutRequest: true, + MarshalMode: args.Mode, + Template: template, + IgnoreFields: ignoreFields, + }) + if err != nil { + return nil, err + } + + setRequest = editedSetRequest.(*edgeservices.SetRouteRulesRequest) + + if args.BackendStageID != nil { + for _, rule := range setRequest.RouteRules { + rule.BackendStageID = args.BackendStageID + } + } + + resp, err := api.SetRouteRules(setRequest, scw.WithContext(ctx)) + if err != nil { + return nil, fmt.Errorf("failed to set route rules: %w", err) + } + + return resp.RouteRules, nil + }, + } +}