@@ -25,11 +25,13 @@ import (
2525 "strings"
2626
2727 "github.com/pkg/errors"
28+ cnstypes "github.com/vmware/govmomi/cns/types"
2829 "github.com/vmware/govmomi/object"
2930 govmomicluster "github.com/vmware/govmomi/vapi/cluster"
3031 "github.com/vmware/govmomi/vim25/mo"
3132 "github.com/vmware/govmomi/vim25/types"
3233 kerrors "k8s.io/apimachinery/pkg/util/errors"
34+ "k8s.io/klog/v2"
3335 ctrl "sigs.k8s.io/controller-runtime"
3436)
3537
@@ -52,8 +54,12 @@ type virtualMachine struct {
5254 object * object.VirtualMachine
5355}
5456
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+
5561// 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 {
5763 errList := []error {}
5864
5965 // Delete vms to cleanup folders and resource pools.
@@ -86,6 +92,11 @@ func (s *Janitor) CleanupVSphere(ctx context.Context, folders, resourcePools, vm
8692 return errors .Wrap (err , "cleaning up folders" )
8793 }
8894
95+ // Delete CNS volumes.
96+ if err := s .DeleteCNSVolumes (ctx , boskosResourceName ); err != nil {
97+ return errors .Wrap (err , "cleaning up volumes" )
98+ }
99+
89100 if skipClusterModule {
90101 return nil
91102 }
@@ -197,6 +208,118 @@ func (s *Janitor) deleteVSphereVMs(ctx context.Context, folder string) error {
197208 return nil
198209}
199210
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+
200323// deleteObjectChildren deletes all child objects in a given object in vSphere if they don't
201324// contain any virtual machine.
202325// An object only gets deleted if:
0 commit comments