11package graph
22
33import (
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+ }
0 commit comments