in eksconfig/add-on-managed-node-groups.go [221:481]
func (cfg *Config) validateAddOnManagedNodeGroups() error {
if !cfg.IsEnabledAddOnManagedNodeGroups() {
return nil
}
n := len(cfg.AddOnManagedNodeGroups.MNGs)
if n == 0 {
return errors.New("empty MNGs")
}
if n > MNGsMaxLimit {
return fmt.Errorf("MNGs %d exceeds maximum number of MNGs which is %d", n, MNGsMaxLimit)
}
if cfg.VersionValue < 1.14 {
return fmt.Errorf("Version %q not supported for AddOnManagedNodeGroups", cfg.Version)
}
if cfg.AddOnManagedNodeGroups.LogsDir == "" {
cfg.AddOnManagedNodeGroups.LogsDir = filepath.Join(filepath.Dir(cfg.ConfigPath), cfg.Name+"-logs-mngs")
}
if cfg.AddOnManagedNodeGroups.LogsTarGzPath == "" {
cfg.AddOnManagedNodeGroups.LogsTarGzPath = filepath.Join(filepath.Dir(cfg.ConfigPath), cfg.Name+"-logs-mngs.tar.gz")
}
if !strings.HasSuffix(cfg.AddOnManagedNodeGroups.LogsTarGzPath, ".tar.gz") {
return fmt.Errorf("AddOnManagedNodeGroups.LogsTarGzPath %q must end with .tar.gz", cfg.AddOnManagedNodeGroups.LogsTarGzPath)
}
switch cfg.AddOnManagedNodeGroups.Role.Create {
case true: // need create one, or already created
if cfg.AddOnManagedNodeGroups.Role.Name == "" {
cfg.AddOnManagedNodeGroups.Role.Name = cfg.Name + "-mng-role"
}
if len(cfg.AddOnManagedNodeGroups.Role.ServicePrincipals) > 0 {
/*
(InvalidParameterException: Following required service principals [ec2.amazonaws.com] were not found in the trust relationships of nodeRole arn:aws:iam::...:role/test-mng-role
{
ClusterName: "test",
Message_: "Following required service principals [ec2.amazonaws.com] were not found in the trust relationships of nodeRole arn:aws:iam::...:role/test-mng-role",
NodegroupName: "test-mng-cpu"
})
*/
found := false
for _, pv := range cfg.AddOnManagedNodeGroups.Role.ServicePrincipals {
if pv == "ec2.amazonaws.com" || pv == "ec2.amazonaws.com.cn" {
found = true
break
}
}
if !found {
return fmt.Errorf("AddOnManagedNodeGroups.Role.ServicePrincipals %q must include 'ec2.amazonaws.com' or 'ec2.amazonaws.com.cn'", cfg.AddOnManagedNodeGroups.Role.ServicePrincipals)
}
}
case false: // use existing one
if cfg.AddOnManagedNodeGroups.Role.ARN == "" {
return fmt.Errorf("AddOnManagedNodeGroups.Role.Create false; expect non-empty RoleARN but got %q", cfg.AddOnManagedNodeGroups.Role.ARN)
}
if cfg.AddOnManagedNodeGroups.Role.Name == "" {
cfg.AddOnManagedNodeGroups.Role.Name = getNameFromARN(cfg.AddOnManagedNodeGroups.Role.ARN)
}
if cfg.IsEnabledAddOnStresserRemote() {
return errors.New("'AddOnStresserRemote.Enable == true' requires 'AddOnManagedNodeGroups.Role.Create == true' but got 'false'")
}
}
if cfg.AddOnManagedNodeGroups.Role.PolicyName == "" {
cfg.AddOnManagedNodeGroups.Role.PolicyName = cfg.Name + "-managed-node-group-policy"
}
names, processed := make(map[string]struct{}), make(map[string]MNG)
for k, cur := range cfg.AddOnManagedNodeGroups.MNGs {
k = strings.ReplaceAll(k, "GetRef.Name", cfg.Name)
cur.Name = strings.ReplaceAll(cur.Name, "GetRef.Name", cfg.Name)
if cur.Name == "" {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].Name is empty", k)
}
if k != cur.Name {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].Name has different Name field %q", k, cur.Name)
}
_, ok := names[cur.Name]
if !ok {
names[cur.Name] = struct{}{}
} else {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].Name %q is redundant", k, cur.Name)
}
if cfg.IsEnabledAddOnNodeGroups() {
_, ok = cfg.AddOnNodeGroups.ASGs[cur.Name]
if ok {
return fmt.Errorf("MNGs[%q] name already exists (conflict) in AddOnNodeGroups.ASGs", cur.Name)
}
}
if cur.ReleaseVersion != "" {
// e.g. "1.16.8-20200609"
ss := strings.Split(cur.ReleaseVersion, ".")
if len(ss) > 2 {
sv := strings.Join(ss[:2], ".")
var err error
cur.ReleaseVersionValue, err = strconv.ParseFloat(sv, 64)
if err != nil {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q] invalid ReleaseVersion %q (%q, %v)", cur.Name, cur.ReleaseVersion, sv, err)
}
}
}
if len(cur.ScaleUpdates) > 0 {
for idx := range cur.ScaleUpdates {
if !cur.ScaleUpdates[idx].Enable {
continue
}
var err error
if cur.ScaleUpdates[idx].InitialWaitString != "" {
cur.ScaleUpdates[idx].InitialWait, err = time.ParseDuration(cur.ScaleUpdates[idx].InitialWaitString)
if err != nil {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q] invalid cur.ScaleUpdates[%d].InitialWaitString %q (%v)", cur.Name, idx, cur.VersionUpgrade.InitialWaitString, err)
}
}
if cur.ScaleUpdates[idx].ASGDesiredCapacity == 0 {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q] invalid cur.ScaleUpdates[%d].ASGDesiredCapacity == 0", cur.Name, idx)
}
if cur.ScaleUpdates[idx].ASGDesiredCapacity < cur.ScaleUpdates[idx].ASGMinSize {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q] invalid cur.ScaleUpdates[%d].ASGDesiredCapacity %d < ASGMinSize %d", cur.Name, idx, cur.ScaleUpdates[idx].ASGDesiredCapacity, cur.ScaleUpdates[idx].ASGMinSize)
}
}
}
// check optional mng version upgrade add-on
if cur.VersionUpgrade != nil && cur.VersionUpgrade.Enable {
var err error
if cur.VersionUpgrade.InitialWaitString != "" {
cur.VersionUpgrade.InitialWait, err = time.ParseDuration(cur.VersionUpgrade.InitialWaitString)
if err != nil {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q] invalid cur.VersionUpgrade.InitialWaitString %q (%v)", cur.Name, cur.VersionUpgrade.InitialWaitString, err)
}
}
// do not set any defaults
// hard to keep everything in sync and find right values:
// - original cluster version
// - cluster upgrade version
// - default mng version
// - custom mng version
if cur.VersionUpgrade.Version == "" {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q] VersionUpgrade.Enable but empty VersionUpgrade.Version", cur.Name)
}
cur.VersionUpgrade.VersionValue, err = strconv.ParseFloat(cur.VersionUpgrade.Version, 64)
if err != nil {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q] invalid VersionUpgrade.Version %q (%v)", cur.Name, cur.VersionUpgrade.Version, err)
}
origVer := cfg.VersionValue
if cur.ReleaseVersionValue > 0.0 {
// e.g. "1.16" in "1.16.8-20200609"
origVer = cur.ReleaseVersionValue
}
delta := cur.VersionUpgrade.VersionValue - origVer
if fmt.Sprintf("%.2f", delta) != "0.01" {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q] VersionUpgrade only supports one minor version upgrade but got %.2f [cluster version %q, mng release version %q, mng upgrade version %q]", cur.Name, delta, cfg.Version, cur.ReleaseVersion, cur.VersionUpgrade.Version)
}
// target version must match with the Kubernetes control plane version
// can't upgrade to 1.17 MNG when EKS is 1.16
// e.g. "Nodegroup Kubernetes version should be equal to Cluster kubernetes version 1.16 or NodeGroup kubernetes version 1.16"
if cur.ReleaseVersionValue == 0.0 && !cfg.IsEnabledAddOnClusterVersionUpgrade() {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q] VersionUpgrade %q would diverge from Parameters.Version %q (IsEnabledAddOnClusterVersionUpgrade %v)", cur.Name, cur.VersionUpgrade.Version, cfg.Version, cfg.IsEnabledAddOnClusterVersionUpgrade())
}
}
if len(cur.InstanceTypes) > 4 {
return fmt.Errorf("too many InstaceTypes[%q]", cur.InstanceTypes)
}
if cur.VolumeSize == 0 {
cur.VolumeSize = DefaultNodeVolumeSize
}
if cur.RemoteAccessUserName == "" {
cur.RemoteAccessUserName = "ec2-user"
}
switch cur.AMIType {
case eks.AMITypesAl2X8664:
if cur.RemoteAccessUserName != "ec2-user" {
return fmt.Errorf("AMIType %q but unexpected RemoteAccessUserName %q", cur.AMIType, cur.RemoteAccessUserName)
}
case eks.AMITypesAl2X8664Gpu:
if cur.RemoteAccessUserName != "ec2-user" {
return fmt.Errorf("AMIType %q but unexpected RemoteAccessUserName %q", cur.AMIType, cur.RemoteAccessUserName)
}
default:
return fmt.Errorf("unknown ASGs[%q].AMIType %q", k, cur.AMIType)
}
switch cur.AMIType {
case eks.AMITypesAl2X8664:
if len(cur.InstanceTypes) == 0 {
cur.InstanceTypes = []string{DefaultNodeInstanceTypeCPU}
}
case eks.AMITypesAl2X8664Gpu:
if len(cur.InstanceTypes) == 0 {
cur.InstanceTypes = []string{DefaultNodeInstanceTypeGPU}
}
default:
return fmt.Errorf("unknown AddOnManagedNodeGroups.MNGs[%q].AMIType %q", k, cur.AMIType)
}
if cfg.IsEnabledAddOnNLBHelloWorld() || cfg.IsEnabledAddOnALB2048() {
for _, itp := range cur.InstanceTypes {
// "m3.xlarge" or "c4.xlarge" will fail with "InvalidTarget: Targets {...} are not supported"
// ref. https://github.com/aws/amazon-vpc-cni-k8s/pull/821
// ref. https://github.com/kubernetes/kubernetes/issues/66044#issuecomment-408188524
switch {
case strings.HasPrefix(itp, "m3."),
strings.HasPrefix(itp, "c4."):
return fmt.Errorf("AddOnNLBHelloWorld.Enable[%v] || AddOnALB2048.Enable[%v], but older instance type InstanceTypes %q for %q",
cfg.IsEnabledAddOnNLBHelloWorld(),
cfg.IsEnabledAddOnALB2048(),
itp, k)
default:
}
}
}
if cur.ASGMinSize == 0 {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].ASGMinSize must be >0", k)
}
if cur.ASGDesiredCapacity == 0 {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].ASGDesiredCapacity must be >0", k)
}
if cur.ASGMaxSize == 0 {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].ASGMaxSize must be >0", k)
}
if cur.ASGMinSize > cur.ASGMaxSize {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].ASGMinSize %d > ASGMaxSize %d", k, cur.ASGMinSize, cur.ASGMaxSize)
}
if cur.ASGDesiredCapacity > cur.ASGMaxSize {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].ASGDesiredCapacity %d > ASGMaxSize %d", k, cur.ASGDesiredCapacity, cur.ASGMaxSize)
}
if cur.ASGMaxSize > MNGMaxLimit {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].ASGMaxSize %d > MNGMaxLimit %d", k, cur.ASGMaxSize, MNGMaxLimit)
}
if cur.ASGDesiredCapacity > MNGMaxLimit {
return fmt.Errorf("AddOnManagedNodeGroups.MNGs[%q].ASGDesiredCapacity %d > MNGMaxLimit %d", k, cur.ASGDesiredCapacity, MNGMaxLimit)
}
if cfg.IsEnabledAddOnNLBHelloWorld() && cfg.AddOnNLBHelloWorld.DeploymentReplicas < int32(cur.ASGDesiredCapacity) {
cfg.AddOnNLBHelloWorld.DeploymentReplicas = int32(cur.ASGDesiredCapacity)
}
if cfg.IsEnabledAddOnNLBGuestbook() && cfg.AddOnNLBGuestbook.DeploymentReplicas < int32(cur.ASGDesiredCapacity) {
cfg.AddOnNLBGuestbook.DeploymentReplicas = int32(cur.ASGDesiredCapacity)
}
if cfg.IsEnabledAddOnALB2048() && cfg.AddOnALB2048.DeploymentReplicasALB < int32(cur.ASGDesiredCapacity) {
cfg.AddOnALB2048.DeploymentReplicasALB = int32(cur.ASGDesiredCapacity)
}
if cfg.IsEnabledAddOnALB2048() && cfg.AddOnALB2048.DeploymentReplicas2048 < int32(cur.ASGDesiredCapacity) {
cfg.AddOnALB2048.DeploymentReplicas2048 = int32(cur.ASGDesiredCapacity)
}
processed[k] = cur
}
cfg.AddOnManagedNodeGroups.MNGs = processed
return nil
}