sigs.k8s.io/gateway-api@v1.0.0/geps/gep-718.md (about) 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 10 Within `RouteForwardTo` (and `HTTPRouteForwardTo` by extension) there exists 11 two mechanisms to specify the upstream network endpoint where the traffic should 12 be 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 16 While working on [#685](https://github.com/kubernetes-sigs/gateway-api/pull/685), 17 it came to light that: 18 1. `BackendRef` itself could be used to point to a Kubernetes Service 19 2. With [GEP-709](https://github.com/kubernetes-sigs/gateway-api/pull/711), 20 there will be a need to introduce a new `namespace` field which will enable use-cases to forward 21 traffic to a services and backends located in different namespaces 22 23 While (1) can be fixed with more validation and documentation, it only solves for 24 the specific case. (2) requires adding two fields: `serviceNamespace` 25 and `BackendRef.Namespace` - something we would like to avoid since the `serviceName` 26 field was introduced only as a shortcut and adding more service-specific fields 27 defeats the original purpose. 28 29 These problems are in addition to the original problem that 30 [#685](https://github.com/kubernetes-sigs/gateway-api/pull/685) attempts to solve: 31 clarifying port requirements when a port is required or not. 32 We have been updating documentation to tackle such corner-cases but that 33 continues to result in more and more elaborations. Some excerpts from our 34 existing 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 56 RouteForwardTo is not the best of the names. Using a noun instead of a verb 57 would help with in-person communication and documentation. 58 Since we are already considering dropping the special case of `service` as a 59 backend, we have an opportunity to further simplify `RouteForwardTo` for better 60 UX. 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 74 The 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. 79 type 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 138 A summarized diff for the changes being proposed: 139 140 ```patch 141 diff --git a/apis/v1alpha1/shared_types.go b/apis/v1alpha1/shared_types.go 142 index 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 237 For Kubernetes Services, an updated `forwardTo` section will read as follows: 238 239 ```yaml 240 ... 241 backendRefs: 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 251 Here, the `kind` field is omitted as it will be injected as a default. 252 253 For custom backends, the API will look like the following: 254 255 ```yaml 256 ... 257 backendRefs: 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 271 For completeness, here is an example of HTTPRoute: 272 273 ```yaml 274 apiVersion: gateway.networking.k8s.io/v1alpha2 275 kind: HTTPRoute 276 metadata: 277 name: bar-route 278 labels: 279 gateway: prod-web-gw 280 spec: 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 302 As suggested in [this comment](https://github.com/kubernetes-sigs/gateway-api/pull/685#discussion_r667285837), 303 we could buy the best of both the worlds by introducing `backendName`: 304 305 ```yaml 306 forwardTo: 307 - backendName: foo 308 port: 80 309 kind: <defaults to Service> 310 ``` 311 312 While this saves one line of YAML (`backendRef:`), it could be potentially 313 violating the `Naming of the reference field` 314 [API convention][api-conventions]. 315 Most of our object references are of the form `XRef`. 316 317 ## Concerns resolved 318 319 A concern was raised around flattening of object reference fields i.e. including 320 port and weight field alongside object reference is by k8s API conventions or not. 321 This 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 326 N/A 327 328 ## References 329 330 Existing 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