Allow IPv6-only subnets

This commit is contained in:
John Gardiner Myers 2021-12-23 17:24:43 -08:00
parent 6b2cd12d6e
commit f9071dd0d5
7 changed files with 47 additions and 18 deletions

View File

@ -299,7 +299,7 @@ func TestMinimalIPv6(t *testing.T) {
// TestMinimalIPv6 runs the test on a minimum IPv6 configuration
func TestMinimalIPv6Private(t *testing.T) {
newIntegrationTest("minimal-ipv6.example.com", "minimal-ipv6-private").
withAddons(awsCCMAddon, awsEBSCSIAddon, dnsControllerAddon).
withAddons(awsCCMAddon, awsEBSCSIAddon, dnsControllerAddon, leaderElectionAddon).
runTestTerraformAWS(t)
}

View File

@ -338,7 +338,11 @@ func ValidateCluster(c *kops.Cluster, strict bool) field.ErrorList {
fieldSubnet := fieldSpec.Child("subnets").Index(i)
if s.CIDR == "" {
if requiresSubnetCIDR && strict {
allErrs = append(allErrs, field.Required(fieldSubnet.Child("cidr"), "subnet did not have a cidr set"))
if !strings.Contains(c.Spec.NonMasqueradeCIDR, ":") || s.IPv6CIDR == "" {
allErrs = append(allErrs, field.Required(fieldSubnet.Child("cidr"), "subnet did not have a cidr set"))
} else if c.IsKubernetesLT("1.23") {
allErrs = append(allErrs, field.Required(fieldSubnet.Child("cidr"), "IPv6-only subnets require Kubernetes 1.23+"))
}
}
} else {
_, subnetCIDR, err := net.ParseCIDR(s.CIDR)

View File

@ -104,7 +104,7 @@ func validateClusterSpec(spec *kops.ClusterSpec, c *kops.Cluster, fieldPath *fie
}
if spec.Topology != nil {
allErrs = append(allErrs, validateTopology(spec.Topology, fieldPath.Child("topology"))...)
allErrs = append(allErrs, validateTopology(c, spec.Topology, fieldPath.Child("topology"))...)
}
// UpdatePolicy
@ -360,7 +360,7 @@ func validateIPv6CIDR(cidr string, fieldPath *field.Path) field.ErrorList {
return allErrs
}
func validateTopology(topology *kops.TopologySpec, fieldPath *field.Path) field.ErrorList {
func validateTopology(c *kops.Cluster, topology *kops.TopologySpec, fieldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if topology.Masters == "" {
@ -373,6 +373,10 @@ func validateTopology(topology *kops.TopologySpec, fieldPath *field.Path) field.
allErrs = append(allErrs, field.Required(fieldPath.Child("nodes"), ""))
} else {
allErrs = append(allErrs, IsValidValue(fieldPath.Child("nodes"), &topology.Nodes, kops.SupportedTopologies)...)
if topology.Nodes == "private" && c.Spec.IsIPv6Only() && c.IsKubernetesLT("1.23") {
allErrs = append(allErrs, field.Forbidden(fieldPath.Child("nodes"), "private topology in IPv6 clusters requires Kubernetes 1.23+"))
}
}
if topology.Bastion != nil {

View File

@ -254,7 +254,6 @@ func (b *NetworkModelBuilder) Build(c *fi.ModelBuilderContext) error {
Lifecycle: b.Lifecycle,
VPC: b.LinkToVPC(),
AvailabilityZone: fi.String(subnetSpec.Zone),
CIDR: fi.String(subnetSpec.CIDR),
Shared: fi.Bool(sharedSubnet),
Tags: tags,
}
@ -263,6 +262,10 @@ func (b *NetworkModelBuilder) Build(c *fi.ModelBuilderContext) error {
subnet.ResourceBasedNaming = fi.Bool(true)
}
if subnetSpec.CIDR != "" {
subnet.CIDR = fi.String(subnetSpec.CIDR)
}
if subnetSpec.IPv6CIDR != "" {
if !sharedVPC {
subnet.AmazonIPv6CIDR = b.LinkToAmazonVPCIPv6CIDR()

View File

@ -33,7 +33,7 @@ spec:
iam: {}
kubelet:
anonymousAuth: false
kubernetesVersion: v1.21.0
kubernetesVersion: v1.23.0
masterInternalName: api.internal.minimal-ipv6.example.com
masterPublicName: api.minimal-ipv6.example.com
networkCIDR: 172.20.0.0/16

View File

@ -106,7 +106,7 @@ func (e *Subnet) Find(c *fi.Context) (*Subnet, error) {
actual.ResourceBasedNaming = fi.Bool(aws.StringValue(subnet.PrivateDnsNameOptionsOnLaunch.HostnameType) == ec2.HostnameTypeResourceName)
if *actual.ResourceBasedNaming {
if !aws.BoolValue(subnet.PrivateDnsNameOptionsOnLaunch.EnableResourceNameDnsARecord) {
if fi.StringValue(actual.CIDR) != "" && !aws.BoolValue(subnet.PrivateDnsNameOptionsOnLaunch.EnableResourceNameDnsARecord) {
actual.ResourceBasedNaming = nil
}
if fi.StringValue(actual.IPv6CIDR) != "" && !aws.BoolValue(subnet.PrivateDnsNameOptionsOnLaunch.EnableResourceNameDnsAAAARecord) {
@ -175,9 +175,8 @@ func (s *Subnet) CheckChanges(a, e, changes *Subnet) error {
errors = append(errors, field.Required(fieldPath.Child("VPC"), "must specify a VPC"))
}
if e.CIDR == nil {
// TODO: Auto-assign CIDR?
errors = append(errors, field.Required(fieldPath.Child("CIDR"), "must specify a CIDR"))
if e.CIDR == nil && e.IPv6CIDR == nil {
errors = append(errors, field.Required(fieldPath.Child("CIDR"), "must specify a CIDR or IPv6CIDR"))
}
}
@ -256,7 +255,7 @@ func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error {
}
if a == nil {
klog.V(2).Infof("Creating Subnet with CIDR: %q", *e.CIDR)
klog.V(2).Infof("Creating Subnet with CIDR: %q IPv6CIDR: %q", fi.StringValue(e.CIDR), fi.StringValue(e.IPv6CIDR))
request := &ec2.CreateSubnetInput{
CidrBlock: e.CIDR,
@ -266,6 +265,10 @@ func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error {
TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeSubnet, e.Tags),
}
if e.CIDR == nil {
request.Ipv6Native = aws.Bool(true)
}
response, err := t.Cloud.EC2().CreateSubnet(request)
if err != nil {
return fmt.Errorf("error creating subnet: %v", err)
@ -300,13 +303,24 @@ func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error {
return fmt.Errorf("error modifying hostname type: %w", err)
}
request = &ec2.ModifySubnetAttributeInput{
SubnetId: e.ID,
EnableResourceNameDnsARecordOnLaunch: &ec2.AttributeBooleanValue{Value: changes.ResourceBasedNaming},
}
_, err = t.Cloud.EC2().ModifySubnetAttribute(request)
if err != nil {
return fmt.Errorf("error modifying A records: %w", err)
if fi.StringValue(e.CIDR) == "" {
request = &ec2.ModifySubnetAttributeInput{
SubnetId: e.ID,
EnableDns64: &ec2.AttributeBooleanValue{Value: aws.Bool(true)},
}
_, err = t.Cloud.EC2().ModifySubnetAttribute(request)
if err != nil {
return fmt.Errorf("error enabling DNS64: %w", err)
}
} else {
request = &ec2.ModifySubnetAttributeInput{
SubnetId: e.ID,
EnableResourceNameDnsARecordOnLaunch: &ec2.AttributeBooleanValue{Value: changes.ResourceBasedNaming},
}
_, err = t.Cloud.EC2().ModifySubnetAttribute(request)
if err != nil {
return fmt.Errorf("error modifying A records: %w", err)
}
}
if fi.StringValue(e.IPv6CIDR) != "" {

View File

@ -174,6 +174,10 @@ func assignCIDRsToSubnets(c *kops.Cluster, cloud fi.Cloud) error {
if subnet.CIDR != "" {
continue
}
// TODO: Replace this with a check against type "dualstack" if has IPv6CIDR
if subnet.IPv6CIDR != "" && subnet.Name != subnet.Zone {
continue
}
if len(bigCIDRs) == 0 {
return fmt.Errorf("insufficient (big) CIDRs remaining for automatic CIDR allocation to subnet %q", subnet.Name)