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

New Data Sources: aws_ec2_instance_type_offering and aws_ec2_instance_type_offerings #12139

Merged
merged 2 commits into from
Mar 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions aws/data_source_aws_ec2_instance_type_offering.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package aws

import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func dataSourceAwsEc2InstanceTypeOffering() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsEc2InstanceTypeOfferingRead,

Schema: map[string]*schema.Schema{
"filter": dataSourceFiltersSchema(),
"instance_type": {
Type: schema.TypeString,
Computed: true,
},
"location_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
ec2.LocationTypeAvailabilityZone,
ec2.LocationTypeAvailabilityZoneId,
ec2.LocationTypeRegion,
}, false),
},
"preferred_instance_types": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}

func dataSourceAwsEc2InstanceTypeOfferingRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

input := &ec2.DescribeInstanceTypeOfferingsInput{}

if v, ok := d.GetOk("filter"); ok {
input.Filters = buildAwsDataSourceFilters(v.(*schema.Set))
}

if v, ok := d.GetOk("location_type"); ok {
input.LocationType = aws.String(v.(string))
}

var foundInstanceTypes []string

for {
output, err := conn.DescribeInstanceTypeOfferings(input)

if err != nil {
return fmt.Errorf("error reading EC2 Instance Type Offerings: %w", err)
}

if output == nil {
break
}

for _, instanceTypeOffering := range output.InstanceTypeOfferings {
if instanceTypeOffering == nil {
continue
}

foundInstanceTypes = append(foundInstanceTypes, aws.StringValue(instanceTypeOffering.InstanceType))
}

if aws.StringValue(output.NextToken) == "" {
break
}

input.NextToken = output.NextToken
}

if len(foundInstanceTypes) == 0 {
return fmt.Errorf("no EC2 Instance Type Offerings found matching criteria; try different search")
}

var resultInstanceType string

// Search preferred instance types in their given order and set result
// instance type for first match found
if l := d.Get("preferred_instance_types").([]interface{}); len(l) > 0 {
for _, elem := range l {
preferredInstanceType, ok := elem.(string)

if !ok {
continue
}

for _, foundInstanceType := range foundInstanceTypes {
if foundInstanceType == preferredInstanceType {
resultInstanceType = preferredInstanceType
break
}
}

if resultInstanceType != "" {
break
}
}
}

if resultInstanceType == "" && len(foundInstanceTypes) > 1 {
return fmt.Errorf("multiple EC2 Instance Offerings found matching criteria; try different search")
}

if resultInstanceType == "" && len(foundInstanceTypes) == 1 {
resultInstanceType = foundInstanceTypes[0]
}

if resultInstanceType == "" {
return fmt.Errorf("no EC2 Instance Type Offerings found matching criteria; try different search")
}

d.Set("instance_type", resultInstanceType)

d.SetId(resource.UniqueId())

return nil
}
143 changes: 143 additions & 0 deletions aws/data_source_aws_ec2_instance_type_offering_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package aws

import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

func TestAccAWSEc2InstanceTypeOfferingDataSource_Filter(t *testing.T) {
dataSourceName := "data.aws_ec2_instance_type_offering.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEc2InstanceTypeOffering(t) },
Providers: testAccProviders,
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: testAccAWSEc2InstanceTypeOfferingDataSourceConfigFilter(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(dataSourceName, "instance_type"),
),
},
},
})
}

func TestAccAWSEc2InstanceTypeOfferingDataSource_LocationType(t *testing.T) {
dataSourceName := "data.aws_ec2_instance_type_offering.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEc2InstanceTypeOffering(t) },
Providers: testAccProviders,
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: testAccAWSEc2InstanceTypeOfferingDataSourceConfigLocationType(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(dataSourceName, "instance_type"),
),
},
},
})
}

func TestAccAWSEc2InstanceTypeOfferingDataSource_PreferredInstanceTypes(t *testing.T) {
dataSourceName := "data.aws_ec2_instance_type_offering.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSEc2InstanceTypeOffering(t) },
Providers: testAccProviders,
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: testAccAWSEc2InstanceTypeOfferingDataSourceConfigPreferredInstanceTypes(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "instance_type", "t3.micro"),
),
},
},
})
}

