From 9e58b1e813ec02574a79cea60f4b6c6414436dc8 Mon Sep 17 00:00:00 2001 From: Aditya Saha Date: Wed, 27 Nov 2024 15:23:58 -0500 Subject: [PATCH 1/9] Scaffold datasource implementation --- .../datasource_droplet_autoscale.go | 476 ++++++++++++++++++ .../resource_droplet_autoscale.go | 1 + digitalocean/provider.go | 2 + 3 files changed, 479 insertions(+) create mode 100644 digitalocean/dropletautoscale/datasource_droplet_autoscale.go create mode 100644 digitalocean/dropletautoscale/resource_droplet_autoscale.go diff --git a/digitalocean/dropletautoscale/datasource_droplet_autoscale.go b/digitalocean/dropletautoscale/datasource_droplet_autoscale.go new file mode 100644 index 000000000..5add25596 --- /dev/null +++ b/digitalocean/dropletautoscale/datasource_droplet_autoscale.go @@ -0,0 +1,476 @@ +package dropletautoscale + +import ( + "context" + + "github.com/digitalocean/godo" + "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/config" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func DataSourceDigitalOceanDropletAutoscale() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceDigitalOceanDropletAutoscaleRead, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Optional: true, + Description: "ID of the Droplet autoscale pool", + ValidateFunc: validation.NoZeroValues, + ExactlyOneOf: []string{"id", "name"}, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Description: "Name of the Droplet autoscale pool", + ValidateFunc: validation.NoZeroValues, + ExactlyOneOf: []string{"id", "name"}, + }, + "list_member_opts": { + Type: schema.TypeList, + Optional: true, + Description: "Pagination options for listing Droplet autoscale pool members", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Description: "Page offset", + }, + "per_page": { + Type: schema.TypeInt, + Description: "Per-page count", + }, + }, + }, + }, + "list_history_opts": { + Type: schema.TypeList, + Optional: true, + Description: "Pagination options for listing Droplet autoscale pool history events", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Description: "Page offset", + }, + "per_page": { + Type: schema.TypeInt, + Description: "Per-page count", + }, + }, + }, + }, + "config": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "min_instances": { + Type: schema.TypeInt, + Computed: true, + Description: "Min number of members", + }, + "max_instances": { + Type: schema.TypeInt, + Computed: true, + Description: "Max number of members", + }, + "target_cpu_utilization": { + Type: schema.TypeFloat, + Computed: true, + Description: "CPU target threshold", + }, + "target_memory_utilization": { + Type: schema.TypeFloat, + Computed: true, + Description: "Memory target threshold", + }, + "cooldown_minutes": { + Type: schema.TypeInt, + Computed: true, + Description: "Cooldown duration", + }, + "target_number_instances": { + Type: schema.TypeInt, + Computed: true, + Description: "Target number of members", + }, + }, + }, + }, + "droplet_template": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "size": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet size", + }, + "region": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet region", + }, + "image": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet image", + }, + "tags": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Description: "Droplet tags", + }, + "ssh_keys": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Description: "Droplet SSH keys", + }, + "vpc_uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet VPC UUID", + }, + "with_droplet_agent": { + Type: schema.TypeBool, + Computed: true, + Description: "Enable droplet agent", + }, + "project_id": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet project ID", + }, + "ipv6": { + Type: schema.TypeBool, + Computed: true, + Description: "Enable droplet IPv6", + }, + "user_data": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet user data", + }, + }, + }, + }, + "current_utilization": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "memory": { + Type: schema.TypeFloat, + Computed: true, + Description: "Average Memory utilization", + }, + "cpu": { + Type: schema.TypeFloat, + Computed: true, + Description: "Average CPU utilization", + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet autoscale pool status", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet autoscale pool create timestamp", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet autoscale pool update timestamp", + }, + "members": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "droplet_id": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet ID", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet create timestamp", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet update timestamp", + }, + "health_status": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet health status", + }, + "unhealthy_reason": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet unhealthy reason", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet state", + }, + "current_utilization": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "memory": { + Type: schema.TypeFloat, + Computed: true, + Description: "Droplet Memory utilization", + }, + "cpu": { + Type: schema.TypeFloat, + Computed: true, + Description: "Droplet CPU utilization", + }, + }, + }, + }, + }, + }, + }, + "history_events": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "history_event_id": { + Type: schema.TypeString, + Computed: true, + Description: "Event ID", + }, + "current_instance_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Event reported current member count", + }, + "desired_instance_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Event reported target member count", + }, + "reason": { + Type: schema.TypeString, + Computed: true, + Description: "Event description", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "Event status", + }, + "error_reason": { + Type: schema.TypeString, + Computed: true, + Description: "Event error reason", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Event create timestamp", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Event update timestamp", + }, + }, + }, + }, + }, + } +} + +func dataSourceDigitalOceanDropletAutoscaleRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*config.CombinedConfig).GodoClient() + + var foundDropletAutoscalePool *godo.DropletAutoscalePool + if id, ok := d.GetOk("id"); ok { + pool, _, err := client.DropletAutoscale.Get(context.Background(), id.(string)) + if err != nil { + return diag.Errorf("Error retrieving Droplet autoscale pool: %v", err) + } + foundDropletAutoscalePool = pool + } else if name, ok := d.GetOk("name"); ok { + dropletAutoscalePoolList := make([]*godo.DropletAutoscalePool, 0) + opts := &godo.ListOptions{ + Page: 1, + PerPage: 100, + } + // Paginate through all active resources + for { + pools, resp, err := client.DropletAutoscale.List(context.Background(), opts) + if err != nil { + return diag.Errorf("Error listing Droplet autoscale pools: %v", err) + } + dropletAutoscalePoolList = append(dropletAutoscalePoolList, pools...) + if resp.Links.IsLastPage() { + break + } + page, err := resp.Links.CurrentPage() + if err != nil { + break + } + opts.Page = page + 1 + } + // Scan through the list to find a resource name match + for i := range dropletAutoscalePoolList { + if dropletAutoscalePoolList[i] == name { + foundDropletAutoscalePool = dropletAutoscalePoolList[i] + break + } + } + } else { + return diag.Errorf("Need to specify either a name or an id to look up the Droplet autoscale pool") + } + if foundDropletAutoscalePool == nil { + return diag.Errorf("Droplet autoscale pool not found") + } + + d.SetId(foundDropletAutoscalePool.ID) + d.Set("name", foundDropletAutoscalePool.Name) + d.Set("config", flattenConfig(foundDropletAutoscalePool.Config)) + d.Set("droplet_template", flattenTemplate(foundDropletAutoscalePool.DropletTemplate)) + d.Set("current_utilization", flattenUtilization(foundDropletAutoscalePool.CurrentUtilization)) + d.Set("status", foundDropletAutoscalePool.Status) + d.Set("created_at", foundDropletAutoscalePool.CreatedAt.UTC().String()) + d.Set("updated_at", foundDropletAutoscalePool.UpdatedAt.UTC().String()) + + if memberOpts, ok := d.GetOk("list_member_opts"); ok { + opts := expandPaginationOpts(memberOpts.([]interface{})) + members, _, err := client.DropletAutoscale.ListMembers(context.Background(), foundDropletAutoscalePool.ID, opts) + if err != nil { + return diag.Errorf("Error listing Droplet autoscale pool members: %v", err) + } + d.Set("members", flattenMembers(members)) + } + + if historyEventOpts, ok := d.GetOk("list_history_opts"); ok { + opts := expandPaginationOpts(historyEventOpts.([]interface{})) + events, _, err := client.DropletAutoscale.ListHistory(context.Background(), foundDropletAutoscalePool.ID, opts) + if err != nil { + return diag.Errorf("Error listing Droplet autoscale pool history events: %v", err) + } + d.Set("history_events", flatterHistoryEvents(events)) + } + + return nil +} + +func expandPaginationOpts(opts []interface{}) *godo.ListOptions { + if len(opts) > 0 { + optsMap := opts[0].(map[string]interface{}) + return &godo.ListOptions{ + Page: optsMap["page"].(int), + PerPage: optsMap["per_page"].(int), + } + } + return nil +} + +func flattenConfig(config *godo.DropletAutoscaleConfiguration) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + if config != nil { + r := make(map[string]interface{}) + r["min_instances"] = config.MinInstances + r["max_instances"] = config.MinInstances + r["target_cpu_utilization"] = config.MinInstances + r["target_memory_utilization"] = config.MinInstances + r["cooldown_minutes"] = config.MinInstances + r["target_number_instances"] = config.MinInstances + result = append(result, r) + } + return result +} + +func flattenTemplate(template *godo.DropletAutoscaleResourceTemplate) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + if template != nil { + r := make(map[string]interface{}) + r["size"] = template.Size + r["region"] = template.Region + r["image"] = template.Image + r["vpc_uuid"] = template.VpcUUID + r["with_droplet_agent"] = template.WithDropletAgent + r["project_id"] = template.ProjectID + r["ipv6"] = template.IPV6 + r["user_data"] = template.UserData + + tagSet := schema.NewSet(schema.HashString, []interface{}{}) + for _, tag := range template.Tags { + tagSet.Add(tag) + } + r["tags"] = tagSet + + keySet := schema.NewSet(schema.HashString, []interface{}{}) + for _, key := range template.SSHKeys { + keySet.Add(key) + } + r["ssh_keys"] = keySet + result = append(result, r) + } + return result +} + +func flattenUtilization(util *godo.DropletAutoscaleResourceUtilization) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + if util != nil { + r := make(map[string]interface{}) + r["memory"] = util.Memory + r["cpu"] = util.CPU + result = append(result, r) + } + return result +} + +func flattenMembers(members []*godo.DropletAutoscaleResource) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(members)) + for _, member := range members { + r := make(map[string]interface{}) + r["droplet_id"] = member.DropletID + r["created_at"] = member.CreatedAt.UTC().String() + r["updated_at"] = member.UpdatedAt.UTC().String() + r["health_status"] = member.HealthStatus + r["unhealthy_reason"] = member.UnhealthyReason + r["status"] = member.Status + r["current_utilization"] = flattenUtilization(member.CurrentUtilization) + result = append(result, r) + } + return result +} + +func flatterHistoryEvents(events []*godo.DropletAutoscaleHistoryEvent) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(events)) + for _, event := range events { + r := make(map[string]interface{}) + r["history_event_id"] = event.HistoryEventID + r["current_instance_count"] = event.CurrentInstanceCount + r["desired_instance_count"] = event.DesiredInstanceCount + r["reason"] = event.Reason + r["status"] = event.Status + r["error_reason"] = event.ErrorReason + r["created_at"] = event.CreatedAt.UTC().String() + r["updated_at"] = event.UpdatedAt.UTC().String() + } + return result +} diff --git a/digitalocean/dropletautoscale/resource_droplet_autoscale.go b/digitalocean/dropletautoscale/resource_droplet_autoscale.go new file mode 100644 index 000000000..9a3eec25d --- /dev/null +++ b/digitalocean/dropletautoscale/resource_droplet_autoscale.go @@ -0,0 +1 @@ +package dropletautoscale diff --git a/digitalocean/provider.go b/digitalocean/provider.go index f0d682d5c..d715059ce 100644 --- a/digitalocean/provider.go +++ b/digitalocean/provider.go @@ -11,6 +11,7 @@ import ( "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/database" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/domain" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/droplet" + "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/dropletautoscale" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/firewall" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/image" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/kubernetes" @@ -109,6 +110,7 @@ func Provider() *schema.Provider { "digitalocean_domain": domain.DataSourceDigitalOceanDomain(), "digitalocean_domains": domain.DataSourceDigitalOceanDomains(), "digitalocean_droplet": droplet.DataSourceDigitalOceanDroplet(), + "digitalocean_droplet_autoscale": dropletautoscale.DataSourceDigitalOceanDropletAutoscale(), "digitalocean_droplets": droplet.DataSourceDigitalOceanDroplets(), "digitalocean_droplet_snapshot": snapshot.DataSourceDigitalOceanDropletSnapshot(), "digitalocean_firewall": firewall.DataSourceDigitalOceanFirewall(), From 336aca9ff23ee8c182bf2c0437924ac5f9708ec0 Mon Sep 17 00:00:00 2001 From: Aditya Saha Date: Wed, 27 Nov 2024 16:34:35 -0500 Subject: [PATCH 2/9] Scaffold resource implementation --- .../resource_droplet_autoscale.go | 288 ++++++++++++++++++ digitalocean/provider.go | 1 + 2 files changed, 289 insertions(+) diff --git a/digitalocean/dropletautoscale/resource_droplet_autoscale.go b/digitalocean/dropletautoscale/resource_droplet_autoscale.go index 9a3eec25d..cf11222f9 100644 --- a/digitalocean/dropletautoscale/resource_droplet_autoscale.go +++ b/digitalocean/dropletautoscale/resource_droplet_autoscale.go @@ -1 +1,289 @@ package dropletautoscale + +import ( + "context" + "net/http" + + "github.com/digitalocean/godo" + "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/config" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceDigitalOceanDropletAutoscale() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceDigitalOceanDropletAutoscaleCreate, + ReadContext: resourceDigitalOceanDropletAutoscaleRead, + UpdateContext: resourceDigitalOceanDropletAutoscaleUpdate, + DeleteContext: resourceDigitalOceanDropletAutoscaleDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the Droplet autoscale pool", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the Droplet autoscale pool", + }, + "config": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "min_instances": { + Type: schema.TypeInt, + Optional: true, + Description: "Min number of members", + ValidateFunc: validation.All(validation.NoZeroValues), + }, + "max_instances": { + Type: schema.TypeInt, + Optional: true, + Description: "Max number of members", + ValidateFunc: validation.All(validation.NoZeroValues), + }, + "target_cpu_utilization": { + Type: schema.TypeFloat, + Optional: true, + Description: "CPU target threshold", + ValidateFunc: validation.All(validation.FloatBetween(0, 1)), + }, + "target_memory_utilization": { + Type: schema.TypeFloat, + Optional: true, + Description: "Memory target threshold", + ValidateFunc: validation.All(validation.FloatBetween(0, 1)), + }, + "cooldown_minutes": { + Type: schema.TypeInt, + Optional: true, + Description: "Cooldown duration", + ValidateFunc: validation.All(validation.NoZeroValues), + }, + "target_number_instances": { + Type: schema.TypeInt, + Optional: true, + Description: "Target number of members", + ValidateFunc: validation.All(validation.NoZeroValues), + }, + }, + }, + }, + "droplet_template": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "size": { + Type: schema.TypeString, + Required: true, + Description: "Droplet size", + }, + "region": { + Type: schema.TypeString, + Required: true, + Description: "Droplet region", + }, + "image": { + Type: schema.TypeString, + Required: true, + Description: "Droplet image", + }, + "tags": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Description: "Droplet tags", + }, + "ssh_keys": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + Description: "Droplet SSH keys", + }, + "vpc_uuid": { + Type: schema.TypeString, + Optional: true, + Description: "Droplet VPC UUID", + }, + "with_droplet_agent": { + Type: schema.TypeBool, + Optional: true, + Description: "Enable droplet agent", + }, + "project_id": { + Type: schema.TypeString, + Optional: true, + Description: "Droplet project ID", + }, + "ipv6": { + Type: schema.TypeBool, + Optional: true, + Description: "Enable droplet IPv6", + }, + "user_data": { + Type: schema.TypeString, + Optional: true, + Description: "Droplet user data", + }, + }, + }, + }, + "current_utilization": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "memory": { + Type: schema.TypeFloat, + Computed: true, + Description: "Average Memory utilization", + }, + "cpu": { + Type: schema.TypeFloat, + Computed: true, + Description: "Average CPU utilization", + }, + }, + }, + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet autoscale pool status", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet autoscale pool create timestamp", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet autoscale pool update timestamp", + }, + }, + } +} + +func expandConfig(config []interface{}) *godo.DropletAutoscaleConfiguration { + if len(config) > 0 { + poolConfig := config[0].(map[string]interface{}) + return &godo.DropletAutoscaleConfiguration{ + MinInstances: poolConfig["min_instances"].(uint64), + MaxInstances: poolConfig["max_instances"].(uint64), + TargetCPUUtilization: poolConfig["target_cpu_utilization"].(float64), + TargetMemoryUtilization: poolConfig["target_memory_utilization"].(float64), + CooldownMinutes: poolConfig["cooldown_minutes"].(uint32), + TargetNumberInstances: poolConfig["target_number_instances"].(uint64), + } + } + return nil +} + +func expandTemplate(template []interface{}) *godo.DropletAutoscaleResourceTemplate { + if len(template) > 0 { + poolTemplate := template[0].(map[string]interface{}) + + var tags []string + if v, ok := poolTemplate["tags"]; ok { + for _, tag := range v.(*schema.Set).List() { + tags = append(tags, tag.(string)) + } + } + + var sshKeys []string + if v, ok := poolTemplate["ssh_keys"]; ok { + for _, key := range v.(*schema.Set).List() { + sshKeys = append(sshKeys, key.(string)) + } + } + + return &godo.DropletAutoscaleResourceTemplate{ + Size: poolTemplate["size"].(string), + Region: poolTemplate["region"].(string), + Image: poolTemplate["image"].(string), + Tags: tags, + SSHKeys: sshKeys, + VpcUUID: poolTemplate["vpc_uuid"].(string), + WithDropletAgent: poolTemplate["with_droplet_agent"].(bool), + ProjectID: poolTemplate["project_id"].(string), + IPV6: poolTemplate["ipv6"].(bool), + UserData: poolTemplate["user_data"].(string), + } + } + return nil +} + +func resourceDigitalOceanDropletAutoscaleCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*config.CombinedConfig).GodoClient() + + pool, _, err := client.DropletAutoscale.Create(context.Background(), &godo.DropletAutoscalePoolRequest{ + Name: d.Get("name").(string), + Config: expandConfig(d.Get("config").([]interface{})), + DropletTemplate: expandTemplate(d.Get("droplet_template").([]interface{})), + }) + if err != nil { + return diag.Errorf("Error creating Droplet autoscale pool: %v", err) + } + + d.SetId(pool.ID) + return resourceDigitalOceanDropletAutoscaleRead(ctx, d, meta) +} + +func resourceDigitalOceanDropletAutoscaleRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*config.CombinedConfig).GodoClient() + + pool, resp, err := client.DropletAutoscale.Get(context.Background(), d.Id()) + if err != nil { + if resp != nil && resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + return diag.Errorf("Error retrieving Droplet autoscale pool: %v", err) + } + + d.Set("name", pool.Name) + d.Set("config", flattenConfig(pool.Config)) + d.Set("droplet_template", flattenTemplate(pool.DropletTemplate)) + d.Set("current_utilization", flattenUtilization(pool.CurrentUtilization)) + d.Set("status", pool.Status) + d.Set("created_at", pool.CreatedAt.UTC().String()) + d.Set("updated_at", pool.UpdatedAt.UTC().String()) + + return nil +} + +func resourceDigitalOceanDropletAutoscaleUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*config.CombinedConfig).GodoClient() + + pool, _, err := client.DropletAutoscale.Update(context.Background(), d.Id(), &godo.DropletAutoscalePoolRequest{ + Name: d.Get("name").(string), + Config: expandConfig(d.Get("config").([]interface{})), + DropletTemplate: expandTemplate(d.Get("droplet_template").([]interface{})), + }) + if err != nil { + return diag.Errorf("Error updating Droplet autoscale pool: %v", err) + } + + d.SetId(pool.ID) + return resourceDigitalOceanDropletAutoscaleRead(ctx, d, meta) +} + +func resourceDigitalOceanDropletAutoscaleDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*config.CombinedConfig).GodoClient() + + _, err := client.DropletAutoscale.DeleteDangerous(context.Background(), d.Id()) + if err != nil { + return diag.Errorf("Error updating Droplet autoscale pool: %v", err) + } + + d.SetId("") + return nil +} diff --git a/digitalocean/provider.go b/digitalocean/provider.go index d715059ce..1c67a4e99 100644 --- a/digitalocean/provider.go +++ b/digitalocean/provider.go @@ -163,6 +163,7 @@ func Provider() *schema.Provider { "digitalocean_database_kafka_topic": database.ResourceDigitalOceanDatabaseKafkaTopic(), "digitalocean_domain": domain.ResourceDigitalOceanDomain(), "digitalocean_droplet": droplet.ResourceDigitalOceanDroplet(), + "digitalocean_droplet_autoscale": dropletautoscale.ResourceDigitalOceanDropletAutoscale(), "digitalocean_droplet_snapshot": snapshot.ResourceDigitalOceanDropletSnapshot(), "digitalocean_firewall": firewall.ResourceDigitalOceanFirewall(), "digitalocean_floating_ip": reservedip.ResourceDigitalOceanFloatingIP(), From 3e63fef4f75b7ff201be7286177674092ef380fc Mon Sep 17 00:00:00 2001 From: Aditya Saha Date: Thu, 28 Nov 2024 14:28:57 -0500 Subject: [PATCH 3/9] Add tests and adjust implementations --- .../datasource_droplet_autoscale.go | 22 +- .../datasource_droplet_autoscale_test.go | 1 + .../resource_droplet_autoscale.go | 252 +++++++- .../resource_droplet_autoscale_test.go | 551 ++++++++++++++++++ 4 files changed, 811 insertions(+), 15 deletions(-) create mode 100644 digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go create mode 100644 digitalocean/dropletautoscale/resource_droplet_autoscale_test.go diff --git a/digitalocean/dropletautoscale/datasource_droplet_autoscale.go b/digitalocean/dropletautoscale/datasource_droplet_autoscale.go index 5add25596..0367549b6 100644 --- a/digitalocean/dropletautoscale/datasource_droplet_autoscale.go +++ b/digitalocean/dropletautoscale/datasource_droplet_autoscale.go @@ -2,6 +2,7 @@ package dropletautoscale import ( "context" + "fmt" "github.com/digitalocean/godo" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/config" @@ -36,10 +37,12 @@ func DataSourceDigitalOceanDropletAutoscale() *schema.Resource { Schema: map[string]*schema.Schema{ "page": { Type: schema.TypeInt, + Required: true, Description: "Page offset", }, "per_page": { Type: schema.TypeInt, + Required: true, Description: "Per-page count", }, }, @@ -53,10 +56,12 @@ func DataSourceDigitalOceanDropletAutoscale() *schema.Resource { Schema: map[string]*schema.Schema{ "page": { Type: schema.TypeInt, + Required: true, Description: "Page offset", }, "per_page": { Type: schema.TypeInt, + Required: true, Description: "Per-page count", }, }, @@ -393,11 +398,11 @@ func flattenConfig(config *godo.DropletAutoscaleConfiguration) []map[string]inte if config != nil { r := make(map[string]interface{}) r["min_instances"] = config.MinInstances - r["max_instances"] = config.MinInstances - r["target_cpu_utilization"] = config.MinInstances - r["target_memory_utilization"] = config.MinInstances - r["cooldown_minutes"] = config.MinInstances - r["target_number_instances"] = config.MinInstances + r["max_instances"] = config.MaxInstances + r["target_cpu_utilization"] = config.TargetCPUUtilization + r["target_memory_utilization"] = config.TargetMemoryUtilization + r["cooldown_minutes"] = config.CooldownMinutes + r["target_number_instances"] = config.TargetNumberInstances result = append(result, r) } return result @@ -447,7 +452,7 @@ func flattenMembers(members []*godo.DropletAutoscaleResource) []map[string]inter result := make([]map[string]interface{}, 0, len(members)) for _, member := range members { r := make(map[string]interface{}) - r["droplet_id"] = member.DropletID + r["droplet_id"] = fmt.Sprint(member.DropletID) r["created_at"] = member.CreatedAt.UTC().String() r["updated_at"] = member.UpdatedAt.UTC().String() r["health_status"] = member.HealthStatus @@ -464,13 +469,14 @@ func flatterHistoryEvents(events []*godo.DropletAutoscaleHistoryEvent) []map[str for _, event := range events { r := make(map[string]interface{}) r["history_event_id"] = event.HistoryEventID - r["current_instance_count"] = event.CurrentInstanceCount - r["desired_instance_count"] = event.DesiredInstanceCount + r["current_instance_count"] = int(event.CurrentInstanceCount) + r["desired_instance_count"] = int(event.DesiredInstanceCount) r["reason"] = event.Reason r["status"] = event.Status r["error_reason"] = event.ErrorReason r["created_at"] = event.CreatedAt.UTC().String() r["updated_at"] = event.UpdatedAt.UTC().String() + result = append(result, r) } return result } diff --git a/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go b/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go new file mode 100644 index 000000000..9a3eec25d --- /dev/null +++ b/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go @@ -0,0 +1 @@ +package dropletautoscale diff --git a/digitalocean/dropletautoscale/resource_droplet_autoscale.go b/digitalocean/dropletautoscale/resource_droplet_autoscale.go index cf11222f9..cf5e31de0 100644 --- a/digitalocean/dropletautoscale/resource_droplet_autoscale.go +++ b/digitalocean/dropletautoscale/resource_droplet_autoscale.go @@ -2,11 +2,15 @@ package dropletautoscale import ( "context" + "fmt" "net/http" + "strings" + "time" "github.com/digitalocean/godo" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/config" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) @@ -31,6 +35,44 @@ func ResourceDigitalOceanDropletAutoscale() *schema.Resource { Required: true, Description: "Name of the Droplet autoscale pool", }, + "list_member_opts": { + Type: schema.TypeList, + Optional: true, + Description: "Pagination options for listing Droplet autoscale pool members", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Required: true, + Description: "Page offset", + }, + "per_page": { + Type: schema.TypeInt, + Required: true, + Description: "Per-page count", + }, + }, + }, + }, + "list_history_opts": { + Type: schema.TypeList, + Optional: true, + Description: "Pagination options for listing Droplet autoscale pool history events", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "page": { + Type: schema.TypeInt, + Required: true, + Description: "Page offset", + }, + "per_page": { + Type: schema.TypeInt, + Required: true, + Description: "Per-page count", + }, + }, + }, + }, "config": { Type: schema.TypeList, Required: true, @@ -168,6 +210,112 @@ func ResourceDigitalOceanDropletAutoscale() *schema.Resource { Computed: true, Description: "Droplet autoscale pool update timestamp", }, + "members": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "droplet_id": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet ID", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet create timestamp", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet update timestamp", + }, + "health_status": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet health status", + }, + "unhealthy_reason": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet unhealthy reason", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "Droplet state", + }, + "current_utilization": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "memory": { + Type: schema.TypeFloat, + Computed: true, + Description: "Droplet Memory utilization", + }, + "cpu": { + Type: schema.TypeFloat, + Computed: true, + Description: "Droplet CPU utilization", + }, + }, + }, + }, + }, + }, + }, + "history_events": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "history_event_id": { + Type: schema.TypeString, + Computed: true, + Description: "Event ID", + }, + "current_instance_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Event reported current member count", + }, + "desired_instance_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Event reported target member count", + }, + "reason": { + Type: schema.TypeString, + Computed: true, + Description: "Event description", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "Event status", + }, + "error_reason": { + Type: schema.TypeString, + Computed: true, + Description: "Event error reason", + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + Description: "Event create timestamp", + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + Description: "Event update timestamp", + }, + }, + }, + }, }, } } @@ -176,12 +324,12 @@ func expandConfig(config []interface{}) *godo.DropletAutoscaleConfiguration { if len(config) > 0 { poolConfig := config[0].(map[string]interface{}) return &godo.DropletAutoscaleConfiguration{ - MinInstances: poolConfig["min_instances"].(uint64), - MaxInstances: poolConfig["max_instances"].(uint64), + MinInstances: uint64(poolConfig["min_instances"].(int)), + MaxInstances: uint64(poolConfig["max_instances"].(int)), TargetCPUUtilization: poolConfig["target_cpu_utilization"].(float64), TargetMemoryUtilization: poolConfig["target_memory_utilization"].(float64), - CooldownMinutes: poolConfig["cooldown_minutes"].(uint32), - TargetNumberInstances: poolConfig["target_number_instances"].(uint64), + CooldownMinutes: uint32(poolConfig["cooldown_minutes"].(int)), + TargetNumberInstances: uint64(poolConfig["target_number_instances"].(int)), } } return nil @@ -232,17 +380,29 @@ func resourceDigitalOceanDropletAutoscaleCreate(ctx context.Context, d *schema.R if err != nil { return diag.Errorf("Error creating Droplet autoscale pool: %v", err) } - d.SetId(pool.ID) + + stateConf := &retry.StateChangeConf{ + Delay: 5 * time.Second, + Pending: []string{"provisioning"}, + Target: []string{"active"}, + Refresh: dropletAutoscaleRefreshFunc(client, d.Id()), + MinTimeout: 15 * time.Second, + Timeout: 15 * time.Minute, + } + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return diag.Errorf("Error waiting for Droplet autoscale pool (%s) to become active: %v", pool.Name, err) + } + return resourceDigitalOceanDropletAutoscaleRead(ctx, d, meta) } func resourceDigitalOceanDropletAutoscaleRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*config.CombinedConfig).GodoClient() - pool, resp, err := client.DropletAutoscale.Get(context.Background(), d.Id()) + pool, _, err := client.DropletAutoscale.Get(context.Background(), d.Id()) if err != nil { - if resp != nil && resp.StatusCode == http.StatusNotFound { + if strings.Contains(err.Error(), fmt.Sprintf("autoscale group with id %s not found", d.Id())) { d.SetId("") return nil } @@ -257,6 +417,28 @@ func resourceDigitalOceanDropletAutoscaleRead(ctx context.Context, d *schema.Res d.Set("created_at", pool.CreatedAt.UTC().String()) d.Set("updated_at", pool.UpdatedAt.UTC().String()) + if memberOpts, ok := d.GetOk("list_member_opts"); ok { + opts := expandPaginationOpts(memberOpts.([]interface{})) + members, _, err := client.DropletAutoscale.ListMembers(context.Background(), d.Id(), opts) + if err != nil { + return diag.Errorf("Error listing Droplet autoscale pool members: %v", err) + } + d.Set("members", flattenMembers(members)) + } else { + d.Set("members", []map[string]interface{}{}) + } + + if historyEventOpts, ok := d.GetOk("list_history_opts"); ok { + opts := expandPaginationOpts(historyEventOpts.([]interface{})) + events, _, err := client.DropletAutoscale.ListHistory(context.Background(), d.Id(), opts) + if err != nil { + return diag.Errorf("Error listing Droplet autoscale pool history events: %v", err) + } + d.Set("history_events", flatterHistoryEvents(events)) + } else { + d.Set("history_events", []map[string]interface{}{}) + } + return nil } @@ -284,6 +466,62 @@ func resourceDigitalOceanDropletAutoscaleDelete(ctx context.Context, d *schema.R return diag.Errorf("Error updating Droplet autoscale pool: %v", err) } + stateConf := &retry.StateChangeConf{ + Delay: 5 * time.Second, + Pending: []string{http.StatusText(http.StatusOK)}, + Target: []string{http.StatusText(http.StatusNotFound)}, + Refresh: dropletAutoscaleRefreshFunc(client, d.Id()), + MinTimeout: 5 * time.Second, + Timeout: 1 * time.Minute, + } + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return diag.Errorf("Error waiting for Droplet autoscale pool (%s) to become be deleted: %v", d.Get("name"), err) + } + d.SetId("") return nil } + +func dropletAutoscaleRefreshFunc(client *godo.Client, poolID string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + // Check autoscale pool status + pool, _, err := client.DropletAutoscale.Get(context.Background(), poolID) + if err != nil { + if strings.Contains(err.Error(), fmt.Sprintf("autoscale group with id %s not found", poolID)) { + return pool, http.StatusText(http.StatusNotFound), nil + } + return nil, "", fmt.Errorf("Error retrieving Droplet autoscale pool: %v", err) + } + if pool.Status != "active" { + return pool, pool.Status, nil + } + members := make([]*godo.DropletAutoscaleResource, 0) + opts := &godo.ListOptions{ + Page: 1, + PerPage: 100, + } + // Paginate through autoscale pool members and validate status + for { + m, resp, err := client.DropletAutoscale.ListMembers(context.Background(), poolID, opts) + if err != nil { + return nil, "", fmt.Errorf("Error listing Droplet autoscale pool members: %v", err) + } + members = append(members, m...) + if resp.Links.IsLastPage() { + break + } + page, err := resp.Links.CurrentPage() + if err != nil { + break + } + opts.Page = page + 1 + } + // Scan through the list to find a non-active provision state + for i := range members { + if members[i].Status != "active" { + return members, members[i].Status, nil + } + } + return members, "active", nil + } +} diff --git a/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go b/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go new file mode 100644 index 000000000..083e8caf6 --- /dev/null +++ b/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go @@ -0,0 +1,551 @@ +package dropletautoscale_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/digitalocean/godo" + "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/acceptance" + "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/config" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccDigitalOceanDropletAutoscale_Static(t *testing.T) { + var autoscalePool godo.DropletAutoscalePool + name := acceptance.RandomTestName() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: testAccCheckDigitalOceanDropletAutoscaleDestroy, + Steps: []resource.TestStep{ + { + // Test create + Config: testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 1, false, false), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), + resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "name", name), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.min_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.max_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_cpu_utilization", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_memory_utilization", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.cooldown_minutes", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_number_instances", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "status", "active"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "created_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "updated_at"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "members.#", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "history_events.#", "0"), + ), + }, + { + // Test update (static scale up) + Config: testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 2, false, false), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), + resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "name", name), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.min_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.max_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_cpu_utilization", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_memory_utilization", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.cooldown_minutes", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_number_instances", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "status", "active"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "created_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "updated_at"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "members.#", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "history_events.#", "0"), + ), + }, + { + // Test listing members + Config: testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 2, true, false), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), + resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "name", name), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.min_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.max_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_cpu_utilization", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_memory_utilization", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.cooldown_minutes", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_number_instances", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "status", "active"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "created_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "updated_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "members.#"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "members.0.droplet_id"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "members.0.created_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "members.0.updated_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "members.0.health_status"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "members.0.status"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "history_events.#", "0"), + ), + }, + { + // Test listing history events + Config: testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 2, false, true), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), + resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "name", name), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.min_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.max_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_cpu_utilization", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_memory_utilization", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.cooldown_minutes", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_number_instances", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "status", "active"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "created_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "updated_at"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "members.#", "0"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "history_events.#"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "history_events.0.history_event_id"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "history_events.0.current_instance_count"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "history_events.0.desired_instance_count"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "history_events.0.reason"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "history_events.0.status"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "history_events.0.created_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "history_events.0.updated_at"), + ), + }, + }, + }) +} + +func TestAccDigitalOceanDropletAutoscale_Dynamic(t *testing.T) { + var autoscalePool godo.DropletAutoscalePool + name := acceptance.RandomTestName() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: testAccCheckDigitalOceanDropletAutoscaleDestroy, + Steps: []resource.TestStep{ + { + // Test create + Config: testAccCheckDigitalOceanDropletAutoscaleConfig_dynamic(name, 1), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), + resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "name", name), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.min_instances", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.max_instances", "3"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_cpu_utilization", "0.5"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_memory_utilization", "0.5"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.cooldown_minutes", "5"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_number_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "status", "active"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "created_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "updated_at"), + ), + }, + { + // Test update (dynamic scale up) + Config: testAccCheckDigitalOceanDropletAutoscaleConfig_dynamic(name, 2), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), + resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "name", name), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.min_instances", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.max_instances", "3"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_cpu_utilization", "0.5"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_memory_utilization", "0.5"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.cooldown_minutes", "5"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "config.0.target_number_instances", "0"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "digitalocean_droplet_autoscale.foobar", "status", "active"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "created_at"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "updated_at"), + ), + }, + }, + }) +} + +func testAccCheckDigitalOceanDropletAutoscaleExists(n string, autoscalePool *godo.DropletAutoscalePool) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource not found: %v", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("Resource ID not set") + } + // Check for valid ID response to validate that the resource has been created + client := acceptance.TestAccProvider.Meta().(*config.CombinedConfig).GodoClient() + pool, _, err := client.DropletAutoscale.Get(context.Background(), rs.Primary.ID) + if err != nil { + return err + } + if pool.ID != rs.Primary.ID { + return fmt.Errorf("Droplet autoscale pool not found") + } + *autoscalePool = *pool + return nil + } +} + +func testAccCheckDigitalOceanDropletAutoscaleDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "digitalocean_droplet_autoscale" { + continue + } + client := acceptance.TestAccProvider.Meta().(*config.CombinedConfig).GodoClient() + _, _, err := client.DropletAutoscale.Get(context.Background(), rs.Primary.ID) + if err != nil { + if strings.Contains(err.Error(), fmt.Sprintf("autoscale group with id %s not found", rs.Primary.ID)) { + return nil + } + return fmt.Errorf("Droplet autoscale pool still exists") + } + } + return nil +} + +func testAccCheckDigitalOceanDropletAutoscaleConfig_static(name string, size int, getMembers, getEvents bool) string { + pubKey1, _, err := acctest.RandSSHKeyPair("digitalocean@acceptance-test") + if err != nil { + fmt.Println("Unable to generate public key", err) + return "" + } + + pubKey2, _, err := acctest.RandSSHKeyPair("digitalocean@acceptance-test") + if err != nil { + fmt.Println("Unable to generate public key", err) + return "" + } + + var memberOpts, eventOpts string + if getMembers { + memberOpts = ` + list_member_opts { + page = 1 + per_page = 10 + } +` + } + if getEvents { + memberOpts = ` + list_history_opts { + page = 1 + per_page = 10 + } +` + } + + return fmt.Sprintf(` +resource "digitalocean_ssh_key" "foo" { + name = "%s" + public_key = "%s" +} + +resource "digitalocean_ssh_key" "bar" { + name = "%s" + public_key = "%s" +} + +resource "digitalocean_tag" "foo" { + name = "%s" +} + +resource "digitalocean_tag" "bar" { + name = "%s" +} + +resource "digitalocean_droplet_autoscale" "foobar" { + name = "%s" + + config { + target_number_instances = %d + } + + droplet_template { + size = "c-2" + region = "s2r1" + image = "547864" + tags = [digitalocean_tag.foo.id, digitalocean_tag.bar.id] + ssh_keys = [digitalocean_ssh_key.foo.id, digitalocean_ssh_key.bar.id] + with_droplet_agent = true + ipv6 = true + user_data = "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n" + } + + # embed pagination parameters for listing members + %s + + # embed pagination parameters for listing history events + %s +}`, + acceptance.RandomTestName("sshKey1"), pubKey1, + acceptance.RandomTestName("sshKey2"), pubKey2, + acceptance.RandomTestName("tag1"), + acceptance.RandomTestName("tag2"), + name, size, memberOpts, eventOpts) +} + +func testAccCheckDigitalOceanDropletAutoscaleConfig_dynamic(name string, size int) string { + pubKey1, _, err := acctest.RandSSHKeyPair("digitalocean@acceptance-test") + if err != nil { + fmt.Println("Unable to generate public key", err) + return "" + } + + pubKey2, _, err := acctest.RandSSHKeyPair("digitalocean@acceptance-test") + if err != nil { + fmt.Println("Unable to generate public key", err) + return "" + } + + return fmt.Sprintf(` +resource "digitalocean_ssh_key" "foo" { + name = "%s" + public_key = "%s" +} + +resource "digitalocean_ssh_key" "bar" { + name = "%s" + public_key = "%s" +} + +resource "digitalocean_tag" "foo" { + name = "%s" +} + +resource "digitalocean_tag" "bar" { + name = "%s" +} + +resource "digitalocean_droplet_autoscale" "foobar" { + name = "%s" + + config { + min_instances = %d + max_instances = 3 + target_cpu_utilization = 0.5 + target_memory_utilization = 0.5 + cooldown_minutes = 5 + } + + droplet_template { + size = "c-2" + region = "s2r1" + image = "547864" + tags = [digitalocean_tag.foo.id, digitalocean_tag.bar.id] + ssh_keys = [digitalocean_ssh_key.foo.id, digitalocean_ssh_key.bar.id] + with_droplet_agent = true + ipv6 = true + user_data = "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n" + } +}`, + acceptance.RandomTestName("sshKey1"), pubKey1, + acceptance.RandomTestName("sshKey2"), pubKey2, + acceptance.RandomTestName("tag1"), + acceptance.RandomTestName("tag2"), + name, size) +} From 67bc337aa32ee9e0771fa997bd2be84d11c2c36c Mon Sep 17 00:00:00 2001 From: Aditya Saha Date: Fri, 29 Nov 2024 13:30:47 -0500 Subject: [PATCH 4/9] Remove supporting members and history in resource --- .../resource_droplet_autoscale.go | 171 +---------------- .../resource_droplet_autoscale_test.go | 178 ++---------------- 2 files changed, 15 insertions(+), 334 deletions(-) diff --git a/digitalocean/dropletautoscale/resource_droplet_autoscale.go b/digitalocean/dropletautoscale/resource_droplet_autoscale.go index cf5e31de0..0dfefc02c 100644 --- a/digitalocean/dropletautoscale/resource_droplet_autoscale.go +++ b/digitalocean/dropletautoscale/resource_droplet_autoscale.go @@ -35,44 +35,6 @@ func ResourceDigitalOceanDropletAutoscale() *schema.Resource { Required: true, Description: "Name of the Droplet autoscale pool", }, - "list_member_opts": { - Type: schema.TypeList, - Optional: true, - Description: "Pagination options for listing Droplet autoscale pool members", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "page": { - Type: schema.TypeInt, - Required: true, - Description: "Page offset", - }, - "per_page": { - Type: schema.TypeInt, - Required: true, - Description: "Per-page count", - }, - }, - }, - }, - "list_history_opts": { - Type: schema.TypeList, - Optional: true, - Description: "Pagination options for listing Droplet autoscale pool history events", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "page": { - Type: schema.TypeInt, - Required: true, - Description: "Page offset", - }, - "per_page": { - Type: schema.TypeInt, - Required: true, - Description: "Per-page count", - }, - }, - }, - }, "config": { Type: schema.TypeList, Required: true, @@ -210,112 +172,6 @@ func ResourceDigitalOceanDropletAutoscale() *schema.Resource { Computed: true, Description: "Droplet autoscale pool update timestamp", }, - "members": { - Type: schema.TypeList, - Computed: true, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "droplet_id": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet ID", - }, - "created_at": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet create timestamp", - }, - "updated_at": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet update timestamp", - }, - "health_status": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet health status", - }, - "unhealthy_reason": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet unhealthy reason", - }, - "status": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet state", - }, - "current_utilization": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "memory": { - Type: schema.TypeFloat, - Computed: true, - Description: "Droplet Memory utilization", - }, - "cpu": { - Type: schema.TypeFloat, - Computed: true, - Description: "Droplet CPU utilization", - }, - }, - }, - }, - }, - }, - }, - "history_events": { - Type: schema.TypeList, - Computed: true, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "history_event_id": { - Type: schema.TypeString, - Computed: true, - Description: "Event ID", - }, - "current_instance_count": { - Type: schema.TypeInt, - Computed: true, - Description: "Event reported current member count", - }, - "desired_instance_count": { - Type: schema.TypeInt, - Computed: true, - Description: "Event reported target member count", - }, - "reason": { - Type: schema.TypeString, - Computed: true, - Description: "Event description", - }, - "status": { - Type: schema.TypeString, - Computed: true, - Description: "Event status", - }, - "error_reason": { - Type: schema.TypeString, - Computed: true, - Description: "Event error reason", - }, - "created_at": { - Type: schema.TypeString, - Computed: true, - Description: "Event create timestamp", - }, - "updated_at": { - Type: schema.TypeString, - Computed: true, - Description: "Event update timestamp", - }, - }, - }, - }, }, } } @@ -382,6 +238,7 @@ func resourceDigitalOceanDropletAutoscaleCreate(ctx context.Context, d *schema.R } d.SetId(pool.ID) + // Setup to poll for autoscale pool scaling up to the desired count stateConf := &retry.StateChangeConf{ Delay: 5 * time.Second, Pending: []string{"provisioning"}, @@ -417,35 +274,13 @@ func resourceDigitalOceanDropletAutoscaleRead(ctx context.Context, d *schema.Res d.Set("created_at", pool.CreatedAt.UTC().String()) d.Set("updated_at", pool.UpdatedAt.UTC().String()) - if memberOpts, ok := d.GetOk("list_member_opts"); ok { - opts := expandPaginationOpts(memberOpts.([]interface{})) - members, _, err := client.DropletAutoscale.ListMembers(context.Background(), d.Id(), opts) - if err != nil { - return diag.Errorf("Error listing Droplet autoscale pool members: %v", err) - } - d.Set("members", flattenMembers(members)) - } else { - d.Set("members", []map[string]interface{}{}) - } - - if historyEventOpts, ok := d.GetOk("list_history_opts"); ok { - opts := expandPaginationOpts(historyEventOpts.([]interface{})) - events, _, err := client.DropletAutoscale.ListHistory(context.Background(), d.Id(), opts) - if err != nil { - return diag.Errorf("Error listing Droplet autoscale pool history events: %v", err) - } - d.Set("history_events", flatterHistoryEvents(events)) - } else { - d.Set("history_events", []map[string]interface{}{}) - } - return nil } func resourceDigitalOceanDropletAutoscaleUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*config.CombinedConfig).GodoClient() - pool, _, err := client.DropletAutoscale.Update(context.Background(), d.Id(), &godo.DropletAutoscalePoolRequest{ + _, _, err := client.DropletAutoscale.Update(context.Background(), d.Id(), &godo.DropletAutoscalePoolRequest{ Name: d.Get("name").(string), Config: expandConfig(d.Get("config").([]interface{})), DropletTemplate: expandTemplate(d.Get("droplet_template").([]interface{})), @@ -454,7 +289,6 @@ func resourceDigitalOceanDropletAutoscaleUpdate(ctx context.Context, d *schema.R return diag.Errorf("Error updating Droplet autoscale pool: %v", err) } - d.SetId(pool.ID) return resourceDigitalOceanDropletAutoscaleRead(ctx, d, meta) } @@ -466,6 +300,7 @@ func resourceDigitalOceanDropletAutoscaleDelete(ctx context.Context, d *schema.R return diag.Errorf("Error updating Droplet autoscale pool: %v", err) } + // Setup to poll for autoscale pool deletion stateConf := &retry.StateChangeConf{ Delay: 5 * time.Second, Pending: []string{http.StatusText(http.StatusOK)}, diff --git a/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go b/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go index 083e8caf6..afc06f4e2 100644 --- a/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go +++ b/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go @@ -18,6 +18,9 @@ func TestAccDigitalOceanDropletAutoscale_Static(t *testing.T) { var autoscalePool godo.DropletAutoscalePool name := acceptance.RandomTestName() + createConfig := testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 1) + updateConfig := strings.ReplaceAll(createConfig, "target_number_instances = 1", "target_number_instances = 2") + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.TestAccPreCheck(t) }, ProviderFactories: acceptance.TestAccProviderFactories, @@ -25,7 +28,7 @@ func TestAccDigitalOceanDropletAutoscale_Static(t *testing.T) { Steps: []resource.TestStep{ { // Test create - Config: testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 1, false, false), + Config: createConfig, Check: resource.ComposeAggregateTestCheckFunc( testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), @@ -69,129 +72,11 @@ func TestAccDigitalOceanDropletAutoscale_Static(t *testing.T) { "digitalocean_droplet_autoscale.foobar", "created_at"), resource.TestCheckResourceAttrSet( "digitalocean_droplet_autoscale.foobar", "updated_at"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "members.#", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "history_events.#", "0"), ), }, { // Test update (static scale up) - Config: testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 2, false, false), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), - resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "name", name), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.#", "1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.min_instances", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.max_instances", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.target_cpu_utilization", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.target_memory_utilization", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.cooldown_minutes", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.target_number_instances", "2"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.#", "1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ipv6", "true"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.tags.#", "2"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ssh_keys.#", "2"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "status", "active"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "created_at"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "updated_at"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "members.#", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "history_events.#", "0"), - ), - }, - { - // Test listing members - Config: testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 2, true, false), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), - resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "name", name), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.#", "1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.min_instances", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.max_instances", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.target_cpu_utilization", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.target_memory_utilization", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.cooldown_minutes", "0"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "config.0.target_number_instances", "2"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.#", "1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ipv6", "true"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.tags.#", "2"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.ssh_keys.#", "2"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "status", "active"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "created_at"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "updated_at"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "members.#"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "members.0.droplet_id"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "members.0.created_at"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "members.0.updated_at"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "members.0.health_status"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "members.0.status"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "history_events.#", "0"), - ), - }, - { - // Test listing history events - Config: testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 2, false, true), + Config: updateConfig, Check: resource.ComposeAggregateTestCheckFunc( testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), @@ -235,24 +120,6 @@ func TestAccDigitalOceanDropletAutoscale_Static(t *testing.T) { "digitalocean_droplet_autoscale.foobar", "created_at"), resource.TestCheckResourceAttrSet( "digitalocean_droplet_autoscale.foobar", "updated_at"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "members.#", "0"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "history_events.#"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "history_events.0.history_event_id"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "history_events.0.current_instance_count"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "history_events.0.desired_instance_count"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "history_events.0.reason"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "history_events.0.status"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "history_events.0.created_at"), - resource.TestCheckResourceAttrSet( - "digitalocean_droplet_autoscale.foobar", "history_events.0.updated_at"), ), }, }, @@ -263,6 +130,9 @@ func TestAccDigitalOceanDropletAutoscale_Dynamic(t *testing.T) { var autoscalePool godo.DropletAutoscalePool name := acceptance.RandomTestName() + createConfig := testAccCheckDigitalOceanDropletAutoscaleConfig_dynamic(name, 1) + updateConfig := strings.ReplaceAll(createConfig, "min_instances = 1", "min_instances = 2") + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.TestAccPreCheck(t) }, ProviderFactories: acceptance.TestAccProviderFactories, @@ -270,7 +140,7 @@ func TestAccDigitalOceanDropletAutoscale_Dynamic(t *testing.T) { Steps: []resource.TestStep{ { // Test create - Config: testAccCheckDigitalOceanDropletAutoscaleConfig_dynamic(name, 1), + Config: createConfig, Check: resource.ComposeAggregateTestCheckFunc( testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), @@ -318,7 +188,7 @@ func TestAccDigitalOceanDropletAutoscale_Dynamic(t *testing.T) { }, { // Test update (dynamic scale up) - Config: testAccCheckDigitalOceanDropletAutoscaleConfig_dynamic(name, 2), + Config: updateConfig, Check: resource.ComposeAggregateTestCheckFunc( testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), resource.TestCheckResourceAttrSet("digitalocean_droplet_autoscale.foobar", "id"), @@ -408,7 +278,7 @@ func testAccCheckDigitalOceanDropletAutoscaleDestroy(s *terraform.State) error { return nil } -func testAccCheckDigitalOceanDropletAutoscaleConfig_static(name string, size int, getMembers, getEvents bool) string { +func testAccCheckDigitalOceanDropletAutoscaleConfig_static(name string, size int) string { pubKey1, _, err := acctest.RandSSHKeyPair("digitalocean@acceptance-test") if err != nil { fmt.Println("Unable to generate public key", err) @@ -421,24 +291,6 @@ func testAccCheckDigitalOceanDropletAutoscaleConfig_static(name string, size int return "" } - var memberOpts, eventOpts string - if getMembers { - memberOpts = ` - list_member_opts { - page = 1 - per_page = 10 - } -` - } - if getEvents { - memberOpts = ` - list_history_opts { - page = 1 - per_page = 10 - } -` - } - return fmt.Sprintf(` resource "digitalocean_ssh_key" "foo" { name = "%s" @@ -475,18 +327,12 @@ resource "digitalocean_droplet_autoscale" "foobar" { ipv6 = true user_data = "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n" } - - # embed pagination parameters for listing members - %s - - # embed pagination parameters for listing history events - %s }`, acceptance.RandomTestName("sshKey1"), pubKey1, acceptance.RandomTestName("sshKey2"), pubKey2, acceptance.RandomTestName("tag1"), acceptance.RandomTestName("tag2"), - name, size, memberOpts, eventOpts) + name, size) } func testAccCheckDigitalOceanDropletAutoscaleConfig_dynamic(name string, size int) string { From 0f3cb751ee42bda7462e649e92d8e50d5034b3a6 Mon Sep 17 00:00:00 2001 From: Aditya Saha Date: Fri, 29 Nov 2024 14:24:20 -0500 Subject: [PATCH 5/9] Add datasource tests --- .../datasource_droplet_autoscale.go | 207 +------------- .../datasource_droplet_autoscale_test.go | 262 +++++++++++++++++- 2 files changed, 262 insertions(+), 207 deletions(-) diff --git a/digitalocean/dropletautoscale/datasource_droplet_autoscale.go b/digitalocean/dropletautoscale/datasource_droplet_autoscale.go index 0367549b6..166c76b06 100644 --- a/digitalocean/dropletautoscale/datasource_droplet_autoscale.go +++ b/digitalocean/dropletautoscale/datasource_droplet_autoscale.go @@ -2,7 +2,6 @@ package dropletautoscale import ( "context" - "fmt" "github.com/digitalocean/godo" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/config" @@ -29,44 +28,6 @@ func DataSourceDigitalOceanDropletAutoscale() *schema.Resource { ValidateFunc: validation.NoZeroValues, ExactlyOneOf: []string{"id", "name"}, }, - "list_member_opts": { - Type: schema.TypeList, - Optional: true, - Description: "Pagination options for listing Droplet autoscale pool members", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "page": { - Type: schema.TypeInt, - Required: true, - Description: "Page offset", - }, - "per_page": { - Type: schema.TypeInt, - Required: true, - Description: "Per-page count", - }, - }, - }, - }, - "list_history_opts": { - Type: schema.TypeList, - Optional: true, - Description: "Pagination options for listing Droplet autoscale pool history events", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "page": { - Type: schema.TypeInt, - Required: true, - Description: "Page offset", - }, - "per_page": { - Type: schema.TypeInt, - Required: true, - Description: "Per-page count", - }, - }, - }, - }, "config": { Type: schema.TypeList, Computed: true, @@ -198,110 +159,6 @@ func DataSourceDigitalOceanDropletAutoscale() *schema.Resource { Computed: true, Description: "Droplet autoscale pool update timestamp", }, - "members": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "droplet_id": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet ID", - }, - "created_at": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet create timestamp", - }, - "updated_at": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet update timestamp", - }, - "health_status": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet health status", - }, - "unhealthy_reason": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet unhealthy reason", - }, - "status": { - Type: schema.TypeString, - Computed: true, - Description: "Droplet state", - }, - "current_utilization": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "memory": { - Type: schema.TypeFloat, - Computed: true, - Description: "Droplet Memory utilization", - }, - "cpu": { - Type: schema.TypeFloat, - Computed: true, - Description: "Droplet CPU utilization", - }, - }, - }, - }, - }, - }, - }, - "history_events": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "history_event_id": { - Type: schema.TypeString, - Computed: true, - Description: "Event ID", - }, - "current_instance_count": { - Type: schema.TypeInt, - Computed: true, - Description: "Event reported current member count", - }, - "desired_instance_count": { - Type: schema.TypeInt, - Computed: true, - Description: "Event reported target member count", - }, - "reason": { - Type: schema.TypeString, - Computed: true, - Description: "Event description", - }, - "status": { - Type: schema.TypeString, - Computed: true, - Description: "Event status", - }, - "error_reason": { - Type: schema.TypeString, - Computed: true, - Description: "Event error reason", - }, - "created_at": { - Type: schema.TypeString, - Computed: true, - Description: "Event create timestamp", - }, - "updated_at": { - Type: schema.TypeString, - Computed: true, - Description: "Event update timestamp", - }, - }, - }, - }, }, } } @@ -340,7 +197,7 @@ func dataSourceDigitalOceanDropletAutoscaleRead(ctx context.Context, d *schema.R } // Scan through the list to find a resource name match for i := range dropletAutoscalePoolList { - if dropletAutoscalePoolList[i] == name { + if dropletAutoscalePoolList[i].Name == name { foundDropletAutoscalePool = dropletAutoscalePoolList[i] break } @@ -361,35 +218,6 @@ func dataSourceDigitalOceanDropletAutoscaleRead(ctx context.Context, d *schema.R d.Set("created_at", foundDropletAutoscalePool.CreatedAt.UTC().String()) d.Set("updated_at", foundDropletAutoscalePool.UpdatedAt.UTC().String()) - if memberOpts, ok := d.GetOk("list_member_opts"); ok { - opts := expandPaginationOpts(memberOpts.([]interface{})) - members, _, err := client.DropletAutoscale.ListMembers(context.Background(), foundDropletAutoscalePool.ID, opts) - if err != nil { - return diag.Errorf("Error listing Droplet autoscale pool members: %v", err) - } - d.Set("members", flattenMembers(members)) - } - - if historyEventOpts, ok := d.GetOk("list_history_opts"); ok { - opts := expandPaginationOpts(historyEventOpts.([]interface{})) - events, _, err := client.DropletAutoscale.ListHistory(context.Background(), foundDropletAutoscalePool.ID, opts) - if err != nil { - return diag.Errorf("Error listing Droplet autoscale pool history events: %v", err) - } - d.Set("history_events", flatterHistoryEvents(events)) - } - - return nil -} - -func expandPaginationOpts(opts []interface{}) *godo.ListOptions { - if len(opts) > 0 { - optsMap := opts[0].(map[string]interface{}) - return &godo.ListOptions{ - Page: optsMap["page"].(int), - PerPage: optsMap["per_page"].(int), - } - } return nil } @@ -447,36 +275,3 @@ func flattenUtilization(util *godo.DropletAutoscaleResourceUtilization) []map[st } return result } - -func flattenMembers(members []*godo.DropletAutoscaleResource) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(members)) - for _, member := range members { - r := make(map[string]interface{}) - r["droplet_id"] = fmt.Sprint(member.DropletID) - r["created_at"] = member.CreatedAt.UTC().String() - r["updated_at"] = member.UpdatedAt.UTC().String() - r["health_status"] = member.HealthStatus - r["unhealthy_reason"] = member.UnhealthyReason - r["status"] = member.Status - r["current_utilization"] = flattenUtilization(member.CurrentUtilization) - result = append(result, r) - } - return result -} - -func flatterHistoryEvents(events []*godo.DropletAutoscaleHistoryEvent) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(events)) - for _, event := range events { - r := make(map[string]interface{}) - r["history_event_id"] = event.HistoryEventID - r["current_instance_count"] = int(event.CurrentInstanceCount) - r["desired_instance_count"] = int(event.DesiredInstanceCount) - r["reason"] = event.Reason - r["status"] = event.Status - r["error_reason"] = event.ErrorReason - r["created_at"] = event.CreatedAt.UTC().String() - r["updated_at"] = event.UpdatedAt.UTC().String() - result = append(result, r) - } - return result -} diff --git a/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go b/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go index 9a3eec25d..a20043630 100644 --- a/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go +++ b/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go @@ -1 +1,261 @@ -package dropletautoscale +package dropletautoscale_test + +import ( + "testing" + + "github.com/digitalocean/godo" + "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/acceptance" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceDigitalOceanDropletAutoscale_Static(t *testing.T) { + var autoscalePool godo.DropletAutoscalePool + name := acceptance.RandomTestName() + + createConfig := testAccCheckDigitalOceanDropletAutoscaleConfig_static(name, 1) + dataSourceIDConfig := ` +data "digitalocean_droplet_autoscale" "foo" { + id = digitalocean_droplet_autoscale.foobar.id +}` + dataSourceNameConfig := ` +data "digitalocean_droplet_autoscale" "foo" { + name = digitalocean_droplet_autoscale.foobar.name +}` + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: testAccCheckDigitalOceanDropletAutoscaleDestroy, + Steps: []resource.TestStep{ + { + // Test create + Config: createConfig, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), + ), + }, + { + // Import by id + Config: createConfig + dataSourceIDConfig, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("data.digitalocean_droplet_autoscale.foo", &autoscalePool), + resource.TestCheckResourceAttrSet("data.digitalocean_droplet_autoscale.foo", "id"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "name", name), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.#", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.min_instances", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.max_instances", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_cpu_utilization", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_memory_utilization", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.cooldown_minutes", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_number_instances", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "status", "active"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "created_at"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "updated_at"), + ), + }, + { + // Import by name + Config: createConfig + dataSourceNameConfig, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("data.digitalocean_droplet_autoscale.foo", &autoscalePool), + resource.TestCheckResourceAttrSet("data.digitalocean_droplet_autoscale.foo", "id"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "name", name), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.#", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.min_instances", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.max_instances", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_cpu_utilization", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_memory_utilization", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.cooldown_minutes", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_number_instances", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "status", "active"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "created_at"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "updated_at"), + ), + }, + }, + }) +} + +func TestAccDataSourceDigitalOceanDropletAutoscale_Dynamic(t *testing.T) { + var autoscalePool godo.DropletAutoscalePool + name := acceptance.RandomTestName() + + createConfig := testAccCheckDigitalOceanDropletAutoscaleConfig_dynamic(name, 1) + dataSourceIDConfig := ` +data "digitalocean_droplet_autoscale" "foo" { + id = digitalocean_droplet_autoscale.foobar.id +}` + dataSourceNameConfig := ` +data "digitalocean_droplet_autoscale" "foo" { + name = digitalocean_droplet_autoscale.foobar.name +}` + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.TestAccPreCheck(t) }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: testAccCheckDigitalOceanDropletAutoscaleDestroy, + Steps: []resource.TestStep{ + { + // Test create + Config: createConfig, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("digitalocean_droplet_autoscale.foobar", &autoscalePool), + ), + }, + { + // Import by id + Config: createConfig + dataSourceIDConfig, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("data.digitalocean_droplet_autoscale.foo", &autoscalePool), + resource.TestCheckResourceAttrSet("data.digitalocean_droplet_autoscale.foo", "id"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "name", name), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.#", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.min_instances", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.max_instances", "3"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_cpu_utilization", "0.5"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_memory_utilization", "0.5"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.cooldown_minutes", "5"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_number_instances", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "status", "active"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "created_at"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "updated_at"), + ), + }, + { + // Import by name + Config: createConfig + dataSourceNameConfig, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDigitalOceanDropletAutoscaleExists("data.digitalocean_droplet_autoscale.foo", &autoscalePool), + resource.TestCheckResourceAttrSet("data.digitalocean_droplet_autoscale.foo", "id"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "name", name), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.#", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.min_instances", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.max_instances", "3"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_cpu_utilization", "0.5"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_memory_utilization", "0.5"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.cooldown_minutes", "5"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "config.0.target_number_instances", "0"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.#", "1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.size", "c-2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "s2r1"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image", "547864"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.with_droplet_agent", "true"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.ipv6", "true"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.user_data", "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.tags.#", "2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.ssh_keys.#", "2"), + resource.TestCheckResourceAttr( + "data.digitalocean_droplet_autoscale.foo", "status", "active"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "created_at"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "updated_at"), + ), + }, + }, + }) +} From c2db11dff8ea7ac85f8e232e2e1e17f5ff25f671 Mon Sep 17 00:00:00 2001 From: Aditya Saha Date: Wed, 4 Dec 2024 17:24:05 -0500 Subject: [PATCH 6/9] Image adjustments --- .../datasource_droplet_autoscale_test.go | 24 +++++++------- .../resource_droplet_autoscale.go | 15 ++++++++- .../resource_droplet_autoscale_test.go | 32 +++++++++---------- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go b/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go index a20043630..2e10f1df5 100644 --- a/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go +++ b/digitalocean/dropletautoscale/datasource_droplet_autoscale_test.go @@ -61,9 +61,9 @@ data "digitalocean_droplet_autoscale" "foo" { resource.TestCheckResourceAttr( "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.size", "c-2"), resource.TestCheckResourceAttr( - "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image", "547864"), + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "nyc3"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image"), resource.TestCheckResourceAttr( "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.with_droplet_agent", "true"), resource.TestCheckResourceAttr( @@ -109,9 +109,9 @@ data "digitalocean_droplet_autoscale" "foo" { resource.TestCheckResourceAttr( "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.size", "c-2"), resource.TestCheckResourceAttr( - "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image", "547864"), + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "nyc3"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image"), resource.TestCheckResourceAttr( "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.with_droplet_agent", "true"), resource.TestCheckResourceAttr( @@ -187,9 +187,9 @@ data "digitalocean_droplet_autoscale" "foo" { resource.TestCheckResourceAttr( "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.size", "c-2"), resource.TestCheckResourceAttr( - "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image", "547864"), + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "nyc3"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image"), resource.TestCheckResourceAttr( "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.with_droplet_agent", "true"), resource.TestCheckResourceAttr( @@ -235,9 +235,9 @@ data "digitalocean_droplet_autoscale" "foo" { resource.TestCheckResourceAttr( "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.size", "c-2"), resource.TestCheckResourceAttr( - "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image", "547864"), + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.region", "nyc3"), + resource.TestCheckResourceAttrSet( + "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.image"), resource.TestCheckResourceAttr( "data.digitalocean_droplet_autoscale.foo", "droplet_template.0.with_droplet_agent", "true"), resource.TestCheckResourceAttr( diff --git a/digitalocean/dropletautoscale/resource_droplet_autoscale.go b/digitalocean/dropletautoscale/resource_droplet_autoscale.go index 0dfefc02c..077305ec7 100644 --- a/digitalocean/dropletautoscale/resource_droplet_autoscale.go +++ b/digitalocean/dropletautoscale/resource_droplet_autoscale.go @@ -37,6 +37,7 @@ func ResourceDigitalOceanDropletAutoscale() *schema.Resource { }, "config": { Type: schema.TypeList, + MaxItems: 1, Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -81,6 +82,7 @@ func ResourceDigitalOceanDropletAutoscale() *schema.Resource { }, "droplet_template": { Type: schema.TypeList, + MaxItems: 1, Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -268,12 +270,23 @@ func resourceDigitalOceanDropletAutoscaleRead(ctx context.Context, d *schema.Res d.Set("name", pool.Name) d.Set("config", flattenConfig(pool.Config)) - d.Set("droplet_template", flattenTemplate(pool.DropletTemplate)) d.Set("current_utilization", flattenUtilization(pool.CurrentUtilization)) d.Set("status", pool.Status) d.Set("created_at", pool.CreatedAt.UTC().String()) d.Set("updated_at", pool.UpdatedAt.UTC().String()) + // Persist existing image specification (id/slug) if it exists + if t, ok := d.GetOk("droplet_template"); ok { + tList := t.([]interface{}) + if len(tList) > 0 { + tMap := tList[0].(map[string]interface{}) + if tMap["image"] != "" { + pool.DropletTemplate.Image = tMap["image"].(string) + } + } + } + d.Set("droplet_template", flattenTemplate(pool.DropletTemplate)) + return nil } diff --git a/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go b/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go index afc06f4e2..95565edaf 100644 --- a/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go +++ b/digitalocean/dropletautoscale/resource_droplet_autoscale_test.go @@ -53,9 +53,9 @@ func TestAccDigitalOceanDropletAutoscale_Static(t *testing.T) { resource.TestCheckResourceAttr( "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "nyc3"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image"), resource.TestCheckResourceAttr( "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), resource.TestCheckResourceAttr( @@ -101,9 +101,9 @@ func TestAccDigitalOceanDropletAutoscale_Static(t *testing.T) { resource.TestCheckResourceAttr( "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "nyc3"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image"), resource.TestCheckResourceAttr( "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), resource.TestCheckResourceAttr( @@ -165,9 +165,9 @@ func TestAccDigitalOceanDropletAutoscale_Dynamic(t *testing.T) { resource.TestCheckResourceAttr( "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "nyc3"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image"), resource.TestCheckResourceAttr( "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), resource.TestCheckResourceAttr( @@ -213,9 +213,9 @@ func TestAccDigitalOceanDropletAutoscale_Dynamic(t *testing.T) { resource.TestCheckResourceAttr( "digitalocean_droplet_autoscale.foobar", "droplet_template.0.size", "c-2"), resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "s2r1"), - resource.TestCheckResourceAttr( - "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image", "547864"), + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.region", "nyc3"), + resource.TestCheckResourceAttrSet( + "digitalocean_droplet_autoscale.foobar", "droplet_template.0.image"), resource.TestCheckResourceAttr( "digitalocean_droplet_autoscale.foobar", "droplet_template.0.with_droplet_agent", "true"), resource.TestCheckResourceAttr( @@ -319,8 +319,8 @@ resource "digitalocean_droplet_autoscale" "foobar" { droplet_template { size = "c-2" - region = "s2r1" - image = "547864" + region = "nyc3" + image = "ubuntu-24-04-x64" tags = [digitalocean_tag.foo.id, digitalocean_tag.bar.id] ssh_keys = [digitalocean_ssh_key.foo.id, digitalocean_ssh_key.bar.id] with_droplet_agent = true @@ -380,8 +380,8 @@ resource "digitalocean_droplet_autoscale" "foobar" { droplet_template { size = "c-2" - region = "s2r1" - image = "547864" + region = "nyc3" + image = "ubuntu-24-04-x64" tags = [digitalocean_tag.foo.id, digitalocean_tag.bar.id] ssh_keys = [digitalocean_ssh_key.foo.id, digitalocean_ssh_key.bar.id] with_droplet_agent = true From c757f07a71ba26757713cf232d5ddca536ebaf68 Mon Sep 17 00:00:00 2001 From: Aditya Saha Date: Thu, 5 Dec 2024 09:08:21 -0500 Subject: [PATCH 7/9] Add test sweeper --- digitalocean/dropletautoscale/sweep.go | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 digitalocean/dropletautoscale/sweep.go diff --git a/digitalocean/dropletautoscale/sweep.go b/digitalocean/dropletautoscale/sweep.go new file mode 100644 index 000000000..7e7fcb904 --- /dev/null +++ b/digitalocean/dropletautoscale/sweep.go @@ -0,0 +1,40 @@ +package dropletautoscale + +import ( + "context" + "log" + "strings" + + "github.com/digitalocean/godo" + "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/config" + "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/sweep" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("digitalocean_droplet_autoscale", &resource.Sweeper{ + Name: "digitalocean_droplet_autoscale", + F: sweepDropletAutoscale, + }) +} + +func sweepDropletAutoscale(region string) error { + meta, err := sweep.SharedConfigForRegion(region) + if err != nil { + return err + } + client := meta.(*config.CombinedConfig).GodoClient() + pools, _, err := client.DropletAutoscale.List(context.Background(), &godo.ListOptions{PerPage: 200}) + if err != nil { + return err + } + for _, pool := range pools { + if strings.HasPrefix(pool.Name, sweep.TestNamePrefix) { + log.Printf("Destroying droplet autoscale pool %s", pool.Name) + if _, err = client.DropletAutoscale.DeleteDangerous(context.Background(), pool.ID); err != nil { + return err + } + } + } + return nil +} From 27ab4b0bcc36dd1aea7972ee3bbbeb8a3bd6ad17 Mon Sep 17 00:00:00 2001 From: Aditya Saha Date: Thu, 5 Dec 2024 10:45:39 -0500 Subject: [PATCH 8/9] Add documentation --- docs/data-sources/droplet_autoscale.md | 41 ++++++++++ docs/resources/droplet_autoscale.md | 102 +++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 docs/data-sources/droplet_autoscale.md create mode 100644 docs/resources/droplet_autoscale.md diff --git a/docs/data-sources/droplet_autoscale.md b/docs/data-sources/droplet_autoscale.md new file mode 100644 index 000000000..2d81a68ce --- /dev/null +++ b/docs/data-sources/droplet_autoscale.md @@ -0,0 +1,41 @@ +--- +page_title: "DigitalOcean: digitalocean_droplet_autoscale" +subcategory: "Droplets" +--- + +# digitalocean\_droplet\_autoscale + +Get information on a Droplet Autoscale pool for use with other managed resources. This datasource provides all the +Droplet Autoscale pool properties as configured on the DigitalOcean account. This is useful if the Droplet Autoscale +pool in question is not managed by Terraform, or any of the relevant data would need to referenced in other managed +resources. + +## Example Usage + +Get the Droplet Autoscale pool by name: + +```hcl +data "digitalocean_droplet_autoscale" "my-imported-autoscale-pool" { + name = digitalocean_droplet_autoscale.my-existing-autoscale-pool.name +} +``` + +Get the Droplet Autoscale pool by ID: + +```hcl +data "digitalocean_droplet_autoscale" "my-imported-autoscale-pool" { + id = digitalocean_droplet_autoscale.my-existing-autoscale-pool.id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Optional) The name of Droplet Autoscale pool. +* `id` - (Optional) The ID of Droplet Autoscale pool. + +## Attributes Reference + +See the [Droplet Autoscale Resource](../resources/droplet_autoscale.md) for details on the +returned attributes - they are identical. diff --git a/docs/resources/droplet_autoscale.md b/docs/resources/droplet_autoscale.md new file mode 100644 index 000000000..a9888eef3 --- /dev/null +++ b/docs/resources/droplet_autoscale.md @@ -0,0 +1,102 @@ +--- +page_title: "DigitalOcean: digitalocean_droplet_autoscale" +subcategory: "Droplets" +--- + +# digitalocean\_droplet\_autoscale + +Provides a DigitalOcean Droplet Autoscale resource. This can be used to create, modify, +read and delete Droplet Autoscale pools. + +## Example Usage + +```hcl +resource "digitalocean_ssh_key" "my-ssh-key" { + name = "terraform-example" + public_key = file("/Users/terraform/.ssh/id_rsa.pub") +} + +resource "digitalocean_tag" "my-tag" { + name = "terraform-example" +} + +resource "digitalocean_droplet_autoscale" "my-autoscale-pool" { + name = "terraform-example" + + config { + min_instances = 10 + max_instances = 50 + target_cpu_utilization = 0.5 + target_memory_utilization = 0.5 + cooldown_minutes = 5 + } + + droplet_template { + size = "c-2" + region = "nyc3" + image = "ubuntu-24-04-x64" + tags = [digitalocean_tag.my-tag.id] + ssh_keys = [digitalocean_ssh_key.my-ssh-key.id] + with_droplet_agent = true + ipv6 = true + user_data = "\n#cloud-config\nruncmd:\n- apt-get update\n- apt-get install -y stress-ng\n" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Droplet Autoscale pool. +* `config` - (Required) The configuration parameters for Droplet Autoscale pool, the supported arguments are +documented below. +* `droplet_template` - (Required) The droplet template parameters for Droplet Autoscale pool, the supported arguments +are documented below. + +`config` supports the following: + +* `min_instances` - The minimum number of instances to maintain in the Droplet Autoscale pool. +* `max_instances` - The maximum number of instances to maintain in the Droplet Autoscale pool. +* `target_cpu_utilization` - The target average CPU load (in range `[0, 1]`) to maintain in the Droplet Autoscale pool. +* `target_memory_utilization` - The target average Memory load (in range `[0, 1]`) to maintain in the Droplet Autoscale +pool. +* `cooldown_minutes` - The cooldown duration between scaling events for the Droplet Autoscale pool. +* `target_number_instances` - The static number of instances to maintain in the pool Droplet Autoscale pool. This +argument cannot be used with any other config options. + +`droplet_template` supports the following: + +* `size` - (Required) Size slug of the Droplet Autoscale pool underlying resource(s). +* `region` - (Required) Region slug of the Droplet Autoscale pool underlying resource(s). +* `image` - (Required) Image slug of the Droplet Autoscale pool underlying resource(s). +* `tags` - List of tags to add to the Droplet Autoscale pool underlying resource(s). +* `ssh_keys` - (Required) SSH fingerprints to add to the Droplet Autoscale pool underlying resource(s). +* `vpc_uuid` - VPC UUID to create the Droplet Autoscale pool underlying resource(s). If not provided, this is inferred +from the specified `region` (default VPC). +* `with_droplet_agent` - Boolean flag to enable metric agent on the Droplet Autoscale pool underlying resource(s). The +metric agent enables collecting resource utilization metrics, which allows making resource based scaling decisions. +* `project_id` - Project UUID to create the Droplet Autoscale pool underlying resource(s). +* `ipv6` - Boolean flag to enable IPv6 networking on the Droplet Autoscale pool underlying resource(s). +* `user_data` - Custom user data that can be added to the Droplet Autoscale pool underlying resource(s). This can be a +cloud init script that user may configure to setup their application workload. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Droplet Autoscale pool. +* `current_utilization` - The current average resource utilization of the Droplet Autoscale pool, this attribute further +embeds `memory` and `cpu` attributes to respectively report utilization data. +* `status` - Droplet Autoscale pool health status; this reflects if the pool is currently healthy and ready to accept +traffic, or in an error state and needs user intervention. +* `created_at` - Created at timestamp for the Droplet Autoscale pool. +* `updated_at` - Updated at timestamp for the Droplet Autoscale pool. + +## Import + +Droplet Autoscale pools can be imported using the Droplet `id`, e.g. + +``` +terraform import digitalocean_droplet_autoscale.my-autoscale-pool 38e66834-d741-47ec-88e7-c70cbdcz0445 +``` From f7e92b97c9d419b270849d6fc16838db099e1bb6 Mon Sep 17 00:00:00 2001 From: Aditya Saha Date: Thu, 5 Dec 2024 10:55:09 -0500 Subject: [PATCH 9/9] Fix text typo --- docs/resources/droplet_autoscale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/resources/droplet_autoscale.md b/docs/resources/droplet_autoscale.md index a9888eef3..a0e3150b5 100644 --- a/docs/resources/droplet_autoscale.md +++ b/docs/resources/droplet_autoscale.md @@ -95,7 +95,7 @@ traffic, or in an error state and needs user intervention. ## Import -Droplet Autoscale pools can be imported using the Droplet `id`, e.g. +Droplet Autoscale pools can be imported using their `id`, e.g. ``` terraform import digitalocean_droplet_autoscale.my-autoscale-pool 38e66834-d741-47ec-88e7-c70cbdcz0445