Skip to content

Commit 8dc0123

Browse files
fix(router): readiness logic for multiple ctrl statuses (#908)
* chore: simplifies condition assertions by using custom funcs Signed-off-by: Bartosz Majsak <[email protected]> * fix(router): readiness logic for multiple ctrl statuses When `HTTPRoute` has status from multiple controllers, current logic fails to recognize it, expecting status only from gateway controller with top-level standard condition `Accepted` being `true`. This change handles scenario of the status with multiple controllers updates, adjusting the logic to check for standard condition. If `gatewayapi.RouteConditionAccepted` is not found in all reported conditions it will be reported as missing. Example: https://gist.github.com/bartoszmajsak/4329206afe107357afdcb9b92ed778bd Signed-off-by: Bartosz Majsak <[email protected]> --------- Signed-off-by: Bartosz Majsak <[email protected]>
1 parent fd1c014 commit 8dc0123

File tree

3 files changed

+283
-81
lines changed

3 files changed

+283
-81
lines changed

pkg/controller/llmisvc/fixture/gwapi_builders.go

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -479,29 +479,77 @@ func WithGatewayReadyStatus() GatewayOption {
479479

480480
// Advanced fixture patterns for custom conditions
481481

482-
// WithCustomHTTPRouteConditions allows setting custom conditions on HTTPRoute parent status
483-
func WithCustomHTTPRouteConditions(parentRef gatewayapi.ParentReference, controllerName string, conditions ...metav1.Condition) HTTPRouteOption {
484-
return WithHTTPRouteParentStatus(parentRef, controllerName, conditions...)
482+
// KuadrantControllerStatus creates a RouteParentStatus for Kuadrant policy controller
483+
func KuadrantControllerStatus(parentRef gatewayapi.ParentReference, generation int64) gatewayapi.RouteParentStatus {
484+
return gatewayapi.RouteParentStatus{
485+
ParentRef: parentRef,
486+
ControllerName: "kuadrant.io/policy-controller",
487+
Conditions: []metav1.Condition{
488+
{
489+
Type: "kuadrant.io/AuthPolicyAffected",
490+
Status: metav1.ConditionTrue,
491+
Reason: "Accepted",
492+
Message: "Object affected by AuthPolicy [openshift-ingress/gateway-auth-policy]",
493+
LastTransitionTime: metav1.Now(),
494+
ObservedGeneration: generation,
495+
},
496+
{
497+
Type: "kuadrant.io/RateLimitPolicyAffected",
498+
Status: metav1.ConditionTrue,
499+
Reason: "Accepted",
500+
Message: "Object affected by RateLimitPolicy [openshift-ingress/gateway-rate-limits]",
501+
LastTransitionTime: metav1.Now(),
502+
ObservedGeneration: generation,
503+
},
504+
{
505+
Type: "kuadrant.io/TokenRateLimitPolicyAffected",
506+
Status: metav1.ConditionTrue,
507+
Reason: "Accepted",
508+
Message: "Object affected by TokenRateLimitPolicy [openshift-ingress/gateway-token-rate-limits]",
509+
LastTransitionTime: metav1.Now(),
510+
ObservedGeneration: generation,
511+
},
512+
},
513+
}
485514
}
486515

487-
// WithGatewayNotReadyStatus sets the Gateway status to not ready (for negative testing)
488-
func WithGatewayNotReadyStatus(reason, message string) GatewayOption {
489-
return func(gw *gatewayapi.Gateway) {
490-
gw.Status.Conditions = []metav1.Condition{
516+
// GatewayAPIControllerStatus creates a RouteParentStatus for Gateway API controller
517+
func GatewayAPIControllerStatus(parentRef gatewayapi.ParentReference, generation int64) gatewayapi.RouteParentStatus {
518+
return gatewayapi.RouteParentStatus{
519+
ParentRef: parentRef,
520+
ControllerName: "openshift.io/gateway-controller/v1",
521+
Conditions: []metav1.Condition{
491522
{
492-
Type: string(gatewayapi.GatewayConditionAccepted),
523+
Type: string(gatewayapi.RouteConditionAccepted),
493524
Status: metav1.ConditionTrue,
494525
Reason: "Accepted",
495-
Message: "Gateway accepted",
526+
Message: "Route was valid",
496527
LastTransitionTime: metav1.Now(),
528+
ObservedGeneration: generation,
497529
},
498530
{
499-
Type: string(gatewayapi.GatewayConditionProgrammed),
500-
Status: metav1.ConditionFalse,
501-
Reason: reason,
502-
Message: message,
531+
Type: string(gatewayapi.RouteConditionResolvedRefs),
532+
Status: metav1.ConditionTrue,
533+
Reason: "ResolvedRefs",
534+
Message: "All references resolved",
503535
LastTransitionTime: metav1.Now(),
536+
ObservedGeneration: generation,
504537
},
538+
},
539+
}
540+
}
541+
542+
// StatusFunc is a function that creates a RouteParentStatus for a given parent ref and generation
543+
type StatusFunc func(parentRef gatewayapi.ParentReference, generation int64) gatewayapi.RouteParentStatus
544+
545+
// WithHTTPRouteMultipleControllerStatus sets HTTPRoute status with multiple controllers
546+
// This simulates real-world scenarios where policy controllers and gateway controllers
547+
// both add status entries for the same parent ref
548+
func WithHTTPRouteMultipleControllerStatus(parentRef gatewayapi.ParentReference, statusFuncs ...StatusFunc) HTTPRouteOption {
549+
return func(route *gatewayapi.HTTPRoute) {
550+
for _, statusFunc := range statusFuncs {
551+
status := statusFunc(parentRef, route.Generation)
552+
route.Status.RouteStatus.Parents = append(route.Status.RouteStatus.Parents, status)
505553
}
506554
}
507555
}

pkg/controller/llmisvc/router_discovery.go

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -206,16 +206,6 @@ func IsExternalAddressNotFound(err error) bool {
206206
return errors.As(err, &externalAddrNotFoundErr)
207207
}
208208

209-
func filter[T any](s []T, predicateFn func(T) bool) []T {
210-
out := make([]T, 0, len(s))
211-
for _, x := range s {
212-
if predicateFn(x) {
213-
out = append(out, x)
214-
}
215-
}
216-
return out
217-
}
218-
219209
// EvaluateGatewayReadiness checks the readiness status of Gateways and returns those that are not ready
220210
func EvaluateGatewayReadiness(ctx context.Context, gateways []*gatewayapi.Gateway) []*gatewayapi.Gateway {
221211
logger := log.FromContext(ctx)
@@ -269,11 +259,6 @@ func IsHTTPRouteReady(route *gatewayapi.HTTPRoute) bool {
269259
return false
270260
}
271261

272-
if len(route.Status.RouteStatus.Parents) != len(route.Spec.ParentRefs) {
273-
// HTTPRoute is ready only when _all_ parents have accepted the route.
274-
return false
275-
}
276-
277262
if cond, missing := nonReadyHTTPRouteTopLevelCondition(route); cond != nil || missing {
278263
return false
279264
}
@@ -286,18 +271,24 @@ func nonReadyHTTPRouteTopLevelCondition(route *gatewayapi.HTTPRoute) (*metav1.Co
286271
return nil, true
287272
}
288273

274+
routeConditionAcceptedMissing := true
275+
289276
for _, parent := range route.Status.RouteStatus.Parents {
290277
cond := meta.FindStatusCondition(parent.Conditions, string(gatewayapi.RouteConditionAccepted))
291278
if cond == nil {
292-
return nil, true
279+
// This can happen when multiple controllers write to the status, e.g., besides the gateway controller, there
280+
// are conditions reported from the policy controller.
281+
// See example https://gist.github.com/bartoszmajsak/4329206afe107357afdcb9b92ed778bd
282+
continue
293283
}
284+
routeConditionAcceptedMissing = false
294285
staleCondition := cond.ObservedGeneration > 0 && cond.ObservedGeneration < route.Generation
295286
if cond.Status != metav1.ConditionTrue || staleCondition {
296287
return cond, false
297288
}
298289
}
299290

300-
return nil, false
291+
return nil, routeConditionAcceptedMissing
301292
}
302293

303294
// IsInferencePoolReady checks if an InferencePool has been accepted by all parents.

0 commit comments

Comments
 (0)