Skip to content

Commit 624fd46

Browse files
committed
fix a lot
1 parent 35d1778 commit 624fd46

File tree

7 files changed

+298
-53
lines changed

7 files changed

+298
-53
lines changed

internal/controller/state/conditions/conditions.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ const (
145145

146146
// PolicyMessageAncestorLimitReached is a message used with PolicyReasonAncestorLimitReached
147147
// when a policy cannot be applied due to the 16 ancestor limit being reached.
148-
PolicyMessageAncestorLimitReached = "Policy cannot be applied because the ancestor status list " +
149-
"has reached the maximum size of 16"
148+
PolicyMessageAncestorLimitReached = "Policies cannot be applied because the ancestor status list " +
149+
"has reached the maximum size of 16. The following policies have been ignored:"
150150
)
151151

152152
// Condition defines a condition to be reported in the status of resources.
@@ -979,7 +979,7 @@ func NewPolicyTargetNotFound(msg string) Condition {
979979

980980
// NewPolicyAncestorLimitReached returns a Condition that indicates that the Policy is not accepted because
981981
// the ancestor status list has reached the maximum size of 16.
982-
func NewPolicyAncestorLimitReached(_ string) Condition {
982+
func NewPolicyAncestorLimitReached() Condition {
983983
return Condition{
984984
Type: string(v1alpha2.PolicyConditionAccepted),
985985
Status: metav1.ConditionFalse,

internal/controller/state/conditions/conditions_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,7 @@ func TestNewPolicyAncestorLimitReached(t *testing.T) {
151151
t.Parallel()
152152
g := NewWithT(t)
153153

154-
policyName := "test-policy"
155-
condition := NewPolicyAncestorLimitReached(policyName)
154+
condition := NewPolicyAncestorLimitReached()
156155

157156
g.Expect(condition.Type).To(Equal(string(v1alpha2.PolicyConditionAccepted)))
158157
g.Expect(condition.Status).To(Equal(metav1.ConditionFalse))

internal/controller/state/graph/backend_tls_policy.go

Lines changed: 129 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package graph
33
import (
44
"fmt"
55
"slices"
6+
"strings"
67

78
"github.com/go-logr/logr"
89
"k8s.io/apimachinery/pkg/types"
@@ -75,12 +76,16 @@ func validateBackendTLSPolicy(
7576
valid = true
7677
ignored = false
7778

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+
// }
8085

8186
if err := validateBackendTLSHostname(backendTLSPolicy); err != nil {
8287
valid = false
83-
conds = append(conds, conditions.NewPolicyInvalid(fmt.Sprintf("invalid hostname: %s", err.Error())))
88+
conds = append(conds, conditions.NewPolicyInvalid(err.Error()))
8489
}
8590

8691
caCertRefs := backendTLSPolicy.Spec.Validation.CACertificateRefs
@@ -182,61 +187,148 @@ func validateBackendTLSWellKnownCACerts(btp *v1alpha3.BackendTLSPolicy) error {
182187
return nil
183188
}
184189

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,
187227
services map[types.NamespacedName]*ReferencedService,
188-
ctlrName string,
189228
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+
}
194248

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 {
198252
continue
199253
}
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+
}
200262

201-
for svcNsName, referencedServices := range services {
202-
if svcNsName.Name != string(refs.Name) {
203-
continue
204-
}
263+
sortGatewaysByCreationTime(existingGateways, gateways)
264+
sortGatewaysByCreationTime(newGateways, gateways)
205265

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),
209285
}
286+
existingNGFGatewayAncestors[gatewayNsName] = struct{}{}
210287
}
288+
}
289+
290+
return existingNGFGatewayAncestors
291+
}
211292

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
216307

217-
// Check ancestor limit for BackendTLS policy
218-
isFull := backendTLSPolicyAncestorsFull(
219-
backendTLSPolicy.Source.Status.Ancestors,
220-
ctlrName,
221-
)
308+
ancestorCount := countNonNGFAncestors(backendTLSPolicy.Source, ctlrName)
222309

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 {
224314
policyName := backendTLSPolicy.Source.Namespace + "/" + backendTLSPolicy.Source.Name
315+
proposedAncestor := createParentReference(v1.GroupName, kinds.Gateway, gatewayNsName)
225316
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)
229320
} 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)
232324
}
233325

234326
LogAncestorLimitReached(logger, policyName, "BackendTLSPolicy", gatewayName)
235-
236327
continue
237328
}
238329

239-
// Gateway can be effectively used by this policy
330+
ancestorCount++
331+
240332
backendTLSPolicy.Gateways = append(backendTLSPolicy.Gateways, gatewayNsName)
241333
}
242334
}

internal/controller/state/graph/policies.go

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,51 @@ const (
7171
)
7272

