diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 04f94cc34..313f74b18 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -174,7 +174,7 @@ func Provider() terraform.ResourceProvider { "aws_autoscaling_group": resourceAwsAutoscalingGroup(), "aws_autoscaling_notification": resourceAwsAutoscalingNotification(), "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), - "aws_autoscaling_schedule": resourceAwsAutoscalingSchedule(), + "aws_autoscaling_schedule": resourceAwsAutoscalingSchedule(), "aws_cloudformation_stack": resourceAwsCloudFormationStack(), "aws_cloudtrail": resourceAwsCloudTrail(), "aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(), diff --git a/builtin/providers/aws/resource_aws_autoscaling_schedule.go b/builtin/providers/aws/resource_aws_autoscaling_schedule.go index 4de57333c..b8a1135de 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_schedule.go +++ b/builtin/providers/aws/resource_aws_autoscaling_schedule.go @@ -3,12 +3,15 @@ package aws import ( "fmt" "log" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/autoscaling" "github.com/hashicorp/terraform/helper/schema" ) +const awsAutoscalingScheduleTimeLayout = "2006-01-02T15:04:05Z" + func resourceAwsAutoscalingSchedule() *schema.Resource { return &schema.Resource{ Create: resourceAwsAutoscalingScheduleCreate, @@ -17,6 +20,10 @@ func resourceAwsAutoscalingSchedule() *schema.Resource { Delete: resourceAwsAutoscalingScheduleDelete, Schema: map[string]*schema.Schema{ + "arn": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, "scheduled_action_name": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -28,14 +35,16 @@ func resourceAwsAutoscalingSchedule() *schema.Resource { ForceNew: true, }, "start_time": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateASGScheduleTimestamp, }, "end_time": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateASGScheduleTimestamp, }, "recurrence": &schema.Schema{ Type: schema.TypeString, @@ -63,25 +72,41 @@ func resourceAwsAutoscalingSchedule() *schema.Resource { func resourceAwsAutoscalingScheduleCreate(d *schema.ResourceData, meta interface{}) error { autoscalingconn := meta.(*AWSClient).autoscalingconn - params := autoscaling.PutScheduledUpdateGroupActionInput{ + params := &autoscaling.PutScheduledUpdateGroupActionInput{ AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)), ScheduledActionName: aws.String(d.Get("scheduled_action_name").(string)), } if attr, ok := d.GetOk("start_time"); ok { - params.StartTime = aws.Time() + t, err := time.Parse(awsAutoscalingScheduleTimeLayout, attr.(string)) + if err != nil { + return fmt.Errorf("Error Parsing AWS Autoscaling Group Schedule Start Time: %s", err.Error()) + } + params.StartTime = aws.Time(t) + } + + if attr, ok := d.GetOk("end_time"); ok { + t, err := time.Parse(awsAutoscalingScheduleTimeLayout, attr.(string)) + if err != nil { + return fmt.Errorf("Error Parsing AWS Autoscaling Group Schedule End Time: %s", err.Error()) + } + params.EndTime = aws.Time(t) + } + + if attr, ok := d.GetOk("recurrance"); ok { + params.Recurrence = aws.String(attr.(string)) } if attr, ok := d.GetOk("min_size"); ok { - params.MinSize = aws.Int(int64(attr.(int))) + params.MinSize = aws.Int64(int64(attr.(int))) } if attr, ok := d.GetOk("max_size"); ok { - params.MaxSize = aws.Int(int64(attr.(int))) + params.MaxSize = aws.Int64(int64(attr.(int))) } if attr, ok := d.GetOk("desired_capacity"); ok { - params.DesiredCapacity = aws.Int(int64(attr.(int))) + params.DesiredCapacity = aws.Int64(int64(attr.(int))) } log.Printf("[INFO] Creating Autoscaling Scheduled Action: %s", d.Get("scheduled_action_name").(string)) @@ -96,18 +121,32 @@ func resourceAwsAutoscalingScheduleCreate(d *schema.ResourceData, meta interface } func resourceAwsAutoscalingScheduleRead(d *schema.ResourceData, meta interface{}) error { + sa, err := resourceAwsASGScheduledActionRetrieve(d, meta) + if err != nil { + return err + } + + d.Set("autoscaling_group_name", sa.AutoScalingGroupName) + d.Set("arn", sa.ScheduledActionARN) + d.Set("desired_capacity", sa.DesiredCapacity) + d.Set("min_size", sa.MinSize) + d.Set("max_size", sa.MaxSize) + d.Set("recurrance", sa.Recurrence) + d.Set("start_time", sa.StartTime.Format(awsAutoscalingScheduleTimeLayout)) + d.Set("end_time", sa.EndTime.Format(awsAutoscalingScheduleTimeLayout)) + return nil } func resourceAwsAutoscalingScheduleDelete(d *schema.ResourceData, meta interface{}) error { autoscalingconn := meta.(*AWSClient).autoscalingconn - params := autoscaling.DeleteScheduledActionInput{ + params := &autoscaling.DeleteScheduledActionInput{ AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)), - ScheduledActionName: aws.String(d.Get("scheduled_action_name").(string)), + ScheduledActionName: aws.String(d.Id()), } - log.Printf("[INFO] Deleting Autoscaling Scheduled Action: %s", d.Get("scheduled_action_name").(string)) + log.Printf("[INFO] Deleting Autoscaling Scheduled Action: %s", d.Id()) _, err := autoscalingconn.DeleteScheduledAction(params) if err != nil { return fmt.Errorf("Error deleting Autoscaling Scheduled Action: %s", err.Error()) @@ -115,3 +154,36 @@ func resourceAwsAutoscalingScheduleDelete(d *schema.ResourceData, meta interface return nil } + +func resourceAwsASGScheduledActionRetrieve(d *schema.ResourceData, meta interface{}) (*autoscaling.ScheduledUpdateGroupAction, error) { + autoscalingconn := meta.(*AWSClient).autoscalingconn + + params := &autoscaling.DescribeScheduledActionsInput{ + AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)), + ScheduledActionNames: []*string{aws.String(d.Id())}, + } + + log.Printf("[INFO] Describing Autoscaling Scheduled Action: %+v", params) + actions, err := autoscalingconn.DescribeScheduledActions(params) + if err != nil { + return nil, fmt.Errorf("Error retrieving Autoscaling Scheduled Actions: %s", err) + } + + if len(actions.ScheduledUpdateGroupActions) != 1 || + *actions.ScheduledUpdateGroupActions[0].ScheduledActionName != d.Id() { + return nil, fmt.Errorf("Unable to find Autoscaling Scheduled Action: %#v", actions.ScheduledUpdateGroupActions) + } + + return actions.ScheduledUpdateGroupActions[0], nil +} + +func validateASGScheduleTimestamp(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + _, err := time.Parse(awsAutoscalingScheduleTimeLayout, value) + if err != nil { + errors = append(errors, fmt.Errorf( + "%q cannot be parsed as iso8601 Timestamp Format", value)) + } + + return +} diff --git a/builtin/providers/aws/resource_aws_autoscaling_schedule_test.go b/builtin/providers/aws/resource_aws_autoscaling_schedule_test.go new file mode 100644 index 000000000..3bd031526 --- /dev/null +++ b/builtin/providers/aws/resource_aws_autoscaling_schedule_test.go @@ -0,0 +1,117 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/autoscaling" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSAutoscalingSchedule_basic(t *testing.T) { + var schedule autoscaling.ScheduledUpdateGroupAction + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAutoscalingScheduleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSAutoscalingScheduleConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckScalingScheduleExists("aws_autoscaling_schedule.foobar", &schedule), + ), + }, + }, + }) +} + +func testAccCheckScalingScheduleExists(n string, policy *autoscaling.ScheduledUpdateGroupAction) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + autoScalingGroup, _ := rs.Primary.Attributes["autoscaling_group_name"] + conn := testAccProvider.Meta().(*AWSClient).autoscalingconn + params := &autoscaling.DescribeScheduledActionsInput{ + AutoScalingGroupName: aws.String(autoScalingGroup), + ScheduledActionNames: []*string{aws.String(rs.Primary.ID)}, + } + + resp, err := conn.DescribeScheduledActions(params) + if err != nil { + return err + } + if len(resp.ScheduledUpdateGroupActions) == 0 { + return fmt.Errorf("Scaling Schedule not found") + } + + return nil + } +} + +func testAccCheckAWSAutoscalingScheduleDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).autoscalingconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_autoscaling_schedule" { + continue + } + + autoScalingGroup, _ := rs.Primary.Attributes["autoscaling_group_name"] + params := &autoscaling.DescribeScheduledActionsInput{ + AutoScalingGroupName: aws.String(autoScalingGroup), + ScheduledActionNames: []*string{aws.String(rs.Primary.ID)}, + } + + resp, err := conn.DescribeScheduledActions(params) + + if err == nil { + if len(resp.ScheduledUpdateGroupActions) != 0 && + *resp.ScheduledUpdateGroupActions[0].ScheduledActionName == rs.Primary.ID { + return fmt.Errorf("Scaling Schedule Still Exists: %s", rs.Primary.ID) + } + } + } + + return nil +} + +var testAccAWSAutoscalingScheduleConfig = fmt.Sprintf(` +resource "aws_launch_configuration" "foobar" { + name = "terraform-test-foobar5" + image_id = "ami-21f78e11" + instance_type = "t1.micro" +} + +resource "aws_autoscaling_group" "foobar" { + availability_zones = ["us-west-2a"] + name = "terraform-test-foobar5" + max_size = 1 + min_size = 1 + health_check_grace_period = 300 + health_check_type = "ELB" + force_delete = true + termination_policies = ["OldestInstance"] + launch_configuration = "${aws_launch_configuration.foobar.name}" + tag { + key = "Foo" + value = "foo-bar" + propagate_at_launch = true + } +} + +resource "aws_autoscaling_schedule" "foobar" { + scheduled_action_name = "foobar" + min_size = 0 + max_size = 1 + desired_capacity = 0 + start_time = "2016-12-11T18:00:00Z" + end_time = "2016-12-12T06:00:00Z" + autoscaling_group_name = "${aws_autoscaling_group.foobar.name}" +} +`) diff --git a/website/source/docs/providers/aws/r/autoscaling_schedule.html.markdown b/website/source/docs/providers/aws/r/autoscaling_schedule.html.markdown new file mode 100644 index 000000000..4ad947293 --- /dev/null +++ b/website/source/docs/providers/aws/r/autoscaling_schedule.html.markdown @@ -0,0 +1,55 @@ +--- +layout: "aws" +page_title: "AWS: aws_autoscaling_schedule" +sidebar_current: "docs-aws-resource-autoscaling-schedule" +description: |- + Provides an AutoScaling Schedule resource. +--- + +# aws\_autoscaling\_schedule + +Provides an AutoScaling Schedule resource. + +## Example Usage +``` +resource "aws_autoscaling_group" "foobar" { + availability_zones = ["us-west-2a"] + name = "terraform-test-foobar5" + max_size = 1 + min_size = 1 + health_check_grace_period = 300 + health_check_type = "ELB" + force_delete = true + termination_policies = ["OldestInstance"] +} + +resource "aws_autoscaling_schedule" "foobar" { + scheduled_action_name = "foobar" + min_size = 0 + max_size = 1 + desired_capacity = 0 + start_time = "2016-12-11T18:00:00Z" + end_time = "2016-12-12T06:00:00Z" + autoscaling_group_name = "${aws_autoscaling_group.foobar.name}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `autoscaling_group_name` - (Required) The name or Amazon Resource Name (ARN) of the Auto Scaling group. +* `scheduled_action_name` - (Required) The name of this scaling action. +* `start_time` - (Optional) The time for this action to start, in "YYYY-MM-DDThh:mm:ssZ" format in UTC/GMT only (for example, 2014-06-01T00:00:00Z ). + If you try to schedule your action in the past, Auto Scaling returns an error message. +* `end_time` - (Optional) The time for this action to end, in "YYYY-MM-DDThh:mm:ssZ" format in UTC/GMT only (for example, 2014-06-01T00:00:00Z ). + If you try to schedule your action in the past, Auto Scaling returns an error messag +* `recurrence` - (Optional) The time when recurring future actions will start. Start time is specified by the user following the Unix cron syntax format. +* `min_size` - (Optional) The minimum size for the Auto Scaling group. +* `max_size` - (Optional) The maximum size for the Auto Scaling group. +* `desired_capacity` - (Optional) The number of EC2 instances that should be running in the group. + +~> **NOTE:** When `start_time` and `end_time` are specified with `recurrence` , they form the boundaries of when the recurring action will start and stop. + +## Attribute Reference +* `arn` - The ARN assigned by AWS to the autoscaling schedule. \ No newline at end of file diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index 1da203dca..c2df5bbf5 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -128,6 +128,10 @@ aws_autoscaling_policy + > + aws_autoscaling_schedule + + > aws_ebs_volume