@@ -17,6 +17,8 @@ limitations under the License.
17
17
package nfs
18
18
19
19
import (
20
+ "archive/tar"
21
+ "compress/gzip"
20
22
"os"
21
23
"path/filepath"
22
24
"reflect"
@@ -31,6 +33,7 @@ import (
31
33
"golang.org/x/net/context"
32
34
"google.golang.org/grpc/codes"
33
35
"google.golang.org/grpc/status"
36
+ "google.golang.org/protobuf/types/known/timestamppb"
34
37
mount "k8s.io/mount-utils"
35
38
)
36
39
@@ -352,6 +355,13 @@ func TestControllerGetCapabilities(t *testing.T) {
352
355
},
353
356
},
354
357
},
358
+ {
359
+ Type : & csi.ControllerServiceCapability_Rpc {
360
+ Rpc : & csi.ControllerServiceCapability_RPC {
361
+ Type : csi .ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT ,
362
+ },
363
+ },
364
+ },
355
365
},
356
366
},
357
367
expectedErr : nil ,
@@ -687,6 +697,8 @@ func TestCopyVolume(t *testing.T) {
687
697
req * csi.CreateVolumeRequest
688
698
dstVol * nfsVolume
689
699
expectErr bool
700
+ prepare func () error
701
+ cleanup func () error
690
702
}{
691
703
{
692
704
desc : "copy volume from valid volume" ,
@@ -707,6 +719,56 @@ func TestCopyVolume(t *testing.T) {
707
719
subDir : "subdir" ,
708
720
uuid : "dst-pv-name" ,
709
721
},
722
+ prepare : func () error { return os .MkdirAll ("/tmp/src-pv-name/subdir" , 0777 ) },
723
+ cleanup : func () error { return os .RemoveAll ("/tmp/src-pv-name" ) },
724
+ },
725
+ {
726
+ desc : "copy volume from valid snapshot" ,
727
+ req : & csi.CreateVolumeRequest {
728
+ Name : "snapshot-name" ,
729
+ VolumeContentSource : & csi.VolumeContentSource {
730
+ Type : & csi.VolumeContentSource_Snapshot {
731
+ Snapshot : & csi.VolumeContentSource_SnapshotSource {
732
+ SnapshotId : "nfs-server.default.svc.cluster.local#share#snapshot-name#snapshot-name#src-pv-name" ,
733
+ },
734
+ },
735
+ },
736
+ },
737
+ dstVol : & nfsVolume {
738
+ id : "nfs-server.default.svc.cluster.local#share#subdir#dst-pv-name" ,
739
+ server : "//nfs-server.default.svc.cluster.local" ,
740
+ baseDir : "share" ,
741
+ subDir : "subdir" ,
742
+ uuid : "dst-pv-name" ,
743
+ },
744
+ prepare : func () error {
745
+ if err := os .MkdirAll ("/tmp/snapshot-name/share/snapshot-name/" , 0777 ); err != nil {
746
+ return err
747
+ }
748
+ file , err := os .Create ("/tmp/snapshot-name/share/snapshot-name/src-pv-name.tar.gz" )
749
+ if err != nil {
750
+ return err
751
+ }
752
+ defer file .Close ()
753
+ gzipWriter := gzip .NewWriter (file )
754
+ defer gzipWriter .Close ()
755
+ tarWriter := tar .NewWriter (gzipWriter )
756
+ defer tarWriter .Close ()
757
+ body := "test file"
758
+ hdr := & tar.Header {
759
+ Name : "test.txt" ,
760
+ Mode : 0777 ,
761
+ Size : int64 (len (body )),
762
+ }
763
+ if err := tarWriter .WriteHeader (hdr ); err != nil {
764
+ return err
765
+ }
766
+ if _ , err := tarWriter .Write ([]byte (body )); err != nil {
767
+ return err
768
+ }
769
+ return nil
770
+ },
771
+ cleanup : func () error { return os .RemoveAll ("/tmp/snapshot-name" ) },
710
772
},
711
773
{
712
774
desc : "copy volume missing source id" ,
@@ -747,17 +809,263 @@ func TestCopyVolume(t *testing.T) {
747
809
},
748
810
expectErr : true ,
749
811
},
750
- }
751
- if err := os .MkdirAll ("/tmp/src-pv-name/subdir" , 0777 ); err != nil {
752
- t .Fatalf ("Unexpected error when creating srcVolume: %v" , err )
812
+ {
813
+ desc : "copy volume from broken snapshot" ,
814
+ req : & csi.CreateVolumeRequest {
815
+ Name : "snapshot-name" ,
816
+ VolumeContentSource : & csi.VolumeContentSource {
817
+ Type : & csi.VolumeContentSource_Snapshot {
818
+ Snapshot : & csi.VolumeContentSource_SnapshotSource {
819
+ SnapshotId : "nfs-server.default.svc.cluster.local#share#snapshot-name#snapshot-name#src-pv-name" ,
820
+ },
821
+ },
822
+ },
823
+ },
824
+ dstVol : & nfsVolume {
825
+ id : "nfs-server.default.svc.cluster.local#share#subdir#dst-pv-name" ,
826
+ server : "//nfs-server.default.svc.cluster.local" ,
827
+ baseDir : "share" ,
828
+ subDir : "subdir" ,
829
+ uuid : "dst-pv-name" ,
830
+ },
831
+ expectErr : true ,
832
+ },
833
+ {
834
+ desc : "copy volume from missing snapshot" ,
835
+ req : & csi.CreateVolumeRequest {
836
+ Name : "snapshot-name" ,
837
+ VolumeContentSource : & csi.VolumeContentSource {
838
+ Type : & csi.VolumeContentSource_Snapshot {
839
+ Snapshot : & csi.VolumeContentSource_SnapshotSource {},
840
+ },
841
+ },
842
+ },
843
+ dstVol : & nfsVolume {
844
+ id : "nfs-server.default.svc.cluster.local#share#subdir#dst-pv-name" ,
845
+ server : "//nfs-server.default.svc.cluster.local" ,
846
+ baseDir : "share" ,
847
+ subDir : "subdir" ,
848
+ uuid : "dst-pv-name" ,
849
+ },
850
+ expectErr : true ,
851
+ },
852
+ {
853
+ desc : "copy volume from snapshot into missing dst volume" ,
854
+ req : & csi.CreateVolumeRequest {
855
+ Name : "snapshot-name" ,
856
+ VolumeContentSource : & csi.VolumeContentSource {
857
+ Type : & csi.VolumeContentSource_Snapshot {
858
+ Snapshot : & csi.VolumeContentSource_SnapshotSource {},
859
+ },
860
+ },
861
+ },
862
+ dstVol : & nfsVolume {
863
+ server : "//nfs-server.default.svc.cluster.local" ,
864
+ baseDir : "share" ,
865
+ subDir : "subdir" ,
866
+ uuid : "dst-pv-name" ,
867
+ },
868
+ expectErr : true ,
869
+ },
753
870
}
754
871
for _ , test := range cases {
755
872
t .Run (test .desc , func (t * testing.T ) {
873
+ if test .prepare != nil {
874
+ if err := test .prepare (); err != nil {
875
+ t .Errorf (`[test: %s] prepare failed: "%v"` , test .desc , err )
876
+ }
877
+ }
756
878
cs := initTestController (t )
757
879
err := cs .copyVolume (context .TODO (), test .req , test .dstVol )
758
880
if (err == nil ) == test .expectErr {
759
881
t .Errorf (`[test: %s] Error expectation mismatch, expected error: "%v", received: %q` , test .desc , test .expectErr , err )
760
882
}
883
+ if test .cleanup != nil {
884
+ if err := test .cleanup (); err != nil {
885
+ t .Errorf (`[test: %s] cleanup failed: "%v"` , test .desc , err )
886
+ }
887
+ }
761
888
})
762
889
}
763
890
}
891
+
892
+ func TestCreateSnapshot (t * testing.T ) {
893
+ cases := []struct {
894
+ desc string
895
+ req * csi.CreateSnapshotRequest
896
+ expResp * csi.CreateSnapshotResponse
897
+ expectErr bool
898
+ prepare func () error
899
+ cleanup func () error
900
+ }{
901
+ {
902
+ desc : "create snapshot with valid request" ,
903
+ req : & csi.CreateSnapshotRequest {
904
+ SourceVolumeId : "nfs-server.default.svc.cluster.local#share#subdir#src-pv-name" ,
905
+ Name : "snapshot-name" ,
906
+ },
907
+ expResp : & csi.CreateSnapshotResponse {
908
+ Snapshot : & csi.Snapshot {
909
+ SnapshotId : "nfs-server.default.svc.cluster.local#share#snapshot-name#snapshot-name#src-pv-name" ,
910
+ SourceVolumeId : "nfs-server.default.svc.cluster.local#share#subdir#src-pv-name" ,
911
+ ReadyToUse : true ,
912
+ SizeBytes : 1 , // doesn't match exact size, just denotes non-zero size expected
913
+ CreationTime : timestamppb .Now (), // doesn't match exact timestamp, just denotes non-zero ts expected
914
+ },
915
+ },
916
+ prepare : func () error { return os .MkdirAll ("/tmp/src-pv-name/subdir" , 0777 ) },
917
+ cleanup : func () error { return os .RemoveAll ("/tmp/src-pv-name" ) },
918
+ },
919
+ {
920
+ desc : "create snapshot from nonexisting volume" ,
921
+ req : & csi.CreateSnapshotRequest {
922
+ SourceVolumeId : "nfs-server.default.svc.cluster.local#share#subdir#src-pv-name" ,
923
+ Name : "snapshot-name" ,
924
+ },
925
+ expectErr : true ,
926
+ },
927
+ }
928
+ for _ , test := range cases {
929
+ t .Run (test .desc , func (t * testing.T ) {
930
+ if test .prepare != nil {
931
+ if err := test .prepare (); err != nil {
932
+ t .Errorf (`[test: %s] prepare failed: "%v"` , test .desc , err )
933
+ }
934
+ }
935
+ cs := initTestController (t )
936
+ resp , err := cs .CreateSnapshot (context .TODO (), test .req )
937
+ if (err == nil ) == test .expectErr {
938
+ t .Errorf (`[test: %s] Error expectation mismatch, expected error: "%v", received: %q` , test .desc , test .expectErr , err )
939
+ }
940
+ if err := matchCreateSnapshotResponse (test .expResp , resp ); err != nil {
941
+ t .Errorf ("[test: %s] failed %q: got resp %+v, expected %+v" , test .desc , err , resp , test .expResp )
942
+ }
943
+ if test .cleanup != nil {
944
+ if err := test .cleanup (); err != nil {
945
+ t .Errorf (`[test: %s] cleanup failed: "%v"` , test .desc , err )
946
+ }
947
+ }
948
+ })
949
+ }
950
+ }
951
+
952
+ func TestDeleteSnapshot (t * testing.T ) {
953
+ cases := []struct {
954
+ desc string
955
+ req * csi.DeleteSnapshotRequest
956
+ expResp * csi.DeleteSnapshotResponse
957
+ expectErr bool
958
+ prepare func () error
959
+ cleanup func () error
960
+ }{
961
+ {
962
+ desc : "delete valid snapshot" ,
963
+ req : & csi.DeleteSnapshotRequest {
964
+ SnapshotId : "nfs-server.default.svc.cluster.local#share#snapshot-name#snapshot-name#src-pv-name" ,
965
+ },
966
+ expResp : & csi.DeleteSnapshotResponse {},
967
+ prepare : func () error {
968
+ if err := os .MkdirAll ("/tmp/snapshot-name/snapshot-name/" , 0777 ); err != nil {
969
+ return err
970
+ }
971
+ f , err := os .OpenFile ("/tmp/snapshot-name/snapshot-name/src-pv-name.tar.gz" , os .O_CREATE , 0777 )
972
+ if err != nil {
973
+ return err
974
+ }
975
+ return f .Close ()
976
+ },
977
+ cleanup : func () error { return os .RemoveAll ("/tmp/snapshot-name" ) },
978
+ },
979
+ {
980
+ desc : "delete nonexisting snapshot" ,
981
+ req : & csi.DeleteSnapshotRequest {
982
+ SnapshotId : "nfs-server.default.svc.cluster.local#share#snapshot-name#snapshot-name#src-pv-name" ,
983
+ },
984
+ expResp : & csi.DeleteSnapshotResponse {},
985
+ },
986
+ {
987
+ desc : "delete snapshot with improper id" ,
988
+ req : & csi.DeleteSnapshotRequest {
989
+ SnapshotId : "incorrect-snap-id" ,
990
+ },
991
+ expResp : & csi.DeleteSnapshotResponse {},
992
+ },
993
+ {
994
+ desc : "delete valid snapshot with mount options" ,
995
+ req : & csi.DeleteSnapshotRequest {
996
+ SnapshotId : "nfs-server.default.svc.cluster.local#share#snapshot-name#snapshot-name#src-pv-name" ,
997
+ Secrets : map [string ]string {"mountoptions" : "nfsvers=4.1" },
998
+ },
999
+ expResp : & csi.DeleteSnapshotResponse {},
1000
+ prepare : func () error {
1001
+ if err := os .MkdirAll ("/tmp/snapshot-name/snapshot-name/" , 0777 ); err != nil {
1002
+ return err
1003
+ }
1004
+ f , err := os .OpenFile ("/tmp/snapshot-name/snapshot-name/src-pv-name.tar.gz" , os .O_CREATE , 0777 )
1005
+ if err != nil {
1006
+ return err
1007
+ }
1008
+ return f .Close ()
1009
+ },
1010
+ cleanup : func () error { return os .RemoveAll ("/tmp/snapshot-name" ) },
1011
+ },
1012
+ }
1013
+ for _ , test := range cases {
1014
+ t .Run (test .desc , func (t * testing.T ) {
1015
+ if test .prepare != nil {
1016
+ if err := test .prepare (); err != nil {
1017
+ t .Errorf (`[test: %s] prepare failed: "%v"` , test .desc , err )
1018
+ }
1019
+ }
1020
+ cs := initTestController (t )
1021
+ resp , err := cs .DeleteSnapshot (context .TODO (), test .req )
1022
+ if (err == nil ) == test .expectErr {
1023
+ t .Errorf (`[test: %s] Error expectation mismatch, expected error: "%v", received: %q` , test .desc , test .expectErr , err )
1024
+ }
1025
+ if ! reflect .DeepEqual (test .expResp , resp ) {
1026
+ t .Errorf ("[test: %s] got resp %+v, expected %+v" , test .desc , resp , test .expResp )
1027
+ }
1028
+ if test .cleanup != nil {
1029
+ if err := test .cleanup (); err != nil {
1030
+ t .Errorf (`[test: %s] cleanup failed: "%v"` , test .desc , err )
1031
+ }
1032
+ }
1033
+ })
1034
+ }
1035
+ }
1036
+
1037
+ func matchCreateSnapshotResponse (e , r * csi.CreateSnapshotResponse ) error {
1038
+ if e == nil && r == nil {
1039
+ return nil
1040
+ }
1041
+ if e == nil || e .Snapshot == nil {
1042
+ return fmt .Errorf ("expected nil response" )
1043
+ }
1044
+ if r == nil || r .Snapshot == nil {
1045
+ return fmt .Errorf ("unexpected nil response" )
1046
+ }
1047
+ es , rs := e .Snapshot , r .Snapshot
1048
+
1049
+ var errs []string
1050
+ // comparing ts and size just for presence, not the exact value
1051
+ if es .CreationTime .IsValid () != rs .CreationTime .IsValid () {
1052
+ errs = append (errs , "CreationTime" )
1053
+ }
1054
+ if (es .SizeBytes == 0 ) != (rs .SizeBytes == 0 ) {
1055
+ errs = append (errs , "SizeBytes" )
1056
+ }
1057
+ // comparing remaining fields for exact match
1058
+ if es .ReadyToUse != rs .ReadyToUse {
1059
+ errs = append (errs , "ReadyToUse" )
1060
+ }
1061
+ if es .SnapshotId != rs .SnapshotId {
1062
+ errs = append (errs , "SnapshotId" )
1063
+ }
1064
+ if es .SourceVolumeId != rs .SourceVolumeId {
1065
+ errs = append (errs , "SourceVolumeId" )
1066
+ }
1067
+ if len (errs ) == 0 {
1068
+ return nil
1069
+ }
1070
+ return fmt .Errorf ("mismatch CreateSnapshotResponse in fields: %v" , strings .Join (errs , ", " ))
1071
+ }
0 commit comments