From 6eefb28871160c166afe4d9db3ff1b11bea35c59 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Wed, 25 Sep 2024 15:13:31 -0700 Subject: [PATCH] api: custom error response (#4259) * api: custom error response Relates to https://github.com/envoyproxy/gateway/issues/1400 Signed-off-by: Arko Dasgupta * address comments Signed-off-by: Arko Dasgupta --------- Signed-off-by: Arko Dasgupta --- api/v1alpha1/backendtrafficpolicy_types.go | 7 + api/v1alpha1/shared_types.go | 79 ++++++++++ api/v1alpha1/zz_generated.deepcopy.go | 146 ++++++++++++++++++ ....envoyproxy.io_backendtrafficpolicies.yaml | 114 ++++++++++++++ site/content/en/latest/api/extension_types.md | 113 ++++++++++++++ site/content/zh/latest/api/extension_types.md | 113 ++++++++++++++ 6 files changed, 572 insertions(+) diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go index 70a400015e9..4e6118e7035 100644 --- a/api/v1alpha1/backendtrafficpolicy_types.go +++ b/api/v1alpha1/backendtrafficpolicy_types.go @@ -69,6 +69,13 @@ type BackendTrafficPolicySpec struct { // +optional // +notImplementedHide Compression []*Compression `json:"compression,omitempty"` + + // ResponseOverride defines the configuration to override specific responses with a custom one. + // If multiple configurations are specified, the first one to match wins. + // + // +optional + // +notImplementedHide + ResponseOverride []*ResponseOverride `json:"responseOverride,omitempty"` } // +kubebuilder:object:root=true diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index 56440f096ca..48e3471c77d 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -609,3 +609,82 @@ type HTTP2Settings struct { // +optional OnInvalidMessage *InvalidMessageAction `json:"onInvalidMessage,omitempty"` } + +// ResponseOverride defines the configuration to override specific responses with a custom one. +type ResponseOverride struct { + // Match configuration. + Match CustomResponseMatch `json:"match"` + // Response configuration. + Response CustomResponse `json:"response"` +} + +// CustomResponseMatch defines the configuration for matching a user response to return a custom one. +type CustomResponseMatch struct { + // Status code to match on. The match evaluates to true if any of the matches are successful. + StatusCode []StatusCodeMatch `json:"statusCode"` +} + +// StatusCodeValueType defines the types of values for the status code match supported by Envoy Gateway. +// +kubebuilder:validation:Enum=Value;Range +type StatusCodeValueType string + +type StatusCodeMatch struct { + // Type is the type of value. + // + // +kubebuilder:default=Value + // +unionDiscriminator + Type *StatusCodeValueType `json:"type"` + + // Value contains the value of the status code. + // + // +optional + Value *string `json:"value,omitempty"` + // ValueRef contains the contents of the body + // specified as a local object reference. + // Only a reference to ConfigMap is supported. + // + // +optional + Range *StatusCodeRange `json:"range,omitempty"` +} + +// StatusCodeRange defines the configuration for define a range of status codes. +type StatusCodeRange struct { + // Start of the range, including the start value. + Start int `json:"start"` + // End of the range, including the end value. + End int `json:"end"` +} + +// CustomResponse defines the configuration for returning a custom response. +type CustomResponse struct { + // Content Type of the response. This will be set in the Content-Type header. + // + // +optional + ContentType *string `json:"contentType,omitempty"` + + // Body of the Custom Response + Body CustomResponseBody `json:"body"` +} + +// ResponseValueType defines the types of values for the response body supported by Envoy Gateway. +// +kubebuilder:validation:Enum=Inline;ValueRef +type ResponseValueType string + +// CustomResponseBody +type CustomResponseBody struct { + // Type is the type of method to use to read the body value. + // + // +unionDiscriminator + Type *ResponseValueType `json:"type"` + + // Inline contains the value as an inline string. + // + // +optional + Inline *string `json:"inline,omitempty"` + // ValueRef contains the contents of the body + // specified as a local object reference. + // Only a reference to ConfigMap is supported. + // + // +optional + ValueRef *gwapiv1.LocalObjectReference `json:"valueRef,omitempty"` +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 31128681193..b07978e609d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -556,6 +556,17 @@ func (in *BackendTrafficPolicySpec) DeepCopyInto(out *BackendTrafficPolicySpec) } } } + if in.ResponseOverride != nil { + in, out := &in.ResponseOverride, &out.ResponseOverride + *out = make([]*ResponseOverride, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(ResponseOverride) + (*in).DeepCopyInto(*out) + } + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendTrafficPolicySpec. @@ -1124,6 +1135,79 @@ func (in *CustomHeaderExtensionSettings) DeepCopy() *CustomHeaderExtensionSettin return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomResponse) DeepCopyInto(out *CustomResponse) { + *out = *in + if in.ContentType != nil { + in, out := &in.ContentType, &out.ContentType + *out = new(string) + **out = **in + } + in.Body.DeepCopyInto(&out.Body) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResponse. +func (in *CustomResponse) DeepCopy() *CustomResponse { + if in == nil { + return nil + } + out := new(CustomResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomResponseBody) DeepCopyInto(out *CustomResponseBody) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(ResponseValueType) + **out = **in + } + if in.Inline != nil { + in, out := &in.Inline, &out.Inline + *out = new(string) + **out = **in + } + if in.ValueRef != nil { + in, out := &in.ValueRef, &out.ValueRef + *out = new(apisv1.LocalObjectReference) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResponseBody. +func (in *CustomResponseBody) DeepCopy() *CustomResponseBody { + if in == nil { + return nil + } + out := new(CustomResponseBody) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomResponseMatch) DeepCopyInto(out *CustomResponseMatch) { + *out = *in + if in.StatusCode != nil { + in, out := &in.StatusCode, &out.StatusCode + *out = make([]StatusCodeMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResponseMatch. +func (in *CustomResponseMatch) DeepCopy() *CustomResponseMatch { + if in == nil { + return nil + } + out := new(CustomResponseMatch) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CustomTag) DeepCopyInto(out *CustomTag) { *out = *in @@ -4639,6 +4723,23 @@ func (in *RequestHeaderCustomTag) DeepCopy() *RequestHeaderCustomTag { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResponseOverride) DeepCopyInto(out *ResponseOverride) { + *out = *in + in.Match.DeepCopyInto(&out.Match) + in.Response.DeepCopyInto(&out.Response) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseOverride. +func (in *ResponseOverride) DeepCopy() *ResponseOverride { + if in == nil { + return nil + } + out := new(ResponseOverride) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Retry) DeepCopyInto(out *Retry) { *out = *in @@ -4889,6 +4990,51 @@ func (in *SourceMatch) DeepCopy() *SourceMatch { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StatusCodeMatch) DeepCopyInto(out *StatusCodeMatch) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(StatusCodeValueType) + **out = **in + } + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(string) + **out = **in + } + if in.Range != nil { + in, out := &in.Range, &out.Range + *out = new(StatusCodeRange) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatusCodeMatch. +func (in *StatusCodeMatch) DeepCopy() *StatusCodeMatch { + if in == nil { + return nil + } + out := new(StatusCodeMatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StatusCodeRange) DeepCopyInto(out *StatusCodeRange) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatusCodeRange. +func (in *StatusCodeRange) DeepCopy() *StatusCodeRange { + if in == nil { + return nil + } + out := new(StatusCodeRange) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StringMatch) DeepCopyInto(out *StringMatch) { *out = *in 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 77133a803fc..7b13e2a123f 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -945,6 +945,120 @@ spec: required: - type type: object + responseOverride: + description: |- + ResponseOverride defines the configuration to override specific responses with a custom one. + If multiple configurations are specified, the first one to match wins. + items: + description: ResponseOverride defines the configuration to override + specific responses with a custom one. + properties: + match: + description: Match configuration. + properties: + statusCode: + description: Status code to match on. The match evaluates + to true if any of the matches are successful. + items: + properties: + range: + description: |- + ValueRef contains the contents of the body + specified as a local object reference. + Only a reference to ConfigMap is supported. + properties: + end: + description: End of the range, including the end + value. + type: integer + start: + description: Start of the range, including the + start value. + type: integer + required: + - end + - start + type: object + type: + default: Value + description: Type is the type of value. + enum: + - Value + - Range + type: string + value: + description: Value contains the value of the status + code. + type: string + required: + - type + type: object + type: array + required: + - statusCode + type: object + response: + description: Response configuration. + properties: + body: + description: Body of the Custom Response + properties: + inline: + description: Inline contains the value as an inline + string. + type: string + type: + description: Type is the type of method to use to read + the body value. + enum: + - Inline + - ValueRef + type: string + valueRef: + description: |- + ValueRef contains the contents of the body + specified as a local object reference. + Only a reference to ConfigMap is supported. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + required: + - type + type: object + contentType: + description: Content Type of the response. This will be + set in the Content-Type header. + type: string + required: + - body + type: object + required: + - match + - response + type: object + type: array retry: description: |- Retry provides more advanced usage, allowing users to customize the number of retries, retry fallback strategy, and retry triggering conditions. diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 3bf53044b08..93d845f3df7 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -852,6 +852,51 @@ _Appears in:_ | `failClosed` | _boolean_ | false | FailClosed is a switch used to control the flow of traffic when client IP detection
fails. If set to true, the listener will respond with 403 Forbidden when the client
IP address cannot be determined. | +#### CustomResponse + + + +CustomResponse defines the configuration for returning a custom response. + +_Appears in:_ +- [ResponseOverride](#responseoverride) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentType` | _string_ | false | Content Type of the response. This will be set in the Content-Type header. | +| `body` | _[CustomResponseBody](#customresponsebody)_ | true | Body of the Custom Response | + + +#### CustomResponseBody + + + +CustomResponseBody + +_Appears in:_ +- [CustomResponse](#customresponse) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[ResponseValueType](#responsevaluetype)_ | true | Type is the type of method to use to read the body value. | +| `inline` | _string_ | false | Inline contains the value as an inline string. | +| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | + + +#### CustomResponseMatch + + + +CustomResponseMatch defines the configuration for matching a user response to return a custom one. + +_Appears in:_ +- [ResponseOverride](#responseoverride) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `statusCode` | _[StatusCodeMatch](#statuscodematch) array_ | true | Status code to match on. The match evaluates to true if any of the matches are successful. | + + #### CustomTag @@ -3450,6 +3495,32 @@ _Appears in:_ | `File` | ResourceProviderTypeFile defines the "File" provider.
| +#### ResponseOverride + + + +ResponseOverride defines the configuration to override specific responses with a custom one. + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `match` | _[CustomResponseMatch](#customresponsematch)_ | true | Match configuration. | +| `response` | _[CustomResponse](#customresponse)_ | true | Response configuration. | + + +#### ResponseValueType + +_Underlying type:_ _string_ + +ResponseValueType defines the types of values for the response body supported by Envoy Gateway. + +_Appears in:_ +- [CustomResponseBody](#customresponsebody) + + + #### RetryOn @@ -3632,6 +3703,48 @@ _Appears in:_ | `Distinct` | SourceMatchDistinct Each IP Address within the specified Source IP CIDR is treated as a distinct client selector
and uses a separate rate limit bucket/counter.
Note: This is only supported for Global Rate Limits.
| +#### StatusCodeMatch + + + + + +_Appears in:_ +- [CustomResponseMatch](#customresponsematch) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[StatusCodeValueType](#statuscodevaluetype)_ | true | Type is the type of value. | +| `value` | _string_ | false | Value contains the value of the status code. | +| `range` | _[StatusCodeRange](#statuscoderange)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | + + +#### StatusCodeRange + + + +StatusCodeRange defines the configuration for define a range of status codes. + +_Appears in:_ +- [StatusCodeMatch](#statuscodematch) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `start` | _integer_ | true | Start of the range, including the start value. | +| `end` | _integer_ | true | End of the range, including the end value. | + + +#### StatusCodeValueType + +_Underlying type:_ _string_ + +StatusCodeValueType defines the types of values for the status code match supported by Envoy Gateway. + +_Appears in:_ +- [StatusCodeMatch](#statuscodematch) + + + #### StringMatch diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 3bf53044b08..93d845f3df7 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -852,6 +852,51 @@ _Appears in:_ | `failClosed` | _boolean_ | false | FailClosed is a switch used to control the flow of traffic when client IP detection
fails. If set to true, the listener will respond with 403 Forbidden when the client
IP address cannot be determined. | +#### CustomResponse + + + +CustomResponse defines the configuration for returning a custom response. + +_Appears in:_ +- [ResponseOverride](#responseoverride) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentType` | _string_ | false | Content Type of the response. This will be set in the Content-Type header. | +| `body` | _[CustomResponseBody](#customresponsebody)_ | true | Body of the Custom Response | + + +#### CustomResponseBody + + + +CustomResponseBody + +_Appears in:_ +- [CustomResponse](#customresponse) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[ResponseValueType](#responsevaluetype)_ | true | Type is the type of method to use to read the body value. | +| `inline` | _string_ | false | Inline contains the value as an inline string. | +| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | + + +#### CustomResponseMatch + + + +CustomResponseMatch defines the configuration for matching a user response to return a custom one. + +_Appears in:_ +- [ResponseOverride](#responseoverride) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `statusCode` | _[StatusCodeMatch](#statuscodematch) array_ | true | Status code to match on. The match evaluates to true if any of the matches are successful. | + + #### CustomTag @@ -3450,6 +3495,32 @@ _Appears in:_ | `File` | ResourceProviderTypeFile defines the "File" provider.
| +#### ResponseOverride + + + +ResponseOverride defines the configuration to override specific responses with a custom one. + +_Appears in:_ +- [BackendTrafficPolicySpec](#backendtrafficpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `match` | _[CustomResponseMatch](#customresponsematch)_ | true | Match configuration. | +| `response` | _[CustomResponse](#customresponse)_ | true | Response configuration. | + + +#### ResponseValueType + +_Underlying type:_ _string_ + +ResponseValueType defines the types of values for the response body supported by Envoy Gateway. + +_Appears in:_ +- [CustomResponseBody](#customresponsebody) + + + #### RetryOn @@ -3632,6 +3703,48 @@ _Appears in:_ | `Distinct` | SourceMatchDistinct Each IP Address within the specified Source IP CIDR is treated as a distinct client selector
and uses a separate rate limit bucket/counter.
Note: This is only supported for Global Rate Limits.
| +#### StatusCodeMatch + + + + + +_Appears in:_ +- [CustomResponseMatch](#customresponsematch) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[StatusCodeValueType](#statuscodevaluetype)_ | true | Type is the type of value. | +| `value` | _string_ | false | Value contains the value of the status code. | +| `range` | _[StatusCodeRange](#statuscoderange)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | + + +#### StatusCodeRange + + + +StatusCodeRange defines the configuration for define a range of status codes. + +_Appears in:_ +- [StatusCodeMatch](#statuscodematch) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `start` | _integer_ | true | Start of the range, including the start value. | +| `end` | _integer_ | true | End of the range, including the end value. | + + +#### StatusCodeValueType + +_Underlying type:_ _string_ + +StatusCodeValueType defines the types of values for the status code match supported by Envoy Gateway. + +_Appears in:_ +- [StatusCodeMatch](#statuscodematch) + + + #### StringMatch