Skip to content

Commit c8ac1b0

Browse files
committed
Make mount and staging dir creation flexible
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.
1 parent d6050bc commit c8ac1b0

File tree

4 files changed

+141
-34
lines changed

4 files changed

+141
-34
lines changed

hack/_apitest2/api_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package apitest2
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path"
7+
"testing"
8+
9+
"github.com/kubernetes-csi/csi-test/pkg/sanity"
10+
)
11+
12+
// TestMyDriverWithCustomTargetPaths verifies that CreateTargetDir and
13+
// CreateStagingDir are called a specific number of times.
14+
func TestMyDriverWithCustomTargetPaths(t *testing.T) {
15+
var createTargetDirCalls, createStagingDirCalls int
16+
17+
wantCreateTargetCalls := 3
18+
wantCreateStagingCalls := 3
19+
20+
// tmpPath could be a CO specific directory under which all the target dirs
21+
// are created. For k8s, it could be /var/lib/kubelet/pods under which the
22+
// mount directories could be created.
23+
tmpPath := path.Join(os.TempDir(), "csi")
24+
config := &sanity.Config{
25+
TargetPath: "foo/target/mount",
26+
StagingPath: "foo/staging/mount",
27+
Address: "/tmp/e2e-csi-sanity.sock",
28+
CreateTargetDir: func(targetPath string) (string, error) {
29+
createTargetDirCalls++
30+
targetPath = path.Join(tmpPath, targetPath)
31+
return targetPath, createTargetDir(targetPath)
32+
},
33+
CreateStagingDir: func(targetPath string) (string, error) {
34+
createStagingDirCalls++
35+
targetPath = path.Join(tmpPath, targetPath)
36+
return targetPath, createTargetDir(targetPath)
37+
},
38+
}
39+
40+
sanity.Test(t, config)
41+
42+
if createTargetDirCalls != wantCreateTargetCalls {
43+
t.Errorf("unexpected number of CreateTargetDir calls:\n(WNT) %d\n(GOT) %d", wantCreateTargetCalls, createTargetDirCalls)
44+
}
45+
46+
if createStagingDirCalls != wantCreateStagingCalls {
47+
t.Errorf("unexpected number of CreateStagingDir calls:\n(WNT) %d\n(GOT) %d", wantCreateStagingCalls, createStagingDirCalls)
48+
}
49+
}
50+
51+
func createTargetDir(targetPath string) error {
52+
fileInfo, err := os.Stat(targetPath)
53+
if err != nil && os.IsNotExist(err) {
54+
return os.MkdirAll(targetPath, 0755)
55+
} else if err != nil {
56+
return err
57+
}
58+
if !fileInfo.IsDir() {
59+
return fmt.Errorf("Target location %s is not a directory", targetPath)
60+
}
61+
62+
return nil
63+
}

hack/e2e.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ runTestAPI()
5454
fi
5555
}
5656

57+
runTestAPIWithCustomTargetPaths()
58+
{
59+
CSI_ENDPOINT=$1 ./bin/mock-driver &
60+
local pid=$!
61+
62+
# Running a specific test to verify that the custom target paths are called
63+
# a deterministic number of times.
64+
GOCACHE=off go test -v ./hack/_apitest2/api_test.go -ginkgo.focus="NodePublishVolume"; ret=$?
65+
66+
if [ $ret -ne 0 ] ; then
67+
exit $ret
68+
fi
69+
}
70+
5771
make
5872

5973
cd cmd/csi-sanity
@@ -69,4 +83,7 @@ rm -f $UDS
6983
runTestAPI "${UDS}"
7084
rm -f $UDS
7185

86+
runTestAPIWithCustomTargetPaths "${UDS}"
87+
rm -rf $UDS
88+
7289
exit 0

