Skip to content

Commit

Permalink
Feature kubernetes-sigs#4784: SubnetSchema support
Browse files Browse the repository at this point in the history
  • Loading branch information
krasoffski authored and fad3t committed Jul 25, 2024
1 parent cc2643d commit ee7f89d
Show file tree
Hide file tree
Showing 12 changed files with 876 additions and 12 deletions.
1 change: 1 addition & 0 deletions api/v1beta1/awscluster_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func (src *AWSCluster) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.NetworkSpec.VPC.EmptyRoutesDefaultVPCSecurityGroup = restored.Spec.NetworkSpec.VPC.EmptyRoutesDefaultVPCSecurityGroup
dst.Spec.NetworkSpec.VPC.PrivateDNSHostnameTypeOnLaunch = restored.Spec.NetworkSpec.VPC.PrivateDNSHostnameTypeOnLaunch
dst.Spec.NetworkSpec.VPC.CarrierGatewayID = restored.Spec.NetworkSpec.VPC.CarrierGatewayID
dst.Spec.NetworkSpec.VPC.SubnetSchema = restored.Spec.NetworkSpec.VPC.SubnetSchema

if restored.Spec.NetworkSpec.VPC.ElasticIPPool != nil {
if dst.Spec.NetworkSpec.VPC.ElasticIPPool == nil {
Expand Down
1 change: 1 addition & 0 deletions api/v1beta1/zz_generated.conversion.go

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

1 change: 1 addition & 0 deletions api/v1beta2/awscluster_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,7 @@ func TestAWSClusterDefaultCNIIngressRules(t *testing.T) {
defaultVPCSpec := VPCSpec{
AvailabilityZoneUsageLimit: &AZUsageLimit,
AvailabilityZoneSelection: &AZSelectionSchemeOrdered,
SubnetSchema: &SubnetSchemaPreferPrivate,
}
g := NewWithT(t)
tests := []struct {
Expand Down
10 changes: 10 additions & 0 deletions api/v1beta2/network_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,16 @@ type VPCSpec struct {
// the API Server.
// +optional
ElasticIPPool *ElasticIPPool `json:"elasticIpPool,omitempty"`

// SubnetSchema specifies how CidrBlock should be divided on subnets in the VPC depending on the number of AZs.
// PreferPrivate - one private subnet for each AZ plus one other subnet that will be further sub-divided for the public subnets.
// PreferPublic - have the reverse logic of PreferPrivate, one public subnet for each AZ plus one other subnet
// that will be further sub-divided for the private subnets.
// Defaults to PreferPrivate
// +optional
// +kubebuilder:default=PreferPrivate
// +kubebuilder:validation:Enum=PreferPrivate;PreferPublic
SubnetSchema *SubnetSchemaType `json:"subnetSchema,omitempty"`
}

// String returns a string representation of the VPC.
Expand Down
18 changes: 18 additions & 0 deletions api/v1beta2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package v1beta2

import (
"strings"

"k8s.io/apimachinery/pkg/util/sets"

clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
Expand Down Expand Up @@ -439,3 +441,19 @@ type PrivateDNSName struct {
// +kubebuilder:validation:Enum:=ip-name;resource-name
HostnameType *string `json:"hostnameType,omitempty"`
}

// SubnetSchemaType specifies how given network should be divided on subnets
// in the VPC depending on the number of AZs.
type SubnetSchemaType string

// Name returns subnet schema type name without prefix.
func (s *SubnetSchemaType) Name() string {
return strings.ToLower(strings.TrimPrefix(string(*s), "Prefer"))
}

var (
// SubnetSchemaPreferPrivate allocates more subnets in the VPC to private subnets.
SubnetSchemaPreferPrivate = SubnetSchemaType("PreferPrivate")
// SubnetSchemaPreferPublic allocates more subnets in the VPC to public subnets.
SubnetSchemaPreferPublic = SubnetSchemaType("PreferPublic")
)
5 changes: 5 additions & 0 deletions api/v1beta2/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,18 @@ spec:
- ip-name
- resource-name
type: string
subnetSchema:
default: PreferPrivate
description: |-
SubnetSchema specifies how CidrBlock should be divided on subnets in the VPC depending on the number of AZs.
PreferPrivate - one private subnet for each AZ plus one other subnet that will be further sub-divided for the public subnets.
PreferPublic - have the reverse logic of PreferPrivate, one public subnet for each AZ plus one other subnet
that will be further sub-divided for the private subnets.
Defaults to PreferPrivate
enum:
- PreferPrivate
- PreferPublic
type: string
tags:
additionalProperties:
type: string
Expand Down Expand Up @@ -2750,6 +2762,18 @@ spec:
- ip-name
- resource-name
type: string
subnetSchema:
default: PreferPrivate
description: |-
SubnetSchema specifies how CidrBlock should be divided on subnets in the VPC depending on the number of AZs.
PreferPrivate - one private subnet for each AZ plus one other subnet that will be further sub-divided for the public subnets.
PreferPublic - have the reverse logic of PreferPrivate, one public subnet for each AZ plus one other subnet
that will be further sub-divided for the private subnets.
Defaults to PreferPrivate
enum:
- PreferPrivate
- PreferPublic
type: string
tags:
additionalProperties:
type: string
Expand Down
12 changes: 12 additions & 0 deletions config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,18 @@ spec:
- ip-name
- resource-name
type: string
subnetSchema:
default: PreferPrivate
description: |-
SubnetSchema specifies how CidrBlock should be divided on subnets in the VPC depending on the number of AZs.
PreferPrivate - one private subnet for each AZ plus one other subnet that will be further sub-divided for the public subnets.
PreferPublic - have the reverse logic of PreferPrivate, one public subnet for each AZ plus one other subnet
that will be further sub-divided for the private subnets.
Defaults to PreferPrivate
enum:
- PreferPrivate
- PreferPublic
type: string
tags:
additionalProperties:
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,18 @@ spec:
- ip-name
- resource-name
type: string
subnetSchema:
default: PreferPrivate
description: |-
SubnetSchema specifies how CidrBlock should be divided on subnets in the VPC depending on the number of AZs.
PreferPrivate - one private subnet for each AZ plus one other subnet that will be further sub-divided for the public subnets.
PreferPublic - have the reverse logic of PreferPrivate, one public subnet for each AZ plus one other subnet
that will be further sub-divided for the private subnets.
Defaults to PreferPrivate
enum:
- PreferPrivate
- PreferPublic
type: string
tags:
additionalProperties:
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func TestDefaultingWebhook(t *testing.T) {
defaultVPCSpec := infrav1.VPCSpec{
AvailabilityZoneUsageLimit: &AZUsageLimit,
AvailabilityZoneSelection: &infrav1.AZSelectionSchemeOrdered,
SubnetSchema: &infrav1.SubnetSchemaPreferPrivate,
}
defaultIdentityRef := &infrav1.AWSIdentityReference{
Kind: infrav1.ControllerIdentityKind,
Expand Down
47 changes: 35 additions & 12 deletions pkg/cloud/services/network/subnets.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,26 +275,38 @@ func (s *Service) getDefaultSubnets() (infrav1.Subnets, error) {
s.scope.Debug("zones selected", "region", s.scope.Region(), "zones", zones)
}

// 1 private subnet for each AZ plus 1 other subnet that will be further sub-divided for the public subnets
// 1 private subnet for each AZ plus 1 other subnet that will be further sub-divided for the public subnets or vice versa if
// the subnet schema is set to prefer public subnets.
// All subnets will have an ipv4 address for now as well. We aren't supporting ipv6-only yet.
numSubnets := len(zones) + 1
var (
subnetCIDRs []*net.IPNet
publicSubnetCIDRs []*net.IPNet
ipv6SubnetCIDRs []*net.IPNet
publicIPv6SubnetCIDRs []*net.IPNet
privateIPv6SubnetCIDRs []*net.IPNet
subnetCIDRs []*net.IPNet
preferredSubnetCIDRs []*net.IPNet
residualSubnetCIDRs []*net.IPNet
ipv6SubnetCIDRs []*net.IPNet
preferredIPv6SubnetCIDRs []*net.IPNet
residualIPv6SubnetCIDRs []*net.IPNet
)
subnetScheme := infrav1.SubnetSchemaPreferPrivate
if s.scope.VPC().SubnetSchema != nil {
subnetScheme = *s.scope.VPC().SubnetSchema
}

residualSubnetsName := infrav1.SubnetSchemaPreferPublic.Name()
if subnetScheme == infrav1.SubnetSchemaPreferPublic {
residualSubnetsName = infrav1.SubnetSchemaPreferPrivate.Name()
}

subnetCIDRs, err = cidr.SplitIntoSubnetsIPv4(s.scope.VPC().CidrBlock, numSubnets)
if err != nil {
return nil, errors.Wrapf(err, "failed splitting VPC CIDR %q into subnets", s.scope.VPC().CidrBlock)
}

publicSubnetCIDRs, err = cidr.SplitIntoSubnetsIPv4(subnetCIDRs[0].String(), len(zones))
residualSubnetCIDRs, err = cidr.SplitIntoSubnetsIPv4(subnetCIDRs[0].String(), len(zones))
if err != nil {
return nil, errors.Wrapf(err, "failed splitting CIDR %q into public subnets", subnetCIDRs[0].String())
return nil, errors.Wrapf(err, "failed splitting CIDR %q into %s subnets", subnetCIDRs[0].String(), residualSubnetsName)
}
privateSubnetCIDRs := append(subnetCIDRs[:0], subnetCIDRs[1:]...)
preferredSubnetCIDRs = append(subnetCIDRs[:0], subnetCIDRs[1:]...)

if s.scope.VPC().IsIPv6Enabled() {
ipv6SubnetCIDRs, err = cidr.SplitIntoSubnetsIPv6(s.scope.VPC().IPv6.CidrBlock, numSubnets)
Expand All @@ -303,12 +315,23 @@ func (s *Service) getDefaultSubnets() (infrav1.Subnets, error) {
}

// We need to take the last, so it doesn't conflict with the rest. The subnetID is increment each time by 1.
publicIPv6SubnetCIDRs, err = cidr.SplitIntoSubnetsIPv6(ipv6SubnetCIDRs[len(ipv6SubnetCIDRs)-1].String(), len(zones))
ipv6SubnetCIDRsStr := ipv6SubnetCIDRs[len(ipv6SubnetCIDRs)-1].String()
residualIPv6SubnetCIDRs, err = cidr.SplitIntoSubnetsIPv6(ipv6SubnetCIDRsStr, len(zones))
if err != nil {
return nil, errors.Wrapf(err, "failed splitting IPv6 CIDR %q into public subnets", ipv6SubnetCIDRs[len(ipv6SubnetCIDRs)-1].String())
return nil, errors.Wrapf(err, "failed splitting IPv6 CIDR %q into %s subnets", ipv6SubnetCIDRsStr, residualSubnetsName)
}
// TODO: this might need to be the last instead of the first..
privateIPv6SubnetCIDRs = append(ipv6SubnetCIDRs[:0], ipv6SubnetCIDRs[1:]...)
preferredIPv6SubnetCIDRs = append(ipv6SubnetCIDRs[:0], ipv6SubnetCIDRs[1:]...)
}

// By default, the preferred subnets are the private subnets and the residual subnets are the public subnets.
privateSubnetCIDRs, publicSubnetCIDRs := preferredSubnetCIDRs, residualSubnetCIDRs
privateIPv6SubnetCIDRs, publicIPv6SubnetCIDRs := preferredIPv6SubnetCIDRs, residualIPv6SubnetCIDRs

// If the subnet schema is set to prefer public, we need to swap the private and public subnets.
if subnetScheme == infrav1.SubnetSchemaPreferPublic {
privateSubnetCIDRs, publicSubnetCIDRs = residualSubnetCIDRs, preferredSubnetCIDRs
privateIPv6SubnetCIDRs, publicIPv6SubnetCIDRs = residualIPv6SubnetCIDRs, preferredIPv6SubnetCIDRs
}

subnets := infrav1.Subnets{}
Expand Down
Loading

0 comments on commit ee7f89d

Please sign in to comment.