Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Application Auto Scaling to support scaling an Amazon EC2 Spot fleet. #8697

Merged
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are changing these from Optional to required, then we may need to verify that the default value from before ecs:service:DesiredCount is actually correct - otherwise we are breaking backwards compatibility

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecs:service:DesiredCount is still a valid value.

Required: true,
ForceNew: true,
ValidateFunc: validateAppautoscalingScalableDimension,
},
"service_namespace": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs",
ForceNew: true,
Type: schema.TypeString,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are changing these from Optional to required, then we may need to verify that the default value from before ecs is actually correct - otherwise we are breaking backwards compatibility

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecs is still a valid value.

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is policy_type being removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is duplicated.

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{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we removing the name here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no name attribute for aws_appautoscaling_target. It's cut and paste from aws_appautoscaling_policy. The documentation even refers to policy. See commit message.

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the defaults in place from pre this PR correct here? Otherwise we are breaking backwards compatibility

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecs:service:DesiredCount is still a valid value.

Required: true,
ForceNew: true,
ValidateFunc: validateAppautoscalingScalableDimension,
},
"service_namespace": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs",
ForceNew: true,
Type: schema.TypeString,
Required: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the defaults in place from pre this PR correct here? Otherwise we are breaking backwards compatibility

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecs is still a valid value.

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