...

Text file src/sigs.k8s.io/gateway-api/geps/gep-718.md

Documentation: sigs.k8s.io/gateway-api/geps

     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