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

fix(vpc): added support for different size ipv6 cidrs #35614

Merged
merged 8 commits into from
Jul 26, 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
31 changes: 31 additions & 0 deletions .changelog/35614.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
```release-note:enhancement
resource/aws_vpc: Support `ipv6_cidr_block` sizes between `/44` and `/60` in increments of /4
```

```release-note:enhancement
resource/aws_vpc: Support `ipv6_netmask_length` values between `44` and `60` in increments of 4
```

```release-note:enhancement
resource/aws_default_vpc: Support `ipv6_cidr_block` sizes between `/44` and `/60` in increments of /4
```

```release-note:enhancement
resource/aws_default_vpc: Support `ipv6_netmask_length` values between `44` and `60` in increments of 4
```

```release-note:enhancement
resource/aws_vpc_ipv6_cidr_block_association: Support `ipv6_cidr_block` sizes between `/44` and `/60` in increments of /4
```

```release-note:enhancement
resource/aws_vpc_ipv6_cidr_block_association: Support `ipv6_netmask_length` values between `44` and `60` in increments of 4
```

```release-note:enhancement
resource/aws_vpc_security_group_ingress_rule: Add `tags` to the `AuthorizeSecurityGroupIngress` EC2 API call instead of making a separate `CreateTags` call
```

```release-note:enhancement
resource/aws_vpc_security_group_egress_rule: Add `tags` to the `AuthorizeSecurityGroupEgress` EC2 API call instead of making a separate `CreateTags` call
```
28 changes: 19 additions & 9 deletions internal/service/ec2/vpc_.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,29 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/enum"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/slices"
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
"github.com/hashicorp/terraform-provider-aws/names"
)

const (
vpcCIDRMaxIPv4Netmask = 28
vpcCIDRMinIPv4Netmask = 16
vpcCIDRMaxIPv6Netmask = 56
vpcCIDRMaxIPv4Netmask = 28
vpcCIDRMinIPv4Netmask = 16
vpcCIDRMaxIPv6Netmask = 60
vpcCIDRMinIPv6Netmask = 44
vpcCIDRIPv6NetmaskStep = 4
)

var (
vpcCIDRValidIPv6Netmasks = tfslices.Range(vpcCIDRMinIPv6Netmask, vpcCIDRMaxIPv6Netmask+1, vpcCIDRIPv6NetmaskStep)
validVPCIPv6CIDRBlock = validation.All(
verify.ValidIPv6CIDRNetworkAddress,
validation.Any(tfslices.ApplyToAll(vpcCIDRValidIPv6Netmasks, func(v int) schema.SchemaValidateFunc {
return validation.IsCIDRNetwork(v, v)
})...),
)
)

