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