1
1
package graph
2
2
3
3
import (
4
+ "bytes"
4
5
"testing"
5
6
7
+ "github.com/go-logr/logr"
6
8
. "github.com/onsi/gomega"
7
9
v1 "k8s.io/api/core/v1"
8
10
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -11,6 +13,7 @@ import (
11
13
"sigs.k8s.io/gateway-api/apis/v1alpha2"
12
14
"sigs.k8s.io/gateway-api/apis/v1alpha3"
13
15
16
+ "github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/conditions"
14
17
"github.com/nginx/nginx-gateway-fabric/v2/internal/framework/helpers"
15
18
"github.com/nginx/nginx-gateway-fabric/v2/internal/framework/kinds"
16
19
)
@@ -660,8 +663,291 @@ func TestAddGatewaysForBackendTLSPolicies(t *testing.T) {
660
663
g := NewWithT (t )
661
664
t .Run (test .name , func (t * testing.T ) {
662
665
t .Parallel ()
663
- addGatewaysForBackendTLSPolicies (test .backendTLSPolicies , test .services , "nginx-gateway" )
666
+ addGatewaysForBackendTLSPolicies (test .backendTLSPolicies , test .services , "nginx-gateway" , nil , logr . Discard () )
664
667
g .Expect (helpers .Diff (test .backendTLSPolicies , test .expected )).To (BeEmpty ())
665
668
})
666
669
}
667
670
}
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