@@ -214,7 +214,8 @@ func (c *client) GetOrCreateVMInstance(
214214 csCluster * infrav1.CloudStackCluster ,
215215 fd * infrav1.CloudStackFailureDomain ,
216216 affinity * infrav1.CloudStackAffinityGroup ,
217- userData string ) error {
217+ userData string ,
218+ ) error {
218219
219220 // Check if VM instance already exists.
220221 if err := c .ResolveVMInstanceDetails (csMachine ); err == nil ||
@@ -245,19 +246,19 @@ func (c *client) GetOrCreateVMInstance(
245246
246247 setIfNotEmpty (csMachine .Spec .SSHKey , p .SetKeypair )
247248
248- userData , err = handleUserData (userData , csMachine .Spec .UncompressedUserData )
249- if err != nil {
250- return err
249+ if csMachine .CompressUserdata () {
250+ userData , err = compress (userData )
251+ if err != nil {
252+ return err
253+ }
251254 }
255+ userData = base64 .StdEncoding .EncodeToString ([]byte (userData ))
252256 setIfNotEmpty (userData , p .SetUserdata )
253257
254258 if len (csMachine .Spec .AffinityGroupIDs ) > 0 {
255259 p .SetAffinitygroupids (csMachine .Spec .AffinityGroupIDs )
256260 } else if strings .ToLower (csMachine .Spec .Affinity ) != "no" && csMachine .Spec .Affinity != "" {
257261 p .SetAffinitygroupids ([]string {affinity .Spec .ID })
258- if err != nil {
259- return err
260- }
261262 }
262263
263264 if csMachine .Spec .Details != nil {
@@ -268,21 +269,22 @@ func (c *client) GetOrCreateVMInstance(
268269 if err != nil {
269270 c .customMetrics .EvaluateErrorAndIncrementAcsReconciliationErrorCounter (err )
270271
271- // Just because an error was returned doesn't mean a (failed) VM wasn't created and will need to be dealt with.
272- // Regretfully the deployVMResp may be nil, so we need to get the VM ID with a separate query, so we
273- // can return it to the caller, so they can clean it up.
274- listVirtualMachineParams := c .cs .VirtualMachine . NewListVirtualMachinesParams ( )
275- listVirtualMachineParams . SetTemplateid ( templateID )
276- listVirtualMachineParams . SetZoneid ( fd . Spec . Zone . ID )
277- listVirtualMachineParams . SetNetworkid ( fd . Spec . Zone . Network . ID )
278- listVirtualMachineParams . SetName ( csMachine . Name )
279- listVirtualMachinesResponse , err2 := c . cs . VirtualMachine . ListVirtualMachines ( listVirtualMachineParams )
280- if err2 != nil || listVirtualMachinesResponse . Count <= 0 {
281- c . customMetrics . EvaluateErrorAndIncrementAcsReconciliationErrorCounter ( err2 )
272+ // CloudStack may have created the VM even though it reported an error. We attempt to
273+ // retrieve the VM so we can populate the CloudStackMachine for the user to manually
274+ // clean up.
275+ vm , findErr := findVirtualMachine ( c .cs .VirtualMachine , templateID , fd , csMachine )
276+ if findErr != nil {
277+ c . customMetrics . EvaluateErrorAndIncrementAcsReconciliationErrorCounter ( findErr )
278+ return fmt . Errorf ( "%v; find virtual machine: %v" , err , findErr )
279+ }
280+
281+ // We didn't find a VM so return the original error.
282+ if vm == nil {
282283 return err
283284 }
284- csMachine .Spec .InstanceID = pointer .String (listVirtualMachinesResponse .VirtualMachines [0 ].Id )
285- csMachine .Status .InstanceState = listVirtualMachinesResponse .VirtualMachines [0 ].State
285+
286+ csMachine .Spec .InstanceID = pointer .String (vm .Id )
287+ csMachine .Status .InstanceState = vm .State
286288 } else {
287289 csMachine .Spec .InstanceID = pointer .String (deployVMResp .Id )
288290 csMachine .Status .Status = pointer .String (metav1 .StatusSuccess )
@@ -292,6 +294,32 @@ func (c *client) GetOrCreateVMInstance(
292294 return c .ResolveVMInstanceDetails (csMachine )
293295}
294296
297+ // findVirtualMachine retrieves a virtual machine by matching its expected name, template, failure
298+ // domain zone and failure domain network. If no virtual machine is found it returns nil, nil.
299+ func findVirtualMachine (
300+ client cloudstack.VirtualMachineServiceIface ,
301+ templateID string ,
302+ failureDomain * infrav1.CloudStackFailureDomain ,
303+ machine * infrav1.CloudStackMachine ,
304+ ) (* cloudstack.VirtualMachine , error ) {
305+ params := client .NewListVirtualMachinesParams ()
306+ params .SetTemplateid (templateID )
307+ params .SetZoneid (failureDomain .Spec .Zone .ID )
308+ params .SetNetworkid (failureDomain .Spec .Zone .Network .ID )
309+ params .SetName (machine .Name )
310+
311+ response , err := client .ListVirtualMachines (params )
312+ if err != nil {
313+ return nil , err
314+ }
315+
316+ if response .Count == 0 {
317+ return nil , nil
318+ }
319+
320+ return response .VirtualMachines [0 ], nil
321+ }
322+
295323// DestroyVMInstance Destroys a VM instance. Assumes machine has been fetched prior and has an instance ID.
296324func (c * client ) DestroyVMInstance (csMachine * infrav1.CloudStackMachine ) error {
297325 // Attempt deletion regardless of machine state.
@@ -346,15 +374,3 @@ func (c *client) listVMInstanceDatadiskVolumeIDs(instanceID string) ([]string, e
346374 return ret , nil
347375
348376}
349-
350- // handleUserData optionally compresses and then base64 encodes userdata
351- func handleUserData (userData string , uncompressed * bool ) (string , error ) {
352- var err error
353- if uncompressed != nil && ! * uncompressed {
354- userData , err = CompressString (userData )
355- if err != nil {
356- return "" , err
357- }
358- }
359- return base64 .StdEncoding .EncodeToString ([]byte (userData )), nil
360- }
0 commit comments