From e8c609cca4b430dcee4823d2b418bb7f645e648e Mon Sep 17 00:00:00 2001 From: Xavier Salazar Date: Thu, 7 Nov 2019 14:37:32 -0800 Subject: [PATCH] Initial commit --- aws/opsworks_layers.go | 231 +++++++++++++++++- ...resource_aws_opsworks_custom_layer_test.go | 167 +++++++++++-- 2 files changed, 370 insertions(+), 28 deletions(-) diff --git a/aws/opsworks_layers.go b/aws/opsworks_layers.go index 6a95857b89e3..5f82b8d474f4 100644 --- a/aws/opsworks_layers.go +++ b/aws/opsworks_layers.go @@ -7,11 +7,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/structure" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/opsworks" - "github.com/hashicorp/terraform-plugin-sdk/helper/structure" ) // OpsWorks has a single concept of "layer" which represents several different @@ -162,7 +163,6 @@ func (lt *opsworksLayerType) SchemaResource() *schema.Resource { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "iops": { Type: schema.TypeInt, Optional: true, @@ -197,11 +197,120 @@ func (lt *opsworksLayerType) SchemaResource() *schema.Resource { }, }, }, + Set: func(v interface{}) int { m := v.(map[string]interface{}) return hashcode.String(m["mount_point"].(string)) }, }, + + "enable_load_based_autoscaling": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "load_based_autoscaling": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "downscaling": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "alarms": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "cpu_threshold": { + Type: schema.TypeFloat, + Optional: true, + Default: 30, + }, + "ignore_metrics_time": { + Type: schema.TypeInt, + Optional: true, + Default: 10, + ValidateFunc: validation.IntBetween(1, 100), + }, + "instance_count": { + Type: schema.TypeInt, + Optional: true, + Default: 1, + }, + "load_threshold": { + Type: schema.TypeFloat, + Optional: true, + Default: -1, + }, + "memory_threshold": { + Type: schema.TypeFloat, + Optional: true, + Default: -1, + }, + "thresholds_wait_time": { + Type: schema.TypeInt, + Optional: true, + Default: 10, + ValidateFunc: validation.IntBetween(1, 100), + }, + }, + }, + }, + "upscaling": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "alarms": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "cpu_threshold": { + Type: schema.TypeFloat, + Optional: true, + Default: 80, + }, + "ignore_metrics_time": { + Type: schema.TypeInt, + Optional: true, + Default: 5, + ValidateFunc: validation.IntBetween(1, 100), + }, + "instance_count": { + Type: schema.TypeInt, + Optional: true, + Default: 1, + }, + "load_threshold": { + Type: schema.TypeFloat, + Optional: true, + Default: -1, + }, + "memory_threshold": { + Type: schema.TypeFloat, + Optional: true, + Default: -1, + }, + "thresholds_wait_time": { + Type: schema.TypeInt, + Optional: true, + Default: 5, + ValidateFunc: validation.IntBetween(1, 100), + }, + }, + }, + }, + }, + }, + }, } if lt.CustomShortName { @@ -331,6 +440,21 @@ func (lt *opsworksLayerType) Read(d *schema.ResourceData, client *opsworks.OpsWo } } + /* get Autoscaling configuration */ + ascRequest := &opsworks.DescribeLoadBasedAutoScalingInput{ + LayerIds: []*string{ + aws.String(d.Id()), + }, + } + autoScalings, err := client.DescribeLoadBasedAutoScaling(ascRequest) + if err != nil { + return err + } + + if autoScalings.LoadBasedAutoScalingConfigurations != nil && len(autoScalings.LoadBasedAutoScalingConfigurations) != 0 && autoScalings.LoadBasedAutoScalingConfigurations[0] != nil { + lt.SetAutoscaling(d, autoScalings.LoadBasedAutoScalingConfigurations[0]) + } + return nil } @@ -372,6 +496,7 @@ func (lt *opsworksLayerType) Create(d *schema.ResourceData, client *opsworks.Ops layerId := *resp.LayerId d.SetId(layerId) + /* Configure ELB */ loadBalancer := aws.String(d.Get("elastic_load_balancer").(string)) if loadBalancer != nil && *loadBalancer != "" { log.Printf("[DEBUG] Attaching load balancer: %s", *loadBalancer) @@ -384,6 +509,14 @@ func (lt *opsworksLayerType) Create(d *schema.ResourceData, client *opsworks.Ops } } + /* Configure Autoscaling */ + if d.Get("load_based_autoscaling") != nil && d.Get("load_based_autoscaling.0.enable").(bool) { + _, err := client.SetLoadBasedAutoScaling(lt.Autoscaling(d)) + if err != nil { + return err + } + } + return lt.Read(d, client) } @@ -445,6 +578,12 @@ func (lt *opsworksLayerType) Update(d *schema.ResourceData, client *opsworks.Ops } } + /* Update Autoscaling */ + _, erra := client.SetLoadBasedAutoScaling(lt.Autoscaling(d)) + if erra != nil { + return erra + } + _, err := client.UpdateLayer(req) if err != nil { return err @@ -643,3 +782,91 @@ func (lt *opsworksLayerType) SetVolumeConfigurations(d *schema.ResourceData, v [ d.Set("ebs_volume", newValue) } + +func (lt *opsworksLayerType) Autoscaling(d *schema.ResourceData) *opsworks.SetLoadBasedAutoScalingInput { + autoScalingInput := &opsworks.SetLoadBasedAutoScalingInput{ + Enable: aws.Bool(d.Get("enable_load_based_autoscaling").(bool)), + LayerId: aws.String(d.Id()), + } + + if d.Get("load_based_autoscaling.0.downscaling") != nil { + autoScalingInput.DownScaling = &opsworks.AutoScalingThresholds{ + Alarms: expandStringList(d.Get("load_based_autoscaling.0.downscaling.0.alarms").([]interface{})), + CpuThreshold: aws.Float64(d.Get("load_based_autoscaling.0.downscaling.0.cpu_threshold").(float64)), + IgnoreMetricsTime: aws.Int64(int64(d.Get("load_based_autoscaling.0.downscaling.0.ignore_metrics_time").(int))), + InstanceCount: aws.Int64(int64(d.Get("load_based_autoscaling.0.downscaling.0.instance_count").(int))), + LoadThreshold: aws.Float64(d.Get("load_based_autoscaling.0.downscaling.0.load_threshold").(float64)), + MemoryThreshold: aws.Float64(d.Get("load_based_autoscaling.0.downscaling.0.memory_threshold").(float64)), + ThresholdsWaitTime: aws.Int64(int64(d.Get("load_based_autoscaling.0.downscaling.0.thresholds_wait_time").(int))), + } + } + + if d.Get("load_based_autoscaling.0.upscaling") != nil { + autoScalingInput.UpScaling = &opsworks.AutoScalingThresholds{ + Alarms: expandStringList(d.Get("load_based_autoscaling.0.upscaling.0.alarms").([]interface{})), + CpuThreshold: aws.Float64(d.Get("load_based_autoscaling.0.upscaling.0.cpu_threshold").(float64)), + IgnoreMetricsTime: aws.Int64(int64(d.Get("load_based_autoscaling.0.upscaling.0.ignore_metrics_time").(int))), + InstanceCount: aws.Int64(int64(d.Get("load_based_autoscaling.0.upscaling.0.instance_count").(int))), + LoadThreshold: aws.Float64(d.Get("load_based_autoscaling.0.upscaling.0.load_threshold").(float64)), + MemoryThreshold: aws.Float64(d.Get("load_based_autoscaling.0.upscaling.0.memory_threshold").(float64)), + ThresholdsWaitTime: aws.Int64(int64(d.Get("load_based_autoscaling.0.upscaling.0.thresholds_wait_time").(int))), + } + } + + return autoScalingInput +} + +func (lt *opsworksLayerType) SetAutoscaling(d *schema.ResourceData, as *opsworks.LoadBasedAutoScalingConfiguration) { + d.Set("enable_load_based_autoscaling", as.Enable) + d.Set("load_based_autoscaling.0.downscaling.0.alarms", flattenStringList(as.DownScaling.Alarms)) + + if as.DownScaling.CpuThreshold != nil { + d.Set("load_based_autoscaling.0.downscaling.0.cpu_threshold", as.DownScaling.CpuThreshold) + } + + if as.DownScaling.InstanceCount != nil { + d.Set("load_based_autoscaling.0.downscaling.0.instance_count", as.DownScaling.InstanceCount) + } + + if as.DownScaling.LoadThreshold != nil { + d.Set("load_based_autoscaling.0.downscaling.0.load_threshold", as.DownScaling.LoadThreshold) + } + + if as.DownScaling.MemoryThreshold != nil { + d.Set("load_based_autoscaling.0.downscaling.0.memory_threshold", as.DownScaling.MemoryThreshold) + } + + if as.DownScaling.ThresholdsWaitTime != nil { + d.Set("load_based_autoscaling.0.downscaling.0.thresholds_wait_time", as.DownScaling.ThresholdsWaitTime) + } + + if as.DownScaling.IgnoreMetricsTime != nil { + d.Set("load_based_autoscaling.0.downscaling.0.ignore_metrics_time", as.DownScaling.IgnoreMetricsTime) + } + + d.Set("load_based_autoscaling.0.upscaling.0.alarms", flattenStringList(as.UpScaling.Alarms)) + + if as.UpScaling.CpuThreshold != nil { + d.Set("load_based_autoscaling.0.upscaling.0.cpu_threshold", as.UpScaling.CpuThreshold) + } + + if as.UpScaling.InstanceCount != nil { + d.Set("load_based_autoscaling.0.upscaling.0.instance_count", as.UpScaling.InstanceCount) + } + + if as.UpScaling.LoadThreshold != nil { + d.Set("load_based_autoscaling.0.upscaling.0.load_threshold", as.UpScaling.LoadThreshold) + } + + if as.UpScaling.MemoryThreshold != nil { + d.Set("load_based_autoscaling.0.upscaling.0.memory_threshold", as.UpScaling.MemoryThreshold) + } + + if as.UpScaling.ThresholdsWaitTime != nil { + d.Set("load_based_autoscaling.0.upscaling.0.thresholds_wait_time", as.UpScaling.ThresholdsWaitTime) + } + + if as.UpScaling.IgnoreMetricsTime != nil { + d.Set("load_based_autoscaling.0.upscaling.0.ignore_metrics_time", as.UpScaling.IgnoreMetricsTime) + } +} diff --git a/aws/resource_aws_opsworks_custom_layer_test.go b/aws/resource_aws_opsworks_custom_layer_test.go index 7f1890e46919..6e4c2d7be6ee 100644 --- a/aws/resource_aws_opsworks_custom_layer_test.go +++ b/aws/resource_aws_opsworks_custom_layer_test.go @@ -50,8 +50,7 @@ func TestAccAWSOpsworksCustomLayer_basic(t *testing.T) { { Config: testAccAwsOpsworksCustomLayerConfigNoVpcCreate(stackName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSOpsworksCustomLayerExists( - "aws_opsworks_custom_layer.tf-acc", &opslayer), + testAccCheckAWSOpsworksCustomLayerExists("aws_opsworks_custom_layer.tf-acc", &opslayer), testAccCheckAWSOpsworksCreateLayerAttributes(&opslayer, stackName), resource.TestCheckResourceAttr( "aws_opsworks_custom_layer.tf-acc", "name", stackName, @@ -166,6 +165,68 @@ func TestAccAWSOpsworksCustomLayer_basic(t *testing.T) { }) } +func TestAccAWSOpsworksCustomLayer_autoscaling(t *testing.T) { + stackName := fmt.Sprintf("tf-%d", acctest.RandInt()) + var opslayer opsworks.Layer + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsOpsworksCustomLayerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsOpsworksCustomLayerAutoscalingGroup(stackName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSOpsworksCustomLayerExists("aws_opsworks_custom_layer.tf-acc", &opslayer), + testAccCheckAWSOpsworksCreateLayerAttributes(&opslayer, stackName), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "name", stackName, + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.enable", "true", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.downscaling.0.cpu_threshold", "20", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.downscaling.0.ignore_metrics_time", "15", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.downscaling.0.instance_count", "2", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.downscaling.0.load_threshold", "5", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.downscaling.0.memory_threshold", "20", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.downscaling.0.thresholds_wait_time", "30", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.upscaling.0.cpu_threshold", "80", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.upscaling.0.ignore_metrics_time", "15", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.upscaling.0.instance_count", "3", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.upscaling.0.load_threshold", "10", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.upscaling.0.memory_threshold", "80", + ), + resource.TestCheckResourceAttr( + "aws_opsworks_custom_layer.tf-acc", "load_based_autoscaling.0.upscaling.0.thresholds_wait_time", "30", + ), + ), + }, + }, + }) + +} + func testAccCheckAWSOpsworksCustomLayerExists( n string, opslayer *opsworks.Layer) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -316,26 +377,26 @@ resource "aws_security_group" "tf-ops-acc-layer2" { func testAccAwsOpsworksCustomLayerConfigNoVpcCreate(name string) string { return fmt.Sprintf(` resource "aws_opsworks_custom_layer" "tf-acc" { - stack_id = "${aws_opsworks_stack.tf-acc.id}" - name = "%s" - short_name = "tf-ops-acc-custom-layer" + stack_id = "${aws_opsworks_stack.tf-acc.id}" + name = "%s" + short_name = "tf-ops-acc-custom-layer" auto_assign_public_ips = true custom_security_group_ids = [ "${aws_security_group.tf-ops-acc-layer1.id}", "${aws_security_group.tf-ops-acc-layer2.id}", ] - drain_elb_on_shutdown = true + drain_elb_on_shutdown = true instance_shutdown_timeout = 300 system_packages = [ "git", "golang", ] ebs_volume { - type = "gp2" + type = "gp2" number_of_disks = 2 - mount_point = "/home" - size = 100 - raid_level = 0 + mount_point = "/home" + size = 100 + raid_level = 0 } } @@ -388,23 +449,24 @@ func testAccAwsOpsworksCustomLayerConfigUpdate(name string) string { resource "aws_security_group" "tf-ops-acc-layer3" { name = "tf-ops-acc-layer-%[1]s" ingress { - from_port = 8 - to_port = -1 - protocol = "icmp" + from_port = 8 + to_port = -1 + protocol = "icmp" cidr_blocks = ["0.0.0.0/0"] } } + resource "aws_opsworks_custom_layer" "tf-acc" { - stack_id = "${aws_opsworks_stack.tf-acc.id}" - name = "%[1]s" - short_name = "tf-ops-acc-custom-layer" + stack_id = "${aws_opsworks_stack.tf-acc.id}" + name = "%[1]s" + short_name = "tf-ops-acc-custom-layer" auto_assign_public_ips = true custom_security_group_ids = [ "${aws_security_group.tf-ops-acc-layer1.id}", "${aws_security_group.tf-ops-acc-layer2.id}", "${aws_security_group.tf-ops-acc-layer3.id}", ] - drain_elb_on_shutdown = false + drain_elb_on_shutdown = false instance_shutdown_timeout = 120 system_packages = [ "git", @@ -412,19 +474,19 @@ resource "aws_opsworks_custom_layer" "tf-acc" { "subversion", ] ebs_volume { - type = "gp2" + type = "gp2" number_of_disks = 2 - mount_point = "/home" - size = 100 - raid_level = 0 + mount_point = "/home" + size = 100 + raid_level = 0 } ebs_volume { - type = "io1" + type = "io1" number_of_disks = 4 - mount_point = "/var" - size = 100 - raid_level = 1 - iops = 3000 + mount_point = "/var" + size = 100 + raid_level = 1 + iops = 3000 } custom_json = "{\"layer_key\": \"layer_value2\"}" } @@ -434,3 +496,56 @@ resource "aws_opsworks_custom_layer" "tf-acc" { %s `, name, testAccAwsOpsworksStackConfigNoVpcCreate(name), testAccAwsOpsworksCustomLayerSecurityGroups(name)) } + +func testAccAwsOpsworksCustomLayerAutoscalingGroup(name string) string { + return fmt.Sprintf(` +resource "aws_opsworks_custom_layer" "tf-acc" { + stack_id = "${aws_opsworks_stack.tf-acc.id}" + name = "%s" + short_name = "tf-ops-acc-custom-layer" + auto_assign_public_ips = true + custom_security_group_ids = [ + "${aws_security_group.tf-ops-acc-layer1.id}", + "${aws_security_group.tf-ops-acc-layer2.id}", + ] + drain_elb_on_shutdown = true + instance_shutdown_timeout = 300 + system_packages = [ + "git", + "golang", + ] + ebs_volume { + type = "gp2" + number_of_disks = 2 + mount_point = "/home" + size = 100 + raid_level = 0 + } + + load_based_autoscaling { + enable = true + downscaling { + cpu_threshold = 20 + ignore_metrics_time = 15 + instance_count = 2 + load_threshold = 5 + memory_threshold = 20 + thresholds_wait_time = 30 + } + + upscaling { + cpu_threshold = 80 + ignore_metrics_time = 15 + instance_count = 3 + load_threshold = 10 + memory_threshold = 80 + thresholds_wait_time = 30 + } + } +} + +%s + +%s +`, name, testAccAwsOpsworksStackConfigNoVpcCreate(name), testAccAwsOpsworksCustomLayerSecurityGroups(name)) +}