diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index 58ec820f01..30b990ae90 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -820,8 +820,10 @@ type CustomRedirect struct { // Path defines parameters used to modify the path of the incoming request. // The modified path is then used to construct the `Location` header. When // empty, the request path is used as-is. + // Only ReplaceFullPath path modifier is supported currently. // // +optional + // +kubebuilder:validation:XValidation:message="only ReplaceFullPath is supported for path.type",rule="self.type == 'ReplaceFullPath'" Path *gwapiv1.HTTPPathModifier `json:"path,omitempty"` // Port is the port to be used in the value of the `Location` diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 8585739337..2b13f9a477 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -1342,6 +1342,7 @@ spec: Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. + Only ReplaceFullPath path modifier is supported currently. properties: replaceFullPath: description: |- @@ -1388,6 +1389,8 @@ spec: - type type: object x-kubernetes-validations: + - message: only ReplaceFullPath is supported for path.type + rule: self.type == 'ReplaceFullPath' - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 4b22f61bac..de6f4c9a67 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -1341,6 +1341,7 @@ spec: Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. + Only ReplaceFullPath path modifier is supported currently. properties: replaceFullPath: description: |- @@ -1387,6 +1388,8 @@ spec: - type type: object x-kubernetes-validations: + - message: only ReplaceFullPath is supported for path.type + rule: self.type == 'ReplaceFullPath' - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index e0f35b263b..b1b2f5abc3 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -1051,25 +1051,52 @@ func buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, resources *resou } } - response := ir.CustomResponse{ - ContentType: ro.Response.ContentType, - } + if ro.Redirect != nil { + redirect := &ir.Redirect{ + Scheme: ro.Redirect.Scheme, + } + if ro.Redirect.Path != nil { + redirect.Path = &ir.HTTPPathModifier{ + FullReplace: ro.Redirect.Path.ReplaceFullPath, + PrefixMatchReplace: ro.Redirect.Path.ReplacePrefixMatch, + } + } + if ro.Redirect.Hostname != nil { + redirect.Hostname = ptr.To(string(*ro.Redirect.Hostname)) + } + if ro.Redirect.Port != nil { + redirect.Port = ptr.To(uint32(*ro.Redirect.Port)) + } + if ro.Redirect.StatusCode != nil { + redirect.StatusCode = ptr.To(int32(*ro.Redirect.StatusCode)) + } - if ro.Response.StatusCode != nil { - response.StatusCode = ptr.To(uint32(*ro.Response.StatusCode)) - } + rules = append(rules, ir.ResponseOverrideRule{ + Name: defaultResponseOverrideRuleName(policy, index), + Match: match, + Redirect: redirect, + }) + } else { + response := &ir.CustomResponse{ + ContentType: ro.Response.ContentType, + } - var err error - response.Body, err = getCustomResponseBody(ro.Response.Body, resources, policy.Namespace) - if err != nil { - return nil, err - } + if ro.Response.StatusCode != nil { + response.StatusCode = ptr.To(uint32(*ro.Response.StatusCode)) + } - rules = append(rules, ir.ResponseOverrideRule{ - Name: defaultResponseOverrideRuleName(policy, index), - Match: match, - Response: response, - }) + var err error + response.Body, err = getCustomResponseBody(ro.Response.Body, resources, policy.Namespace) + if err != nil { + return nil, err + } + + rules = append(rules, ir.ResponseOverrideRule{ + Name: defaultResponseOverrideRuleName(policy, index), + Match: match, + Response: response, + }) + } } return &ir.ResponseOverride{ Name: irConfigName(policy), diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.in.yaml index 2337e7cc1b..94a4e1a475 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.in.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.in.yaml @@ -81,6 +81,25 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-3 + spec: + hostnames: + - bar.envoyproxy.io + parentRefs: + - namespace: default + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 configMaps: - apiVersion: v1 kind: ConfigMap @@ -178,3 +197,25 @@ backendTrafficPolicies: - value: 403 response: statusCode: 401 + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route-3 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-3 + responseOverride: + - match: + statusCodes: + - value: 200 + redirect: + scheme: https + hostname: www.redirect.com + port: 8443 + statusCode: 301 + path: + type: ReplaceFullPath + replaceFullPath: /redirect/path diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml index 334e980e75..b60b4342be 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml @@ -84,6 +84,45 @@ backendTrafficPolicies: status: "True" type: Accepted controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route-3 + namespace: default + spec: + responseOverride: + - match: + statusCodes: + - type: null + value: 200 + redirect: + hostname: www.redirect.com + path: + replaceFullPath: /redirect/path + type: ReplaceFullPath + port: 8443 + scheme: https + statusCode: 301 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-3 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: default + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy metadata: @@ -193,7 +232,7 @@ gateways: protocol: HTTP status: listeners: - - attachedRoutes: 2 + - attachedRoutes: 3 conditions: - lastTransitionTime: null message: Sending translated listener configuration to the data plane @@ -327,6 +366,44 @@ httpRoutes: name: gateway-2 namespace: default sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-3 + namespace: default + spec: + hostnames: + - bar.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: default + sectionName: http infraIR: default/gateway-1: proxy: @@ -553,6 +630,51 @@ xdsIR: name: backendtrafficpolicy/default/policy-for-route-2/responseoverride/rule/0 response: statusCode: 401 + - destination: + metadata: + kind: HTTPRoute + name: httproute-3 + namespace: default + name: httproute/default/httproute-3/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-3/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: bar.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-3 + namespace: default + name: httproute/default/httproute-3/rule/0/match/0/bar_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + traffic: + responseOverride: + name: backendtrafficpolicy/default/policy-for-route-3 + rules: + - match: + statusCodes: + - value: 200 + name: backendtrafficpolicy/default/policy-for-route-3/responseoverride/rule/0 + redirect: + hostname: www.redirect.com + path: + fullReplace: /redirect/path + prefixMatchReplace: null + port: 8443 + scheme: https + statusCode: 301 readyListener: address: 0.0.0.0 ipFamily: IPv4 diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml index 3066da5154..92f7dfaf36 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-full-path-replace-https.out.yaml @@ -137,7 +137,6 @@ xdsIR: name: "" prefix: / redirect: - hostname: null path: fullReplace: /redirected prefixMatchReplace: null diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.out.yaml index f34ca6b658..805aa7d1cc 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-hostname.out.yaml @@ -136,7 +136,6 @@ xdsIR: prefix: / redirect: hostname: redirected.com - path: null port: 443 scheme: https statusCode: 301 diff --git a/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml b/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml index 29dc762bb0..046f1f2ee6 100644 --- a/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml +++ b/internal/gatewayapi/testdata/httproute-with-redirect-filter-prefix-replace-with-port-http.out.yaml @@ -138,7 +138,6 @@ xdsIR: name: "" prefix: / redirect: - hostname: null path: fullReplace: null prefixMatchReplace: /redirected diff --git a/internal/ir/xds.go b/internal/ir/xds.go index c17167e41e..cc2c0c19ac 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -591,7 +591,9 @@ type ResponseOverrideRule struct { // Match configuration. Match CustomResponseMatch `json:"match"` // Response configuration. - Response CustomResponse `json:"response"` + Response *CustomResponse `json:"response,omitempty"` + // Redirect configuration + Redirect *Redirect `json:"redirect,omitempty"` } // CustomResponseMatch defines the configuration for matching a user response to return a custom one. @@ -1741,15 +1743,15 @@ func (r URLRewrite) Validate() error { // +k8s:deepcopy-gen=true type Redirect struct { // Scheme configures the replacement of the request's scheme. - Scheme *string `json:"scheme" yaml:"scheme"` + Scheme *string `json:"scheme,omitempty" yaml:"scheme,omitempty"` // Hostname configures the replacement of the request's hostname. - Hostname *string `json:"hostname" yaml:"hostname"` + Hostname *string `json:"hostname,omitempty" yaml:"hostname,omitempty"` // Path contains config for rewriting the path of the request. - Path *HTTPPathModifier `json:"path" yaml:"path"` + Path *HTTPPathModifier `json:"path,omitempty" yaml:"path,omitempty"` // Port configures the replacement of the request's port. - Port *uint32 `json:"port" yaml:"port"` + Port *uint32 `json:"port,omitempty" yaml:"port,omitempty"` // Status code configures the redirection response's status code. - StatusCode *int32 `json:"statusCode" yaml:"statusCode"` + StatusCode *int32 `json:"statusCode,omitempty" yaml:"statusCode,omitempty"` } // Validate the fields within the Redirect structure diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 118f64937a..9e9db40280 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -2974,7 +2974,16 @@ func (in *ResponseOverride) DeepCopy() *ResponseOverride { func (in *ResponseOverrideRule) DeepCopyInto(out *ResponseOverrideRule) { *out = *in in.Match.DeepCopyInto(&out.Match) - in.Response.DeepCopyInto(&out.Response) + if in.Response != nil { + in, out := &in.Response, &out.Response + *out = new(CustomResponse) + (*in).DeepCopyInto(*out) + } + if in.Redirect != nil { + in, out := &in.Redirect, &out.Redirect + *out = new(Redirect) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseOverrideRule. diff --git a/internal/xds/translator/custom_response.go b/internal/xds/translator/custom_response.go index 14645ec82f..e8192782ab 100644 --- a/internal/xds/translator/custom_response.go +++ b/internal/xds/translator/custom_response.go @@ -18,6 +18,7 @@ import ( respv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/custom_response/v3" hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" policyv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/http/custom_response/local_response_policy/v3" + redirectpolicyv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/http/custom_response/redirect_policy/v3" envoymatcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "google.golang.org/protobuf/types/known/anypb" @@ -57,9 +58,9 @@ func (c *customResponse) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener * continue } - // Only generates one CustomResponse Envoy filter for each unique name. + // Only generates one CustomResponse/CustomRedirect Envoy filter for each unique name. // For example, if there are two routes under the same gateway with the - // same CustomResponse config, only one CustomResponse filter will be generated. + // same CustomResponse/CustomRedirect config, only one CustomResponse/CustomRedirect filter will be generated. if hcmContainsFilter(mgr, c.customResponseFilterName(route.Traffic.ResponseOverride)) { continue } @@ -368,6 +369,57 @@ func (c *customResponse) buildStatusCodeCELMatcher(codeRange ir.StatusCodeRange) } func (c *customResponse) buildAction(r ir.ResponseOverrideRule) (*matcherv3.Matcher_OnMatch_Action, error) { + var ( + pb *anypb.Any + err error + ) + + if r.Redirect != nil { + pb, err = c.buildRedirectAction(r) + } else { + pb, err = c.buildResponseAction(r) + } + + if err != nil { + return nil, err + } + return &matcherv3.Matcher_OnMatch_Action{ + Action: &cncfv3.TypedExtensionConfig{ + Name: r.Name, + TypedConfig: pb, + }, + }, nil +} + +func (c *customResponse) buildRedirectAction(r ir.ResponseOverrideRule) (*anypb.Any, error) { + redirectAction := &routev3.RedirectAction{} + if r.Redirect.Scheme != nil { + redirectAction.SchemeRewriteSpecifier = &routev3.RedirectAction_SchemeRedirect{ + SchemeRedirect: *r.Redirect.Scheme, + } + } + if r.Redirect.Hostname != nil { + redirectAction.HostRedirect = *r.Redirect.Hostname + } + if r.Redirect.Port != nil { + redirectAction.PortRedirect = *r.Redirect.Port + } + if r.Redirect.Path != nil && r.Redirect.Path.FullReplace != nil { + redirectAction.PathRewriteSpecifier = &routev3.RedirectAction_PathRedirect{ + PathRedirect: *r.Redirect.Path.FullReplace, + } + } + redirect := &redirectpolicyv3.RedirectPolicy{ + RedirectActionSpecifier: &redirectpolicyv3.RedirectPolicy_RedirectAction{ + RedirectAction: redirectAction, + }, + StatusCode: wrapperspb.UInt32(uint32(*r.Redirect.StatusCode)), + } + + return proto.ToAnyWithValidation(redirect) +} + +func (c *customResponse) buildResponseAction(r ir.ResponseOverrideRule) (*anypb.Any, error) { response := &policyv3.LocalResponsePolicy{} if r.Response.Body != nil && *r.Response.Body != "" { response.Body = &corev3.DataSource{ @@ -391,21 +443,7 @@ func (c *customResponse) buildAction(r ir.ResponseOverrideRule) (*matcherv3.Matc response.StatusCode = &wrapperspb.UInt32Value{Value: *r.Response.StatusCode} } - var ( - pb *anypb.Any - err error - ) - - if pb, err = proto.ToAnyWithValidation(response); err != nil { - return nil, err - } - - return &matcherv3.Matcher_OnMatch_Action{ - Action: &cncfv3.TypedExtensionConfig{ - Name: r.Name, - TypedConfig: pb, - }, - }, nil + return proto.ToAnyWithValidation(response) } // routeContainsResponseOverride returns true if ResponseOverride exists for the provided route. diff --git a/internal/xds/translator/testdata/in/xds-ir/custom-response.yaml b/internal/xds/translator/testdata/in/xds-ir/custom-response.yaml index 011e973970..66a8bb601e 100644 --- a/internal/xds/translator/testdata/in/xds-ir/custom-response.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/custom-response.yaml @@ -55,6 +55,17 @@ http: "error": "Internal Server Error" } contentType: application/json + - match: + statusCodes: + - value: 401 + name: backendtrafficpolicy/default/policy-for-gateway/responseoverride/rule/2 + redirect: + scheme: https + hostname: www.redirect.com + port: 8443 + statusCode: 301 + path: + fullReplace: /redirect/path - destination: name: httproute/default/httproute-2/rule/0 settings: diff --git a/internal/xds/translator/testdata/out/xds-ir/custom-response.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/custom-response.listeners.yaml index 455f453eda..c6f76649f7 100644 --- a/internal/xds/translator/testdata/out/xds-ir/custom-response.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/custom-response.listeners.yaml @@ -110,6 +110,25 @@ name: http-attributes-cel-match-input typedConfig: '@type': type.googleapis.com/xds.type.matcher.v3.HttpAttributesCelMatchInput + - onMatch: + action: + name: backendtrafficpolicy/default/policy-for-gateway/responseoverride/rule/2 + typedConfig: + '@type': type.googleapis.com/envoy.extensions.http.custom_response.redirect_policy.v3.RedirectPolicy + redirectAction: + hostRedirect: www.redirect.com + pathRedirect: /redirect/path + portRedirect: 8443 + schemeRedirect: https + statusCode: 301 + predicate: + singlePredicate: + input: + name: http-response-status-code-match-input + typedConfig: + '@type': type.googleapis.com/envoy.type.matcher.v3.HttpResponseStatusCodeMatchInput + valueMatch: + exact: "401" - disabled: true name: envoy.filters.http.custom_response/backendtrafficpolicy/default/policy-for-route typedConfig: diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index cf672ebfcc..8c91ecc4f8 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -958,7 +958,7 @@ _Appears in:_ | --- | --- | --- | --- | --- | | `scheme` | _string_ | false | | Scheme is the scheme to be used in the value of the `Location` header in
the response. When empty, the scheme of the request is used. | | `hostname` | _[PreciseHostname](#precisehostname)_ | false | | Hostname is the hostname to be used in the value of the `Location`
header in the response.
When empty, the hostname in the `Host` header of the request is used. | -| `path` | _[HTTPPathModifier](#httppathmodifier)_ | false | | Path defines parameters used to modify the path of the incoming request.
The modified path is then used to construct the `Location` header. When
empty, the request path is used as-is. | +| `path` | _[HTTPPathModifier](#httppathmodifier)_ | false | | Path defines parameters used to modify the path of the incoming request.
The modified path is then used to construct the `Location` header. When
empty, the request path is used as-is.
Only ReplaceFullPath path modifier is supported currently. | | `port` | _[PortNumber](#portnumber)_ | false | | Port is the port to be used in the value of the `Location`
header in the response.
If redirect scheme is not-empty, the well-known port associated with the redirect scheme will be used.
Specifically "http" to port 80 and "https" to port 443. If the redirect scheme does not have a
well-known port or redirect scheme is empty, the listener port of the Gateway will be used.
Port will not be added in the 'Location' header if scheme is HTTP and port is 80
or scheme is HTTPS and port is 443. | | `statusCode` | _integer_ | false | 302 | StatusCode is the HTTP status code to be used in response. | diff --git a/test/cel-validation/backendtrafficpolicy_test.go b/test/cel-validation/backendtrafficpolicy_test.go index 825e024af2..9cc9f27afb 100644 --- a/test/cel-validation/backendtrafficpolicy_test.go +++ b/test/cel-validation/backendtrafficpolicy_test.go @@ -1374,6 +1374,50 @@ func TestBackendTrafficPolicyTarget(t *testing.T) { }, wantErrors: []string{}, }, + { + desc: "custom redirect in response override", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ResponseOverride: []*egv1a1.ResponseOverride{ + { + Match: egv1a1.CustomResponseMatch{ + StatusCodes: []egv1a1.StatusCodeMatch{ + { + Type: ptr.To(egv1a1.StatusCodeValueTypeRange), + Range: &egv1a1.StatusCodeRange{ + Start: 100, + End: 200, + }, + }, + }, + }, + Redirect: &egv1a1.CustomRedirect{ + Scheme: ptr.To("https"), + Hostname: ptr.To(gwapiv1a2.PreciseHostname("redirect.host")), + Path: &gwapiv1.HTTPPathModifier{ + Type: "ReplacePrefixMatch", + ReplacePrefixMatch: ptr.To("/redirect"), + }, + Port: ptr.To(gwapiv1a2.PortNumber(9090)), + StatusCode: ptr.To(302), + }, + }, + }, + } + }, + wantErrors: []string{ + "spec.responseOverride[0].redirect.path: Invalid value: \"object\": only ReplaceFullPath is supported for path.type", + }, + }, { desc: "status value required for type in response override", mutate: func(btp *egv1a1.BackendTrafficPolicy) { diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index 53aa8ecc72..dd85fd9c88 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -18981,6 +18981,7 @@ spec: Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. + Only ReplaceFullPath path modifier is supported currently. properties: replaceFullPath: description: |- @@ -19027,6 +19028,8 @@ spec: - type type: object x-kubernetes-validations: + - message: only ReplaceFullPath is supported for path.type + rule: self.type == 'ReplaceFullPath' - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index 1a9e61ad89..9a78984c13 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -1669,6 +1669,7 @@ spec: Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. + Only ReplaceFullPath path modifier is supported currently. properties: replaceFullPath: description: |- @@ -1715,6 +1716,8 @@ spec: - type type: object x-kubernetes-validations: + - message: only ReplaceFullPath is supported for path.type + rule: self.type == 'ReplaceFullPath' - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath)