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

Add aws_dedicated_host for terraform-provider-aws #10817

Merged
merged 38 commits into from
Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ef2934a
Added initial commit
rpomykala Nov 7, 2019
4f3c514
Added pointer to a hostID
rpomykala Nov 7, 2019
b93bd42
Added tags
rpomykala Nov 9, 2019
3728b79
Replaced error strings with host instead of 'instance'
rpomykala Nov 9, 2019
134c9e9
Added data source
rpomykala Nov 9, 2019
05cafa5
Change Printf format from *string to string
rpomykala Nov 9, 2019
b8c84ac
Added two more options to update function
rpomykala Nov 9, 2019
e4bbf40
small cleanup
rpomykala Nov 9, 2019
dc1012a
Added data_source_aws_dedicated_host
rpomykala Nov 10, 2019
4b4fd8d
Cleanup code and add website resource
rpomykala Nov 12, 2019
0da3341
Fixed hostOpts, added tests and fixed data source
rpomykala Apr 10, 2020
f70a99d
Fix to website page
rpomykala Apr 10, 2020
b6669f0
Fix tagspec and website
rpomykala Apr 10, 2020
ab63ffc
Fix filters
rpomykala Apr 10, 2020
4117335
Website linting
rpomykala Apr 10, 2020
084f760
Tags fix
rpomykala Apr 10, 2020
302df62
Docscheck fix
rpomykala Apr 10, 2020
77bfd7e
Final fixes for linting
rpomykala Apr 10, 2020
b4da87b
AWSerr refactor
rpomykala Apr 15, 2020
b65f9c5
extraneous forcenew removed
rpomykala Apr 15, 2020
7460312
Add validatefunc
rpomykala Apr 15, 2020
b9a4d61
removed incompatible type
rpomykala Apr 15, 2020
6d95478
removed incompatible type
rpomykala Apr 15, 2020
1666112
Refactored acc to conversations
rpomykala Apr 19, 2020
e9be3b5
Improved testing for data_source and added web resource, added attrib…
rpomykala Apr 22, 2020
104d903
Add missing attribute
rpomykala Apr 22, 2020
2159ccb
Added resource_aws_dedicated_host_test.go
rpomykala Apr 23, 2020
92dffe8
Migrate to v2 SDK
rpomykala Oct 22, 2020
bd5387e
Corrections after rebase.
ewbankkit Sep 23, 2021
6ebe19d
'aws_dedicated_host' -> 'aws_ec2_host'; Rename files.
ewbankkit Sep 23, 2021
e006f22
'aws_dedicated_host' -> 'aws_ec2_host'; Rename functions and resources.
ewbankkit Sep 23, 2021
7596cbb
r/aws_ec2_host: Tidy up attributes.
ewbankkit Sep 23, 2021
99eeb77
r/aws_ec2_host: Simplify.
ewbankkit Sep 24, 2021
936a572
r/aws_ec2_host: Get acceptance tests working.
ewbankkit Sep 24, 2021
fe47867
Add CHANGELOG entry.
ewbankkit Sep 24, 2021
f190485
d/aws_ec2_host: Get acceptance tests working.
ewbankkit Sep 24, 2021
7cd20a9
Fix terrafmt errors.
ewbankkit Sep 24, 2021
2e7a7a4
Fix semgrep error.
ewbankkit Sep 24, 2021
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
7 changes: 7 additions & 0 deletions .changelog/10817.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-resource
aws_ec2_host
```

```release-note:new-data-source
aws_ec2_host
```
107 changes: 107 additions & 0 deletions aws/data_source_aws_ec2_host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package aws

import (
"fmt"

"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{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"auto_placement": {
Type: schema.TypeString,
Computed: true,
},
"availability_zone": {
Type: schema.TypeString,
Computed: true,
},
"cores": {
Type: schema.TypeInt,
Computed: true,
},
"filter": dataSourceFiltersSchema(),
"host_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"host_recovery": {
Type: schema.TypeString,
Computed: true,
},
"instance_family": {
Type: schema.TypeString,
Computed: true,
},
"instance_type": {
Type: schema.TypeString,
Computed: true,
},
"owner_id": {
Type: schema.TypeString,
Computed: true,
},
"sockets": {
Type: schema.TypeInt,
Computed: true,
},
"tags": tagsSchemaComputed(),
"total_vcpus": {
Type: schema.TypeInt,
Computed: true,
},
},
}
}

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

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

if err != nil {
return tfresource.SingularDataSourceFindError("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)
}

return nil
}
119 changes: 119 additions & 0 deletions aws/data_source_aws_ec2_host_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +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: testAccDataSourceAWSEc2HostConfig(rName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(dataSourceName, "arn", resourceName, "arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "auto_placement", resourceName, "auto_placement"),
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"),
),
},
},
})
}

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.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"),
),
},
},
})
}

func testAccDataSourceAWSEc2HostConfig(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 = {
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]
}

filter {
name = "instance-type"
values = [aws_ec2_host.test.instance_type]
}

filter {
name = "tag-key"
values = [%[1]q]
}
}
`, rName))
}
5 changes: 5 additions & 0 deletions aws/internal/service/ec2/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ const (
ErrCodeInvalidCarrierGatewayIDNotFound = "InvalidCarrierGatewayID.NotFound"
)

