From 297c3a00675cff62fb39a1a3eb07d88c705903db Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Sun, 28 Oct 2018 12:44:00 +0000 Subject: [PATCH 1/6] resource/aws_capacity_reservation: New resource --- aws/provider.go | 1 + aws/resource_aws_capacity_reservation.go | 278 ++++++++++++++++++ aws/resource_aws_capacity_reservation_test.go | 175 +++++++++++ website/docs/r/capacity_reservation.markdown | 52 ++++ 4 files changed, 506 insertions(+) create mode 100644 aws/resource_aws_capacity_reservation.go create mode 100644 aws/resource_aws_capacity_reservation_test.go create mode 100644 website/docs/r/capacity_reservation.markdown diff --git a/aws/provider.go b/aws/provider.go index cc63f5bb152a..5802e1ca1981 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -325,6 +325,7 @@ func Provider() terraform.ResourceProvider { "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), "aws_autoscaling_schedule": resourceAwsAutoscalingSchedule(), "aws_budgets_budget": resourceAwsBudgetsBudget(), + "aws_capacity_reservation": resourceAwsCapacityReservation(), "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), "aws_cloudformation_stack": resourceAwsCloudFormationStack(), "aws_cloudfront_distribution": resourceAwsCloudFrontDistribution(), diff --git a/aws/resource_aws_capacity_reservation.go b/aws/resource_aws_capacity_reservation.go new file mode 100644 index 000000000000..5a0e4aafb662 --- /dev/null +++ b/aws/resource_aws_capacity_reservation.go @@ -0,0 +1,278 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsCapacityReservation() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsCapacityReservationCreate, + Read: resourceAwsCapacityReservationRead, + Update: resourceAwsCapacityReservationUpdate, + Delete: resourceAwsCapacityReservationDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "availability_zone": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "ebs_optimized": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + "end_date": { + Type: schema.TypeString, + Optional: true, + }, + "end_date_type": { + Type: schema.TypeString, + Optional: true, + Default: ec2.EndDateTypeUnlimited, + ValidateFunc: validation.StringInSlice([]string{ + ec2.EndDateTypeUnlimited, + ec2.EndDateTypeLimited, + }, false), + }, + "ephemeral_storage": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + "instance_count": { + Type: schema.TypeInt, + Required: true, + }, + "instance_match_criteria": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: ec2.InstanceMatchCriteriaOpen, + ValidateFunc: validation.StringInSlice([]string{ + ec2.InstanceMatchCriteriaOpen, + ec2.InstanceMatchCriteriaTargeted, + }, false), + }, + "instance_platform": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + ec2.CapacityReservationInstancePlatformLinuxUnix, + ec2.CapacityReservationInstancePlatformRedHatEnterpriseLinux, + ec2.CapacityReservationInstancePlatformSuselinux, + ec2.CapacityReservationInstancePlatformWindows, + ec2.CapacityReservationInstancePlatformWindowswithSqlserver, + ec2.CapacityReservationInstancePlatformWindowswithSqlserverEnterprise, + ec2.CapacityReservationInstancePlatformWindowswithSqlserverStandard, + ec2.CapacityReservationInstancePlatformWindowswithSqlserverWeb, + }, false), + }, + "instance_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "tags": tagsSchema(), + "tenancy": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: ec2.CapacityReservationTenancyDefault, + ValidateFunc: validation.StringInSlice([]string{ + ec2.CapacityReservationTenancyDefault, + ec2.CapacityReservationTenancyDedicated, + }, false), + }, + }, + } +} + +func resourceAwsCapacityReservationCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + opts := &ec2.CreateCapacityReservationInput{ + AvailabilityZone: aws.String(d.Get("availability_zone").(string)), + InstanceCount: aws.Int64(int64(d.Get("instance_count").(int))), + InstancePlatform: aws.String(d.Get("instance_platform").(string)), + InstanceType: aws.String(d.Get("instance_type").(string)), + } + + if v, ok := d.GetOk("ebs_optimized"); ok { + opts.EbsOptimized = aws.Bool(v.(bool)) + } + + if v, ok := d.GetOk("end_date"); ok { + t, err := time.Parse(time.RFC3339, v.(string)) + if err != nil { + return fmt.Errorf("Error parsing capacity reservation end date: %s", err.Error()) + } + opts.EndDate = aws.Time(t) + } + + if v, ok := d.GetOk("end_date_type"); ok { + opts.EndDateType = aws.String(v.(string)) + } + + if v, ok := d.GetOk("ephemeral_storage"); ok { + opts.EphemeralStorage = aws.Bool(v.(bool)) + } + + if v, ok := d.GetOk("instance_match_criteria"); ok { + opts.InstanceMatchCriteria = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tenancy"); ok { + opts.Tenancy = aws.String(v.(string)) + } + + restricted := meta.(*AWSClient).IsChinaCloud() + if !restricted { + + tagsSpec := make([]*ec2.TagSpecification, 0) + + if v, ok := d.GetOk("tags"); ok { + tags := tagsFromMap(v.(map[string]interface{})) + + spec := &ec2.TagSpecification{ + // There is no constant in the SDK for this resource type + ResourceType: aws.String("capacity-reservation"), + Tags: tags, + } + + tagsSpec = append(tagsSpec, spec) + } + + if len(tagsSpec) > 0 { + opts.TagSpecifications = tagsSpec + } + } + + log.Printf("[DEBUG] Capacity reservation: %s", opts) + + out, err := conn.CreateCapacityReservation(opts) + if err != nil { + return fmt.Errorf("Error creating capacity reservation: %s", err) + } + d.SetId(*out.CapacityReservation.CapacityReservationId) + return resourceAwsCapacityReservationRead(d, meta) +} + +func resourceAwsCapacityReservationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + resp, err := conn.DescribeCapacityReservations(&ec2.DescribeCapacityReservationsInput{ + CapacityReservationIds: []*string{aws.String(d.Id())}, + }) + + if err != nil { + // TODO: Check if error is raised if capacity reservation has gone + if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" { + d.SetId("") + return nil + } + + // Some other error, report it + return err + } + + // If nothing was found, then return no state + if len(resp.CapacityReservations) == 0 { + d.SetId("") + return nil + } + + reservation := resp.CapacityReservations[0] + + d.Set("availability_zone", reservation.AvailabilityZone) + d.Set("ebs_optimized", reservation.EbsOptimized) + d.Set("end_date", reservation.EndDate) + d.Set("end_date_type", reservation.EndDateType) + d.Set("ephemeral_storage", reservation.EphemeralStorage) + d.Set("instance_count", reservation.TotalInstanceCount) + d.Set("instance_match_criteria", reservation.InstanceMatchCriteria) + d.Set("instance_platform", reservation.InstancePlatform) + d.Set("instance_type", reservation.InstanceType) + d.Set("tags", tagsToMap(reservation.Tags)) + d.Set("tenancy", reservation.Tenancy) + + return nil +} + +func resourceAwsCapacityReservationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + d.Partial(true) + restricted := meta.(*AWSClient).IsChinaCloud() + + if d.HasChange("tags") { + if !d.IsNewResource() || restricted { + if err := setTags(conn, d); err != nil { + return err + } else { + d.SetPartial("tags") + } + } + } + + d.Partial(false) + + opts := &ec2.ModifyCapacityReservationInput{ + CapacityReservationId: aws.String(d.Id()), + } + + if d.HasChange("end_date") { + if v, ok := d.GetOk("end_date"); ok { + t, err := time.Parse(time.RFC3339, v.(string)) + if err != nil { + return fmt.Errorf("Error parsing capacity reservation end date: %s", err.Error()) + } + opts.EndDate = aws.Time(t) + } + } + + if d.HasChange("end_date_type") { + opts.EndDateType = aws.String(d.Get("end_date_type").(string)) + } + + if d.HasChange("instance_count") { + opts.InstanceCount = aws.Int64(int64(d.Get("instance_count").(int))) + } + + log.Printf("[DEBUG] Capacity reservation: %s", opts) + + _, err := conn.ModifyCapacityReservation(opts) + if err != nil { + return fmt.Errorf("Error modifying capacity reservation: %s", err) + } + return resourceAwsCapacityReservationRead(d, meta) +} + +func resourceAwsCapacityReservationDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + opts := &ec2.CancelCapacityReservationInput{ + CapacityReservationId: aws.String(d.Id()), + } + + _, err := conn.CancelCapacityReservation(opts) + if err != nil { + return fmt.Errorf("Error cancelling capacity reservation: %s", err) + } + + return nil +} diff --git a/aws/resource_aws_capacity_reservation_test.go b/aws/resource_aws_capacity_reservation_test.go new file mode 100644 index 000000000000..5ef0bbd2f621 --- /dev/null +++ b/aws/resource_aws_capacity_reservation_test.go @@ -0,0 +1,175 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +// func init() { +// resource.AddTestSweepers("aws_capacity_reservation", &resource.Sweeper{ +// Name: "aws_capacity_reservation", +// F: testSweepCapacityReservations, +// }) +// } + +// func testSweepInstances(region string) error { +// client, err := sharedClientForRegion(region) +// if err != nil { +// return fmt.Errorf("error getting client: %s", err) +// } +// conn := client.(*AWSClient).ec2conn + +// err = conn.DescribeInstancesPages(&ec2.DescribeInstancesInput{}, func(page *ec2.DescribeInstancesOutput, isLast bool) bool { +// if len(page.Reservations) == 0 { +// log.Print("[DEBUG] No EC2 Instances to sweep") +// return false +// } + +// for _, reservation := range page.Reservations { +// for _, instance := range reservation.Instances { +// var nameTag string +// id := aws.StringValue(instance.InstanceId) + +// for _, instanceTag := range instance.Tags { +// if aws.StringValue(instanceTag.Key) == "Name" { +// nameTag = aws.StringValue(instanceTag.Value) +// break +// } +// } + +// if !strings.HasPrefix(nameTag, "tf-acc-test-") { +// log.Printf("[INFO] Skipping EC2 Instance: %s", id) +// continue +// } + +// log.Printf("[INFO] Terminating EC2 Instance: %s", id) +// err := awsTerminateInstance(conn, id, 5*time.Minute) +// if err != nil { +// log.Printf("[ERROR] Error terminating EC2 Instance (%s): %s", id, err) +// } +// } +// } +// return !isLast +// }) +// if err != nil { +// if testSweepSkipSweepError(err) { +// log.Printf("[WARN] Skipping EC2 Instance sweep for %s: %s", region, err) +// return nil +// } +// return fmt.Errorf("Error retrieving EC2 Instances: %s", err) +// } + +// return nil +// } + +func TestAccAWSCapacityReservation_importBasic(t *testing.T) { + resourceName := "aws_capacity_reservation.default" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCapacityReservationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCapacityReservationConfig, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSCapacityReservation_basic(t *testing.T) { + var cr ec2.CapacityReservation + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCapacityReservationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCapacityReservationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckCapacityReservationExists("aws_capacity_reservation.default", &cr), + resource.TestCheckResourceAttr("aws_capacity_reservation.default", "instance_type", "t2.micro"), + ), + }, + }, + }) +} + +func testAccCheckCapacityReservationExists(resourceName string, cr *ec2.CapacityReservation) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).ec2conn + resp, err := conn.DescribeCapacityReservations(&ec2.DescribeCapacityReservationsInput{ + CapacityReservationIds: []*string{aws.String(rs.Primary.ID)}, + }) + + if err != nil { + return fmt.Errorf("DescribeCapacityReservations error: %v", err) + } + + if len(resp.CapacityReservations) > 0 { + *cr = *resp.CapacityReservations[0] + return nil + } + + return fmt.Errorf("Capacity Reservation not found") + } +} + +func testAccCheckCapacityReservationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_capacity_reservation" { + continue + } + + // Try to find the resource + resp, err := conn.DescribeCapacityReservations(&ec2.DescribeCapacityReservationsInput{ + CapacityReservationIds: []*string{aws.String(rs.Primary.ID)}, + }) + if err == nil { + for _, r := range resp.CapacityReservations { + if *r.State != "cancelled" { + return fmt.Errorf("Found uncancelled Capacity Reservation: %s", r) + } + } + } + + return err + } + + return nil + +} + +const testAccCapacityReservationConfig = ` +resource "aws_capacity_reservation" "default" { + instance_type = "t2.micro" + instance_platform = "Linux/UNIX" + availability_zone = "us-west-2a" + instance_count = 1 + + tags { + Name = "Foo" + } +} +` diff --git a/website/docs/r/capacity_reservation.markdown b/website/docs/r/capacity_reservation.markdown new file mode 100644 index 000000000000..dc99e7e34b76 --- /dev/null +++ b/website/docs/r/capacity_reservation.markdown @@ -0,0 +1,52 @@ +--- +layout: "aws" +page_title: "AWS: aws_capacity_reservaton" +sidebar_current: "docs-aws-resource-capacity-reservation" +description: |- + Provides an EC2 Capacity Reservation. This allows you to reserve capacity for your Amazon EC2 instances in a specific Availability Zone for any duration. +--- + +# aws_capacity_reservaton + +Provides an EC2 Capacity Reservation. This allows you to reserve capacity for your Amazon EC2 instances in a specific Availability Zone for any duration. + +## Example Usage + +```hcl +resource "aws_capacity_reservation" "default" { + instance_type = "t2.micro" + instance_platform = "Linux/UNIX" + availability_zone = "eu-west-1a" + instance_count = 1 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `availability_zone` - (Required) The Availability Zone in which to create the Capacity Reservation. +* `ebs_optimized` - (Optional) Indicates whether the Capacity Reservation supports EBS-optimized instances. +* `end_date` - (Optional) The date and time at which the Capacity Reservation expires. When a Capacity Reservation expires, the reserved capacity is released and you can no longer launch instances into it. +* `end_date_type` - (Optional) Indicates the way in which the Capacity Reservation ends. Specify either `unlimited` or `limited`. +* `ephemeral_storage` - (Optional) Indicates whether the Capacity Reservation supports instances with temporary, block-level storage. +* `instance_count` - (Required) The number of instances for which to reserve capacity. +* `instance_match_criteria` - (Optional) Indicates the type of instance launches that the Capacity Reservation accepts. Specify either `open` or `targeted`. +* `instance_platform` - (Required) The type of operating system for which to reserve capacity. Valid options are `Linux/UNIX`, `Red Hat Enterprise Linux`, `SUSE Linux`, `Windows`, `Windows with SQL Server`, `Windows with SQL Server Enterprise`, `Windows with SQL Server Standard` or `Windows with SQL Server Web`. +* `instance_type` - (Required) The instance type for which to reserve capacity. +* `tags` - (Optional) A mapping of tags to assign to the resource. +* `tenancy` - (Optional) Indicates the tenancy of the Capacity Reservation. Specify either `default` or `dedicated`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The Capacity Reservation ID. + +## Import + +Capacity Reservations can be imported using the `id`, e.g. + +``` +$ terraform import aws_capacity_reservaton.web cr-0123456789abcdef0 +``` From d14bb03760fb3efde86991c1f34b4fa4fb0c0aaf Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Tue, 30 Oct 2018 15:35:39 +0000 Subject: [PATCH 2/6] Rename `aws_capacity_reservation` to `aws_ec2_capacity_reservation` --- aws/provider.go | 2 +- ... resource_aws_ec2_capacity_reservation.go} | 22 +++---- ...urce_aws_ec2_capacity_reservation_test.go} | 57 +++++++++++++++++++ ...down => ec2_capacity_reservation.markdown} | 10 ++-- 4 files changed, 74 insertions(+), 17 deletions(-) rename aws/{resource_aws_capacity_reservation.go => resource_aws_ec2_capacity_reservation.go} (90%) rename aws/{resource_aws_capacity_reservation_test.go => resource_aws_ec2_capacity_reservation_test.go} (71%) rename website/docs/r/{capacity_reservation.markdown => ec2_capacity_reservation.markdown} (89%) diff --git a/aws/provider.go b/aws/provider.go index 5802e1ca1981..1be88abd755e 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -325,7 +325,6 @@ func Provider() terraform.ResourceProvider { "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), "aws_autoscaling_schedule": resourceAwsAutoscalingSchedule(), "aws_budgets_budget": resourceAwsBudgetsBudget(), - "aws_capacity_reservation": resourceAwsCapacityReservation(), "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), "aws_cloudformation_stack": resourceAwsCloudFormationStack(), "aws_cloudfront_distribution": resourceAwsCloudFrontDistribution(), @@ -404,6 +403,7 @@ func Provider() terraform.ResourceProvider { "aws_dynamodb_table": resourceAwsDynamoDbTable(), "aws_dynamodb_table_item": resourceAwsDynamoDbTableItem(), "aws_dynamodb_global_table": resourceAwsDynamoDbGlobalTable(), + "aws_ec2_capacity_reservation": resourceAwsEc2CapacityReservation(), "aws_ec2_fleet": resourceAwsEc2Fleet(), "aws_ebs_snapshot": resourceAwsEbsSnapshot(), "aws_ebs_snapshot_copy": resourceAwsEbsSnapshotCopy(), diff --git a/aws/resource_aws_capacity_reservation.go b/aws/resource_aws_ec2_capacity_reservation.go similarity index 90% rename from aws/resource_aws_capacity_reservation.go rename to aws/resource_aws_ec2_capacity_reservation.go index 5a0e4aafb662..7094a61469e8 100644 --- a/aws/resource_aws_capacity_reservation.go +++ b/aws/resource_aws_ec2_capacity_reservation.go @@ -12,12 +12,12 @@ import ( "github.com/hashicorp/terraform/helper/validation" ) -func resourceAwsCapacityReservation() *schema.Resource { +func resourceAwsEc2CapacityReservation() *schema.Resource { return &schema.Resource{ - Create: resourceAwsCapacityReservationCreate, - Read: resourceAwsCapacityReservationRead, - Update: resourceAwsCapacityReservationUpdate, - Delete: resourceAwsCapacityReservationDelete, + Create: resourceAwsEc2CapacityReservationCreate, + Read: resourceAwsEc2CapacityReservationRead, + Update: resourceAwsEc2CapacityReservationUpdate, + Delete: resourceAwsEc2CapacityReservationDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -102,7 +102,7 @@ func resourceAwsCapacityReservation() *schema.Resource { } } -func resourceAwsCapacityReservationCreate(d *schema.ResourceData, meta interface{}) error { +func resourceAwsEc2CapacityReservationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn opts := &ec2.CreateCapacityReservationInput{ @@ -169,10 +169,10 @@ func resourceAwsCapacityReservationCreate(d *schema.ResourceData, meta interface return fmt.Errorf("Error creating capacity reservation: %s", err) } d.SetId(*out.CapacityReservation.CapacityReservationId) - return resourceAwsCapacityReservationRead(d, meta) + return resourceAwsEc2CapacityReservationRead(d, meta) } -func resourceAwsCapacityReservationRead(d *schema.ResourceData, meta interface{}) error { +func resourceAwsEc2CapacityReservationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn resp, err := conn.DescribeCapacityReservations(&ec2.DescribeCapacityReservationsInput{ @@ -213,7 +213,7 @@ func resourceAwsCapacityReservationRead(d *schema.ResourceData, meta interface{} return nil } -func resourceAwsCapacityReservationUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceAwsEc2CapacityReservationUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn d.Partial(true) @@ -259,10 +259,10 @@ func resourceAwsCapacityReservationUpdate(d *schema.ResourceData, meta interface if err != nil { return fmt.Errorf("Error modifying capacity reservation: %s", err) } - return resourceAwsCapacityReservationRead(d, meta) + return resourceAwsEc2CapacityReservationRead(d, meta) } -func resourceAwsCapacityReservationDelete(d *schema.ResourceData, meta interface{}) error { +func resourceAwsEc2CapacityReservationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn opts := &ec2.CancelCapacityReservationInput{ diff --git a/aws/resource_aws_capacity_reservation_test.go b/aws/resource_aws_ec2_capacity_reservation_test.go similarity index 71% rename from aws/resource_aws_capacity_reservation_test.go rename to aws/resource_aws_ec2_capacity_reservation_test.go index 5ef0bbd2f621..bbec0093fad2 100644 --- a/aws/resource_aws_capacity_reservation_test.go +++ b/aws/resource_aws_ec2_capacity_reservation_test.go @@ -105,6 +105,43 @@ func TestAccAWSCapacityReservation_basic(t *testing.T) { }) } +func TestAccAWSCapacityReservation_endDate(t *testing.T) { + var cr ec2.CapacityReservation + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCapacityReservationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCapacityReservationConfig_endDate, + Check: resource.ComposeTestCheckFunc( + testAccCheckCapacityReservationExists("aws_capacity_reservation.default", &cr), + resource.TestCheckResourceAttr("aws_capacity_reservation.default", "end_date", "2019-10-31T07:39:57Z"), + resource.TestCheckResourceAttr("aws_capacity_reservation.default", "end_date_type", "limited"), + ), + }, + }, + }) +} + +func TestAccAWSCapacityReservation_tags(t *testing.T) { + var cr ec2.CapacityReservation + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCapacityReservationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCapacityReservationConfig_tags, + Check: resource.ComposeTestCheckFunc( + testAccCheckCapacityReservationExists("aws_capacity_reservation.default", &cr), + resource.TestCheckResourceAttr("aws_capacity_reservation.default", "instance_type", "t2.micro"), + ), + }, + }, + }) +} + func testAccCheckCapacityReservationExists(resourceName string, cr *ec2.CapacityReservation) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -162,6 +199,26 @@ func testAccCheckCapacityReservationDestroy(s *terraform.State) error { } const testAccCapacityReservationConfig = ` +resource "aws_capacity_reservation" "default" { + instance_type = "t2.micro" + instance_platform = "Linux/UNIX" + availability_zone = "us-west-2a" + instance_count = 1 +} +` + +const testAccCapacityReservationConfig_endDate = ` +resource "aws_capacity_reservation" "default" { + instance_type = "t2.micro" + instance_platform = "Linux/UNIX" + availability_zone = "us-west-2a" + instance_count = 1 + end_date = "2019-10-31T07:39:57Z" + end_date_type = "limited" +} +` + +const testAccCapacityReservationConfig_tags = ` resource "aws_capacity_reservation" "default" { instance_type = "t2.micro" instance_platform = "Linux/UNIX" diff --git a/website/docs/r/capacity_reservation.markdown b/website/docs/r/ec2_capacity_reservation.markdown similarity index 89% rename from website/docs/r/capacity_reservation.markdown rename to website/docs/r/ec2_capacity_reservation.markdown index dc99e7e34b76..3f759d31ca80 100644 --- a/website/docs/r/capacity_reservation.markdown +++ b/website/docs/r/ec2_capacity_reservation.markdown @@ -1,19 +1,19 @@ --- layout: "aws" -page_title: "AWS: aws_capacity_reservaton" -sidebar_current: "docs-aws-resource-capacity-reservation" +page_title: "AWS: aws_ec2_capacity_reservaton" +sidebar_current: "docs-aws-resource-ec2-capacity-reservation" description: |- Provides an EC2 Capacity Reservation. This allows you to reserve capacity for your Amazon EC2 instances in a specific Availability Zone for any duration. --- -# aws_capacity_reservaton +# aws_ec2_capacity_reservaton Provides an EC2 Capacity Reservation. This allows you to reserve capacity for your Amazon EC2 instances in a specific Availability Zone for any duration. ## Example Usage ```hcl -resource "aws_capacity_reservation" "default" { +resource "aws_ec2_capacity_reservation" "default" { instance_type = "t2.micro" instance_platform = "Linux/UNIX" availability_zone = "eu-west-1a" @@ -48,5 +48,5 @@ In addition to all arguments above, the following attributes are exported: Capacity Reservations can be imported using the `id`, e.g. ``` -$ terraform import aws_capacity_reservaton.web cr-0123456789abcdef0 +$ terraform import aws_ec2_capacity_reservaton.web cr-0123456789abcdef0 ``` From c4c3f9dbdb4182ebe04e9e3aadc9a4fbbfa0d952 Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Wed, 31 Oct 2018 08:08:40 +0000 Subject: [PATCH 3/6] Update tests --- ...ource_aws_ec2_capacity_reservation_test.go | 162 ++++++++---------- website/aws.erb | 4 + 2 files changed, 80 insertions(+), 86 deletions(-) diff --git a/aws/resource_aws_ec2_capacity_reservation_test.go b/aws/resource_aws_ec2_capacity_reservation_test.go index bbec0093fad2..602317025e43 100644 --- a/aws/resource_aws_ec2_capacity_reservation_test.go +++ b/aws/resource_aws_ec2_capacity_reservation_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "log" "testing" "github.com/aws/aws-sdk-go/aws" @@ -10,73 +11,62 @@ import ( "github.com/hashicorp/terraform/terraform" ) -// func init() { -// resource.AddTestSweepers("aws_capacity_reservation", &resource.Sweeper{ -// Name: "aws_capacity_reservation", -// F: testSweepCapacityReservations, -// }) -// } - -// func testSweepInstances(region string) error { -// client, err := sharedClientForRegion(region) -// if err != nil { -// return fmt.Errorf("error getting client: %s", err) -// } -// conn := client.(*AWSClient).ec2conn - -// err = conn.DescribeInstancesPages(&ec2.DescribeInstancesInput{}, func(page *ec2.DescribeInstancesOutput, isLast bool) bool { -// if len(page.Reservations) == 0 { -// log.Print("[DEBUG] No EC2 Instances to sweep") -// return false -// } - -// for _, reservation := range page.Reservations { -// for _, instance := range reservation.Instances { -// var nameTag string -// id := aws.StringValue(instance.InstanceId) - -// for _, instanceTag := range instance.Tags { -// if aws.StringValue(instanceTag.Key) == "Name" { -// nameTag = aws.StringValue(instanceTag.Value) -// break -// } -// } - -// if !strings.HasPrefix(nameTag, "tf-acc-test-") { -// log.Printf("[INFO] Skipping EC2 Instance: %s", id) -// continue -// } - -// log.Printf("[INFO] Terminating EC2 Instance: %s", id) -// err := awsTerminateInstance(conn, id, 5*time.Minute) -// if err != nil { -// log.Printf("[ERROR] Error terminating EC2 Instance (%s): %s", id, err) -// } -// } -// } -// return !isLast -// }) -// if err != nil { -// if testSweepSkipSweepError(err) { -// log.Printf("[WARN] Skipping EC2 Instance sweep for %s: %s", region, err) -// return nil -// } -// return fmt.Errorf("Error retrieving EC2 Instances: %s", err) -// } - -// return nil -// } - -func TestAccAWSCapacityReservation_importBasic(t *testing.T) { - resourceName := "aws_capacity_reservation.default" +func init() { + resource.AddTestSweepers("aws_ec2_capacity_reservation", &resource.Sweeper{ + Name: "aws_ec2_capacity_reservation", + F: testSweepEc2CapacityReservations, + }) +} + +func testSweepEc2CapacityReservations(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).ec2conn + + resp, err := conn.DescribeCapacityReservations(&ec2.DescribeCapacityReservationsInput{}) + + if err != nil { + return fmt.Errorf("Error retrieving capacity reservations: %s", err) + } + + if len(resp.CapacityReservations) == 0 { + log.Print("[DEBUG] No capacity reservations to sweep") + return nil + } + + for _, r := range resp.CapacityReservations { + if *r.State != "cancelled" { + id := aws.StringValue(r.CapacityReservationId) + + log.Printf("[INFO] Cancelling capacity reservation EC2 Instance: %s", id) + + opts := &ec2.CancelCapacityReservationInput{ + CapacityReservationId: aws.String(id), + } + + _, err := conn.CancelCapacityReservation(opts) + + if err != nil { + log.Printf("[ERROR] Error cancelling capacity reservation (%s): %s", id, err) + } + } + } + + return nil +} + +func TestAccAWSEc2CapacityReservation_importBasic(t *testing.T) { + resourceName := "aws_ec2_capacity_reservation.default" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckCapacityReservationDestroy, + CheckDestroy: testAccCheckEc2CapacityReservationDestroy, Steps: []resource.TestStep{ { - Config: testAccCapacityReservationConfig, + Config: testAccEc2CapacityReservationConfig, }, { ResourceName: resourceName, @@ -87,62 +77,62 @@ func TestAccAWSCapacityReservation_importBasic(t *testing.T) { }) } -func TestAccAWSCapacityReservation_basic(t *testing.T) { +func TestAccAWSEc2CapacityReservation_basic(t *testing.T) { var cr ec2.CapacityReservation resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckCapacityReservationDestroy, + CheckDestroy: testAccCheckEc2CapacityReservationDestroy, Steps: []resource.TestStep{ { - Config: testAccCapacityReservationConfig, + Config: testAccEc2CapacityReservationConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckCapacityReservationExists("aws_capacity_reservation.default", &cr), - resource.TestCheckResourceAttr("aws_capacity_reservation.default", "instance_type", "t2.micro"), + testAccCheckEc2CapacityReservationExists("aws_ec2_capacity_reservation.default", &cr), + resource.TestCheckResourceAttr("aws_ec2_capacity_reservation.default", "instance_type", "t2.micro"), ), }, }, }) } -func TestAccAWSCapacityReservation_endDate(t *testing.T) { +func TestAccAWSEc2CapacityReservation_endDate(t *testing.T) { var cr ec2.CapacityReservation resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckCapacityReservationDestroy, + CheckDestroy: testAccCheckEc2CapacityReservationDestroy, Steps: []resource.TestStep{ { - Config: testAccCapacityReservationConfig_endDate, + Config: testAccEc2CapacityReservationConfig_endDate, Check: resource.ComposeTestCheckFunc( - testAccCheckCapacityReservationExists("aws_capacity_reservation.default", &cr), - resource.TestCheckResourceAttr("aws_capacity_reservation.default", "end_date", "2019-10-31T07:39:57Z"), - resource.TestCheckResourceAttr("aws_capacity_reservation.default", "end_date_type", "limited"), + testAccCheckEc2CapacityReservationExists("aws_ec2_capacity_reservation.default", &cr), + resource.TestCheckResourceAttr("aws_ec2_capacity_reservation.default", "end_date", "2019-10-31T07:39:57Z"), + resource.TestCheckResourceAttr("aws_ec2_capacity_reservation.default", "end_date_type", "limited"), ), }, }, }) } -func TestAccAWSCapacityReservation_tags(t *testing.T) { +func TestAccAWSEc2CapacityReservation_tags(t *testing.T) { var cr ec2.CapacityReservation resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckCapacityReservationDestroy, + CheckDestroy: testAccCheckEc2CapacityReservationDestroy, Steps: []resource.TestStep{ { - Config: testAccCapacityReservationConfig_tags, + Config: testAccEc2CapacityReservationConfig_tags, Check: resource.ComposeTestCheckFunc( - testAccCheckCapacityReservationExists("aws_capacity_reservation.default", &cr), - resource.TestCheckResourceAttr("aws_capacity_reservation.default", "instance_type", "t2.micro"), + testAccCheckEc2CapacityReservationExists("aws_ec2_capacity_reservation.default", &cr), + resource.TestCheckResourceAttr("aws_ec2_capacity_reservation.default", "instance_type", "t2.micro"), ), }, }, }) } -func testAccCheckCapacityReservationExists(resourceName string, cr *ec2.CapacityReservation) resource.TestCheckFunc { +func testAccCheckEc2CapacityReservationExists(resourceName string, cr *ec2.CapacityReservation) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -171,11 +161,11 @@ func testAccCheckCapacityReservationExists(resourceName string, cr *ec2.Capacity } } -func testAccCheckCapacityReservationDestroy(s *terraform.State) error { +func testAccCheckEc2CapacityReservationDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_capacity_reservation" { + if rs.Type != "aws_ec2_capacity_reservation" { continue } @@ -198,8 +188,8 @@ func testAccCheckCapacityReservationDestroy(s *terraform.State) error { } -const testAccCapacityReservationConfig = ` -resource "aws_capacity_reservation" "default" { +const testAccEc2CapacityReservationConfig = ` +resource "aws_ec2_capacity_reservation" "default" { instance_type = "t2.micro" instance_platform = "Linux/UNIX" availability_zone = "us-west-2a" @@ -207,8 +197,8 @@ resource "aws_capacity_reservation" "default" { } ` -const testAccCapacityReservationConfig_endDate = ` -resource "aws_capacity_reservation" "default" { +const testAccEc2CapacityReservationConfig_endDate = ` +resource "aws_ec2_capacity_reservation" "default" { instance_type = "t2.micro" instance_platform = "Linux/UNIX" availability_zone = "us-west-2a" @@ -218,8 +208,8 @@ resource "aws_capacity_reservation" "default" { } ` -const testAccCapacityReservationConfig_tags = ` -resource "aws_capacity_reservation" "default" { +const testAccEc2CapacityReservationConfig_tags = ` +resource "aws_ec2_capacity_reservation" "default" { instance_type = "t2.micro" instance_platform = "Linux/UNIX" availability_zone = "us-west-2a" diff --git a/website/aws.erb b/website/aws.erb index 8b63b0268524..399ffc3742db 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1002,6 +1002,10 @@ aws_ebs_volume + > + aws_ec2_capacity_reservation + + > aws_ec2_fleet From bff7a2286580f7b64a2ac703fcab327289a3c46d Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Wed, 31 Oct 2018 09:51:08 +0000 Subject: [PATCH 4/6] Fix documentation typo --- website/docs/r/ec2_capacity_reservation.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/r/ec2_capacity_reservation.markdown b/website/docs/r/ec2_capacity_reservation.markdown index 3f759d31ca80..b87e103bde52 100644 --- a/website/docs/r/ec2_capacity_reservation.markdown +++ b/website/docs/r/ec2_capacity_reservation.markdown @@ -1,12 +1,12 @@ --- layout: "aws" -page_title: "AWS: aws_ec2_capacity_reservaton" +page_title: "AWS: aws_ec2_capacity_reservation" sidebar_current: "docs-aws-resource-ec2-capacity-reservation" description: |- Provides an EC2 Capacity Reservation. This allows you to reserve capacity for your Amazon EC2 instances in a specific Availability Zone for any duration. --- -# aws_ec2_capacity_reservaton +# aws_ec2_capacity_reservation Provides an EC2 Capacity Reservation. This allows you to reserve capacity for your Amazon EC2 instances in a specific Availability Zone for any duration. @@ -48,5 +48,5 @@ In addition to all arguments above, the following attributes are exported: Capacity Reservations can be imported using the `id`, e.g. ``` -$ terraform import aws_ec2_capacity_reservaton.web cr-0123456789abcdef0 +$ terraform import aws_ec2_capacity_reservation.web cr-0123456789abcdef0 ``` From 9f4422cf7a5bba5a505dfdb0f0729044e4814c5e Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Wed, 31 Oct 2018 17:29:54 +0000 Subject: [PATCH 5/6] Addressing review comments --- aws/resource_aws_ec2_capacity_reservation.go | 33 ++++---- ...ource_aws_ec2_capacity_reservation_test.go | 80 ++++++++++--------- .../docs/r/ec2_capacity_reservation.markdown | 2 +- 3 files changed, 61 insertions(+), 54 deletions(-) diff --git a/aws/resource_aws_ec2_capacity_reservation.go b/aws/resource_aws_ec2_capacity_reservation.go index 7094a61469e8..2236f31964bd 100644 --- a/aws/resource_aws_ec2_capacity_reservation.go +++ b/aws/resource_aws_ec2_capacity_reservation.go @@ -6,7 +6,6 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -35,8 +34,9 @@ func resourceAwsEc2CapacityReservation() *schema.Resource { Default: false, }, "end_date": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.ValidateRFC3339TimeString, }, "end_date_type": { Type: schema.TypeString, @@ -119,7 +119,7 @@ func resourceAwsEc2CapacityReservationCreate(d *schema.ResourceData, meta interf if v, ok := d.GetOk("end_date"); ok { t, err := time.Parse(time.RFC3339, v.(string)) if err != nil { - return fmt.Errorf("Error parsing capacity reservation end date: %s", err.Error()) + return fmt.Errorf("Error parsing EC2 Capacity Reservation end date: %s", err.Error()) } opts.EndDate = aws.Time(t) } @@ -166,7 +166,7 @@ func resourceAwsEc2CapacityReservationCreate(d *schema.ResourceData, meta interf out, err := conn.CreateCapacityReservation(opts) if err != nil { - return fmt.Errorf("Error creating capacity reservation: %s", err) + return fmt.Errorf("Error creating EC2 Capacity Reservation: %s", err) } d.SetId(*out.CapacityReservation.CapacityReservationId) return resourceAwsEc2CapacityReservationRead(d, meta) @@ -180,24 +180,23 @@ func resourceAwsEc2CapacityReservationRead(d *schema.ResourceData, meta interfac }) if err != nil { - // TODO: Check if error is raised if capacity reservation has gone - if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" { - d.SetId("") - return nil - } - - // Some other error, report it - return err + return fmt.Errorf("Error describing EC2 Capacity Reservations: %s", err) } // If nothing was found, then return no state if len(resp.CapacityReservations) == 0 { + log.Printf("[WARN] EC2 Capacity Reservation (%s) not found, removing from state", d.Id()) d.SetId("") - return nil } reservation := resp.CapacityReservations[0] + if aws.StringValue(reservation.State) != ec2.CapacityReservationStateCancelled && aws.StringValue(reservation.State) != ec2.CapacityReservationStateExpired { + log.Printf("[WARN] EC2 Capacity Reservation (%s) no longer active, removing from state", d.Id()) + d.SetId("") + return nil + } + d.Set("availability_zone", reservation.AvailabilityZone) d.Set("ebs_optimized", reservation.EbsOptimized) d.Set("end_date", reservation.EndDate) @@ -239,7 +238,7 @@ func resourceAwsEc2CapacityReservationUpdate(d *schema.ResourceData, meta interf if v, ok := d.GetOk("end_date"); ok { t, err := time.Parse(time.RFC3339, v.(string)) if err != nil { - return fmt.Errorf("Error parsing capacity reservation end date: %s", err.Error()) + return fmt.Errorf("Error parsing EC2 Capacity Reservation end date: %s", err.Error()) } opts.EndDate = aws.Time(t) } @@ -257,7 +256,7 @@ func resourceAwsEc2CapacityReservationUpdate(d *schema.ResourceData, meta interf _, err := conn.ModifyCapacityReservation(opts) if err != nil { - return fmt.Errorf("Error modifying capacity reservation: %s", err) + return fmt.Errorf("Error modifying EC2 Capacity Reservation: %s", err) } return resourceAwsEc2CapacityReservationRead(d, meta) } @@ -271,7 +270,7 @@ func resourceAwsEc2CapacityReservationDelete(d *schema.ResourceData, meta interf _, err := conn.CancelCapacityReservation(opts) if err != nil { - return fmt.Errorf("Error cancelling capacity reservation: %s", err) + return fmt.Errorf("Error cancelling EC2 Capacity Reservation: %s", err) } return nil diff --git a/aws/resource_aws_ec2_capacity_reservation_test.go b/aws/resource_aws_ec2_capacity_reservation_test.go index 602317025e43..e0da595742bc 100644 --- a/aws/resource_aws_ec2_capacity_reservation_test.go +++ b/aws/resource_aws_ec2_capacity_reservation_test.go @@ -28,19 +28,19 @@ func testSweepEc2CapacityReservations(region string) error { resp, err := conn.DescribeCapacityReservations(&ec2.DescribeCapacityReservationsInput{}) if err != nil { - return fmt.Errorf("Error retrieving capacity reservations: %s", err) + return fmt.Errorf("Error retrieving EC2 Capacity Reservations: %s", err) } if len(resp.CapacityReservations) == 0 { - log.Print("[DEBUG] No capacity reservations to sweep") + log.Print("[DEBUG] No EC2 Capacity Reservations to sweep") return nil } for _, r := range resp.CapacityReservations { - if *r.State != "cancelled" { + if aws.StringValue(r.State) != ec2.CapacityReservationStateCancelled && aws.StringValue(r.State) != ec2.CapacityReservationStateExpired { id := aws.StringValue(r.CapacityReservationId) - log.Printf("[INFO] Cancelling capacity reservation EC2 Instance: %s", id) + log.Printf("[INFO] Cancelling EC2 Capacity Reservation EC2 Instance: %s", id) opts := &ec2.CancelCapacityReservationInput{ CapacityReservationId: aws.String(id), @@ -49,7 +49,7 @@ func testSweepEc2CapacityReservations(region string) error { _, err := conn.CancelCapacityReservation(opts) if err != nil { - log.Printf("[ERROR] Error cancelling capacity reservation (%s): %s", id, err) + log.Printf("[ERROR] Error cancelling EC2 Capacity Reservation (%s): %s", id, err) } } } @@ -57,26 +57,6 @@ func testSweepEc2CapacityReservations(region string) error { return nil } -func TestAccAWSEc2CapacityReservation_importBasic(t *testing.T) { - resourceName := "aws_ec2_capacity_reservation.default" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckEc2CapacityReservationDestroy, - Steps: []resource.TestStep{ - { - Config: testAccEc2CapacityReservationConfig, - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - func TestAccAWSEc2CapacityReservation_basic(t *testing.T) { var cr ec2.CapacityReservation resource.ParallelTest(t, resource.TestCase{ @@ -91,6 +71,11 @@ func TestAccAWSEc2CapacityReservation_basic(t *testing.T) { resource.TestCheckResourceAttr("aws_ec2_capacity_reservation.default", "instance_type", "t2.micro"), ), }, + { + ResourceName: "aws_ec2_capacity_reservation.default", + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -110,6 +95,11 @@ func TestAccAWSEc2CapacityReservation_endDate(t *testing.T) { resource.TestCheckResourceAttr("aws_ec2_capacity_reservation.default", "end_date_type", "limited"), ), }, + { + ResourceName: "aws_ec2_capacity_reservation.default", + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -125,9 +115,15 @@ func TestAccAWSEc2CapacityReservation_tags(t *testing.T) { Config: testAccEc2CapacityReservationConfig_tags, Check: resource.ComposeTestCheckFunc( testAccCheckEc2CapacityReservationExists("aws_ec2_capacity_reservation.default", &cr), - resource.TestCheckResourceAttr("aws_ec2_capacity_reservation.default", "instance_type", "t2.micro"), + resource.TestCheckResourceAttr("aws_ec2_capacity_reservation.default", "tags.%", "1"), + resource.TestCheckResourceAttr("aws_ec2_capacity_reservation.default", "tags.Name", "foo"), ), }, + { + ResourceName: "aws_ec2_capacity_reservation.default", + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -149,15 +145,21 @@ func testAccCheckEc2CapacityReservationExists(resourceName string, cr *ec2.Capac }) if err != nil { - return fmt.Errorf("DescribeCapacityReservations error: %v", err) + return fmt.Errorf("Error retrieving EC2 Capacity Reservations: %s", err) } - if len(resp.CapacityReservations) > 0 { - *cr = *resp.CapacityReservations[0] - return nil + if len(resp.CapacityReservations) == 0 { + return fmt.Errorf("EC2 Capacity Reservation (%s) not found", rs.Primary.ID) + } + + reservation := resp.CapacityReservations[0] + + if aws.StringValue(reservation.State) != ec2.CapacityReservationStateActive && aws.StringValue(reservation.State) != ec2.CapacityReservationStatePending { + return fmt.Errorf("EC2 Capacity Reservation (%s) found in unexpected state: %s", rs.Primary.ID, aws.StringValue(reservation.State)) } - return fmt.Errorf("Capacity Reservation not found") + *cr = *reservation + return nil } } @@ -175,8 +177,8 @@ func testAccCheckEc2CapacityReservationDestroy(s *terraform.State) error { }) if err == nil { for _, r := range resp.CapacityReservations { - if *r.State != "cancelled" { - return fmt.Errorf("Found uncancelled Capacity Reservation: %s", r) + if aws.StringValue(r.State) != ec2.CapacityReservationStateCancelled && aws.StringValue(r.State) != ec2.CapacityReservationStateExpired { + return fmt.Errorf("Found uncancelled EC2 Capacity Reservation: %s", r) } } } @@ -189,19 +191,23 @@ func testAccCheckEc2CapacityReservationDestroy(s *terraform.State) error { } const testAccEc2CapacityReservationConfig = ` +data "aws_availability_zones" "available" {} + resource "aws_ec2_capacity_reservation" "default" { instance_type = "t2.micro" instance_platform = "Linux/UNIX" - availability_zone = "us-west-2a" + availability_zone = "${data.aws_availability_zones.available.names[0]}" instance_count = 1 } ` const testAccEc2CapacityReservationConfig_endDate = ` +data "aws_availability_zones" "available" {} + resource "aws_ec2_capacity_reservation" "default" { instance_type = "t2.micro" instance_platform = "Linux/UNIX" - availability_zone = "us-west-2a" + availability_zone = "${data.aws_availability_zones.available.names[0]}" instance_count = 1 end_date = "2019-10-31T07:39:57Z" end_date_type = "limited" @@ -209,10 +215,12 @@ resource "aws_ec2_capacity_reservation" "default" { ` const testAccEc2CapacityReservationConfig_tags = ` +data "aws_availability_zones" "available" {} + resource "aws_ec2_capacity_reservation" "default" { instance_type = "t2.micro" instance_platform = "Linux/UNIX" - availability_zone = "us-west-2a" + availability_zone = "${data.aws_availability_zones.available.names[0]}" instance_count = 1 tags { diff --git a/website/docs/r/ec2_capacity_reservation.markdown b/website/docs/r/ec2_capacity_reservation.markdown index b87e103bde52..713c2097d346 100644 --- a/website/docs/r/ec2_capacity_reservation.markdown +++ b/website/docs/r/ec2_capacity_reservation.markdown @@ -27,7 +27,7 @@ The following arguments are supported: * `availability_zone` - (Required) The Availability Zone in which to create the Capacity Reservation. * `ebs_optimized` - (Optional) Indicates whether the Capacity Reservation supports EBS-optimized instances. -* `end_date` - (Optional) The date and time at which the Capacity Reservation expires. When a Capacity Reservation expires, the reserved capacity is released and you can no longer launch instances into it. +* `end_date` - (Optional) The date and time at which the Capacity Reservation expires. When a Capacity Reservation expires, the reserved capacity is released and you can no longer launch instances into it. Valid values: [RFC3339 time string](https://tools.ietf.org/html/rfc3339#section-5.8) (`YYYY-MM-DDTHH:MM:SSZ`) * `end_date_type` - (Optional) Indicates the way in which the Capacity Reservation ends. Specify either `unlimited` or `limited`. * `ephemeral_storage` - (Optional) Indicates whether the Capacity Reservation supports instances with temporary, block-level storage. * `instance_count` - (Required) The number of instances for which to reserve capacity. From 6b53580cdfbddce1bda438394d941a30f99d61f5 Mon Sep 17 00:00:00 2001 From: Gareth Oakley Date: Wed, 31 Oct 2018 17:53:10 +0000 Subject: [PATCH 6/6] Addressing review comments II --- aws/resource_aws_ec2_capacity_reservation.go | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/aws/resource_aws_ec2_capacity_reservation.go b/aws/resource_aws_ec2_capacity_reservation.go index 2236f31964bd..13a67f6af8dc 100644 --- a/aws/resource_aws_ec2_capacity_reservation.go +++ b/aws/resource_aws_ec2_capacity_reservation.go @@ -140,12 +140,10 @@ func resourceAwsEc2CapacityReservationCreate(d *schema.ResourceData, meta interf opts.Tenancy = aws.String(v.(string)) } - restricted := meta.(*AWSClient).IsChinaCloud() - if !restricted { - + if v, ok := d.GetOk("tags"); ok { tagsSpec := make([]*ec2.TagSpecification, 0) - if v, ok := d.GetOk("tags"); ok { + if len(tagsSpec) > 0 { tags := tagsFromMap(v.(map[string]interface{})) spec := &ec2.TagSpecification{ @@ -155,9 +153,7 @@ func resourceAwsEc2CapacityReservationCreate(d *schema.ResourceData, meta interf } tagsSpec = append(tagsSpec, spec) - } - if len(tagsSpec) > 0 { opts.TagSpecifications = tagsSpec } } @@ -216,15 +212,12 @@ func resourceAwsEc2CapacityReservationUpdate(d *schema.ResourceData, meta interf conn := meta.(*AWSClient).ec2conn d.Partial(true) - restricted := meta.(*AWSClient).IsChinaCloud() if d.HasChange("tags") { - if !d.IsNewResource() || restricted { - if err := setTags(conn, d); err != nil { - return err - } else { - d.SetPartial("tags") - } + if err := setTags(conn, d); err != nil { + return err + } else { + d.SetPartial("tags") } }