Skip to content

Commit 0afbb24

Browse files
committed
subnets: wait till IPv6 CIDR is associated with subnets
There is a brief period where the IPv6 CIDR is not yet associated with the subnets. Thus, when CAPA creates the default dualstack subnets, it should wait until the IPv6 CIDR is associated before proceeding. If not, CAPA will misinterprete the subnet as non-IPv6 and proceed its reconcilation. The consequence is that CAPA will skip creating a route to eigw. Route to eigw for destination "::/0" to eigw is required for EC2 instance time sync on start-up.
1 parent 9e72818 commit 0afbb24

File tree

1 file changed

+37
-13
lines changed

1 file changed

+37
-13
lines changed

pkg/cloud/services/network/subnets.go

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -507,9 +507,8 @@ func (s *Service) createSubnet(sn *infrav1.SubnetSpec) (*infrav1.SubnetSpec, err
507507
),
508508
},
509509
}
510-
if s.scope.VPC().IsIPv6Enabled() {
510+
if sn.IsIPv6 {
511511
input.Ipv6CidrBlock = aws.String(sn.IPv6CidrBlock)
512-
sn.IsIPv6 = true
513512
}
514513
out, err := s.EC2Client.CreateSubnet(context.TODO(), input)
515514
if err != nil {
@@ -521,7 +520,32 @@ func (s *Service) createSubnet(sn *infrav1.SubnetSpec) (*infrav1.SubnetSpec, err
521520
s.scope.Info("Created subnet", "id", *out.Subnet.SubnetId, "public", sn.IsPublic, "az", sn.AvailabilityZone, "cidr", sn.CidrBlock, "ipv6", sn.IsIPv6, "ipv6-cidr", sn.IPv6CidrBlock)
522521

523522
wReq := &ec2.DescribeSubnetsInput{SubnetIds: []string{aws.ToString(out.Subnet.SubnetId)}}
524-
if err := ec2.NewSubnetAvailableWaiter(s.EC2Client).Wait(context.TODO(), wReq, time.Minute*5); err != nil {
523+
if err := ec2.NewSubnetAvailableWaiter(s.EC2Client).Wait(context.TODO(), wReq, time.Minute*5, func(sawo *ec2.SubnetAvailableWaiterOptions) {
524+
// There is a brief period where the IPv6 CIDR is not yet associated with the subnets.
525+
// We need to additionally wait till the CIDR is associated.
526+
if sn.IsIPv6 {
527+
// Default handler will check for subnet state "available".
528+
subnetStateCheck := sawo.Retryable
529+
sawo.Retryable = func(ctx context.Context, dsi *ec2.DescribeSubnetsInput, dso *ec2.DescribeSubnetsOutput, err error) (bool, error) {
530+
available, err := subnetStateCheck(ctx, dsi, dso, err)
531+
if err != nil {
532+
return false, err
533+
}
534+
535+
cidrAssociated := true
536+
for _, subnet := range dso.Subnets {
537+
for _, set := range subnet.Ipv6CidrBlockAssociationSet {
538+
if set.Ipv6CidrBlockState.State != types.SubnetCidrBlockStateCodeAssociated {
539+
cidrAssociated = false
540+
break
541+
}
542+
}
543+
}
544+
545+
return available && cidrAssociated, nil
546+
}
547+
}
548+
}); err != nil {
525549
return nil, errors.Wrapf(err, "failed to wait for subnet %q", *out.Subnet.SubnetId)
526550
}
527551

@@ -613,23 +637,23 @@ func (s *Service) createSubnet(sn *infrav1.SubnetSpec) (*infrav1.SubnetSpec, err
613637
ID: sn.ID,
614638
ResourceID: *out.Subnet.SubnetId,
615639
AvailabilityZone: *out.Subnet.AvailabilityZone,
616-
CidrBlock: *out.Subnet.CidrBlock, // TODO: this will panic in case of IPv6 only subnets...
617-
IsPublic: sn.IsPublic,
618-
Tags: sn.Tags,
640+
// In case of IPv6-only subnets, cidrBlock (IPv4) is empty.
641+
CidrBlock: aws.ToString(out.Subnet.CidrBlock),
642+
IsPublic: sn.IsPublic,
643+
Tags: sn.Tags,
619644
}
620645
for _, set := range out.Subnet.Ipv6CidrBlockAssociationSet {
621-
if set.Ipv6CidrBlockState.State == types.SubnetCidrBlockStateCodeAssociated {
622-
subnet.IPv6CidrBlock = aws.ToString(set.Ipv6CidrBlock)
623-
subnet.IsIPv6 = true
624-
}
646+
// The IPv6 CIDR is already ensured to be associated so we don't need to check for its association state.
647+
subnet.IPv6CidrBlock = aws.ToString(set.Ipv6CidrBlock)
648+
subnet.IsIPv6 = true
625649
}
626650

627651
s.scope.Debug("Created new subnet in VPC with cidr and availability zone ",
628-
"subnet-id", *out.Subnet.SubnetId,
652+
"subnet-id", subnet.ResourceID,
629653
"vpc-id", *out.Subnet.VpcId,
630-
"cidr-block", *out.Subnet.CidrBlock,
654+
"cidr-block", subnet.CidrBlock,
631655
"ipv6-cidr-block", subnet.IPv6CidrBlock,
632-
"availability-zone", *out.Subnet.AvailabilityZone)
656+
"availability-zone", subnet.AvailabilityZone)
633657

634658
return subnet, nil
635659
}

0 commit comments

Comments
 (0)