Skip to content

Commit 7541597

Browse files
committed
sanity/node.go: add repetition loop to test idempotency
Repeating node volume operations helps to test idempotency requirements. sanity/sanity.go: add idempotency count variable in config.
1 parent 33de331 commit 7541597

File tree

2 files changed

+197
-160
lines changed

2 files changed

+197
-160
lines changed

pkg/sanity/node.go

+188-160
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,183 @@ func isPluginCapabilitySupported(c csi.IdentityClient,
6666
return false
6767
}
6868

69+
func runControllerTest(sc *TestContext, c csi.NodeClient, s csi.ControllerClient, cl *Cleanup, controllerPublishSupported bool, nodeStageSupported bool, nodeVolumeStatsSupported bool, count int) {
70+
71+
name := UniqueString("sanity-node-full")
72+
73+
By("getting node information")
74+
ni, err := c.NodeGetInfo(
75+
context.Background(),
76+
&csi.NodeGetInfoRequest{})
77+
Expect(err).NotTo(HaveOccurred())
78+
Expect(ni).NotTo(BeNil())
79+
Expect(ni.GetNodeId()).NotTo(BeEmpty())
80+
81+
var accReqs *csi.TopologyRequirement
82+
if ni.AccessibleTopology != nil {
83+
// Topology requirements are honored if provided by the driver
84+
accReqs = &csi.TopologyRequirement{
85+
Requisite: []*csi.Topology{ni.AccessibleTopology},
86+
}
87+
}
88+
89+
// Create Volume First
90+
By("creating a single node writer volume")
91+
vol, err := s.CreateVolume(
92+
context.Background(),
93+
&csi.CreateVolumeRequest{
94+
Name: name,
95+
VolumeCapabilities: []*csi.VolumeCapability{
96+
TestVolumeCapabilityWithAccessType(sc, csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER),
97+
},
98+
CapacityRange: &csi.CapacityRange{
99+
RequiredBytes: TestVolumeSize(sc),
100+
},
101+
Secrets: sc.Secrets.CreateVolumeSecret,
102+
Parameters: sc.Config.TestVolumeParameters,
103+
AccessibilityRequirements: accReqs,
104+
},
105+
)
106+
Expect(err).NotTo(HaveOccurred())
107+
Expect(vol).NotTo(BeNil())
108+
Expect(vol.GetVolume()).NotTo(BeNil())
109+
Expect(vol.GetVolume().GetVolumeId()).NotTo(BeEmpty())
110+
cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetVolumeId()})
111+
112+
var conpubvol *csi.ControllerPublishVolumeResponse
113+
if controllerPublishSupported {
114+
By("controller publishing volume")
115+
116+
conpubvol, err = s.ControllerPublishVolume(
117+
context.Background(),
118+
&csi.ControllerPublishVolumeRequest{
119+
VolumeId: vol.GetVolume().GetVolumeId(),
120+
NodeId: ni.GetNodeId(),
121+
VolumeCapability: TestVolumeCapabilityWithAccessType(sc, csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER),
122+
VolumeContext: vol.GetVolume().GetVolumeContext(),
123+
Readonly: false,
124+
Secrets: sc.Secrets.ControllerPublishVolumeSecret,
125+
},
126+
)
127+
Expect(err).NotTo(HaveOccurred())
128+
cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetVolumeId(), NodeID: ni.GetNodeId()})
129+
Expect(conpubvol).NotTo(BeNil())
130+
}
131+
// NodeStageVolume
132+
if nodeStageSupported {
133+
for i := 0; i < count; i++ {
134+
By("node staging volume")
135+
nodestagevol, err := c.NodeStageVolume(
136+
context.Background(),
137+
&csi.NodeStageVolumeRequest{
138+
VolumeId: vol.GetVolume().GetVolumeId(),
139+
VolumeCapability: TestVolumeCapabilityWithAccessType(sc, csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER),
140+
StagingTargetPath: sc.StagingPath,
141+
VolumeContext: vol.GetVolume().GetVolumeContext(),
142+
PublishContext: conpubvol.GetPublishContext(),
143+
Secrets: sc.Secrets.NodeStageVolumeSecret,
144+
},
145+
)
146+
Expect(err).NotTo(HaveOccurred())
147+
Expect(nodestagevol).NotTo(BeNil())
148+
}
149+
}
150+
// NodePublishVolume
151+
var stagingPath string
152+
if nodeStageSupported {
153+
stagingPath = sc.StagingPath
154+
}
155+
for i := 0; i < count; i++ {
156+
By("publishing the volume on a node")
157+
nodepubvol, err := c.NodePublishVolume(
158+
context.Background(),
159+
&csi.NodePublishVolumeRequest{
160+
VolumeId: vol.GetVolume().GetVolumeId(),
161+
TargetPath: sc.TargetPath + "/target",
162+
StagingTargetPath: stagingPath,
163+
VolumeCapability: TestVolumeCapabilityWithAccessType(sc, csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER),
164+
VolumeContext: vol.GetVolume().GetVolumeContext(),
165+
PublishContext: conpubvol.GetPublishContext(),
166+
Secrets: sc.Secrets.NodePublishVolumeSecret,
167+
},
168+
)
169+
Expect(err).NotTo(HaveOccurred())
170+
Expect(nodepubvol).NotTo(BeNil())
171+
}
172+
173+
// NodeGetVolumeStats
174+
if nodeVolumeStatsSupported {
175+
By("Get node volume stats")
176+
statsResp, err := c.NodeGetVolumeStats(
177+
context.Background(),
178+
&csi.NodeGetVolumeStatsRequest{
179+
VolumeId: vol.GetVolume().GetVolumeId(),
180+
VolumePath: sc.TargetPath + "/target",
181+
},
182+
)
183+
Expect(err).ToNot(HaveOccurred())
184+
Expect(statsResp.GetUsage()).ToNot(BeNil())
185+
}
186+
187+
// NodeUnpublishVolume
188+
for i := 0; i < count; i++ {
189+
By("cleaning up calling nodeunpublish")
190+
nodeunpubvol, err := c.NodeUnpublishVolume(
191+
context.Background(),
192+
&csi.NodeUnpublishVolumeRequest{
193+
VolumeId: vol.GetVolume().GetVolumeId(),
194+
TargetPath: sc.TargetPath + "/target",
195+
})
196+
Expect(err).NotTo(HaveOccurred())
197+
Expect(nodeunpubvol).NotTo(BeNil())
198+
}
199+
200+
if nodeStageSupported {
201+
for i := 0; i < count; i++ {
202+
By("cleaning up calling nodeunstage")
203+
nodeunstagevol, err := c.NodeUnstageVolume(
204+
context.Background(),
205+
&csi.NodeUnstageVolumeRequest{
206+
VolumeId: vol.GetVolume().GetVolumeId(),
207+
StagingTargetPath: sc.StagingPath,
208+
},
209+
)
210+
Expect(err).NotTo(HaveOccurred())
211+
Expect(nodeunstagevol).NotTo(BeNil())
212+
}
213+
}
214+
215+
if controllerPublishSupported {
216+
for i := 0; i < count; i++ {
217+
By("cleaning up calling controllerunpublishing")
218+
219+
controllerunpubvol, err := s.ControllerUnpublishVolume(
220+
context.Background(),
221+
&csi.ControllerUnpublishVolumeRequest{
222+
VolumeId: vol.GetVolume().GetVolumeId(),
223+
NodeId: ni.GetNodeId(),
224+
Secrets: sc.Secrets.ControllerUnpublishVolumeSecret,
225+
},
226+
)
227+
Expect(err).NotTo(HaveOccurred())
228+
Expect(controllerunpubvol).NotTo(BeNil())
229+
}
230+
}
231+
232+
for i := 0; i < count; i++ {
233+
By("cleaning up deleting the volume")
234+
235+
_, err = s.DeleteVolume(
236+
context.Background(),
237+
&csi.DeleteVolumeRequest{
238+
VolumeId: vol.GetVolume().GetVolumeId(),
239+
Secrets: sc.Secrets.DeleteVolumeSecret,
240+
},
241+
)
242+
Expect(err).NotTo(HaveOccurred())
243+
}
244+
}
245+
69246
var _ = DescribeSanity("Node Service", func(sc *TestContext) {
70247
var (
71248
cl *Cleanup
@@ -618,167 +795,18 @@ var _ = DescribeSanity("Node Service", func(sc *TestContext) {
618795
if !providesControllerService {
619796
Skip("Controller Service not provided: CreateVolume not supported")
620797
}
621-
622-
name := UniqueString("sanity-node-full")
623-
624-
By("getting node information")
625-
ni, err := c.NodeGetInfo(
626-
context.Background(),
627-
&csi.NodeGetInfoRequest{})
628-
Expect(err).NotTo(HaveOccurred())
629-
Expect(ni).NotTo(BeNil())
630-
Expect(ni.GetNodeId()).NotTo(BeEmpty())
631-
632-
var accReqs *csi.TopologyRequirement
633-
if ni.AccessibleTopology != nil {
634-
// Topology requirements are honored if provided by the driver
635-
accReqs = &csi.TopologyRequirement{
636-
Requisite: []*csi.Topology{ni.AccessibleTopology},
637-
}
638-
}
639-
640-
// Create Volume First
641-
By("creating a single node writer volume")
642-
vol, err := s.CreateVolume(
643-
context.Background(),
644-
&csi.CreateVolumeRequest{
645-
Name: name,
646-
VolumeCapabilities: []*csi.VolumeCapability{
647-
TestVolumeCapabilityWithAccessType(sc, csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER),
648-
},
649-
CapacityRange: &csi.CapacityRange{
650-
RequiredBytes: TestVolumeSize(sc),
651-
},
652-
Secrets: sc.Secrets.CreateVolumeSecret,
653-
Parameters: sc.Config.TestVolumeParameters,
654-
AccessibilityRequirements: accReqs,
655-
},
656-
)
657-
Expect(err).NotTo(HaveOccurred())
658-
Expect(vol).NotTo(BeNil())
659-
Expect(vol.GetVolume()).NotTo(BeNil())
660-
Expect(vol.GetVolume().GetVolumeId()).NotTo(BeEmpty())
661-
cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetVolumeId()})
662-
663-
var conpubvol *csi.ControllerPublishVolumeResponse
664-
if controllerPublishSupported {
665-
By("controller publishing volume")
666-
667-
conpubvol, err = s.ControllerPublishVolume(
668-
context.Background(),
669-
&csi.ControllerPublishVolumeRequest{
670-
VolumeId: vol.GetVolume().GetVolumeId(),
671-
NodeId: ni.GetNodeId(),
672-
VolumeCapability: TestVolumeCapabilityWithAccessType(sc, csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER),
673-
VolumeContext: vol.GetVolume().GetVolumeContext(),
674-
Readonly: false,
675-
Secrets: sc.Secrets.ControllerPublishVolumeSecret,
676-
},
677-
)
678-
Expect(err).NotTo(HaveOccurred())
679-
cl.RegisterVolume(name, VolumeInfo{VolumeID: vol.GetVolume().GetVolumeId(), NodeID: ni.GetNodeId()})
680-
Expect(conpubvol).NotTo(BeNil())
681-
}
682-
// NodeStageVolume
683-
if nodeStageSupported {
684-
By("node staging volume")
685-
nodestagevol, err := c.NodeStageVolume(
686-
context.Background(),
687-
&csi.NodeStageVolumeRequest{
688-
VolumeId: vol.GetVolume().GetVolumeId(),
689-
VolumeCapability: TestVolumeCapabilityWithAccessType(sc, csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER),
690-
StagingTargetPath: sc.StagingPath,
691-
VolumeContext: vol.GetVolume().GetVolumeContext(),
692-
PublishContext: conpubvol.GetPublishContext(),
693-
Secrets: sc.Secrets.NodeStageVolumeSecret,
694-
},
695-
)
696-
Expect(err).NotTo(HaveOccurred())
697-
Expect(nodestagevol).NotTo(BeNil())
698-
}
699-
// NodePublishVolume
700-
By("publishing the volume on a node")
701-
var stagingPath string
702-
if nodeStageSupported {
703-
stagingPath = sc.StagingPath
704-
}
705-
nodepubvol, err := c.NodePublishVolume(
706-
context.Background(),
707-
&csi.NodePublishVolumeRequest{
708-
VolumeId: vol.GetVolume().GetVolumeId(),
709-
TargetPath: sc.TargetPath + "/target",
710-
StagingTargetPath: stagingPath,
711-
VolumeCapability: TestVolumeCapabilityWithAccessType(sc, csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER),
712-
VolumeContext: vol.GetVolume().GetVolumeContext(),
713-
PublishContext: conpubvol.GetPublishContext(),
714-
Secrets: sc.Secrets.NodePublishVolumeSecret,
715-
},
716-
)
717-
Expect(err).NotTo(HaveOccurred())
718-
Expect(nodepubvol).NotTo(BeNil())
719-
720-
// NodeGetVolumeStats
721-
if nodeVolumeStatsSupported {
722-
By("Get node volume stats")
723-
statsResp, err := c.NodeGetVolumeStats(
724-
context.Background(),
725-
&csi.NodeGetVolumeStatsRequest{
726-
VolumeId: vol.GetVolume().GetVolumeId(),
727-
VolumePath: sc.TargetPath + "/target",
728-
},
729-
)
730-
Expect(err).ToNot(HaveOccurred())
731-
Expect(statsResp.GetUsage()).ToNot(BeNil())
732-
}
733-
734-
// NodeUnpublishVolume
735-
By("cleaning up calling nodeunpublish")
736-
nodeunpubvol, err := c.NodeUnpublishVolume(
737-
context.Background(),
738-
&csi.NodeUnpublishVolumeRequest{
739-
VolumeId: vol.GetVolume().GetVolumeId(),
740-
TargetPath: sc.TargetPath + "/target",
741-
})
742-
Expect(err).NotTo(HaveOccurred())
743-
Expect(nodeunpubvol).NotTo(BeNil())
744-
745-
if nodeStageSupported {
746-
By("cleaning up calling nodeunstage")
747-
nodeunstagevol, err := c.NodeUnstageVolume(
748-
context.Background(),
749-
&csi.NodeUnstageVolumeRequest{
750-
VolumeId: vol.GetVolume().GetVolumeId(),
751-
StagingTargetPath: sc.StagingPath,
752-
},
753-
)
754-
Expect(err).NotTo(HaveOccurred())
755-
Expect(nodeunstagevol).NotTo(BeNil())
798+
By("runControllerTest")
799+
runControllerTest(sc, c, s, cl, controllerPublishSupported, nodeStageSupported, nodeVolumeStatsSupported, 1)
800+
})
801+
It("should be idempotent", func() {
802+
if !providesControllerService {
803+
Skip("Controller Service not provided: CreateVolume not supported")
756804
}
757-
758-
if controllerPublishSupported {
759-
By("cleaning up calling controllerunpublishing")
760-
761-
controllerunpubvol, err := s.ControllerUnpublishVolume(
762-
context.Background(),
763-
&csi.ControllerUnpublishVolumeRequest{
764-
VolumeId: vol.GetVolume().GetVolumeId(),
765-
NodeId: ni.GetNodeId(),
766-
Secrets: sc.Secrets.ControllerUnpublishVolumeSecret,
767-
},
768-
)
769-
Expect(err).NotTo(HaveOccurred())
770-
Expect(controllerunpubvol).NotTo(BeNil())
805+
if sc.Config.IdempotentCount < 0 {
806+
Skip("Config.IdempotentCount is negative, skip tests")
771807
}
772-
773-
By("cleaning up deleting the volume")
774-
775-
_, err = s.DeleteVolume(
776-
context.Background(),
777-
&csi.DeleteVolumeRequest{
778-
VolumeId: vol.GetVolume().GetVolumeId(),
779-
Secrets: sc.Secrets.DeleteVolumeSecret,
780-
},
781-
)
782-
Expect(err).NotTo(HaveOccurred())
808+
count := sc.Config.IdempotentCount
809+
By("runControllerTest with Idempotent count")
810+
runControllerTest(sc, c, s, cl, controllerPublishSupported, nodeStageSupported, nodeVolumeStatsSupported, count)
783811
})
784812
})

pkg/sanity/sanity.go

+9
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ type TestConfig struct {
156156
// generator for valid Volume and Node IDs. Defaults to
157157
// DefaultIDGenerator.
158158
IDGen IDGenerator
159+
160+
// Repeat count for Volume operations to test idempotency requirements.
161+
// some tests can optionally run repeated variants for those Volume operations
162+
// that are required to be idempotent, based on this count value.
163+
// -1 : Skip idempotency test
164+
// n : repeat n times
165+
// Note that missing explicit value leads to default repetition count.
166+
IdempotentCount int
159167
}
160168

161169
// TestContext gets initialized by the sanity package before each test
@@ -185,6 +193,7 @@ func NewTestConfig() TestConfig {
185193
TestVolumeSize: 10 * 1024 * 1024 * 1024, // 10 GiB
186194
TestVolumeAccessType: "mount",
187195
IDGen: &DefaultIDGenerator{},
196+
IdempotentCount: 10,
188197

189198
DialOptions: []grpc.DialOption{grpc.WithInsecure()},
190199
ControllerDialOptions: []grpc.DialOption{grpc.WithInsecure()},

0 commit comments

Comments
 (0)