github.com/openshift/installer@v1.4.17/pkg/asset/quota/gcp/gcp.go (about)

     1  package gcp
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strconv"
     7  	"strings"
     8  
     9  	machineapi "github.com/openshift/api/machine/v1beta1"
    10  	"github.com/openshift/installer/pkg/quota"
    11  	"github.com/openshift/installer/pkg/types"
    12  )
    13  
    14  // Constraints returns a list of quota constraints based on the InstallConfig.
    15  // These constraints can be used to check if there is enough quota for creating a cluster
    16  // for the isntall config.
    17  func Constraints(client *Client, config *types.InstallConfig, controlPlanes []machineapi.Machine, computes []machineapi.MachineSet) []quota.Constraint {
    18  	ctrplConfigs := make([]*machineapi.GCPMachineProviderSpec, len(controlPlanes))
    19  	for i, m := range controlPlanes {
    20  		ctrplConfigs[i] = m.Spec.ProviderSpec.Value.Object.(*machineapi.GCPMachineProviderSpec)
    21  	}
    22  	computeReplicas := make([]int64, len(computes))
    23  	computeConfigs := make([]*machineapi.GCPMachineProviderSpec, len(computes))
    24  	for i, w := range computes {
    25  		computeReplicas[i] = int64(*w.Spec.Replicas)
    26  		computeConfigs[i] = w.Spec.Template.Spec.ProviderSpec.Value.Object.(*machineapi.GCPMachineProviderSpec)
    27  	}
    28  
    29  	var ret []quota.Constraint
    30  	for _, gen := range []constraintGenerator{
    31  		network(config),
    32  		apiExternal(config),
    33  		apiInternal(config),
    34  		controlPlane(client, config, ctrplConfigs),
    35  		compute(client, config, computeReplicas, computeConfigs),
    36  		others,
    37  	} {
    38  		ret = append(ret, gen()...)
    39  	}
    40  	return aggregate(ret)
    41  }
    42  
    43  func aggregate(quotas []quota.Constraint) []quota.Constraint {
    44  	sort.SliceStable(quotas, func(i, j int) bool {
    45  		return quotas[i].Name < quotas[j].Name
    46  	})
    47  
    48  	i := 0
    49  	for j := 1; j < len(quotas); j++ {
    50  		if quotas[i].Name == quotas[j].Name && quotas[i].Region == quotas[j].Region {
    51  			quotas[i].Count += quotas[j].Count
    52  		} else {
    53  			i++
    54  			if i != j {
    55  				quotas[i] = quotas[j]
    56  			}
    57  		}
    58  	}
    59  	return quotas[:i+1]
    60  }
    61  
    62  // constraintGenerator generates a list of constraints.
    63  type constraintGenerator func() []quota.Constraint
    64  
    65  func network(config *types.InstallConfig) func() []quota.Constraint {
    66  	return func() []quota.Constraint {
    67  		net := []quota.Constraint{{
    68  			Name:   "compute.googleapis.com/networks",
    69  			Region: "global",
    70  			Count:  1,
    71  		}, {
    72  			Name:   "compute.googleapis.com/subnetworks",
    73  			Region: "global",
    74  			Count:  2,
    75  		}, {
    76  			Name:   "compute.googleapis.com/routers",
    77  			Region: "global",
    78  			Count:  1,
    79  		}}
    80  
    81  		firewalls := []quota.Constraint{{
    82  			Name:   "compute.googleapis.com/firewalls",
    83  			Region: "global",
    84  			Count:  6,
    85  		}}
    86  
    87  		if len(config.Platform.GCP.Network) > 0 {
    88  			return firewalls
    89  		}
    90  		return append(net, firewalls...)
    91  	}
    92  }
    93  
    94  func apiExternal(config *types.InstallConfig) func() []quota.Constraint {
    95  	return func() []quota.Constraint {
    96  		if config.Publish == types.InternalPublishingStrategy {
    97  			return nil
    98  		}
    99  		return []quota.Constraint{{
   100  			Name:   "compute.googleapis.com/health_checks",
   101  			Region: "global",
   102  			Count:  1,
   103  		}, {
   104  			Name:   "compute.googleapis.com/forwarding_rules",
   105  			Region: "global",
   106  			Count:  1,
   107  		}, {
   108  			Name:   "compute.googleapis.com/target_pools",
   109  			Region: "global",
   110  			Count:  1,
   111  		}, {
   112  			Name:   "compute.googleapis.com/regional_static_addresses",
   113  			Region: config.Platform.GCP.Region,
   114  			Count:  1,
   115  		}}
   116  	}
   117  }
   118  
   119  func apiInternal(config *types.InstallConfig) func() []quota.Constraint {
   120  	return func() []quota.Constraint {
   121  		return []quota.Constraint{{
   122  			Name:   "compute.googleapis.com/health_checks",
   123  			Region: "global",
   124  			Count:  1,
   125  		}, {
   126  			Name:   "compute.googleapis.com/forwarding_rules",
   127  			Region: "global",
   128  			Count:  1,
   129  		}, {
   130  			Name:   "compute.googleapis.com/backend_services",
   131  			Region: "global",
   132  			Count:  1,
   133  		}, {
   134  			Name:   "compute.googleapis.com/regional_static_addresses",
   135  			Region: config.Platform.GCP.Region,
   136  			Count:  1,
   137  		}}
   138  	}
   139  }
   140  
   141  func controlPlane(client MachineTypeGetter, config *types.InstallConfig, machines []*machineapi.GCPMachineProviderSpec) func() []quota.Constraint {
   142  	return func() []quota.Constraint {
   143  		var ret []quota.Constraint
   144  		for _, m := range machines {
   145  			q := machineTypeToQuota(client, m.Zone, m.MachineType)
   146  			q.Region = config.Platform.GCP.Region
   147  			ret = append(ret, q)
   148  		}
   149  
   150  		ret = append(ret, quota.Constraint{
   151  			Name:   "iam.googleapis.com/quota/service-account-count",
   152  			Region: "global",
   153  			Count:  1,
   154  		})
   155  		return ret
   156  	}
   157  }
   158  
   159  func compute(client MachineTypeGetter, config *types.InstallConfig, replicas []int64, machines []*machineapi.GCPMachineProviderSpec) func() []quota.Constraint {
   160  	return func() []quota.Constraint {
   161  		var ret []quota.Constraint
   162  		for idx, m := range machines {
   163  			q := machineTypeToQuota(client, m.Zone, m.MachineType)
   164  			q.Count = q.Count * replicas[idx]
   165  			q.Region = config.Platform.GCP.Region
   166  			ret = append(ret, q)
   167  		}
   168  
   169  		ret = append(ret, quota.Constraint{
   170  			Name:   "iam.googleapis.com/quota/service-account-count",
   171  			Region: "global",
   172  			Count:  1,
   173  		})
   174  		return ret
   175  	}
   176  }
   177  
   178  func others() []quota.Constraint {
   179  	return []quota.Constraint{{
   180  		Name:   "compute.googleapis.com/images",
   181  		Region: "global",
   182  		Count:  1,
   183  	}, {
   184  		Name:   "iam.googleapis.com/quota/service-account-count",
   185  		Region: "global",
   186  		Count:  3,
   187  	}}
   188  }
   189  
   190  func machineTypeToQuota(client MachineTypeGetter, zone string, machineType string) quota.Constraint {
   191  	var name string
   192  	class := strings.SplitN(machineType, "-", 2)[0]
   193  	switch class {
   194  	case "c2", "m1", "m2", "n2", "n2d":
   195  		name = fmt.Sprintf("compute.googleapis.com/%s_cpus", class)
   196  	default:
   197  		name = "compute.googleapis.com/cpus"
   198  	}
   199  
   200  	info, err := client.GetMachineType(zone, machineType)
   201  	if err != nil {
   202  		return quota.Constraint{Name: name, Count: guessMachineCPUCount(machineType)}
   203  	}
   204  	return quota.Constraint{Name: name, Count: info.GuestCpus}
   205  }
   206  
   207  // the guess is based on https://cloud.google.com/compute/docs/machine-types
   208  func guessMachineCPUCount(machineType string) int64 {
   209  	split := strings.Split(machineType, "-")
   210  	switch len(split) {
   211  	case 4:
   212  		if c, err := strconv.ParseInt(split[2], 10, 0); err == nil {
   213  			return c
   214  		}
   215  	case 3:
   216  		switch split[0] {
   217  		case "c2", "m1", "m2", "n1", "n2", "n2d", "e2":
   218  			if c, err := strconv.ParseInt(split[2], 10, 0); err == nil {
   219  				return c
   220  			}
   221  		}
   222  	case 2:
   223  		switch split[0] {
   224  		case "e2":
   225  			return 2
   226  		case "f1", "g1":
   227  			return 1
   228  		}
   229  	}
   230  	return 0
   231  }