Skip to content

Commit ad523ad

Browse files
committed
Fix policy attachment when ancestors slice is full
1 parent 6c410e5 commit ad523ad

13 files changed

+237
-45
lines changed

internal/controller/state/change_processor.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ func (c *ChangeProcessorImpl) Process() *graph.Graph {
268268
c.cfg.GatewayClassName,
269269
c.cfg.PlusSecrets,
270270
c.cfg.Validators,
271+
c.cfg.Logger,
271272
)
272273

273274
return c.latestGraph

internal/controller/state/conditions/conditions.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ const (
133133
// GatewayReasonParamsRefInvalid is used with the "GatewayResolvedRefs" condition when the
134134
// parametersRef resource is invalid.
135135
GatewayReasonParamsRefInvalid v1.GatewayConditionReason = "ParametersRefInvalid"
136+
137+
// PolicyReasonAncestorLimitReached is used with the "PolicyAccepted" condition when a policy
138+
// cannot be applied because the ancestor status list has reached the maximum size of 16.
139+
PolicyReasonAncestorLimitReached v1alpha2.PolicyConditionReason = "AncestorLimitReached"
140+
141+
// PolicyMessageAncestorLimitReached is a message used with PolicyReasonAncestorLimitReached
142+
// when a policy cannot be applied due to the 16 ancestor limit being reached.
143+
PolicyMessageAncestorLimitReached = "Policy cannot be applied because the ancestor status list " +
144+
"has reached the maximum size of 16"
136145
)
137146

138147
// Condition defines a condition to be reported in the status of resources.
@@ -933,6 +942,17 @@ func NewPolicyTargetNotFound(msg string) Condition {
933942
}
934943
}
935944

945+
// NewPolicyAncestorLimitReached returns a Condition that indicates that the Policy is not accepted because
946+
// the ancestor status list has reached the maximum size of 16.
947+
func NewPolicyAncestorLimitReached(_ string) Condition {
948+
return Condition{
949+
Type: string(v1alpha2.PolicyConditionAccepted),
950+
Status: metav1.ConditionFalse,
951+
Reason: string(PolicyReasonAncestorLimitReached),
952+
Message: PolicyMessageAncestorLimitReached,
953+
}
954+
}
955+
936956
// NewPolicyNotAcceptedTargetConflict returns a Condition that indicates that the Policy is not accepted
937957
// because the target resource has a conflict with another resource when attempting to apply this policy.
938958
func NewPolicyNotAcceptedTargetConflict(msg string) Condition {

internal/controller/state/conditions/conditions_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
. "github.com/onsi/gomega"
77
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
"sigs.k8s.io/gateway-api/apis/v1alpha2"
89
)
910

1011
func TestDeduplicateConditions(t *testing.T) {
@@ -145,3 +146,16 @@ func TestHasMatchingCondition(t *testing.T) {
145146
})
146147
}
147148
}
149+
150+
func TestNewPolicyAncestorLimitReached(t *testing.T) {
151+
t.Parallel()
152+
g := NewWithT(t)
153+
154+
policyName := "test-policy"
155+
condition := NewPolicyAncestorLimitReached(policyName)
156+
157+
g.Expect(condition.Type).To(Equal(string(v1alpha2.PolicyConditionAccepted)))
158+
g.Expect(condition.Status).To(Equal(metav1.ConditionFalse))
159+
g.Expect(condition.Reason).To(Equal(string(PolicyReasonAncestorLimitReached)))
160+
g.Expect(condition.Message).To(Equal(PolicyMessageAncestorLimitReached))
161+
}

internal/controller/state/dataplane/configuration.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ func newBackendGroup(
390390
UpstreamName: ref.ServicePortReference(),
391391
Weight: ref.Weight,
392392
Valid: valid,
393-
VerifyTLS: convertBackendTLS(ref.BackendTLSPolicy),
393+
VerifyTLS: convertBackendTLS(ref.BackendTLSPolicy, gatewayName),
394394
})
395395
}
396396

@@ -401,10 +401,15 @@ func newBackendGroup(
401401
}
402402
}
403403

