github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/provider/azure_provider.go (about)

     1  package provider
     2  
     3  import (
     4  	"math/big"
     5  	"math/rand"
     6  	"net/netip"
     7  	"strconv"
     8  
     9  	"github.com/kyma-project/kyma-environment-broker/internal/networking"
    10  
    11  	"github.com/kyma-project/control-plane/components/provisioner/pkg/gqlschema"
    12  	"github.com/kyma-project/kyma-environment-broker/internal"
    13  	"github.com/kyma-project/kyma-environment-broker/internal/broker"
    14  	"github.com/kyma-project/kyma-environment-broker/internal/ptr"
    15  )
    16  
    17  const (
    18  	DefaultAzureRegion         = "eastus"
    19  	DefaultEuAccessAzureRegion = "switzerlandnorth"
    20  	DefaultAzureMultiZoneCount = 3
    21  )
    22  
    23  var europeAzure = "westeurope"
    24  var usAzure = "eastus"
    25  var asiaAzure = "southeastasia"
    26  
    27  var trialPurpose = "evaluation"
    28  
    29  var toAzureSpecific = map[string]*string{
    30  	string(broker.Europe): &europeAzure,
    31  	string(broker.Us):     &usAzure,
    32  	string(broker.Asia):   &asiaAzure,
    33  }
    34  
    35  type (
    36  	AzureInput struct {
    37  		MultiZone                    bool
    38  		ControlPlaneFailureTolerance string
    39  	}
    40  	AzureLiteInput  struct{}
    41  	AzureTrialInput struct {
    42  		PlatformRegionMapping map[string]string
    43  	}
    44  	AzureFreemiumInput struct{}
    45  )
    46  
    47  func (p *AzureInput) Defaults() *gqlschema.ClusterConfigInput {
    48  	zonesCount := 1
    49  	if p.MultiZone {
    50  		zonesCount = DefaultAzureMultiZoneCount
    51  	}
    52  	var controlPlaneFailureTolerance *string = nil
    53  	if p.ControlPlaneFailureTolerance != "" {
    54  		controlPlaneFailureTolerance = &p.ControlPlaneFailureTolerance
    55  	}
    56  	return &gqlschema.ClusterConfigInput{
    57  		GardenerConfig: &gqlschema.GardenerConfigInput{
    58  			DiskType:       ptr.String("Standard_LRS"),
    59  			VolumeSizeGb:   ptr.Integer(50),
    60  			MachineType:    "Standard_D4_v3",
    61  			Region:         DefaultAzureRegion,
    62  			Provider:       "azure",
    63  			WorkerCidr:     networking.DefaultNodesCIDR,
    64  			AutoScalerMin:  3,
    65  			AutoScalerMax:  20,
    66  			MaxSurge:       zonesCount,
    67  			MaxUnavailable: 0,
    68  			ProviderSpecificConfig: &gqlschema.ProviderSpecificInput{
    69  				AzureConfig: &gqlschema.AzureProviderConfigInput{
    70  					VnetCidr:         networking.DefaultNodesCIDR,
    71  					AzureZones:       generateAzureZones(networking.DefaultNodesCIDR, generateRandomAzureZones(zonesCount)),
    72  					EnableNatGateway: ptr.Bool(true),
    73  				},
    74  			},
    75  			ControlPlaneFailureTolerance: controlPlaneFailureTolerance,
    76  		},
    77  	}
    78  }
    79  
    80  func (p *AzureInput) ApplyParameters(input *gqlschema.ClusterConfigInput, pp internal.ProvisioningParameters) {
    81  	if internal.IsEuAccess(pp.PlatformRegion) {
    82  		updateString(&input.GardenerConfig.Region, ptr.String(DefaultEuAccessAzureRegion))
    83  		return
    84  	}
    85  	workerCidr := networking.DefaultNodesCIDR
    86  	if pp.Parameters.Networking != nil {
    87  		workerCidr = pp.Parameters.Networking.NodesCidr
    88  	}
    89  	input.GardenerConfig.WorkerCidr = workerCidr
    90  	input.GardenerConfig.ProviderSpecificConfig.AzureConfig.VnetCidr = workerCidr
    91  	zonesCount := 1
    92  	if p.MultiZone {
    93  		zonesCount = DefaultAzureMultiZoneCount
    94  	}
    95  
    96  	// explicit zones list is provided
    97  	if len(pp.Parameters.Zones) > 0 {
    98  		var zones []int
    99  		for _, inputZone := range pp.Parameters.Zones {
   100  			zone, err := strconv.Atoi(inputZone)
   101  			if err != nil || zone < 1 || zone > 3 {
   102  				continue
   103  			}
   104  			zones = append(zones, zone)
   105  		}
   106  		input.GardenerConfig.ProviderSpecificConfig.AzureConfig.AzureZones = generateAzureZones(workerCidr, zones)
   107  	} else {
   108  		input.GardenerConfig.ProviderSpecificConfig.AzureConfig.AzureZones = generateAzureZones(workerCidr, generateRandomAzureZones(zonesCount))
   109  	}
   110  }
   111  
   112  func (p *AzureInput) Profile() gqlschema.KymaProfile {
   113  	return gqlschema.KymaProfileProduction
   114  }
   115  
   116  func (p *AzureInput) Provider() internal.CloudProvider {
   117  	return internal.Azure
   118  }
   119  
   120  func (p *AzureLiteInput) Defaults() *gqlschema.ClusterConfigInput {
   121  	return &gqlschema.ClusterConfigInput{
   122  		GardenerConfig: &gqlschema.GardenerConfigInput{
   123  			DiskType:       ptr.String("Standard_LRS"),
   124  			VolumeSizeGb:   ptr.Integer(50),
   125  			MachineType:    "Standard_D4_v3",
   126  			Region:         DefaultAzureRegion,
   127  			Provider:       "azure",
   128  			WorkerCidr:     networking.DefaultNodesCIDR,
   129  			AutoScalerMin:  2,
   130  			AutoScalerMax:  10,
   131  			MaxSurge:       1,
   132  			MaxUnavailable: 0,
   133  			ProviderSpecificConfig: &gqlschema.ProviderSpecificInput{
   134  				AzureConfig: &gqlschema.AzureProviderConfigInput{
   135  					VnetCidr: networking.DefaultNodesCIDR,
   136  					AzureZones: []*gqlschema.AzureZoneInput{
   137  						{
   138  							Name: generateRandomAzureZone(),
   139  							Cidr: networking.DefaultNodesCIDR,
   140  						},
   141  					},
   142  					EnableNatGateway: ptr.Bool(true),
   143  				},
   144  			},
   145  		},
   146  	}
   147  }
   148  
   149  func (p *AzureLiteInput) ApplyParameters(input *gqlschema.ClusterConfigInput, pp internal.ProvisioningParameters) {
   150  	if internal.IsEuAccess(pp.PlatformRegion) {
   151  		updateString(&input.GardenerConfig.Region, ptr.String(DefaultEuAccessAzureRegion))
   152  	}
   153  
   154  	updateAzureSingleNodeWorkerCidr(input, pp)
   155  }
   156  
   157  func updateAzureSingleNodeWorkerCidr(input *gqlschema.ClusterConfigInput, pp internal.ProvisioningParameters) {
   158  	workerCIDR := networking.DefaultNodesCIDR
   159  	if pp.Parameters.Networking != nil {
   160  		workerCIDR = pp.Parameters.Networking.NodesCidr
   161  	}
   162  	input.GardenerConfig.WorkerCidr = workerCIDR
   163  	input.GardenerConfig.ProviderSpecificConfig.AzureConfig.VnetCidr = workerCIDR
   164  	input.GardenerConfig.ProviderSpecificConfig.AzureConfig.AzureZones[0].Cidr = workerCIDR
   165  }
   166  
   167  func (p *AzureLiteInput) Profile() gqlschema.KymaProfile {
   168  	return gqlschema.KymaProfileEvaluation
   169  }
   170  
   171  func (p *AzureLiteInput) Provider() internal.CloudProvider {
   172  	return internal.Azure
   173  }
   174  
   175  func (p *AzureTrialInput) Defaults() *gqlschema.ClusterConfigInput {
   176  	return azureTrialDefaults()
   177  }
   178  
   179  func azureTrialDefaults() *gqlschema.ClusterConfigInput {
   180  	return &gqlschema.ClusterConfigInput{
   181  		GardenerConfig: &gqlschema.GardenerConfigInput{
   182  			DiskType:       ptr.String("Standard_LRS"),
   183  			VolumeSizeGb:   ptr.Integer(50),
   184  			MachineType:    "Standard_D4_v3",
   185  			Region:         DefaultAzureRegion,
   186  			Provider:       "azure",
   187  			WorkerCidr:     networking.DefaultNodesCIDR,
   188  			AutoScalerMin:  1,
   189  			AutoScalerMax:  1,
   190  			MaxSurge:       1,
   191  			MaxUnavailable: 0,
   192  			Purpose:        &trialPurpose,
   193  			ProviderSpecificConfig: &gqlschema.ProviderSpecificInput{
   194  				AzureConfig: &gqlschema.AzureProviderConfigInput{
   195  					VnetCidr: networking.DefaultNodesCIDR,
   196  					AzureZones: []*gqlschema.AzureZoneInput{
   197  						{
   198  							Name: generateRandomAzureZone(),
   199  							Cidr: networking.DefaultNodesCIDR,
   200  						},
   201  					},
   202  					EnableNatGateway: ptr.Bool(false),
   203  				},
   204  			},
   205  		},
   206  	}
   207  }
   208  
   209  func (p *AzureTrialInput) ApplyParameters(input *gqlschema.ClusterConfigInput, pp internal.ProvisioningParameters) {
   210  	params := pp.Parameters
   211  
   212  	if internal.IsEuAccess(pp.PlatformRegion) {
   213  		updateString(&input.GardenerConfig.Region, ptr.String(DefaultEuAccessAzureRegion))
   214  		return
   215  	}
   216  
   217  	// read platform region if exists
   218  	if pp.PlatformRegion != "" {
   219  		abstractRegion, found := p.PlatformRegionMapping[pp.PlatformRegion]
   220  		if found {
   221  			r := toAzureSpecific[abstractRegion]
   222  			updateString(&input.GardenerConfig.Region, r)
   223  		}
   224  	}
   225  
   226  	if params.Region != nil && *params.Region != "" {
   227  		updateString(&input.GardenerConfig.Region, toAzureSpecific[*params.Region])
   228  	}
   229  
   230  	updateAzureSingleNodeWorkerCidr(input, pp)
   231  }
   232  
   233  func (p *AzureTrialInput) Provider() internal.CloudProvider {
   234  	return internal.Azure
   235  }
   236  
   237  func (p *AzureTrialInput) Profile() gqlschema.KymaProfile {
   238  	return gqlschema.KymaProfileEvaluation
   239  }
   240  
   241  func (p *AzureFreemiumInput) Defaults() *gqlschema.ClusterConfigInput {
   242  	return azureTrialDefaults()
   243  }
   244  
   245  func (p *AzureFreemiumInput) ApplyParameters(input *gqlschema.ClusterConfigInput, params internal.ProvisioningParameters) {
   246  	updateSlice(&input.GardenerConfig.ProviderSpecificConfig.AzureConfig.Zones, params.Parameters.Zones)
   247  }
   248  
   249  func (p *AzureFreemiumInput) Profile() gqlschema.KymaProfile {
   250  	return gqlschema.KymaProfileEvaluation
   251  }
   252  
   253  func (p *AzureFreemiumInput) Provider() internal.CloudProvider {
   254  	return internal.Azure
   255  }
   256  
   257  func generateRandomAzureZone() int {
   258  	const (
   259  		min = 1
   260  		max = 3
   261  	)
   262  
   263  	// generates random number from 1-3 range
   264  	getRandomNumber := func() int {
   265  		return rand.Intn(max-min+1) + min
   266  	}
   267  
   268  	return getRandomNumber()
   269  }
   270  
   271  func generateRandomAzureZones(zonesCount int) []int {
   272  	zones := []int{1, 2, 3}
   273  	if zonesCount > 3 {
   274  		zonesCount = 3
   275  	}
   276  
   277  	rand.Shuffle(len(zones), func(i, j int) { zones[i], zones[j] = zones[j], zones[i] })
   278  	return zones[:zonesCount]
   279  }
   280  
   281  func generateAzureZones(workerCidr string, zoneNames []int) []*gqlschema.AzureZoneInput {
   282  	var zones []*gqlschema.AzureZoneInput
   283  
   284  	cidr, _ := netip.ParsePrefix(workerCidr)
   285  	workerPrefixLength := cidr.Bits() + 3
   286  	workerPrefix, _ := cidr.Addr().Prefix(workerPrefixLength)
   287  	// delta - it is the difference between CIDRs of two zones:
   288  	//    zone1:   "10.250.0.0/19",
   289  	//    zone2:   "10.250.32.0/19",
   290  	delta := big.NewInt(1)
   291  	delta.Lsh(delta, uint(32-workerPrefixLength))
   292  
   293  	// zoneIPValue - it is an integer, which is based on IP bytes
   294  	zoneIPValue := new(big.Int).SetBytes(workerPrefix.Addr().AsSlice())
   295  
   296  	for _, name := range zoneNames {
   297  		zoneWorkerIP, _ := netip.AddrFromSlice(zoneIPValue.Bytes())
   298  		zoneWorkerCidr := netip.PrefixFrom(zoneWorkerIP, workerPrefixLength)
   299  		zoneIPValue.Add(zoneIPValue, delta)
   300  		zones = append(zones, &gqlschema.AzureZoneInput{
   301  			Name: name,
   302  			Cidr: zoneWorkerCidr.String(),
   303  		})
   304  	}
   305  	return zones
   306  }