diff --git a/.travis.yml b/.travis.yml index 414ec5fb..261662d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,7 @@ matrix: include: - go: 1.10.3 script: -- go fmt $(go list ./... | grep -v vendor) | wc -l | grep 0 -- go vet $(go list ./... | grep -v vendor) -- go test $(go list ./... | grep -v vendor | grep -v "cmd/csi-sanity") -- ./hack/e2e.sh +- make test after_success: - if [ "${TRAVIS_BRANCH}" == "master" ] && [ "${TRAVIS_PULL_REQUEST}" == "false" ]; then docker login -u "${DOCKER_USERNAME}" -p "${DOCKER_PASSWORD}" quay.io; diff --git a/Makefile b/Makefile index b31541f6..621cec71 100644 --- a/Makefile +++ b/Makefile @@ -38,5 +38,15 @@ container: $(APP) push: container docker push $(IMAGE_NAME):$(IMAGE_VERSION) -.PHONY: all clean container push - +test: + files=$$(find ./ -name '*.go' | grep -v '^./vendor' ); \ + if [ $$(gofmt -d $$files | wc -l) -ne 0 ]; then \ + echo "formatting errors:"; \ + gofmt -d $$files; \ + false; \ + fi + go vet $$(go list ./... | grep -v vendor) + go test $$(go list ./... | grep -v vendor | grep -v "cmd/csi-sanity") + ./hack/e2e.sh + +.PHONY: all clean container push test diff --git a/driver/driver.go b/driver/driver.go index c3bad11f..a8cd796f 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -19,7 +19,7 @@ limitations under the License. package driver import ( - context "context" + "context" "errors" "net" "sync" @@ -27,7 +27,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - csi "github.com/container-storage-interface/spec/lib/go/csi/v0" + "github.com/container-storage-interface/spec/lib/go/csi/v0" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) diff --git a/hack/e2e.sh b/hack/e2e.sh index 81f3a02e..777250eb 100755 --- a/hack/e2e.sh +++ b/hack/e2e.sh @@ -11,10 +11,10 @@ CSI_MOCK_VERSION="master" # See https://github.com/grpc/grpc/blob/master/doc/naming.md runTest() { - CSI_ENDPOINT=$1 mock & + CSI_ENDPOINT=$1 ./bin/mock & local pid=$! - csi-sanity $TESTARGS --csi.endpoint=$2; ret=$? + ./cmd/csi-sanity/csi-sanity $TESTARGS --csi.endpoint=$2; ret=$? kill -9 $pid if [ $ret -ne 0 ] ; then @@ -24,10 +24,10 @@ runTest() runTestWithCreds() { - CSI_ENDPOINT=$1 CSI_ENABLE_CREDS=true mock & + CSI_ENDPOINT=$1 CSI_ENABLE_CREDS=true ./bin/mock & local pid=$! - csi-sanity $TESTARGS --csi.endpoint=$2 --csi.secrets=mock/mocksecret.yaml; ret=$? + ./cmd/csi-sanity/csi-sanity $TESTARGS --csi.endpoint=$2 --csi.secrets=mock/mocksecret.yaml; ret=$? kill -9 $pid if [ $ret -ne 0 ] ; then @@ -35,7 +35,7 @@ runTestWithCreds() fi } -go install ./mock || exit 1 +go build -o bin/mock ./mock || exit 1 cd cmd/csi-sanity make clean install || exit 1 diff --git a/pkg/sanity/cleanup.go b/pkg/sanity/cleanup.go new file mode 100644 index 00000000..699efe7b --- /dev/null +++ b/pkg/sanity/cleanup.go @@ -0,0 +1,134 @@ +/* +Copyright 2018 Intel Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sanity + +import ( + "context" + "log" + + "github.com/container-storage-interface/spec/lib/go/csi/v0" + + . "github.com/onsi/ginkgo" +) + +// VolumeInfo keeps track of the information needed to delete a volume. +type VolumeInfo struct { + // Node on which the volume was published, empty if none + // or publishing is not supported. + NodeID string + + // Volume ID assigned by CreateVolume. + VolumeID string +} + +// Cleanup keeps track of resources, in particular volumes, which need +// to be freed when testing is done. +type Cleanup struct { + Context *SanityContext + ControllerClient csi.ControllerClient + NodeClient csi.NodeClient + ControllerPublishSupported bool + NodeStageSupported bool + + // Maps from volume name to the node ID for which the volume + // is published and the volume ID. + volumes map[string]VolumeInfo +} + +// RegisterVolume adds or updates an entry for the volume with the +// given name. +func (cl *Cleanup) RegisterVolume(name string, info VolumeInfo) { + if cl.volumes == nil { + cl.volumes = make(map[string]VolumeInfo) + } + cl.volumes[name] = info +} + +// MaybeRegisterVolume adds or updates an entry for the volume with +// the given name if CreateVolume was successful. +func (cl *Cleanup) MaybeRegisterVolume(name string, vol *csi.CreateVolumeResponse, err error) { + if err == nil && vol.GetVolume().GetId() != "" { + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) + } +} + +// UnregisterVolume removes the entry for the volume with the +// given name, thus preventing all cleanup operations for it. +func (cl *Cleanup) UnregisterVolume(name string) { + if cl.volumes != nil { + delete(cl.volumes, name) + } +} + +// DeleteVolumes stops using the registered volumes and tries to delete all of them. +func (cl *Cleanup) DeleteVolumes() { + if cl.volumes == nil { + return + } + logger := log.New(GinkgoWriter, "cleanup: ", 0) + ctx := context.Background() + + for name, info := range cl.volumes { + logger.Printf("deleting %s = %s", name, info.VolumeID) + if _, err := cl.NodeClient.NodeUnpublishVolume( + ctx, + &csi.NodeUnpublishVolumeRequest{ + VolumeId: info.VolumeID, + TargetPath: cl.Context.Config.TargetPath, + }, + ); err != nil { + logger.Printf("warning: NodeUnpublishVolume: %s", err) + } + + if cl.NodeStageSupported { + if _, err := cl.NodeClient.NodeUnstageVolume( + ctx, + &csi.NodeUnstageVolumeRequest{ + VolumeId: info.VolumeID, + StagingTargetPath: cl.Context.Config.StagingPath, + }, + ); err != nil { + logger.Printf("warning: NodeUnstageVolume: %s", err) + } + } + + if cl.ControllerPublishSupported && info.NodeID != "" { + if _, err := cl.ControllerClient.ControllerUnpublishVolume( + ctx, + &csi.ControllerUnpublishVolumeRequest{ + VolumeId: info.VolumeID, + NodeId: info.NodeID, + ControllerUnpublishSecrets: cl.Context.Secrets.ControllerUnpublishVolumeSecret, + }, + ); err != nil { + logger.Printf("warning: ControllerUnpublishVolume: %s", err) + } + } + + if _, err := cl.ControllerClient.DeleteVolume( + ctx, + &csi.DeleteVolumeRequest{ + VolumeId: info.VolumeID, + ControllerDeleteSecrets: cl.Context.Secrets.DeleteVolumeSecret, + }, + ); err != nil { + logger.Printf("error: DeleteVolume: %s", err) + } + + cl.UnregisterVolume(name) + } +} diff --git a/pkg/sanity/controller.go b/pkg/sanity/controller.go index 568d78b8..294a1e0d 100644 --- a/pkg/sanity/controller.go +++ b/pkg/sanity/controller.go @@ -17,13 +17,13 @@ limitations under the License. package sanity import ( + "context" "fmt" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/container-storage-interface/spec/lib/go/csi/v0" - "golang.org/x/net/context" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -77,279 +77,853 @@ func isControllerCapabilitySupported( return false } -var _ = DescribeSanity("ControllerGetCapabilities [Controller Server]", func(sc *SanityContext) { +var _ = DescribeSanity("Controller Service", func(sc *SanityContext) { var ( c csi.ControllerClient - ) - - BeforeEach(func() { - c = csi.NewControllerClient(sc.Conn) - }) - - It("should return appropriate capabilities", func() { - caps, err := c.ControllerGetCapabilities( - context.Background(), - &csi.ControllerGetCapabilitiesRequest{}) - - By("checking successful response") - Expect(err).NotTo(HaveOccurred()) - Expect(caps).NotTo(BeNil()) - Expect(caps.GetCapabilities()).NotTo(BeNil()) - - for _, cap := range caps.GetCapabilities() { - Expect(cap.GetRpc()).NotTo(BeNil()) - - switch cap.GetRpc().GetType() { - case csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME: - case csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME: - case csi.ControllerServiceCapability_RPC_LIST_VOLUMES: - case csi.ControllerServiceCapability_RPC_GET_CAPACITY: - case csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT: - case csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS: - default: - Fail(fmt.Sprintf("Unknown capability: %v\n", cap.GetRpc().GetType())) - } - } - }) -}) + n csi.NodeClient -var _ = DescribeSanity("GetCapacity [Controller Server]", func(sc *SanityContext) { - var ( - c csi.ControllerClient + cl *Cleanup ) BeforeEach(func() { c = csi.NewControllerClient(sc.Conn) + n = csi.NewNodeClient(sc.Conn) - if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_GET_CAPACITY) { - Skip("GetCapacity not supported") + cl = &Cleanup{ + NodeClient: n, + ControllerClient: c, + Context: sc, } }) - It("should return capacity (no optional values added)", func() { - _, err := c.GetCapacity( - context.Background(), - &csi.GetCapacityRequest{}) - Expect(err).NotTo(HaveOccurred()) - - // Since capacity is int64 we will not be checking it - // The value of zero is a possible value. + AfterEach(func() { + cl.DeleteVolumes() }) -}) - -var _ = DescribeSanity("ListVolumes [Controller Server]", func(sc *SanityContext) { - var ( - c csi.ControllerClient - ) - BeforeEach(func() { - c = csi.NewControllerClient(sc.Conn) + Describe("ControllerGetCapabilities", func() { + It("should return appropriate capabilities", func() { + caps, err := c.ControllerGetCapabilities( + context.Background(), + &csi.ControllerGetCapabilitiesRequest{}) - if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_LIST_VOLUMES) { - Skip("ListVolumes not supported") - } + By("checking successful response") + Expect(err).NotTo(HaveOccurred()) + Expect(caps).NotTo(BeNil()) + Expect(caps.GetCapabilities()).NotTo(BeNil()) + + for _, cap := range caps.GetCapabilities() { + Expect(cap.GetRpc()).NotTo(BeNil()) + + switch cap.GetRpc().GetType() { + case csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME: + case csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME: + case csi.ControllerServiceCapability_RPC_LIST_VOLUMES: + case csi.ControllerServiceCapability_RPC_GET_CAPACITY: + case csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT: + case csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS: + default: + Fail(fmt.Sprintf("Unknown capability: %v\n", cap.GetRpc().GetType())) + } + } + }) }) - It("should return appropriate values (no optional values added)", func() { - vols, err := c.ListVolumes( - context.Background(), - &csi.ListVolumesRequest{}) - Expect(err).NotTo(HaveOccurred()) - Expect(vols).NotTo(BeNil()) + Describe("GetCapacity", func() { + BeforeEach(func() { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_GET_CAPACITY) { + Skip("GetCapacity not supported") + } + }) - for _, vol := range vols.GetEntries() { - verifyVolumeInfo(vol.GetVolume()) - } + It("should return capacity (no optional values added)", func() { + _, err := c.GetCapacity( + context.Background(), + &csi.GetCapacityRequest{}) + Expect(err).NotTo(HaveOccurred()) + + // Since capacity is int64 we will not be checking it + // The value of zero is a possible value. + }) }) - // TODO: Add test to test for tokens + Describe("ListVolumes", func() { + BeforeEach(func() { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_LIST_VOLUMES) { + Skip("ListVolumes not supported") + } + }) - // TODO: Add test which checks list of volume is there when created, - // and not there when deleted. -}) + It("should return appropriate values (no optional values added)", func() { + vols, err := c.ListVolumes( + context.Background(), + &csi.ListVolumesRequest{}) + Expect(err).NotTo(HaveOccurred()) + Expect(vols).NotTo(BeNil()) -var _ = DescribeSanity("CreateVolume [Controller Server]", func(sc *SanityContext) { - var ( - c csi.ControllerClient - ) + for _, vol := range vols.GetEntries() { + verifyVolumeInfo(vol.GetVolume()) + } + }) - BeforeEach(func() { - c = csi.NewControllerClient(sc.Conn) + // TODO: Add test to test for tokens - if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME) { - Skip("CreateVolume not supported") - } + // TODO: Add test which checks list of volume is there when created, + // and not there when deleted. }) - It("should fail when no name is provided", func() { + Describe("CreateVolume", func() { + BeforeEach(func() { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME) { + Skip("CreateVolume not supported") + } + }) - req := &csi.CreateVolumeRequest{} + It("should fail when no name is provided", func() { + vol, err := c.CreateVolume( + context.Background(), + &csi.CreateVolumeRequest{ + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + cl.MaybeRegisterVolume("", vol, err) + Expect(err).To(HaveOccurred()) + + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) + + It("should fail when no volume capabilities are provided", func() { + name := uniqueString("sanity-controller-create-no-volume-capabilities") + vol, err := c.CreateVolume( + context.Background(), + &csi.CreateVolumeRequest{ + Name: name, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + cl.MaybeRegisterVolume(name, vol, err) + Expect(err).To(HaveOccurred()) + + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) + + It("should return appropriate values SingleNodeWriter NoCapacity Type:Mount", func() { + + By("creating a volume") + name := uniqueString("sanity-controller-create-single-no-capacity") + + vol, err := c.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, + }, + }, + }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(vol).NotTo(BeNil()) + Expect(vol.GetVolume()).NotTo(BeNil()) + Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } + By("cleaning up deleting the volume") - _, err := c.CreateVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) + + It("should return appropriate values SingleNodeWriter WithCapacity 1Gi Type:Mount", func() { + + By("creating a volume") + name := uniqueString("sanity-controller-create-single-with-capacity") + + vol, err := c.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, + }, + }, + }, + CapacityRange: &csi.CapacityRange{ + RequiredBytes: TestVolumeSize(sc), + }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + if serverError, ok := status.FromError(err); ok && + (serverError.Code() == codes.OutOfRange || serverError.Code() == codes.Unimplemented) { + Skip("Required bytes not supported") + } + Expect(err).NotTo(HaveOccurred()) + Expect(vol).NotTo(BeNil()) + Expect(vol.GetVolume()).NotTo(BeNil()) + Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) + Expect(vol.GetVolume().GetCapacityBytes()).To(BeNumerically(">=", TestVolumeSize(sc))) - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + By("cleaning up deleting the volume") + + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) + It("should not fail when requesting to create a volume with already exisiting name and same capacity.", func() { + + By("creating a volume") + name := uniqueString("sanity-controller-create-twice") + size := TestVolumeSize(sc) + + vol1, err := c.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, + }, + }, + }, + CapacityRange: &csi.CapacityRange{ + RequiredBytes: size, + }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(vol1).NotTo(BeNil()) + Expect(vol1.GetVolume()).NotTo(BeNil()) + Expect(vol1.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol1.GetVolume().GetId()}) + Expect(vol1.GetVolume().GetCapacityBytes()).To(BeNumerically(">=", size)) + + vol2, err := c.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, + }, + }, + }, + CapacityRange: &csi.CapacityRange{ + RequiredBytes: size, + }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(vol2).NotTo(BeNil()) + Expect(vol2.GetVolume()).NotTo(BeNil()) + Expect(vol2.GetVolume().GetId()).NotTo(BeEmpty()) + Expect(vol2.GetVolume().GetCapacityBytes()).To(BeNumerically(">=", size)) + Expect(vol1.GetVolume().GetId()).To(Equal(vol2.GetVolume().GetId())) + + By("cleaning up deleting the volume") + + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol1.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) + It("should fail when requesting to create a volume with already exisiting name and different capacity.", func() { + + By("creating a volume") + name := uniqueString("sanity-controller-create-twice-different") + size1 := TestVolumeSize(sc) + + vol1, err := c.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, + }, + }, + }, + CapacityRange: &csi.CapacityRange{ + RequiredBytes: size1, + LimitBytes: size1, + }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + Expect(err).ToNot(HaveOccurred()) + Expect(vol1).NotTo(BeNil()) + Expect(vol1.GetVolume()).NotTo(BeNil()) + Expect(vol1.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol1.GetVolume().GetId()}) + size2 := 2 * TestVolumeSize(sc) + + _, err = c.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, + }, + }, + }, + CapacityRange: &csi.CapacityRange{ + RequiredBytes: size2, + LimitBytes: size2, + }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.AlreadyExists)) + + By("cleaning up deleting the volume") + + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol1.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) }) - It("should fail when no volume capabilities are provided", func() { + Describe("DeleteVolume", func() { + BeforeEach(func() { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME) { + Skip("DeleteVolume not supported") + } + }) - req := &csi.CreateVolumeRequest{ - Name: "name", - } + It("should fail when no volume id is provided", func() { - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } + _, err := c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) - _, err := c.CreateVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) + It("should succeed when an invalid volume id is used", func() { - It("should return appropriate values SingleNodeWriter NoCapacity Type:Mount", func() { + _, err := c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: "reallyfakevolumeid", + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should return appropriate values (no optional values added)", func() { + + // Create Volume First + By("creating a volume") + name := uniqueString("sanity-controller-create-appropriate") + + vol, err := c.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, + }, + }, + }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(vol).NotTo(BeNil()) + Expect(vol.GetVolume()).NotTo(BeNil()) + Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) - By("creating a volume") - name := "sanity" + // Delete Volume + By("deleting a volume") - req := &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, + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) + }) + + Describe("ValidateVolumeCapabilities", func() { + It("should fail when no volume id is provided", func() { + + _, err := c.ValidateVolumeCapabilities( + context.Background(), + &csi.ValidateVolumeCapabilitiesRequest{}) + Expect(err).To(HaveOccurred()) + + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) + + It("should fail when no volume capabilities are provided", func() { + + _, err := c.ValidateVolumeCapabilities( + context.Background(), + &csi.ValidateVolumeCapabilitiesRequest{ + VolumeId: "id", + }) + Expect(err).To(HaveOccurred()) + + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) + + It("should return appropriate values (no optional values added)", func() { + + // Create Volume First + By("creating a single node writer volume") + name := uniqueString("sanity-controller-validate") + + vol, err := c.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, + }, + }, }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, }, - }, - } + ) + Expect(err).NotTo(HaveOccurred()) + Expect(vol).NotTo(BeNil()) + Expect(vol.GetVolume()).NotTo(BeNil()) + Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) + + // ValidateVolumeCapabilities + By("validating volume capabilities") + valivolcap, err := c.ValidateVolumeCapabilities( + context.Background(), + &csi.ValidateVolumeCapabilitiesRequest{ + VolumeId: vol.GetVolume().GetId(), + 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(valivolcap).NotTo(BeNil()) + Expect(valivolcap.GetSupported()).To(BeTrue()) - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } + By("cleaning up deleting the volume") - vol, err := c.CreateVolume(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - Expect(vol).NotTo(BeNil()) - Expect(vol.GetVolume()).NotTo(BeNil()) - Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) + + It("should fail when the requested volume does not exist", func() { + + _, err := c.ValidateVolumeCapabilities( + context.Background(), + &csi.ValidateVolumeCapabilitiesRequest{ + VolumeId: "some-vol-id", + VolumeCapabilities: []*csi.VolumeCapability{ + { + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + }, + }, + ) + Expect(err).To(HaveOccurred()) - By("cleaning up deleting the volume") + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.NotFound)) + }) + }) - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - } + Describe("ControllerPublishVolume", func() { + BeforeEach(func() { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) { + Skip("ControllerPublishVolume not supported") + } + }) - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } + It("should fail when no volume id is provided", func() { - _, err = c.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) - }) + _, err := c.ControllerPublishVolume( + context.Background(), + &csi.ControllerPublishVolumeRequest{ + ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) - It("should return appropriate values SingleNodeWriter WithCapacity 1Gi Type:Mount", func() { + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) - By("creating a volume") - name := "sanity" + It("should fail when no node id is provided", func() { - req := &csi.CreateVolumeRequest{ - Name: name, - VolumeCapabilities: []*csi.VolumeCapability{ - { - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, + _, err := c.ControllerPublishVolume( + context.Background(), + &csi.ControllerPublishVolumeRequest{ + VolumeId: "id", + ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) + + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) + + It("should fail when no volume capability is provided", func() { + + _, err := c.ControllerPublishVolume( + context.Background(), + &csi.ControllerPublishVolumeRequest{ + VolumeId: "id", + NodeId: "fakenode", + ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) + + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) + + It("should return appropriate values (no optional values added)", func() { + + // Create Volume First + By("creating a single node writer volume") + name := uniqueString("sanity-controller-publish") + + vol, err := c.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, + }, + }, }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(vol).NotTo(BeNil()) + Expect(vol.GetVolume()).NotTo(BeNil()) + Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) + + By("getting a node id") + nid, err := n.NodeGetId( + context.Background(), + &csi.NodeGetIdRequest{}) + Expect(err).NotTo(HaveOccurred()) + Expect(nid).NotTo(BeNil()) + Expect(nid.GetNodeId()).NotTo(BeEmpty()) + + // ControllerPublishVolume + By("calling controllerpublish on that volume") + + conpubvol, err := c.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, + ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret, }, - }, - CapacityRange: &csi.CapacityRange{ - RequiredBytes: TestVolumeSize(sc), - }, - } + ) + Expect(err).NotTo(HaveOccurred()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId(), NodeID: nid.GetNodeId()}) + Expect(conpubvol).NotTo(BeNil()) + + By("cleaning up unpublishing the volume") + + conunpubvol, err := c.ControllerUnpublishVolume( + context.Background(), + &csi.ControllerUnpublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + // NodeID is optional in ControllerUnpublishVolume + NodeId: nid.GetNodeId(), + ControllerUnpublishSecrets: sc.Secrets.ControllerUnpublishVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(conunpubvol).NotTo(BeNil()) - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } + By("cleaning up deleting the volume") - vol, err := c.CreateVolume(context.Background(), req) - if serverError, ok := status.FromError(err); ok { - if serverError.Code() == codes.OutOfRange || serverError.Code() == codes.Unimplemented { - Skip("Required bytes not supported") - } else { - Expect(err).NotTo(HaveOccurred()) - } - } else { + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) + + It("should fail when the volume does not exist", func() { + By("calling controller publish on a non-existent volume") + + conpubvol, err := c.ControllerPublishVolume( + context.Background(), + &csi.ControllerPublishVolumeRequest{ + VolumeId: "some-vol-id", + NodeId: "some-node-id", + VolumeCapability: &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + Readonly: false, + ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) + Expect(conpubvol).To(BeNil()) + + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.NotFound)) + }) + + It("should fail when the node does not exist", func() { + + // Create Volume First + By("creating a single node writer volume") + name := uniqueString("sanity-controller-wrong-node") + + vol, err := c.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, + }, + }, + }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, + }, + ) Expect(err).NotTo(HaveOccurred()) Expect(vol).NotTo(BeNil()) Expect(vol.GetVolume()).NotTo(BeNil()) Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) - Expect(vol.GetVolume().GetCapacityBytes()).To(BeNumerically(">=", TestVolumeSize(sc))) - } - By("cleaning up deleting the volume") + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - } + // ControllerPublishVolume + By("calling controllerpublish on that volume") - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } + conpubvol, err := c.ControllerPublishVolume( + context.Background(), + &csi.ControllerPublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + NodeId: "some-fake-node-id", + VolumeCapability: &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + Readonly: false, + ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) + Expect(conpubvol).To(BeNil()) - _, err = c.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) - }) - It("should not fail when requesting to create a volume with already exisiting name and same capacity.", func() { + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.NotFound)) - By("creating a volume") - name := "sanity" - size := TestVolumeSize(sc) + By("cleaning up deleting the volume") - req := &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, + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) + + It("should fail when the volume is already published but is incompatible", func() { + + // Create Volume First + By("creating a single node writer volume") + name := uniqueString("sanity-controller-published-incompatible") + + vol, err := c.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, + }, + }, }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, }, - }, - CapacityRange: &csi.CapacityRange{ - RequiredBytes: size, - }, - } + ) + Expect(err).NotTo(HaveOccurred()) + Expect(vol).NotTo(BeNil()) + Expect(vol.GetVolume()).NotTo(BeNil()) + Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } + By("getting a node id") + nid, err := n.NodeGetId( + context.Background(), + &csi.NodeGetIdRequest{}) + Expect(err).NotTo(HaveOccurred()) + Expect(nid).NotTo(BeNil()) + Expect(nid.GetNodeId()).NotTo(BeEmpty()) - vol1, err := c.CreateVolume(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - Expect(vol1).NotTo(BeNil()) - Expect(vol1.GetVolume()).NotTo(BeNil()) - Expect(vol1.GetVolume().GetId()).NotTo(BeEmpty()) - Expect(vol1.GetVolume().GetCapacityBytes()).To(BeNumerically(">=", size)) - - req2 := &csi.CreateVolumeRequest{ - Name: name, - VolumeCapabilities: []*csi.VolumeCapability{ - { + // ControllerPublishVolume + By("calling controllerpublish on that volume") + + pubReq := &csi.ControllerPublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + NodeId: nid.GetNodeId(), + VolumeCapability: &csi.VolumeCapability{ AccessType: &csi.VolumeCapability_Mount{ Mount: &csi.VolumeCapability_MountVolume{}, }, @@ -357,313 +931,122 @@ var _ = DescribeSanity("CreateVolume [Controller Server]", func(sc *SanityContex Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, }, }, - }, - CapacityRange: &csi.CapacityRange{ - RequiredBytes: size, - }, - } + Readonly: false, + ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret, + } - if sc.Secrets != nil { - req2.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } + conpubvol, err := c.ControllerPublishVolume(context.Background(), pubReq) + Expect(err).NotTo(HaveOccurred()) + Expect(conpubvol).NotTo(BeNil()) - vol2, err := c.CreateVolume(context.Background(), req2) - Expect(err).NotTo(HaveOccurred()) - Expect(vol2).NotTo(BeNil()) - Expect(vol2.GetVolume()).NotTo(BeNil()) - Expect(vol2.GetVolume().GetId()).NotTo(BeEmpty()) - Expect(vol2.GetVolume().GetCapacityBytes()).To(BeNumerically(">=", size)) - Expect(vol1.GetVolume().GetId()).To(Equal(vol2.GetVolume().GetId())) + // Publish again with different attributes. + pubReq.Readonly = true - By("cleaning up deleting the volume") + conpubvol, err = c.ControllerPublishVolume(context.Background(), pubReq) + Expect(err).To(HaveOccurred()) + Expect(conpubvol).To(BeNil()) - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol1.GetVolume().GetId(), - } + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.AlreadyExists)) - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } + By("cleaning up unpublishing the volume") - _, err = c.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) - }) - It("should fail when requesting to create a volume with already exisiting name and different capacity.", func() { - - By("creating a volume") - name := "sanity" - size1 := TestVolumeSize(sc) - - req := &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, - }, + conunpubvol, err := c.ControllerUnpublishVolume( + context.Background(), + &csi.ControllerUnpublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + // NodeID is optional in ControllerUnpublishVolume + NodeId: nid.GetNodeId(), + ControllerUnpublishSecrets: sc.Secrets.ControllerUnpublishVolumeSecret, }, - }, - CapacityRange: &csi.CapacityRange{ - RequiredBytes: size1, - LimitBytes: size1, - }, - } - - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } - - vol1, err := c.CreateVolume(context.Background(), req) - Expect(err).ToNot(HaveOccurred()) - Expect(vol1).NotTo(BeNil()) - Expect(vol1.GetVolume()).NotTo(BeNil()) - Expect(vol1.GetVolume().GetId()).NotTo(BeEmpty()) - size2 := 2 * TestVolumeSize(sc) - - req2 := &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, - }, - }, - }, - CapacityRange: &csi.CapacityRange{ - RequiredBytes: size2, - LimitBytes: size2, - }, - } - - if sc.Secrets != nil { - req2.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } - - _, err = c.CreateVolume(context.Background(), req2) - Expect(err).To(HaveOccurred()) - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.AlreadyExists)) - - By("cleaning up deleting the volume") - - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol1.GetVolume().GetId(), - } - - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } - - _, err = c.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) - }) -}) - -var _ = DescribeSanity("DeleteVolume [Controller Server]", func(sc *SanityContext) { - var ( - c csi.ControllerClient - ) - - BeforeEach(func() { - c = csi.NewControllerClient(sc.Conn) - - if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME) { - Skip("DeleteVolume not supported") - } - }) - - It("should fail when no volume id is provided", func() { - - req := &csi.DeleteVolumeRequest{} - - if sc.Secrets != nil { - req.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } - - _, err := c.DeleteVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) - - It("should succeed when an invalid volume id is used", func() { - - req := &csi.DeleteVolumeRequest{ - VolumeId: "reallyfakevolumeid", - } + ) - if sc.Secrets != nil { - req.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } - - _, err := c.DeleteVolume(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - }) - - It("should return appropriate values (no optional values added)", func() { + Expect(err).NotTo(HaveOccurred()) + Expect(conunpubvol).NotTo(BeNil()) - // Create Volume First - By("creating a volume") - name := "sanity" + By("cleaning up deleting the volume") - createReq := &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, - }, + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, }, - }, - } - - if sc.Secrets != nil { - createReq.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } - - vol, err := c.CreateVolume(context.Background(), createReq) - - Expect(err).NotTo(HaveOccurred()) - Expect(vol).NotTo(BeNil()) - Expect(vol.GetVolume()).NotTo(BeNil()) - Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) - - // Delete Volume - By("deleting a volume") - - req := &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - } - - if sc.Secrets != nil { - req.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } - - _, err = c.DeleteVolume(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - }) -}) - -var _ = DescribeSanity("ValidateVolumeCapabilities [Controller Server]", func(sc *SanityContext) { - var ( - c csi.ControllerClient - ) - - BeforeEach(func() { - c = csi.NewControllerClient(sc.Conn) - }) - - It("should fail when no volume id is provided", func() { - - _, err := c.ValidateVolumeCapabilities( - context.Background(), - &csi.ValidateVolumeCapabilitiesRequest{}) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) - - It("should fail when no volume capabilities are provided", func() { - - _, err := c.ValidateVolumeCapabilities( - context.Background(), - &csi.ValidateVolumeCapabilitiesRequest{ - VolumeId: "id", - }) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) }) - It("should return appropriate values (no optional values added)", func() { + Describe("ControllerUnpublishVolume", func() { + BeforeEach(func() { + if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) { + Skip("ControllerUnpublishVolume not supported") + } + }) - // Create Volume First - By("creating a single node writer volume") - name := "sanity" + It("should fail when no volume id is provided", func() { - req := &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, - }, + _, err := c.ControllerUnpublishVolume( + context.Background(), + &csi.ControllerUnpublishVolumeRequest{ + ControllerUnpublishSecrets: sc.Secrets.ControllerUnpublishVolumeSecret, }, - }, - } - - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } - - vol, err := c.CreateVolume(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - Expect(vol).NotTo(BeNil()) - Expect(vol.GetVolume()).NotTo(BeNil()) - Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) - - // ValidateVolumeCapabilities - By("validating volume capabilities") - valivolcap, err := c.ValidateVolumeCapabilities( - context.Background(), - &csi.ValidateVolumeCapabilitiesRequest{ - VolumeId: vol.GetVolume().GetId(), - VolumeCapabilities: []*csi.VolumeCapability{ - { - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + ) + Expect(err).To(HaveOccurred()) + + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) + + It("should return appropriate values (no optional values added)", func() { + + // Create Volume First + By("creating a single node writer volume") + name := uniqueString("sanity-controller-unpublish") + + vol, err := c.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, + }, }, }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, }, - }) - Expect(err).NotTo(HaveOccurred()) - Expect(valivolcap).NotTo(BeNil()) - Expect(valivolcap.GetSupported()).To(BeTrue()) - - By("cleaning up deleting the volume") - - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - } - - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } - - _, err = c.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) - }) - - It("should fail when the requested volume does not exist", func() { + ) + Expect(err).NotTo(HaveOccurred()) + Expect(vol).NotTo(BeNil()) + Expect(vol.GetVolume()).NotTo(BeNil()) + Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) - _, err := c.ValidateVolumeCapabilities( - context.Background(), - &csi.ValidateVolumeCapabilitiesRequest{ - VolumeId: "some-vol-id", - VolumeCapabilities: []*csi.VolumeCapability{ - { + By("getting a node id") + nid, err := n.NodeGetId( + context.Background(), + &csi.NodeGetIdRequest{}) + Expect(err).NotTo(HaveOccurred()) + Expect(nid).NotTo(BeNil()) + Expect(nid.GetNodeId()).NotTo(BeEmpty()) + + // ControllerPublishVolume + By("calling controllerpublish on that volume") + + conpubvol, err := c.ControllerPublishVolume( + context.Background(), + &csi.ControllerPublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + NodeId: nid.GetNodeId(), + VolumeCapability: &csi.VolumeCapability{ AccessType: &csi.VolumeCapability_Mount{ Mount: &csi.VolumeCapability_MountVolume{}, }, @@ -671,507 +1054,41 @@ var _ = DescribeSanity("ValidateVolumeCapabilities [Controller Server]", func(sc Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, }, }, + Readonly: false, + ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret, }, - }, - ) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.NotFound)) - }) -}) - -var _ = DescribeSanity("ControllerPublishVolume [Controller Server]", func(sc *SanityContext) { - var ( - c csi.ControllerClient - n csi.NodeClient - ) - - BeforeEach(func() { - c = csi.NewControllerClient(sc.Conn) - n = csi.NewNodeClient(sc.Conn) - - if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) { - Skip("ControllerPublishVolume not supported") - } - }) - - It("should fail when no volume id is provided", func() { - - req := &csi.ControllerPublishVolumeRequest{} - - if sc.Secrets != nil { - req.ControllerPublishSecrets = sc.Secrets.ControllerPublishVolumeSecret - } - - _, err := c.ControllerPublishVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) - - It("should fail when no node id is provided", func() { - - req := &csi.ControllerPublishVolumeRequest{ - VolumeId: "id", - } - - if sc.Secrets != nil { - req.ControllerPublishSecrets = sc.Secrets.ControllerPublishVolumeSecret - } - - _, err := c.ControllerPublishVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) - - It("should fail when no volume capability is provided", func() { - - req := &csi.ControllerPublishVolumeRequest{ - VolumeId: "id", - NodeId: "fakenode", - } - - if sc.Secrets != nil { - req.ControllerPublishSecrets = sc.Secrets.ControllerPublishVolumeSecret - } - - _, err := c.ControllerPublishVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) - - It("should return appropriate values (no optional values added)", func() { - - // Create Volume First - By("creating a single node writer volume") - name := "sanity" - req := &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, - }, - }, - }, - } - - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } - - vol, err := c.CreateVolume(context.Background(), req) - 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 := n.NodeGetId( - context.Background(), - &csi.NodeGetIdRequest{}) - Expect(err).NotTo(HaveOccurred()) - Expect(nid).NotTo(BeNil()) - Expect(nid.GetNodeId()).NotTo(BeEmpty()) - - // ControllerPublishVolume - By("calling controllerpublish on that volume") - - pubReq := &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, - } - - if sc.Secrets != nil { - pubReq.ControllerPublishSecrets = sc.Secrets.ControllerPublishVolumeSecret - } - - conpubvol, err := c.ControllerPublishVolume(context.Background(), pubReq) - Expect(err).NotTo(HaveOccurred()) - Expect(conpubvol).NotTo(BeNil()) - - By("cleaning up unpublishing the volume") - - unpubReq := &csi.ControllerUnpublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - // NodeID is optional in ControllerUnpublishVolume - NodeId: nid.GetNodeId(), - } - - if sc.Secrets != nil { - unpubReq.ControllerUnpublishSecrets = sc.Secrets.ControllerUnpublishVolumeSecret - } - - conunpubvol, err := c.ControllerUnpublishVolume(context.Background(), unpubReq) - Expect(err).NotTo(HaveOccurred()) - Expect(conunpubvol).NotTo(BeNil()) - - By("cleaning up deleting the volume") - - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - } - - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } - - _, err = c.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) - }) - - It("should fail when the volume does not exist", func() { - - By("calling controller publish on a non-existent volume") - - pubReq := &csi.ControllerPublishVolumeRequest{ - VolumeId: "some-vol-id", - NodeId: "some-node-id", - VolumeCapability: &csi.VolumeCapability{ - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - }, - }, - Readonly: false, - } - - if sc.Secrets != nil { - pubReq.ControllerPublishSecrets = sc.Secrets.ControllerPublishVolumeSecret - } - - conpubvol, err := c.ControllerPublishVolume(context.Background(), pubReq) - Expect(err).To(HaveOccurred()) - Expect(conpubvol).To(BeNil()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.NotFound)) - }) - - It("should fail when the node does not exist", func() { - - // Create Volume First - By("creating a single node writer volume") - name := "sanity" - req := &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, - }, - }, - }, - } - - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } - - vol, err := c.CreateVolume(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - Expect(vol).NotTo(BeNil()) - Expect(vol.GetVolume()).NotTo(BeNil()) - Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) - - // ControllerPublishVolume - By("calling controllerpublish on that volume") - - pubReq := &csi.ControllerPublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - NodeId: "some-fake-node-id", - VolumeCapability: &csi.VolumeCapability{ - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - }, - }, - Readonly: false, - } - - if sc.Secrets != nil { - pubReq.ControllerPublishSecrets = sc.Secrets.ControllerPublishVolumeSecret - } - - conpubvol, err := c.ControllerPublishVolume(context.Background(), pubReq) - Expect(err).To(HaveOccurred()) - Expect(conpubvol).To(BeNil()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.NotFound)) - - By("cleaning up deleting the volume") - - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - } - - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } - - _, err = c.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) - }) - - It("should fail when the volume is already published but is incompatible", func() { - - // Create Volume First - By("creating a single node writer volume") - name := "sanity" - req := &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, - }, - }, - }, - } - - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } - - vol, err := c.CreateVolume(context.Background(), req) - 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 := n.NodeGetId( - context.Background(), - &csi.NodeGetIdRequest{}) - Expect(err).NotTo(HaveOccurred()) - Expect(nid).NotTo(BeNil()) - Expect(nid.GetNodeId()).NotTo(BeEmpty()) - - // ControllerPublishVolume - By("calling controllerpublish on that volume") - - pubReq := &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, - } - - if sc.Secrets != nil { - pubReq.ControllerPublishSecrets = sc.Secrets.ControllerPublishVolumeSecret - } - - conpubvol, err := c.ControllerPublishVolume(context.Background(), pubReq) - Expect(err).NotTo(HaveOccurred()) - Expect(conpubvol).NotTo(BeNil()) - - // Publish again with different attributes. - pubReq.Readonly = true - - conpubvol, err = c.ControllerPublishVolume(context.Background(), pubReq) - Expect(err).To(HaveOccurred()) - Expect(conpubvol).To(BeNil()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.AlreadyExists)) - - By("cleaning up unpublishing the volume") - - unpubReq := &csi.ControllerUnpublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - // NodeID is optional in ControllerUnpublishVolume - NodeId: nid.GetNodeId(), - } - - if sc.Secrets != nil { - unpubReq.ControllerUnpublishSecrets = sc.Secrets.ControllerUnpublishVolumeSecret - } - - conunpubvol, err := c.ControllerUnpublishVolume(context.Background(), unpubReq) - Expect(err).NotTo(HaveOccurred()) - Expect(conunpubvol).NotTo(BeNil()) - - By("cleaning up deleting the volume") - - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - } - - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } - - _, err = c.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) - }) -}) - -var _ = DescribeSanity("ControllerUnpublishVolume [Controller Server]", func(sc *SanityContext) { - var ( - c csi.ControllerClient - n csi.NodeClient - ) - - BeforeEach(func() { - c = csi.NewControllerClient(sc.Conn) - n = csi.NewNodeClient(sc.Conn) - - if !isControllerCapabilitySupported(c, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) { - Skip("ControllerUnpublishVolume not supported") - } - }) - - It("should fail when no volume id is provided", func() { - - req := &csi.ControllerUnpublishVolumeRequest{} - - if sc.Secrets != nil { - req.ControllerUnpublishSecrets = sc.Secrets.ControllerUnpublishVolumeSecret - } - - _, err := c.ControllerUnpublishVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) - - It("should return appropriate values (no optional values added)", func() { - - // Create Volume First - By("creating a single node writer volume") - name := "sanity" - - req := &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()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId(), NodeID: nid.GetNodeId()}) + Expect(conpubvol).NotTo(BeNil()) + + // ControllerUnpublishVolume + By("calling controllerunpublish on that volume") + + conunpubvol, err := c.ControllerUnpublishVolume( + context.Background(), + &csi.ControllerUnpublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + // NodeID is optional in ControllerUnpublishVolume + NodeId: nid.GetNodeId(), + ControllerUnpublishSecrets: sc.Secrets.ControllerUnpublishVolumeSecret, }, - }, - } - - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } - - vol, err := c.CreateVolume(context.Background(), req) - 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 := n.NodeGetId( - context.Background(), - &csi.NodeGetIdRequest{}) - Expect(err).NotTo(HaveOccurred()) - Expect(nid).NotTo(BeNil()) - Expect(nid.GetNodeId()).NotTo(BeEmpty()) + ) + Expect(err).NotTo(HaveOccurred()) + Expect(conunpubvol).NotTo(BeNil()) - // ControllerPublishVolume - By("calling controllerpublish on that volume") + By("cleaning up deleting the volume") - pubReq := &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, + _, err = c.DeleteVolume( + context.Background(), + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, }, - }, - Readonly: false, - } - - if sc.Secrets != nil { - pubReq.ControllerPublishSecrets = sc.Secrets.ControllerPublishVolumeSecret - } - - conpubvol, err := c.ControllerPublishVolume(context.Background(), pubReq) - Expect(err).NotTo(HaveOccurred()) - Expect(conpubvol).NotTo(BeNil()) - - // ControllerUnpublishVolume - By("calling controllerunpublish on that volume") - - unpubReq := &csi.ControllerUnpublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - // NodeID is optional in ControllerUnpublishVolume - NodeId: nid.GetNodeId(), - } - - if sc.Secrets != nil { - unpubReq.ControllerUnpublishSecrets = sc.Secrets.ControllerUnpublishVolumeSecret - } - - conunpubvol, err := c.ControllerUnpublishVolume(context.Background(), unpubReq) - Expect(err).NotTo(HaveOccurred()) - Expect(conunpubvol).NotTo(BeNil()) - - By("cleaning up deleting the volume") - - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - } - - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } - - _, err = c.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) + ) + Expect(err).NotTo(HaveOccurred()) + cl.UnregisterVolume(name) + }) }) }) diff --git a/pkg/sanity/identity.go b/pkg/sanity/identity.go index 2d3e1e3e..e60439b3 100644 --- a/pkg/sanity/identity.go +++ b/pkg/sanity/identity.go @@ -17,20 +17,20 @@ limitations under the License. package sanity import ( + "context" "fmt" "regexp" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - csi "github.com/container-storage-interface/spec/lib/go/csi/v0" - context "golang.org/x/net/context" + "github.com/container-storage-interface/spec/lib/go/csi/v0" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) -var _ = DescribeSanity("GetPluginCapabilities [Identity Service]", func(sc *SanityContext) { +var _ = DescribeSanity("Identity Service", func(sc *SanityContext) { var ( c csi.IdentityClient ) @@ -39,75 +39,61 @@ var _ = DescribeSanity("GetPluginCapabilities [Identity Service]", func(sc *Sani c = csi.NewIdentityClient(sc.Conn) }) - It("should return appropriate capabilities", func() { - req := &csi.GetPluginCapabilitiesRequest{} - res, err := c.GetPluginCapabilities(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - Expect(res).NotTo(BeNil()) - - By("checking successful response") - Expect(res.GetCapabilities()).NotTo(BeNil()) - for _, cap := range res.GetCapabilities() { - switch cap.GetService().GetType() { - case csi.PluginCapability_Service_CONTROLLER_SERVICE: - case csi.PluginCapability_Service_ACCESSIBILITY_CONSTRAINTS: - default: - Fail(fmt.Sprintf("Unknown capability: %v\n", cap.GetService().GetType())) + Describe("GetPluginCapabilities", func() { + It("should return appropriate capabilities", func() { + req := &csi.GetPluginCapabilitiesRequest{} + res, err := c.GetPluginCapabilities(context.Background(), req) + Expect(err).NotTo(HaveOccurred()) + Expect(res).NotTo(BeNil()) + + By("checking successful response") + Expect(res.GetCapabilities()).NotTo(BeNil()) + for _, cap := range res.GetCapabilities() { + switch cap.GetService().GetType() { + case csi.PluginCapability_Service_CONTROLLER_SERVICE: + case csi.PluginCapability_Service_ACCESSIBILITY_CONSTRAINTS: + default: + Fail(fmt.Sprintf("Unknown capability: %v\n", cap.GetService().GetType())) + } } - } - }) - -}) - -var _ = DescribeSanity("Probe [Identity Service]", func(sc *SanityContext) { - var ( - c csi.IdentityClient - ) + }) - BeforeEach(func() { - c = csi.NewIdentityClient(sc.Conn) }) - It("should return appropriate information", func() { - req := &csi.ProbeRequest{} - res, err := c.Probe(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - Expect(res).NotTo(BeNil()) - - By("verifying return status") - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code() == codes.FailedPrecondition || - serverError.Code() == codes.OK).To(BeTrue()) - - if res.GetReady() != nil { - Expect(res.GetReady().GetValue() == true || - res.GetReady().GetValue() == false).To(BeTrue()) - } - }) -}) - -var _ = DescribeSanity("GetPluginInfo [Identity Server]", func(sc *SanityContext) { - var ( - c csi.IdentityClient - ) - - BeforeEach(func() { - c = csi.NewIdentityClient(sc.Conn) + Describe("Probe", func() { + It("should return appropriate information", func() { + req := &csi.ProbeRequest{} + res, err := c.Probe(context.Background(), req) + Expect(err).NotTo(HaveOccurred()) + Expect(res).NotTo(BeNil()) + + By("verifying return status") + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code() == codes.FailedPrecondition || + serverError.Code() == codes.OK).To(BeTrue()) + + if res.GetReady() != nil { + Expect(res.GetReady().GetValue() == true || + res.GetReady().GetValue() == false).To(BeTrue()) + } + }) }) - It("should return appropriate information", func() { - req := &csi.GetPluginInfoRequest{} - res, err := c.GetPluginInfo(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - Expect(res).NotTo(BeNil()) - - By("verifying name size and characters") - Expect(res.GetName()).ToNot(HaveLen(0)) - Expect(len(res.GetName())).To(BeNumerically("<=", 63)) - Expect(regexp. - MustCompile("^[a-zA-Z][A-Za-z0-9-\\.\\_]{0,61}[a-zA-Z]$"). - MatchString(res.GetName())).To(BeTrue()) + Describe("GetPluginInfo", func() { + It("should return appropriate information", func() { + req := &csi.GetPluginInfoRequest{} + res, err := c.GetPluginInfo(context.Background(), req) + Expect(err).NotTo(HaveOccurred()) + Expect(res).NotTo(BeNil()) + + By("verifying name size and characters") + Expect(res.GetName()).ToNot(HaveLen(0)) + Expect(len(res.GetName())).To(BeNumerically("<=", 63)) + Expect(regexp. + MustCompile("^[a-zA-Z][A-Za-z0-9-\\.\\_]{0,61}[a-zA-Z]$"). + MatchString(res.GetName())).To(BeTrue()) + }) }) }) diff --git a/pkg/sanity/node.go b/pkg/sanity/node.go index 117d317a..a98f5151 100644 --- a/pkg/sanity/node.go +++ b/pkg/sanity/node.go @@ -17,13 +17,13 @@ limitations under the License. package sanity import ( + "context" "fmt" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - csi "github.com/container-storage-interface/spec/lib/go/csi/v0" - context "golang.org/x/net/context" + "github.com/container-storage-interface/spec/lib/go/csi/v0" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -68,97 +68,20 @@ func isPluginCapabilitySupported(c csi.IdentityClient, return false } -var _ = DescribeSanity("NodeGetCapabilities [Node Server]", func(sc *SanityContext) { +var _ = DescribeSanity("Node Service", func(sc *SanityContext) { var ( - c csi.NodeClient - ) - - BeforeEach(func() { - c = csi.NewNodeClient(sc.Conn) - }) - - It("should return appropriate capabilities", func() { - caps, err := c.NodeGetCapabilities( - context.Background(), - &csi.NodeGetCapabilitiesRequest{}) - - By("checking successful response") - Expect(err).NotTo(HaveOccurred()) - Expect(caps).NotTo(BeNil()) + cl *Cleanup + c csi.NodeClient + s csi.ControllerClient - for _, cap := range caps.GetCapabilities() { - Expect(cap.GetRpc()).NotTo(BeNil()) - - 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())) - } - } - }) -}) - -var _ = DescribeSanity("NodeGetId [Node Server]", func(sc *SanityContext) { - var ( - c csi.NodeClient - ) - - BeforeEach(func() { - c = csi.NewNodeClient(sc.Conn) - }) - - It("should return appropriate values", func() { - nid, err := c.NodeGetId( - context.Background(), - &csi.NodeGetIdRequest{}) - - Expect(err).NotTo(HaveOccurred()) - Expect(nid).NotTo(BeNil()) - Expect(nid.GetNodeId()).NotTo(BeEmpty()) - }) -}) - -var _ = DescribeSanity("NodeGetInfo [Node Server]", func(sc *SanityContext) { - var ( - c csi.NodeClient - i csi.IdentityClient - accessibilityConstraintSupported bool - ) - - BeforeEach(func() { - c = csi.NewNodeClient(sc.Conn) - i = csi.NewIdentityClient(sc.Conn) - accessibilityConstraintSupported = isPluginCapabilitySupported(i, csi.PluginCapability_Service_ACCESSIBILITY_CONSTRAINTS) - }) - - It("should return approproate values", func() { - ninfo, err := c.NodeGetInfo( - context.Background(), - &csi.NodeGetInfoRequest{}) - - Expect(err).NotTo(HaveOccurred()) - Expect(ninfo).NotTo(BeNil()) - Expect(ninfo.GetNodeId()).NotTo(BeEmpty()) - Expect(ninfo.GetMaxVolumesPerNode()).NotTo(BeNumerically("<", 0)) - - if accessibilityConstraintSupported { - Expect(ninfo.GetAccessibleTopology()).NotTo(BeNil()) - } - }) -}) - -var _ = DescribeSanity("NodePublishVolume [Node Server]", func(sc *SanityContext) { - var ( - s csi.ControllerClient - c csi.NodeClient controllerPublishSupported bool nodeStageSupported bool ) BeforeEach(func() { - s = csi.NewControllerClient(sc.Conn) c = csi.NewNodeClient(sc.Conn) + s = csi.NewControllerClient(sc.Conn) + controllerPublishSupported = isControllerCapabilitySupported( s, csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) @@ -167,453 +90,439 @@ var _ = DescribeSanity("NodePublishVolume [Node Server]", func(sc *SanityContext err := createMountTargetLocation(sc.Config.StagingPath) Expect(err).NotTo(HaveOccurred()) } + cl = &Cleanup{ + Context: sc, + NodeClient: c, + ControllerClient: s, + ControllerPublishSupported: controllerPublishSupported, + NodeStageSupported: nodeStageSupported, + } }) - It("should fail when no volume id is provided", func() { + AfterEach(func() { + cl.DeleteVolumes() + }) - req := &csi.NodePublishVolumeRequest{} + Describe("NodeGetCapabilities", func() { + It("should return appropriate capabilities", func() { + caps, err := c.NodeGetCapabilities( + context.Background(), + &csi.NodeGetCapabilitiesRequest{}) - if sc.Secrets != nil { - req.NodePublishSecrets = sc.Secrets.NodePublishVolumeSecret - } + By("checking successful response") + Expect(err).NotTo(HaveOccurred()) + Expect(caps).NotTo(BeNil()) - _, err := c.NodePublishVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) + for _, cap := range caps.GetCapabilities() { + Expect(cap.GetRpc()).NotTo(BeNil()) - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + 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())) + } + } + }) }) - It("should fail when no target path is provided", func() { + Describe("NodeGetId", func() { + It("should return appropriate values", func() { + nid, err := c.NodeGetId( + context.Background(), + &csi.NodeGetIdRequest{}) - req := &csi.NodePublishVolumeRequest{ - VolumeId: "id", - } - - if sc.Secrets != nil { - req.NodePublishSecrets = sc.Secrets.NodePublishVolumeSecret - } - - _, err := c.NodePublishVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + Expect(err).NotTo(HaveOccurred()) + Expect(nid).NotTo(BeNil()) + Expect(nid.GetNodeId()).NotTo(BeEmpty()) + }) }) - It("should fail when no volume capability is provided", func() { + Describe("NodeGetInfo", func() { + var ( + i csi.IdentityClient + accessibilityConstraintSupported bool + ) - req := &csi.NodePublishVolumeRequest{ - VolumeId: "id", - TargetPath: sc.Config.TargetPath, - } + BeforeEach(func() { + i = csi.NewIdentityClient(sc.Conn) + accessibilityConstraintSupported = isPluginCapabilitySupported(i, csi.PluginCapability_Service_ACCESSIBILITY_CONSTRAINTS) + }) - if sc.Secrets != nil { - req.NodePublishSecrets = sc.Secrets.NodePublishVolumeSecret - } + It("should return approproate values", func() { + ninfo, err := c.NodeGetInfo( + context.Background(), + &csi.NodeGetInfoRequest{}) - _, err := c.NodePublishVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) + Expect(ninfo).NotTo(BeNil()) + Expect(ninfo.GetNodeId()).NotTo(BeEmpty()) + Expect(ninfo.GetMaxVolumesPerNode()).NotTo(BeNumerically("<", 0)) - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + if accessibilityConstraintSupported { + Expect(ninfo.GetAccessibleTopology()).NotTo(BeNil()) + } + }) }) - It("should return appropriate values (no optional values added)", func() { - testFullWorkflowSuccess(sc, s, c, controllerPublishSupported, nodeStageSupported) - }) -}) + Describe("NodePublishVolume", func() { + It("should fail when no volume id is provided", func() { + _, err := c.NodePublishVolume( + context.Background(), + &csi.NodePublishVolumeRequest{ + NodePublishSecrets: sc.Secrets.NodePublishVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) -var _ = DescribeSanity("NodeUnpublishVolume [Node Server]", func(sc *SanityContext) { - var ( - s csi.ControllerClient - c csi.NodeClient - controllerPublishSupported bool - nodeStageSupported bool - ) + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) - BeforeEach(func() { - s = csi.NewControllerClient(sc.Conn) - c = csi.NewNodeClient(sc.Conn) - controllerPublishSupported = isControllerCapabilitySupported( - s, - csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) - nodeStageSupported = isNodeCapabilitySupported(c, csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME) - if nodeStageSupported { - err := createMountTargetLocation(sc.Config.StagingPath) - Expect(err).NotTo(HaveOccurred()) - } - }) + It("should fail when no target path is provided", func() { + _, err := c.NodePublishVolume( + context.Background(), + &csi.NodePublishVolumeRequest{ + VolumeId: "id", + NodePublishSecrets: sc.Secrets.NodePublishVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) - It("should fail when no volume id is provided", func() { + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) - _, err := c.NodeUnpublishVolume( - context.Background(), - &csi.NodeUnpublishVolumeRequest{}) - Expect(err).To(HaveOccurred()) + It("should fail when no volume capability is provided", func() { + _, err := c.NodePublishVolume( + context.Background(), + &csi.NodePublishVolumeRequest{ + VolumeId: "id", + TargetPath: sc.Config.TargetPath, + NodePublishSecrets: sc.Secrets.NodePublishVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) }) - It("should fail when no target path is provided", func() { + Describe("NodeUnpublishVolume", func() { + It("should fail when no volume id is provided", func() { - _, err := c.NodeUnpublishVolume( - context.Background(), - &csi.NodeUnpublishVolumeRequest{ - VolumeId: "id", - }) - Expect(err).To(HaveOccurred()) + _, err := c.NodeUnpublishVolume( + context.Background(), + &csi.NodeUnpublishVolumeRequest{}) + Expect(err).To(HaveOccurred()) - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) - It("should return appropriate values (no optional values added)", func() { - testFullWorkflowSuccess(sc, s, c, controllerPublishSupported, nodeStageSupported) - }) -}) + It("should fail when no target path is provided", func() { -// TODO: Tests for NodeStageVolume/NodeUnstageVolume -func testFullWorkflowSuccess(sc *SanityContext, s csi.ControllerClient, c csi.NodeClient, controllerPublishSupported, nodeStageSupported bool) { - // Create Volume First - By("creating a single node writer volume") - name := "sanity" - req := &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, - }, - }, - }, - } + _, err := c.NodeUnpublishVolume( + context.Background(), + &csi.NodeUnpublishVolumeRequest{ + VolumeId: "id", + }) + Expect(err).To(HaveOccurred()) - if sc.Secrets != nil { - req.ControllerCreateSecrets = sc.Secrets.CreateVolumeSecret - } + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) + }) - vol, err := s.CreateVolume(context.Background(), req) - Expect(err).NotTo(HaveOccurred()) - Expect(vol).NotTo(BeNil()) - Expect(vol.GetVolume()).NotTo(BeNil()) - Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + Describe("NodeStageVolume", func() { + var ( + device string + ) - 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") - - pubReq := &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, - }, - }, - VolumeAttributes: vol.GetVolume().GetAttributes(), - Readonly: false, - } + BeforeEach(func() { + if !nodeStageSupported { + Skip("NodeStageVolume not supported") + } - if sc.Secrets != nil { - pubReq.ControllerPublishSecrets = sc.Secrets.ControllerPublishVolumeSecret - } + device = "/dev/mock" + }) - conpubvol, err = s.ControllerPublishVolume(context.Background(), pubReq) - Expect(err).NotTo(HaveOccurred()) - 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{}, + It("should fail when no volume id is provided", func() { + _, err := c.NodeStageVolume( + context.Background(), + &csi.NodeStageVolumeRequest{ + StagingTargetPath: sc.Config.StagingPath, + VolumeCapability: &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + PublishInfo: map[string]string{ + "device": device, + }, + NodeStageSecrets: sc.Secrets.NodeStageVolumeSecret, }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - }, - }, - StagingTargetPath: sc.Config.StagingPath, - VolumeAttributes: vol.GetVolume().GetAttributes(), - } - if controllerPublishSupported { - nodeStageVolReq.PublishInfo = conpubvol.GetPublishInfo() - } - if sc.Secrets != nil { - nodeStageVolReq.NodeStageSecrets = sc.Secrets.NodeStageVolumeSecret - } - nodestagevol, err := c.NodeStageVolume( - context.Background(), nodeStageVolReq) - Expect(err).NotTo(HaveOccurred()) - Expect(nodestagevol).NotTo(BeNil()) - } - // NodePublishVolume - By("publishing the volume on a node") - nodepubvolRequest := &csi.NodePublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - TargetPath: sc.Config.TargetPath, - VolumeCapability: &csi.VolumeCapability{ - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - }, - }, - VolumeAttributes: vol.GetVolume().GetAttributes(), - } - if nodeStageSupported { - nodepubvolRequest.StagingTargetPath = sc.Config.StagingPath - } - if controllerPublishSupported { - nodepubvolRequest.PublishInfo = conpubvol.GetPublishInfo() - } - if sc.Secrets != nil { - nodepubvolRequest.NodePublishSecrets = sc.Secrets.NodePublishVolumeSecret - } - nodepubvol, err := c.NodePublishVolume(context.Background(), nodepubvolRequest) - Expect(err).NotTo(HaveOccurred()) - Expect(nodepubvol).NotTo(BeNil()) + ) + Expect(err).To(HaveOccurred()) - // NodeUnpublishVolume - By("cleaning up calling nodeunpublish") - nodeunpubvol, err := c.NodeUnpublishVolume( - context.Background(), - &csi.NodeUnpublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - TargetPath: sc.Config.TargetPath, + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) }) - Expect(err).NotTo(HaveOccurred()) - Expect(nodeunpubvol).NotTo(BeNil()) - if nodeStageSupported { - By("cleaning up calling nodeunstage") - nodeunstagevol, err := c.NodeUnstageVolume( - context.Background(), - &csi.NodeUnstageVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - StagingTargetPath: sc.Config.StagingPath, - }, - ) - Expect(err).NotTo(HaveOccurred()) - Expect(nodeunstagevol).NotTo(BeNil()) - } + It("should fail when no staging target path is provided", func() { + _, err := c.NodeStageVolume( + context.Background(), + &csi.NodeStageVolumeRequest{ + VolumeId: "id", + VolumeCapability: &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + PublishInfo: map[string]string{ + "device": device, + }, + NodeStageSecrets: sc.Secrets.NodeStageVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) - if controllerPublishSupported { - By("cleaning up calling controllerunpublishing") + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) - unpubReq := &csi.ControllerUnpublishVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - NodeId: nid.GetNodeId(), - } + It("should fail when no volume capability is provided", func() { + _, err := c.NodeStageVolume( + context.Background(), + &csi.NodeStageVolumeRequest{ + VolumeId: "id", + StagingTargetPath: sc.Config.StagingPath, + PublishInfo: map[string]string{ + "device": device, + }, + NodeStageSecrets: sc.Secrets.NodeStageVolumeSecret, + }, + ) + Expect(err).To(HaveOccurred()) - if sc.Secrets != nil { - unpubReq.ControllerUnpublishSecrets = sc.Secrets.ControllerUnpublishVolumeSecret - } + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) + }) - controllerunpubvol, err := s.ControllerUnpublishVolume(context.Background(), unpubReq) - Expect(err).NotTo(HaveOccurred()) - Expect(controllerunpubvol).NotTo(BeNil()) - } + Describe("NodeUnstageVolume", func() { + BeforeEach(func() { + if !nodeStageSupported { + Skip("NodeUnstageVolume not supported") + } + }) - By("cleaning up deleting the volume") + It("should fail when no volume id is provided", func() { - delReq := &csi.DeleteVolumeRequest{ - VolumeId: vol.GetVolume().GetId(), - } + _, err := c.NodeUnstageVolume( + context.Background(), + &csi.NodeUnstageVolumeRequest{ + StagingTargetPath: sc.Config.StagingPath, + }) + Expect(err).To(HaveOccurred()) - if sc.Secrets != nil { - delReq.ControllerDeleteSecrets = sc.Secrets.DeleteVolumeSecret - } + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) - _, err = s.DeleteVolume(context.Background(), delReq) - Expect(err).NotTo(HaveOccurred()) -} + It("should fail when no staging target path is provided", func() { -var _ = DescribeSanity("NodeStageVolume [Node Server]", func(sc *SanityContext) { - var ( - s csi.ControllerClient - c csi.NodeClient - controllerPublishSupported bool - nodeStageSupported bool - device string - ) + _, err := c.NodeUnstageVolume( + context.Background(), + &csi.NodeUnstageVolumeRequest{ + VolumeId: "id", + }) + Expect(err).To(HaveOccurred()) - BeforeEach(func() { - s = csi.NewControllerClient(sc.Conn) - c = csi.NewNodeClient(sc.Conn) - device = "/dev/mock" - controllerPublishSupported = isControllerCapabilitySupported( - s, - csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) - nodeStageSupported = isNodeCapabilitySupported(c, csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME) - if nodeStageSupported { - err := createMountTargetLocation(sc.Config.StagingPath) - Expect(err).NotTo(HaveOccurred()) - } else { - Skip("NodeStageVolume not supported") - } + serverError, ok := status.FromError(err) + Expect(ok).To(BeTrue()) + Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) + }) }) - It("should fail when no volume id is provided", func() { + It("should work", func() { + name := uniqueString("sanity-node-full") - req := &csi.NodeStageVolumeRequest{ - StagingTargetPath: sc.Config.StagingPath, - VolumeCapability: &csi.VolumeCapability{ - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, - }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + // Create Volume First + By("creating a single node writer volume") + 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, + }, + }, }, + ControllerCreateSecrets: sc.Secrets.CreateVolumeSecret, }, - PublishInfo: map[string]string{ - "device": device, - }, - } - - if sc.Secrets != nil { - req.NodeStageSecrets = sc.Secrets.NodeStageVolumeSecret - } - - _, err := c.NodeStageVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) + ) + Expect(err).NotTo(HaveOccurred()) + Expect(vol).NotTo(BeNil()) + Expect(vol.GetVolume()).NotTo(BeNil()) + Expect(vol.GetVolume().GetId()).NotTo(BeEmpty()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId()}) - It("should fail when no staging target path is provided", func() { + 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()) - req := &csi.NodeStageVolumeRequest{ - VolumeId: "id", - VolumeCapability: &csi.VolumeCapability{ - AccessType: &csi.VolumeCapability_Mount{ - Mount: &csi.VolumeCapability_MountVolume{}, + 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, + }, + }, + VolumeAttributes: vol.GetVolume().GetAttributes(), + Readonly: false, + ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret, }, - AccessMode: &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + ) + Expect(err).NotTo(HaveOccurred()) + cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetId(), NodeID: nid.GetNodeId()}) + Expect(conpubvol).NotTo(BeNil()) + } + // NodeStageVolume + if nodeStageSupported { + By("node staging volume") + nodestagevol, err := c.NodeStageVolume( + context.Background(), + &csi.NodeStageVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + VolumeCapability: &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + StagingTargetPath: sc.Config.StagingPath, + VolumeAttributes: vol.GetVolume().GetAttributes(), + PublishInfo: conpubvol.GetPublishInfo(), + NodeStageSecrets: sc.Secrets.NodeStageVolumeSecret, }, - }, - PublishInfo: map[string]string{ - "device": device, - }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(nodestagevol).NotTo(BeNil()) } - - if sc.Secrets != nil { - req.NodeStageSecrets = sc.Secrets.NodeStageVolumeSecret + // NodePublishVolume + By("publishing the volume on a node") + var stagingPath string + if nodeStageSupported { + stagingPath = sc.Config.StagingPath } - - _, err := c.NodeStageVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) - - It("should fail when no volume capability is provided", func() { - - req := &csi.NodeStageVolumeRequest{ - VolumeId: "id", - StagingTargetPath: sc.Config.StagingPath, - PublishInfo: map[string]string{ - "device": device, + nodepubvol, err := c.NodePublishVolume( + context.Background(), + &csi.NodePublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + TargetPath: sc.Config.TargetPath, + StagingTargetPath: stagingPath, + VolumeCapability: &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, + }, + }, + VolumeAttributes: vol.GetVolume().GetAttributes(), + PublishInfo: conpubvol.GetPublishInfo(), + NodePublishSecrets: sc.Secrets.NodePublishVolumeSecret, }, - } - - if sc.Secrets != nil { - req.NodeStageSecrets = sc.Secrets.NodeStageVolumeSecret - } - - _, err := c.NodeStageVolume(context.Background(), req) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) - - It("should return appropriate values (no optional values added)", func() { - testFullWorkflowSuccess(sc, s, c, controllerPublishSupported, nodeStageSupported) - }) -}) + ) + Expect(err).NotTo(HaveOccurred()) + Expect(nodepubvol).NotTo(BeNil()) -var _ = DescribeSanity("NodeUnstageVolume [Node Server]", func(sc *SanityContext) { - var ( - s csi.ControllerClient - c csi.NodeClient - controllerPublishSupported bool - nodeStageSupported bool - ) + // NodeUnpublishVolume + By("cleaning up calling nodeunpublish") + nodeunpubvol, err := c.NodeUnpublishVolume( + context.Background(), + &csi.NodeUnpublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + TargetPath: sc.Config.TargetPath, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(nodeunpubvol).NotTo(BeNil()) - BeforeEach(func() { - s = csi.NewControllerClient(sc.Conn) - c = csi.NewNodeClient(sc.Conn) - controllerPublishSupported = isControllerCapabilitySupported( - s, - csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME) - nodeStageSupported = isNodeCapabilitySupported(c, csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME) if nodeStageSupported { - err := createMountTargetLocation(sc.Config.StagingPath) + By("cleaning up calling nodeunstage") + nodeunstagevol, err := c.NodeUnstageVolume( + context.Background(), + &csi.NodeUnstageVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + StagingTargetPath: sc.Config.StagingPath, + }, + ) Expect(err).NotTo(HaveOccurred()) - } else { - Skip("NodeUnstageVolume not supported") + Expect(nodeunstagevol).NotTo(BeNil()) } - }) - It("should fail when no volume id is provided", func() { - - _, err := c.NodeUnstageVolume( - context.Background(), - &csi.NodeUnstageVolumeRequest{ - StagingTargetPath: sc.Config.StagingPath, - }) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) + if controllerPublishSupported { + By("cleaning up calling controllerunpublishing") + + controllerunpubvol, err := s.ControllerUnpublishVolume( + context.Background(), + &csi.ControllerUnpublishVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + NodeId: nid.GetNodeId(), + ControllerUnpublishSecrets: sc.Secrets.ControllerUnpublishVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) + Expect(controllerunpubvol).NotTo(BeNil()) + } - It("should fail when no staging target path is provided", func() { + By("cleaning up deleting the volume") - _, err := c.NodeUnstageVolume( + _, err = s.DeleteVolume( context.Background(), - &csi.NodeUnstageVolumeRequest{ - VolumeId: "id", - }) - Expect(err).To(HaveOccurred()) - - serverError, ok := status.FromError(err) - Expect(ok).To(BeTrue()) - Expect(serverError.Code()).To(Equal(codes.InvalidArgument)) - }) - - It("should return appropriate values (no optional values added)", func() { - testFullWorkflowSuccess(sc, s, c, controllerPublishSupported, nodeStageSupported) + &csi.DeleteVolumeRequest{ + VolumeId: vol.GetVolume().GetId(), + ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret, + }, + ) + Expect(err).NotTo(HaveOccurred()) }) }) diff --git a/pkg/sanity/sanity.go b/pkg/sanity/sanity.go index 3e57e611..9a4de8be 100644 --- a/pkg/sanity/sanity.go +++ b/pkg/sanity/sanity.go @@ -17,6 +17,7 @@ limitations under the License. package sanity import ( + "crypto/rand" "fmt" "io/ioutil" "os" @@ -87,6 +88,8 @@ func (sc *SanityContext) setup() { if len(sc.Config.SecretsFile) > 0 { sc.Secrets, err = loadSecrets(sc.Config.SecretsFile) Expect(err).NotTo(HaveOccurred()) + } else { + sc.Secrets = &CSISecrets{} } By("connecting to CSI driver") @@ -138,3 +141,23 @@ func loadSecrets(path string) (*CSISecrets, error) { return &creds, nil } + +var uniqueSuffix = "-" + pseudoUUID() + +// pseudoUUID returns a unique string generated from random +// bytes, empty string in case of error. +func pseudoUUID() string { + b := make([]byte, 8) + if _, err := rand.Read(b); err != nil { + // Shouldn't happen?! + return "" + } + return fmt.Sprintf("%08X-%08X", b[0:4], b[4:8]) +} + +// uniqueString returns a unique string by appending a random +// number. In case of an error, just the prefix is returned, so it +// alone should already be fairly unique. +func uniqueString(prefix string) string { + return prefix + uniqueSuffix +} diff --git a/pkg/sanity/tests.go b/pkg/sanity/tests.go index e11f0d76..47763b75 100644 --- a/pkg/sanity/tests.go +++ b/pkg/sanity/tests.go @@ -42,16 +42,15 @@ func DescribeSanity(text string, body func(*SanityContext)) bool { func registerTestsInGinkgo(sc *SanityContext) { for _, test := range tests { Describe(test.text, func() { - BeforeEach(func() { sc.setup() }) + test.body(sc) + AfterEach(func() { sc.teardown() }) - - test.body(sc) }) } } diff --git a/test/co_test.go b/test/co_test.go index a8c17e97..5a2bbe27 100644 --- a/test/co_test.go +++ b/test/co_test.go @@ -16,16 +16,16 @@ limitations under the License. package test import ( + "context" "fmt" "reflect" "testing" - csi "github.com/container-storage-interface/spec/lib/go/csi/v0" - gomock "github.com/golang/mock/gomock" + "github.com/container-storage-interface/spec/lib/go/csi/v0" + "github.com/golang/mock/gomock" "github.com/golang/protobuf/proto" mock_driver "github.com/kubernetes-csi/csi-test/driver" mock_utils "github.com/kubernetes-csi/csi-test/utils" - "golang.org/x/net/context" ) func TestPluginInfoResponse(t *testing.T) { diff --git a/test/driver_test.go b/test/driver_test.go index 4b0122b6..82080eb3 100644 --- a/test/driver_test.go +++ b/test/driver_test.go @@ -21,7 +21,7 @@ import ( "sync" "testing" - csi "github.com/container-storage-interface/spec/lib/go/csi/v0" + "github.com/container-storage-interface/spec/lib/go/csi/v0" "github.com/kubernetes-csi/csi-test/utils" "google.golang.org/grpc" "google.golang.org/grpc/reflection"