Skip to content

Commit

Permalink
Make mount and staging dir creation flexible
Browse files Browse the repository at this point in the history
This change adds an option to attach callback functions to CSI sanity
for mount target directory creation. This could be used to customize the
target and staging mount path creation.

Also, adds `targetPath` and `stagingPath` fields to `SanityContext` to
store the actual target and staging paths, derived from the sanity
config.

Adds an e2e test section to test the above setup.
  • Loading branch information
darkowlzz committed Mar 20, 2019
1 parent 82d19e4 commit 4000ec5
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 34 deletions.
63 changes: 63 additions & 0 deletions hack/_apitest2/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package apitest2

import (
"fmt"
"os"
"path"
"testing"

"github.com/kubernetes-csi/csi-test/pkg/sanity"
)

// TestMyDriverWithCustomTargetPaths verifies that CreateTargetDir and
// CreateStagingDir are called a specific number of times.
func TestMyDriverWithCustomTargetPaths(t *testing.T) {
var createTargetDirCalls, createStagingDirCalls int

wantCreateTargetCalls := 3
wantCreateStagingCalls := 3

// tmpPath could be a CO specific directory under which all the target dirs
// are created. For k8s, it could be /var/lib/kubelet/pods under which the
// mount directories could be created.
tmpPath := path.Join(os.TempDir(), "csi")
config := &sanity.Config{
TargetPath: "foo/target/mount",
StagingPath: "foo/staging/mount",
Address: "/tmp/e2e-csi-sanity.sock",
CreateTargetDir: func(targetPath string) (string, error) {
createTargetDirCalls++
targetPath = path.Join(tmpPath, targetPath)
return targetPath, createTargetDir(targetPath)
},
CreateStagingDir: func(targetPath string) (string, error) {
createStagingDirCalls++
targetPath = path.Join(tmpPath, targetPath)
return targetPath, createTargetDir(targetPath)
},
}

sanity.Test(t, config)

if createTargetDirCalls != wantCreateTargetCalls {
t.Errorf("unexpected number of CreateTargetDir calls:\n(WNT) %d\n(GOT) %d", wantCreateTargetCalls, createTargetDirCalls)
}

if createStagingDirCalls != wantCreateStagingCalls {
t.Errorf("unexpected number of CreateStagingDir calls:\n(WNT) %d\n(GOT) %d", wantCreateStagingCalls, createStagingDirCalls)
}
}

func createTargetDir(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
}
17 changes: 17 additions & 0 deletions hack/e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ runTestAPI()
fi
}

runTestAPIWithCustomTargetPaths()
{
CSI_ENDPOINT=$1 ./bin/mock-driver &
local pid=$!

# Running a specific test to verify that the custom target paths are called
# a deterministic number of times.
GOCACHE=off go test -v ./hack/_apitest2/api_test.go -ginkgo.focus="NodePublishVolume"; ret=$?

if [ $ret -ne 0 ] ; then
exit $ret
fi
}

make

cd cmd/csi-sanity
Expand All @@ -88,4 +102,7 @@ runTestWithDifferentAddresses "${UDS_NODE}" "${UDS_CONTROLLER}"
rm -f $UDS_NODE
rm -f $UDS_CONTROLLER

runTestAPIWithCustomTargetPaths "${UDS}"
rm -rf $UDS

