Repository: envoyproxy/gateway
Pull request #4334 (2024-09-26 02:01:44)

api: direct response
Relates to https://github.com/envoyproxy/gateway/issues/2714


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/envoyproxy/gateway/pull/4334?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 66.02%. Comparing base [(`6eefb28`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/6eefb28871160c166afe4d9db3ff1b11bea35c59?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) to head [(`c124a28`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/c124a28ae17906fcc910d35c291129bebbceb27c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #4334      +/-   ##
==========================================
+ Coverage   65.98%   66.02%   +0.03%     
==========================================
  Files         197      197              
  Lines       23964    23964              
==========================================
+ Hits        15813    15822       +9     
+ Misses       7025     7017       -8     
+ Partials     1126     1125       -1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/envoyproxy/gateway/pull/4334?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).


Patch:
diff --git a/api/v1alpha1/backend_types.go b/api/v1alpha1/backend_types.go
index a2022c2c5..bb407981a 100644
--- a/api/v1alpha1/backend_types.go
+++ b/api/v1alpha1/backend_types.go
@@ -131,7 +131,6 @@ type BackendSpec struct {
 	// the health of the active backends falls below 72%.
 	//
 	// +optional
-	// +notImplementedHide
 	Fallback *bool `json:"fallback,omitempty"`
 }
 
diff --git a/api/v1alpha1/httproutefilter_types.go b/api/v1alpha1/httproutefilter_types.go
index 7f56ca07d..9ae8be598 100644
--- a/api/v1alpha1/httproutefilter_types.go
+++ b/api/v1alpha1/httproutefilter_types.go
@@ -33,6 +33,8 @@ type HTTPRouteFilter struct {
 type HTTPRouteFilterSpec struct {
 	// +optional
 	URLRewrite *HTTPURLRewriteFilter `json:"urlRewrite,omitempty"`
+	// +optional
+	DirectResponse *HTTPDirectResponseFilter `json:"directResponse,omitempty"`
 }
 
 // HTTPURLRewriteFilter define rewrites of HTTP URL components such as path and host
@@ -49,6 +51,24 @@ type HTTPURLRewriteFilter struct {
 	Path *HTTPPathModifier `json:"path,omitempty"`
 }
 
+// HTTPDirectResponseFilter defines the configuration to return a fixed response.
+type HTTPDirectResponseFilter struct {
+	// Content Type of the response. This will be set in the Content-Type header.
+	//
+	// +optional
+	ContentType *string `json:"contentType,omitempty"`
+
+	// Body of the Response
+	//
+	// +optional
+	Body *CustomResponseBody `json:"body,omitempty"`
+
+	// Status Code of the HTTP response
+	// If unset, defaults to 200.
+	// +optional
+	StatusCode *int `json:"statusCode,omitempty"`
+}
+
 // HTTPPathModifierType defines the type of path redirect or rewrite.
 type HTTPPathModifierType string
 
diff --git a/api/v1alpha1/ratelimit_types.go b/api/v1alpha1/ratelimit_types.go
index deb859400..d7a437510 100644
--- a/api/v1alpha1/ratelimit_types.go
+++ b/api/v1alpha1/ratelimit_types.go
@@ -101,8 +101,6 @@ type RateLimitSelectCondition struct {
 	// meaning, a request MUST match all the specified headers.
 	// At least one of headers or sourceCIDR condition must be specified.
 	//
-	// +listType=map
-	// +listMapKey=name
 	// +optional
 	// +kubebuilder:validation:MaxItems=16
 	Headers []HeaderMatch `json:"headers,omitempty"`
@@ -138,13 +136,6 @@ type SourceMatch struct {
 	// +kubebuilder:validation:MinLength=1
 	// +kubebuilder:validation:MaxLength=256
 	Value string `json:"value"`
-
-	// Invert specifies whether the value match result will be inverted.
-	//
-	// +optional
-	// +kubebuilder:default=false
-	// +notImplementedHide
-	Invert *bool `json:"invert,omitempty"`
 }
 
 // HeaderMatch defines the match attributes within the HTTP Headers of the request.
diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go
index 8e98b904e..fe795c833 100644
--- a/api/v1alpha1/shared_types.go
+++ b/api/v1alpha1/shared_types.go
@@ -669,7 +669,9 @@ type CustomResponse struct {
 	ContentType *string `json:"contentType,omitempty"`
 
 	// Body of the Custom Response
-	Body CustomResponseBody `json:"body"`
+	//
+	// +optional
+	Body *CustomResponseBody `json:"body,omitempty"`
 }
 
 // ResponseValueType defines the types of values for the response body supported by Envoy Gateway.
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index a5e8dc183..696c99259 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -1148,7 +1148,11 @@ func (in *CustomResponse) DeepCopyInto(out *CustomResponse) {
 		*out = new(string)
 		**out = **in
 	}
-	in.Body.DeepCopyInto(&out.Body)
+	if in.Body != nil {
+		in, out := &in.Body, &out.Body
+		*out = new(CustomResponseBody)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResponse.
@@ -2688,6 +2692,36 @@ func (in *HTTPClientTimeout) DeepCopy() *HTTPClientTimeout {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *HTTPDirectResponseFilter) DeepCopyInto(out *HTTPDirectResponseFilter) {
+	*out = *in
+	if in.ContentType != nil {
+		in, out := &in.ContentType, &out.ContentType
+		*out = new(string)
+		**out = **in
+	}
+	if in.Body != nil {
+		in, out := &in.Body, &out.Body
+		*out = new(CustomResponseBody)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.StatusCode != nil {
+		in, out := &in.StatusCode, &out.StatusCode
+		*out = new(int)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPDirectResponseFilter.
+func (in *HTTPDirectResponseFilter) DeepCopy() *HTTPDirectResponseFilter {
+	if in == nil {
+		return nil
+	}
+	out := new(HTTPDirectResponseFilter)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *HTTPExtAuthService) DeepCopyInto(out *HTTPExtAuthService) {
 	*out = *in
@@ -2820,6 +2854,11 @@ func (in *HTTPRouteFilterSpec) DeepCopyInto(out *HTTPRouteFilterSpec) {
 		*out = new(HTTPURLRewriteFilter)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.DirectResponse != nil {
+		in, out := &in.DirectResponse, &out.DirectResponse
+		*out = new(HTTPDirectResponseFilter)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteFilterSpec.
@@ -5075,11 +5114,6 @@ func (in *SourceMatch) DeepCopyInto(out *SourceMatch) {
 		*out = new(SourceMatchType)
 		**out = **in
 	}
-	if in.Invert != nil {
-		in, out := &in.Invert, &out.Invert
-		*out = new(bool)
-		**out = **in
-	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceMatch.
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 c0dce5938..7b2e93731 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -727,19 +727,11 @@ spec:
                                       type: object
                                     maxItems: 16
                                     type: array
-                                    x-kubernetes-list-map-keys:
-                                    - name
-                                    x-kubernetes-list-type: map
                                   sourceCIDR:
                                     description: |-
                                       SourceCIDR is the client IP Address range to match on.
                                       At least one of headers or sourceCIDR condition must be specified.
                                     properties:
-                                      invert:
-                                        default: false
-                                        description: Invert specifies whether the
-                                          value match result will be inverted.
-                                        type: boolean
                                       type:
                                         default: Exact
                                         enum:
@@ -870,19 +862,11 @@ spec:
                                       type: object
                                     maxItems: 16
                                     type: array
-                                    x-kubernetes-list-map-keys:
-                                    - name
-                                    x-kubernetes-list-type: map
                                   sourceCIDR:
                                     description: |-
                                       SourceCIDR is the client IP Address range to match on.
                                       At least one of headers or sourceCIDR condition must be specified.
                                     properties:
-                                      invert:
-                                        default: false
-                                        description: Invert specifies whether the
-                                          value match result will be inverted.
-                                        type: boolean
                                       type:
                                         default: Exact
                                         enum:
@@ -1051,8 +1035,6 @@ spec:
                           description: Content Type of the response. This will be
                             set in the Content-Type header.
                           type: string
-                      required:
-                      - body
                       type: object
                   required:
                   - match
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml
index 7a55ec887..8a75fec42 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml
@@ -49,6 +49,66 @@ spec:
           spec:
             description: Spec defines the desired state of HTTPRouteFilter.
             properties:
+              directResponse:
+                description: HTTPDirectResponseFilter defines the configuration to
+                  return a fixed response.
+                properties:
+                  body:
+                    description: Body of the 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
+                  statusCode:
+                    description: |-
+                      Status Code of the HTTP response
+                      If unset, defaults to 200.
+                    type: integer
+                type: object
               urlRewrite:
                 description: HTTPURLRewriteFilter define rewrites of HTTP URL components
                   such as path and host
diff --git a/charts/gateway-helm/templates/infra-manager-rbac.yaml b/charts/gateway-helm/templates/infra-manager-rbac.yaml
index 02382bc27..74c0ec628 100644
--- a/charts/gateway-helm/templates/infra-manager-rbac.yaml
+++ b/charts/gateway-helm/templates/infra-manager-rbac.yaml
@@ -11,10 +11,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -25,6 +27,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -36,6 +39,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 apiVersion: rbac.authorization.k8s.io/v1
diff --git a/examples/extension-server/go.mod b/examples/extension-server/go.mod
index c4a08feca..92af04381 100644
--- a/examples/extension-server/go.mod
+++ b/examples/extension-server/go.mod
@@ -4,7 +4,7 @@ go 1.23.1
 
 require (
 	github.com/envoyproxy/gateway v1.0.2
-	github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568
+	github.com/envoyproxy/go-control-plane v0.13.1
 	github.com/urfave/cli/v2 v2.27.4
 	google.golang.org/grpc v1.67.1
 	google.golang.org/protobuf v1.35.1
diff --git a/examples/extension-server/go.sum b/examples/extension-server/go.sum
index 7d995a604..1df719e00 100644
--- a/examples/extension-server/go.sum
+++ b/examples/extension-server/go.sum
@@ -11,8 +11,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568 h1:bUMUmkPtm/z62/8WiVbxtqTK8I7AzXGYn+qB8JAzAXw=
-github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
+github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
+github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
 github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
 github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
 github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
diff --git a/go.mod b/go.mod
index 482af942a..ee7928f0a 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
 	github.com/docker/cli v27.3.1+incompatible
 	github.com/dominikbraun/graph v0.23.0
-	github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568
+	github.com/envoyproxy/go-control-plane v0.13.1
 	github.com/envoyproxy/ratelimit v1.4.1-0.20230427142404-e2a87f41d3a7
 	github.com/evanphx/json-patch/v5 v5.9.0
 	github.com/fatih/color v1.17.0
diff --git a/go.sum b/go.sum
index c5d453f9e..93ade68d7 100644
--- a/go.sum
+++ b/go.sum
@@ -222,8 +222,8 @@ github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568 h1:bUMUmkPtm/z62/8WiVbxtqTK8I7AzXGYn+qB8JAzAXw=
-github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
+github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
+github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
 github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go
index 1239caadc..f82158715 100644
--- a/internal/gatewayapi/route.go
+++ b/internal/gatewayapi/route.go
@@ -1291,6 +1291,7 @@ func (t *Translator) processDestination(backendRefContext BackendRefContext,
 			resources,
 			envoyProxy,
 		)
+
 		ds.Filters = t.processDestinationFilters(routeType, backendRefContext, parentRef, route, resources)
 	}
 
@@ -1672,9 +1673,18 @@ func (t *Translator) processBackendDestinationSetting(backendRef gwapiv1.Backend
 		}
 	}
 
-	return &ir.DestinationSetting{
+	ds := &ir.DestinationSetting{
 		Protocol:    dstProtocol,
 		Endpoints:   dstEndpoints,
 		AddressType: dstAddrType,
 	}
+
+	if backend.Spec.Fallback != nil {
+		// set only the secondary priority, the backend defaults to a primary priority if unset.
+		if ptr.Deref(backend.Spec.Fallback, false) {
+			ds.Priority = ptr.To(uint32(1))
+		}
+	}
+
+	return ds
 }
diff --git a/internal/infrastructure/kubernetes/infra_resource.go b/internal/infrastructure/kubernetes/infra_resource.go
index 04d0a2e0c..16cd72b95 100644
--- a/internal/infrastructure/kubernetes/infra_resource.go
+++ b/internal/infrastructure/kubernetes/infra_resource.go
@@ -398,6 +398,7 @@ func (i *Infra) deleteServiceAccount(ctx context.Context, r ResourceRender) (err
 
 	return i.Client.DeleteAllOf(ctx, sa, &client.DeleteAllOfOptions{
 		ListOptions: client.ListOptions{
+			Namespace:     ns,
 			LabelSelector: r.LabelSelector(),
 		},
 	})
@@ -437,6 +438,7 @@ func (i *Infra) deleteDeployment(ctx context.Context, r ResourceRender) (err err
 
 	return i.Client.DeleteAllOf(ctx, deployment, &client.DeleteAllOfOptions{
 		ListOptions: client.ListOptions{
+			Namespace:     ns,
 			LabelSelector: r.LabelSelector(),
 		},
 	})
@@ -476,6 +478,7 @@ func (i *Infra) deleteDaemonSet(ctx context.Context, r ResourceRender) (err erro
 
 	return i.Client.DeleteAllOf(ctx, daemonSet, &client.DeleteAllOfOptions{
 		ListOptions: client.ListOptions{
+			Namespace:     ns,
 			LabelSelector: r.LabelSelector(),
 		},
 	})
@@ -510,6 +513,7 @@ func (i *Infra) deleteConfigMap(ctx context.Context, r ResourceRender) (err erro
 
 	return i.Client.DeleteAllOf(ctx, cm, &client.DeleteAllOfOptions{
 		ListOptions: client.ListOptions{
+			Namespace:     ns,
 			LabelSelector: r.LabelSelector(),
 		},
 	})
@@ -544,6 +548,7 @@ func (i *Infra) deleteService(ctx context.Context, r ResourceRender) (err error)
 
 	return i.Client.DeleteAllOf(ctx, svc, &client.DeleteAllOfOptions{
 		ListOptions: client.ListOptions{
+			Namespace:     ns,
 			LabelSelector: r.LabelSelector(),
 		},
 	})
@@ -583,6 +588,7 @@ func (i *Infra) deleteHPA(ctx context.Context, r ResourceRender) (err error) {
 
 	return i.Client.DeleteAllOf(ctx, hpa, &client.DeleteAllOfOptions{
 		ListOptions: client.ListOptions{
+			Namespace:     ns,
 			LabelSelector: r.LabelSelector(),
 		},
 	})
@@ -622,6 +628,7 @@ func (i *Infra) deletePDB(ctx context.Context, r ResourceRender) (err error) {
 
 	return i.Client.DeleteAllOf(ctx, pdb, &client.DeleteAllOfOptions{
 		ListOptions: client.ListOptions{
+			Namespace:     ns,
 			LabelSelector: r.LabelSelector(),
 		},
 	})


Test patch:
diff --git a/internal/gatewayapi/testdata/backend-with-fallback.in.yaml b/internal/gatewayapi/testdata/backend-with-fallback.in.yaml
new file mode 100644
index 000000000..667a75a65
--- /dev/null
+++ b/internal/gatewayapi/testdata/backend-with-fallback.in.yaml
@@ -0,0 +1,58 @@
+gateways:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    metadata:
+      namespace: envoy-gateway
+      name: gateway-1
+    spec:
+      gatewayClassName: envoy-gateway-class
+      listeners:
+        - name: http
+          protocol: HTTP
+          port: 80
+          allowedRoutes:
+            namespaces:
+              from: All
+httpRoutes:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: HTTPRoute
+    metadata:
+      namespace: default
+      name: httproute-1
+    spec:
+      parentRefs:
+        - namespace: envoy-gateway
+          name: gateway-1
+      rules:
+        - matches:
+            - path:
+                value: "/"
+          backendRefs:
+            - group: gateway.envoyproxy.io
+              kind: Backend
+              name: backend-1
+            - group: gateway.envoyproxy.io
+              kind: Backend
+              name: backend-2
+backends:
+  - apiVersion: gateway.envoyproxy.io/v1alpha1
+    kind: Backend
+    metadata:
+      name: backend-1
+      namespace: default
+    spec:
+      endpoints:
+        - ip:
+            address: 1.1.1.1
+            port: 3001
+  - apiVersion: gateway.envoyproxy.io/v1alpha1
+    kind: Backend
+    metadata:
+      name: backend-2
+      namespace: default
+    spec:
+      fallback: true
+      endpoints:
+        - ip:
+            address: 2.2.2.2
+            port: 3001
diff --git a/internal/gatewayapi/testdata/backend-with-fallback.out.yaml b/internal/gatewayapi/testdata/backend-with-fallback.out.yaml
new file mode 100644
index 000000000..74bd61795
--- /dev/null
+++ b/internal/gatewayapi/testdata/backend-with-fallback.out.yaml
@@ -0,0 +1,180 @@
+backends:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: Backend
+  metadata:
+    creationTimestamp: null
+    name: backend-1
+    namespace: default
+  spec:
+    endpoints:
+    - ip:
+        address: 1.1.1.1
+        port: 3001
+  status:
+    conditions:
+    - lastTransitionTime: null
+      message: The Backend was accepted
+      reason: Accepted
+      status: "True"
+      type: Accepted
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: Backend
+  metadata:
+    creationTimestamp: null
+    name: backend-2
+    namespace: default
+  spec:
+    endpoints:
+    - ip:
+        address: 2.2.2.2
+        port: 3001
+    fallback: true
+  status:
+    conditions:
+    - lastTransitionTime: null
+      message: The Backend was accepted
+      reason: Accepted
+      status: "True"
+      type: Accepted
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    creationTimestamp: null
+    name: gateway-1
+    namespace: envoy-gateway
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - allowedRoutes:
+        namespaces:
+          from: All
+      name: http
+      port: 80
+      protocol: HTTP
+  status:
+    listeners:
+    - attachedRoutes: 1
+      conditions:
+      - lastTransitionTime: null
+        message: Sending translated listener configuration to the data plane
+        reason: Programmed
+        status: "True"
+        type: Programmed
+      - lastTransitionTime: null
+        message: Listener has been successfully translated
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Listener references have been resolved
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      name: http
+      supportedKinds:
+      - group: gateway.networking.k8s.io
+        kind: HTTPRoute
+      - group: gateway.networking.k8s.io
+        kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    name: httproute-1
+    namespace: default
+  spec:
+    parentRefs:
+    - name: gateway-1
+      namespace: envoy-gateway
+    rules:
+    - backendRefs:
+      - group: gateway.envoyproxy.io
+        kind: Backend
+        name: backend-1
+      - group: gateway.envoyproxy.io
+        kind: Backend
+        name: backend-2
+      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-1
+        namespace: envoy-gateway
+infraIR:
+  envoy-gateway/gateway-1:
+    proxy:
+      listeners:
+      - address: null
+        name: envoy-gateway/gateway-1/http
+        ports:
+        - containerPort: 10080
+          name: http-80
+          protocol: HTTP
+          servicePort: 80
+      metadata:
+        labels:
+          gateway.envoyproxy.io/owning-gateway-name: gateway-1
+          gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+      name: envoy-gateway/gateway-1
+xdsIR:
+  envoy-gateway/gateway-1:
+    accessLog:
+      text:
+      - path: /dev/stdout
+    http:
+    - address: 0.0.0.0
+      hostnames:
+      - '*'
+      isHTTP2: false
+      metadata:
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      name: envoy-gateway/gateway-1/http
+      path:
+        escapedSlashesAction: UnescapeAndRedirect
+        mergeSlashes: true
+      port: 10080
+      routes:
+      - destination:
+          name: httproute/default/httproute-1/rule/0
+          settings:
+          - addressType: IP
+            endpoints:
+            - host: 1.1.1.1
+              port: 3001
+            weight: 1
+          - addressType: IP
+            endpoints:
+            - host: 2.2.2.2
+              port: 3001
+            priority: 1
+            weight: 1
+        hostname: '*'
+        isHTTP2: false
+        metadata:
+          kind: HTTPRoute
+          name: httproute-1
+          namespace: default
+        name: httproute/default/httproute-1/rule/0/match/0/*
+        pathMatch:
+          distinct: false
+          name: ""
+          prefix: /
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index ddc32a202..fe361099a 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -384,6 +384,7 @@ _Appears in:_
 | ---   | ---  | ---      | ---         |
 | `endpoints` | _[BackendEndpoint](#backendendpoint) array_ |  true  | Endpoints defines the endpoints to be used when connecting to the backend. |
 | `appProtocols` | _[AppProtocolType](#appprotocoltype) array_ |  false  | AppProtocols defines the application protocols to be supported when connecting to the backend. |
+| `fallback` | _boolean_ |  false  | Fallback indicates whether the backend is designated as a fallback.<br />It is highly recommended to configure active or passive health checks to ensure that failover can be detected<br />when the active backends become unhealthy and to automatically readjust once the primary backends are healthy again.<br />The overprovisioning factor is set to 1.4, meaning the fallback backends will only start receiving traffic when<br />the health of the active backends falls below 72%. |
 
 
 #### BackendStatus
@@ -865,7 +866,7 @@ _Appears in:_
 | 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 |
+| `body` | _[CustomResponseBody](#customresponsebody)_ |  false  | Body of the Custom Response |
 
 
 #### CustomResponseBody
@@ -876,6 +877,7 @@ CustomResponseBody
 
 _Appears in:_
 - [CustomResponse](#customresponse)
+- [HTTPDirectResponseFilter](#httpdirectresponsefilter)
 
 | Field | Type | Required | Description |
 | ---   | ---  | ---      | ---         |
@@ -1921,6 +1923,22 @@ _Appears in:_
 | `idleTimeout` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ |  false  | IdleTimeout for an HTTP connection. Idle time is defined as a period in which there are no active requests in the connection.<br />Default: 1 hour. |
 
 
+#### HTTPDirectResponseFilter
+
+
+
+HTTPDirectResponseFilter defines the configuration to return a fixed response.
+
+_Appears in:_
+- [HTTPRouteFilterSpec](#httproutefilterspec)
+
+| Field | Type | Required | Description |
+| ---   | ---  | ---      | ---         |
+| `contentType` | _string_ |  false  | Content Type of the response. This will be set in the Content-Type header. |
+| `body` | _[CustomResponseBody](#customresponsebody)_ |  false  | Body of the Response |
+| `statusCode` | _integer_ |  false  | Status Code of the HTTP response<br />If unset, defaults to 200. |
+
+
 #### HTTPExtAuthService
 
 
@@ -2044,6 +2062,7 @@ _Appears in:_
 | Field | Type | Required | Description |
 | ---   | ---  | ---      | ---         |
 | `urlRewrite` | _[HTTPURLRewriteFilter](#httpurlrewritefilter)_ |  false  |  |
+| `directResponse` | _[HTTPDirectResponseFilter](#httpdirectresponsefilter)_ |  false  |  |
 
 
 #### HTTPStatus
diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md
index ddc32a202..fe361099a 100644
--- a/site/content/zh/latest/api/extension_types.md
+++ b/site/content/zh/latest/api/extension_types.md
@@ -384,6 +384,7 @@ _Appears in:_
 | ---   | ---  | ---      | ---         |
 | `endpoints` | _[BackendEndpoint](#backendendpoint) array_ |  true  | Endpoints defines the endpoints to be used when connecting to the backend. |
 | `appProtocols` | _[AppProtocolType](#appprotocoltype) array_ |  false  | AppProtocols defines the application protocols to be supported when connecting to the backend. |
+| `fallback` | _boolean_ |  false  | Fallback indicates whether the backend is designated as a fallback.<br />It is highly recommended to configure active or passive health checks to ensure that failover can be detected<br />when the active backends become unhealthy and to automatically readjust once the primary backends are healthy again.<br />The overprovisioning factor is set to 1.4, meaning the fallback backends will only start receiving traffic when<br />the health of the active backends falls below 72%. |
 
 
 #### BackendStatus
@@ -865,7 +866,7 @@ _Appears in:_
 | 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 |
+| `body` | _[CustomResponseBody](#customresponsebody)_ |  false  | Body of the Custom Response |
 
 
 #### CustomResponseBody
@@ -876,6 +877,7 @@ CustomResponseBody
 
 _Appears in:_
 - [CustomResponse](#customresponse)
+- [HTTPDirectResponseFilter](#httpdirectresponsefilter)
 
 | Field | Type | Required | Description |
 | ---   | ---  | ---      | ---         |
@@ -1921,6 +1923,22 @@ _Appears in:_
 | `idleTimeout` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ |  false  | IdleTimeout for an HTTP connection. Idle time is defined as a period in which there are no active requests in the connection.<br />Default: 1 hour. |
 
 
+#### HTTPDirectResponseFilter
+
+
+
+HTTPDirectResponseFilter defines the configuration to return a fixed response.
+
+_Appears in:_
+- [HTTPRouteFilterSpec](#httproutefilterspec)
+
+| Field | Type | Required | Description |
+| ---   | ---  | ---      | ---         |
+| `contentType` | _string_ |  false  | Content Type of the response. This will be set in the Content-Type header. |
+| `body` | _[CustomResponseBody](#customresponsebody)_ |  false  | Body of the Response |
+| `statusCode` | _integer_ |  false  | Status Code of the HTTP response<br />If unset, defaults to 200. |
+
+
 #### HTTPExtAuthService
 
 
@@ -2044,6 +2062,7 @@ _Appears in:_
 | Field | Type | Required | Description |
 | ---   | ---  | ---      | ---         |
 | `urlRewrite` | _[HTTPURLRewriteFilter](#httpurlrewritefilter)_ |  false  |  |
+| `directResponse` | _[HTTPDirectResponseFilter](#httpdirectresponsefilter)_ |  false  |  |
 
 
 #### HTTPStatus
diff --git a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml
index 8a1513469..3746f12a1 100644
--- a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml
+++ b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml
@@ -204,10 +204,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -218,6 +220,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -229,6 +232,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 # Source: gateway-helm/templates/leader-election-rbac.yaml
diff --git a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml
index a71e46fe7..582fab0e8 100644
--- a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml
+++ b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml
@@ -219,10 +219,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -233,6 +235,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -244,6 +247,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 # Source: gateway-helm/templates/leader-election-rbac.yaml
diff --git a/test/helm/gateway-helm/default-config.out.yaml b/test/helm/gateway-helm/default-config.out.yaml
index 140d271c6..b4aac21b1 100644
--- a/test/helm/gateway-helm/default-config.out.yaml
+++ b/test/helm/gateway-helm/default-config.out.yaml
@@ -204,10 +204,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -218,6 +220,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -229,6 +232,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 # Source: gateway-helm/templates/leader-election-rbac.yaml
diff --git a/test/helm/gateway-helm/deployment-custom-topology.out.yaml b/test/helm/gateway-helm/deployment-custom-topology.out.yaml
index 586b64b55..37f58022b 100644
--- a/test/helm/gateway-helm/deployment-custom-topology.out.yaml
+++ b/test/helm/gateway-helm/deployment-custom-topology.out.yaml
@@ -204,10 +204,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -218,6 +220,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -229,6 +232,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 # Source: gateway-helm/templates/leader-election-rbac.yaml
diff --git a/test/helm/gateway-helm/deployment-images-config.out.yaml b/test/helm/gateway-helm/deployment-images-config.out.yaml
index 10f849e1d..8506b87b0 100644
--- a/test/helm/gateway-helm/deployment-images-config.out.yaml
+++ b/test/helm/gateway-helm/deployment-images-config.out.yaml
@@ -204,10 +204,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -218,6 +220,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -229,6 +232,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 # Source: gateway-helm/templates/leader-election-rbac.yaml
diff --git a/test/helm/gateway-helm/deployment-priorityclass.out.yaml b/test/helm/gateway-helm/deployment-priorityclass.out.yaml
index 4f735c420..3f8274641 100644
--- a/test/helm/gateway-helm/deployment-priorityclass.out.yaml
+++ b/test/helm/gateway-helm/deployment-priorityclass.out.yaml
@@ -204,10 +204,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -218,6 +220,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -229,6 +232,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 # Source: gateway-helm/templates/leader-election-rbac.yaml
diff --git a/test/helm/gateway-helm/envoy-gateway-config.out.yaml b/test/helm/gateway-helm/envoy-gateway-config.out.yaml
index 041599582..9a3f5b484 100644
--- a/test/helm/gateway-helm/envoy-gateway-config.out.yaml
+++ b/test/helm/gateway-helm/envoy-gateway-config.out.yaml
@@ -206,10 +206,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -220,6 +222,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -231,6 +234,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 # Source: gateway-helm/templates/leader-election-rbac.yaml
diff --git a/test/helm/gateway-helm/global-images-config.out.yaml b/test/helm/gateway-helm/global-images-config.out.yaml
index f280fc9f2..38be258c7 100644
--- a/test/helm/gateway-helm/global-images-config.out.yaml
+++ b/test/helm/gateway-helm/global-images-config.out.yaml
@@ -208,10 +208,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -222,6 +224,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -233,6 +236,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 # Source: gateway-helm/templates/leader-election-rbac.yaml
diff --git a/test/helm/gateway-helm/service-annotations.out.yaml b/test/helm/gateway-helm/service-annotations.out.yaml
index ec50a16e3..532988e91 100644
--- a/test/helm/gateway-helm/service-annotations.out.yaml
+++ b/test/helm/gateway-helm/service-annotations.out.yaml
@@ -204,10 +204,12 @@ rules:
   resources:
   - serviceaccounts
   - services
+  - configmaps
   verbs:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - apps
@@ -218,6 +220,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 - apiGroups:
   - autoscaling
@@ -229,6 +232,7 @@ rules:
   - create
   - get
   - delete
+  - deletecollection
   - patch
 ---
 # Source: gateway-helm/templates/leader-election-rbac.yaml