Skip to content

Commit 8694d47

Browse files
committed
test: copy volume from snapshot unit tests
1 parent ce66bf8 commit 8694d47

File tree

1 file changed

+311
-3
lines changed

1 file changed

+311
-3
lines changed

pkg/nfs/controllerserver_test.go

Lines changed: 311 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package nfs
1818

1919
import (
20+
"archive/tar"
21+
"compress/gzip"
2022
"os"
2123
"path/filepath"
2224
"reflect"
@@ -31,6 +33,7 @@ import (
3133
"golang.org/x/net/context"
3234
"google.golang.org/grpc/codes"
3335
"google.golang.org/grpc/status"
36+
"google.golang.org/protobuf/types/known/timestamppb"
3437
mount "k8s.io/mount-utils"
3538
)
3639

@@ -352,6 +355,13 @@ func TestControllerGetCapabilities(t *testing.T) {
352355
},
353356
},
354357
},
358+
{
359+
Type: &csi.ControllerServiceCapability_Rpc{
360+
Rpc: &csi.ControllerServiceCapability_RPC{
361+
Type: csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
362+
},
363+
},
364+
},
355365
},
356366
},
357367
expectedErr: nil,
@@ -687,6 +697,8 @@ func TestCopyVolume(t *testing.T) {
687697
req *csi.CreateVolumeRequest
688698
dstVol *nfsVolume
689699
expectErr bool
700+
prepare func() error
701+
cleanup func() error
690702
}{
691703
{
692704
desc: "copy volume from valid volume",
@@ -707,6 +719,56 @@ func TestCopyVolume(t *testing.T) {
707719
subDir: "subdir",
708720
uuid: "dst-pv-name",
709721
},
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") },
710772
},
711773
{
712774
desc: "copy volume missing source id",
@@ -747,17 +809,263 @@ func TestCopyVolume(t *testing.T) {
747809
},
748810
expectErr: true,
749811
},
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+
},
753870
}
754871
for _, test := range cases {
755872
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+
}
756878
cs := initTestController(t)
757879
err := cs.copyVolume(context.TODO(), test.req, test.dstVol)
758880
if (err == nil) == test.expectErr {
759881
t.Errorf(`[test: %s] Error expectation mismatch, expected error: "%v", received: %q`, test.desc, test.expectErr, err)
760882
}
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+
}
761888
})
762889
}
763890
}
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

Comments
 (0)