@@ -3,7 +3,9 @@ package graph
3
3
import (
4
4
"fmt"
5
5
"slices"
6
+ "strings"
6
7
8
+ "github.com/go-logr/logr"
7
9
"k8s.io/apimachinery/pkg/types"
8
10
"k8s.io/apimachinery/pkg/util/validation/field"
9
11
v1 "sigs.k8s.io/gateway-api/apis/v1"
@@ -18,7 +20,8 @@ type BackendTLSPolicy struct {
18
20
Source * v1alpha3.BackendTLSPolicy
19
21
// CaCertRef is the name of the ConfigMap that contains the CA certificate.
20
22
CaCertRef types.NamespacedName
21
- // Gateways are the names of the Gateways that are being checked for this BackendTLSPolicy.
23
+ // Gateways are the names of the Gateways for which this BackendTLSPolicy is effectively applied.
24
+ // Only contains gateways where the policy can be applied (not limited by ancestor status).
22
25
Gateways []types.NamespacedName
23
26
// Conditions include Conditions for the BackendTLSPolicy.
24
27
Conditions []conditions.Condition
@@ -34,7 +37,6 @@ func processBackendTLSPolicies(
34
37
backendTLSPolicies map [types.NamespacedName ]* v1alpha3.BackendTLSPolicy ,
35
38
configMapResolver * configMapResolver ,
36
39
secretResolver * secretResolver ,
37
- ctlrName string ,
38
40
gateways map [types.NamespacedName ]* Gateway ,
39
41
) map [types.NamespacedName ]* BackendTLSPolicy {
40
42
if len (backendTLSPolicies ) == 0 || len (gateways ) == 0 {
@@ -45,7 +47,7 @@ func processBackendTLSPolicies(
45
47
for nsname , backendTLSPolicy := range backendTLSPolicies {
46
48
var caCertRef types.NamespacedName
47
49
48
- valid , ignored , conds := validateBackendTLSPolicy (backendTLSPolicy , configMapResolver , secretResolver , ctlrName )
50
+ valid , ignored , conds := validateBackendTLSPolicy (backendTLSPolicy , configMapResolver , secretResolver )
49
51
50
52
if valid && ! ignored && backendTLSPolicy .Spec .Validation .CACertificateRefs != nil {
51
53
caCertRef = types.NamespacedName {
@@ -68,17 +70,10 @@ func validateBackendTLSPolicy(
68
70
backendTLSPolicy * v1alpha3.BackendTLSPolicy ,
69
71
configMapResolver * configMapResolver ,
70
72
secretResolver * secretResolver ,
71
- ctlrName string ,
72
73
) (valid , ignored bool , conds []conditions.Condition ) {
73
74
valid = true
74
75
ignored = false
75
76
76
- // FIXME (kate-osborn): https://github.com/nginx/nginx-gateway-fabric/issues/1987
77
- if backendTLSPolicyAncestorsFull (backendTLSPolicy .Status .Ancestors , ctlrName ) {
78
- valid = false
79
- ignored = true
80
- }
81
-
82
77
if err := validateBackendTLSHostname (backendTLSPolicy ); err != nil {
83
78
valid = false
84
79
conds = append (conds , conditions .NewPolicyInvalid (fmt .Sprintf ("invalid hostname: %s" , err .Error ())))
@@ -183,31 +178,148 @@ func validateBackendTLSWellKnownCACerts(btp *v1alpha3.BackendTLSPolicy) error {
183
178
return nil
184
179
}
185
180
181
+ // countNonNGFAncestors counts the number of non-NGF ancestors in policy status.
182
+ func countNonNGFAncestors (policy * v1alpha3.BackendTLSPolicy , ctlrName string ) int {
183
+ nonNGFCount := 0
184
+ for _ , ancestor := range policy .Status .Ancestors {
185
+ if string (ancestor .ControllerName ) != ctlrName {
186
+ nonNGFCount ++
187
+ }
188
+ }
189
+ return nonNGFCount
190
+ }
191
+
192
+ // addPolicyAncestorLimitCondition adds or updates a PolicyAncestorLimitReached condition.
193
+ func addPolicyAncestorLimitCondition (
194
+ conds []conditions.Condition ,
195
+ policyName string ,
196
+ policyType string ,
197
+ ) []conditions.Condition {
198
+ for i , condition := range conds {
199
+ if condition .Reason == string (conditions .PolicyReasonAncestorLimitReached ) {
200
+ if ! strings .Contains (condition .Message , policyName ) {
201
+ conds [i ].Message = fmt .Sprintf ("%s, %s %s" , condition .Message , policyType , policyName )
202
+ }
203
+ return conds
204
+ }
205
+ }
206
+
207
+ newCondition := conditions .NewPolicyAncestorLimitReached (policyType , policyName )
208
+ return append (conds , newCondition )
209
+ }
210
+
211
+ // collectOrderedGateways collects gateways in spec order (services) then creation time order (gateways within service).
212
+ func collectOrderedGateways (
213
+ policy * v1alpha3.BackendTLSPolicy ,
214
+ services map [types.NamespacedName ]* ReferencedService ,
215
+ gateways map [types.NamespacedName ]* Gateway ,
216
+ existingNGFGatewayAncestors map [types.NamespacedName ]struct {},
217
+ ) []types.NamespacedName {
218
+ seenGateways := make (map [types.NamespacedName ]struct {})
219
+ existingGateways := make ([]types.NamespacedName , 0 )
220
+ newGateways := make ([]types.NamespacedName , 0 )
221
+
222
+ // Process services in spec order to maintain deterministic gateway ordering
223
+ for _ , refs := range policy .Spec .TargetRefs {
224
+ if refs .Kind != kinds .Service {
225
+ continue
226
+ }
227
+
228
+ svcNsName := types.NamespacedName {
229
+ Namespace : policy .Namespace ,
230
+ Name : string (refs .Name ),
231
+ }
232
+
233
+ referencedService , exists := services [svcNsName ]
234
+ if ! exists {
235
+ continue
236
+ }
237
+
238
+ // Add to ordered lists, categorizing existing vs new, skipping duplicates
239
+ for gateway := range referencedService .GatewayNsNames {
240
+ if _ , seen := seenGateways [gateway ]; seen {
241
+ continue
242
+ }
243
+ seenGateways [gateway ] = struct {}{}
244
+ if _ , exists := existingNGFGatewayAncestors [gateway ]; exists {
245
+ existingGateways = append (existingGateways , gateway )
246
+ } else {
247
+ newGateways = append (newGateways , gateway )
248
+ }
249
+ }
250
+ }
251
+
252
+ sortGatewaysByCreationTime (existingGateways , gateways )
253
+ sortGatewaysByCreationTime (newGateways , gateways )
254
+
255
+ return append (existingGateways , newGateways ... )
256
+ }
257
+
258
+ func extractExistingNGFGatewayAncestors (
259
+ backendTLSPolicy * v1alpha3.BackendTLSPolicy ,
260
+ ctlrName string ,
261
+ ) map [types.NamespacedName ]struct {} {
262
+ existingNGFGatewayAncestors := make (map [types.NamespacedName ]struct {})
263
+
264
+ for _ , ancestor := range backendTLSPolicy .Status .Ancestors {
265
+ if string (ancestor .ControllerName ) != ctlrName {
266
+ continue
267
+ }
268
+
269
+ if ancestor .AncestorRef .Kind != nil && * ancestor .AncestorRef .Kind == v1 .Kind (kinds .Gateway ) &&
270
+ ancestor .AncestorRef .Namespace != nil {
271
+ gatewayNsName := types.NamespacedName {
272
+ Namespace : string (* ancestor .AncestorRef .Namespace ),
273
+ Name : string (ancestor .AncestorRef .Name ),
274
+ }
275
+ existingNGFGatewayAncestors [gatewayNsName ] = struct {}{}
276
+ }
277
+ }
278
+
279
+ return existingNGFGatewayAncestors
280
+ }
281
+
186
282
func addGatewaysForBackendTLSPolicies (
187
283
backendTLSPolicies map [types.NamespacedName ]* BackendTLSPolicy ,
188
284
services map [types.NamespacedName ]* ReferencedService ,
285
+ ctlrName string ,
286
+ gateways map [types.NamespacedName ]* Gateway ,
287
+ logger logr.Logger ,
189
288
) {
190
289
for _ , backendTLSPolicy := range backendTLSPolicies {
191
- gateways := make (map [types.NamespacedName ]struct {})
290
+ existingNGFGatewayAncestors := extractExistingNGFGatewayAncestors (backendTLSPolicy .Source , ctlrName )
291
+ orderedGateways := collectOrderedGateways (
292
+ backendTLSPolicy .Source ,
293
+ services ,
294
+ gateways ,
295
+ existingNGFGatewayAncestors ,
296
+ )
192
297
193
- for _ , refs := range backendTLSPolicy .Source .Spec .TargetRefs {
194
- if refs .Kind != kinds .Service {
195
- continue
196
- }
298
+ ancestorCount := countNonNGFAncestors (backendTLSPolicy .Source , ctlrName )
197
299
198
- for svcNsName , referencedServices := range services {
199
- if svcNsName .Name != string (refs .Name ) {
200
- continue
201
- }
300
+ // Process each gateway, respecting ancestor limits
301
+ for _ , gatewayNsName := range orderedGateways {
302
+ // Check if adding this gateway would exceed the ancestor limit
303
+ if ancestorCount >= maxAncestors {
304
+ policyName := backendTLSPolicy .Source .Namespace + "/" + backendTLSPolicy .Source .Name
305
+ proposedAncestor := createParentReference (v1 .GroupName , kinds .Gateway , gatewayNsName )
306
+ gatewayName := getAncestorName (proposedAncestor )
202
307
203
- for gateway := range referencedServices .GatewayNsNames {
204
- gateways [gateway ] = struct {}{}
308
+ if gateway , ok := gateways [gatewayNsName ]; ok {
309
+ gateway .Conditions = addPolicyAncestorLimitCondition (gateway .Conditions , policyName , kinds .BackendTLSPolicy )
310
+ } else {
311
+ // This should never happen, but we'll log it if it does
312
+ logger .Error (fmt .Errorf ("gateway not found in the graph" ),
313
+ "Gateway not found in the graph" , "policy" , policyName , "ancestor" , gatewayName )
205
314
}
315
+
316
+ logAncestorLimitReached (logger , policyName , "BackendTLSPolicy" , gatewayName )
317
+ continue
206
318
}
207
- }
208
319
209
- for gateway := range gateways {
210
- backendTLSPolicy .Gateways = append (backendTLSPolicy .Gateways , gateway )
320
+ ancestorCount ++
321
+
322
+ backendTLSPolicy .Gateways = append (backendTLSPolicy .Gateways , gatewayNsName )
211
323
}
212
324
}
213
325
}
0 commit comments