@@ -3,7 +3,9 @@ package graph
33import (
44 "fmt"
55 "slices"
6+ "strings"
67
8+ "github.com/go-logr/logr"
79 "k8s.io/apimachinery/pkg/types"
810 "k8s.io/apimachinery/pkg/util/validation/field"
911 v1 "sigs.k8s.io/gateway-api/apis/v1"
@@ -18,7 +20,8 @@ type BackendTLSPolicy struct {
1820 Source * v1alpha3.BackendTLSPolicy
1921 // CaCertRef is the name of the ConfigMap that contains the CA certificate.
2022 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).
2225 Gateways []types.NamespacedName
2326 // Conditions include Conditions for the BackendTLSPolicy.
2427 Conditions []conditions.Condition
@@ -34,7 +37,6 @@ func processBackendTLSPolicies(
3437 backendTLSPolicies map [types.NamespacedName ]* v1alpha3.BackendTLSPolicy ,
3538 configMapResolver * configMapResolver ,
3639 secretResolver * secretResolver ,
37- ctlrName string ,
3840 gateways map [types.NamespacedName ]* Gateway ,
3941) map [types.NamespacedName ]* BackendTLSPolicy {
4042 if len (backendTLSPolicies ) == 0 || len (gateways ) == 0 {
@@ -45,7 +47,7 @@ func processBackendTLSPolicies(
4547 for nsname , backendTLSPolicy := range backendTLSPolicies {
4648 var caCertRef types.NamespacedName
4749
48- valid , ignored , conds := validateBackendTLSPolicy (backendTLSPolicy , configMapResolver , secretResolver , ctlrName )
50+ valid , ignored , conds := validateBackendTLSPolicy (backendTLSPolicy , configMapResolver , secretResolver )
4951
5052 if valid && ! ignored && backendTLSPolicy .Spec .Validation .CACertificateRefs != nil {
5153 caCertRef = types.NamespacedName {
@@ -68,17 +70,10 @@ func validateBackendTLSPolicy(
6870 backendTLSPolicy * v1alpha3.BackendTLSPolicy ,
6971 configMapResolver * configMapResolver ,
7072 secretResolver * secretResolver ,
71- ctlrName string ,
7273) (valid , ignored bool , conds []conditions.Condition ) {
7374 valid = true
7475 ignored = false
7576
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-
8277 if err := validateBackendTLSHostname (backendTLSPolicy ); err != nil {
8378 valid = false
8479 conds = append (conds , conditions .NewPolicyInvalid (fmt .Sprintf ("invalid hostname: %s" , err .Error ())))
@@ -183,31 +178,148 @@ func validateBackendTLSWellKnownCACerts(btp *v1alpha3.BackendTLSPolicy) error {
183178 return nil
184179}
185180
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+
186282func addGatewaysForBackendTLSPolicies (
187283 backendTLSPolicies map [types.NamespacedName ]* BackendTLSPolicy ,
188284 services map [types.NamespacedName ]* ReferencedService ,
285+ ctlrName string ,
286+ gateways map [types.NamespacedName ]* Gateway ,
287+ logger logr.Logger ,
189288) {
190289 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+ )
192297
193- for _ , refs := range backendTLSPolicy .Source .Spec .TargetRefs {
194- if refs .Kind != kinds .Service {
195- continue
196- }
298+ ancestorCount := countNonNGFAncestors (backendTLSPolicy .Source , ctlrName )
197299
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 )
202307
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 )
205314 }
315+
316+ logAncestorLimitReached (logger , policyName , "BackendTLSPolicy" , gatewayName )
317+ continue
206318 }
207- }
208319
209- for gateway := range gateways {
210- backendTLSPolicy .Gateways = append (backendTLSPolicy .Gateways , gateway )
320+ ancestorCount ++
321+
322+ backendTLSPolicy .Gateways = append (backendTLSPolicy .Gateways , gatewayNsName )
211323 }
212324 }
213325}
0 commit comments