Skip to content

Commit 82d5c91

Browse files
committed
wait for asg to move to disabled state before making modifications to lb rule associated to an asg or autoscale vm profile
1 parent 6e2d1d3 commit 82d5c91

File tree

2 files changed

+332
-76
lines changed

2 files changed

+332
-76
lines changed

cloudstack/resource_cloudstack_autoscale_vm_profile.go

Lines changed: 156 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,9 @@ func resourceCloudStackAutoScaleVMProfileRead(d *schema.ResourceData, meta inter
278278
}
279279

280280
if p.Userdatadetails != "" {
281-
d.Set("user_data_details", map[string]interface{}{})
281+
if _, ok := d.GetOk("user_data_details"); !ok {
282+
d.Set("user_data_details", map[string]interface{}{})
283+
}
282284
}
283285

284286
if p.Autoscaleuserid != "" {
@@ -308,85 +310,185 @@ func resourceCloudStackAutoScaleVMProfileRead(d *schema.ResourceData, meta inter
308310
return nil
309311
}
310312

311-
func resourceCloudStackAutoScaleVMProfileUpdate(d *schema.ResourceData, meta interface{}) error {
312-
cs := meta.(*cloudstack.CloudStackClient)
313+
// waitForVMGroupsState waits for the specified VM groups to reach the desired state
314+
func waitForVMGroupsState(cs *cloudstack.CloudStackClient, groupIDs []string, desiredState string) error {
315+
maxRetries := 30 // 30 * 2 seconds = 60 seconds max wait
316+
for i := 0; i < maxRetries; i++ {
317+
allInDesiredState := true
318+
319+
for _, groupID := range groupIDs {
320+
group, _, err := cs.AutoScale.GetAutoScaleVmGroupByID(groupID)
321+
if err != nil {
322+
return fmt.Errorf("Error checking state of VM group %s: %s", groupID, err)
323+
}
324+
325+
groupInDesiredState := false
326+
if desiredState == "disabled" {
327+
groupInDesiredState = (group.State == "disabled")
328+
} else if desiredState == "enabled" {
329+
groupInDesiredState = (group.State == "enabled")
330+
} else {
331+
groupInDesiredState = (group.State == desiredState)
332+
}
313333

314-
p := cs.AutoScale.NewUpdateAutoScaleVmProfileParams(d.Id())
334+
if !groupInDesiredState {
335+
allInDesiredState = false
336+
log.Printf("[DEBUG] VM group %s is in state '%s', waiting for '%s'", groupID, group.State, desiredState)
337+
break
338+
}
339+
}
315340

316-
if d.HasChange("template") {
317-
zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
318-
if e != nil {
319-
return e.Error()
341+
if allInDesiredState {
342+
log.Printf("[INFO] All VM groups have reached desired state: %s", desiredState)
343+
return nil
320344
}
321-
templateid, e := retrieveTemplateID(cs, zoneid, d.Get("template").(string))
322-
if e != nil {
323-
return e.Error()
345+
346+
if i < maxRetries-1 {
347+
log.Printf("[INFO] Waiting for VM groups to reach state '%s' (attempt %d/%d)", desiredState, i+1, maxRetries)
348+
time.Sleep(2 * time.Second)
324349
}
325-
p.SetTemplateid(templateid)
326350
}
327351

328-
if d.HasChange("destroy_vm_grace_period") {
329-
if v, ok := d.GetOk("destroy_vm_grace_period"); ok {
330-
duration, err := time.ParseDuration(v.(string))
331-
if err != nil {
332-
return err
333-
}
334-
p.SetExpungevmgraceperiod(int(duration.Seconds()))
352+
return fmt.Errorf("Timeout waiting for VM groups to reach state '%s' after %d seconds", desiredState, maxRetries*2)
353+
}
354+
355+
func waitForVMGroupsToBeDisabled(cs *cloudstack.CloudStackClient, profileID string) error {
356+
log.Printf("[DEBUG] Waiting for VM groups using profile %s to be disabled", profileID)
357+
listParams := cs.AutoScale.NewListAutoScaleVmGroupsParams()
358+
listParams.SetVmprofileid(profileID)
359+
360+
groups, err := cs.AutoScale.ListAutoScaleVmGroups(listParams)
361+
if err != nil {
362+
log.Printf("[ERROR] Failed to list VM groups for profile %s: %s", profileID, err)
363+
return fmt.Errorf("Error listing autoscale VM groups: %s", err)
364+
}
365+
366+
log.Printf("[DEBUG] Found %d VM groups using profile %s", len(groups.AutoScaleVmGroups), profileID)
367+
368+
var groupIDs []string
369+
for _, group := range groups.AutoScaleVmGroups {
370+
log.Printf("[DEBUG] VM group %s (%s) current state: %s", group.Name, group.Id, group.State)
371+
groupIDs = append(groupIDs, group.Id)
372+
}
373+
374+
if len(groupIDs) == 0 {
375+
log.Printf("[DEBUG] No VM groups found using profile %s", profileID)
376+
return nil
377+
}
378+
379+
log.Printf("[INFO] Waiting for %d VM groups to be disabled for profile update", len(groupIDs))
380+
if err := waitForVMGroupsState(cs, groupIDs, "disabled"); err != nil {
381+
return fmt.Errorf("Autoscale VM groups must be disabled before updating profile: %s", err)
382+
}
383+
384+
log.Printf("[DEBUG] All VM groups are now disabled for profile %s", profileID)
385+
return nil
386+
}
387+
388+
func resourceCloudStackAutoScaleVMProfileUpdate(d *schema.ResourceData, meta interface{}) error {
389+
cs := meta.(*cloudstack.CloudStackClient)
390+
log.Printf("[DEBUG] Profile update requested for ID: %s", d.Id())
391+
for _, key := range []string{"template", "destroy_vm_grace_period", "counter_param_list", "user_data", "user_data_id", "user_data_details", "autoscale_user_id", "display", "metadata"} {
392+
if d.HasChange(key) {
393+
old, new := d.GetChange(key)
394+
log.Printf("[DEBUG] Field '%s' changed from %v to %v", key, old, new)
335395
}
336396
}
337397

338-
if d.HasChange("counter_param_list") {
339-
if v, ok := d.GetOk("counter_param_list"); ok {
340-
nv := make(map[string]string)
341-
for k, v := range v.(map[string]interface{}) {
342-
nv[k] = v.(string)
398+
// Check if we only have metadata changes (which don't require CloudStack API update)
399+
onlyMetadataChanges := d.HasChange("metadata") &&
400+
!d.HasChange("template") &&
401+
!d.HasChange("destroy_vm_grace_period") &&
402+
!d.HasChange("counter_param_list") &&
403+
!d.HasChange("user_data") &&
404+
!d.HasChange("user_data_id") &&
405+
!d.HasChange("user_data_details") &&
406+
!d.HasChange("autoscale_user_id") &&
407+
!d.HasChange("display")
408+
409+
if !onlyMetadataChanges {
410+
if err := waitForVMGroupsToBeDisabled(cs, d.Id()); err != nil {
411+
return fmt.Errorf("Autoscale VM groups must be disabled before updating profile: %s", err)
412+
}
413+
414+
p := cs.AutoScale.NewUpdateAutoScaleVmProfileParams(d.Id())
415+
416+
if d.HasChange("template") {
417+
zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
418+
if e != nil {
419+
return e.Error()
420+
}
421+
templateid, e := retrieveTemplateID(cs, zoneid, d.Get("template").(string))
422+
if e != nil {
423+
return e.Error()
343424
}
344-
p.SetCounterparam(nv)
425+
p.SetTemplateid(templateid)
345426
}
346-
}
347427

348-
if d.HasChange("user_data") {
349-
if v, ok := d.GetOk("user_data"); ok {
350-
p.SetUserdata(v.(string))
428+
if d.HasChange("destroy_vm_grace_period") {
429+
if v, ok := d.GetOk("destroy_vm_grace_period"); ok {
430+
duration, err := time.ParseDuration(v.(string))
431+
if err != nil {
432+
return err
433+
}
434+
p.SetExpungevmgraceperiod(int(duration.Seconds()))
435+
}
351436
}
352-
}
353437

354-
if d.HasChange("user_data_id") {
355-
if v, ok := d.GetOk("user_data_id"); ok {
356-
p.SetUserdataid(v.(string))
438+
if d.HasChange("counter_param_list") {
439+
if v, ok := d.GetOk("counter_param_list"); ok {
440+
nv := make(map[string]string)
441+
for k, v := range v.(map[string]interface{}) {
442+
nv[k] = v.(string)
443+
}
444+
p.SetCounterparam(nv)
445+
}
357446
}
358-
}
359447

360-
if d.HasChange("user_data_details") {
361-
if v, ok := d.GetOk("user_data_details"); ok {
362-
nv := make(map[string]string)
363-
for k, v := range v.(map[string]interface{}) {
364-
nv[k] = v.(string)
448+
if d.HasChange("user_data") {
449+
if v, ok := d.GetOk("user_data"); ok {
450+
p.SetUserdata(v.(string))
365451
}
366-
p.SetUserdatadetails(nv)
367452
}
368-
}
369453

370-
if d.HasChange("autoscale_user_id") {
371-
if v, ok := d.GetOk("autoscale_user_id"); ok {
372-
p.SetAutoscaleuserid(v.(string))
454+
if d.HasChange("user_data_id") {
455+
if v, ok := d.GetOk("user_data_id"); ok {
456+
p.SetUserdataid(v.(string))
457+
}
373458
}
374-
}
375459

376-
if d.HasChange("display") {
377-
if v, ok := d.GetOk("display"); ok {
378-
p.SetFordisplay(v.(bool))
460+
if d.HasChange("user_data_details") {
461+
if v, ok := d.GetOk("user_data_details"); ok {
462+
nv := make(map[string]string)
463+
for k, v := range v.(map[string]interface{}) {
464+
nv[k] = v.(string)
465+
}
466+
p.SetUserdatadetails(nv)
467+
}
379468
}
380-
}
381469

382-
_, err := cs.AutoScale.UpdateAutoScaleVmProfile(p)
383-
if err != nil {
384-
return fmt.Errorf("Error updating AutoScaleVmProfile %s: %s", d.Id(), err)
470+
if d.HasChange("autoscale_user_id") {
471+
if v, ok := d.GetOk("autoscale_user_id"); ok {
472+
p.SetAutoscaleuserid(v.(string))
473+
}
474+
}
475+
476+
if d.HasChange("display") {
477+
if v, ok := d.GetOk("display"); ok {
478+
p.SetFordisplay(v.(bool))
479+
}
480+
}
481+
482+
log.Printf("[DEBUG] Performing CloudStack API update for profile %s", d.Id())
483+
_, updateErr := cs.AutoScale.UpdateAutoScaleVmProfile(p)
484+
if updateErr != nil {
485+
return fmt.Errorf("Error updating AutoScaleVmProfile %s: %s", d.Id(), updateErr)
486+
}
385487
}
386488

387489
if d.HasChange("metadata") {
388-
if err := updateMetadata(cs, d, "AutoScaleVmProfile"); err != nil {
389-
return fmt.Errorf("Error updating tags on AutoScaleVmProfile %s: %s", d.Id(), err)
490+
if metadataErr := updateMetadata(cs, d, "AutoScaleVmProfile"); metadataErr != nil {
491+
return fmt.Errorf("Error updating tags on AutoScaleVmProfile %s: %s", d.Id(), metadataErr)
390492
}
391493
}
392494

0 commit comments

Comments
 (0)