diff --git a/cmd/csi-sanity/sanity_test.go b/cmd/csi-sanity/sanity_test.go index 5d288154..4410934d 100644 --- a/cmd/csi-sanity/sanity_test.go +++ b/cmd/csi-sanity/sanity_test.go @@ -29,16 +29,16 @@ const ( ) var ( - VERSION = "(dev)" - endpoint string - mountPoint string - version bool + VERSION = "(dev)" + endpoint string + mountDir string + version bool ) func init() { flag.StringVar(&endpoint, prefix+"endpoint", "", "CSI endpoint") flag.BoolVar(&version, prefix+"version", false, "Version of this program") - flag.StringVar(&mountPoint, prefix+"mountpoint", os.TempDir()+"/csi", "Mount point for NodePublish") + flag.StringVar(&mountDir, prefix+"mountdir", os.TempDir()+"/csi", "Mount point for NodePublish") flag.Parse() } @@ -50,5 +50,5 @@ func TestSanity(t *testing.T) { if len(endpoint) == 0 { t.Fatalf("--%sendpoint must be provided with an CSI endpoint", prefix) } - sanity.Test(t, endpoint, mountPoint) + sanity.Test(t, endpoint, mountDir) } diff --git a/mock/service/controller.go b/mock/service/controller.go index e2d802d7..721b4d30 100644 --- a/mock/service/controller.go +++ b/mock/service/controller.go @@ -162,6 +162,10 @@ func (s *service) ControllerUnpublishVolume( s.volsRWL.Lock() defer s.volsRWL.Unlock() + if len(req.GetVolumeId()) == 0 { + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("Expected a volume ID, got none")) + } + i, v := s.findVolNoLock("id", req.VolumeId) if i < 0 { return nil, status.Error(codes.NotFound, req.VolumeId) diff --git a/pkg/sanity/controller.go b/pkg/sanity/controller.go index e6c6ad9b..8688811b 100644 --- a/pkg/sanity/controller.go +++ b/pkg/sanity/controller.go @@ -34,7 +34,7 @@ func verifyVolumeInfo(v *csi.Volume) { Expect(v.GetId()).NotTo(BeEmpty()) } -func isCapabilitySupported( +func isControllerCapabilitySupported( c csi.ControllerClient, capType csi.ControllerServiceCapability_RPC_Type, ) bool { @@ -97,7 +97,7 @@ var _ = Describe("GetCapacity [Controller Server]", func() { BeforeEach(func() { c = csi.NewControllerClient(conn) - if !isCapabilitySupported(c, csi.ControllerServiceCapability_RPC_GET_CAPACITY) { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_GET_CAPACITY) { Skip("GetCapacity not supported") } }) @@ -121,7 +121,7 @@ var _ = Describe("ListVolumes [Controller Server]", func() { BeforeEach(func() { c = csi.NewControllerClient(conn) - if !isCapabilitySupported(c, csi.ControllerServiceCapability_RPC_LIST_VOLUMES) { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_LIST_VOLUMES) { Skip("ListVolumes not supported") } }) @@ -152,7 +152,7 @@ var _ = Describe("CreateVolume [Controller Server]", func() { BeforeEach(func() { c = csi.NewControllerClient(conn) - if !isCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME) { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME) { Skip("CreateVolume not supported") } }) @@ -419,7 +419,7 @@ var _ = Describe("DeleteVolume [Controller Server]", func() { BeforeEach(func() { c = csi.NewControllerClient(conn) - if !isCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME) { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME) { Skip("DeleteVolume not supported") } }) @@ -585,7 +585,7 @@ var _ = Describe("ControllerPublishVolume [Controller Server]", func() { c = csi.NewControllerClient(conn) n = csi.NewNodeClient(conn) - if !isCapabilitySupported(c, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) { Skip("ControllerPublishVolume not supported") } }) @@ -715,7 +715,7 @@ var _ = Describe("ControllerUnpublishVolume [Controller Server]", func() { c = csi.NewControllerClient(conn) n = csi.NewNodeClient(conn) - if !isCapabilitySupported(c, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) { Skip("ControllerUnpublishVolume not supported") } }) @@ -729,7 +729,7 @@ var _ = Describe("ControllerUnpublishVolume [Controller Server]", func() { serverError, ok := status.FromError(err) Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.NotFound)) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) }) It("should return appropriate values (no optional values added)", func() { diff --git a/pkg/sanity/node.go b/pkg/sanity/node.go index e3f39022..4f923173 100644 --- a/pkg/sanity/node.go +++ b/pkg/sanity/node.go @@ -29,6 +29,26 @@ import ( . "github.com/onsi/gomega" ) +func isNodeCapabilitySupported(c csi.NodeClient, + capType csi.NodeServiceCapability_RPC_Type, +) bool { + + caps, err := c.NodeGetCapabilities( + context.Background(), + &csi.NodeGetCapabilitiesRequest{}) + Expect(err).NotTo(HaveOccurred()) + Expect(caps).NotTo(BeNil()) + Expect(caps.GetCapabilities()).NotTo(BeNil()) + + for _, cap := range caps.GetCapabilities() { + Expect(cap.GetRpc()).NotTo(BeNil()) + if cap.GetRpc().GetType() == capType { + return true + } + } + return false +} + var _ = Describe("NodeGetCapabilities [Node Server]", func() { var ( c csi.NodeClient @@ -53,6 +73,7 @@ var _ = Describe("NodeGetCapabilities [Node Server]", func() { switch cap.GetRpc().GetType() { case csi.NodeServiceCapability_RPC_UNKNOWN: + case csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME: default: Fail(fmt.Sprintf("Unknown capability: %v\n", cap.GetRpc().GetType())) } @@ -85,14 +106,16 @@ var _ = Describe("NodePublishVolume [Node Server]", func() { s csi.ControllerClient c csi.NodeClient controllerPublishSupported bool + nodeStageSupported bool ) BeforeEach(func() { s = csi.NewControllerClient(conn) c = csi.NewNodeClient(conn) - controllerPublishSupported = isCapabilitySupported( + controllerPublishSupported = isControllerCapabilitySupported( s, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) + nodeStageSupported = isNodeCapabilitySupported(c, csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME) }) It("should fail when no volume id is provided", func() { @@ -137,109 +160,7 @@ var _ = Describe("NodePublishVolume [Node Server]", func() { }) It("should return appropriate values (no optional values added)", func() { - - // Create Volume First - By("creating a single node writer volume") - name := "sanity" - vol, err := s.CreateVolume( - context.Background(), - &csi.CreateVolumeRequest{ - Name: name, - VolumeCapabilities: []*csi.VolumeCapability{ - { - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - }, - }, - }, - }) - Expect(err).NotTo(HaveOccurred()) - Expect(vol).NotTo(BeNil()) - Expect(vol.GetVolume()).NotTo(BeNil()) - Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) - - By("getting a node id") - nid, err := c.NodeGetId( - context.Background(), - &csi.NodeGetIdRequest{}) - Expect(err).NotTo(HaveOccurred()) - Expect(nid).NotTo(BeNil()) - Expect(nid.GetNodeId()).NotTo(BeEmpty()) - var conpubvol *csi.ControllerPublishVolumeResponse - if controllerPublishSupported { - By("controller publishing volume") - conpubvol, err = s.ControllerPublishVolume( - context.Background(), - &csi.ControllerPublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - NodeId: nid.GetNodeId(), - VolumeCapability: &csi.VolumeCapability{ - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - }, - }, - Readonly: false, - }) - Expect(err).NotTo(HaveOccurred()) - Expect(conpubvol).NotTo(BeNil()) - } - // NodePublishVolume - By("publishing the volume on a node") - nodepubvolRequest := &csi.NodePublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - TargetPath: csiTargetPath, - VolumeCapability: &csi.VolumeCapability{ - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - }, - }, - } - if controllerPublishSupported { - nodepubvolRequest.PublishInfo = conpubvol.GetPublishInfo() - } - nodepubvol, err := c.NodePublishVolume(context.Background(), nodepubvolRequest) - Expect(err).NotTo(HaveOccurred()) - Expect(nodepubvol).NotTo(BeNil()) - - // NodeUnpublishVolume - By("cleaning up calling nodeunpublish") - nodeunpubvol, err := c.NodeUnpublishVolume( - context.Background(), - &csi.NodeUnpublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - TargetPath: csiTargetPath, - }) - Expect(err).NotTo(HaveOccurred()) - Expect(nodeunpubvol).NotTo(BeNil()) - - if controllerPublishSupported { - By("cleaning up calling controllerunpublishing the volume") - nodeunpubvol, err := c.NodeUnpublishVolume( - context.Background(), - &csi.NodeUnpublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - TargetPath: csiTargetPath, - }) - Expect(err).NotTo(HaveOccurred()) - Expect(nodeunpubvol).NotTo(BeNil()) - } - - By("cleaning up deleting the volume") - _, err = s.DeleteVolume( - context.Background(), - &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - }) - Expect(err).NotTo(HaveOccurred()) + testFullWorkflowSuccess(s, c, controllerPublishSupported, nodeStageSupported) }) }) @@ -248,14 +169,16 @@ var _ = Describe("NodeUnpublishVolume [Node Server]", func() { s csi.ControllerClient c csi.NodeClient controllerPublishSupported bool + nodeStageSupported bool ) BeforeEach(func() { s = csi.NewControllerClient(conn) c = csi.NewNodeClient(conn) - controllerPublishSupported = isCapabilitySupported( + controllerPublishSupported = isControllerCapabilitySupported( s, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) + nodeStageSupported = isNodeCapabilitySupported(c, csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME) }) It("should fail when no volume id is provided", func() { @@ -285,58 +208,68 @@ var _ = Describe("NodeUnpublishVolume [Node Server]", func() { }) It("should return appropriate values (no optional values added)", func() { + testFullWorkflowSuccess(s, c, controllerPublishSupported, nodeStageSupported) + }) +}) - // Create Volume First - By("creating a single node writer volume") - name := "sanity" - vol, err := s.CreateVolume( +// TODO: Tests for NodeStageVolume/NodeUnstageVolume +func testFullWorkflowSuccess(s csi.ControllerClient, c csi.NodeClient, controllerPublishSupported, nodeStageSupported bool) { + // Create Volume First + By("creating a single node writer volume") + name := "sanity" + vol, err := s.CreateVolume( + context.Background(), + &csi.CreateVolumeRequest{ + Name: name, + VolumeCapabilities: []*csi.VolumeCapability{ + { + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + }, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(vol).NotTo(BeNil()) + Expect(vol.GetVolume()).NotTo(BeNil()) + Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + + By("getting a node id") + nid, err := c.NodeGetId( + context.Background(), + &csi.NodeGetIdRequest{}) + Expect(err).NotTo(HaveOccurred()) + Expect(nid).NotTo(BeNil()) + Expect(nid.GetNodeId()).NotTo(BeEmpty()) + var conpubvol *csi.ControllerPublishVolumeResponse + if controllerPublishSupported { + By("controller publishing volume") + conpubvol, err = s.ControllerPublishVolume( context.Background(), - &csi.CreateVolumeRequest{ - Name: name, - VolumeCapabilities: []*csi.VolumeCapability{ - { - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - }, + &csi.ControllerPublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + NodeId: nid.GetNodeId(), + VolumeCapability: &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, }, }, + Readonly: false, }) Expect(err).NotTo(HaveOccurred()) - Expect(vol).NotTo(BeNil()) - Expect(vol.GetVolume()).NotTo(BeNil()) - Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) - - // ControllerPublishVolume - var conpubvol *csi.ControllerPublishVolumeResponse - if controllerPublishSupported { - By("calling controllerpublish on the volume") - conpubvol, err = s.ControllerPublishVolume( - context.Background(), - &csi.ControllerPublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - NodeId: "io.kubernetes.storage.mock", - VolumeCapability: &csi.VolumeCapability{ - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - }, - }, - Readonly: false, - }) - Expect(err).NotTo(HaveOccurred()) - Expect(conpubvol).NotTo(BeNil()) - } - - // NodePublishVolume - By("publishing the volume on a node") - nodepubvolRequest := &csi.NodePublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - TargetPath: csiTargetPath, + Expect(conpubvol).NotTo(BeNil()) + } + // NodeStageVolume + if nodeStageSupported { + By("node staging volume") + nodeStageVolReq := &csi.NodeStageVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), VolumeCapability: &csi.VolumeCapability{ AccessType: &csi.VolumeCapability_Mount{ Mount: &csi.VolumeCapability_MountVolume{}, @@ -345,15 +278,53 @@ var _ = Describe("NodeUnpublishVolume [Node Server]", func() { Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, }, }, + StagingTargetPath: csiStagingTargetPath, } if controllerPublishSupported { - nodepubvolRequest.PublishInfo = conpubvol.GetPublishInfo() + nodeStageVolReq.PublishInfo = conpubvol.GetPublishInfo() } - nodepubvol, err := c.NodePublishVolume(context.Background(), nodepubvolRequest) + nodestagevol, err := c.NodeStageVolume( + context.Background(), nodeStageVolReq) Expect(err).NotTo(HaveOccurred()) - Expect(nodepubvol).NotTo(BeNil()) + Expect(nodestagevol).NotTo(BeNil()) + } + // NodePublishVolume + By("publishing the volume on a node") + nodepubvolRequest := &csi.NodePublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + TargetPath: csiTargetPath, + VolumeCapability: &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + } + if nodeStageSupported { + nodepubvolRequest.StagingTargetPath = csiStagingTargetPath + } + if controllerPublishSupported { + nodepubvolRequest.PublishInfo = conpubvol.GetPublishInfo() + } + nodepubvol, err := c.NodePublishVolume(context.Background(), nodepubvolRequest) + Expect(err).NotTo(HaveOccurred()) + Expect(nodepubvol).NotTo(BeNil()) + + // NodeUnpublishVolume + By("cleaning up calling nodeunpublish") + nodeunpubvol, err := c.NodeUnpublishVolume( + context.Background(), + &csi.NodeUnpublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + TargetPath: csiTargetPath, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(nodeunpubvol).NotTo(BeNil()) - // NodeUnpublishVolume + if controllerPublishSupported { + By("cleaning up calling controllerunpublishing the volume") nodeunpubvol, err := c.NodeUnpublishVolume( context.Background(), &csi.NodeUnpublishVolumeRequest{ @@ -362,27 +333,13 @@ var _ = Describe("NodeUnpublishVolume [Node Server]", func() { }) Expect(err).NotTo(HaveOccurred()) Expect(nodeunpubvol).NotTo(BeNil()) - - if controllerPublishSupported { - By("cleaning up unpublishing the volume") - nodeunpubvol, err := c.NodeUnpublishVolume( - context.Background(), - &csi.NodeUnpublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - TargetPath: csiTargetPath, - }) - Expect(err).NotTo(HaveOccurred()) - Expect(nodeunpubvol).NotTo(BeNil()) - } - - By("cleaning up deleting the volume") - _, err = s.DeleteVolume( - context.Background(), - &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - }) - Expect(err).NotTo(HaveOccurred()) - }) -}) - -// TODO: Tests for NodeStageVolume/NodeUnstageVolume + } + + By("cleaning up deleting the volume") + _, err = s.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + }) + Expect(err).NotTo(HaveOccurred()) +} diff --git a/pkg/sanity/sanity.go b/pkg/sanity/sanity.go index 0acb159c..c144c159 100644 --- a/pkg/sanity/sanity.go +++ b/pkg/sanity/sanity.go @@ -19,6 +19,7 @@ package sanity import ( "fmt" "os" + "path/filepath" "sync" "testing" @@ -31,19 +32,23 @@ import ( ) var ( - driverAddress string - csiTargetPath string - conn *grpc.ClientConn - lock sync.Mutex + driverAddress string + csiMountDir string + csiTargetPath string + csiStagingTargetPath string + conn *grpc.ClientConn + lock sync.Mutex ) // Test will test the CSI driver at the specified address -func Test(t *testing.T, address, mountPoint string) { +func Test(t *testing.T, address, mountDir string) { lock.Lock() defer lock.Unlock() driverAddress = address - csiTargetPath = mountPoint + csiMountDir = mountDir + csiTargetPath = filepath.Join(mountDir, "mount") + csiStagingTargetPath = filepath.Join(mountDir, "stage") RegisterFailHandler(Fail) RunSpecs(t, "CSI Driver Test Suite") } @@ -56,13 +61,22 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) By("creating mount directory") + err = createMountTargetLocation(csiMountDir) + Expect(err).NotTo(HaveOccurred()) + + By("creating mount and staging directories") err = createMountTargetLocation(csiTargetPath) Expect(err).NotTo(HaveOccurred()) + err = createMountTargetLocation(csiStagingTargetPath) + Expect(err).NotTo(HaveOccurred()) + }) var _ = AfterSuite(func() { conn.Close() os.Remove(csiTargetPath) + os.Remove(csiStagingTargetPath) + os.Remove(csiMountDir) }) func createMountTargetLocation(targetPath string) error {