Skip to content

Commit

Permalink
Merge pull request #3706 from Azure/mrwinston/hotfix-dev-cluster-crea…
Browse files Browse the repository at this point in the history
…te-better-subnet-creation

[Dev Tooling] generateSubnets should check if subnets are already taken
  • Loading branch information
mociarain committed Aug 1, 2024
2 parents 1cee7a1 + c157292 commit 0250e2f
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 13 deletions.
1 change: 1 addition & 0 deletions pkg/util/azureclient/mgmt/network/subnets.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
// SubnetsClient is a minimal interface for azure SubnetsClient
type SubnetsClient interface {
Get(ctx context.Context, resourceGroupName string, virtualNetworkName string, subnetName string, expand string) (result mgmtnetwork.Subnet, err error)
List(ctx context.Context, resourceGroupName string, virtualNetworkName string) (result mgmtnetwork.SubnetListResultPage, err error)
SubnetsClientAddons
}

Expand Down
122 changes: 109 additions & 13 deletions pkg/util/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"math/rand"
"net"
"net/http"
"os"
"strings"
Expand Down Expand Up @@ -66,6 +67,8 @@ type Cluster struct {
vaultsClient armkeyvault.VaultsClient
}

const GenerateSubnetMaxTries = 100

func New(log *logrus.Entry, environment env.Core, ci bool) (*Cluster, error) {
if env.IsLocalDevelopmentMode() {
if err := env.ValidateVars("AZURE_FP_CLIENT_ID"); err != nil {
Expand Down Expand Up @@ -200,7 +203,11 @@ func (c *Cluster) Create(ctx context.Context, vnetResourceGroup, clusterName str
return err
}

addressPrefix, masterSubnet, workerSubnet := c.generateSubnets()
addressPrefix, masterSubnet, workerSubnet, err := c.generateSubnets()

if err != nil {
return err
}

var kvName string
if len(vnetResourceGroup) > 10 {
Expand Down Expand Up @@ -345,22 +352,111 @@ func (c *Cluster) Create(ctx context.Context, vnetResourceGroup, clusterName str
return nil
}

func (c *Cluster) generateSubnets() (vnetPrefix string, masterSubnet string, workerSubnet string) {
// pick a random 23 in range [10.3.0.0, 10.127.255.0]
// ipRangesContainCIDR checks, weather any of the ipRanges overlap with the cidr string. In case cidr isn't valid, false is returned.
func ipRangesContainCIDR(ipRanges []*net.IPNet, cidr string) (bool, error) {
_, cidrNet, err := net.ParseCIDR(cidr)
if err != nil {
return false, err
}

for _, snet := range ipRanges {
if snet.Contains(cidrNet.IP) || cidrNet.Contains(snet.IP) {
return true, nil
}
}
return false, nil
}

// GetIPRangesFromSubnet converts a given azure subnet to a list if IPNets.
// Because an az subnet can cover multiple ipranges, we need to return a slice
// instead of just a single ip range. This function never errors. If something
// goes wrong, it instead returns an empty list.
func GetIPRangesFromSubnet(subnet mgmtnetwork.Subnet) []*net.IPNet {
ipRanges := []*net.IPNet{}
if subnet.AddressPrefix != nil {
_, ipRange, err := net.ParseCIDR(*subnet.AddressPrefix)
if err == nil {
ipRanges = append(ipRanges, ipRange)
}
}

if subnet.AddressPrefixes == nil {
return ipRanges
}

for _, snetPrefix := range *subnet.AddressPrefixes {
_, ipRange, err := net.ParseCIDR(snetPrefix)
if err == nil {
ipRanges = append(ipRanges, ipRange)
}
}
return ipRanges
}

// getAllDevSubnets queries azure to retrieve all subnets assigned the vnet
// `dev-vnet` in the current resource group
func (c *Cluster) getAllDevSubnets() ([]mgmtnetwork.Subnet, error) {
allSubnets := []mgmtnetwork.Subnet{}
availSnetResults, err := c.subnets.List(context.Background(), c.env.ResourceGroup(), "dev-vnet")
if err != nil {
return allSubnets, err
}
allSubnets = append(allSubnets, availSnetResults.Values()...)
for availSnetResults.NotDone() {
err = availSnetResults.NextWithContext(context.Background())
if err != nil {
break
}
allSubnets = append(allSubnets, availSnetResults.Values()...)
}

return allSubnets, nil
}

func (c *Cluster) generateSubnets() (vnetPrefix string, masterSubnet string, workerSubnet string, err error) {
// pick a random 23 in range [10.3.0.0, 10.127.255.0], making sure it doesn't
// conflict with other subnets present in out dev-vnet
// 10.0.0.0/16 is used by dev-vnet to host CI
// 10.1.0.0/24 is used by rp-vnet to host Proxy VM
// 10.2.0.0/24 is used by dev-vpn-vnet to host VirtualNetworkGateway
var x, y int
// Local Dev clusters are limited to /16 dev-vnet
if !c.ci {
x, y = 0, 2*rand.Intn(128)
} else {
x, y = rand.Intn((124))+3, 2*rand.Intn(128)

allSubnets, err := c.getAllDevSubnets()
if err != nil {
c.log.Warnf("Error getting existing subnets. Continuing regardless: %v", err)
}
vnetPrefix = fmt.Sprintf("10.%d.%d.0/23", x, y)
masterSubnet = fmt.Sprintf("10.%d.%d.0/24", x, y)
workerSubnet = fmt.Sprintf("10.%d.%d.0/24", x, y+1)
return

ipRanges := []*net.IPNet{}
for _, snet := range allSubnets {
ipRanges = append(ipRanges, GetIPRangesFromSubnet(snet)...)
}

for i := 1; i < GenerateSubnetMaxTries; i++ {
var x, y int
// Local Dev clusters are limited to /16 dev-vnet
if !c.ci {
x, y = 0, 2*rand.Intn(128)
} else {
x, y = rand.Intn((124))+3, 2*rand.Intn(128)
}
c.log.Infof("Generate Subnet try: %d\n", i)
vnetPrefix = fmt.Sprintf("10.%d.%d.0/23", x, y)
masterSubnet = fmt.Sprintf("10.%d.%d.0/24", x, y)
workerSubnet = fmt.Sprintf("10.%d.%d.0/24", x, y+1)

masterSubnetOverlaps, err := ipRangesContainCIDR(ipRanges, workerSubnet)
if err != nil || masterSubnetOverlaps {
continue
}

workerSubnetOverlaps, err := ipRangesContainCIDR(ipRanges, workerSubnet)
if err != nil || workerSubnetOverlaps {
continue
}

return vnetPrefix, masterSubnet, workerSubnet, nil
}

return vnetPrefix, masterSubnet, workerSubnet, fmt.Errorf("was not able to generate master and worker subnets after %v tries", GenerateSubnetMaxTries)
}

func (c *Cluster) Delete(ctx context.Context, vnetResourceGroup, clusterName string) error {
Expand Down
15 changes: 15 additions & 0 deletions pkg/util/mocks/azureclient/mgmt/network/network.go

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

0 comments on commit 0250e2f

Please sign in to comment.