Skip to content

Commit

Permalink
d/aws_ec2_host: Get acceptance tests working.
Browse files Browse the repository at this point in the history
  • Loading branch information
ewbankkit committed Sep 24, 2021
1 parent fe47867 commit f190485
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 140 deletions.
143 changes: 50 additions & 93 deletions aws/data_source_aws_ec2_host.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,42 @@
package aws

import (
"errors"
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/finder"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

func dataSourceAwsEc2Host() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsAwsEc2HostRead,

Schema: map[string]*schema.Schema{
"filter": dataSourceFiltersSchema(),
"tags": tagsSchemaComputed(),
"host_id": {
"arn": {
Type: schema.TypeString,
Required: true,
Computed: true,
},
"auto_placement": {
Type: schema.TypeString,
Computed: true,
},
"availability_zone": {
Type: schema.TypeString,
Computed: true,
},
"instance_type": {
"cores": {
Type: schema.TypeInt,
Computed: true,
},
"filter": dataSourceFiltersSchema(),
"host_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"host_recovery": {
Expand All @@ -38,113 +47,61 @@ func dataSourceAwsEc2Host() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"cores": {
"instance_type": {
Type: schema.TypeString,
Computed: true,
},
"total_vcpus": {
"owner_id": {
Type: schema.TypeString,
Computed: true,
},
"sockets": {
Type: schema.TypeString,
Type: schema.TypeInt,
Computed: true,
},
"auto_placement": {
Type: schema.TypeString,
"tags": tagsSchemaComputed(),
"total_vcpus": {
Type: schema.TypeInt,
Computed: true,
},
},
}
}

func dataSourceAwsAwsEc2HostRead(d *schema.ResourceData, meta interface{}) error {
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig
conn := meta.(*AWSClient).ec2conn
hostID, hostIDOk := d.GetOk("host_id")
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

filters, filtersOk := d.GetOk("filter")
tags, tagsOk := d.GetOk("tags")
host, err := finder.HostByIDAndFilters(conn, d.Get("host_id").(string), buildAwsDataSourceFilters(d.Get("filter").(*schema.Set)))

params := &ec2.DescribeHostsInput{}
if hostIDOk {
params.HostIds = []*string{aws.String(hostID.(string))}
}
if filtersOk {
params.Filter = buildAwsDataSourceFilters(filters.(*schema.Set))
}
resp, err := conn.DescribeHosts(params)
if err != nil {
return err
}
// If no hosts were returned, return
if resp.Hosts == nil || len(resp.Hosts) == 0 {
return errors.New("Your query returned no results. Please change your search criteria and try again.")
return tfresource.SingularDataSourceReadError("EC2 Host", err)
}

d.SetId(aws.StringValue(host.HostId))

arn := arn.ARN{
Partition: meta.(*AWSClient).partition,
Service: ec2.ServiceName,
Region: meta.(*AWSClient).region,
AccountID: aws.StringValue(host.OwnerId),
Resource: fmt.Sprintf("dedicated-host/%s", d.Id()),
}.String()
d.Set("arn", arn)
d.Set("auto_placement", host.AutoPlacement)
d.Set("availability_zone", host.AvailabilityZone)
d.Set("cores", host.HostProperties.Cores)
d.Set("host_id", host.HostId)
d.Set("host_recovery", host.HostRecovery)
d.Set("instance_family", host.HostProperties.InstanceFamily)
d.Set("instance_type", host.HostProperties.InstanceType)
d.Set("owner_id", host.OwnerId)
d.Set("sockets", host.HostProperties.Sockets)
d.Set("total_vcpus", host.HostProperties.TotalVCpus)

if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(host.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
}

var filteredHosts []*ec2.Host

// loop through reservations, and remove terminated hosts, populate host slice
for _, host := range resp.Hosts {

if host.State != nil && *host.State != "terminated" {
filteredHosts = append(filteredHosts, host)
}

}
var host *ec2.Host
if len(filteredHosts) < 1 {
return errors.New("Your query returned no results. Please change your search criteria and try again.")
}

if len(filteredHosts) > 1 {
return errors.New(`Your query returned more than one result. Please try a more
specific search criteria.`)
} else {
host = filteredHosts[0]
}
if tagsOk {
params.Filter = append(params.Filter, ec2TagFiltersFromMap(tags.(map[string]interface{}))...)
}
log.Printf("[DEBUG] aws_dedicated_host - Single host ID found: %s", *host.HostId)
if err := hostDescriptionAttributes(d, host, ignoreTagsConfig); err != nil {
return err
}
return nil
}
func hostDescriptionAttributes(d *schema.ResourceData, host *ec2.Host, ignoreTagsConfig *keyvaluetags.IgnoreConfig) error {

d.SetId(*host.HostId)
d.Set("instance_state", host.State)

if host.AvailabilityZone != nil {
d.Set("availability_zone", host.AvailabilityZone)
}
if host.HostRecovery != nil {
d.Set("host_recovery", host.HostRecovery)
}
if host.AutoPlacement != nil {
d.Set("auto_placement", host.AutoPlacement)
}
if host.HostProperties.InstanceType != nil {
d.Set("instance_type", host.HostProperties.InstanceType)
}
if host.HostProperties.InstanceFamily != nil {
d.Set("instance_family", host.HostProperties.InstanceFamily)
}
if host.HostProperties.Cores != nil {
d.Set("cores", host.HostProperties.Cores)
}
if host.HostProperties.Sockets != nil {
d.Set("sockets", host.HostProperties.Sockets)
}
if host.HostProperties.TotalVCpus != nil {
d.Set("total_vcpus", host.HostProperties.TotalVCpus)
}

if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(host.Tags).IgnoreConfig(ignoreTagsConfig).IgnoreAws().Map()); err != nil {
return fmt.Errorf("error setting tags: %s", err)
}
return nil

}
109 changes: 81 additions & 28 deletions aws/data_source_aws_ec2_host_test.go
Original file line number Diff line number Diff line change
@@ -1,66 +1,119 @@
package aws

import (
"fmt"
"testing"

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

func TestAccAWSEc2HostDataSource_basic(t *testing.T) {
dataSourceName := "data.aws_ec2_host.test"
resourceName := "aws_ec2_host.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID),
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDedicatedHostDataSourceConfig,
Config: testAccDataSourceAWSEc2HostConfig(rName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(dataSourceName, "instance_type", resourceName, "instance_type"),
resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "auto_placement", resourceName, "auto_placement"),
resource.TestCheckResourceAttrPair(dataSourceName, "availability_zone", resourceName, "availability_zone"),
resource.TestCheckResourceAttrPair(dataSourceName, "instance_family", resourceName, "instance_family"),
resource.TestCheckResourceAttrPair(dataSourceName, "cores", resourceName, "cores"),
resource.TestCheckResourceAttrPair(dataSourceName, "total_vcpus", resourceName, "total_vcpus"),
resource.TestCheckResourceAttrPair(dataSourceName, "sockets", resourceName, "sockets"),
resource.TestCheckResourceAttrSet(dataSourceName, "cores"),
resource.TestCheckResourceAttrPair(dataSourceName, "host_id", resourceName, "id"),
resource.TestCheckResourceAttrPair(dataSourceName, "host_recovery", resourceName, "host_recovery"),
resource.TestCheckResourceAttrPair(dataSourceName, "instance_family", resourceName, "instance_family"),
resource.TestCheckResourceAttrPair(dataSourceName, "instance_type", resourceName, "instance_type"),
resource.TestCheckResourceAttrPair(dataSourceName, "owner_id", resourceName, "owner_id"),
resource.TestCheckResourceAttrSet(dataSourceName, "sockets"),
resource.TestCheckResourceAttrPair(dataSourceName, "tags.%", resourceName, "tags.%"),
resource.TestCheckResourceAttrSet(dataSourceName, "total_vcpus"),
),
},
},
})
}

