@@ -3,6 +3,7 @@ package graph
3
3
import (
4
4
"fmt"
5
5
"slices"
6
+ "strings"
6
7
7
8
"github.com/go-logr/logr"
8
9
"k8s.io/apimachinery/pkg/types"
@@ -75,12 +76,16 @@ func validateBackendTLSPolicy(
75
76
valid = true
76
77
ignored = false
77
78
78
- // Note: Ancestor limit checking moved to addGatewaysForBackendTLSPolicies for per-gateway effectiveness tracking
79
- // The policy may be partially effective (work for some gateways but not others due to ancestor limits)
79
+ // Ancestor limit handling is now done during gateway assignment phase, not validation
80
+ // if backendTLSPolicyAncestorsFull(backendTLSPolicy.Status.Ancestors, ctlrName) {
81
+ // valid = false
82
+ // ignored = true
83
+ // return
84
+ // }
80
85
81
86
if err := validateBackendTLSHostname (backendTLSPolicy ); err != nil {
82
87
valid = false
83
- conds = append (conds , conditions .NewPolicyInvalid (fmt . Sprintf ( "invalid hostname: %s" , err .Error () )))
88
+ conds = append (conds , conditions .NewPolicyInvalid (err .Error ()))
84
89
}
85
90
86
91
caCertRefs := backendTLSPolicy .Spec .Validation .CACertificateRefs
@@ -182,61 +187,148 @@ func validateBackendTLSWellKnownCACerts(btp *v1alpha3.BackendTLSPolicy) error {
182
187
return nil
183
188
}
184
189
185
- func addGatewaysForBackendTLSPolicies (
186
- backendTLSPolicies map [types.NamespacedName ]* BackendTLSPolicy ,
190
+ // countNonNGFAncestors counts the number of non-NGF ancestors in policy status.
191
+ func countNonNGFAncestors (policy * v1alpha3.BackendTLSPolicy , ctlrName string ) int {
192
+ nonNGFCount := 0
193
+ for _ , ancestor := range policy .Status .Ancestors {
194
+ if string (ancestor .ControllerName ) != ctlrName {
195
+ nonNGFCount ++
196
+ }
197
+ }
198
+ return nonNGFCount
199
+ }
200
+
201
+ // addPolicyAncestorLimitCondition adds or updates a PolicyAncestorLimitReached condition.
202
+ func addPolicyAncestorLimitCondition (
203
+ conds []conditions.Condition ,
204
+ policyName string ,
205
+ policyType string ,
206
+ ) []conditions.Condition {
207
+ const policyAncestorLimitReachedType = "PolicyAncestorLimitReached"
208
+
209
+ for i , condition := range conds {
210
+ if condition .Type == policyAncestorLimitReachedType {
211
+ if ! strings .Contains (condition .Message , policyName ) {
212
+ conds [i ].Message = fmt .Sprintf ("%s, %s %s" , condition .Message , policyType , policyName )
213
+ }
214
+ return conds
215
+ }
216
+ }
217
+
218
+ newCondition := conditions .NewPolicyAncestorLimitReached ()
219
+ newCondition .Message = fmt .Sprintf ("%s %s %s" , newCondition .Message , policyType , policyName )
220
+ conds = append (conds , newCondition )
221
+ return conds
222
+ }
223
+
224
+ // collectOrderedGateways collects gateways in spec order (services) then creation time order (gateways within service).
225
+ func collectOrderedGateways (
226
+ policy * v1alpha3.BackendTLSPolicy ,
187
227
services map [types.NamespacedName ]* ReferencedService ,
188
- ctlrName string ,
189
228
gateways map [types.NamespacedName ]* Gateway ,
190
- logger logr.Logger ,
191
- ) {
192
- for _ , backendTLSPolicy := range backendTLSPolicies {
193
- potentialGateways := make (map [types.NamespacedName ]struct {})
229
+ existingNGFGatewayAncestors map [types.NamespacedName ]struct {},
230
+ ) (existingGateways []types.NamespacedName , newGateways []types.NamespacedName ) {
231
+ seenGateways := make (map [types.NamespacedName ]struct {})
232
+
233
+ // Process services in spec order to maintain deterministic gateway ordering
234
+ for _ , refs := range policy .Spec .TargetRefs {
235
+ if refs .Kind != kinds .Service {
236
+ continue
237
+ }
238
+
239
+ svcNsName := types.NamespacedName {
240
+ Namespace : policy .Namespace ,
241
+ Name : string (refs .Name ),
242
+ }
243
+
244
+ referencedService , exists := services [svcNsName ]
245
+ if ! exists {
246
+ continue
247
+ }
194
248
195
- // First, collect all potential gateways for this policy
196
- for _ , refs := range backendTLSPolicy . Source . Spec . TargetRefs {
197
- if refs . Kind != kinds . Service {
249
+ // Add to ordered lists, categorizing existing vs new, skipping duplicates
250
+ for gateway := range referencedService . GatewayNsNames {
251
+ if _ , seen := seenGateways [ gateway ]; seen {
198
252
continue
199
253
}
254
+ seenGateways [gateway ] = struct {}{}
255
+ if _ , exists := existingNGFGatewayAncestors [gateway ]; exists {
256
+ existingGateways = append (existingGateways , gateway )
257
+ } else {
258
+ newGateways = append (newGateways , gateway )
259
+ }
260
+ }
261
+ }
200
262
201
- for svcNsName , referencedServices := range services {
202
- if svcNsName .Name != string (refs .Name ) {
203
- continue
204
- }
263
+ sortGatewaysByCreationTime (existingGateways , gateways )
264
+ sortGatewaysByCreationTime (newGateways , gateways )
205
265
206
- for gateway := range referencedServices .GatewayNsNames {
207
- potentialGateways [gateway ] = struct {}{}
208
- }
266
+ return existingGateways , newGateways
267
+ }
268
+
269
+ func extractExistingNGFGatewayAncestors (
270
+ backendTLSPolicy * v1alpha3.BackendTLSPolicy ,
271
+ ctlrName string ,
272
+ ) map [types.NamespacedName ]struct {} {
273
+ existingNGFGatewayAncestors := make (map [types.NamespacedName ]struct {})
274
+
275
+ for _ , ancestor := range backendTLSPolicy .Status .Ancestors {
276
+ if string (ancestor .ControllerName ) != ctlrName {
277
+ continue
278
+ }
279
+
280
+ if ancestor .AncestorRef .Kind != nil && * ancestor .AncestorRef .Kind == v1 .Kind (kinds .Gateway ) &&
281
+ ancestor .AncestorRef .Namespace != nil {
282
+ gatewayNsName := types.NamespacedName {
283
+ Namespace : string (* ancestor .AncestorRef .Namespace ),
284
+ Name : string (ancestor .AncestorRef .Name ),
209
285
}
286
+ existingNGFGatewayAncestors [gatewayNsName ] = struct {}{}
210
287
}
288
+ }
289
+
290
+ return existingNGFGatewayAncestors
291
+ }
211
292
212
- // Now check each potential gateway against ancestor limits
213
- for gatewayNsName := range potentialGateways {
214
- // Create a proposed ancestor reference for this gateway
215
- proposedAncestor := createParentReference (v1 .GroupName , kinds .Gateway , gatewayNsName )
293
+ func addGatewaysForBackendTLSPolicies (
294
+ backendTLSPolicies map [types.NamespacedName ]* BackendTLSPolicy ,
295
+ services map [types.NamespacedName ]* ReferencedService ,
296
+ ctlrName string ,
297
+ gateways map [types.NamespacedName ]* Gateway ,
298
+ logger logr.Logger ,
299
+ ) {
300
+ for _ , backendTLSPolicy := range backendTLSPolicies {
301
+ existingNGFGatewayAncestors := extractExistingNGFGatewayAncestors (backendTLSPolicy .Source , ctlrName )
302
+ existingGateways , newGateways := collectOrderedGateways (
303
+ backendTLSPolicy .Source , services , gateways , existingNGFGatewayAncestors )
304
+
305
+ existingGateways = append (existingGateways , newGateways ... )
306
+ orderedGateways := existingGateways
216
307
217
- // Check ancestor limit for BackendTLS policy
218
- isFull := backendTLSPolicyAncestorsFull (
219
- backendTLSPolicy .Source .Status .Ancestors ,
220
- ctlrName ,
221
- )
308
+ ancestorCount := countNonNGFAncestors (backendTLSPolicy .Source , ctlrName )
222
309
223
- if isFull {
310
+ // Process each gateway, respecting ancestor limits
311
+ for _ , gatewayNsName := range orderedGateways {
312
+ // Check if adding this gateway would exceed the ancestor limit
313
+ if ancestorCount >= maxAncestors {
224
314
policyName := backendTLSPolicy .Source .Namespace + "/" + backendTLSPolicy .Source .Name
315
+ proposedAncestor := createParentReference (v1 .GroupName , kinds .Gateway , gatewayNsName )
225
316
gatewayName := getAncestorName (proposedAncestor )
226
- gateway , ok := gateways [ gatewayNsName ]
227
- if ok {
228
- gateway .Conditions = append (gateway .Conditions , conditions . NewPolicyAncestorLimitReached ( policyName ) )
317
+
318
+ if gateway , ok := gateways [ gatewayNsName ]; ok {
319
+ gateway .Conditions = addPolicyAncestorLimitCondition (gateway .Conditions , policyName , kinds . BackendTLSPolicy )
229
320
} else {
230
- // Not found in the graph, log the issue. I don't think this should happen.
231
- logger .Info ("Gateway not found in the graph" , "policy" , policyName , "ancestor" , gatewayName )
321
+ // This should never happen, but we'll log it if it does
322
+ logger .Error (fmt .Errorf ("gateway not found in the graph" ),
323
+ "Gateway not found in the graph" , "policy" , policyName , "ancestor" , gatewayName )
232
324
}
233
325
234
326
LogAncestorLimitReached (logger , policyName , "BackendTLSPolicy" , gatewayName )
235
-
236
327
continue
237
328
}
238
329
239
- // Gateway can be effectively used by this policy
330
+ ancestorCount ++
331
+
240
332
backendTLSPolicy .Gateways = append (backendTLSPolicy .Gateways , gatewayNsName )
241
333
}
242
334
}
0 commit comments