1
16
17
18
19 package testsuites
20
21 import (
22 "context"
23 "fmt"
24 "path/filepath"
25
26 "github.com/onsi/ginkgo/v2"
27
28 v1 "k8s.io/api/core/v1"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/util/errors"
31 "k8s.io/kubernetes/test/e2e/framework"
32 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
33 e2eoutput "k8s.io/kubernetes/test/e2e/framework/pod/output"
34 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
35 e2evolume "k8s.io/kubernetes/test/e2e/framework/volume"
36 storageframework "k8s.io/kubernetes/test/e2e/storage/framework"
37 imageutils "k8s.io/kubernetes/test/utils/image"
38 admissionapi "k8s.io/pod-security-admission/api"
39 )
40
41 type volumesTestSuite struct {
42 tsInfo storageframework.TestSuiteInfo
43 }
44
45 var _ storageframework.TestSuite = &volumesTestSuite{}
46
47
48
49 func InitCustomVolumesTestSuite(patterns []storageframework.TestPattern) storageframework.TestSuite {
50 return &volumesTestSuite{
51 tsInfo: storageframework.TestSuiteInfo{
52 Name: "volumes",
53 TestPatterns: patterns,
54 SupportedSizeRange: e2evolume.SizeRange{
55 Min: "1Mi",
56 },
57 },
58 }
59 }
60
61
62
63 func InitVolumesTestSuite() storageframework.TestSuite {
64 patterns := []storageframework.TestPattern{
65
66 storageframework.DefaultFsInlineVolume,
67 storageframework.DefaultFsPreprovisionedPV,
68 storageframework.DefaultFsDynamicPV,
69
70 storageframework.Ext3InlineVolume,
71 storageframework.Ext3PreprovisionedPV,
72 storageframework.Ext3DynamicPV,
73
74 storageframework.Ext4InlineVolume,
75 storageframework.Ext4PreprovisionedPV,
76 storageframework.Ext4DynamicPV,
77
78 storageframework.XfsInlineVolume,
79 storageframework.XfsPreprovisionedPV,
80 storageframework.XfsDynamicPV,
81
82 storageframework.NtfsInlineVolume,
83 storageframework.NtfsPreprovisionedPV,
84 storageframework.NtfsDynamicPV,
85
86 storageframework.BlockVolModePreprovisionedPV,
87 storageframework.BlockVolModeDynamicPV,
88 }
89 return InitCustomVolumesTestSuite(patterns)
90 }
91
92 func (t *volumesTestSuite) GetTestSuiteInfo() storageframework.TestSuiteInfo {
93 return t.tsInfo
94 }
95
96 func (t *volumesTestSuite) SkipUnsupportedTests(driver storageframework.TestDriver, pattern storageframework.TestPattern) {
97 if pattern.VolMode == v1.PersistentVolumeBlock {
98 skipTestIfBlockNotSupported(driver)
99 }
100 }
101
102 func skipExecTest(driver storageframework.TestDriver) {
103 dInfo := driver.GetDriverInfo()
104 if !dInfo.Capabilities[storageframework.CapExec] {
105 e2eskipper.Skipf("Driver %q does not support exec - skipping", dInfo.Name)
106 }
107 }
108
109 func skipTestIfBlockNotSupported(driver storageframework.TestDriver) {
110 dInfo := driver.GetDriverInfo()
111 if !dInfo.Capabilities[storageframework.CapBlock] {
112 e2eskipper.Skipf("Driver %q does not provide raw block - skipping", dInfo.Name)
113 }
114 }
115
116 func (t *volumesTestSuite) DefineTests(driver storageframework.TestDriver, pattern storageframework.TestPattern) {
117 type local struct {
118 config *storageframework.PerTestConfig
119
120 resource *storageframework.VolumeResource
121
122 migrationCheck *migrationOpCheck
123 }
124 var dInfo = driver.GetDriverInfo()
125 var l local
126
127
128
129 f := framework.NewFrameworkWithCustomTimeouts("volume", storageframework.GetDriverTimeouts(driver))
130 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
131
132 init := func(ctx context.Context) {
133 l = local{}
134
135
136 l.config = driver.PrepareTest(ctx, f)
137 l.migrationCheck = newMigrationOpCheck(ctx, f.ClientSet, f.ClientConfig(), dInfo.InTreePluginName)
138 testVolumeSizeRange := t.GetTestSuiteInfo().SupportedSizeRange
139 l.resource = storageframework.CreateVolumeResource(ctx, driver, l.config, pattern, testVolumeSizeRange)
140 if l.resource.VolSource == nil {
141 e2eskipper.Skipf("Driver %q does not define volumeSource - skipping", dInfo.Name)
142 }
143 }
144
145 cleanup := func(ctx context.Context) {
146 var errs []error
147 if l.resource != nil {
148 errs = append(errs, l.resource.CleanupResource(ctx))
149 l.resource = nil
150 }
151
152 framework.ExpectNoError(errors.NewAggregate(errs), "while cleaning up resource")
153 l.migrationCheck.validateMigrationVolumeOpCounts(ctx)
154 }
155
156 ginkgo.It("should store data", func(ctx context.Context) {
157 init(ctx)
158 ginkgo.DeferCleanup(e2evolume.TestServerCleanup, f, storageframework.ConvertTestConfig(l.config))
159 ginkgo.DeferCleanup(cleanup)
160
161 tests := []e2evolume.Test{
162 {
163 Volume: *l.resource.VolSource,
164 Mode: pattern.VolMode,
165 File: "index.html",
166
167 ExpectedContent: fmt.Sprintf("Hello from %s from namespace %s",
168 dInfo.Name, f.Namespace.Name),
169 },
170 }
171 config := storageframework.ConvertTestConfig(l.config)
172 var fsGroup *int64
173 if framework.NodeOSDistroIs("windows") && dInfo.Capabilities[storageframework.CapFsGroup] {
174 fsGroupVal := int64(1234)
175 fsGroup = &fsGroupVal
176 }
177
178
179
180
181 e2evolume.InjectContent(ctx, f, config, fsGroup, pattern.FsType, tests)
182 if driver.GetDriverInfo().Capabilities[storageframework.CapPersistence] {
183 e2evolume.TestVolumeClient(ctx, f, config, fsGroup, pattern.FsType, tests)
184 } else {
185 ginkgo.By("Skipping persistence check for non-persistent volume")
186 }
187 })
188
189
190 if pattern.VolMode != v1.PersistentVolumeBlock {
191 ginkgo.It("should allow exec of files on the volume", func(ctx context.Context) {
192 skipExecTest(driver)
193 init(ctx)
194 ginkgo.DeferCleanup(cleanup)
195
196 testScriptInPod(ctx, f, string(pattern.VolType), l.resource.VolSource, l.config)
197 })
198 }
199 }
200
201 func testScriptInPod(
202 ctx context.Context,
203 f *framework.Framework,
204 volumeType string,
205 source *v1.VolumeSource,
206 config *storageframework.PerTestConfig) {
207
208 const (
209 volPath = "/vol1"
210 volName = "vol1"
211 )
212 suffix := generateSuffixForPodName(volumeType)
213 fileName := fmt.Sprintf("test-%s", suffix)
214 var content string
215 if framework.NodeOSDistroIs("windows") {
216 content = fmt.Sprintf("ls -n %s", volPath)
217 } else {
218 content = fmt.Sprintf("ls %s", volPath)
219 }
220 command := generateWriteandExecuteScriptFileCmd(content, fileName, volPath)
221 pod := &v1.Pod{
222 ObjectMeta: metav1.ObjectMeta{
223 Name: fmt.Sprintf("exec-volume-test-%s", suffix),
224 Namespace: f.Namespace.Name,
225 },
226 Spec: v1.PodSpec{
227 Containers: []v1.Container{
228 {
229 Name: fmt.Sprintf("exec-container-%s", suffix),
230 Image: e2epod.GetTestImage(imageutils.Nginx),
231 Command: command,
232 VolumeMounts: []v1.VolumeMount{
233 {
234 Name: volName,
235 MountPath: volPath,
236 },
237 },
238 },
239 },
240 Volumes: []v1.Volume{
241 {
242 Name: volName,
243 VolumeSource: *source,
244 },
245 },
246 RestartPolicy: v1.RestartPolicyNever,
247 },
248 }
249 e2epod.SetNodeSelection(&pod.Spec, config.ClientNodeSelection)
250 ginkgo.By(fmt.Sprintf("Creating pod %s", pod.Name))
251 e2eoutput.TestContainerOutput(ctx, f, "exec-volume-test", pod, 0, []string{fileName})
252
253 ginkgo.By(fmt.Sprintf("Deleting pod %s", pod.Name))
254 err := e2epod.DeletePodWithWait(ctx, f.ClientSet, pod)
255 framework.ExpectNoError(err, "while deleting pod")
256 }
257
258
259
260
261 func generateWriteandExecuteScriptFileCmd(content, fileName, filePath string) []string {
262
263 if framework.NodeOSDistroIs("windows") {
264 scriptName := fmt.Sprintf("%s.ps1", fileName)
265 fullPath := filepath.Join(filePath, scriptName)
266
267 cmd := "echo \"" + content + "\" > " + fullPath + "; .\\" + fullPath
268 framework.Logf("generated pod command %s", cmd)
269 return []string{"powershell", "/c", cmd}
270 }
271 scriptName := fmt.Sprintf("%s.sh", fileName)
272 fullPath := filepath.Join(filePath, scriptName)
273 cmd := fmt.Sprintf("echo \"%s\" > %s; chmod u+x %s; %s;", content, fullPath, fullPath, fullPath)
274 return []string{"/bin/sh", "-ec", cmd}
275 }
276
View as plain text