Skip to content

Commit 7e4845c

Browse files
committed
skip deleted aigatewayroutes for extproc secret updates
Signed-off-by: siddharth1036 <[email protected]>
1 parent 8a739ab commit 7e4845c

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

internal/controller/gateway.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ func (c *GatewayController) reconcileFilterConfigSecret(ctx context.Context, con
164164
var err error
165165
llmCosts := map[string]struct{}{}
166166
for i := range aiGatewayRoutes {
167+
if !aiGatewayRoutes[i].GetDeletionTimestamp().IsZero() {
168+
c.logger.Info("AIGatewayRoute is being deleted, skipping exptroc secret update", "namespace", aiGatewayRoutes[i].Namespace, "name", aiGatewayRoutes[i].Name)
169+
continue
170+
}
167171
aiGatewayRoute := &aiGatewayRoutes[i]
168172
spec := aiGatewayRoute.Spec
169173
for ruleIndex := range spec.Rules {

internal/controller/gateway_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,116 @@ func TestGatewayController_reconcileFilterConfigSecret(t *testing.T) {
283283
}
284284
}
285285

286+
func TestGatewayController_reconcileFilterConfigSecret_SkipsDeletedRoutes(t *testing.T) {
287+
fakeClient := requireNewFakeClientWithIndexes(t)
288+
kube := fake2.NewClientset()
289+
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true, Level: zapcore.DebugLevel})))
290+
c := NewGatewayController(fakeClient, kube, ctrl.Log,
291+
"docker.io/envoyproxy/ai-gateway-extproc:latest", false, nil)
292+
293+
const gwNamespace = "ns"
294+
now := metav1.Now()
295+
296+
// Create routes: one active, one being deleted.
297+
routes := []aigv1a1.AIGatewayRoute{
298+
{
299+
ObjectMeta: metav1.ObjectMeta{
300+
Name: "active-route",
301+
Namespace: gwNamespace,
302+
DeletionTimestamp: nil, // Active route.
303+
},
304+
Spec: aigv1a1.AIGatewayRouteSpec{
305+
Rules: []aigv1a1.AIGatewayRouteRule{
306+
{
307+
BackendRefs: []aigv1a1.AIGatewayRouteRuleBackendRef{
308+
{Name: "apple"},
309+
},
310+
Matches: []aigv1a1.AIGatewayRouteRuleMatch{
311+
{
312+
Headers: []gwapiv1.HTTPHeaderMatch{
313+
{
314+
Name: aigv1a1.AIModelHeaderKey,
315+
Value: "mymodel",
316+
},
317+
},
318+
},
319+
},
320+
},
321+
},
322+
},
323+
},
324+
{
325+
ObjectMeta: metav1.ObjectMeta{
326+
Name: "deleting-route",
327+
Namespace: gwNamespace,
328+
DeletionTimestamp: &now, // Route being deleted.
329+
},
330+
Spec: aigv1a1.AIGatewayRouteSpec{
331+
Rules: []aigv1a1.AIGatewayRouteRule{
332+
{
333+
BackendRefs: []aigv1a1.AIGatewayRouteRuleBackendRef{
334+
{Name: "orange"},
335+
},
336+
Matches: []aigv1a1.AIGatewayRouteRuleMatch{
337+
{
338+
Headers: []gwapiv1.HTTPHeaderMatch{
339+
{
340+
Name: aigv1a1.AIModelHeaderKey,
341+
Value: "deletedmodel",
342+
},
343+
},
344+
},
345+
},
346+
},
347+
},
348+
},
349+
},
350+
}
351+
352+
// Create AIServiceBackends for both routes.
353+
for _, backend := range []*aigv1a1.AIServiceBackend{
354+
{
355+
ObjectMeta: metav1.ObjectMeta{Name: "apple", Namespace: gwNamespace},
356+
Spec: aigv1a1.AIServiceBackendSpec{
357+
BackendRef: gwapiv1.BackendObjectReference{Name: "some-backend1", Namespace: ptr.To[gwapiv1.Namespace](gwNamespace)},
358+
},
359+
},
360+
{
361+
ObjectMeta: metav1.ObjectMeta{Name: "orange", Namespace: gwNamespace},
362+
Spec: aigv1a1.AIServiceBackendSpec{
363+
BackendRef: gwapiv1.BackendObjectReference{Name: "some-backend2", Namespace: ptr.To[gwapiv1.Namespace](gwNamespace)},
364+
},
365+
},
366+
} {
367+
err := fakeClient.Create(t.Context(), backend)
368+
require.NoError(t, err)
369+
}
370+
371+
const someNamespace = "some-namespace"
372+
configName := FilterConfigSecretPerGatewayName("gw", gwNamespace)
373+
374+
// Reconcile filter config secret.
375+
err := c.reconcileFilterConfigSecret(t.Context(), configName, someNamespace, routes, "foouuid")
376+
require.NoError(t, err)
377+
378+
// Verify the secret was created and only contains data from the active route.
379+
secret, err := kube.CoreV1().Secrets(someNamespace).Get(t.Context(), configName, metav1.GetOptions{})
380+
require.NoError(t, err)
381+
configStr, ok := secret.StringData[FilterConfigKeyInSecret]
382+
require.True(t, ok)
383+
384+
var fc filterapi.Config
385+
require.NoError(t, yaml.Unmarshal([]byte(configStr), &fc))
386+
387+
// Should only have one model (from the active route), not two (deleted route should be skipped).
388+
require.Len(t, fc.Models, 1)
389+
require.Equal(t, "mymodel", fc.Models[0].Name)
390+
391+
// Should only have one backend (from the active route).
392+
require.Len(t, fc.Backends, 1)
393+
require.Contains(t, fc.Backends[0].Name, "apple")
394+
}
395+
286396
func TestGatewayController_bspToFilterAPIBackendAuth(t *testing.T) {
287397
fakeClient := requireNewFakeClientWithIndexes(t)
288398
kube := fake2.NewClientset()

0 commit comments

Comments
 (0)