// @SDKResource("aws_vpc", name="VPC")
Expand Down Expand Up @@ -140,9 +152,7 @@ func resourceVPC() *schema.Resource {
Computed: true,
ConflictsWith: []string{"ipv6_netmask_length", "assign_generated_ipv6_cidr_block"},
RequiredWith: []string{"ipv6_ipam_pool_id"},
ValidateFunc: validation.All(
verify.ValidIPv6CIDRNetworkAddress,
validation.IsCIDRNetwork(vpcCIDRMaxIPv6Netmask, vpcCIDRMaxIPv6Netmask)),
ValidateFunc: validVPCIPv6CIDRBlock,
},
"ipv6_cidr_block_network_border_group": {
Type: schema.TypeString,
Expand All @@ -158,7 +168,7 @@ func resourceVPC() *schema.Resource {
"ipv6_netmask_length": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntInSlice([]int{vpcCIDRMaxIPv6Netmask}),
ValidateFunc: validation.IntInSlice(vpcCIDRValidIPv6Netmasks),
ConflictsWith: []string{"ipv6_cidr_block"},
RequiredWith: []string{"ipv6_ipam_pool_id"},
},
Expand Down Expand Up @@ -729,7 +739,7 @@ func findIPAMPoolAllocationsForVPC(ctx context.Context, conn *ec2.Client, poolID
return nil, err
}

output = slices.Filter(output, func(v types.IpamPoolAllocation) bool {
output = tfslices.Filter(output, func(v types.IpamPoolAllocation) bool {
return string(v.ResourceType) == string(types.IpamPoolAllocationResourceTypeVpc) && aws.ToString(v.ResourceId) == vpcID
})

Expand Down
6 changes: 2 additions & 4 deletions internal/service/ec2/vpc_default_vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,7 @@ func resourceDefaultVPC() *schema.Resource {
Computed: true,
ConflictsWith: []string{"ipv6_netmask_length", "assign_generated_ipv6_cidr_block"},
RequiredWith: []string{"ipv6_ipam_pool_id"},
ValidateFunc: validation.All(
verify.ValidIPv6CIDRNetworkAddress,
validation.IsCIDRNetwork(vpcCIDRMaxIPv6Netmask, vpcCIDRMaxIPv6Netmask)),
ValidateFunc: validVPCIPv6CIDRBlock,
},
"ipv6_cidr_block_network_border_group": {
Type: schema.TypeString,
Expand All @@ -136,7 +134,7 @@ func resourceDefaultVPC() *schema.Resource {
"ipv6_netmask_length": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntInSlice([]int{vpcCIDRMaxIPv6Netmask}),
ValidateFunc: validation.IntInSlice(vpcCIDRValidIPv6Netmasks),
ConflictsWith: []string{"ipv6_cidr_block"},
RequiredWith: []string{"ipv6_ipam_pool_id"},
},
Expand Down
15 changes: 6 additions & 9 deletions internal/service/ec2/vpc_ipv6_cidr_block_association.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
"github.com/hashicorp/terraform-provider-aws/names"
)

Expand All @@ -44,13 +43,11 @@ func resourceVPCIPv6CIDRBlockAssociation() *schema.Resource {
},
Schema: map[string]*schema.Schema{
"ipv6_cidr_block": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validation.All(
verify.ValidIPv6CIDRNetworkAddress,
validation.IsCIDRNetwork(vpcCIDRMaxIPv6Netmask, vpcCIDRMaxIPv6Netmask)),
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validVPCIPv6CIDRBlock,
},
// ipam parameters are not required by the API but other usage mechanisms are not implemented yet. TODO ipv6 options:
// --amazon-provided-ipv6-cidr-block
Expand All @@ -64,7 +61,7 @@ func resourceVPCIPv6CIDRBlockAssociation() *schema.Resource {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
ValidateFunc: validation.IntInSlice([]int{vpcCIDRMaxIPv6Netmask}),
ValidateFunc: validation.IntInSlice(vpcCIDRValidIPv6Netmasks),
ConflictsWith: []string{"ipv6_cidr_block"},
// This RequiredWith setting should be applied once L57 is completed
// RequiredWith: []string{"ipv6_ipam_pool_id"},
Expand Down
5 changes: 3 additions & 2 deletions internal/service/ec2/vpc_security_group_egress_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ func (r *securityGroupEgressRuleResource) create(ctx context.Context, data *secu
conn := r.Meta().EC2Client(ctx)

input := &ec2.AuthorizeSecurityGroupEgressInput{
GroupId: fwflex.StringFromFramework(ctx, data.SecurityGroupID),
IpPermissions: []awstypes.IpPermission{data.expandIPPermission(ctx)},
GroupId: fwflex.StringFromFramework(ctx, data.SecurityGroupID),
IpPermissions: []awstypes.IpPermission{data.expandIPPermission(ctx)},
TagSpecifications: getTagSpecificationsIn(ctx, awstypes.ResourceTypeSecurityGroupRule),
}

output, err := conn.AuthorizeSecurityGroupEgress(ctx, input)
Expand Down
12 changes: 3 additions & 9 deletions internal/service/ec2/vpc_security_group_ingress_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ func (r *securityGroupIngressRuleResource) create(ctx context.Context, data *sec
conn := r.Meta().EC2Client(ctx)

input := &ec2.AuthorizeSecurityGroupIngressInput{
GroupId: fwflex.StringFromFramework(ctx, data.SecurityGroupID),
IpPermissions: []awstypes.IpPermission{data.expandIPPermission(ctx)},
GroupId: fwflex.StringFromFramework(ctx, data.SecurityGroupID),
IpPermissions: []awstypes.IpPermission{data.expandIPPermission(ctx)},
TagSpecifications: getTagSpecificationsIn(ctx, awstypes.ResourceTypeSecurityGroupRule),
}

output, err := conn.AuthorizeSecurityGroupIngress(ctx, input)
Expand Down Expand Up @@ -258,13 +259,6 @@ func (r *securityGroupRuleResource) Create(ctx context.Context, request resource
data.SecurityGroupRuleID = types.StringValue(securityGroupRuleID)
data.setID()

conn := r.Meta().EC2Client(ctx)
if err := createTags(ctx, conn, data.ID.ValueString(), getTagsIn(ctx)); err != nil {
response.Diagnostics.AddError(fmt.Sprintf("setting VPC Security Group Rule (%s) tags", data.ID.ValueString()), err.Error())

return
}

response.Diagnostics.Append(response.State.Set(ctx, &data)...)
}

Expand Down
34 changes: 33 additions & 1 deletion internal/slices/slices.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

package slices

import "slices"
import (
"slices"
)

// Reverse returns a reversed copy of the slice `s`.
func Reverse[S ~[]E, E any](s S) S {
Expand Down Expand Up @@ -146,3 +148,33 @@ func IndexOf[S ~[]any, E comparable](s S, v E) int {
}
return -1
}

type signed interface {
~int | ~int32 | ~int64
}

// Range returns a slice of integers from `start` to `stop` (exclusive) using the specified `step`.
func Range[T signed](start, stop, step T) []T {
v := make([]T, 0)

switch {
case step > 0:
if start >= stop {
return nil
}
for i := start; i < stop; i += step {
v = append(v, i)
}
case step < 0:
if start <= stop {
return nil
}
for i := start; i > stop; i += step {
v = append(v, i)
}
default:
return nil
}

return v
}
84 changes: 84 additions & 0 deletions internal/slices/slices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,87 @@ func TestIndexOf(t *testing.T) {
})
}
}

func TestRange(t *testing.T) {
t.Parallel()

type testCase struct {
start, stop, step int
expected []int
}
tests := map[string]testCase{
"0 step": {
start: 0,
stop: 10,
step: 0,
expected: nil,
},
"start == stop": {
start: 0,
stop: 0,
step: 1,
expected: nil,
},
"start == 0, step == 1": {
start: 0,
stop: 10,
step: 1,
expected: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
},
"start == 1, step == 1": {
start: 1,
stop: 11,
step: 1,
expected: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
},
"start == 0, step == 5": {
start: 0,
stop: 30,
step: 5,
expected: []int{0, 5, 10, 15, 20, 25},
},
"start == 0, step == 11": {
start: 0,
stop: 30,
step: 11,
expected: []int{0, 11, 22},
},
"start == 0, step == -1": {
start: 0,
stop: -10,
step: -1,
expected: []int{0, -1, -2, -3, -4, -5, -6, -7, -8, -9},
},
"start == 0, stop = 5, step == -1": {
start: 0,
stop: 5,
step: -1,
expected: nil,
},
"start == 0, stop = -5, step == 1": {
start: 0,
stop: -5,
step: 1,
expected: nil,
},
"start == 1, step == -5": {
start: 1,
stop: -30,
step: -5,
expected: []int{1, -4, -9, -14, -19, -24, -29},
},
}

for name, test := range tests {
name, test := name, test
t.Run(name, func(t *testing.T) {
t.Parallel()

got := Range(test.start, test.stop, test.step)

if diff := cmp.Diff(got, test.expected); diff != "" {
t.Errorf("unexpected diff (+wanted, -got): %s", diff)
}
})
}
}
2 changes: 1 addition & 1 deletion website/docs/r/vpc.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ This resource supports the following arguments:
* `ipv4_netmask_length` - (Optional) The netmask length of the IPv4 CIDR you want to allocate to this VPC. Requires specifying a `ipv4_ipam_pool_id`.
* `ipv6_cidr_block` - (Optional) IPv6 CIDR block to request from an IPAM Pool. Can be set explicitly or derived from IPAM using `ipv6_netmask_length`.
* `ipv6_ipam_pool_id` - (Optional) IPAM Pool ID for a IPv6 pool. Conflicts with `assign_generated_ipv6_cidr_block`.
* `ipv6_netmask_length` - (Optional) Netmask length to request from IPAM Pool. Conflicts with `ipv6_cidr_block`. This can be omitted if IPAM pool as a `allocation_default_netmask_length` set. Valid values: `56`.
* `ipv6_netmask_length` - (Optional) Netmask length to request from IPAM Pool. Conflicts with `ipv6_cidr_block`. This can be omitted if IPAM pool as a `allocation_default_netmask_length` set. Valid values are from `44` to `60` in increments of 4.
* `ipv6_cidr_block_network_border_group` - (Optional) By default when an IPv6 CIDR is assigned to a VPC a default ipv6_cidr_block_network_border_group will be set to the region of the VPC. This can be changed to restrict advertisement of public addresses to specific Network Border Groups such as LocalZones.
* `enable_dns_support` - (Optional) A boolean flag to enable/disable DNS support in the VPC. Defaults to true.
* `enable_network_address_usage_metrics` - (Optional) Indicates whether Network Address Usage metrics are enabled for your VPC. Defaults to false.
Expand Down
Loading