Skip to content

Commit bb881e7

Browse files
committed
add support to delete asg with cleanup. Support to enable and disable asg
1 parent 09200bd commit bb881e7

File tree

2 files changed

+217
-57
lines changed

2 files changed

+217
-57
lines changed

cloudstack/resource_cloudstack_autoscale_policy.go

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package cloudstack
2222
import (
2323
"fmt"
2424
"log"
25+
"strings"
2526

2627
"github.com/apache/cloudstack-go/v2/cloudstack"
2728
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -43,8 +44,14 @@ func resourceCloudStackAutoScalePolicy() *schema.Resource {
4344
"action": {
4445
Type: schema.TypeString,
4546
Required: true,
46-
Description: "the action to be executed if all the conditions evaluate to true for the specified duration",
47+
Description: "the action to be executed if all the conditions evaluate to true for the specified duration (case insensitive: scaleup/SCALEUP or scaledown/SCALEDOWN)",
4748
ForceNew: true,
49+
StateFunc: func(val interface{}) string {
50+
return strings.ToUpper(val.(string))
51+
},
52+
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
53+
return strings.ToUpper(old) == strings.ToUpper(new)
54+
},
4855
},
4956
"duration": {
5057
Type: schema.TypeInt,
@@ -98,7 +105,19 @@ func resourceCloudStackAutoScalePolicyCreate(d *schema.ResourceData, meta interf
98105
d.SetId(resp.Id)
99106
log.Printf("[DEBUG] Autoscale policy created with ID: %s", resp.Id)
100107

101-
return resourceCloudStackAutoScalePolicyRead(d, meta)
108+
conditionSet := schema.NewSet(schema.HashString, []interface{}{})
109+
for _, id := range conditionIds {
110+
conditionSet.Add(id)
111+
}
112+
d.Set("condition_ids", conditionSet)
113+
d.Set("name", d.Get("name").(string))
114+
d.Set("action", action)
115+
d.Set("duration", duration)
116+
if v, ok := d.GetOk("quiet_time"); ok {
117+
d.Set("quiet_time", v.(int))
118+
}
119+
120+
return nil
102121
}
103122

104123
func resourceCloudStackAutoScalePolicyRead(d *schema.ResourceData, meta interface{}) error {
@@ -120,23 +139,24 @@ func resourceCloudStackAutoScalePolicyRead(d *schema.ResourceData, meta interfac
120139

121140
policy := resp.AutoScalePolicies[0]
122141
d.Set("name", policy.Name)
142+
// CloudStack always returns uppercase actions (SCALEUP/SCALEDOWN)
143+
// Our StateFunc normalizes user input to uppercase, so this should match
123144
d.Set("action", policy.Action)
124145
d.Set("duration", policy.Duration)
125146
d.Set("quiet_time", policy.Quiettime)
126147

127148
conditionIds := schema.NewSet(schema.HashString, []interface{}{})
128149
for _, condition := range policy.Conditions {
129-
var conditionInterface interface{} = condition
130-
switch v := conditionInterface.(type) {
131-
case string:
132-
conditionIds.Add(v)
133-
case map[string]interface{}:
134-
if id, ok := v["id"].(string); ok {
135-
conditionIds.Add(id)
136-
}
137-
default:
138-
log.Printf("[DEBUG] Unexpected condition type: %T, value: %+v", condition, condition)
139-
conditionIds.Add(fmt.Sprintf("%v", condition))
150+
// Try to extract the ID from the condition object
151+
if condition == nil {
152+
continue
153+
}
154+
155+
// The condition is a *cloudstack.Condition struct, so access the Id field directly
156+
if condition.Id != "" {
157+
conditionIds.Add(condition.Id)
158+
} else {
159+
log.Printf("[WARN] Condition has empty ID: %+v", condition)
140160
}
141161
}
142162
d.Set("condition_ids", conditionIds)

cloudstack/resource_cloudstack_autoscale_vm_group.go

Lines changed: 184 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"log"
2525
"strings"
26+
"time"
2627

2728
"github.com/apache/cloudstack-go/v2/cloudstack"
2829
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -93,6 +94,27 @@ func resourceCloudStackAutoScaleVMGroup() *schema.Resource {
9394
Optional: true,
9495
Description: "an optional field, whether to the display the group to the end user or not",
9596
},
97+
98+
"state": {
99+
Type: schema.TypeString,
100+
Optional: true,
101+
Default: "enable",
102+
Description: "the state of the autoscale vm group (enable or disable)",
103+
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
104+
value := v.(string)
105+
if value != "enable" && value != "disable" {
106+
errors = append(errors, fmt.Errorf("state must be either 'enable' or 'disable'"))
107+
}
108+
return
109+
},
110+
},
111+
112+
"cleanup": {
113+
Type: schema.TypeBool,
114+
Optional: true,
115+
Default: false,
116+
Description: "true if all the members of autoscale vm group has to be cleaned up, false otherwise",
117+
},
96118
},
97119
}
98120
}
@@ -144,6 +166,16 @@ func resourceCloudStackAutoScaleVMGroupCreate(d *schema.ResourceData, meta inter
144166
d.SetId(resp.Id)
145167
log.Printf("[DEBUG] Autoscale VM group created with ID: %s", resp.Id)
146168

169+
requestedState := d.Get("state").(string)
170+
if requestedState == "disable" {
171+
log.Printf("[DEBUG] Disabling autoscale VM group as requested: %s", resp.Id)
172+
disableParams := cs.AutoScale.NewDisableAutoScaleVmGroupParams(resp.Id)
173+
_, err = cs.AutoScale.DisableAutoScaleVmGroup(disableParams)
174+
if err != nil {
175+
return fmt.Errorf("Error disabling autoscale VM group after creation: %s", err)
176+
}
177+
}
178+
147179
if v, ok := d.GetOk("name"); ok {
148180
d.Set("name", v.(string))
149181
}
@@ -170,6 +202,8 @@ func resourceCloudStackAutoScaleVMGroupCreate(d *schema.ResourceData, meta inter
170202
}
171203
d.Set("scaledown_policy_ids", scaleDownSet)
172204

205+
d.Set("state", requestedState)
206+
173207
return nil
174208
}
175209

@@ -199,18 +233,35 @@ func resourceCloudStackAutoScaleVMGroupRead(d *schema.ResourceData, meta interfa
199233
d.Set("vm_profile_id", group.Vmprofileid)
200234
d.Set("display", group.Fordisplay)
201235

236+
terraformState := "enable"
237+
if strings.ToLower(group.State) == "disabled" {
238+
terraformState = "disable"
239+
}
240+
241+
currentConfigState := d.Get("state").(string)
242+
log.Printf("[DEBUG] CloudStack API state: %s, mapped to Terraform state: %s", group.State, terraformState)
243+
log.Printf("[DEBUG] Current config state: %s, will set state to: %s", currentConfigState, terraformState)
244+
245+
d.Set("state", terraformState)
246+
202247
scaleUpPolicyIds := schema.NewSet(schema.HashString, []interface{}{})
203248
if group.Scaleuppolicies != nil {
204-
for _, policyId := range group.Scaleuppolicies {
205-
scaleUpPolicyIds.Add(policyId)
249+
for _, policy := range group.Scaleuppolicies {
250+
// Extract the ID from the AutoScalePolicy object
251+
if policy != nil {
252+
scaleUpPolicyIds.Add(policy.Id)
253+
}
206254
}
207255
}
208256
d.Set("scaleup_policy_ids", scaleUpPolicyIds)
209257

210258
scaleDownPolicyIds := schema.NewSet(schema.HashString, []interface{}{})
211259
if group.Scaledownpolicies != nil {
212-
for _, policyId := range group.Scaledownpolicies {
213-
scaleDownPolicyIds.Add(policyId)
260+
for _, policy := range group.Scaledownpolicies {
261+
// Extract the ID from the AutoScalePolicy object
262+
if policy != nil {
263+
scaleDownPolicyIds.Add(policy.Id)
264+
}
214265
}
215266
}
216267
d.Set("scaledown_policy_ids", scaleDownPolicyIds)
@@ -221,67 +272,137 @@ func resourceCloudStackAutoScaleVMGroupRead(d *schema.ResourceData, meta interfa
221272
func resourceCloudStackAutoScaleVMGroupUpdate(d *schema.ResourceData, meta interface{}) error {
222273
cs := meta.(*cloudstack.CloudStackClient)
223274

275+
oldState, newState := d.GetChange("state")
276+
log.Printf("[DEBUG] State in terraform config: %s", d.Get("state").(string))
277+
log.Printf("[DEBUG] Old state: %s, New state: %s", oldState.(string), newState.(string))
278+
279+
changes := []string{}
280+
if d.HasChange("name") {
281+
changes = append(changes, "name")
282+
}
283+
if d.HasChange("min_members") {
284+
changes = append(changes, "min_members")
285+
}
286+
if d.HasChange("max_members") {
287+
changes = append(changes, "max_members")
288+
}
289+
if d.HasChange("interval") {
290+
changes = append(changes, "interval")
291+
}
292+
if d.HasChange("scaleup_policy_ids") {
293+
changes = append(changes, "scaleup_policy_ids")
294+
}
295+
if d.HasChange("scaledown_policy_ids") {
296+
changes = append(changes, "scaledown_policy_ids")
297+
}
298+
if d.HasChange("display") {
299+
changes = append(changes, "display")
300+
}
301+
if d.HasChange("state") {
302+
changes = append(changes, "state")
303+
}
304+
log.Printf("[DEBUG] Detected changes in autoscale VM group: %v", changes)
305+
224306
if d.HasChange("name") || d.HasChange("min_members") || d.HasChange("max_members") ||
225307
d.HasChange("interval") || d.HasChange("scaleup_policy_ids") ||
226-
d.HasChange("scaledown_policy_ids") || d.HasChange("display") {
308+
d.HasChange("scaledown_policy_ids") || d.HasChange("display") || d.HasChange("state") {
227309

228310
log.Printf("[DEBUG] Updating autoscale VM group: %s", d.Id())
229311

230-
p := cs.AutoScale.NewUpdateAutoScaleVmGroupParams(d.Id())
312+
// Check current state to determine operation order
313+
currentState := "enable"
314+
if oldState, newState := d.GetChange("state"); d.HasChange("state") {
315+
currentState = oldState.(string)
316+
log.Printf("[DEBUG] State change detected: %s -> %s", currentState, newState.(string))
317+
}
231318

232-
if d.HasChange("name") {
233-
if v, ok := d.GetOk("name"); ok {
234-
p.SetName(v.(string))
319+
if d.HasChange("state") {
320+
newState := d.Get("state").(string)
321+
if newState == "disable" && currentState == "enable" {
322+
log.Printf("[DEBUG] Disabling autoscale VM group before other updates: %s", d.Id())
323+
disableParams := cs.AutoScale.NewDisableAutoScaleVmGroupParams(d.Id())
324+
_, err := cs.AutoScale.DisableAutoScaleVmGroup(disableParams)
325+
if err != nil {
326+
return fmt.Errorf("Error disabling autoscale VM group: %s", err)
327+
}
328+
// Wait a moment for disable to take effect
329+
time.Sleep(1 * time.Second)
235330
}
236331
}
237332

238-
if d.HasChange("min_members") {
239-
minmembers := d.Get("min_members").(int)
240-
p.SetMinmembers(minmembers)
241-
}
333+
if d.HasChange("name") || d.HasChange("min_members") || d.HasChange("max_members") ||
334+
d.HasChange("interval") || d.HasChange("scaleup_policy_ids") ||
335+
d.HasChange("scaledown_policy_ids") || d.HasChange("display") {
242336

243-
if d.HasChange("max_members") {
244-
maxmembers := d.Get("max_members").(int)
245-
p.SetMaxmembers(maxmembers)
246-
}
337+
p := cs.AutoScale.NewUpdateAutoScaleVmGroupParams(d.Id())
247338

248-
if d.HasChange("interval") {
249-
if v, ok := d.GetOk("interval"); ok {
250-
p.SetInterval(v.(int))
339+
if d.HasChange("name") {
340+
if v, ok := d.GetOk("name"); ok {
341+
p.SetName(v.(string))
342+
}
251343
}
252-
}
253344

254-
if d.HasChange("scaleup_policy_ids") {
255-
scaleUpPolicyIds := []string{}
256-
if v, ok := d.GetOk("scaleup_policy_ids"); ok {
257-
scaleUpSet := v.(*schema.Set)
258-
for _, id := range scaleUpSet.List() {
259-
scaleUpPolicyIds = append(scaleUpPolicyIds, id.(string))
345+
if d.HasChange("min_members") {
346+
minmembers := d.Get("min_members").(int)
347+
p.SetMinmembers(minmembers)
348+
}
349+
350+
if d.HasChange("max_members") {
351+
maxmembers := d.Get("max_members").(int)
352+
p.SetMaxmembers(maxmembers)
353+
}
354+
355+
if d.HasChange("interval") {
356+
if v, ok := d.GetOk("interval"); ok {
357+
p.SetInterval(v.(int))
260358
}
261359
}
262-
p.SetScaleuppolicyids(scaleUpPolicyIds)
263-
}
264360

265-
if d.HasChange("scaledown_policy_ids") {
266-
scaleDownPolicyIds := []string{}
267-
if v, ok := d.GetOk("scaledown_policy_ids"); ok {
268-
scaleDownSet := v.(*schema.Set)
269-
for _, id := range scaleDownSet.List() {
270-
scaleDownPolicyIds = append(scaleDownPolicyIds, id.(string))
361+
if d.HasChange("scaleup_policy_ids") {
362+
scaleUpPolicyIds := []string{}
363+
if v, ok := d.GetOk("scaleup_policy_ids"); ok {
364+
scaleUpSet := v.(*schema.Set)
365+
for _, id := range scaleUpSet.List() {
366+
scaleUpPolicyIds = append(scaleUpPolicyIds, id.(string))
367+
}
271368
}
369+
p.SetScaleuppolicyids(scaleUpPolicyIds)
370+
}
371+
372+
if d.HasChange("scaledown_policy_ids") {
373+
scaleDownPolicyIds := []string{}
374+
if v, ok := d.GetOk("scaledown_policy_ids"); ok {
375+
scaleDownSet := v.(*schema.Set)
376+
for _, id := range scaleDownSet.List() {
377+
scaleDownPolicyIds = append(scaleDownPolicyIds, id.(string))
378+
}
379+
}
380+
p.SetScaledownpolicyids(scaleDownPolicyIds)
272381
}
273-
p.SetScaledownpolicyids(scaleDownPolicyIds)
274-
}
275382

276-
if d.HasChange("display") {
277-
if v, ok := d.GetOk("display"); ok {
278-
p.SetFordisplay(v.(bool))
383+
if d.HasChange("display") {
384+
if v, ok := d.GetOk("display"); ok {
385+
p.SetFordisplay(v.(bool))
386+
}
387+
}
388+
389+
log.Printf("[DEBUG] Applying parameter updates to autoscale VM group: %s", d.Id())
390+
_, err := cs.AutoScale.UpdateAutoScaleVmGroup(p)
391+
if err != nil {
392+
return fmt.Errorf("Error updating autoscale VM group parameters: %s", err)
279393
}
280394
}
281395

282-
_, err := cs.AutoScale.UpdateAutoScaleVmGroup(p)
283-
if err != nil {
284-
return fmt.Errorf("Error updating autoscale VM group: %s", err)
396+
if d.HasChange("state") {
397+
newState := d.Get("state").(string)
398+
if newState == "enable" {
399+
log.Printf("[DEBUG] Enabling autoscale VM group after updates: %s", d.Id())
400+
enableParams := cs.AutoScale.NewEnableAutoScaleVmGroupParams(d.Id())
401+
_, err := cs.AutoScale.EnableAutoScaleVmGroup(enableParams)
402+
if err != nil {
403+
return fmt.Errorf("Error enabling autoscale VM group: %s", err)
404+
}
405+
}
285406
}
286407

287408
log.Printf("[DEBUG] Autoscale VM group updated successfully: %s", d.Id())
@@ -295,8 +416,27 @@ func resourceCloudStackAutoScaleVMGroupDelete(d *schema.ResourceData, meta inter
295416

296417
p := cs.AutoScale.NewDeleteAutoScaleVmGroupParams(d.Id())
297418

419+
cleanup := d.Get("cleanup").(bool)
420+
p.SetCleanup(cleanup)
421+
log.Printf("[DEBUG] Deleting autoscale VM group with cleanup=%t", cleanup)
422+
log.Printf("[DEBUG] This deletion was triggered by Terraform - check if this should be an update instead")
423+
424+
log.Printf("[DEBUG] Disabling autoscale VM group before deletion: %s", d.Id())
425+
disableParams := cs.AutoScale.NewDisableAutoScaleVmGroupParams(d.Id())
426+
_, err := cs.AutoScale.DisableAutoScaleVmGroup(disableParams)
427+
if err != nil {
428+
if !strings.Contains(err.Error(), "Invalid parameter id value") &&
429+
!strings.Contains(err.Error(), "entity does not exist") &&
430+
!strings.Contains(err.Error(), "already disabled") {
431+
return fmt.Errorf("Error disabling autoscale VM group: %s", err)
432+
}
433+
}
434+
435+
time.Sleep(2 * time.Second)
436+
log.Printf("[DEBUG] Autoscale VM group disabled, proceeding with deletion: %s", d.Id())
437+
298438
log.Printf("[DEBUG] Deleting autoscale VM group: %s", d.Id())
299-
_, err := cs.AutoScale.DeleteAutoScaleVmGroup(p)
439+
_, err = cs.AutoScale.DeleteAutoScaleVmGroup(p)
300440
if err != nil {
301441
// This is a very poor way to be told the ID does no longer exist :(
302442
if strings.Contains(err.Error(), fmt.Sprintf(

0 commit comments

Comments
 (0)