Skip to content

Commit

Permalink
Update Application Auto Scaling to support scaling an Amazon EC2 Spot…
Browse files Browse the repository at this point in the history
… fleet. (#8697)

* provider/aws: Update Application Auto Scaling service model

  - Add support for automatically scaling an Amazon EC2 Spot fleet.

* Remove duplicate policy_type check.

* Test creating a scalable target for a splot fleet request.

* Test creating a scaling policy for a splot fleet request.

* Update resource docs to support scaling an Amazon EC2 Spot fleet.

  - aws_appautoscaling_policy
  - aws_appautoscaling_target

* Remove arn attribute from aws_appautoscaling_target

  - No arn is generated or returned for this resource.

* Remove optional name attribute from aws_appautoscaling_target

  - ScalableTargets do not have a name
  - I think this was copied from aws_appautoscaling_policy

* AWS Application Autoscaling resource documentation tweaks

  - include a target resource in the policy example
  - sort attributes by alpha
  - fixup markdown
  - add spaces to test config
  • Loading branch information
niclic authored and stack72 committed Feb 2, 2017
1 parent e2603be commit b30ef0f
Show file tree
Hide file tree
Showing 8 changed files with 374 additions and 79 deletions.
20 changes: 8 additions & 12 deletions builtin/providers/aws/resource_aws_appautoscaling_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ func resourceAwsAppautoscalingPolicy() *schema.Resource {
Required: true,
},
"scalable_dimension": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs:service:DesiredCount",
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateAppautoscalingScalableDimension,
},
"service_namespace": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs",
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateAppautoscalingServiceNamespace,
},
"adjustment_type": &schema.Schema{
Type: schema.TypeString,
Expand Down Expand Up @@ -258,10 +258,6 @@ func getAwsAppautoscalingPutScalingPolicyInput(d *schema.ResourceData) (applicat
params.ServiceNamespace = aws.String(v.(string))
}

if v, ok := d.GetOk("policy_type"); ok {
params.PolicyType = aws.String(v.(string))
}

if v, ok := d.GetOk("scalable_dimension"); ok {
params.ScalableDimension = aws.String(v.(string))
}
Expand Down
119 changes: 119 additions & 0 deletions builtin/providers/aws/resource_aws_appautoscaling_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ func TestAccAWSAppautoScalingPolicy_basic(t *testing.T) {
})
}

func TestAccAWSAppautoScalingPolicy_spotFleetRequest(t *testing.T) {
var policy applicationautoscaling.ScalingPolicy

randPolicyName := fmt.Sprintf("test-appautoscaling-policy-%s", acctest.RandString(5))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAppautoscalingPolicyDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSAppautoscalingPolicySpotFleetRequestConfig(randPolicyName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAppautoscalingPolicyExists("aws_appautoscaling_policy.test", &policy),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.test", "name", randPolicyName),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.test", "service_namespace", "ec2"),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.test", "scalable_dimension", "ec2:spot-fleet-request:TargetCapacity"),
),
},
},
})
}

