Skip to content

Commit

Permalink
Merge pull request #35614 from k1rk/ipv6-update-mask
Browse files Browse the repository at this point in the history
fix(vpc): added support for different size ipv6 cidrs
  • Loading branch information
ewbankkit authored Jul 26, 2024
2 parents 0f124c9 + 4021bcc commit 8d67bd5
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 35 deletions.
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

0 comments on commit 8d67bd5

Please sign in to comment.