7373
// attachPolicies attaches the graph's processed policies to the resources they target. It modifies the graph in place.
74+
// extractExistingNGFGatewayAncestorsForPolicy extracts existing NGF gateway ancestors from policy status.
75+
func extractExistingNGFGatewayAncestorsForPolicy(policy *Policy, ctlrName string) map[types.NamespacedName]struct{} {
76+
existingNGFGatewayAncestors := make(map[types.NamespacedName]struct{})
77+
78+
for _, ancestor := range policy.Source.GetPolicyStatus().Ancestors {
79+
if string(ancestor.ControllerName) != ctlrName {
80+
continue
81+
}
82+
83+
if ancestor.AncestorRef.Kind != nil && *ancestor.AncestorRef.Kind == v1.Kind(kinds.Gateway) &&
84+
ancestor.AncestorRef.Namespace != nil {
85+
gatewayNsName := types.NamespacedName{
86+
Namespace: string(*ancestor.AncestorRef.Namespace),
87+
Name: string(ancestor.AncestorRef.Name),
88+
}
89+
existingNGFGatewayAncestors[gatewayNsName] = struct{}{}
90+
}
91+
}
92+
93+
return existingNGFGatewayAncestors
94+
}
95+
96+
// collectOrderedGatewaysForService collects gateways for a service with existing gateway prioritization.
97+
func collectOrderedGatewaysForService(
98+
svc *ReferencedService,
99+
gateways map[types.NamespacedName]*Gateway,
100+
existingNGFGatewayAncestors map[types.NamespacedName]struct{},
101+
) (existingGateways []types.NamespacedName, newGateways []types.NamespacedName) {
102+
existingGateways = make([]types.NamespacedName, 0, len(svc.GatewayNsNames))
103+
newGateways = make([]types.NamespacedName, 0, len(svc.GatewayNsNames))
104+
105+
for gwNsName := range svc.GatewayNsNames {
106+
if _, exists := existingNGFGatewayAncestors[gwNsName]; exists {
107+
existingGateways = append(existingGateways, gwNsName)
108+
} else {
109+
newGateways = append(newGateways, gwNsName)
110+
}
111+
}
112+
113+
sortGatewaysByCreationTime(existingGateways, gateways)
114+
sortGatewaysByCreationTime(newGateways, gateways)
115+
116+
return existingGateways, newGateways
117+
}
118+
74119
func (g *Graph) attachPolicies(validator validation.PolicyValidator, ctlrName string, logger logr.Logger) {
75120
if len(g.Gateways) == 0 {
76121
return
@@ -109,8 +154,20 @@ func attachPolicyToService(
109154
) {
110155
var attachedToAnyGateway bool
111156

112-
for gwNsName, gw := range gws {
113-
if _, belongsToGw := svc.GatewayNsNames[gwNsName]; !belongsToGw {
157+
// Extract existing NGF gateway ancestors from policy status
158+
existingNGFGatewayAncestors := extractExistingNGFGatewayAncestorsForPolicy(policy, ctlrName)
159+
160+
// Collect and order gateways with existing gateway prioritization
161+
existingGateways, newGateways := collectOrderedGatewaysForService(
162+
svc, gws, existingNGFGatewayAncestors)
163+
164+
existingGateways = append(existingGateways, newGateways...)
165+
orderedGateways := existingGateways
166+
167+
for _, gwNsName := range orderedGateways {
168+
gw := gws[gwNsName]
169+
170+
if gw == nil || gw.Source == nil {
114171
continue
115172
}
116173

@@ -129,11 +186,20 @@ func attachPolicyToService(
129186
continue
130187
}
131188

189+
// Check if this is an existing gateway from policy status
190+
_, isExistingGateway := existingNGFGatewayAncestors[gwNsName]
191+
192+
if isExistingGateway {
193+
// Existing gateway from policy status - mark as attached but don't add to ancestors
194+
attachedToAnyGateway = true
195+
continue
196+
}
197+
132198
if ngfPolicyAncestorsFull(policy, ctlrName) {
133199
policyName := getPolicyName(policy.Source)
134200
policyKind := getPolicyKind(policy.Source)
135201

136-
gw.Conditions = append(gw.Conditions, conditions.NewPolicyAncestorLimitReached(policyName))
202+
gw.Conditions = addPolicyAncestorLimitCondition(gw.Conditions, policyName, policyKind)
137203
LogAncestorLimitReached(logger, policyName, policyKind, gwNsName.String())
138204

139205
// Mark this gateway as invalid for the policy due to ancestor limits
@@ -144,10 +210,6 @@ func attachPolicyToService(
144210
if !gw.Valid {
145211
policy.InvalidForGateways[gwNsName] = struct{}{}
146212
ancestor.Conditions = []conditions.Condition{conditions.NewPolicyTargetNotFound("Parent Gateway is invalid")}
147-
if ancestorsContainsAncestorRef(policy.Ancestors, ancestor.Ancestor) {
148-
continue
149-
}
150-
151213
policy.Ancestors = append(policy.Ancestors, ancestor)
152214
continue
153215
}
@@ -185,7 +247,7 @@ func attachPolicyToRoute(
185247
policyKind := getPolicyKind(policy.Source)
186248
routeName := getAncestorName(ancestorRef)
187249

188-
route.Conditions = append(route.Conditions, conditions.NewPolicyAncestorLimitReached(policyName))
250+
route.Conditions = addPolicyAncestorLimitCondition(route.Conditions, policyName, policyKind)
189251
LogAncestorLimitReached(logger, policyName, policyKind, routeName)
190252

191253
return
@@ -258,7 +320,7 @@ func attachPolicyToGateway(
258320
policyKind := getPolicyKind(policy.Source)
259321

260322
if exists {
261-
gw.Conditions = append(gw.Conditions, conditions.NewPolicyAncestorLimitReached(policyName))
323+
gw.Conditions = addPolicyAncestorLimitCondition(gw.Conditions, policyName, policyKind)
262324
} else {
263325
// Situation where gateway target is not found and the ancestors slice is full so I cannot add the condition.
264326
// Log in the controller log.

0 commit comments

Comments
 (0)