func testAccCheckAWSAppautoscalingPolicyExists(n string, policy *applicationautoscaling.ScalingPolicy) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand Down Expand Up @@ -171,3 +194,99 @@ resource "aws_appautoscaling_policy" "foobar_simple" {
}
`, randClusterName, randClusterName, randClusterName, randPolicyName)
}

func testAccAWSAppautoscalingPolicySpotFleetRequestConfig(
randPolicyName string) string {
return fmt.Sprintf(`
resource "aws_iam_role" "fleet_role" {
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"spotfleet.amazonaws.com",
"ec2.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "fleet_role_policy" {
role = "${aws_iam_role.fleet_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetRole"
}
resource "aws_spot_fleet_request" "test" {
iam_fleet_role = "${aws_iam_role.fleet_role.arn}"
spot_price = "0.005"
target_capacity = 2
valid_until = "2019-11-04T20:44:20Z"
terminate_instances_with_expiration = true
launch_specification {
instance_type = "m3.medium"
ami = "ami-d06a90b0"
}
}
resource "aws_iam_role" "autoscale_role" {
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "application-autoscaling.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "autoscale_role_policy_a" {
role = "${aws_iam_role.autoscale_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetRole"
}
resource "aws_iam_role_policy_attachment" "autoscale_role_policy_b" {
role = "${aws_iam_role.autoscale_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetAutoscaleRole"
}
resource "aws_appautoscaling_target" "test" {
service_namespace = "ec2"
resource_id = "spot-fleet-request/${aws_spot_fleet_request.test.id}"
scalable_dimension = "ec2:spot-fleet-request:TargetCapacity"
role_arn = "${aws_iam_role.autoscale_role.arn}"
min_capacity = 1
max_capacity = 3
}
resource "aws_appautoscaling_policy" "test" {
name = "%s"
service_namespace = "ec2"
resource_id = "spot-fleet-request/${aws_spot_fleet_request.test.id}"
scalable_dimension = "ec2:spot-fleet-request:TargetCapacity"
adjustment_type = "ChangeInCapacity"
cooldown = 60
metric_aggregation_type = "Average"
step_adjustment {
metric_interval_lower_bound = 0
scaling_adjustment = 1
}
depends_on = ["aws_appautoscaling_target.test"]
}
`, randPolicyName)
}
35 changes: 8 additions & 27 deletions builtin/providers/aws/resource_aws_appautoscaling_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,6 @@ func resourceAwsAppautoscalingTarget() *schema.Resource {
Delete: resourceAwsAppautoscalingTargetDelete,

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
// https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1862-L1873
value := v.(string)
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 255 characters", k))
}
return
},
},
"arn": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"max_capacity": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Expand All @@ -60,16 +41,16 @@ func resourceAwsAppautoscalingTarget() *schema.Resource {
ForceNew: true,
},
"scalable_dimension": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs:service:DesiredCount",
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateAppautoscalingScalableDimension,
},
"service_namespace": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs",
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateAppautoscalingServiceNamespace,
},
},
}
Expand Down
107 changes: 105 additions & 2 deletions builtin/providers/aws/resource_aws_appautoscaling_target_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ func TestAccAWSAppautoScalingTarget_basic(t *testing.T) {
})
}

func TestAccAWSAppautoScalingTarget_spotFleetRequest(t *testing.T) {
var target applicationautoscaling.ScalableTarget

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "aws_appautoscaling_target.test",
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAppautoscalingTargetDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSAppautoscalingTargetSpotFleetRequestConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAppautoscalingTargetExists("aws_appautoscaling_target.test", &target),
resource.TestCheckResourceAttr("aws_appautoscaling_target.test", "service_namespace", "ec2"),
resource.TestCheckResourceAttr("aws_appautoscaling_target.test", "scalable_dimension", "ec2:spot-fleet-request:TargetCapacity"),
),
},
},
})
}

func testAccCheckAWSAppautoscalingTargetDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn

Expand Down Expand Up @@ -174,6 +195,7 @@ EOF
resource "aws_ecs_cluster" "foo" {
name = "%s"
}
resource "aws_ecs_task_definition" "task" {
family = "foobar"
container_definitions = <<EOF
Expand All @@ -188,6 +210,7 @@ resource "aws_ecs_task_definition" "task" {
]
EOF
}
resource "aws_ecs_service" "service" {
name = "foobar"
cluster = "${aws_ecs_cluster.foo.id}"
Expand All @@ -197,11 +220,12 @@ resource "aws_ecs_service" "service" {
deployment_maximum_percent = 200
deployment_minimum_healthy_percent = 50
}
resource "aws_appautoscaling_target" "bar" {
service_namespace = "ecs"
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
scalable_dimension = "ecs:service:DesiredCount"
role_arn = "${aws_iam_role.autoscale_role.arn}"
role_arn = "${aws_iam_role.autoscale_role.arn}"
min_capacity = 1
max_capacity = 3
}
Expand Down Expand Up @@ -266,6 +290,7 @@ EOF
resource "aws_ecs_cluster" "foo" {
name = "%s"
}
resource "aws_ecs_task_definition" "task" {
family = "foobar"
container_definitions = <<EOF
Expand All @@ -280,6 +305,7 @@ resource "aws_ecs_task_definition" "task" {
]
EOF
}
resource "aws_ecs_service" "service" {
name = "foobar"
cluster = "${aws_ecs_cluster.foo.id}"
Expand All @@ -289,13 +315,90 @@ resource "aws_ecs_service" "service" {
deployment_maximum_percent = 200
deployment_minimum_healthy_percent = 50
}
resource "aws_appautoscaling_target" "bar" {
service_namespace = "ecs"
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
scalable_dimension = "ecs:service:DesiredCount"
role_arn = "${aws_iam_role.autoscale_role.arn}"
role_arn = "${aws_iam_role.autoscale_role.arn}"
min_capacity = 2
max_capacity = 8
}
`, randClusterName, randClusterName, randClusterName)
}

var testAccAWSAppautoscalingTargetSpotFleetRequestConfig = fmt.Sprintf(`
resource "aws_iam_role" "fleet_role" {
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"spotfleet.amazonaws.com",
"ec2.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "fleet_role_policy" {
role = "${aws_iam_role.fleet_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetRole"
}
resource "aws_spot_fleet_request" "test" {
iam_fleet_role = "${aws_iam_role.fleet_role.arn}"
spot_price = "0.005"
target_capacity = 2
valid_until = "2019-11-04T20:44:20Z"
terminate_instances_with_expiration = true
launch_specification {
instance_type = "m3.medium"
ami = "ami-d06a90b0"
}
}
resource "aws_iam_role" "autoscale_role" {
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "application-autoscaling.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "autoscale_role_policy_a" {
role = "${aws_iam_role.autoscale_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetRole"
}
resource "aws_iam_role_policy_attachment" "autoscale_role_policy_b" {
role = "${aws_iam_role.autoscale_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetAutoscaleRole"
}
resource "aws_appautoscaling_target" "test" {
service_namespace = "ec2"
resource_id = "spot-fleet-request/${aws_spot_fleet_request.test.id}"
scalable_dimension = "ec2:spot-fleet-request:TargetCapacity"
role_arn = "${aws_iam_role.autoscale_role.arn}"
min_capacity = 1
max_capacity = 3
}
`)
Loading

0 comments on commit b30ef0f

Please sign in to comment.