Skip to content

Commit

Permalink
Add ipv6 eks support
Browse files Browse the repository at this point in the history
Signed-off-by: Mikkel Oscar Lyderik Larsen <[email protected]>
  • Loading branch information
mikkeloscar committed Sep 30, 2024
1 parent da14f45 commit a3b2a31
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 70 deletions.
2 changes: 2 additions & 0 deletions provisioner/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,7 @@ type EKSClusterDetails struct {
Endpoint string
CertificateAuthority string
OIDCIssuerURL string
ServiceIPv6CIDR string
}

func (a *awsAdapter) GetEKSClusterDetails(cluster *api.Cluster) (*EKSClusterDetails, error) {
Expand All @@ -787,5 +788,6 @@ func (a *awsAdapter) GetEKSClusterDetails(cluster *api.Cluster) (*EKSClusterDeta
Endpoint: aws.StringValue(resp.Cluster.Endpoint),
CertificateAuthority: aws.StringValue(resp.Cluster.CertificateAuthority.Data),
OIDCIssuerURL: aws.StringValue(resp.Cluster.Identity.Oidc.Issuer),
ServiceIPv6CIDR: aws.StringValue(resp.Cluster.KubernetesNetworkConfig.ServiceIpv6Cidr),
}, nil
}
25 changes: 20 additions & 5 deletions provisioner/aws_az.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ import (
"sort"
)

// SubnetInfo has information about a subnet.
type SubnetInfo struct {
SubnetID string
SubnetIPV6CIDRs []string
}

// AZInfo tracks information about available AZs based on explicit restrictions or available subnets
type AZInfo struct {
subnets map[string]string
subnets map[string]SubnetInfo
}

