Skip to content

Route handling of invalid BackendRefs needs clarification #1112

Closed
@mikemorris

Description

@mikemorris

What would you like to be added:

Add documentation, possibly a new page in the Reference section of the website, and additional RouteConditionReason and ListenerConditionReason types as needed to clarify intended handling of invalid BackendRefs configurations. This is a bit more complicated than just #1109, as the implications of initially applying or resolving changes to a Route's BackendRefs or a ReferencePolicy may require triggering actions or settings statuses across several objects.

Why this is needed:

There are a few distinct cases by which a BackendRef could be or become invalid, and I think it may be helpful to define and use more specific terms than "invalid" in the spec, as well as centralize and specify the intended behavior for each case more explicitly.

One specific difficulty is the lack of specificity around when a Route Accepted condition should be set, and whether an Accepted { status: false, reason: Invalid } condition should be set in any of these cases (and/or reflected in a ListenerConditionType status) given the lack of granularity in indicating status per BackendRef versus the status of the Route or Listener as a whole.

It's also slightly unclear if attachedRoutes on ListenerStatus is intended to communicate the same thing as a route having an Accepted { status: true, reason: Accepted } route parent status condition referencing that listener because of the difference between the words used ("attached" vs "accepted", refs #1111).

"not accepted"

This case is when the Route has not been accepted by a Gateway Listener. The HTTPRouteInvalidCrossNamespace conformance test illustrates one example where this is an intended outcome, but it's not clear how this status should be communicated. The test suggests that no RouteParentStatus should be set on the Route because, having been rejected, it does not have an actual parent, only an intended parent - setting an Accepted { status: False } condition (what RouteConditionReason?) could be an alternative way to communicate this status though.

RouteParentStatus conditions

  • None?
  • [Alternatively?] Accepted { status: False }

Listener handling

  • A Route that is in a namespace not allowed by a Gateway Listener's allowedRoutes should be rejected by the GatewayController.
  • This is similar to the InvalidRouteKinds case, but I'm guessing that no similar status should be set due to cross-namespace security concerns because a Route in another namespace shouldn't be able to have any impact on a Gateway to which it is not allowed to bind.

ListenerStatus

  • attachedRoutes should not count this Route
  • Conditions:
    • Ready { status: True, reason: Ready }

