Skip to content

Commit

Permalink
Add IPv6CIDR range check to configValidator.
Browse files Browse the repository at this point in the history
  • Loading branch information
axel7born committed Dec 6, 2023
1 parent 196f04c commit fde766d
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 22 deletions.
65 changes: 45 additions & 20 deletions pkg/aws/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,20 @@ func (c *Client) DeleteVpcDhcpOptions(ctx context.Context, id string) error {
return ignoreNotFound(err)
}

// RetryableIPv6CIDRError is a custom error type.
type RetryableIPv6CIDRError struct{}

// Error prints the error message of the RetryableIPv6CIDRError error.
func (e *RetryableIPv6CIDRError) Error() string {
return "no ipv6 CIDR assigned"
}

// RetryableIPv6CIDRError returns true if the error indicates that getting the IPv6 CIDR can be retried.
func IsRetryableIPv6CIDRError(err error) bool {
_, ok := err.(*RetryableIPv6CIDRError)
return ok
}

// CreateVpc creates a VPC resource.
func (c *Client) CreateVpc(ctx context.Context, desired *VPC) (*VPC, error) {
input := &ec2.CreateVpcInput{
Expand All @@ -687,38 +701,49 @@ func (c *Client) CreateVpc(ctx context.Context, desired *VPC) (*VPC, error) {

// WaitForIPv6Cidr waits for the ipv6 cidr block association
func (c *Client) WaitForIPv6Cidr(ctx context.Context, vpcID string) (string, error) {
// Custom waiting loop
waitInput := &ec2.DescribeVpcsInput{
VpcIds: []*string{aws.String(vpcID)},
}
var ipv6CidrBlock string

maxRetries := 30
waitInterval := 10 * time.Second
for i := 0; i < maxRetries; i++ {
select {
case <-ctx.Done():
return "", ctx.Err()
case <-time.After(waitInterval):
resp, err := c.EC2.DescribeVpcs(waitInput)
if err != nil {
return "", fmt.Errorf("error describing VPC: %v", err)
ipv6CidrBlock, err := c.GetIPv6Cidr(ctx, vpcID)
if err == nil {
return ipv6CidrBlock, nil
}
if len(resp.Vpcs) > 0 {
for _, assoc := range resp.Vpcs[0].Ipv6CidrBlockAssociationSet {
if assoc != nil && aws.StringValue(assoc.Ipv6CidrBlockState.State) == "associated" {
ipv6CidrBlock = *assoc.Ipv6CidrBlock
vpc, err := c.GetVpc(ctx, vpcID)
if err != nil {
return "", err
}
vpc.IPv6CidrBlock = ipv6CidrBlock
return ipv6CidrBlock, nil
}
if !IsRetryableIPv6CIDRError(err) {
return "", err
}
}
}
return "", fmt.Errorf("no IPv6 CIDR Block was assigned to VPC")
}

func (c *Client) GetIPv6Cidr(ctx context.Context, vpcID string) (string, error) {
var ipv6CidrBlock string
describeVPCInput := &ec2.DescribeVpcsInput{
VpcIds: []*string{aws.String(vpcID)},
}
resp, err := c.EC2.DescribeVpcs(describeVPCInput)
if err != nil {
return "", fmt.Errorf("error describing VPC: %v", err)
}
if len(resp.Vpcs) > 0 {
for _, assoc := range resp.Vpcs[0].Ipv6CidrBlockAssociationSet {
if assoc != nil && aws.StringValue(assoc.Ipv6CidrBlockState.State) == "associated" {
ipv6CidrBlock = *assoc.Ipv6CidrBlock
vpc, err := c.GetVpc(ctx, vpcID)
if err != nil {
return "", err
}
vpc.IPv6CidrBlock = ipv6CidrBlock
return ipv6CidrBlock, nil
}
}
}
return "", fmt.Errorf("No IPv6 CIDR Block was assigned to VPC")
return "", &RetryableIPv6CIDRError{}
}

// UpdateVpcAttribute sets/updates a VPC attribute if needed.
Expand Down
15 changes: 15 additions & 0 deletions pkg/aws/client/mock/mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/aws/client/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type Interface interface {
FindVpcDhcpOptionsByTags(ctx context.Context, tags Tags) ([]*DhcpOptions, error)
DeleteVpcDhcpOptions(ctx context.Context, id string) error
CreateVpc(ctx context.Context, vpc *VPC) (*VPC, error)
GetIPv6Cidr(ctx context.Context, vpcID string) (string, error)
WaitForIPv6Cidr(ctx context.Context, vpcID string) (string, error)
AddVpcDhcpOptionAssociation(vpcId string, dhcpOptionsId *string) error
UpdateVpcAttribute(ctx context.Context, vpcId, attributeName string, value bool) error
Expand Down
12 changes: 10 additions & 2 deletions pkg/controller/infrastructure/configvalidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (c *configValidator) Validate(ctx context.Context, infra *extensionsv1alpha
// Validate infrastructure config
if config.Networks.VPC.ID != nil {
logger.Info("Validating infrastructure networks.vpc.id")
allErrs = append(allErrs, c.validateVPC(ctx, awsClient, *config.Networks.VPC.ID, infra.Spec.Region, field.NewPath("networks", "vpc", "id"))...)
allErrs = append(allErrs, c.validateVPC(ctx, awsClient, *config.Networks.VPC.ID, infra.Spec.Region, field.NewPath("networks", "vpc", "id"), config.DualStack != nil && config.DualStack.Enabled)...)
}

var (
Expand All @@ -97,7 +97,7 @@ func (c *configValidator) Validate(ctx context.Context, infra *extensionsv1alpha
return allErrs
}

func (c *configValidator) validateVPC(ctx context.Context, awsClient awsclient.Interface, vpcID, region string, fldPath *field.Path) field.ErrorList {
func (c *configValidator) validateVPC(ctx context.Context, awsClient awsclient.Interface, vpcID, region string, fldPath *field.Path, dualStack bool) field.ErrorList {
allErrs := field.ErrorList{}

// Verify that the VPC exists and the enableDnsSupport and enableDnsHostnames VPC attributes are both true
Expand All @@ -116,6 +116,14 @@ func (c *configValidator) validateVPC(ctx context.Context, awsClient awsclient.I
}
}

if dualStack {
_, err := awsClient.GetIPv6Cidr(ctx, vpcID)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, vpcID, fmt.Sprintf("VPC %s has no ipv6 CIDR", vpcID)))
return allErrs
}
}

// Verify that there is an internet gateway attached to the VPC
internetGatewayID, err := awsClient.GetVPCInternetGateway(ctx, vpcID)
if err != nil {
Expand Down

0 comments on commit fde766d

Please sign in to comment.