...
1# GEP-718: Rework forwardTo segment in routes
2
3* Issue: [#718](https://github.com/kubernetes-sigs/gateway-api/issues/718)
4* Status: Standard
5
6## Motivation
7
8### Complications due to the `serviceName` shortcut
9
10Within `RouteForwardTo` (and `HTTPRouteForwardTo` by extension) there exists
11two mechanisms to specify the upstream network endpoint where the traffic should
12be forwarded to:
13- `ServiceName`: the name of the Kubernetes Service
14- `BackendRef`: a LocalObjectReference to a resource, this is an extension point in the API
15
16While working on [#685](https://github.com/kubernetes-sigs/gateway-api/pull/685),
17it came to light that:
181. `BackendRef` itself could be used to point to a Kubernetes Service
192. With [GEP-709](https://github.com/kubernetes-sigs/gateway-api/pull/711),
20there will be a need to introduce a new `namespace` field which will enable use-cases to forward
21traffic to a services and backends located in different namespaces
22
23While (1) can be fixed with more validation and documentation, it only solves for
24the specific case. (2) requires adding two fields: `serviceNamespace`
25and `BackendRef.Namespace` - something we would like to avoid since the `serviceName`
26field was introduced only as a shortcut and adding more service-specific fields
27defeats the original purpose.
28
29These problems are in addition to the original problem that
30[#685](https://github.com/kubernetes-sigs/gateway-api/pull/685) attempts to solve:
31clarifying port requirements when a port is required or not.
32We have been updating documentation to tackle such corner-cases but that
33continues to result in more and more elaborations. Some excerpts from our
34existing documentation:
35```
36 // ServiceName refers to the name of the Service to mirror matched requests
37 // to. When specified, this takes the place of BackendRef. If both
38 // BackendRef and ServiceName are specified, ServiceName will be given
39 // precedence.
40
41 // BackendRef is a local object reference to mirror matched requests to. If
42 // both BackendRef and ServiceName are specified, ServiceName will be given
43 // precedence.
44
45 // Weight specifies the proportion of HTTP requests forwarded to the backend
46 // referenced by the ServiceName or BackendRef field. This is computed as
47
48 // Port specifies the destination port number to use for the
49 // backend referenced by the ServiceName or BackendRef field.
50 // If unspecified, the destination port in the request is used
51 // when forwarding to a backendRef or serviceName.
52```
53
54### Simplifying `RouteForwardTo`
55
56RouteForwardTo is not the best of the names. Using a noun instead of a verb
57would help with in-person communication and documentation.
58Since we are already considering dropping the special case of `service` as a
59backend, we have an opportunity to further simplify `RouteForwardTo` for better
60UX.
61
62## Proposal
63
64- Remove the `ServiceName` field from `RouteForwardTo`
65- Introduce `Service` as a default for `BackendRef.Kind`
66- Pull fields from `RouteForwardTo.BackendRef` into `RouteForwardTo` itself
67- Rename `RouteForwardTo` to `BackendRef`
68- Rename `Route.Spec.Rules[].ForwardTo[]` to `Route.Spec.Rules[].BackendRefs[]` in
69 all routes
70- Apply the same to HTTP types and filters
71
72## API
73
74The updated `BackendRef` (formerly `RouteForwardTo`) struct will be:
75(HTTP version has been omitted for brevity)
76
77```go
78// BackendRef defines how and where a request should be forwarded from the Gateway.
79type BackendRef struct {
80 // Group is the group of the backend resource.
81 //
82 // +kubebuilder:validation:MinLength=1
83 // +kubebuilder:validation:MaxLength=253
84 Group string `json:"group"`
85
86 // Kind is kind of the backend resource.
87 //
88 // Support: Core (Kubernetes Service)
89 // Support: Implementation-specific (any other resource)
90 //
91 // +optional
92 // +kubebuilder:validation:MinLength=1
93 // +kubebuilder:validation:MaxLength=253
94 // +kubebuilder:default="service"
95 Kind *string `json:"kind"`
96
97 // Name is the name of the backend resource to forward matched requests to.
98 //
99 // If the referent cannot be found, the rule is not included in the route.
100 // The controller should raise the "ResolvedRefs" condition on the Gateway
101 // with the "DegradedRoutes" reason. The gateway status for this route should
102 // be updated with a condition that describes the error more specifically.
103 //
104 // +kubebuilder:validation:MinLength=1
105 // +kubebuilder:validation:MaxLength=253
106 Name string `json:"name"`
107
108 // Port specifies the destination port number to use for the
109 // backend resource.
110 // This field is required when the backend is a Kubernetes Service.
111 //
112 // Support: Core
113 //
114 // +optional
115 Port *PortNumber `json:"port,omitempty"`
116
117 // Weight specifies the proportion of HTTP requests forwarded to the backend
118 // This is computed as weight/(sum of all weights in this Backends list).
119 // For non-zero values, there may be some epsilon from the exact proportion
120 // defined here depending on the precision an implementation supports. Weight
121 // is not a percentage and the sum of weights does not need to equal 100.
122 //
123 // If only one backend is specified and it has a weight greater than 0, 100%
124 // of the traffic is forwarded to that backend. If weight is set to 0, no
125 // traffic should be forwarded for this entry. If unspecified, weight
126 // defaults to 1.
127 //
128 // Support: Extended
129 //
130 // +optional
131 // +kubebuilder:default=1
132 // +kubebuilder:validation:Minimum=0
133 // +kubebuilder:validation:Maximum=1000000
134 Weight *int32 `json:"weight,omitempty"`
135}
136```
137
138A summarized diff for the changes being proposed:
139
140```patch
141diff --git a/apis/v1alpha1/shared_types.go b/apis/v1alpha1/shared_types.go
142index 458145f..de720cd 100644
143--- a/apis/v1alpha1/shared_types.go
144+++ b/apis/v1alpha1/shared_types.go
145@@ -94,51 +94,39 @@ type GatewayReference struct {
146 Namespace string `json:"namespace"`
147 }
148
149-// RouteForwardTo defines how a Route should forward a request.
150-type RouteForwardTo struct {
151- // ServiceName refers to the name of the Service to forward matched requests
152- // to. When specified, this takes the place of BackendRef. If both
153- // BackendRef and ServiceName are specified, ServiceName will be given
154- // precedence.
155+// BackendRef defines how and where a request should be forwarded from the Gateway.
156+type BackendRef struct {
157+ // Name is the name of the backend resource to forward matched requests to.
158 //
159 // If the referent cannot be found, the rule is not included in the route.
160 // The controller should raise the "ResolvedRefs" condition on the Gateway
161 // with the "DegradedRoutes" reason. The gateway status for this route should
162 // be updated with a condition that describes the error more specifically.
163 //
164- // The protocol to use is defined using AppProtocol field (introduced in
165- // Kubernetes 1.18) in the Service resource. In the absence of the
166- // AppProtocol field a `networking.x-k8s.io/app-protocol` annotation on the
167- // BackendPolicy resource may be used to define the protocol. If the
168- // AppProtocol field is available, this annotation should not be used. The
169- // AppProtocol field, when populated, takes precedence over the annotation
170- // in the BackendPolicy resource. For custom backends, it is encouraged to
171- // add a semantically-equivalent field in the Custom Resource Definition.
172+ // +kubebuilder:validation:MinLength=1
173+ // +kubebuilder:validation:MaxLength=253
174+ Name string `json:"name"`
175+
176+ // Kind is kind of the backend resource.
177 //
178- // Support: Core
179+ // Support: Core (Kubernetes Service)
180+ // Support: Custom (any other resource)
181 //
182 // +optional
183+ // +kubebuilder:validation:MinLength=1
184 // +kubebuilder:validation:MaxLength=253
185- ServiceName *string `json:"serviceName,omitempty"`
186+ // +kubebuilder:default="service"
187+ Kind *string `json:"kind"`
188
189- // BackendRef is a reference to a backend to forward matched requests to. If
190- // both BackendRef and ServiceName are specified, ServiceName will be given
191- // precedence.
192- //
193- // If the referent cannot be found, the rule is not included in the route.
194- // The controller should raise the "ResolvedRefs" condition on the Gateway
195- // with the "DegradedRoutes" reason. The gateway status for this route should
196- // be updated with a condition that describes the error more specifically.
197+ // Group is the group of the backend resource.
198 //
199- // Support: Custom
200- //
201- // +optional
202- BackendRef *LocalObjectReference `json:"backendRef,omitempty"`
203+ // +kubebuilder:validation:MinLength=1
204+ // +kubebuilder:validation:MaxLength=253
205+ Group string `json:"group"`
206
207 // Port specifies the destination port number to use for the
208- // backend referenced by the ServiceName or BackendRef field.
209- // If unspecified, the destination port in the request is used
210- // when forwarding to a backendRef or serviceName.
211+ // backend resource.
212+ // This field is required when the backend is a Kubernetes Service.
213 //
214 // Support: Core
215 //
216@@ -146,11 +134,10 @@ type RouteForwardTo struct {
217 Port *PortNumber `json:"port,omitempty"`
218
219 // Weight specifies the proportion of HTTP requests forwarded to the backend
220- // referenced by the ServiceName or BackendRef field. This is computed as
221- // weight/(sum of all weights in this ForwardTo list). For non-zero values,
222- // there may be some epsilon from the exact proportion defined here
223- // depending on the precision an implementation supports. Weight is not a
224- // percentage and the sum of weights does not need to equal 100.
225+ // This is computed as weight/(sum of all weights in this Backends list).
226+ // For non-zero values, there may be some epsilon from the exact proportion
227+ // defined here depending on the precision an implementation supports. Weight
228+ // is not a percentage and the sum of weights does not need to equal 100.
229 //
230 // If only one backend is specified and it has a weight greater than 0, 100%
231 // of the traffic is forwarded to that backend. If weight is set to 0, no
232```
233
234## Examples
235
236
237For Kubernetes Services, an updated `forwardTo` section will read as follows:
238
239```yaml
240...
241backendRefs:
242- name: foo-service-v1
243 port: 80
244 weight: 80
245- name: foo-service-canary
246 port: 80
247 weight: 20
248...
249```
250
251Here, the `kind` field is omitted as it will be injected as a default.
252
253For custom backends, the API will look like the following:
254
255```yaml
256...
257backendRefs:
258- name: foo-v1
259 kind: server
260 group: networking.acme.io
261 port: 80
262 weight: 80
263- name: foo-v1-canary
264 kind: server
265 group: networking.acme.io
266 port: 80
267 weight: 20
268...
269```
270
271For completeness, here is an example of HTTPRoute:
272
273```yaml
274apiVersion: gateway.networking.k8s.io/v1alpha2
275kind: HTTPRoute
276metadata:
277 name: bar-route
278 labels:
279 gateway: prod-web-gw
280spec:
281 hostnames:
282 - "bar.example.com"
283 rules:
284 - matches:
285 - headers:
286 type: Exact
287 values:
288 env: canary
289 backendRefs:
290 - name: foo-service-v1
291 port: 80
292 weight: 80
293 - name: foo-service-canary
294 port: 80
295 weight: 20
296```
297
298## Alternatives considered
299
300### Rename serviceName to backendName
301
302As suggested in [this comment](https://github.com/kubernetes-sigs/gateway-api/pull/685#discussion_r667285837),
303we could buy the best of both the worlds by introducing `backendName`:
304
305```yaml
306forwardTo:
307- backendName: foo
308 port: 80
309 kind: <defaults to Service>
310```
311
312While this saves one line of YAML (`backendRef:`), it could be potentially
313violating the `Naming of the reference field`
314[API convention][api-conventions].
315Most of our object references are of the form `XRef`.
316
317## Concerns resolved
318
319A concern was raised around flattening of object reference fields i.e. including
320port and weight field alongside object reference is by k8s API conventions or not.
321This is not a problem and has been confirmed with API maintainers
322([comment](https://github.com/kubernetes-sigs/gateway-api/pull/719#discussion_r678555744)).
323
324## Out of scope
325
326N/A
327
328## References
329
330Existing documentation:
331- [RouteForwardTo](https://github.com/kubernetes-sigs/gateway-api/blob/a9d45b51c396fbed022204af0185b00a4ac7e282/apis/v1alpha2/shared_types.go#L97-L167)
332- [HTTPRouteForwardTo](https://github.com/kubernetes-sigs/gateway-api/blob/a9d45b51c396fbed022204af0185b00a4ac7e282/apis/v1alpha2/httproute_types.go#L731-L813)
333- [#685](https://github.com/kubernetes-sigs/gateway-api/pull/685)
334 - [Comment thread that spawned this GEP](https://github.com/kubernetes-sigs/gateway-api/pull/685#discussion_r640204333)
335- [#578](https://github.com/kubernetes-sigs/gateway-api/issues/578)
336
337[api-conventions]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#naming-of-the-reference-field
View as plain text