TODO:

  • Update the HTTPRouteInvalidCrossNamespace conformance test to check for an Accepted {status: False} condition. This feels reasonable enough because even though the route was not accepted by the gateway (and therefore shouldn't be able to affect the gateway in any way), there should be a definitive controller which evaluated and rejected the route which has sufficient privileges to manage a route status appropriately.
  • Add an InvalidRouteKind RouteConditionReason and update the HTTPRouteDisallowedKind conformance test for communicating the case where a route is rejected because its kind is not specified in the allowedRoutes.kinds field of the listener it is targeting.
  • Add an InvalidRouteNamespace RouteConditionReason and conformance test for communicating the cases (Selector, Same) where a route is rejected because it lives in a namespace that is not allowed by the allowedRoutes.namespaces field of the listener it is targeting.
  • Add an InvalidRouteHostname RouteConditionReason and update the HTTPRouteListenerHostnameMatching conformance test or add a new one for communicating the case where a route is rejected because it does not have at least one intersecting hostname with the listener it is targeting.

"unpermitted"

This case is when a Route has a BackendRef to an object in another namespace, where the object in the other namespace does not have a ReferencePolicy explicitly allowing the reference. This could reasonably be expected to be checked when the Route is created, and should be clarified to note that it should additionally be checked whenever the Route is modified or a ReferencePolicy with a to field resolving to a BackendRef of the route is deleted, modified, or created.

This should be expected to be dynamic - revoking a ReferencePolicy should stop routing traffic to a BackendRef.

RouteParentStatus conditions

  • Accepted { status: True }
  • ResolvedRefs { status: False, reason: RefNotPermitted }
  • Ready { status: False, reason: Invalid } [Not Implemented]

Listener handling

  • As described in HTTPBackendRef

    If there is a cross-namespace reference to an existing object that is not covered by a ReferencePolicy, the controller must ensure the “ResolvedRefs” condition on the Route is set to status: False, with the “RefNotPermitted” reason and not configure this backend in the underlying implementation.

  • This wording is likewise a bit confusing in that it only describes how each specific BackendRef should be handled, and not whether this should impact if the entire Route is determined to be "invalid". Notably, this case omits any mention of "dropping" anything from a Gateway as the "not found" case describes.

  • Should likely implement the same behavior described above in the "not found" section for sending HTTP 503 responses for invalid unpermitted BackendRefs.

ListenerStatus

  • attachedRoutes should count this Route as long as it has RouteConditionType Accepted { status: True }
  • Conditions:
    • ResolvedRefs { status: False, reason: RefNotPermitted }
    • UNRESOLVED: Should this set Ready { status: False, reason: Invalid } if any single BackendRef on an accepted route is not permitted?

TODO:

  • UNRESOLVED: (needs consensus before implementation) Update the HTTPRouteInvalidCrossNamespaceBackendRef conformance test to assert that a route with zero permitted backends:
    • sets either an Accepted {status: True} or Accepted {status: False} route condition
    • is either counted (if accepted) or not counted (if rejected) in listener attachedRoutes
    • sets a ResolvedRefs {status: False, reason: ReasonNotPermitted} listener condition if route issues of accepted routes should "bubble up" to listeners
  • conformance: add test for partial acceptance of a route with one allowed and one unpermitted backend reference #1143

"not found"

This case is when the Route has at least one BackendRef that does not exist or cannot be resolved. This could reasonably be expected to be checked when the Route is created, but unclear if/when state changes could cause this to need to be updated.

UNRESOLVED:

  • Does this case include zero service instances ready?
  • It's unclear if this case should prevent the Route from being accepted by a Gateway when initially applied, even if all BackendRefs on a Route are not found. I could see potential value in a use case for being able to configure Routes before deploying a new service.

RouteParentStatus conditions

  • ResolvedRefs { status: False, reason: InvalidBackendRef }

Listener handling

  • As described in HTTPBackendRef:

    If the referent cannot be found, this HTTPBackendRef is invalid and must be dropped from the Gateway. The controller must ensure the “ResolvedRefs” condition on the Route is set to status: False and not configure this backend in the underlying implementation.

  • The part that's a bit confusing with this wording is that it only describes how each specific BackendRef should be handled, and not whether this should impact if the entire Route is determined to be "invalid". The term "dropped" seems to imply active management of some object directly related to a Gateway (as opposed to the underlying implementation), which seems to be overly broad behavior if inferred to mean removing an entire Route from a Listener if one BackendRef became invalid.

  • The backendRefs field on HTTPRouteRule additionally states:

    If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the rule performs no forwarding. If there are also no filters specified that would result in a response being sent, a HTTP 503 status code is returned. 503 responses must be sent so that the overall weight is respected; if an invalid backend is requested to have 80% of requests, then 80% of requests must get a 503 instead.

    (cobbling this information together from disjointed sources is an example of the difficulty described in docs: API specification information architecture obscures important content #1031)

ListenerStatus

  • attachedRoutes should count this Route as long as it has RouteConditionType Accepted { status: True }
  • Conditions
    • ResolvedRefs { status: False, reason: InvalidBackendRef }
      • Would require adding InvalidBackendRef ListenerConditionReason
      • UNRESOLVED: Should a route with this issue should affect listener status?
    • UNRESOLVED: Should this set Ready { status: False, reason: Invalid } if any single BackendRef on an accepted route is not found?

TODO:

  • Add InvalidBackendRef RouteConditionReason, with a description similar to the InvalidCertificateRef ListenerConditionReason
  • Clarify lifecycle watch expectations of when this status would be updated (service created/deleted, zero endpoints ready?)

"invalid"

This case is when the Route is syntactically or semantically invalid.

NOTE:

  • Ideally, the admission webhook should prevent this Route from ever being applied to avoid accidentally dropping traffic if modifying an existing Route.

RouteParentStatus conditions

  • Accepted { status: False, reason: Invalid | InvalidParameters }

Listener handling

  • A Route that is syntactically or semantically invalid should be rejected by the GatewayController or admission webhook.

ListenerStatus

  • attachedRoutes should not count this Route
  • Conditions:
    • Ready { status: True, reason: Ready }

"not ready yet"

This case is when the Route is syntactically or semantically valid, all BackendRefs exist, can be resolved, and are permitted by a ReferencePolicy as applicable, and the route has been accepted by the GatewayController, but the underlying implementation is not fully configured or routing traffic yet. Expected to be a transient state.

NOTE:

  • It can be quite difficult for implementations to know when a route has transitioned from this state to being fully "ready" and actively able to route traffic.

RouteParentStatus conditions

  • Accepted { status: True }
  • Ready { status: False, reason: Pending } [Not Implemented]

Listener handling

  • Not yet defined.

ListenerStatus

  • attachedRoutes should count this Route
  • Conditions:
    • Ready { status: True, reason: Ready }

Metadata

Metadata

Assignees

Labels

kind/featureCategorizes issue or PR as related to a new feature.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions