add csi-test to vendor
This commit is contained in:
62
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/README.md
generated
vendored
Normal file
62
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/README.md
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
# CSI Driver Sanity Tester
|
||||
|
||||
This library provides a simple way to ensure that a CSI driver conforms to
|
||||
the CSI specification. There are two ways to leverage this testing framework.
|
||||
For CSI drivers written in Golang, the framework provides a simple API function
|
||||
to call to test the driver. Another way to run the test suite is to use the
|
||||
command line program [csi-sanity](https://github.com/kubernetes-csi/csi-test/tree/master/cmd/csi-sanity).
|
||||
|
||||
## For Golang CSI Drivers
|
||||
This framework leverages the Ginkgo BDD testing framework to deliver a descriptive
|
||||
test suite for your driver. To test your driver, simply call the API in one of your
|
||||
Golang `TestXXX` functions. For example:
|
||||
|
||||
```go
|
||||
func TestMyDriver(t *testing.T) {
|
||||
// Setup the full driver and its environment
|
||||
... setup driver ...
|
||||
config := &sanity.Config{
|
||||
TargetPath: ...
|
||||
StagingPath: ...
|
||||
Address: endpoint,
|
||||
}
|
||||
|
||||
|
||||
// Now call the test suite
|
||||
sanity.Test(t, config)
|
||||
}
|
||||
```
|
||||
|
||||
Only one such test function is supported because under the hood a
|
||||
Ginkgo test suite gets constructed and executed by the call.
|
||||
|
||||
Alternatively, the tests can also be embedded inside a Ginkgo test
|
||||
suite. In that case it is possible to define multiple tests with
|
||||
different configurations:
|
||||
|
||||
```go
|
||||
var _ = Describe("MyCSIDriver", func () {
|
||||
Context("Config A", func () {
|
||||
var config &sanity.Config
|
||||
|
||||
BeforeEach() {
|
||||
... setup driver and config...
|
||||
}
|
||||
|
||||
AfterEach() {
|
||||
...tear down driver...
|
||||
}
|
||||
|
||||
Describe("CSI sanity", func() {
|
||||
sanity.GinkgoTest(config)
|
||||
})
|
||||
})
|
||||
|
||||
Context("Config B", func () {
|
||||
...
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Command line program
|
||||
Please see [csi-sanity](https://github.com/kubernetes-csi/csi-test/tree/master/cmd/csi-sanity)
|
134
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/cleanup.go
generated
vendored
Normal file
134
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/cleanup.go
generated
vendored
Normal file
@@ -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)
|
||||
}
|
||||
}
|
1626
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/controller.go
generated
vendored
Normal file
1626
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/controller.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
99
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/identity.go
generated
vendored
Normal file
99
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/identity.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2017 Luis Pabón luis@portworx.com
|
||||
|
||||
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"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi/v0"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = DescribeSanity("Identity Service", func(sc *SanityContext) {
|
||||
var (
|
||||
c csi.IdentityClient
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
c = csi.NewIdentityClient(sc.Conn)
|
||||
})
|
||||
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
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())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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())
|
||||
})
|
||||
})
|
||||
})
|
528
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/node.go
generated
vendored
Normal file
528
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/node.go
generated
vendored
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
Copyright 2017 Kubernetes Authors.
|
||||
|
||||
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"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi/v0"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func isNodeCapabilitySupported(c csi.NodeClient,
|
||||
capType csi.NodeServiceCapability_RPC_Type,
|
||||
) bool {
|
||||
|
||||
caps, err := c.NodeGetCapabilities(
|
||||
context.Background(),
|
||||
&csi.NodeGetCapabilitiesRequest{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(caps).NotTo(BeNil())
|
||||
|
||||
for _, cap := range caps.GetCapabilities() {
|
||||
Expect(cap.GetRpc()).NotTo(BeNil())
|
||||
if cap.GetRpc().GetType() == capType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isPluginCapabilitySupported(c csi.IdentityClient,
|
||||
capType csi.PluginCapability_Service_Type,
|
||||
) bool {
|
||||
|
||||
caps, err := c.GetPluginCapabilities(
|
||||
context.Background(),
|
||||
&csi.GetPluginCapabilitiesRequest{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(caps).NotTo(BeNil())
|
||||
Expect(caps.GetCapabilities()).NotTo(BeNil())
|
||||
|
||||
for _, cap := range caps.GetCapabilities() {
|
||||
Expect(cap.GetService()).NotTo(BeNil())
|
||||
if cap.GetService().GetType() == capType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
|
||||
var (
|
||||
cl *Cleanup
|
||||
c csi.NodeClient
|
||||
s csi.ControllerClient
|
||||
|
||||
controllerPublishSupported bool
|
||||
nodeStageSupported bool
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
c = csi.NewNodeClient(sc.Conn)
|
||||
s = csi.NewControllerClient(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())
|
||||
}
|
||||
cl = &Cleanup{
|
||||
Context: sc,
|
||||
NodeClient: c,
|
||||
ControllerClient: s,
|
||||
ControllerPublishSupported: controllerPublishSupported,
|
||||
NodeStageSupported: nodeStageSupported,
|
||||
}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
cl.DeleteVolumes()
|
||||
})
|
||||
|
||||
Describe("NodeGetCapabilities", func() {
|
||||
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())
|
||||
|
||||
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()))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NodeGetId", func() {
|
||||
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())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NodeGetInfo", func() {
|
||||
var (
|
||||
i csi.IdentityClient
|
||||
accessibilityConstraintSupported bool
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
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())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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())
|
||||
|
||||
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() {
|
||||
_, err := c.NodePublishVolume(
|
||||
context.Background(),
|
||||
&csi.NodePublishVolumeRequest{
|
||||
VolumeId: "id",
|
||||
NodePublishSecrets: sc.Secrets.NodePublishVolumeSecret,
|
||||
},
|
||||
)
|
||||
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.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))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NodeUnpublishVolume", func() {
|
||||
It("should fail when no volume id is provided", func() {
|
||||
|
||||
_, 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))
|
||||
})
|
||||
|
||||
It("should fail when no target path is provided", func() {
|
||||
|
||||
_, err := c.NodeUnpublishVolume(
|
||||
context.Background(),
|
||||
&csi.NodeUnpublishVolumeRequest{
|
||||
VolumeId: "id",
|
||||
})
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
serverError, ok := status.FromError(err)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(serverError.Code()).To(Equal(codes.InvalidArgument))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NodeStageVolume", func() {
|
||||
var (
|
||||
device string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
if !nodeStageSupported {
|
||||
Skip("NodeStageVolume not supported")
|
||||
}
|
||||
|
||||
device = "/dev/mock"
|
||||
})
|
||||
|
||||
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,
|
||||
},
|
||||
)
|
||||
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 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())
|
||||
|
||||
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.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())
|
||||
|
||||
serverError, ok := status.FromError(err)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(serverError.Code()).To(Equal(codes.InvalidArgument))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("NodeUnstageVolume", func() {
|
||||
BeforeEach(func() {
|
||||
if !nodeStageSupported {
|
||||
Skip("NodeUnstageVolume not supported")
|
||||
}
|
||||
})
|
||||
|
||||
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))
|
||||
})
|
||||
|
||||
It("should fail when no staging target path is provided", func() {
|
||||
|
||||
_, err := c.NodeUnstageVolume(
|
||||
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 work", func() {
|
||||
name := uniqueString("sanity-node-full")
|
||||
|
||||
// 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,
|
||||
},
|
||||
)
|
||||
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 := c.NodeGetId(
|
||||
context.Background(),
|
||||
&csi.NodeGetIdRequest{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(nid).NotTo(BeNil())
|
||||
Expect(nid.GetNodeId()).NotTo(BeEmpty())
|
||||
|
||||
var conpubvol *csi.ControllerPublishVolumeResponse
|
||||
if controllerPublishSupported {
|
||||
By("controller publishing volume")
|
||||
|
||||
conpubvol, err = s.ControllerPublishVolume(
|
||||
context.Background(),
|
||||
&csi.ControllerPublishVolumeRequest{
|
||||
VolumeId: vol.GetVolume().GetId(),
|
||||
NodeId: nid.GetNodeId(),
|
||||
VolumeCapability: &csi.VolumeCapability{
|
||||
AccessType: &csi.VolumeCapability_Mount{
|
||||
Mount: &csi.VolumeCapability_MountVolume{},
|
||||
},
|
||||
AccessMode: &csi.VolumeCapability_AccessMode{
|
||||
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
|
||||
},
|
||||
},
|
||||
VolumeAttributes: vol.GetVolume().GetAttributes(),
|
||||
Readonly: false,
|
||||
ControllerPublishSecrets: sc.Secrets.ControllerPublishVolumeSecret,
|
||||
},
|
||||
)
|
||||
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,
|
||||
},
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(nodestagevol).NotTo(BeNil())
|
||||
}
|
||||
// NodePublishVolume
|
||||
By("publishing the volume on a node")
|
||||
var stagingPath string
|
||||
if nodeStageSupported {
|
||||
stagingPath = sc.Config.StagingPath
|
||||
}
|
||||
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,
|
||||
},
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(nodepubvol).NotTo(BeNil())
|
||||
|
||||
// NodeUnpublishVolume
|
||||
By("cleaning up calling nodeunpublish")
|
||||
nodeunpubvol, err := c.NodeUnpublishVolume(
|
||||
context.Background(),
|
||||
&csi.NodeUnpublishVolumeRequest{
|
||||
VolumeId: vol.GetVolume().GetId(),
|
||||
TargetPath: sc.Config.TargetPath,
|
||||
})
|
||||
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())
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
By("cleaning up deleting the volume")
|
||||
|
||||
_, err = s.DeleteVolume(
|
||||
context.Background(),
|
||||
&csi.DeleteVolumeRequest{
|
||||
VolumeId: vol.GetVolume().GetId(),
|
||||
ControllerDeleteSecrets: sc.Secrets.DeleteVolumeSecret,
|
||||
},
|
||||
)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
163
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/sanity.go
generated
vendored
Normal file
163
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/sanity.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
Copyright 2017 Luis Pabón luis@portworx.com
|
||||
|
||||
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 (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-csi/csi-test/utils"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
// CSISecrets consists of secrets used in CSI credentials.
|
||||
type CSISecrets struct {
|
||||
CreateVolumeSecret map[string]string `yaml:"CreateVolumeSecret"`
|
||||
DeleteVolumeSecret map[string]string `yaml:"DeleteVolumeSecret"`
|
||||
ControllerPublishVolumeSecret map[string]string `yaml:"ControllerPublishVolumeSecret"`
|
||||
ControllerUnpublishVolumeSecret map[string]string `yaml:"ControllerUnpublishVolumeSecret"`
|
||||
NodeStageVolumeSecret map[string]string `yaml:"NodeStageVolumeSecret"`
|
||||
NodePublishVolumeSecret map[string]string `yaml:"NodePublishVolumeSecret"`
|
||||
CreateSnapshotSecret map[string]string `yaml:"CreateSnapshotSecret"`
|
||||
DeleteSnapshotSecret map[string]string `yaml:"DeleteSnapshotSecret"`
|
||||
}
|
||||
|
||||
// Config provides the configuration for the sanity tests. It
|
||||
// needs to be initialized by the user of the sanity package.
|
||||
type Config struct {
|
||||
TargetPath string
|
||||
StagingPath string
|
||||
Address string
|
||||
SecretsFile string
|
||||
TestVolumeSize int64
|
||||
}
|
||||
|
||||
// SanityContext holds the variables that each test can depend on. It
|
||||
// gets initialized before each test block runs.
|
||||
type SanityContext struct {
|
||||
Config *Config
|
||||
Conn *grpc.ClientConn
|
||||
Secrets *CSISecrets
|
||||
}
|
||||
|
||||
// Test will test the CSI driver at the specified address by
|
||||
// setting up a Ginkgo suite and running it.
|
||||
func Test(t *testing.T, reqConfig *Config) {
|
||||
sc := &SanityContext{
|
||||
Config: reqConfig,
|
||||
}
|
||||
|
||||
registerTestsInGinkgo(sc)
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "CSI Driver Test Suite")
|
||||
}
|
||||
|
||||
func GinkgoTest(reqConfig *Config) {
|
||||
sc := &SanityContext{
|
||||
Config: reqConfig,
|
||||
}
|
||||
|
||||
registerTestsInGinkgo(sc)
|
||||
}
|
||||
|
||||
func (sc *SanityContext) setup() {
|
||||
var err error
|
||||
|
||||
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")
|
||||
sc.Conn, err = utils.Connect(sc.Config.Address)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("creating mount and staging directories")
|
||||
err = createMountTargetLocation(sc.Config.TargetPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(sc.Config.StagingPath) > 0 {
|
||||
err = createMountTargetLocation(sc.Config.StagingPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *SanityContext) teardown() {
|
||||
if sc.Conn != nil {
|
||||
sc.Conn.Close()
|
||||
sc.Conn = nil
|
||||
}
|
||||
}
|
||||
|
||||
func createMountTargetLocation(targetPath string) error {
|
||||
fileInfo, err := os.Stat(targetPath)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
return os.MkdirAll(targetPath, 0755)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if !fileInfo.IsDir() {
|
||||
return fmt.Errorf("Target location %s is not a directory", targetPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSecrets(path string) (*CSISecrets, error) {
|
||||
var creds CSISecrets
|
||||
|
||||
yamlFile, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return &creds, fmt.Errorf("failed to read file %q: #%v", path, err)
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(yamlFile, &creds)
|
||||
if err != nil {
|
||||
return &creds, fmt.Errorf("error unmarshaling yaml: #%v", err)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
56
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/tests.go
generated
vendored
Normal file
56
vendor/github.com/kubernetes-csi/csi-test/pkg/sanity/tests.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 (
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
text string
|
||||
body func(*SanityContext)
|
||||
}
|
||||
|
||||
var tests []test
|
||||
|
||||
// DescribeSanity must be used instead of the usual Ginkgo Describe to
|
||||
// register a test block. The difference is that the body function
|
||||
// will be called multiple times with the right context (when
|
||||
// setting up a Ginkgo suite or a testing.T test, with the right
|
||||
// configuration).
|
||||
func DescribeSanity(text string, body func(*SanityContext)) bool {
|
||||
tests = append(tests, test{text, body})
|
||||
return true
|
||||
}
|
||||
|
||||
// registerTestsInGinkgo invokes the actual Gingko Describe
|
||||
// for the tests registered earlier with DescribeSanity.
|
||||
func registerTestsInGinkgo(sc *SanityContext) {
|
||||
for _, test := range tests {
|
||||
Describe(test.text, func() {
|
||||
BeforeEach(func() {
|
||||
sc.setup()
|
||||
})
|
||||
|
||||
test.body(sc)
|
||||
|
||||
AfterEach(func() {
|
||||
sc.teardown()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user