1
16
17 package framework
18
19 import (
20 "context"
21 "fmt"
22 "time"
23
24 "github.com/onsi/ginkgo/v2"
25 v1 "k8s.io/api/core/v1"
26 storagev1 "k8s.io/api/storage/v1"
27 apierrors "k8s.io/apimachinery/pkg/api/errors"
28 "k8s.io/apimachinery/pkg/api/resource"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 utilerrors "k8s.io/apimachinery/pkg/util/errors"
31 "k8s.io/kubernetes/test/e2e/framework"
32 e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
33 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
34 e2evolume "k8s.io/kubernetes/test/e2e/framework/volume"
35 storageutils "k8s.io/kubernetes/test/e2e/storage/utils"
36 )
37
38
39
40
41
42 type VolumeResource struct {
43 Config *PerTestConfig
44 Pattern TestPattern
45 VolSource *v1.VolumeSource
46 Pvc *v1.PersistentVolumeClaim
47 Pv *v1.PersistentVolume
48 Sc *storagev1.StorageClass
49
50 Volume TestVolume
51 }
52
53
54
55 func CreateVolumeResource(ctx context.Context, driver TestDriver, config *PerTestConfig, pattern TestPattern, testVolumeSizeRange e2evolume.SizeRange) *VolumeResource {
56 return CreateVolumeResourceWithAccessModes(ctx, driver, config, pattern, testVolumeSizeRange, driver.GetDriverInfo().RequiredAccessModes)
57 }
58
59
60 func CreateVolumeResourceWithAccessModes(ctx context.Context, driver TestDriver, config *PerTestConfig, pattern TestPattern, testVolumeSizeRange e2evolume.SizeRange, accessModes []v1.PersistentVolumeAccessMode) *VolumeResource {
61 r := VolumeResource{
62 Config: config,
63 Pattern: pattern,
64 }
65 dInfo := driver.GetDriverInfo()
66 f := config.Framework
67 cs := f.ClientSet
68
69
70 r.Volume = CreateVolume(ctx, driver, config, pattern.VolType)
71
72 switch pattern.VolType {
73 case InlineVolume:
74 framework.Logf("Creating resource for inline volume")
75 if iDriver, ok := driver.(InlineVolumeTestDriver); ok {
76 r.VolSource = iDriver.GetVolumeSource(false, pattern.FsType, r.Volume)
77 }
78 case PreprovisionedPV:
79 framework.Logf("Creating resource for pre-provisioned PV")
80 if pDriver, ok := driver.(PreprovisionedPVTestDriver); ok {
81 pvSource, volumeNodeAffinity := pDriver.GetPersistentVolumeSource(false, pattern.FsType, r.Volume)
82 if pvSource != nil {
83 r.Pv, r.Pvc = createPVCPV(ctx, f, dInfo.Name, pvSource, volumeNodeAffinity, pattern.VolMode, accessModes)
84 r.VolSource = storageutils.CreateVolumeSource(r.Pvc.Name, false )
85 }
86 }
87 case DynamicPV, GenericEphemeralVolume:
88 framework.Logf("Creating resource for dynamic PV")
89 if dDriver, ok := driver.(DynamicPVTestDriver); ok {
90 var err error
91 driverVolumeSizeRange := dDriver.GetDriverInfo().SupportedSizeRange
92 claimSize, err := storageutils.GetSizeRangesIntersection(testVolumeSizeRange, driverVolumeSizeRange)
93 framework.ExpectNoError(err, "determine intersection of test size range %+v and driver size range %+v", testVolumeSizeRange, driverVolumeSizeRange)
94 framework.Logf("Using claimSize:%s, test suite supported size:%v, driver(%s) supported size:%v ", claimSize, testVolumeSizeRange, dDriver.GetDriverInfo().Name, testVolumeSizeRange)
95 r.Sc = dDriver.GetDynamicProvisionStorageClass(ctx, r.Config, pattern.FsType)
96
97 if pattern.BindingMode != "" {
98 r.Sc.VolumeBindingMode = &pattern.BindingMode
99 }
100 r.Sc.AllowVolumeExpansion = &pattern.AllowExpansion
101
102 ginkgo.By("creating a StorageClass " + r.Sc.Name)
103
104 r.Sc, err = cs.StorageV1().StorageClasses().Create(ctx, r.Sc, metav1.CreateOptions{})
105 framework.ExpectNoError(err)
106
107 switch pattern.VolType {
108 case DynamicPV:
109 r.Pv, r.Pvc = createPVCPVFromDynamicProvisionSC(
110 ctx, f, dInfo.Name, claimSize, r.Sc, pattern.VolMode, accessModes)
111 r.VolSource = storageutils.CreateVolumeSource(r.Pvc.Name, false )
112 case GenericEphemeralVolume:
113 driverVolumeSizeRange := dDriver.GetDriverInfo().SupportedSizeRange
114 claimSize, err := storageutils.GetSizeRangesIntersection(testVolumeSizeRange, driverVolumeSizeRange)
115 framework.ExpectNoError(err, "determine intersection of test size range %+v and driver size range %+v", testVolumeSizeRange, driverVolumeSizeRange)
116 r.VolSource = createEphemeralVolumeSource(r.Sc.Name, pattern.VolMode, accessModes, claimSize)
117 }
118 }
119 case CSIInlineVolume:
120 framework.Logf("Creating resource for CSI ephemeral inline volume")
121 if eDriver, ok := driver.(EphemeralTestDriver); ok {
122 attributes, _, _ := eDriver.GetVolume(config, 0)
123 r.VolSource = &v1.VolumeSource{
124 CSI: &v1.CSIVolumeSource{
125 Driver: eDriver.GetCSIDriverName(config),
126 VolumeAttributes: attributes,
127 },
128 }
129 if pattern.FsType != "" {
130 r.VolSource.CSI.FSType = &pattern.FsType
131 }
132 }
133 default:
134 framework.Failf("VolumeResource doesn't support: %s", pattern.VolType)
135 }
136
137 if r.VolSource == nil {
138 e2eskipper.Skipf("Driver %s doesn't support %v -- skipping", dInfo.Name, pattern.VolType)
139 }
140
141 return &r
142 }
143
144 func createEphemeralVolumeSource(scName string, volMode v1.PersistentVolumeMode, accessModes []v1.PersistentVolumeAccessMode, claimSize string) *v1.VolumeSource {
145 if len(accessModes) == 0 {
146 accessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}
147 }
148 if volMode == "" {
149 volMode = v1.PersistentVolumeFilesystem
150 }
151 return &v1.VolumeSource{
152 Ephemeral: &v1.EphemeralVolumeSource{
153 VolumeClaimTemplate: &v1.PersistentVolumeClaimTemplate{
154 Spec: v1.PersistentVolumeClaimSpec{
155 StorageClassName: &scName,
156 AccessModes: accessModes,
157 VolumeMode: &volMode,
158 Resources: v1.VolumeResourceRequirements{
159 Requests: v1.ResourceList{
160 v1.ResourceStorage: resource.MustParse(claimSize),
161 },
162 },
163 },
164 },
165 },
166 }
167 }
168
169
170 func (r *VolumeResource) CleanupResource(ctx context.Context) error {
171 f := r.Config.Framework
172 var cleanUpErrs []error
173 if r.Pvc != nil || r.Pv != nil {
174 switch r.Pattern.VolType {
175 case PreprovisionedPV:
176 ginkgo.By("Deleting pv and pvc")
177 if errs := e2epv.PVPVCCleanup(ctx, f.ClientSet, f.Namespace.Name, r.Pv, r.Pvc); len(errs) != 0 {
178 framework.Failf("Failed to delete PVC or PV: %v", utilerrors.NewAggregate(errs))
179 }
180 case DynamicPV:
181 ginkgo.By("Deleting pvc")
182
183 if r.Pv != nil && r.Pv.Spec.PersistentVolumeReclaimPolicy != v1.PersistentVolumeReclaimDelete {
184 framework.Failf("Test framework does not currently support Dynamically Provisioned Persistent Volume %v specified with reclaim policy that isn't %v",
185 r.Pv.Name, v1.PersistentVolumeReclaimDelete)
186 }
187 if r.Pvc != nil {
188 cs := f.ClientSet
189 pv := r.Pv
190 if pv == nil && r.Pvc.Name != "" {
191
192 pvc, err := cs.CoreV1().PersistentVolumeClaims(r.Pvc.Namespace).Get(ctx, r.Pvc.Name, metav1.GetOptions{})
193 switch {
194 case err == nil:
195 if pvc.Spec.VolumeName != "" {
196 pv, err = cs.CoreV1().PersistentVolumes().Get(ctx, pvc.Spec.VolumeName, metav1.GetOptions{})
197 if err != nil {
198 cleanUpErrs = append(cleanUpErrs, fmt.Errorf("failed to find PV %v: %w", pvc.Spec.VolumeName, err))
199 }
200 }
201 case apierrors.IsNotFound(err):
202
203
204 default:
205 cleanUpErrs = append(cleanUpErrs, fmt.Errorf("failed to find PVC %v: %w", r.Pvc.Name, err))
206 }
207 }
208
209 err := e2epv.DeletePersistentVolumeClaim(ctx, f.ClientSet, r.Pvc.Name, f.Namespace.Name)
210 if err != nil {
211 cleanUpErrs = append(cleanUpErrs, fmt.Errorf("failed to delete PVC %v: %w", r.Pvc.Name, err))
212 }
213
214 if pv != nil {
215 err = e2epv.WaitForPersistentVolumeDeleted(ctx, f.ClientSet, pv.Name, 5*time.Second, f.Timeouts.PVDelete)
216 if err != nil {
217 cleanUpErrs = append(cleanUpErrs, fmt.Errorf(
218 "persistent Volume %v not deleted by dynamic provisioner: %w", pv.Name, err))
219 }
220 }
221 }
222 default:
223 framework.Failf("Found PVC (%v) or PV (%v) but not running Preprovisioned or Dynamic test pattern", r.Pvc, r.Pv)
224 }
225 }
226
227 if r.Sc != nil {
228 ginkgo.By("Deleting sc")
229 if err := storageutils.DeleteStorageClass(ctx, f.ClientSet, r.Sc.Name); err != nil {
230 cleanUpErrs = append(cleanUpErrs, fmt.Errorf("failed to delete StorageClass %v: %w", r.Sc.Name, err))
231 }
232 }
233
234
235 if r.Volume != nil {
236 if err := storageutils.TryFunc(func() {
237 r.Volume.DeleteVolume(ctx)
238 }); err != nil {
239 cleanUpErrs = append(cleanUpErrs, fmt.Errorf("failed to delete Volume: %w", err))
240 }
241 }
242 return utilerrors.NewAggregate(cleanUpErrs)
243 }
244
245 func createPVCPV(
246 ctx context.Context,
247 f *framework.Framework,
248 name string,
249 pvSource *v1.PersistentVolumeSource,
250 volumeNodeAffinity *v1.VolumeNodeAffinity,
251 volMode v1.PersistentVolumeMode,
252 accessModes []v1.PersistentVolumeAccessMode,
253 ) (*v1.PersistentVolume, *v1.PersistentVolumeClaim) {
254 pvConfig := e2epv.PersistentVolumeConfig{
255 NamePrefix: fmt.Sprintf("%s-", name),
256 StorageClassName: f.Namespace.Name,
257 PVSource: *pvSource,
258 NodeAffinity: volumeNodeAffinity,
259 AccessModes: accessModes,
260 }
261
262 pvcConfig := e2epv.PersistentVolumeClaimConfig{
263 StorageClassName: &f.Namespace.Name,
264 AccessModes: accessModes,
265 }
266
267 if volMode != "" {
268 pvConfig.VolumeMode = &volMode
269 pvcConfig.VolumeMode = &volMode
270 }
271
272 framework.Logf("Creating PVC and PV")
273 pv, pvc, err := e2epv.CreatePVCPV(ctx, f.ClientSet, f.Timeouts, pvConfig, pvcConfig, f.Namespace.Name, false)
274 framework.ExpectNoError(err, "PVC, PV creation failed")
275
276 err = e2epv.WaitOnPVandPVC(ctx, f.ClientSet, f.Timeouts, f.Namespace.Name, pv, pvc)
277 framework.ExpectNoError(err, "PVC, PV failed to bind")
278
279 return pv, pvc
280 }
281
282 func createPVCPVFromDynamicProvisionSC(
283 ctx context.Context,
284 f *framework.Framework,
285 name string,
286 claimSize string,
287 sc *storagev1.StorageClass,
288 volMode v1.PersistentVolumeMode,
289 accessModes []v1.PersistentVolumeAccessMode,
290 ) (*v1.PersistentVolume, *v1.PersistentVolumeClaim) {
291 cs := f.ClientSet
292 ns := f.Namespace.Name
293
294 ginkgo.By("creating a claim")
295 pvcCfg := e2epv.PersistentVolumeClaimConfig{
296 NamePrefix: name,
297 ClaimSize: claimSize,
298 StorageClassName: &(sc.Name),
299 AccessModes: accessModes,
300 VolumeMode: &volMode,
301 }
302
303 pvc := e2epv.MakePersistentVolumeClaim(pvcCfg, ns)
304
305 var err error
306 pvc, err = e2epv.CreatePVC(ctx, cs, ns, pvc)
307 framework.ExpectNoError(err)
308
309 if !isDelayedBinding(sc) {
310 err = e2epv.WaitForPersistentVolumeClaimPhase(ctx, v1.ClaimBound, cs, pvc.Namespace, pvc.Name, framework.Poll, f.Timeouts.ClaimProvision)
311 framework.ExpectNoError(err)
312 }
313
314 pvc, err = cs.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(ctx, pvc.Name, metav1.GetOptions{})
315 framework.ExpectNoError(err)
316
317 var pv *v1.PersistentVolume
318 if !isDelayedBinding(sc) {
319 pv, err = cs.CoreV1().PersistentVolumes().Get(ctx, pvc.Spec.VolumeName, metav1.GetOptions{})
320 framework.ExpectNoError(err)
321 }
322
323 return pv, pvc
324 }
325
326 func isDelayedBinding(sc *storagev1.StorageClass) bool {
327 if sc.VolumeBindingMode != nil {
328 return *sc.VolumeBindingMode == storagev1.VolumeBindingWaitForFirstConsumer
329 }
330 return false
331 }
332
View as plain text