func()

in eksconfig/add-on-node-groups.go [129:341]


func (cfg *Config) validateAddOnNodeGroups() error {
	if !cfg.IsEnabledAddOnNodeGroups() {
		return nil
	}

	switch cfg.AddOnNodeGroups.Role.Create {
	case true: // need create one, or already created
		if cfg.AddOnNodeGroups.Role.Name == "" {
			cfg.AddOnNodeGroups.Role.Name = cfg.Name + "-node-group-role"
		}
		// just ignore...
		// could be populated from previous run
		// do not error, so long as RoleCreate false, role won't be deleted

	case false: // use existing one
		if cfg.AddOnNodeGroups.Role.ARN == "" {
			return fmt.Errorf("Role.Create false; expect non-empty RoleARN but got %q", cfg.AddOnNodeGroups.Role.ARN)
		}
		if cfg.AddOnNodeGroups.Role.Name == "" {
			cfg.AddOnNodeGroups.Role.Name = getNameFromARN(cfg.AddOnNodeGroups.Role.ARN)
		}
	}
	if cfg.AddOnNodeGroups.Role.PolicyName == "" {
		cfg.AddOnNodeGroups.Role.PolicyName = cfg.Name + "-node-group-policy"
	}
	if cfg.AddOnNodeGroups.Role.InstanceProfileName == "" {
		cfg.AddOnNodeGroups.Role.InstanceProfileName = cfg.Name + "-node-group-instance-profile"
	}

	n := len(cfg.AddOnNodeGroups.ASGs)
	if n == 0 {
		return errors.New("empty ASGs")
	}
	if n > NGsMaxLimit {
		return fmt.Errorf("NGs %d exceeds maximum number of NGs which is %d", n, NGsMaxLimit)
	}

	if cfg.VersionValue < 1.14 {
		return fmt.Errorf("version %q not supported for AddOnNodeGroups", cfg.Version)
	}

	if cfg.AddOnNodeGroups.LogsDir == "" {
		cfg.AddOnNodeGroups.LogsDir = filepath.Join(filepath.Dir(cfg.ConfigPath), cfg.Name+"-logs-ngs")
	}
	if cfg.AddOnNodeGroups.LogsTarGzPath == "" {
		cfg.AddOnNodeGroups.LogsTarGzPath = filepath.Join(filepath.Dir(cfg.ConfigPath), cfg.Name+"-logs-ngs.tar.gz")
	}
	if !strings.HasSuffix(cfg.AddOnNodeGroups.LogsTarGzPath, ".tar.gz") {
		return fmt.Errorf("AddOnNodeGroups.LogsTarGzPath %q must end with .tar.gz", cfg.AddOnNodeGroups.LogsTarGzPath)
	}

	names, processed := make(map[string]struct{}), make(map[string]ASG)
	for k, cur := range cfg.AddOnNodeGroups.ASGs {
		k = strings.ReplaceAll(k, "GetRef.Name", cfg.Name)
		cur.Name = strings.ReplaceAll(cur.Name, "GetRef.Name", cfg.Name)
		if cur.Name == "" {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].Name is empty", k)
		}
		if k != cur.Name {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].Name has different Name field %q", k, cur.Name)
		}
		_, ok := names[cur.Name]
		if !ok {
			names[cur.Name] = struct{}{}
		} else {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].Name %q is redundant", k, cur.Name)
		}

		if cur.VolumeSize == 0 {
			cur.VolumeSize = DefaultNodeVolumeSize
		}
		if cur.VolumeType == "" {
			cur.VolumeType = DefaultNodeVolumeType
		}
		if cur.RemoteAccessUserName == "" {
			cur.RemoteAccessUserName = "ec2-user"
		}

		if cur.ImageID == "" && cur.ImageIDSSMParameter == "" {
			return fmt.Errorf("%q both ImageID and ImageIDSSMParameter are empty", cur.Name)
		}
		// prefer "ImageIDSSMParameter"
		if cur.ImageID != "" && cur.ImageIDSSMParameter != "" {
			cur.ImageID = ""
		}
		if cur.LaunchTemplateName == "" {
			cur.LaunchTemplateName = cur.Name + "-launch-template"
		}

		switch cur.AMIType {
		case ec2config.AMITypeBottleRocketCPU:
			if cur.RemoteAccessUserName != "ec2-user" {
				return fmt.Errorf("AMIType %q but unexpected RemoteAccessUserName %q", cur.AMIType, cur.RemoteAccessUserName)
			}
			if cur.SSM != nil {
				if cur.SSM.DocumentName != "" && cfg.S3.BucketName == "" {
					return fmt.Errorf("AMIType %q requires SSMDocumentName %q but no S3BucketName", cur.AMIType, cur.SSM.DocumentName)
				}
			}
			if cur.KubeletExtraArgs != "" {
				return fmt.Errorf("AMIType %q but unexpected KubeletExtraArgs %q", cur.AMIType, cur.KubeletExtraArgs)
			}
		case fmt.Sprint(aws_eks_v2_types.AMITypesAl2X8664):
			if cur.RemoteAccessUserName != "ec2-user" {
				return fmt.Errorf("AMIType %q but unexpected RemoteAccessUserName %q", cur.AMIType, cur.RemoteAccessUserName)
			}
		case fmt.Sprint(aws_eks_v2_types.AMITypesAl2Arm64):
			if cur.RemoteAccessUserName != "ec2-user" {
				return fmt.Errorf("AMIType %q but unexpected RemoteAccessUserName %q", cur.AMIType, cur.RemoteAccessUserName)
			}
		case fmt.Sprint(aws_eks_v2_types.AMITypesAl2X8664Gpu):
			if cur.RemoteAccessUserName != "ec2-user" {
				return fmt.Errorf("AMIType %q but unexpected RemoteAccessUserName %q", cur.AMIType, cur.RemoteAccessUserName)
			}
		case ec2config.AMITypeWindowsServerCore2019X8664:
			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 ec2config.AMITypeBottleRocketCPU:
			if cur.InstanceType == "" {
				cur.InstanceType = DefaultNodeInstanceTypeCPU
			}
		case fmt.Sprint(aws_eks_v2_types.AMITypesAl2X8664), ec2config.AMITypeWindowsServerCore2019X8664:
			if cur.InstanceType == "" {
				cur.InstanceType = DefaultNodeInstanceTypeCPU
			}
		case fmt.Sprint(aws_eks_v2_types.AMITypesAl2X8664Gpu):
			if cur.InstanceType == "" {
				cur.InstanceType = DefaultNodeInstanceTypeGPU
			}
		default:
			return fmt.Errorf("unknown AddOnNodeGroups.ASGs[%q].AMIType %q", k, cur.AMIType)
		}

		if cfg.IsEnabledAddOnNLBHelloWorld() || cfg.IsEnabledAddOnALB2048() {
			// "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(cur.InstanceType, "m3."),
				strings.HasPrefix(cur.InstanceType, "c4."):
				return fmt.Errorf("AddOnNLBHelloWorld.Enable[%v] || AddOnALB2048.Enable[%v], but older instance type InstanceType %q for %q",
					cfg.IsEnabledAddOnNLBHelloWorld(),
					cfg.IsEnabledAddOnALB2048(),
					cur.InstanceType, k)
			}
		}

		if cur.ASGMinSize == 0 {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].ASGMinSize must be >0", k)
		}
		if cur.ASGDesiredCapacity == 0 {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].ASGDesiredCapacity must be >0", k)
		}
		if cur.ASGMaxSize == 0 {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].ASGMaxSize must be >0", k)
		}
		if cur.ASGMinSize > cur.ASGMaxSize {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].ASGMinSize %d > ASGMaxSize %d", k, cur.ASGMinSize, cur.ASGMaxSize)
		}
		if cur.ASGDesiredCapacity > cur.ASGMaxSize {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].ASGDesiredCapacity %d > ASGMaxSize %d", k, cur.ASGDesiredCapacity, cur.ASGMaxSize)
		}
		if cur.ASGMaxSize > NGMaxLimit {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].ASGMaxSize %d > NGMaxLimit %d", k, cur.ASGMaxSize, NGMaxLimit)
		}
		if cur.ASGDesiredCapacity > NGMaxLimit {
			return fmt.Errorf("AddOnNodeGroups.ASGs[%q].ASGDesiredCapacity %d > NGMaxLimit %d", k, cur.ASGDesiredCapacity, NGMaxLimit)
		}

		if cur.SSM != nil {
			switch cur.SSM.DocumentCreate {
			case true: // need create one, or already created
				if cur.SSM.DocumentName == "" {
					cur.SSM.DocumentName = cur.Name + "SSMDocument"
				}
				cur.SSM.DocumentName = strings.ReplaceAll(cur.SSM.DocumentName, "GetRef.Name", cfg.Name)
				cur.SSM.DocumentName = regex.ReplaceAllString(cur.SSM.DocumentName, "")
				if cur.SSM.DocumentExecutionTimeoutSeconds == 0 {
					cur.SSM.DocumentExecutionTimeoutSeconds = 3600
				}
				if cur.SSM.DocumentCommands == "" {
					return errors.New("empty SSM.DocumentCommands")
				}

			case false: // use existing one, or don't run any SSM
			}
		}

		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.AddOnNodeGroups.ASGs = processed
	return nil
}