exit 0
34 changes: 15 additions & 19 deletions pkg/sanity/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,6 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
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())
}
nodeVolumeStatsSupported = isNodeCapabilitySupported(c, csi.NodeServiceCapability_RPC_GET_VOLUME_STATS)
cl = &Cleanup{
Context: sc,
Expand Down Expand Up @@ -190,7 +186,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
context.Background(),
&csi.NodePublishVolumeRequest{
VolumeId: "id",
TargetPath: sc.Config.TargetPath,
TargetPath: sc.targetPath,
Secrets: sc.Secrets.NodePublishVolumeSecret,
},
)
Expand Down Expand Up @@ -247,7 +243,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
_, err := c.NodeStageVolume(
context.Background(),
&csi.NodeStageVolumeRequest{
StagingTargetPath: sc.Config.StagingPath,
StagingTargetPath: sc.stagingPath,
VolumeCapability: &csi.VolumeCapability{
AccessType: &csi.VolumeCapability_Mount{
Mount: &csi.VolumeCapability_MountVolume{},
Expand Down Expand Up @@ -329,7 +325,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
context.Background(),
&csi.NodeStageVolumeRequest{
VolumeId: vol.GetVolume().GetVolumeId(),
StagingTargetPath: sc.Config.StagingPath,
StagingTargetPath: sc.stagingPath,
PublishContext: map[string]string{
"device": device,
},
Expand Down Expand Up @@ -368,7 +364,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
_, err := c.NodeUnstageVolume(
context.Background(),
&csi.NodeUnstageVolumeRequest{
StagingTargetPath: sc.Config.StagingPath,
StagingTargetPath: sc.stagingPath,
})
Expect(err).To(HaveOccurred())

Expand Down Expand Up @@ -519,7 +515,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
},
},
StagingTargetPath: sc.Config.StagingPath,
StagingTargetPath: sc.stagingPath,
VolumeContext: vol.GetVolume().GetVolumeContext(),
PublishContext: conpubvol.GetPublishContext(),
Secrets: sc.Secrets.NodeStageVolumeSecret,
Expand All @@ -532,13 +528,13 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
By("publishing the volume on a node")
var stagingPath string
if nodeStageSupported {
stagingPath = sc.Config.StagingPath
stagingPath = sc.stagingPath
}
nodepubvol, err := c.NodePublishVolume(
context.Background(),
&csi.NodePublishVolumeRequest{
VolumeId: vol.GetVolume().GetVolumeId(),
TargetPath: sc.Config.TargetPath,
TargetPath: sc.targetPath,
StagingTargetPath: stagingPath,
VolumeCapability: &csi.VolumeCapability{
AccessType: &csi.VolumeCapability_Mount{
Expand Down Expand Up @@ -577,7 +573,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
context.Background(),
&csi.NodeUnpublishVolumeRequest{
VolumeId: vol.GetVolume().GetVolumeId(),
TargetPath: sc.Config.TargetPath,
TargetPath: sc.targetPath,
})
Expect(err).NotTo(HaveOccurred())
Expect(nodeunpubvol).NotTo(BeNil())
Expand All @@ -588,7 +584,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
context.Background(),
&csi.NodeUnstageVolumeRequest{
VolumeId: vol.GetVolume().GetVolumeId(),
StagingTargetPath: sc.Config.StagingPath,
StagingTargetPath: sc.stagingPath,
},
)
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -703,7 +699,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
},
},
StagingTargetPath: sc.Config.StagingPath,
StagingTargetPath: sc.stagingPath,
VolumeContext: vol.GetVolume().GetVolumeContext(),
PublishContext: conpubvol.GetPublishContext(),
Secrets: sc.Secrets.NodeStageVolumeSecret,
Expand All @@ -716,13 +712,13 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
By("publishing the volume on a node")
var stagingPath string
if nodeStageSupported {
stagingPath = sc.Config.StagingPath
stagingPath = sc.stagingPath
}
nodepubvol, err := c.NodePublishVolume(
context.Background(),
&csi.NodePublishVolumeRequest{
VolumeId: vol.GetVolume().GetVolumeId(),
TargetPath: sc.Config.TargetPath,
TargetPath: sc.targetPath,
StagingTargetPath: stagingPath,
VolumeCapability: &csi.VolumeCapability{
AccessType: &csi.VolumeCapability_Mount{
Expand All @@ -747,7 +743,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
context.Background(),
&csi.NodeGetVolumeStatsRequest{
VolumeId: vol.GetVolume().GetVolumeId(),
VolumePath: sc.Config.TargetPath,
VolumePath: sc.targetPath,
},
)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -760,7 +756,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
context.Background(),
&csi.NodeUnpublishVolumeRequest{
VolumeId: vol.GetVolume().GetVolumeId(),
TargetPath: sc.Config.TargetPath,
TargetPath: sc.targetPath,
})
Expect(err).NotTo(HaveOccurred())
Expect(nodeunpubvol).NotTo(BeNil())
Expand All @@ -771,7 +767,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
context.Background(),
&csi.NodeUnstageVolumeRequest{
VolumeId: vol.GetVolume().GetVolumeId(),
StagingTargetPath: sc.Config.StagingPath,
StagingTargetPath: sc.stagingPath,
},
)
Expect(err).NotTo(HaveOccurred())
Expand Down
61 changes: 46 additions & 15 deletions pkg/sanity/sanity.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ type Config struct {
TestNodeVolumeAttachLimit bool

JUnitFile string

// Callback functions to customize the creation of target and staging
// directories. Returns the new paths for mount and staging.
// If not defined, directories are created in the default way at TargetPath
// and StagingPath.
CreateTargetDir func(path string) (string, error)
CreateStagingDir func(path string) (string, error)
}

// SanityContext holds the variables that each test can depend on. It
Expand All @@ -72,6 +79,10 @@ type SanityContext struct {

connAddress string
controllerConnAddress string

// Target and staging paths derived from the sanity config.
targetPath string
stagingPath string
}

// Test will test the CSI driver at the specified address by
Expand Down Expand Up @@ -153,12 +164,15 @@ func (sc *SanityContext) setup() {
}

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())
}
// If callback function for creating target dir is specified, use it.
targetPath, err := createMountTargetLocation(sc.Config.TargetPath, sc.Config.CreateTargetDir)
Expect(err).NotTo(HaveOccurred(), "failed to create target directory %s", targetPath)
sc.targetPath = targetPath

// If callback function for creating staging dir is specified, use it.
stagingPath, err := createMountTargetLocation(sc.Config.StagingPath, sc.Config.CreateStagingDir)
Expect(err).NotTo(HaveOccurred(), "failed to create staging directory %s", stagingPath)
sc.stagingPath = stagingPath
}

func (sc *SanityContext) teardown() {
Expand All @@ -174,18 +188,35 @@ func (sc *SanityContext) teardown() {
// (https://github.com/kubernetes-csi/csi-test/pull/98).
}

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
func createMountTargetLocation(targetPath string, customCreateDir func(string) (string, error)) (string, error) {

// Return the target path if empty.
if len(targetPath) < 0 {
return targetPath, nil
}
if !fileInfo.IsDir() {
return fmt.Errorf("Target location %s is not a directory", targetPath)

var newTargetPath string

if customCreateDir != nil {
newpath, err := customCreateDir(targetPath)
if err != nil {
return "", err
}
newTargetPath = newpath
} else {
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)
}
newTargetPath = targetPath
}

return nil
return newTargetPath, nil
}

func loadSecrets(path string) (*CSISecrets, error) {
Expand Down

0 comments on commit 4000ec5

Please sign in to comment.