func testAccPreCheckAWSEc2InstanceTypeOffering(t *testing.T) {
conn := testAccProvider.Meta().(*AWSClient).ec2conn

input := &ec2.DescribeInstanceTypeOfferingsInput{
MaxResults: aws.Int64(5),
}

_, err := conn.DescribeInstanceTypeOfferings(input)

if testAccPreCheckSkipError(err) {
t.Skipf("skipping acceptance testing: %s", err)
}

if err != nil {
t.Fatalf("unexpected PreCheck error: %s", err)
}
}

func testAccAWSEc2InstanceTypeOfferingDataSourceConfigFilter() string {
return fmt.Sprintf(`
# Rather than hardcode an instance type in the testing,
# use the first result from all available offerings.
data "aws_ec2_instance_type_offerings" "test" {}

data "aws_ec2_instance_type_offering" "test" {
filter {
name = "instance-type"
values = [tolist(data.aws_ec2_instance_type_offerings.test.instance_types)[0]]
}
}
`)
}

func testAccAWSEc2InstanceTypeOfferingDataSourceConfigLocationType() string {
return fmt.Sprintf(`
data "aws_availability_zones" "available" {
state = "available"
}

# Rather than hardcode an instance type in the testing,
# use the first result from all available offerings.
data "aws_ec2_instance_type_offerings" "test" {
filter {
name = "location"
values = [data.aws_availability_zones.available.names[0]]
}

location_type = "availability-zone"
}

data "aws_ec2_instance_type_offering" "test" {
filter {
name = "instance-type"
values = [tolist(data.aws_ec2_instance_type_offerings.test.instance_types)[0]]
}

filter {
name = "location"
values = [data.aws_availability_zones.available.names[0]]
}

location_type = "availability-zone"
}
`)
}

func testAccAWSEc2InstanceTypeOfferingDataSourceConfigPreferredInstanceTypes() string {
return fmt.Sprintf(`
data "aws_ec2_instance_type_offering" "test" {
filter {
name = "instance-type"
values = ["t1.micro", "t2.micro", "t3.micro"]
}

preferred_instance_types = ["t3.micro", "t2.micro", "t1.micro"]
}
`)
}
85 changes: 85 additions & 0 deletions aws/data_source_aws_ec2_instance_type_offerings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package aws

import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func dataSourceAwsEc2InstanceTypeOfferings() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsEc2InstanceTypeOfferingsRead,

Schema: map[string]*schema.Schema{
"filter": dataSourceFiltersSchema(),
"instance_types": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"location_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
ec2.LocationTypeAvailabilityZone,
ec2.LocationTypeAvailabilityZoneId,
ec2.LocationTypeRegion,
}, false),
},
},
}
}

func dataSourceAwsEc2InstanceTypeOfferingsRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

input := &ec2.DescribeInstanceTypeOfferingsInput{}

if v, ok := d.GetOk("filter"); ok {
input.Filters = buildAwsDataSourceFilters(v.(*schema.Set))
}

if v, ok := d.GetOk("location_type"); ok {
input.LocationType = aws.String(v.(string))
}

var instanceTypes []string

for {
output, err := conn.DescribeInstanceTypeOfferings(input)

if err != nil {
return fmt.Errorf("error reading EC2 Instance Type Offerings: %w", err)
}

if output == nil {
break
}

for _, instanceTypeOffering := range output.InstanceTypeOfferings {
if instanceTypeOffering == nil {
continue
}

instanceTypes = append(instanceTypes, aws.StringValue(instanceTypeOffering.InstanceType))
}

if aws.StringValue(output.NextToken) == "" {
break
}

input.NextToken = output.NextToken
}

if err := d.Set("instance_types", instanceTypes); err != nil {
return fmt.Errorf("error setting instance_types: %s", err)
}

d.SetId(resource.UniqueId())

return nil
}
Loading