// RestrictAZs returns a new AZInfo that is restricted to provided AZs
func (info *AZInfo) RestrictAZs(availableAZs []string) *AZInfo {
result := make(map[string]string)
result := make(map[string]SubnetInfo)

for _, az := range availableAZs {
if subnet, ok := info.subnets[az]; ok {
Expand All @@ -28,17 +34,26 @@ func (info *AZInfo) SubnetsByAZ() map[string]string {
result := make(map[string]string)
for _, az := range info.AvailabilityZones() {
subnet := info.subnets[az]
result[az] = subnet
result[az] = subnet.SubnetID

if existing, ok := result[subnetAllAZName]; ok {
result[subnetAllAZName] = existing + "," + subnet
result[subnetAllAZName] = existing + "," + subnet.SubnetID
} else {
result[subnetAllAZName] = subnet
result[subnetAllAZName] = subnet.SubnetID
}
}
return result
}

// SubnetIPV6CIDRs returns a list of available subnet IPV6 CIDRs.
func (info *AZInfo) SubnetIPV6CIDRs() []string {
var result []string
for _, subnetInfo := range info.subnets {
result = append(result, subnetInfo.SubnetIPV6CIDRs...)
}
return result
}

// AvailabilityZones returns a list of available AZs
func (info *AZInfo) AvailabilityZones() []string {
var result []string
Expand Down
14 changes: 10 additions & 4 deletions provisioner/aws_az_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ import (

var (
info = &AZInfo{
subnets: map[string]string{
"eu-central-1a": "subnet-1a",
"eu-central-1b": "subnet-1b",
"eu-central-1c": "subnet-1c",
subnets: map[string]SubnetInfo{
"eu-central-1a": SubnetInfo{
SubnetID: "subnet-1a",
},
"eu-central-1b": SubnetInfo{
SubnetID: "subnet-1b",
},
"eu-central-1c": SubnetInfo{
SubnetID: "subnet-1c",
},
},
}
)
Expand Down
20 changes: 12 additions & 8 deletions provisioner/clusterpy.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const (
stackTagValueTrue = "true"
subnetsConfigItemKey = "subnets"
subnetsValueKey = "subnets"
subnetIPV6CIDRsKey = "subnet_ipv6_cidrs"
availabilityZonesConfigItemKey = "availability_zones"
availabilityZonesValueKey = "availability_zones"
vpcIDConfigItemKey = "vpc_id"
Expand Down Expand Up @@ -251,6 +252,7 @@ func (p *clusterpyProvisioner) provision(
values := map[string]interface{}{
subnetsValueKey: azInfo.SubnetsByAZ(),
availabilityZonesValueKey: azInfo.AvailabilityZones(),
subnetIPV6CIDRsKey: strings.Join(azInfo.SubnetIPV6CIDRs(), ","),
"hosted_zone": hostedZone,
"load_balancer_certificate": loadBalancerCert.ID(),
"vpc_ipv4_cidr": aws.StringValue(vpc.CidrBlock),
Expand Down Expand Up @@ -303,7 +305,6 @@ func (p *clusterpyProvisioner) provision(
postOptions, err = p.hook.Execute(
awsAdapter,
cluster,
outputs,
)
if err != nil {
return err
Expand All @@ -312,11 +313,8 @@ func (p *clusterpyProvisioner) provision(
if postOptions.APIServerURL != "" {
cluster.APIServerURL = postOptions.APIServerURL
}
if postOptions.AZInfo != nil {
azInfo = postOptions.AZInfo
}
for k, v := range postOptions.TemplateValues {
values[k] = v
if postOptions.ServiceIPv6CIDR != "" {
cluster.ConfigItems["service_ipv6_cidr"] = postOptions.ServiceIPv6CIDR
}
}

Expand Down Expand Up @@ -648,9 +646,15 @@ func selectSubnetIDs(subnets []*ec2.Subnet) *AZInfo {
}
}

result := make(map[string]string, len(subnetsByAZ))
result := make(map[string]SubnetInfo, len(subnetsByAZ))
for az, subnet := range subnetsByAZ {
result[az] = aws.StringValue(subnet.SubnetId)
subnetInfo := SubnetInfo{
SubnetID: aws.StringValue(subnet.SubnetId),
}
for _, ipv6Cidr := range subnet.Ipv6CidrBlockAssociationSet {
subnetInfo.SubnetIPV6CIDRs = append(subnetInfo.SubnetIPV6CIDRs, aws.StringValue(ipv6Cidr.Ipv6CidrBlock))
}
result[az] = subnetInfo
}

return &AZInfo{subnets: result}
Expand Down
8 changes: 3 additions & 5 deletions provisioner/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ type (
Execute(
adapter awsInterface,
cluster *api.Cluster,
cloudFormationOutput map[string]string,
) (
*HookResponse,
error,
Expand All @@ -56,10 +55,9 @@ type (
// HookResponse contain configuration parameters that a provisioner can use
// at a later stage.
HookResponse struct {
APIServerURL string
AZInfo *AZInfo
CAData []byte
TemplateValues map[string]interface{}
APIServerURL string
CAData []byte
ServiceIPv6CIDR string
}

// Options is the options that can be passed to a provisioner when initialized.
Expand Down
21 changes: 21 additions & 0 deletions provisioner/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func renderTemplate(context *templateContext, file string) (string, error) {
},
"nodeCIDRMaxNodesPodCIDR": nodeCIDRMaxNodes,
"nodeCIDRMaxPods": nodeCIDRMaxPods,
"addressNFromIPV6CIDR": addressNFromIPV6CIDR,
"parseInt64": parseInt64,
"generateJWKSDocument": generateJWKSDocument,
"generateOIDCDiscoveryDocument": generateOIDCDiscoveryDocument,
Expand Down Expand Up @@ -581,6 +582,26 @@ func nodeCIDRMaxPods(maskSize int64, extraCapacity int64) (int64, error) {
return maxPods, nil
}

// addressNFromIPV6CIDR takes an IPv6 CIDR and returns the Nth address in the
// subnet.
func addressNFromIPV6CIDR(cidr string, n int) (string, error) {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return "", err
}

ip := ipNet.IP
ip = ip.To16()
if ip == nil {
return "", fmt.Errorf("invalid IP address: %s", ipNet.IP)
}
ip = ip.Mask(ipNet.Mask)
for i := 0; i < n; i++ {
ip[15]++
}
return ip.String(), nil
}

func kubernetesSizeToKiloBytes(quantity string, scale float64) (string, error) {
resource, err := k8sresource.ParseQuantity(quantity)
if err != nil {
Expand Down
21 changes: 1 addition & 20 deletions provisioner/zalando_eks.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ func NewZalandoEKSCreationHook(
func (z *ZalandoEKSCreationHook) Execute(
adapter awsInterface,
cluster *api.Cluster,
cloudFormationOutput map[string]string,
) (*HookResponse, error) {
res := &HookResponse{}

Expand Down Expand Up @@ -201,25 +200,7 @@ func (z *ZalandoEKSCreationHook) Execute(

res.APIServerURL = clusterDetails.Endpoint
res.CAData = decodedCA

subnets := map[string]string{}
for key, az := range map[string]string{
"EKSSubneta": "eu-central-1a",
"EKSSubnetb": "eu-central-1b",
"EKSSubnetc": "eu-central-1c",
} {
if v, ok := cloudFormationOutput[key]; ok {
subnets[az] = v
}
}
if len(subnets) > 0 {
res.AZInfo = &AZInfo{
subnets: subnets,
}
res.TemplateValues = map[string]interface{}{
subnetsValueKey: subnets,
}
}
res.ServiceIPv6CIDR = clusterDetails.ServiceIPv6CIDR

return res, nil
}
28 changes: 0 additions & 28 deletions provisioner/zalando_eks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,36 +41,9 @@ func (r *mockRegistry) UpdateConfigItems(_ *api.Cluster, _ map[string]string) er

func TestCreationHookExecute(t *testing.T) {
for _, tc := range []struct {
cfOutput map[string]string
expected *HookResponse
}{
{
cfOutput: map[string]string{
"EKSSubneta": "subnet-123",
"EKSSubnetb": "subnet-456",
"EKSSubnetc": "subnet-789",
},
expected: &HookResponse{
APIServerURL: "https://api.cluster.local",
CAData: []byte("blah"),
AZInfo: &AZInfo{
subnets: map[string]string{
"eu-central-1a": "subnet-123",
"eu-central-1b": "subnet-456",
"eu-central-1c": "subnet-789",
},
},
TemplateValues: map[string]interface{}{
subnetsValueKey: map[string]string{
"eu-central-1a": "subnet-123",
"eu-central-1b": "subnet-456",
"eu-central-1c": "subnet-789",
},
},
},
},
{
cfOutput: map[string]string{},
expected: &HookResponse{
APIServerURL: "https://api.cluster.local",
CAData: []byte("blah"),
Expand All @@ -81,7 +54,6 @@ func TestCreationHookExecute(t *testing.T) {
res, err := z.Execute(
&mockAWSAdapter{},
&api.Cluster{},
tc.cfOutput,
)

require.NoError(t, err)
Expand Down

0 comments on commit a3b2a31

Please sign in to comment.