Skip to content

Commit

Permalink
Merge pull request #37142 from achernev/f-aws_launch_template-primary…
Browse files Browse the repository at this point in the history
…_ipv6

Added the `PrimaryIpv6` argument in launch template network configuration.
  • Loading branch information
ewbankkit authored Jul 5, 2024
2 parents 51c0145 + a04285f commit 74cd6e1
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 43 deletions.
7 changes: 7 additions & 0 deletions .changelog/36754.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_ec2_capacity_reservation: Add configurable timeouts
```

```release-note:enhancement
resource/aws_ec2_capacity_reservation: Retry `InsufficientInstanceCapacity` errors
```
7 changes: 7 additions & 0 deletions .changelog/37142.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_launch_template: Add `network_interfaces.primary_ipv6` argument
```

```release-note:enhancement
data-source/aws_launch_template: Add `network_interfaces.primary_ipv6` attribute
```
12 changes: 9 additions & 3 deletions internal/service/ec2/ec2_capacity_reservation.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ func resourceCapacityReservation() *schema.Resource {

CustomizeDiff: verify.SetTagsDiff,

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Update: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
},

Schema: map[string]*schema.Schema{
names.AttrARN: {
Type: schema.TypeString,
Expand Down Expand Up @@ -175,7 +181,7 @@ func resourceCapacityReservationCreate(ctx context.Context, d *schema.ResourceDa

d.SetId(aws.ToString(output.CapacityReservation.CapacityReservationId))

if err := waitCapacityReservationActive(ctx, conn, d.Id()); err != nil {
if _, err := waitCapacityReservationActive(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for EC2 Capacity Reservation (%s) create: %s", d.Id(), err)
}

Expand Down Expand Up @@ -245,7 +251,7 @@ func resourceCapacityReservationUpdate(ctx context.Context, d *schema.ResourceDa
return sdkdiag.AppendErrorf(diags, "updating EC2 Capacity Reservation (%s): %s", d.Id(), err)
}

if err := waitCapacityReservationActive(ctx, conn, d.Id()); err != nil {
if _, err := waitCapacityReservationActive(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for EC2 Capacity Reservation (%s) update: %s", d.Id(), err)
}
}
Expand All @@ -270,7 +276,7 @@ func resourceCapacityReservationDelete(ctx context.Context, d *schema.ResourceDa
return sdkdiag.AppendErrorf(diags, "deleting EC2 Capacity Reservation (%s): %s", d.Id(), err)
}

if _, err := waitCapacityReservationDeleted(ctx, conn, d.Id()); err != nil {
if _, err := waitCapacityReservationDeleted(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for EC2 Capacity Reservation (%s) delete: %s", d.Id(), err)
}

Expand Down
33 changes: 23 additions & 10 deletions internal/service/ec2/ec2_launch_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"context"
"fmt"
"log"
"strconv"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
Expand Down Expand Up @@ -830,6 +829,12 @@ func resourceLaunchTemplate() *schema.Resource {
Type: schema.TypeString,
Optional: true,
},
"primary_ipv6": {
Type: nullable.TypeNullableBool,
Optional: true,
DiffSuppressFunc: nullable.DiffSuppressNullableBool,
ValidateFunc: nullable.ValidateTypeStringNullableBool,
},
"private_ip_address": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -1034,7 +1039,7 @@ func resourceLaunchTemplateRead(ctx context.Context, d *schema.ResourceData, met
return sdkdiag.AppendErrorf(diags, "reading EC2 Launch Template (%s): %s", d.Id(), err)
}

version := strconv.FormatInt(aws.ToInt64(lt.LatestVersionNumber), 10)
version := flex.Int64ToStringValue(lt.LatestVersionNumber)
ltv, err := findLaunchTemplateVersionByTwoPartKey(ctx, conn, d.Id(), version)

if err != nil {
Expand Down Expand Up @@ -1134,9 +1139,9 @@ func resourceLaunchTemplateUpdate(ctx context.Context, d *schema.ResourceData, m
}

if d.Get("update_default_version").(bool) {
input.DefaultVersion = aws.String(strconv.FormatInt(latestVersion, 10))
input.DefaultVersion = flex.Int64ValueToString(latestVersion)
} else if d.HasChange("default_version") {
input.DefaultVersion = aws.String(strconv.Itoa(d.Get("default_version").(int)))
input.DefaultVersion = flex.IntValueToString(d.Get("default_version").(int))
}

_, err := conn.ModifyLaunchTemplate(ctx, input)
Expand Down Expand Up @@ -2019,6 +2024,10 @@ func expandLaunchTemplateInstanceNetworkInterfaceSpecificationRequest(tfMap map[
apiObject.NetworkInterfaceId = aws.String(v)
}

if v, null, _ := nullable.Bool(tfMap["primary_ipv6"].(string)).ValueBool(); !null {
apiObject.PrimaryIpv6 = aws.Bool(v)
}

if v, ok := tfMap[names.AttrSecurityGroups].(*schema.Set); ok && v.Len() > 0 {
for _, v := range v.List() {
apiObject.Groups = append(apiObject.Groups, v.(string))
Expand Down Expand Up @@ -2183,7 +2192,7 @@ func flattenResponseLaunchTemplateData(ctx context.Context, conn *ec2.Client, d
d.Set("disable_api_stop", apiObject.DisableApiStop)
d.Set("disable_api_termination", apiObject.DisableApiTermination)
if apiObject.EbsOptimized != nil {
d.Set("ebs_optimized", strconv.FormatBool(aws.ToBool(apiObject.EbsOptimized)))
d.Set("ebs_optimized", flex.BoolToStringValue(apiObject.EbsOptimized))
} else {
d.Set("ebs_optimized", "")
}
Expand Down Expand Up @@ -2341,11 +2350,11 @@ func flattenLaunchTemplateEBSBlockDevice(apiObject *awstypes.LaunchTemplateEbsBl
tfMap := map[string]interface{}{}

if v := apiObject.DeleteOnTermination; v != nil {
tfMap[names.AttrDeleteOnTermination] = strconv.FormatBool(aws.ToBool(v))
tfMap[names.AttrDeleteOnTermination] = flex.BoolToStringValue(v)
}

if v := apiObject.Encrypted; v != nil {
tfMap[names.AttrEncrypted] = strconv.FormatBool(aws.ToBool(v))
tfMap[names.AttrEncrypted] = flex.BoolToStringValue(v)
}

if v := apiObject.Iops; v != nil {
Expand Down Expand Up @@ -2879,15 +2888,15 @@ func flattenLaunchTemplateInstanceNetworkInterfaceSpecification(apiObject awstyp
tfMap := map[string]interface{}{}

if v := apiObject.AssociateCarrierIpAddress; v != nil {
tfMap["associate_carrier_ip_address"] = strconv.FormatBool(aws.ToBool(v))
tfMap["associate_carrier_ip_address"] = flex.BoolToStringValue(v)
}

if v := apiObject.AssociatePublicIpAddress; v != nil {
tfMap["associate_public_ip_address"] = strconv.FormatBool(aws.ToBool(v))
tfMap["associate_public_ip_address"] = flex.BoolToStringValue(v)
}

if v := apiObject.DeleteOnTermination; v != nil {
tfMap[names.AttrDeleteOnTermination] = strconv.FormatBool(aws.ToBool(v))
tfMap[names.AttrDeleteOnTermination] = flex.BoolToStringValue(v)
}

if v := apiObject.Description; v != nil {
Expand Down Expand Up @@ -2966,6 +2975,10 @@ func flattenLaunchTemplateInstanceNetworkInterfaceSpecification(apiObject awstyp
tfMap[names.AttrNetworkInterfaceID] = aws.ToString(v)
}

if v := apiObject.PrimaryIpv6; v != nil {
tfMap["primary_ipv6"] = flex.BoolToStringValue(v)
}

if v := apiObject.PrivateIpAddress; v != nil {
tfMap["private_ip_address"] = aws.ToString(v)
}
Expand Down
8 changes: 6 additions & 2 deletions internal/service/ec2/ec2_launch_template_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package ec2
import (
"context"
"fmt"
"strconv"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
Expand All @@ -16,6 +15,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/names"
Expand Down Expand Up @@ -673,6 +673,10 @@ func dataSourceLaunchTemplate() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"primary_ipv6": {
Type: schema.TypeString,
Computed: true,
},
"private_ip_address": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -819,7 +823,7 @@ func dataSourceLaunchTemplateRead(ctx context.Context, d *schema.ResourceData, m

d.SetId(aws.ToString(lt.LaunchTemplateId))

version := strconv.FormatInt(aws.ToInt64(lt.LatestVersionNumber), 10)
version := flex.Int64ToStringValue(lt.LatestVersionNumber)
ltv, err := findLaunchTemplateVersionByTwoPartKey(ctx, conn, d.Id(), version)

if err != nil {
Expand Down
87 changes: 87 additions & 0 deletions internal/service/ec2/ec2_launch_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ func TestAccEC2LaunchTemplate_networkInterface(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.ipv6_prefixes.#", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.network_card_index", acctest.Ct0),
resource.TestCheckResourceAttrSet(resourceName, "network_interfaces.0.network_interface_id"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.primary_ipv6", ""),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.private_ip_address", ""),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.security_groups.#", acctest.Ct0),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.subnet_id", ""),
Expand Down Expand Up @@ -1391,6 +1392,54 @@ func TestAccEC2LaunchTemplate_instanceMarketOptions(t *testing.T) {
})
}

func TestAccEC2LaunchTemplate_primaryIPv6(t *testing.T) {
ctx := acctest.Context(t)
var template awstypes.LaunchTemplate
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_launch_template.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.EC2ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckLaunchTemplateDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccLaunchTemplateConfig_primaryIPv6(rName, acctest.CtTrue),
Check: resource.ComposeTestCheckFunc(
testAccCheckLaunchTemplateExists(ctx, resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", acctest.Ct1),
resource.TestCheckResourceAttrSet(resourceName, "network_interfaces.0.network_interface_id"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.primary_ipv6", acctest.CtTrue),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccLaunchTemplateConfig_primaryIPv6(rName, acctest.CtFalse),
Check: resource.ComposeTestCheckFunc(
testAccCheckLaunchTemplateExists(ctx, resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", acctest.Ct1),
resource.TestCheckResourceAttrSet(resourceName, "network_interfaces.0.network_interface_id"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.primary_ipv6", acctest.CtFalse),
),
},
{
Config: testAccLaunchTemplateConfig_primaryIPv6(rName, "null"),
Check: resource.ComposeTestCheckFunc(
testAccCheckLaunchTemplateExists(ctx, resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", acctest.Ct1),
resource.TestCheckResourceAttrSet(resourceName, "network_interfaces.0.network_interface_id"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.primary_ipv6", ""),
),
},
},
})
}

func TestAccEC2LaunchTemplate_instanceRequirements_memoryMiBAndVCPUCount(t *testing.T) {
ctx := acctest.Context(t)
var template awstypes.LaunchTemplate
Expand Down Expand Up @@ -3834,6 +3883,44 @@ resource "aws_launch_template" "test" {
`, rName, associatePublicIPAddress)
}

func testAccLaunchTemplateConfig_primaryIPv6(rName, primaryIPv6 string) string {
return fmt.Sprintf(`
resource "aws_vpc" "test" {
cidr_block = "10.1.0.0/16"
tags = {
Name = %[1]q
}
}
resource "aws_subnet" "test" {
vpc_id = aws_vpc.test.id
cidr_block = "10.1.0.0/24"
tags = {
Name = %[1]q
}
}
resource "aws_network_interface" "test" {
subnet_id = aws_subnet.test.id
tags = {
Name = %[1]q
}
}
resource "aws_launch_template" "test" {
name = %[1]q
network_interfaces {
network_interface_id = aws_network_interface.test.id
primary_ipv6 = %[2]s
}
}
`, rName, primaryIPv6)
}

func testAccLaunchTemplateConfig_associateCarrierIPAddress(rName, associateCarrierIPAddress string) string {
return fmt.Sprintf(`
resource "aws_vpc" "test" {
Expand Down
21 changes: 4 additions & 17 deletions internal/service/ec2/service_package.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import (
retry_sdkv2 "github.com/aws/aws-sdk-go-v2/aws/retry"
ec2_sdkv2 "github.com/aws/aws-sdk-go-v2/service/ec2"
aws_sdkv1 "github.com/aws/aws-sdk-go/aws"
request_sdkv1 "github.com/aws/aws-sdk-go/aws/request"
session_sdkv1 "github.com/aws/aws-sdk-go/aws/session"
ec2_sdkv1 "github.com/aws/aws-sdk-go/service/ec2"
tfawserr_sdkv1 "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
tfawserr_sdkv2 "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
Expand All @@ -38,21 +36,6 @@ func (p *servicePackage) NewConn(ctx context.Context, config map[string]any) (*e
return ec2_sdkv1.New(sess.Copy(&cfg)), nil
}

// CustomizeConn customizes a new AWS SDK for Go v1 client for this service package's AWS API.
func (p *servicePackage) CustomizeConn(ctx context.Context, conn *ec2_sdkv1.EC2) (*ec2_sdkv1.EC2, error) {
conn.Handlers.Retry.PushBack(func(r *request_sdkv1.Request) {
switch err := r.Error; r.Operation.Name {
case "RunInstances":
// `InsufficientInstanceCapacity` error has status code 500 and AWS SDK try retry this error by default.
if tfawserr_sdkv1.ErrCodeEquals(err, errCodeInsufficientInstanceCapacity) {
r.Retryable = aws_sdkv1.Bool(false)
}
}
})

return conn, nil
}

// NewClient returns a new AWS SDK for Go v2 client for this service package's AWS API.
func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*ec2_sdkv2.Client, error) {
cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config))
Expand All @@ -66,6 +49,10 @@ func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (
return aws_sdkv2.TrueTernary
}

if tfawserr_sdkv2.ErrCodeEquals(err, errCodeInsufficientInstanceCapacity) { // CreateCapacityReservation, RunInstances
return aws_sdkv2.TrueTernary
}

if tfawserr_sdkv2.ErrMessageContains(err, errCodeOperationNotPermitted, "Endpoint cannot be created while another endpoint is being created") { // CreateClientVpnEndpoint
return aws_sdkv2.TrueTernary
}
Expand Down
21 changes: 10 additions & 11 deletions internal/service/ec2/waitv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,30 +57,29 @@ func waitAvailabilityZoneGroupNotOptedIn(ctx context.Context, conn *ec2.Client,
return nil, err
}

const (
CapacityReservationActiveTimeout = 2 * time.Minute
CapacityReservationDeletedTimeout = 2 * time.Minute
)

func waitCapacityReservationActive(ctx context.Context, conn *ec2.Client, id string) error {
func waitCapacityReservationActive(ctx context.Context, conn *ec2.Client, id string, timeout time.Duration) (*awstypes.CapacityReservation, error) { //nolint:unparam
stateConf := &retry.StateChangeConf{
Pending: enum.Slice(awstypes.CapacityReservationStatePending),
Target: enum.Slice(awstypes.CapacityReservationStateActive),
Refresh: statusCapacityReservation(ctx, conn, id),
Timeout: CapacityReservationActiveTimeout,
Timeout: timeout,
}

_, err := stateConf.WaitForStateContext(ctx)
outputRaw, err := stateConf.WaitForStateContext(ctx)

return err
if output, ok := outputRaw.(*awstypes.CapacityReservation); ok {
return output, err
}

return nil, err
}

func waitCapacityReservationDeleted(ctx context.Context, conn *ec2.Client, id string) (*awstypes.CapacityReservation, error) {
func waitCapacityReservationDeleted(ctx context.Context, conn *ec2.Client, id string, timeout time.Duration) (*awstypes.CapacityReservation, error) {
stateConf := &retry.StateChangeConf{
Pending: enum.Slice(awstypes.CapacityReservationStateActive),
Target: []string{},
Refresh: statusCapacityReservation(ctx, conn, id),
Timeout: CapacityReservationDeletedTimeout,
Timeout: timeout,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)
Expand Down
Loading

0 comments on commit 74cd6e1

Please sign in to comment.