1
16
17 package framework
18
19 import (
20 "context"
21 "fmt"
22
23 "github.com/onsi/ginkgo/v2"
24
25 apierrors "k8s.io/apimachinery/pkg/api/errors"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28 "k8s.io/apimachinery/pkg/types"
29 utilerrors "k8s.io/apimachinery/pkg/util/errors"
30 "k8s.io/apimachinery/pkg/util/uuid"
31 "k8s.io/kubernetes/test/e2e/framework"
32 "k8s.io/kubernetes/test/e2e/storage/utils"
33 )
34
35
36 type SnapshotResource struct {
37 Config *PerTestConfig
38 Pattern TestPattern
39
40 Vs *unstructured.Unstructured
41 Vscontent *unstructured.Unstructured
42 Vsclass *unstructured.Unstructured
43 }
44
45
46
47
48 func CreateSnapshot(ctx context.Context, sDriver SnapshottableTestDriver, config *PerTestConfig, pattern TestPattern, pvcName string, pvcNamespace string, timeouts *framework.TimeoutContext, parameters map[string]string) (*unstructured.Unstructured, *unstructured.Unstructured) {
49 defer ginkgo.GinkgoRecover()
50 var err error
51 if pattern.SnapshotType != DynamicCreatedSnapshot && pattern.SnapshotType != PreprovisionedCreatedSnapshot {
52 err = fmt.Errorf("SnapshotType must be set to either DynamicCreatedSnapshot or PreprovisionedCreatedSnapshot")
53 framework.ExpectNoError(err)
54 }
55 dc := config.Framework.DynamicClient
56
57 ginkgo.By("creating a SnapshotClass")
58 sclass := sDriver.GetSnapshotClass(ctx, config, parameters)
59 if sclass == nil {
60 framework.Failf("Failed to get snapshot class based on test config")
61 }
62 sclass.Object["deletionPolicy"] = pattern.SnapshotDeletionPolicy.String()
63
64 sclass, err = dc.Resource(utils.SnapshotClassGVR).Create(ctx, sclass, metav1.CreateOptions{})
65 framework.ExpectNoError(err)
66
67 sclass, err = dc.Resource(utils.SnapshotClassGVR).Get(ctx, sclass.GetName(), metav1.GetOptions{})
68 framework.ExpectNoError(err)
69
70 ginkgo.By("creating a dynamic VolumeSnapshot")
71
72 snapshot := getSnapshot(pvcName, pvcNamespace, sclass.GetName())
73
74 snapshot, err = dc.Resource(utils.SnapshotGVR).Namespace(snapshot.GetNamespace()).Create(ctx, snapshot, metav1.CreateOptions{})
75 framework.ExpectNoError(err)
76
77 return sclass, snapshot
78 }
79
80
81
82 func CreateSnapshotResource(ctx context.Context, sDriver SnapshottableTestDriver, config *PerTestConfig, pattern TestPattern, pvcName string, pvcNamespace string, timeouts *framework.TimeoutContext, parameters map[string]string) *SnapshotResource {
83 var err error
84 r := SnapshotResource{
85 Config: config,
86 Pattern: pattern,
87 }
88 r.Vsclass, r.Vs = CreateSnapshot(ctx, sDriver, config, pattern, pvcName, pvcNamespace, timeouts, parameters)
89
90 dc := r.Config.Framework.DynamicClient
91
92 r.Vscontent = utils.GetSnapshotContentFromSnapshot(ctx, dc, r.Vs, timeouts.SnapshotCreate)
93
94 if pattern.SnapshotType == PreprovisionedCreatedSnapshot {
95
96
97
98
99
100
101 ginkgo.By("updating the snapshot content deletion policy to retain")
102 r.Vscontent.Object["spec"].(map[string]interface{})["deletionPolicy"] = "Retain"
103
104 r.Vscontent, err = dc.Resource(utils.SnapshotContentGVR).Update(ctx, r.Vscontent, metav1.UpdateOptions{})
105 framework.ExpectNoError(err)
106
107 ginkgo.By("recording properties of the preprovisioned snapshot")
108 snapshotHandle := r.Vscontent.Object["status"].(map[string]interface{})["snapshotHandle"].(string)
109 framework.Logf("Recording snapshot content handle: %s", snapshotHandle)
110 snapshotContentAnnotations := r.Vscontent.GetAnnotations()
111 framework.Logf("Recording snapshot content annotations: %v", snapshotContentAnnotations)
112 csiDriverName := r.Vsclass.Object["driver"].(string)
113 framework.Logf("Recording snapshot driver: %s", csiDriverName)
114
115
116
117
118
119 ginkgo.By("deleting the snapshot and snapshot content")
120 err = dc.Resource(utils.SnapshotGVR).Namespace(r.Vs.GetNamespace()).Delete(ctx, r.Vs.GetName(), metav1.DeleteOptions{})
121 if apierrors.IsNotFound(err) {
122 err = nil
123 }
124 framework.ExpectNoError(err)
125
126 ginkgo.By("checking the Snapshot has been deleted")
127 err = utils.WaitForNamespacedGVRDeletion(ctx, dc, utils.SnapshotGVR, r.Vs.GetName(), r.Vs.GetNamespace(), framework.Poll, timeouts.SnapshotDelete)
128 framework.ExpectNoError(err)
129
130 err = dc.Resource(utils.SnapshotContentGVR).Delete(ctx, r.Vscontent.GetName(), metav1.DeleteOptions{})
131 if apierrors.IsNotFound(err) {
132 err = nil
133 }
134 framework.ExpectNoError(err)
135
136 ginkgo.By("checking the Snapshot content has been deleted")
137 err = utils.WaitForGVRDeletion(ctx, dc, utils.SnapshotContentGVR, r.Vscontent.GetName(), framework.Poll, timeouts.SnapshotDelete)
138 framework.ExpectNoError(err)
139
140 ginkgo.By("creating a snapshot content with the snapshot handle")
141 uuid := uuid.NewUUID()
142
143 snapName := getPreProvisionedSnapshotName(uuid)
144 snapcontentName := getPreProvisionedSnapshotContentName(uuid)
145
146 r.Vscontent = getPreProvisionedSnapshotContent(snapcontentName, snapshotContentAnnotations, snapName, pvcNamespace, snapshotHandle, pattern.SnapshotDeletionPolicy.String(), csiDriverName)
147 r.Vscontent, err = dc.Resource(utils.SnapshotContentGVR).Create(ctx, r.Vscontent, metav1.CreateOptions{})
148 framework.ExpectNoError(err)
149
150 ginkgo.By("creating a snapshot with that snapshot content")
151 r.Vs = getPreProvisionedSnapshot(snapName, pvcNamespace, snapcontentName)
152 r.Vs, err = dc.Resource(utils.SnapshotGVR).Namespace(r.Vs.GetNamespace()).Create(ctx, r.Vs, metav1.CreateOptions{})
153 framework.ExpectNoError(err)
154
155 err = utils.WaitForSnapshotReady(ctx, dc, r.Vs.GetNamespace(), r.Vs.GetName(), framework.Poll, timeouts.SnapshotCreate)
156 framework.ExpectNoError(err)
157
158 ginkgo.By("getting the snapshot and snapshot content")
159 r.Vs, err = dc.Resource(utils.SnapshotGVR).Namespace(r.Vs.GetNamespace()).Get(ctx, r.Vs.GetName(), metav1.GetOptions{})
160 framework.ExpectNoError(err)
161
162 r.Vscontent, err = dc.Resource(utils.SnapshotContentGVR).Get(ctx, r.Vscontent.GetName(), metav1.GetOptions{})
163 framework.ExpectNoError(err)
164 }
165 return &r
166 }
167
168
169 func (sr *SnapshotResource) CleanupResource(ctx context.Context, timeouts *framework.TimeoutContext) error {
170 var err error
171 var cleanupErrs []error
172
173 dc := sr.Config.Framework.DynamicClient
174
175 if sr.Vs != nil {
176 framework.Logf("deleting snapshot %q/%q", sr.Vs.GetNamespace(), sr.Vs.GetName())
177
178 sr.Vs, err = dc.Resource(utils.SnapshotGVR).Namespace(sr.Vs.GetNamespace()).Get(ctx, sr.Vs.GetName(), metav1.GetOptions{})
179 switch {
180 case err == nil:
181 snapshotStatus := sr.Vs.Object["status"].(map[string]interface{})
182 snapshotContentName := snapshotStatus["boundVolumeSnapshotContentName"].(string)
183 framework.Logf("received snapshotStatus %v", snapshotStatus)
184 framework.Logf("snapshotContentName %s", snapshotContentName)
185
186 boundVsContent, err := dc.Resource(utils.SnapshotContentGVR).Get(ctx, snapshotContentName, metav1.GetOptions{})
187 switch {
188 case err == nil:
189 if boundVsContent.Object["spec"].(map[string]interface{})["deletionPolicy"] != "Delete" {
190
191
192
193 boundVsContent.Object["spec"].(map[string]interface{})["deletionPolicy"] = "Delete"
194 boundVsContent, err = dc.Resource(utils.SnapshotContentGVR).Update(ctx, boundVsContent, metav1.UpdateOptions{})
195 framework.ExpectNoError(err)
196 }
197 err = dc.Resource(utils.SnapshotGVR).Namespace(sr.Vs.GetNamespace()).Delete(ctx, sr.Vs.GetName(), metav1.DeleteOptions{})
198 if apierrors.IsNotFound(err) {
199 err = nil
200 }
201 framework.ExpectNoError(err)
202
203 err = utils.WaitForGVRDeletion(ctx, dc, utils.SnapshotContentGVR, boundVsContent.GetName(), framework.Poll, timeouts.SnapshotDelete)
204 framework.ExpectNoError(err)
205
206 case apierrors.IsNotFound(err):
207
208 err = dc.Resource(utils.SnapshotGVR).Namespace(sr.Vs.GetNamespace()).Delete(ctx, sr.Vs.GetName(), metav1.DeleteOptions{})
209 if apierrors.IsNotFound(err) {
210 err = nil
211 }
212 framework.ExpectNoError(err)
213
214 err = utils.WaitForNamespacedGVRDeletion(ctx, dc, utils.SnapshotGVR, sr.Vs.GetName(), sr.Vs.GetNamespace(), framework.Poll, timeouts.SnapshotDelete)
215 framework.ExpectNoError(err)
216 default:
217 cleanupErrs = append(cleanupErrs, err)
218 }
219 case apierrors.IsNotFound(err):
220
221 default:
222 cleanupErrs = append(cleanupErrs, err)
223 }
224 }
225 if sr.Vscontent != nil {
226 framework.Logf("deleting snapshot content %q", sr.Vscontent.GetName())
227
228 sr.Vscontent, err = dc.Resource(utils.SnapshotContentGVR).Get(ctx, sr.Vscontent.GetName(), metav1.GetOptions{})
229 switch {
230 case err == nil:
231 if sr.Vscontent.Object["spec"].(map[string]interface{})["deletionPolicy"] != "Delete" {
232
233
234
235 sr.Vscontent.Object["spec"].(map[string]interface{})["deletionPolicy"] = "Delete"
236 sr.Vscontent, err = dc.Resource(utils.SnapshotContentGVR).Update(ctx, sr.Vscontent, metav1.UpdateOptions{})
237 framework.ExpectNoError(err)
238 }
239 err = dc.Resource(utils.SnapshotContentGVR).Delete(ctx, sr.Vscontent.GetName(), metav1.DeleteOptions{})
240 if apierrors.IsNotFound(err) {
241 err = nil
242 }
243 framework.ExpectNoError(err)
244
245 err = utils.WaitForGVRDeletion(ctx, dc, utils.SnapshotContentGVR, sr.Vscontent.GetName(), framework.Poll, timeouts.SnapshotDelete)
246 framework.ExpectNoError(err)
247 case apierrors.IsNotFound(err):
248
249 default:
250 cleanupErrs = append(cleanupErrs, err)
251 }
252 }
253 if sr.Vsclass != nil {
254 framework.Logf("deleting snapshot class %q", sr.Vsclass.GetName())
255
256 err = dc.Resource(utils.SnapshotClassGVR).Delete(ctx, sr.Vsclass.GetName(), metav1.DeleteOptions{})
257 if err != nil && !apierrors.IsNotFound(err) {
258 framework.Failf("Error deleting snapshot class %q. Error: %v", sr.Vsclass.GetName(), err)
259 }
260 err = utils.WaitForGVRDeletion(ctx, dc, utils.SnapshotClassGVR, sr.Vsclass.GetName(), framework.Poll, timeouts.SnapshotDelete)
261 framework.ExpectNoError(err)
262 }
263 return utilerrors.NewAggregate(cleanupErrs)
264 }
265
266 func getSnapshot(claimName string, ns, snapshotClassName string) *unstructured.Unstructured {
267 snapshot := &unstructured.Unstructured{
268 Object: map[string]interface{}{
269 "kind": "VolumeSnapshot",
270 "apiVersion": utils.SnapshotAPIVersion,
271 "metadata": map[string]interface{}{
272 "generateName": "snapshot-",
273 "namespace": ns,
274 },
275 "spec": map[string]interface{}{
276 "volumeSnapshotClassName": snapshotClassName,
277 "source": map[string]interface{}{
278 "persistentVolumeClaimName": claimName,
279 },
280 },
281 },
282 }
283
284 return snapshot
285 }
286 func getPreProvisionedSnapshot(snapName, ns, snapshotContentName string) *unstructured.Unstructured {
287 snapshot := &unstructured.Unstructured{
288 Object: map[string]interface{}{
289 "kind": "VolumeSnapshot",
290 "apiVersion": utils.SnapshotAPIVersion,
291 "metadata": map[string]interface{}{
292 "name": snapName,
293 "namespace": ns,
294 },
295 "spec": map[string]interface{}{
296 "source": map[string]interface{}{
297 "volumeSnapshotContentName": snapshotContentName,
298 },
299 },
300 },
301 }
302
303 return snapshot
304 }
305 func getPreProvisionedSnapshotContent(snapcontentName string, snapshotContentAnnotations map[string]string, snapshotName, snapshotNamespace, snapshotHandle, deletionPolicy, csiDriverName string) *unstructured.Unstructured {
306 snapshotContent := &unstructured.Unstructured{
307 Object: map[string]interface{}{
308 "kind": "VolumeSnapshotContent",
309 "apiVersion": utils.SnapshotAPIVersion,
310 "metadata": map[string]interface{}{
311 "name": snapcontentName,
312 "annotations": snapshotContentAnnotations,
313 },
314 "spec": map[string]interface{}{
315 "source": map[string]interface{}{
316 "snapshotHandle": snapshotHandle,
317 },
318 "volumeSnapshotRef": map[string]interface{}{
319 "name": snapshotName,
320 "namespace": snapshotNamespace,
321 },
322 "driver": csiDriverName,
323 "deletionPolicy": deletionPolicy,
324 },
325 },
326 }
327
328 return snapshotContent
329 }
330
331 func getPreProvisionedSnapshotContentName(uuid types.UID) string {
332 return fmt.Sprintf("pre-provisioned-snapcontent-%s", string(uuid))
333 }
334
335 func getPreProvisionedSnapshotName(uuid types.UID) string {
336 return fmt.Sprintf("pre-provisioned-snapshot-%s", string(uuid))
337 }
338
View as plain text