func TestAccAWSEc2HostDataSource_Filter(t *testing.T) {
dataSourceName := "data.aws_ec2_host.test"
resourceName := "aws_ec2_host.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID),
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAWSEc2HostConfigFilter(rName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "auto_placement", resourceName, "auto_placement"),
resource.TestCheckResourceAttr(dataSourceName, "tags.%", "2"),
resource.TestCheckResourceAttr(dataSourceName, "tags.tag1", "test-value1"),
resource.TestCheckResourceAttr(dataSourceName, "tags.tag2", "test-value2"),
resource.TestCheckResourceAttrPair(dataSourceName, "availability_zone", resourceName, "availability_zone"),
resource.TestCheckResourceAttrSet(dataSourceName, "cores"),
resource.TestCheckResourceAttrPair(dataSourceName, "host_id", resourceName, "id"),
resource.TestCheckResourceAttrPair(dataSourceName, "host_recovery", resourceName, "host_recovery"),
resource.TestCheckResourceAttrPair(dataSourceName, "instance_family", resourceName, "instance_family"),
resource.TestCheckResourceAttrPair(dataSourceName, "instance_type", resourceName, "instance_type"),
resource.TestCheckResourceAttrPair(dataSourceName, "owner_id", resourceName, "owner_id"),
resource.TestCheckResourceAttrSet(dataSourceName, "sockets"),
resource.TestCheckResourceAttrPair(dataSourceName, "tags.%", resourceName, "tags.%"),
resource.TestCheckResourceAttrSet(dataSourceName, "total_vcpus"),
),
},
},
})
}