404-
func convertBackendTLS(btp *graph.BackendTLSPolicy) *VerifyTLS {
404+
func convertBackendTLS(btp *graph.BackendTLSPolicy, gwNsName types.NamespacedName) *VerifyTLS {
405405
if btp == nil || !btp.Valid {
406406
return nil
407407
}
408+
409+
if !slices.Contains(btp.Gateways, gwNsName) {
410+
return nil
411+
}
412+
408413
verify := &VerifyTLS{}
409414
if btp.CaCertRef.Name != "" {
410415
verify.CertBundleID = generateCertBundleID(btp.CaCertRef)

internal/controller/state/dataplane/configuration_test.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ func TestBuildConfiguration(t *testing.T) {
607607
},
608608
CaCertRef: types.NamespacedName{Namespace: "test", Name: "configmap-1"},
609609
Valid: true,
610+
Gateways: []types.NamespacedName{gatewayNsName},
610611
}
611612

612613
expHTTPSHR8Groups[0].Backends[0].VerifyTLS = &VerifyTLS{
@@ -661,6 +662,7 @@ func TestBuildConfiguration(t *testing.T) {
661662
},
662663
CaCertRef: types.NamespacedName{Namespace: "test", Name: "configmap-2"},
663664
Valid: true,
665+
Gateways: []types.NamespacedName{gatewayNsName},
664666
}
665667

666668
expHTTPSHR9Groups[0].Backends[0].VerifyTLS = &VerifyTLS{
@@ -3569,6 +3571,9 @@ func TestHostnameMoreSpecific(t *testing.T) {
35693571

35703572
func TestConvertBackendTLS(t *testing.T) {
35713573
t.Parallel()
3574+
3575+
testGateway := types.NamespacedName{Namespace: "test", Name: "gateway"}
3576+
35723577
btpCaCertRefs := &graph.BackendTLSPolicy{
35733578
Source: &v1alpha3.BackendTLSPolicy{
35743579
ObjectMeta: metav1.ObjectMeta{
@@ -3588,6 +3593,7 @@ func TestConvertBackendTLS(t *testing.T) {
35883593
},
35893594
Valid: true,
35903595
CaCertRef: types.NamespacedName{Namespace: "test", Name: "ca-cert"},
3596+
Gateways: []types.NamespacedName{testGateway},
35913597
}
35923598

35933599
btpWellKnownCerts := &graph.BackendTLSPolicy{
@@ -3598,7 +3604,8 @@ func TestConvertBackendTLS(t *testing.T) {
35983604
},
35993605
},
36003606
},
3601-
Valid: true,
3607+
Valid: true,
3608+
Gateways: []types.NamespacedName{testGateway},
36023609
}
36033610

36043611
expectedWithCertPath := &VerifyTLS{
@@ -3615,31 +3622,41 @@ func TestConvertBackendTLS(t *testing.T) {
36153622

36163623
tests := []struct {
36173624
btp *graph.BackendTLSPolicy
3625+
gwNsName types.NamespacedName
36183626
expected *VerifyTLS
36193627
msg string
36203628
}{
36213629
{
36223630
btp: nil,
3631+
gwNsName: testGateway,
36233632
expected: nil,
36243633
msg: "nil backend tls policy",
36253634
},
36263635
{
36273636
btp: btpCaCertRefs,
3637+
gwNsName: testGateway,
36283638
expected: expectedWithCertPath,
36293639
msg: "normal case with cert path",
36303640
},
36313641
{
36323642
btp: btpWellKnownCerts,
3643+
gwNsName: testGateway,
36333644
expected: expectedWithWellKnownCerts,
36343645
msg: "normal case no cert path",
36353646
},
3647+
{
3648+
btp: btpCaCertRefs,
3649+
gwNsName: types.NamespacedName{Namespace: "test", Name: "unsupported-gateway"},
3650+
expected: nil,
3651+
msg: "gateway not supported by policy",
3652+
},
36363653
}
36373654

36383655
for _, tc := range tests {
36393656
t.Run(tc.msg, func(t *testing.T) {
36403657
t.Parallel()
36413658
g := NewWithT(t)
3642-
g.Expect(convertBackendTLS(tc.btp)).To(Equal(tc.expected))
3659+
g.Expect(convertBackendTLS(tc.btp, tc.gwNsName)).To(Equal(tc.expected))
36433660
})
36443661
}
36453662
}

internal/controller/state/graph/backend_tls_policy.go

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ type BackendTLSPolicy struct {
1818
Source *v1alpha3.BackendTLSPolicy
1919
// CaCertRef is the name of the ConfigMap that contains the CA certificate.
2020
CaCertRef types.NamespacedName
21-
// Gateways are the names of the Gateways that are being checked for this BackendTLSPolicy.
21+
// Gateways are the names of the Gateways for which this BackendTLSPolicy is effectively applied.
22+
// Only contains gateways where the policy can be applied (not limited by ancestor status).
2223
Gateways []types.NamespacedName
2324
// Conditions include Conditions for the BackendTLSPolicy.
2425
Conditions []conditions.Condition
@@ -68,16 +69,13 @@ func validateBackendTLSPolicy(
6869
backendTLSPolicy *v1alpha3.BackendTLSPolicy,
6970
configMapResolver *configMapResolver,
7071
secretResolver *secretResolver,
71-
ctlrName string,
72+
_ 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-
}
77+
// Note: Ancestor limit checking moved to addGatewaysForBackendTLSPolicies for per-gateway effectiveness tracking
78+
// The policy may be partially effective (work for some gateways but not others due to ancestor limits)
8179

8280
if err := validateBackendTLSHostname(backendTLSPolicy); err != nil {
8381
valid = false
@@ -186,10 +184,12 @@ func validateBackendTLSWellKnownCACerts(btp *v1alpha3.BackendTLSPolicy) error {
186184
func addGatewaysForBackendTLSPolicies(
187185
backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy,
188186
services map[types.NamespacedName]*ReferencedService,
187+
ctlrName string,
189188
) {
190189
for _, backendTLSPolicy := range backendTLSPolicies {
191-
gateways := make(map[types.NamespacedName]struct{})
190+
potentialGateways := make(map[types.NamespacedName]struct{})
192191

192+
// First, collect all potential gateways for this policy
193193
for _, refs := range backendTLSPolicy.Source.Spec.TargetRefs {
194194
if refs.Kind != kinds.Service {
195195
continue
@@ -201,13 +201,32 @@ func addGatewaysForBackendTLSPolicies(
201201
}
202202

203203
for gateway := range referencedServices.GatewayNsNames {
204-
gateways[gateway] = struct{}{}
204+
potentialGateways[gateway] = struct{}{}
205205
}
206206
}
207207
}
208208

209-
for gateway := range gateways {
210-
backendTLSPolicy.Gateways = append(backendTLSPolicy.Gateways, gateway)
209+
// Now check each potential gateway against ancestor limits
210+
for gatewayNsName := range potentialGateways {
211+
// Create a proposed ancestor reference for this gateway
212+
proposedAncestor := createParentReference(v1.GroupName, kinds.Gateway, gatewayNsName)
213+
214+
// Check ancestor limit for BackendTLS policy
215+
isFull := backendTLSPolicyAncestorsFull(
216+
backendTLSPolicy.Source.Status.Ancestors,
217+
ctlrName,
218+
)
219+
220+
if isFull {
221+
policyName := backendTLSPolicy.Source.Namespace + "/" + backendTLSPolicy.Source.Name
222+
gatewayName := getAncestorName(proposedAncestor)
223+
LogAncestorLimitReached(policyName, "BackendTLSPolicy", gatewayName)
224+
225+
continue
226+
}
227+
228+
// Gateway can be effectively used by this policy
229+
backendTLSPolicy.Gateways = append(backendTLSPolicy.Gateways, gatewayNsName)
211230
}
212231
}
213232
}

internal/controller/state/graph/backend_tls_policy_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ func TestValidateBackendTLSPolicy(t *testing.T) {
399399
},
400400
},
401401
{
402-
name: "invalid case with too many ancestors",
402+
name: "valid case with many ancestors (ancestor limit now handled during gateway assignment)",
403403
tlsPolicy: &v1alpha3.BackendTLSPolicy{
404404
ObjectMeta: metav1.ObjectMeta{
405405
Name: "tls-policy",
@@ -416,7 +416,7 @@ func TestValidateBackendTLSPolicy(t *testing.T) {
416416
Ancestors: ancestors,
417417
},
418418
},
419-
ignored: true,
419+
isValid: true,
420420
},
421421
}
422422

@@ -660,7 +660,7 @@ func TestAddGatewaysForBackendTLSPolicies(t *testing.T) {
660660
g := NewWithT(t)
661661
t.Run(test.name, func(t *testing.T) {
662662
t.Parallel()
663-
addGatewaysForBackendTLSPolicies(test.backendTLSPolicies, test.services)
663+
addGatewaysForBackendTLSPolicies(test.backendTLSPolicies, test.services, "nginx-gateway")
664664
g.Expect(helpers.Diff(test.backendTLSPolicies, test.expected)).To(BeEmpty())
665665
})
666666
}

internal/controller/state/graph/graph.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"sigs.k8s.io/gateway-api/apis/v1alpha3"
1414
"sigs.k8s.io/gateway-api/apis/v1beta1"
1515

16+
"github.com/go-logr/logr"
17+
1618
ngfAPIv1alpha1 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1"
1719
ngfAPIv1alpha2 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha2"
1820
"github.com/nginx/nginx-gateway-fabric/v2/internal/controller/nginx/config/policies"
@@ -198,7 +200,10 @@ func BuildGraph(
198200
gcName string,
199201
plusSecrets map[types.NamespacedName][]PlusSecretFile,
200202
validators validation.Validators,
203+
logger logr.Logger,
201204
) *Graph {
205+
// Set the logger for the graph package
206+
SetLogger(logger)
202207
processedGwClasses, gcExists := processGatewayClasses(state.GatewayClasses, gcName, controllerName)
203208
if gcExists && processedGwClasses.Winner == nil {
204209
// configured GatewayClass does not reference this controller
@@ -269,7 +274,7 @@ func BuildGraph(
269274

270275
referencedServices := buildReferencedServices(routes, l4routes, gws)
271276

272-
addGatewaysForBackendTLSPolicies(processedBackendTLSPolicies, referencedServices)
277+
addGatewaysForBackendTLSPolicies(processedBackendTLSPolicies, referencedServices, controllerName)
273278

274279
// policies must be processed last because they rely on the state of the other resources in the graph
275280
processedPolicies := processPolicies(

internal/controller/state/graph/graph_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package graph
33
import (
44
"testing"
55

6+
"github.com/go-logr/logr"
67
. "github.com/onsi/gomega"
78
"github.com/onsi/gomega/format"
89
v1 "k8s.io/api/core/v1"
@@ -1309,6 +1310,7 @@ func TestBuildGraph(t *testing.T) {
13091310
GenericValidator: &validationfakes.FakeGenericValidator{},
13101311
PolicyValidator: fakePolicyValidator,
13111312
},
1313+
logr.Discard(),
13121314
)
13131315

13141316
g.Expect(helpers.Diff(test.expected, result)).To(BeEmpty())

internal/controller/state/graph/multiple_gateways_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package graph
33
import (
44
"testing"
55

6+
"github.com/go-logr/logr"
67
. "github.com/onsi/gomega"
78
"github.com/onsi/gomega/format"
89
v1 "k8s.io/api/core/v1"
@@ -398,6 +399,7 @@ func Test_MultipleGateways_WithNginxProxy(t *testing.T) {
398399
GenericValidator: &validationfakes.FakeGenericValidator{},
399400
PolicyValidator: fakePolicyValidator,
400401
},
402+
logr.Discard(),
401403
)
402404

403405
g.Expect(helpers.Diff(test.expGraph, result)).To(BeEmpty())
@@ -886,6 +888,7 @@ func Test_MultipleGateways_WithListeners(t *testing.T) {
886888
GenericValidator: &validationfakes.FakeGenericValidator{},
887889
PolicyValidator: fakePolicyValidator,
888890
},
891+
logr.Discard(),
889892
)
890893

891894
g.Expect(helpers.Diff(test.expGraph, result)).To(BeEmpty())

0 commit comments

Comments
 (0)