pkg/sanity/node.go

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,6 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
8787
s,
8888
csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME)
8989
nodeStageSupported = isNodeCapabilitySupported(c, csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME)
90-
if nodeStageSupported {
91-
err := createMountTargetLocation(sc.Config.StagingPath)
92-
Expect(err).NotTo(HaveOccurred())
93-
}
9490
nodeVolumeStatsSupported = isNodeCapabilitySupported(c, csi.NodeServiceCapability_RPC_GET_VOLUME_STATS)
9591
cl = &Cleanup{
9692
Context: sc,
@@ -191,7 +187,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
191187
context.Background(),
192188
&csi.NodePublishVolumeRequest{
193189
VolumeId: "id",
194-
TargetPath: sc.Config.TargetPath,
190+
TargetPath: sc.targetPath,
195191
Secrets: sc.Secrets.NodePublishVolumeSecret,
196192
},
197193
)
@@ -248,7 +244,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
248244
_, err := c.NodeStageVolume(
249245
context.Background(),
250246
&csi.NodeStageVolumeRequest{
251-
StagingTargetPath: sc.Config.StagingPath,
247+
StagingTargetPath: sc.stagingPath,
252248
VolumeCapability: &csi.VolumeCapability{
253249
AccessType: &csi.VolumeCapability_Mount{
254250
Mount: &csi.VolumeCapability_MountVolume{},
@@ -330,7 +326,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
330326
context.Background(),
331327
&csi.NodeStageVolumeRequest{
332328
VolumeId: vol.GetVolume().GetVolumeId(),
333-
StagingTargetPath: sc.Config.StagingPath,
329+
StagingTargetPath: sc.stagingPath,
334330
PublishContext: map[string]string{
335331
"device": device,
336332
},
@@ -369,7 +365,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
369365
_, err := c.NodeUnstageVolume(
370366
context.Background(),
371367
&csi.NodeUnstageVolumeRequest{
372-
StagingTargetPath: sc.Config.StagingPath,
368+
StagingTargetPath: sc.stagingPath,
373369
})
374370
Expect(err).To(HaveOccurred())
375371

@@ -520,7 +516,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
520516
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
521517
},
522518
},
523-
StagingTargetPath: sc.Config.StagingPath,
519+
StagingTargetPath: sc.stagingPath,
524520
VolumeContext: vol.GetVolume().GetVolumeContext(),
525521
PublishContext: conpubvol.GetPublishContext(),
526522
Secrets: sc.Secrets.NodeStageVolumeSecret,
@@ -533,13 +529,13 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
533529
By("publishing the volume on a node")
534530
var stagingPath string
535531
if nodeStageSupported {
536-
stagingPath = sc.Config.StagingPath
532+
stagingPath = sc.stagingPath
537533
}
538534
nodepubvol, err := c.NodePublishVolume(
539535
context.Background(),
540536
&csi.NodePublishVolumeRequest{
541537
VolumeId: vol.GetVolume().GetVolumeId(),
542-
TargetPath: sc.Config.TargetPath,
538+
TargetPath: sc.targetPath,
543539
StagingTargetPath: stagingPath,
544540
VolumeCapability: &csi.VolumeCapability{
545541
AccessType: &csi.VolumeCapability_Mount{
@@ -578,7 +574,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
578574
context.Background(),
579575
&csi.NodeUnpublishVolumeRequest{
580576
VolumeId: vol.GetVolume().GetVolumeId(),
581-
TargetPath: sc.Config.TargetPath,
577+
TargetPath: sc.targetPath,
582578
})
583579
Expect(err).NotTo(HaveOccurred())
584580
Expect(nodeunpubvol).NotTo(BeNil())
@@ -589,7 +585,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
589585
context.Background(),
590586
&csi.NodeUnstageVolumeRequest{
591587
VolumeId: vol.GetVolume().GetVolumeId(),
592-
StagingTargetPath: sc.Config.StagingPath,
588+
StagingTargetPath: sc.stagingPath,
593589
},
594590
)
595591
Expect(err).NotTo(HaveOccurred())
@@ -704,7 +700,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
704700
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
705701
},
706702
},
707-
StagingTargetPath: sc.Config.StagingPath,
703+
StagingTargetPath: sc.stagingPath,
708704
VolumeContext: vol.GetVolume().GetVolumeContext(),
709705
PublishContext: conpubvol.GetPublishContext(),
710706
Secrets: sc.Secrets.NodeStageVolumeSecret,
@@ -717,13 +713,13 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
717713
By("publishing the volume on a node")
718714
var stagingPath string
719715
if nodeStageSupported {
720-
stagingPath = sc.Config.StagingPath
716+
stagingPath = sc.stagingPath
721717
}
722718
nodepubvol, err := c.NodePublishVolume(
723719
context.Background(),
724720
&csi.NodePublishVolumeRequest{
725721
VolumeId: vol.GetVolume().GetVolumeId(),
726-
TargetPath: sc.Config.TargetPath,
722+
TargetPath: sc.targetPath,
727723
StagingTargetPath: stagingPath,
728724
VolumeCapability: &csi.VolumeCapability{
729725
AccessType: &csi.VolumeCapability_Mount{
@@ -748,7 +744,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
748744
context.Background(),
749745
&csi.NodeGetVolumeStatsRequest{
750746
VolumeId: vol.GetVolume().GetVolumeId(),
751-
VolumePath: sc.Config.TargetPath,
747+
VolumePath: sc.targetPath,
752748
},
753749
)
754750
Expect(err).ToNot(HaveOccurred())
@@ -761,7 +757,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
761757
context.Background(),
762758
&csi.NodeUnpublishVolumeRequest{
763759
VolumeId: vol.GetVolume().GetVolumeId(),
764-
TargetPath: sc.Config.TargetPath,
760+
TargetPath: sc.targetPath,
765761
})
766762
Expect(err).NotTo(HaveOccurred())
767763
Expect(nodeunpubvol).NotTo(BeNil())
@@ -772,7 +768,7 @@ var _ = DescribeSanity("Node Service", func(sc *SanityContext) {
772768
context.Background(),
773769
&csi.NodeUnstageVolumeRequest{
774770
VolumeId: vol.GetVolume().GetVolumeId(),
775-
StagingTargetPath: sc.Config.StagingPath,
771+
StagingTargetPath: sc.stagingPath,
776772
},
777773
)
778774
Expect(err).NotTo(HaveOccurred())

pkg/sanity/sanity.go

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ type Config struct {
5858
TestVolumeParameters map[string]string
5959

6060
JUnitFile string
61+
62+
// Callback functions to customize the creation of target and staging
63+
// directories. Returns the new paths for mount and staging.
64+
// If not defined, directories are created in the default way at TargetPath
65+
// and StagingPath.
66+
CreateTargetDir func(path string) (string, error)
67+
CreateStagingDir func(path string) (string, error)
6168
}
6269

6370
// SanityContext holds the variables that each test can depend on. It
@@ -68,6 +75,10 @@ type SanityContext struct {
6875
Secrets *CSISecrets
6976

7077
connAddress string
78+
79+
// Target and staging paths derived from the sanity config.
80+
targetPath string
81+
stagingPath string
7182
}
7283

7384
// Test will test the CSI driver at the specified address by
@@ -135,12 +146,15 @@ func (sc *SanityContext) setup() {
135146
}
136147

137148
By("creating mount and staging directories")
138-
err = createMountTargetLocation(sc.Config.TargetPath)
139-
Expect(err).NotTo(HaveOccurred())
140-
if len(sc.Config.StagingPath) > 0 {
141-
err = createMountTargetLocation(sc.Config.StagingPath)
142-
Expect(err).NotTo(HaveOccurred())
143-
}
149+
// If callback function for creating target dir is specified, use it.
150+
targetPath, err := createMountTargetLocation(sc.Config.TargetPath, sc.Config.CreateTargetDir)
151+
Expect(err).NotTo(HaveOccurred(), "failed to create target directory %s", targetPath)
152+
sc.targetPath = targetPath
153+
154+
// If callback function for creating staging dir is specified, use it.
155+
stagingPath, err := createMountTargetLocation(sc.Config.StagingPath, sc.Config.CreateStagingDir)
156+
Expect(err).NotTo(HaveOccurred(), "failed to create staging directory %s", stagingPath)
157+
sc.stagingPath = stagingPath
144158
}
145159

146160
func (sc *SanityContext) teardown() {
@@ -156,18 +170,35 @@ func (sc *SanityContext) teardown() {
156170
// (https://github.com/kubernetes-csi/csi-test/pull/98).
157171
}
158172

159-
func createMountTargetLocation(targetPath string) error {
160-
fileInfo, err := os.Stat(targetPath)
161-
if err != nil && os.IsNotExist(err) {
162-
return os.MkdirAll(targetPath, 0755)
163-
} else if err != nil {
164-
return err
173+
func createMountTargetLocation(targetPath string, customCreateDir func(string) (string, error)) (string, error) {
174+
175+
// Return the target path if empty.
176+
if len(targetPath) < 0 {
177+
return targetPath, nil
165178
}
166-
if !fileInfo.IsDir() {
167-
return fmt.Errorf("Target location %s is not a directory", targetPath)
179+
180+
var newTargetPath string
181+
182+
if customCreateDir != nil {
183+
newpath, err := customCreateDir(targetPath)
184+
if err != nil {
185+
return "", err
186+
}
187+
newTargetPath = newpath
188+
} else {
189+
fileInfo, err := os.Stat(targetPath)
190+
if err != nil && os.IsNotExist(err) {
191+
return "", os.MkdirAll(targetPath, 0755)
192+
} else if err != nil {
193+
return "", err
194+
}
195+
if !fileInfo.IsDir() {
196+
return "", fmt.Errorf("Target location %s is not a directory", targetPath)
197+
}
198+
newTargetPath = targetPath
168199
}
169200

170-
return nil
201+
return newTargetPath, nil
171202
}
172203

173204
func loadSecrets(path string) (*CSISecrets, error) {

0 commit comments

Comments
 (0)