const testAccDedicatedHostDataSourceConfig = `
func testAccDataSourceAWSEc2HostConfig(rName string) string {
return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(`
resource "aws_ec2_host" "test" {
instance_type = "c5.xlarge"
availability_zone = "${data.aws_availability_zones.available.names[0]}"
host_recovery = "on"
auto_placement = "on"
tags = {
tag1 = "test-value1"
tag2 = "test-value2"
availability_zone = data.aws_availability_zones.available.names[0]
instance_type = "a1.large"
tags = {
Name = %[1]q
}
}
data "aws_ec2_host" "test" {
host_id = aws_ec2_host.test.id
}
`, rName))
}

func testAccDataSourceAWSEc2HostConfigFilter(rName string) string {
return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(`
resource "aws_ec2_host" "test" {
availability_zone = data.aws_availability_zones.available.names[0]
instance_type = "a1.large"
tags = {
%[1]q = "True"
}
}
data "aws_ec2_host" "test" {
filter {
name = "availability-zone"
values = [aws_ec2_host.test.availability_zone]
}
data "aws_availability_zones" "available" {
state = "available"
filter {
name = "opt-in-status"
values = ["opt-in-not-required"]
}
filter {
name = "instance-type"
values = [aws_ec2_host.test.instance_type]
}
`
filter {
name = "tag-key"
values = [%[1]q]
}
}
`, rName))
}
6 changes: 4 additions & 2 deletions aws/internal/service/ec2/finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,10 @@ func HostByID(conn *ec2.EC2, id string) (*ec2.Host, error) {
}

func HostByIDAndFilters(conn *ec2.EC2, id string, filters []*ec2.Filter) (*ec2.Host, error) {
input := &ec2.DescribeHostsInput{
HostIds: aws.StringSlice([]string{id}),
input := &ec2.DescribeHostsInput{}

if id != "" {
input.HostIds = aws.StringSlice([]string{id})
}

if len(filters) > 0 {
Expand Down
14 changes: 14 additions & 0 deletions aws/internal/tfresource/not_found_error.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tfresource

import (
"errors"
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
Expand Down Expand Up @@ -77,3 +78,16 @@ func (e *TooManyResultsError) As(target interface{}) bool {

return true
}

// SingularDataSourceReadError returns a standard error message for a non-nil resource read error.
func SingularDataSourceReadError(resourceType string, err error) error {
if NotFound(err) {
if errors.Is(err, &TooManyResultsError{}) {
return fmt.Errorf("multiple %[1]ss matched; use additional constraints to reduce matches to a single %[1]s", resourceType)
}

return fmt.Errorf("no matching %[1]s found", resourceType)
}

return fmt.Errorf("error reading %s: %w", resourceType, err)
}
Loading

0 comments on commit f190485

Please sign in to comment.