@@ -18,6 +18,7 @@ package cnsregistervolume
18
18
19
19
import (
20
20
"context"
21
+ "encoding/json"
21
22
"fmt"
22
23
"reflect"
23
24
"testing"
@@ -791,30 +792,43 @@ var _ = Describe("checkExistingPVCDataSourceRef", func() {
791
792
792
793
var _ = Describe ("validatePVCTopologyCompatibility" , func () {
793
794
var (
794
- ctx context.Context
795
- pvc * corev1.PersistentVolumeClaim
796
- volumeDatastoreURL string
797
- mockTopologyMgr * mockTopologyService
798
- mockVC * cnsvsphere.VirtualCenter
795
+ ctx context.Context
796
+ pvc * corev1.PersistentVolumeClaim
797
+ volumeDatastoreURL string
798
+ mockTopologyMgr * mockTopologyService
799
+ mockVC * cnsvsphere.VirtualCenter
800
+ datastoreAccessibleTopology []map [string ]string
801
+ mockK8sClient * k8sfake.Clientset
799
802
)
800
803
801
804
BeforeEach (func () {
802
805
ctx = context .Background ()
803
806
volumeDatastoreURL = "dummy-datastore-url"
804
807
mockTopologyMgr = & mockTopologyService {}
805
808
mockVC = & cnsvsphere.VirtualCenter {}
809
+ datastoreAccessibleTopology = []map [string ]string {
810
+ {"topology.kubernetes.io/zone" : "zone-1" },
811
+ }
806
812
807
813
pvc = & corev1.PersistentVolumeClaim {
808
814
ObjectMeta : metav1.ObjectMeta {
809
815
Name : "test-pvc" ,
810
816
Namespace : "test-namespace" ,
811
817
},
812
818
}
819
+
820
+ // Create a fake Kubernetes client
821
+ mockK8sClient = k8sfake .NewSimpleClientset ()
813
822
})
814
823
815
824
Context ("when PVC has no topology annotation" , func () {
816
825
It ("should return nil without error" , func () {
817
- err := validatePVCTopologyCompatibility (ctx , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC )
826
+ // Add the PVC to the fake client so it can be updated
827
+ _ , err := mockK8sClient .CoreV1 ().PersistentVolumeClaims (pvc .Namespace ).Create (ctx , pvc , metav1.CreateOptions {})
828
+ Expect (err ).To (BeNil ())
829
+
830
+ err = validatePVCTopologyCompatibility (ctx , mockK8sClient , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC ,
831
+ datastoreAccessibleTopology )
818
832
Expect (err ).To (BeNil ())
819
833
})
820
834
})
@@ -827,7 +841,12 @@ var _ = Describe("validatePVCTopologyCompatibility", func() {
827
841
})
828
842
829
843
It ("should return nil without error" , func () {
830
- err := validatePVCTopologyCompatibility (ctx , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC )
844
+ // Add the PVC to the fake client so it can be updated
845
+ _ , err := mockK8sClient .CoreV1 ().PersistentVolumeClaims (pvc .Namespace ).Create (ctx , pvc , metav1.CreateOptions {})
846
+ Expect (err ).To (BeNil ())
847
+
848
+ err = validatePVCTopologyCompatibility (ctx , mockK8sClient , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC ,
849
+ datastoreAccessibleTopology )
831
850
Expect (err ).To (BeNil ())
832
851
})
833
852
})
@@ -840,7 +859,8 @@ var _ = Describe("validatePVCTopologyCompatibility", func() {
840
859
})
841
860
842
861
It ("should return error for invalid JSON" , func () {
843
- err := validatePVCTopologyCompatibility (ctx , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC )
862
+ err := validatePVCTopologyCompatibility (ctx , mockK8sClient , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC ,
863
+ datastoreAccessibleTopology )
844
864
Expect (err ).ToNot (BeNil ())
845
865
Expect (err .Error ()).To (ContainSubstring ("failed to parse topology annotation" ))
846
866
})
@@ -855,7 +875,8 @@ var _ = Describe("validatePVCTopologyCompatibility", func() {
855
875
})
856
876
857
877
It ("should return error from topology manager" , func () {
858
- err := validatePVCTopologyCompatibility (ctx , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC )
878
+ err := validatePVCTopologyCompatibility (ctx , mockK8sClient , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC ,
879
+ datastoreAccessibleTopology )
859
880
Expect (err ).ToNot (BeNil ())
860
881
Expect (err .Error ()).To (ContainSubstring ("failed to get topology for volume datastore" ))
861
882
})
@@ -872,7 +893,8 @@ var _ = Describe("validatePVCTopologyCompatibility", func() {
872
893
})
873
894
874
895
It ("should return nil without error" , func () {
875
- err := validatePVCTopologyCompatibility (ctx , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC )
896
+ err := validatePVCTopologyCompatibility (ctx , mockK8sClient , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC ,
897
+ datastoreAccessibleTopology )
876
898
Expect (err ).To (BeNil ())
877
899
})
878
900
})
@@ -888,11 +910,124 @@ var _ = Describe("validatePVCTopologyCompatibility", func() {
888
910
})
889
911
890
912
It ("should return error for incompatible zones" , func () {
891
- err := validatePVCTopologyCompatibility (ctx , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC )
913
+ err := validatePVCTopologyCompatibility (ctx , mockK8sClient , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC ,
914
+ datastoreAccessibleTopology )
892
915
Expect (err ).ToNot (BeNil ())
893
916
Expect (err .Error ()).To (ContainSubstring ("is not compatible with volume placement" ))
894
917
})
895
918
})
919
+
920
+ Context ("when PVC exists without topology annotation and annotation needs to be added" , func () {
921
+ var originalPVC * corev1.PersistentVolumeClaim
922
+
923
+ BeforeEach (func () {
924
+ // Create a PVC with some existing annotations but no topology annotation
925
+ originalPVC = & corev1.PersistentVolumeClaim {
926
+ ObjectMeta : metav1.ObjectMeta {
927
+ Name : "existing-pvc" ,
928
+ Namespace : "test-namespace" ,
929
+ Annotations : map [string ]string {
930
+ "some.other/annotation" : "existing-value" ,
931
+ "another/annotation" : "another-value" ,
932
+ },
933
+ },
934
+ Spec : corev1.PersistentVolumeClaimSpec {
935
+ AccessModes : []corev1.PersistentVolumeAccessMode {corev1 .ReadWriteOnce },
936
+ },
937
+ }
938
+ pvc = originalPVC
939
+ })
940
+
941
+ It ("should add topology annotation to existing PVC and return nil" , func () {
942
+ // Verify PVC initially has no topology annotation
943
+ _ , exists := pvc .Annotations ["csi.vsphere.volume-accessible-topology" ]
944
+ Expect (exists ).To (BeFalse ())
945
+
946
+ // Add the PVC to the fake client so it can be updated
947
+ _ , err := mockK8sClient .CoreV1 ().PersistentVolumeClaims (pvc .Namespace ).Create (ctx , pvc , metav1.CreateOptions {})
948
+ Expect (err ).To (BeNil ())
949
+
950
+ // Call the function
951
+ err = validatePVCTopologyCompatibility (ctx , mockK8sClient , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC ,
952
+ datastoreAccessibleTopology )
953
+ Expect (err ).To (BeNil ())
954
+
955
+ // Verify topology annotation was added
956
+ topologyAnnotation , exists := pvc .Annotations ["csi.vsphere.volume-accessible-topology" ]
957
+ Expect (exists ).To (BeTrue ())
958
+ Expect (topologyAnnotation ).ToNot (BeEmpty ())
959
+
960
+ // Verify the annotation contains the expected topology data
961
+ expectedAnnotation := `[{"topology.kubernetes.io/zone":"zone-1"}]`
962
+ Expect (topologyAnnotation ).To (Equal (expectedAnnotation ))
963
+
964
+ // Verify existing annotations are preserved
965
+ Expect (pvc .Annotations ["some.other/annotation" ]).To (Equal ("existing-value" ))
966
+ Expect (pvc .Annotations ["another/annotation" ]).To (Equal ("another-value" ))
967
+ })
968
+
969
+ It ("should handle PVC with nil annotations map" , func () {
970
+ // Create PVC with nil annotations
971
+ pvcWithNilAnnotations := & corev1.PersistentVolumeClaim {
972
+ ObjectMeta : metav1.ObjectMeta {
973
+ Name : "pvc-nil-annotations" ,
974
+ Namespace : "test-namespace" ,
975
+ Annotations : nil ,
976
+ },
977
+ }
978
+
979
+ // Add the PVC to the fake client so it can be updated
980
+ _ , err := mockK8sClient .CoreV1 ().PersistentVolumeClaims (pvcWithNilAnnotations .Namespace ).Create (ctx ,
981
+ pvcWithNilAnnotations , metav1.CreateOptions {})
982
+ Expect (err ).To (BeNil ())
983
+
984
+ // Call the function
985
+ err = validatePVCTopologyCompatibility (ctx , mockK8sClient , pvcWithNilAnnotations , volumeDatastoreURL ,
986
+ mockTopologyMgr , mockVC , datastoreAccessibleTopology )
987
+ Expect (err ).To (BeNil ())
988
+
989
+ // Verify annotations map was created and topology annotation was added
990
+ Expect (pvcWithNilAnnotations .Annotations ).ToNot (BeNil ())
991
+ topologyAnnotation , exists := pvcWithNilAnnotations .Annotations ["csi.vsphere.volume-accessible-topology" ]
992
+ Expect (exists ).To (BeTrue ())
993
+ Expect (topologyAnnotation ).To (Equal (`[{"topology.kubernetes.io/zone":"zone-1"}]` ))
994
+ })
995
+
996
+ It ("should handle complex topology data with multiple zones" , func () {
997
+ // Use more complex topology data
998
+ complexTopology := []map [string ]string {
999
+ {"topology.kubernetes.io/zone" : "zone-a" , "topology.kubernetes.io/region" : "us-west" },
1000
+ {"topology.kubernetes.io/zone" : "zone-b" , "topology.kubernetes.io/region" : "us-west" },
1001
+ }
1002
+
1003
+ // Add the PVC to the fake client so it can be updated
1004
+ _ , err := mockK8sClient .CoreV1 ().PersistentVolumeClaims (pvc .Namespace ).Create (ctx , pvc , metav1.CreateOptions {})
1005
+ Expect (err ).To (BeNil ())
1006
+
1007
+ err = validatePVCTopologyCompatibility (ctx , mockK8sClient , pvc , volumeDatastoreURL , mockTopologyMgr , mockVC ,
1008
+ complexTopology )
1009
+ Expect (err ).To (BeNil ())
1010
+
1011
+ // Verify the complex topology was properly serialized
1012
+ topologyAnnotation := pvc .Annotations ["csi.vsphere.volume-accessible-topology" ]
1013
+ Expect (topologyAnnotation ).ToNot (BeEmpty ())
1014
+
1015
+ // Parse the annotation to verify it contains both topology segments
1016
+ var parsedTopology []map [string ]string
1017
+ err = json .Unmarshal ([]byte (topologyAnnotation ), & parsedTopology )
1018
+ Expect (err ).To (BeNil ())
1019
+ Expect (len (parsedTopology )).To (Equal (2 ))
1020
+ Expect (parsedTopology [0 ]).To (Equal (map [string ]string {
1021
+ "topology.kubernetes.io/zone" : "zone-a" ,
1022
+ "topology.kubernetes.io/region" : "us-west" ,
1023
+ }))
1024
+ Expect (parsedTopology [1 ]).To (Equal (map [string ]string {
1025
+ "topology.kubernetes.io/zone" : "zone-b" ,
1026
+ "topology.kubernetes.io/region" : "us-west" ,
1027
+ }))
1028
+ })
1029
+
1030
+ })
896
1031
})
897
1032
898
1033
var _ = Describe ("isTopologyCompatible" , func () {
0 commit comments