@@ -25,11 +25,13 @@ import (
25
25
"strings"
26
26
27
27
"github.com/pkg/errors"
28
+ cnstypes "github.com/vmware/govmomi/cns/types"
28
29
"github.com/vmware/govmomi/object"
29
30
govmomicluster "github.com/vmware/govmomi/vapi/cluster"
30
31
"github.com/vmware/govmomi/vim25/mo"
31
32
"github.com/vmware/govmomi/vim25/types"
32
33
kerrors "k8s.io/apimachinery/pkg/util/errors"
34
+ "k8s.io/klog/v2"
33
35
ctrl "sigs.k8s.io/controller-runtime"
34
36
)
35
37
@@ -52,8 +54,12 @@ type virtualMachine struct {
52
54
object * object.VirtualMachine
53
55
}
54
56
57
+ // boskosResourceLabel is used to identify volumes created in e2e tests.
58
+ // The value should contain the boskos resource name.
59
+ const boskosResourceLabel = "capv-e2e-test-boskos-resource"
60
+
55
61
// CleanupVSphere cleans up vSphere VMs, folders and resource pools.
56
- func (s * Janitor ) CleanupVSphere (ctx context.Context , folders , resourcePools , vmFolders []string , skipClusterModule bool ) error {
62
+ func (s * Janitor ) CleanupVSphere (ctx context.Context , folders , resourcePools , vmFolders []string , boskosResourceName string , skipClusterModule bool ) error {
57
63
errList := []error {}
58
64
59
65
// Delete vms to cleanup folders and resource pools.
@@ -86,6 +92,11 @@ func (s *Janitor) CleanupVSphere(ctx context.Context, folders, resourcePools, vm
86
92
return errors .Wrap (err , "cleaning up folders" )
87
93
}
88
94
95
+ // Delete CNS volumes.
96
+ if err := s .DeleteCNSVolumes (ctx , boskosResourceName ); err != nil {
97
+ return errors .Wrap (err , "cleaning up volumes" )
98
+ }
99
+
89
100
if skipClusterModule {
90
101
return nil
91
102
}
@@ -197,6 +208,118 @@ func (s *Janitor) deleteVSphereVMs(ctx context.Context, folder string) error {
197
208
return nil
198
209
}
199
210
211
+ // DeleteCNSVolumes deletes all volumes from tests.
212
+ func (s * Janitor ) DeleteCNSVolumes (ctx context.Context , boskosResourceName string ) error {
213
+ log := ctrl .LoggerFrom (ctx ).WithName ("volumes" )
214
+ ctx = ctrl .LoggerInto (ctx , log )
215
+
216
+ log .Info ("Deleting volumes" )
217
+
218
+ type cnsVolumeToDelete struct {
219
+ volumeID cnstypes.CnsVolumeId
220
+ pvcName string
221
+ pvcNamespace string
222
+ }
223
+ volumesToDelete := []cnsVolumeToDelete {}
224
+
225
+ queryFilter := cnstypes.CnsQueryFilter {
226
+ Labels : []types.KeyValue {
227
+ {
228
+ Key : boskosResourceLabel ,
229
+ Value : boskosResourceName ,
230
+ },
231
+ },
232
+ }
233
+
234
+ for {
235
+ res , err := s .vSphereClients .CNS .QueryVolume (ctx , queryFilter )
236
+ if err != nil {
237
+ return err
238
+ }
239
+
240
+ for _ , volume := range res .Volumes {
241
+ var pvcMetadata * cnstypes.CnsKubernetesEntityMetadata
242
+ for _ , meta := range volume .Metadata .EntityMetadata {
243
+ k8sMetadata , ok := meta .(* cnstypes.CnsKubernetesEntityMetadata )
244
+ if ! ok {
245
+ continue
246
+ }
247
+ if k8sMetadata .EntityType != string (cnstypes .CnsKubernetesEntityTypePVC ) {
248
+ continue
249
+ }
250
+ pvcMetadata = k8sMetadata
251
+ }
252
+
253
+ if pvcMetadata == nil {
254
+ // Ignoring non-PVC volumes.
255
+ continue
256
+ }
257
+
258
+ var matchesBoskosResourcename bool
259
+ // Check again that the volume has a matching label.
260
+ for _ , v := range pvcMetadata .Labels {
261
+ if v .Key != boskosResourceLabel {
262
+ continue
263
+ }
264
+ if v .Value != boskosResourceName {
265
+ continue
266
+ }
267
+ matchesBoskosResourcename = true
268
+ }
269
+
270
+ // Ignore not matching volume.
271
+ if ! matchesBoskosResourcename {
272
+ continue
273
+ }
274
+
275
+ volumesToDelete = append (volumesToDelete , cnsVolumeToDelete {
276
+ volumeID : volume .VolumeId ,
277
+ pvcName : pvcMetadata .EntityName ,
278
+ pvcNamespace : pvcMetadata .Namespace ,
279
+ })
280
+ }
281
+
282
+ if res .Cursor .Offset == res .Cursor .TotalRecords || len (res .Volumes ) == 0 {
283
+ break
284
+ }
285
+
286
+ queryFilter .Cursor = & res .Cursor
287
+ }
288
+
289
+ if len (volumesToDelete ) == 0 {
290
+ log .Info ("No CNS Volumes to delete" )
291
+ return nil
292
+ }
293
+
294
+ deleteTasks := []* object.Task {}
295
+ for _ , volume := range volumesToDelete {
296
+ log := log .WithValues ("volumeID" , volume .volumeID , "PersistentVolumeClaim" , klog .KRef (volume .pvcNamespace , volume .pvcName ))
297
+
298
+ log .Info ("Deleting CNS Volume in vSphere" )
299
+
300
+ if s .dryRun {
301
+ // Skipping actual delete on dryRun.
302
+ continue
303
+ }
304
+
305
+ // Trigger deletion of the CNS Volume
306
+ task , err := s .vSphereClients .CNS .DeleteVolume (ctx , []cnstypes.CnsVolumeId {volume .volumeID }, true )
307
+ if err != nil {
308
+ return errors .Wrap (err , "failed to create CNS Volume deletion task" )
309
+ }
310
+
311
+ log .Info ("Created CNS Volume deletion task" , "task" , task .Reference ().Value )
312
+ deleteTasks = append (deleteTasks , task )
313
+ }
314
+
315
+ // Wait for all delete tasks to succeed.
316
+ if err := waitForTasksFinished (ctx , deleteTasks , false ); err != nil {
317
+ return errors .Wrap (err , "failed to wait for CNS Volume deletion tasks to finish" )
318
+ }
319
+
320
+ return nil
321
+ }
322
+
200
323
// deleteObjectChildren deletes all child objects in a given object in vSphere if they don't
201
324
// contain any virtual machine.
202
325
// An object only gets deleted if:
0 commit comments