Skip to content

Commit ba4c79c

Browse files
Bug 1943378: Fix InstanceCreate volume cleanup
Using the deferred function, this solution triggers the cleanup of the unused root volume in all failure cases of InstanceCreate.
1 parent 0ff90e6 commit ba4c79c

File tree

1 file changed

+22
-20
lines changed

1 file changed

+22
-20
lines changed

pkg/cloud/openstack/clients/machineservice.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ func GetSecurityGroups(is *InstanceService, sg_param []openstackconfigv1.Securit
462462
// If ServerGroupName is nonempty and no server group exists with that name,
463463
// then InstanceCreate creates a server group with that name.
464464
func (is *InstanceService) InstanceCreate(clusterName string, name string, clusterSpec *openstackconfigv1.OpenstackClusterProviderSpec, config *openstackconfigv1.OpenstackProviderSpec, cmd string, keyName string, configClient configclient.ConfigV1Interface) (instance *Instance, err error) {
465+
// This variable is used in early returns to trigger the deletion of
466+
// associated resources in case of failure to create the server.
467+
needsCleanup := true
468+
465469
if config == nil {
466470
return nil, fmt.Errorf("create Options need be specified to create instace")
467471
}
@@ -475,8 +479,6 @@ func (is *InstanceService) InstanceCreate(clusterName string, name string, clust
475479
}
476480
}
477481

478-
var cleanupOperationsInCaseOfServerCreationFailure []func() error
479-
480482
// Set default Tags
481483
machineTags := []string{
482484
"cluster-api-provider-openstack",
@@ -713,7 +715,7 @@ func (is *InstanceService) InstanceCreate(clusterName string, name string, clust
713715
volumeName := name
714716

715717
// if source type is "image" then we have to create a volume from the image first
716-
klog.Infof("Creating bootable volume with name %q from image %v.", volumeName, config.RootVolume.SourceUUID)
718+
klog.Infof("Creating bootable volume with name %q from image %q.", volumeName, config.RootVolume.SourceUUID)
717719

718720
// Deleting any volumes with the same name, as they may
719721
// be leftovers from a previous failed try.
@@ -750,21 +752,23 @@ func (is *InstanceService) InstanceCreate(clusterName string, name string, clust
750752
if err != nil {
751753
return nil, fmt.Errorf("Create bootable volume err: %v", err)
752754
}
753-
754-
cleanupOperationsInCaseOfServerCreationFailure = append(cleanupOperationsInCaseOfServerCreationFailure, func() error {
755-
return volumes.Delete(is.volumeClient, volume.ID, nil).ExtractErr()
756-
})
757755
volumeID = volume.ID
758756

759-
err = volumes.WaitForStatus(is.volumeClient, volumeID, "available", 300)
760-
if err != nil {
761-
klog.Infof("Bootable volume %v creation failed. Removing...", volumeID)
762-
err = volumes.Delete(is.volumeClient, volumeID, volumes.DeleteOpts{}).ExtractErr()
763-
if err != nil {
764-
return nil, fmt.Errorf("Bootable volume deletion err: %v", err)
757+
defer func() {
758+
// If the server is created in Nova, the lifetime of the associated volume will be
759+
// bound to it. This deferred cleanup function tackles the case where a volume was created
760+
// but never attached to a server. In that case, the volume must be removed manually.
761+
if needsCleanup == true {
762+
if err := volumes.Delete(is.volumeClient, volumeID, nil).ExtractErr(); err != nil {
763+
klog.Infof("Failed to delete stale volume %q", volumeID)
764+
} else {
765+
klog.Infof("Deleted stale volume %q", volumeID)
766+
}
765767
}
768+
}()
766769

767-
return nil, fmt.Errorf("Bootable volume %v is not available err: %v", volumeID, err)
770+
if err := volumes.WaitForStatus(is.volumeClient, volumeID, "available", 300); err != nil {
771+
return nil, fmt.Errorf("bootable volume is not available: %v", err)
768772
}
769773

770774
klog.Infof("Bootable volume %v was created successfully.", volumeID)
@@ -839,19 +843,17 @@ func (is *InstanceService) InstanceCreate(clusterName string, name string, clust
839843
}
840844
}
841845

842-
server, err := servers.Create(is.computeClient, keypairs.CreateOptsExt{
846+
server, err = servers.Create(is.computeClient, keypairs.CreateOptsExt{
843847
CreateOptsBuilder: serverCreateOpts,
844848
KeyName: keyName,
845849
}).Extract()
846850
if err != nil {
847-
for _, cleanup := range cleanupOperationsInCaseOfServerCreationFailure {
848-
if e := cleanup(); e != nil {
849-
err = fmt.Errorf("%w. Additionally: %v", err, e)
850-
}
851-
}
852851
return nil, err
853852
}
854853

854+
// Disabling cleanup functions because server creation was successful.
855+
needsCleanup = false
856+
855857
is.computeClient.Microversion = ""
856858
return serverToInstance(server), nil
857859
}

0 commit comments

Comments
 (0)