Skip to content

Commit 3041aa9

Browse files
committed
Add more unit tests
1 parent ad523ad commit 3041aa9

File tree

6 files changed

+680
-27
lines changed

6 files changed

+680
-27
lines changed

internal/controller/state/graph/backend_tls_policy.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"slices"
66

7+
"github.com/go-logr/logr"
78
"k8s.io/apimachinery/pkg/types"
89
"k8s.io/apimachinery/pkg/util/validation/field"
910
v1 "sigs.k8s.io/gateway-api/apis/v1"
@@ -185,6 +186,8 @@ func addGatewaysForBackendTLSPolicies(
185186
backendTLSPolicies map[types.NamespacedName]*BackendTLSPolicy,
186187
services map[types.NamespacedName]*ReferencedService,
187188
ctlrName string,
189+
gateways map[types.NamespacedName]*Gateway,
190+
logger logr.Logger,
188191
) {
189192
for _, backendTLSPolicy := range backendTLSPolicies {
190193
potentialGateways := make(map[types.NamespacedName]struct{})
@@ -220,7 +223,15 @@ func addGatewaysForBackendTLSPolicies(
220223
if isFull {
221224
policyName := backendTLSPolicy.Source.Namespace + "/" + backendTLSPolicy.Source.Name
222225
gatewayName := getAncestorName(proposedAncestor)
223-
LogAncestorLimitReached(policyName, "BackendTLSPolicy", gatewayName)
226+
gateway, ok := gateways[gatewayNsName]
227+
if ok {
228+
gateway.Conditions = append(gateway.Conditions, conditions.NewPolicyAncestorLimitReached(policyName))
229+
} 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)
232+
}
233+
234+
LogAncestorLimitReached(logger, policyName, "BackendTLSPolicy", gatewayName)
224235

225236
continue
226237
}

internal/controller/state/graph/backend_tls_policy_test.go

Lines changed: 287 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package graph
22

33
import (
4+
"bytes"
45
"testing"
56

7+
"github.com/go-logr/logr"
68
. "github.com/onsi/gomega"
79
v1 "k8s.io/api/core/v1"
810
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -11,6 +13,7 @@ import (
1113
"sigs.k8s.io/gateway-api/apis/v1alpha2"
1214
"sigs.k8s.io/gateway-api/apis/v1alpha3"
1315

16+
"github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/conditions"
1417
"github.com/nginx/nginx-gateway-fabric/v2/internal/framework/helpers"
1518
"github.com/nginx/nginx-gateway-fabric/v2/internal/framework/kinds"
1619
)
@@ -660,8 +663,291 @@ func TestAddGatewaysForBackendTLSPolicies(t *testing.T) {
660663
g := NewWithT(t)
661664
t.Run(test.name, func(t *testing.T) {
662665
t.Parallel()
663-
addGatewaysForBackendTLSPolicies(test.backendTLSPolicies, test.services, "nginx-gateway")
666+
addGatewaysForBackendTLSPolicies(test.backendTLSPolicies, test.services, "nginx-gateway", nil, logr.Discard())
664667
g.Expect(helpers.Diff(test.backendTLSPolicies, test.expected)).To(BeEmpty())
665668
})
666669
}
667670
}
671+
672+
func TestAddGatewaysForBackendTLSPoliciesAncestorLimit(t *testing.T) {
673+
t.Parallel()
674+
675+
// Create a test logger that captures log output
676+
var logBuf bytes.Buffer
677+
testLogger := logr.New(&testLogSink{buffer: &logBuf})
678+
679+
// Create BackendTLSPolicy with 16 ancestors (full)
680+
getAncestorRef := func(ctlrName, parentName string) v1alpha2.PolicyAncestorStatus {
681+
return v1alpha2.PolicyAncestorStatus{
682+
ControllerName: gatewayv1.GatewayController(ctlrName),
683+
AncestorRef: gatewayv1.ParentReference{
684+
Name: gatewayv1.ObjectName(parentName),
685+
Namespace: helpers.GetPointer(gatewayv1.Namespace("test")),
686+
Group: helpers.GetPointer[gatewayv1.Group](gatewayv1.GroupName),
687+
Kind: helpers.GetPointer[gatewayv1.Kind](kinds.Gateway),
688+
},
689+
}
690+
}
691+
692+
// Create 16 ancestors from different controllers to simulate full list
693+
fullAncestors := make([]v1alpha2.PolicyAncestorStatus, 16)
694+
for i := range 16 {
695+
fullAncestors[i] = getAncestorRef("other-controller", "other-gateway")
696+
}
697+
698+
btpWithFullAncestors := &BackendTLSPolicy{
699+
Source: &v1alpha3.BackendTLSPolicy{
700+
ObjectMeta: metav1.ObjectMeta{
701+
Name: "btp-full-ancestors",
702+
Namespace: "test",
703+
},
704+
Spec: v1alpha3.BackendTLSPolicySpec{
705+
TargetRefs: []v1alpha2.LocalPolicyTargetReferenceWithSectionName{
706+
{
707+
LocalPolicyTargetReference: v1alpha2.LocalPolicyTargetReference{
708+
Kind: "Service",
709+
Name: "service1",
710+
},
711+
},
712+
},
713+
},
714+
Status: v1alpha2.PolicyStatus{
715+
Ancestors: fullAncestors,
716+
},
717+
},
718+
}
719+
720+
btpNormal := &BackendTLSPolicy{
721+
Source: &v1alpha3.BackendTLSPolicy{
722+
ObjectMeta: metav1.ObjectMeta{
723+
Name: "btp-normal",
724+
Namespace: "test",
725+
},
726+
Spec: v1alpha3.BackendTLSPolicySpec{
727+
TargetRefs: []v1alpha2.LocalPolicyTargetReferenceWithSectionName{
728+
{
729+
LocalPolicyTargetReference: v1alpha2.LocalPolicyTargetReference{
730+
Kind: "Service",
731+
Name: "service2",
732+
},
733+
},
734+
},
735+
},
736+
Status: v1alpha2.PolicyStatus{
737+
Ancestors: []v1alpha2.PolicyAncestorStatus{}, // Empty ancestors list
738+
},
739+
},
740+
}
741+
742+
services := map[types.NamespacedName]*ReferencedService{
743+
{Namespace: "test", Name: "service1"}: {
744+
GatewayNsNames: map[types.NamespacedName]struct{}{
745+
{Namespace: "test", Name: "gateway1"}: {},
746+
},
747+
},
748+
{Namespace: "test", Name: "service2"}: {
749+
GatewayNsNames: map[types.NamespacedName]struct{}{
750+
{Namespace: "test", Name: "gateway2"}: {},
751+
},
752+
},
753+
}
754+
755+
// Create gateways - one will receive ancestor limit condition
756+
gateways := map[types.NamespacedName]*Gateway{
757+
{Namespace: "test", Name: "gateway1"}: {
758+
Source: &gatewayv1.Gateway{
759+
ObjectMeta: metav1.ObjectMeta{Name: "gateway1", Namespace: "test"},
760+
},
761+
Conditions: []conditions.Condition{}, // Start with empty conditions
762+
},
763+
{Namespace: "test", Name: "gateway2"}: {
764+
Source: &gatewayv1.Gateway{
765+
ObjectMeta: metav1.ObjectMeta{Name: "gateway2", Namespace: "test"},
766+
},
767+
Conditions: []conditions.Condition{}, // Start with empty conditions
768+
},
769+
}
770+
771+
backendTLSPolicies := map[types.NamespacedName]*BackendTLSPolicy{
772+
{Namespace: "test", Name: "btp-full-ancestors"}: btpWithFullAncestors,
773+
{Namespace: "test", Name: "btp-normal"}: btpNormal,
774+
}
775+
776+
g := NewWithT(t)
777+
778+
// Execute the function
779+
addGatewaysForBackendTLSPolicies(backendTLSPolicies, services, "nginx-gateway", gateways, testLogger)
780+
781+
// Verify that the policy with full ancestors doesn't get any gateways assigned
782+
g.Expect(btpWithFullAncestors.Gateways).To(BeEmpty(), "Policy with full ancestors should not get gateways assigned")
783+
784+
// Verify that the normal policy gets its gateway assigned
785+
g.Expect(btpNormal.Gateways).To(HaveLen(1))
786+
g.Expect(btpNormal.Gateways[0]).To(Equal(types.NamespacedName{Namespace: "test", Name: "gateway2"}))
787+
788+
// Verify that gateway1 received the ancestor limit condition
789+
gateway1 := gateways[types.NamespacedName{Namespace: "test", Name: "gateway1"}]
790+
g.Expect(gateway1.Conditions).To(HaveLen(1), "Gateway should have received ancestor limit condition")
791+
792+
condition := gateway1.Conditions[0]
793+
g.Expect(condition.Type).To(Equal(string(v1alpha2.PolicyConditionAccepted)))
794+
g.Expect(condition.Status).To(Equal(metav1.ConditionFalse))
795+
g.Expect(condition.Reason).To(Equal(string(conditions.PolicyReasonAncestorLimitReached)))
796+
g.Expect(condition.Message).To(ContainSubstring("ancestor status list has reached the maximum size of 16"))
797+
798+
// Verify that gateway2 did not receive any conditions (normal case)
799+
gateway2 := gateways[types.NamespacedName{Namespace: "test", Name: "gateway2"}]
800+
g.Expect(gateway2.Conditions).To(BeEmpty(), "Normal gateway should not have conditions")
801+
802+
// Verify logging function works - test the logging function directly
803+
LogAncestorLimitReached(testLogger, "test/btp-full-ancestors", "BackendTLSPolicy", "test/gateway1")
804+
logOutput := logBuf.String()
805+
806+
g.Expect(logOutput).To(ContainSubstring("Policy ancestor limit reached"))
807+
g.Expect(logOutput).To(ContainSubstring("policy=test/btp-full-ancestors"))
808+
g.Expect(logOutput).To(ContainSubstring("policyKind=BackendTLSPolicy"))
809+
g.Expect(logOutput).To(ContainSubstring("ancestor=test/gateway1"))
810+
}
811+
812+
func TestBackendTLSPolicyAncestorsFullFunc(t *testing.T) {
813+
t.Parallel()
814+
815+
getAncestorRef := func(ctlrName, parentName string) v1alpha2.PolicyAncestorStatus {
816+
return v1alpha2.PolicyAncestorStatus{
817+
ControllerName: gatewayv1.GatewayController(ctlrName),
818+
AncestorRef: gatewayv1.ParentReference{
819+
Name: gatewayv1.ObjectName(parentName),
820+
Namespace: helpers.GetPointer(gatewayv1.Namespace("test")),
821+
Group: helpers.GetPointer[gatewayv1.Group](gatewayv1.GroupName),
822+
Kind: helpers.GetPointer[gatewayv1.Kind](kinds.Gateway),
823+
},
824+
}
825+
}
826+
827+
tests := []struct {
828+
name string
829+
ctlrName string
830+
ancestors []v1alpha2.PolicyAncestorStatus
831+
expected bool
832+
}{
833+
{
834+
name: "empty ancestors list",
835+
ancestors: []v1alpha2.PolicyAncestorStatus{},
836+
ctlrName: "nginx-gateway",
837+
expected: false,
838+
},
839+
{
840+
name: "less than 16 ancestors",
841+
ancestors: []v1alpha2.PolicyAncestorStatus{
842+
getAncestorRef("other-controller", "gateway1"),
843+
getAncestorRef("other-controller", "gateway2"),
844+
},
845+
ctlrName: "nginx-gateway",
846+
expected: false,
847+
},
848+
{
849+
name: "exactly 16 ancestors, none from our controller",
850+
ancestors: func() []v1alpha2.PolicyAncestorStatus {
851+
ancestors := make([]v1alpha2.PolicyAncestorStatus, 16)
852+
for i := range 16 {
853+
ancestors[i] = getAncestorRef("other-controller", "gateway")
854+
}
855+
return ancestors
856+
}(),
857+
ctlrName: "nginx-gateway",
858+
expected: true,
859+
},
860+
{
861+
name: "exactly 16 ancestors, one from our controller",
862+
ancestors: func() []v1alpha2.PolicyAncestorStatus {
863+
ancestors := make([]v1alpha2.PolicyAncestorStatus, 16)
864+
for i := range 15 {
865+
ancestors[i] = getAncestorRef("other-controller", "gateway")
866+
}
867+
ancestors[15] = getAncestorRef("nginx-gateway", "our-gateway")
868+
return ancestors
869+
}(),
870+
ctlrName: "nginx-gateway",
871+
expected: false, // Not full because we can overwrite our own entry
872+
},
873+
{
874+
name: "more than 16 ancestors, none from our controller",
875+
ancestors: func() []v1alpha2.PolicyAncestorStatus {
876+
ancestors := make([]v1alpha2.PolicyAncestorStatus, 20)
877+
for i := range 20 {
878+
ancestors[i] = getAncestorRef("other-controller", "gateway")
879+
}
880+
return ancestors
881+
}(),
882+
ctlrName: "nginx-gateway",
883+
expected: true,
884+
},
885+
{
886+
name: "more than 16 ancestors, one from our controller",
887+
ancestors: func() []v1alpha2.PolicyAncestorStatus {
888+
ancestors := make([]v1alpha2.PolicyAncestorStatus, 20)
889+
for i := range 19 {
890+
ancestors[i] = getAncestorRef("other-controller", "gateway")
891+
}
892+
ancestors[19] = getAncestorRef("nginx-gateway", "our-gateway")
893+
return ancestors
894+
}(),
895+
ctlrName: "nginx-gateway",
896+
expected: false, // Not full because we can overwrite our own entry
897+
},
898+
}
899+
900+
for _, test := range tests {
901+
t.Run(test.name, func(t *testing.T) {
902+
t.Parallel()
903+
g := NewWithT(t)
904+
905+
result := backendTLSPolicyAncestorsFull(test.ancestors, test.ctlrName)
906+
g.Expect(result).To(Equal(test.expected))
907+
})
908+
}
909+
}
910+
911+
// testLogSink implements logr.LogSink for testing.
912+
type testLogSink struct {
913+
buffer *bytes.Buffer
914+
}
915+
916+
func (s *testLogSink) Init(_ logr.RuntimeInfo) {}
917+
918+
func (s *testLogSink) Enabled(_ int) bool {
919+
return true
920+
}
921+
922+
func (s *testLogSink) Info(_ int, msg string, keysAndValues ...interface{}) {
923+
s.buffer.WriteString(msg)
924+
for i := 0; i < len(keysAndValues); i += 2 {
925+
if i+1 < len(keysAndValues) {
926+
s.buffer.WriteString(" ")
927+
if key, ok := keysAndValues[i].(string); ok {
928+
s.buffer.WriteString(key)
929+
}
930+
s.buffer.WriteString("=")
931+
if value, ok := keysAndValues[i+1].(string); ok {
932+
s.buffer.WriteString(value)
933+
}
934+
}
935+
}
936+
s.buffer.WriteString("\n")
937+
}
938+
939+
func (s *testLogSink) Error(err error, msg string, _ ...interface{}) {
940+
s.buffer.WriteString("ERROR: ")
941+
s.buffer.WriteString(msg)
942+
s.buffer.WriteString(" error=")
943+
s.buffer.WriteString(err.Error())
944+
s.buffer.WriteString("\n")
945+
}
946+
947+
func (s *testLogSink) WithValues(_ ...interface{}) logr.LogSink {
948+
return s
949+
}
950+
951+
func (s *testLogSink) WithName(_ string) logr.LogSink {
952+
return s
953+
}

internal/controller/state/graph/graph.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,6 @@ func BuildGraph(
202202
validators validation.Validators,
203203
logger logr.Logger,
204204
) *Graph {
205-
// Set the logger for the graph package
206-
SetLogger(logger)
207205
processedGwClasses, gcExists := processGatewayClasses(state.GatewayClasses, gcName, controllerName)
208206
if gcExists && processedGwClasses.Winner == nil {
209207
// configured GatewayClass does not reference this controller
@@ -274,7 +272,7 @@ func BuildGraph(
274272

275273
referencedServices := buildReferencedServices(routes, l4routes, gws)
276274

277-
addGatewaysForBackendTLSPolicies(processedBackendTLSPolicies, referencedServices, controllerName)
275+
addGatewaysForBackendTLSPolicies(processedBackendTLSPolicies, referencedServices, controllerName, gws, logger)
278276

279277
// policies must be processed last because they rely on the state of the other resources in the graph
280278
processedPolicies := processPolicies(
@@ -307,7 +305,7 @@ func BuildGraph(
307305
PlusSecrets: plusSecrets,
308306
}
309307

310-
g.attachPolicies(validators.PolicyValidator, controllerName)
308+
g.attachPolicies(validators.PolicyValidator, controllerName, logger)
311309

312310
return g
313311
}

0 commit comments

Comments
 (0)