Skip to content

Commit f863e99

Browse files
authored
controller: isolate invalid backends in config propagation (#1174)
**Description** This changes the logic in the config construction in Gateway object controller where we collect all the backends as well as the attached BSP in a way that invalid backend/bsp won't block others to be reconciled. **Related Issues/PRs (if applicable)** Fixes #1122 Signed-off-by: Takeshi Yoneda <[email protected]>
1 parent 0dea552 commit f863e99

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

internal/controller/gateway.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ func (c *GatewayController) reconcileFilterConfigSecret(ctx context.Context, con
166166
for i := range aiGatewayRoutes {
167167
aiGatewayRoute := &aiGatewayRoutes[i]
168168
spec := aiGatewayRoute.Spec
169-
for i := range spec.Rules {
170-
rule := &spec.Rules[i]
169+
for ruleIndex := range spec.Rules {
170+
rule := &spec.Rules[ruleIndex]
171171
for _, m := range rule.Matches {
172172
for _, h := range m.Headers {
173173
// If explicitly set to something that is not an exact match, skip.
@@ -184,10 +184,10 @@ func (c *GatewayController) reconcileFilterConfigSecret(ctx context.Context, con
184184
})
185185
}
186186
}
187-
for j := range rule.BackendRefs {
188-
backendRef := &rule.BackendRefs[j]
187+
for backendRefIndex := range rule.BackendRefs {
188+
backendRef := &rule.BackendRefs[backendRefIndex]
189189
b := filterapi.Backend{}
190-
b.Name = internalapi.PerRouteRuleRefBackendName(aiGatewayRoute.Namespace, backendRef.Name, aiGatewayRoute.Name, i, j)
190+
b.Name = internalapi.PerRouteRuleRefBackendName(aiGatewayRoute.Namespace, backendRef.Name, aiGatewayRoute.Name, ruleIndex, backendRefIndex)
191191
b.ModelNameOverride = backendRef.ModelNameOverride
192192
if backendRef.IsInferencePool() {
193193
// We assume that InferencePools are all OpenAI schema.
@@ -197,14 +197,20 @@ func (c *GatewayController) reconcileFilterConfigSecret(ctx context.Context, con
197197
var bsp *aigv1a1.BackendSecurityPolicy
198198
backendObj, bsp, err = c.backendWithMaybeBSP(ctx, aiGatewayRoute.Namespace, backendRef.Name)
199199
if err != nil {
200-
return fmt.Errorf("failed to get AIServiceBackend %s: %w", b.Name, err)
200+
c.logger.Error(err, "failed to get backend or backend security policy. Skipping this backend.",
201+
"backend_name", backendRef.Name, "aigatewayroute", aiGatewayRoute.Name,
202+
"namespace", aiGatewayRoute.Namespace)
203+
continue
201204
}
202205
b.HeaderMutation = headerMutationToFilterAPI(backendObj.Spec.HeaderMutation)
203206
b.Schema = schemaToFilterAPI(backendObj.Spec.APISchema)
204207
if bsp != nil {
205208
b.Auth, err = c.bspToFilterAPIBackendAuth(ctx, bsp)
206209
if err != nil {
207-
return fmt.Errorf("failed to create backend auth: %w", err)
210+
c.logger.Error(err, "failed to get backend auth from backend security policy. Skipping this backend.",
211+
"backend_name", backendRef.Name, "backend_security_policy", bsp.Name,
212+
"aigatewayroute", aiGatewayRoute.Name, "namespace", aiGatewayRoute.Namespace)
213+
continue
208214
}
209215
}
210216
}

internal/controller/gateway_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ func TestGatewayController_reconcileFilterConfigSecret(t *testing.T) {
163163
kube := fake2.NewClientset()
164164
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true, Level: zapcore.DebugLevel})))
165165
c := NewGatewayController(fakeClient, kube, ctrl.Log,
166-
167166
"docker.io/envoyproxy/ai-gateway-extproc:latest", false, nil)
168167

169168
const gwNamespace = "ns"
@@ -173,7 +172,11 @@ func TestGatewayController_reconcileFilterConfigSecret(t *testing.T) {
173172
Spec: aigv1a1.AIGatewayRouteSpec{
174173
Rules: []aigv1a1.AIGatewayRouteRule{
175174
{
176-
BackendRefs: []aigv1a1.AIGatewayRouteRuleBackendRef{{Name: "apple"}},
175+
BackendRefs: []aigv1a1.AIGatewayRouteRuleBackendRef{
176+
{Name: "apple"},
177+
{Name: "invalid-bsp-backend"}, // This should be ignored as the BSP is invalid.
178+
{Name: "non-existent-backend"}, // This should be ignored as the backend does not exist.
179+
},
177180
Matches: []aigv1a1.AIGatewayRouteRuleMatch{
178181
{
179182
Headers: []gwapiv1.HTTPHeaderMatch{
@@ -221,11 +224,36 @@ func TestGatewayController_reconcileFilterConfigSecret(t *testing.T) {
221224
BackendRef: gwapiv1.BackendObjectReference{Name: "some-backend1", Namespace: ptr.To[gwapiv1.Namespace](gwNamespace)},
222225
},
223226
},
227+
{
228+
ObjectMeta: metav1.ObjectMeta{Name: "invalid-bsp-backend", Namespace: gwNamespace},
229+
Spec: aigv1a1.AIServiceBackendSpec{
230+
BackendRef: gwapiv1.BackendObjectReference{Name: "some-backend1", Namespace: ptr.To[gwapiv1.Namespace](gwNamespace)},
231+
},
232+
},
224233
} {
225234
err := fakeClient.Create(t.Context(), aigwRoute)
226235
require.NoError(t, err)
227236
}
228237

238+
// Create a BackendSecurityPolicy that is invalid (missing secret ref).
239+
err := fakeClient.Create(t.Context(), &aigv1a1.BackendSecurityPolicy{
240+
ObjectMeta: metav1.ObjectMeta{Name: "invalid-bsp", Namespace: gwNamespace},
241+
Spec: aigv1a1.BackendSecurityPolicySpec{
242+
Type: aigv1a1.BackendSecurityPolicyTypeAPIKey,
243+
APIKey: &aigv1a1.BackendSecurityPolicyAPIKey{
244+
SecretRef: &gwapiv1.SecretObjectReference{Name: "non-existent-secret"},
245+
},
246+
TargetRefs: []gwapiv1a2.LocalPolicyTargetReference{
247+
{
248+
Kind: "AIServiceBackend",
249+
Group: "aigateway.envoyproxy.io",
250+
Name: "invalid-bsp-backend",
251+
},
252+
},
253+
},
254+
})
255+
require.NoError(t, err)
256+
229257
for range 2 { // Reconcile twice to make sure the secret update path is working.
230258
const someNamespace = "some-namespace"
231259
configName := FilterConfigSecretPerGatewayName("gw", gwNamespace)

0 commit comments

Comments
 (0)