fix: gateway router should wait for accepted condition

It can take some time for changes to propagate for cloud load balancers,
so flagger should ensure the route changes are current before proceeding
with any more.

Signed-off-by: Steven Davidovitz <sdavidovitz@groq.com>
This commit is contained in:
Steven Davidovitz
2025-10-15 17:27:12 -07:00
parent ae72e15049
commit bb7ad65462
3 changed files with 125 additions and 1 deletions
+20
View File
@@ -273,6 +273,26 @@ func (gwr *GatewayAPIRouter) GetRoutes(canary *flaggerv1.Canary) (
err = fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err)
return
}
currentGeneration := httpRoute.GetGeneration()
for _, parentRef := range httpRoute.Spec.CommonRouteSpec.ParentRefs {
for _, parentStatus := range httpRoute.Status.Parents {
if !reflect.DeepEqual(parentStatus.ParentRef, parentRef) {
continue
}
for _, condition := range parentStatus.Conditions {
if condition.Type == string(v1.RouteConditionAccepted) && (condition.Status != metav1.ConditionTrue || condition.ObservedGeneration < currentGeneration) {
err = fmt.Errorf(
"HTTPRoute %s.%s parent %s is not ready (status: %s, observed generation: %d, current generation: %d)",
apexSvcName, hrNamespace, parentRef.Name, string(condition.Status), condition.ObservedGeneration, currentGeneration,
)
return 0, 0, false, err
}
}
}
}
var weightedRule *v1.HTTPRouteRule
for _, rule := range httpRoute.Spec.Rules {
// If session affinity is enabled, then we are only interested in the rule
+104
View File
@@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
v1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1"
@@ -603,3 +604,106 @@ func TestGatewayAPIRouter_makeFilters_CORS(t *testing.T) {
// Assert MaxAge (24h = 86400 seconds)
assert.Equal(t, int32(86400), corsFilter.CORS.MaxAge)
}
func TestGatewayAPIRouter_GetRoutes(t *testing.T) {
canary := newTestGatewayAPICanary()
mocks := newFixture(canary)
router := &GatewayAPIRouter{
gatewayAPIClient: mocks.meshClient,
kubeClient: mocks.kubeClient,
logger: mocks.logger,
}
httpRoute := &v1.HTTPRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "podinfo",
Generation: 1,
},
Spec: v1.HTTPRouteSpec{
Rules: []v1.HTTPRouteRule{
{
BackendRefs: []v1.HTTPBackendRef{
{
BackendRef: v1.BackendRef{
BackendObjectReference: v1.BackendObjectReference{
Name: "podinfo-canary",
},
Weight: ptr.To(int32(10)),
},
},
{
BackendRef: v1.BackendRef{
BackendObjectReference: v1.BackendObjectReference{
Name: "podinfo-primary",
},
Weight: ptr.To(int32(90)),
},
},
},
},
},
CommonRouteSpec: v1.CommonRouteSpec{
ParentRefs: []v1.ParentReference{
{
Name: "podinfo",
},
},
},
},
}
httpRoute, err := router.gatewayAPIClient.GatewayapiV1().HTTPRoutes("default").Create(context.TODO(), httpRoute, metav1.CreateOptions{})
require.NoError(t, err)
t.Run("httproute generation", func(t *testing.T) {
httpRoute.ObjectMeta.Generation = 5
httpRoute.Status.Parents = []v1.RouteParentStatus{
{
ParentRef: v1.ParentReference{
Name: "podinfo",
SectionName: ptr.To(v1.SectionName("https")),
},
Conditions: []metav1.Condition{
{
Type: string(v1.RouteConditionAccepted),
Status: metav1.ConditionTrue,
ObservedGeneration: 1,
},
},
},
{
ParentRef: v1.ParentReference{
Name: "podinfo",
},
Conditions: []metav1.Condition{
{
Type: string(v1.RouteConditionAccepted),
Status: metav1.ConditionFalse,
ObservedGeneration: 4,
},
},
},
}
httpRoute, err := router.gatewayAPIClient.GatewayapiV1().HTTPRoutes("default").Update(context.TODO(), httpRoute, metav1.UpdateOptions{})
require.NoError(t, err)
_, _, _, err = router.GetRoutes(canary)
require.Error(t, err)
httpRoute.Status.Parents[1].Conditions[0].ObservedGeneration = 5
_, err = router.gatewayAPIClient.GatewayapiV1().HTTPRoutes("default").Update(context.TODO(), httpRoute, metav1.UpdateOptions{})
require.NoError(t, err)
_, _, _, err = router.GetRoutes(canary)
require.Error(t, err)
httpRoute.Status.Parents[1].Conditions[0].Status = metav1.ConditionTrue
_, err = router.gatewayAPIClient.GatewayapiV1().HTTPRoutes("default").Update(context.TODO(), httpRoute, metav1.UpdateOptions{})
require.NoError(t, err)
primaryWeight, canaryWeight, mirrored, err := router.GetRoutes(canary)
require.NoError(t, err)
assert.Equal(t, 90, primaryWeight)
assert.Equal(t, 10, canaryWeight)
assert.False(t, mirrored)
})
}