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

Added the PrimaryIpv6 argument in launch template network configuration. #37142

Merged
merged 18 commits into from
Jul 5, 2024
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
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
Loading