const (
ErrCodeClientInvalidHostIDNotFound = "Client.InvalidHostID.NotFound"
ErrCodeInvalidHostIDNotFound = "InvalidHostID.NotFound"
)

const (
ErrCodeInvalidNetworkInterfaceIDNotFound = "InvalidNetworkInterfaceID.NotFound"
)
Expand Down
56 changes: 56 additions & 0 deletions aws/internal/service/ec2/finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,62 @@ func ClientVpnRouteByID(conn *ec2.EC2, routeID string) (*ec2.DescribeClientVpnRo
return ClientVpnRoute(conn, endpointID, targetSubnetID, destinationCidr)
}

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

return Host(conn, input)
}

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

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

if len(filters) > 0 {
input.Filter = filters
}

return Host(conn, input)
}

func Host(conn *ec2.EC2, input *ec2.DescribeHostsInput) (*ec2.Host, error) {
output, err := conn.DescribeHosts(input)

if tfawserr.ErrCodeEquals(err, tfec2.ErrCodeInvalidHostIDNotFound) {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil || len(output.Hosts) == 0 || output.Hosts[0] == nil || output.Hosts[0].HostProperties == nil {
return nil, tfresource.NewEmptyResultError(input)
}

if count := len(output.Hosts); count > 1 {
return nil, tfresource.NewTooManyResultsError(count, input)
}

host := output.Hosts[0]

if state := aws.StringValue(host.State); state == ec2.AllocationStateReleased || state == ec2.AllocationStateReleasedPermanentFailure {
return nil, &resource.NotFoundError{
Message: state,
LastRequest: input,
}
}

return host, nil
}

// InstanceByID looks up a Instance by ID. When not found, returns nil and potentially an API error.
func InstanceByID(conn *ec2.EC2, id string) (*ec2.Instance, error) {
input := &ec2.DescribeInstancesInput{
Expand Down
20 changes: 18 additions & 2 deletions aws/internal/service/ec2/waiter/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,22 @@ func VpnGatewayVpcAttachmentState(conn *ec2.EC2, vpnGatewayID, vpcID string) res
}
}

func HostState(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := finder.HostByID(conn, id)

if tfresource.NotFound(err) {
return nil, "", nil
}

if err != nil {
return nil, "", err
}

return output, aws.StringValue(output.State), nil
}
}

func ManagedPrefixListState(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := finder.ManagedPrefixListByID(conn, id)
Expand All @@ -499,7 +515,7 @@ func ManagedPrefixListState(conn *ec2.EC2, id string) resource.StateRefreshFunc

func VpcEndpointState(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
vpcEndpoint, err := finder.VpcEndpointByID(conn, id)
output, err := finder.VpcEndpointByID(conn, id)

if tfresource.NotFound(err) {
return nil, "", nil
Expand All @@ -509,7 +525,7 @@ func VpcEndpointState(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return nil, "", err
}

return vpcEndpoint, aws.StringValue(vpcEndpoint.State), nil
return output, aws.StringValue(output.State), nil
}
}

Expand Down
Loading