func()

in eksconfig/config.go [1167:1446]


func (cfg *Config) validateConfig() error {
	if len(cfg.Name) == 0 {
		return errors.New("name is empty")
	}
	if cfg.Name != strings.ToLower(cfg.Name) {
		return fmt.Errorf("name %q must be in lower-case", cfg.Name)
	}

	if cfg.LogColorOverride == "" {
		_, cerr := terminal.IsColor()
		if cfg.LogColor && cerr != nil {
			cfg.LogColor = false
			fmt.Fprintf(os.Stderr, "[WARN] LogColor is set to 'false' due to error %+v", cerr)
		}
	} else {
		// non-empty override, don't even run "terminal.IsColor"
		ov, perr := strconv.ParseBool(cfg.LogColorOverride)
		if perr != nil {
			return fmt.Errorf("failed to parse LogColorOverride %q (%v)", cfg.LogColorOverride, perr)
		}
		cfg.LogColor = ov
		fmt.Fprintf(os.Stderr, "[WARN] LogColor is overwritten with %q", cfg.LogColorOverride)
	}

	if len(cfg.LogOutputs) == 0 {
		return errors.New("LogOutputs is not empty")
	}

	if cfg.Clients == 0 {
		cfg.Clients = DefaultClients
	}
	if cfg.ClientQPS == 0 {
		cfg.ClientQPS = DefaultClientQPS
	}
	if cfg.ClientBurst == 0 {
		cfg.ClientBurst = DefaultClientBurst
	}
	if cfg.ClientTimeout == time.Duration(0) {
		cfg.ClientTimeout = DefaultClientTimeout
	}
	cfg.ClientTimeoutString = cfg.ClientTimeout.String()

	if cfg.ConfigPath == "" {
		rootDir, err := os.Getwd()
		if err != nil {
			rootDir = filepath.Join(os.TempDir(), cfg.Name)
			if err := os.MkdirAll(rootDir, 0700); err != nil {
				return err
			}
		}
		cfg.ConfigPath = filepath.Join(rootDir, cfg.Name+".yaml")
		var p string
		p, err = filepath.Abs(cfg.ConfigPath)
		if err != nil {
			panic(err)
		}
		cfg.ConfigPath = p
	}
	if err := os.MkdirAll(filepath.Dir(cfg.ConfigPath), 0700); err != nil {
		return err
	}
	if err := fileutil.IsDirWriteable(filepath.Dir(cfg.ConfigPath)); err != nil {
		return err
	}

	if len(cfg.LogOutputs) == 1 && (cfg.LogOutputs[0] == "stderr" || cfg.LogOutputs[0] == "stdout") {
		cfg.LogOutputs = append(cfg.LogOutputs, strings.ReplaceAll(cfg.ConfigPath, ".yaml", "")+".log")
	}
	logFilePath := ""
	for _, fpath := range cfg.LogOutputs {
		if filepath.Ext(fpath) == ".log" {
			logFilePath = fpath
			break
		}
	}
	if logFilePath == "" {
		return fmt.Errorf("*.log file not found in %q", cfg.LogOutputs)
	}

	if cfg.Version == "" {
		return errors.New("empty Parameters.Version")
	}
	var err error
	cfg.VersionValue, err = strconv.ParseFloat(cfg.Version, 64)
	if err != nil {
		return fmt.Errorf("cannot parse Parameters.Version %q (%v)", cfg.Version, err)
	}

	if len(cfg.Role.ServicePrincipals) == 0 {
		return errors.New("empty Role.ServicePrincipals")
	}
	if len(cfg.Role.ManagedPolicyARNs) == 0 {
		return errors.New("empty Role.ManagedPolicyARNs")
	}
	// e.g.,
	// "api error LimitExceeded: Cannot exceed quota for PoliciesPerRole: 10"
	if len(cfg.Role.ManagedPolicyARNs) > 9 {
		return fmt.Errorf("too many ManagedPolicyARNs %q", cfg.Role.ManagedPolicyARNs)
	}

	found := false
	for _, v := range cfg.Role.ServicePrincipals {
		if v == "eks.amazonaws.com" {
			found = true
			break
		}
	}
	if !found {
		return fmt.Errorf("Role.ServicePrincipals missing 'eks.amazonaws.com' (%q)", cfg.Role.ServicePrincipals)
	}
	found = false
	for _, v := range cfg.Role.ManagedPolicyARNs {
		if v == "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" {
			found = true
			break
		}
	}
	if !found {
		return fmt.Errorf("Role.ManagedPolicyARNs missing 'arn:aws:iam::aws:policy/AmazonEKSClusterPolicy' (%q)", cfg.Role.ManagedPolicyARNs)
	}

	switch cfg.Role.Create {
	case true: // need create one, or already created
		if cfg.Role.Name == "" {
			cfg.Role.Name = cfg.Name + "-role"
		}
	case false: // use existing one
		if cfg.Role.ARN == "" {
			return fmt.Errorf("Role.Create false; expect non-empty RoleARN but got %q", cfg.Role.ARN)
		}
		if cfg.Role.Name == "" {
			cfg.Role.Name = getNameFromARN(cfg.Role.ARN)
		}
	}
	if cfg.Role.PolicyName == "" {
		cfg.Role.PolicyName = cfg.Name + "-policy"
	}

	switch cfg.VPC.Create {
	case true: // need create one, or already created
	// just ignore...
	// could be populated from previous run
	// do not error, so long as VPCCreate false, VPC won't be deleted
	case false: // use existing one
		if cfg.VPC.ID == "" {
			return fmt.Errorf("RoleCreate false; expect non-empty VPCID but got %q", cfg.VPC.ID)
		}
	}

	if cfg.VPC.NodeGroupSecurityGroupName == "" {
		cfg.VPC.NodeGroupSecurityGroupName = cfg.Name + "-node-group-security-group"
	}
	if len(cfg.VPC.PublicSubnetCIDRs) < 2 {
		return fmt.Errorf("unexpected number of VPC.PublicSubnetCIDRs %v (expected at least 2)", cfg.VPC.PublicSubnetCIDRs)
	}

	switch cfg.Encryption.CMKCreate {
	case true: // need create one, or already created
		// just ignore...
		// could be populated from previous run
		// do not error, so long as EncryptionCMKCreate false, CMK won't be deleted
	case false: // use existing one
	}

	switch cfg.RemoteAccessKeyCreate {
	case true: // need create one, or already created
		if cfg.RemoteAccessKeyName == "" {
			cfg.RemoteAccessKeyName = cfg.Name + "-remote-access-key"
		}
		if cfg.RemoteAccessPrivateKeyPath == "" {
			cfg.RemoteAccessPrivateKeyPath = filepath.Join(os.TempDir(), randutil.String(10)+".insecure.key")
		}

	case false: // use existing one
		if cfg.RemoteAccessKeyName == "" {
			return fmt.Errorf("RemoteAccessKeyCreate false; expect non-empty RemoteAccessKeyName but got %q", cfg.RemoteAccessKeyName)
		}
		if cfg.RemoteAccessPrivateKeyPath == "" {
			return fmt.Errorf("RemoteAccessKeyCreate false; expect non-empty RemoteAccessPrivateKeyPath but got %q", cfg.RemoteAccessPrivateKeyPath)
		}
		if !fileutil.Exist(cfg.RemoteAccessPrivateKeyPath) {
			return fmt.Errorf("RemoteAccessPrivateKeyPath %q does not exist", cfg.RemoteAccessPrivateKeyPath)
		}
	}
	keyDir := filepath.Dir(cfg.RemoteAccessPrivateKeyPath)
	if err := fileutil.IsDirWriteable(keyDir); err != nil {
		return err
	}

	if cfg.KubectlCommandsOutputPath == "" {
		cfg.KubectlCommandsOutputPath = strings.ReplaceAll(cfg.ConfigPath, ".yaml", "") + ".kubectl.sh"
	}
	if filepath.Ext(cfg.KubectlCommandsOutputPath) != ".sh" {
		cfg.KubectlCommandsOutputPath = cfg.KubectlCommandsOutputPath + ".sh"
	}
	if err := fileutil.IsDirWriteable(filepath.Dir(cfg.KubectlCommandsOutputPath)); err != nil {
		return err
	}
	if cfg.RemoteAccessCommandsOutputPath == "" {
		cfg.RemoteAccessCommandsOutputPath = strings.ReplaceAll(cfg.ConfigPath, ".yaml", "") + ".ssh.sh"
	}
	if filepath.Ext(cfg.RemoteAccessCommandsOutputPath) != ".sh" {
		cfg.RemoteAccessCommandsOutputPath = cfg.RemoteAccessCommandsOutputPath + ".sh"
	}
	if err := fileutil.IsDirWriteable(filepath.Dir(cfg.RemoteAccessCommandsOutputPath)); err != nil {
		return err
	}

	if cfg.CommandAfterCreateClusterOutputPath == "" {
		cfg.CommandAfterCreateClusterOutputPath = strings.ReplaceAll(cfg.ConfigPath, ".yaml", "") + ".after-create-cluster.out.log"
	}
	if filepath.Ext(cfg.CommandAfterCreateClusterOutputPath) != ".log" {
		cfg.CommandAfterCreateClusterOutputPath = cfg.CommandAfterCreateClusterOutputPath + ".log"
	}
	if cfg.CommandAfterCreateClusterTimeout == time.Duration(0) {
		cfg.CommandAfterCreateClusterTimeout = DefaultCommandAfterCreateClusterTimeout
	}
	if err := fileutil.IsDirWriteable(filepath.Dir(cfg.CommandAfterCreateClusterOutputPath)); err != nil {
		return err
	}
	cfg.CommandAfterCreateClusterTimeoutString = cfg.CommandAfterCreateClusterTimeout.String()

	if cfg.CommandAfterCreateAddOnsOutputPath == "" {
		cfg.CommandAfterCreateAddOnsOutputPath = strings.ReplaceAll(cfg.ConfigPath, ".yaml", "") + ".after-create-add-ons.out.log"
	}
	if filepath.Ext(cfg.CommandAfterCreateAddOnsOutputPath) != ".log" {
		cfg.CommandAfterCreateAddOnsOutputPath = cfg.CommandAfterCreateAddOnsOutputPath + ".log"
	}
	if err := fileutil.IsDirWriteable(filepath.Dir(cfg.CommandAfterCreateAddOnsOutputPath)); err != nil {
		return err
	}
	if cfg.CommandAfterCreateAddOnsTimeout == time.Duration(0) {
		cfg.CommandAfterCreateAddOnsTimeout = DefaultCommandAfterCreateAddOnsTimeout
	}
	cfg.CommandAfterCreateAddOnsTimeoutString = cfg.CommandAfterCreateAddOnsTimeout.String()

	if cfg.KubeConfigPath == "" {
		cfg.KubeConfigPath = strings.ReplaceAll(cfg.ConfigPath, ".yaml", "") + ".kubeconfig.yaml"
	}
	if err := fileutil.IsDirWriteable(filepath.Dir(cfg.KubeConfigPath)); err != nil {
		return err
	}

	if cfg.KubectlPath == "" && cfg.KubectlDownloadURL == "" {
		return errors.New("empty KubectlPath and KubectlDownloadURL")
	}
	if !strings.ContainsAny(cfg.KubectlDownloadURL, runtime.GOOS) {
		return fmt.Errorf("kubectl-download-url %q build OS mismatch, expected %q", cfg.KubectlDownloadURL, runtime.GOOS)
	}

	if err := cfg.evaluateCommandRefs(); err != nil {
		return err
	}

	switch cfg.S3.BucketCreate {
	case true: // need create one, or already created
		if cfg.S3.BucketName == "" {
			cfg.S3.BucketName = cfg.Name + "-s3-bucket"
		}
		if cfg.S3.BucketLifecycleExpirationDays > 0 && cfg.S3.BucketLifecycleExpirationDays < 3 {
			cfg.S3.BucketLifecycleExpirationDays = 3
		}
	case false: // use existing one
		if cfg.S3.BucketName == "" {
			return errors.New("empty S3BucketName")
		}
	}

	if cfg.CWNamespace == "" {
		cfg.CWNamespace = "aws-k8s-tester-eks"
	}

	if cfg.Status == nil {
		cfg.Status = &Status{
			Up:                   false,
			PrivateDNSToNodeInfo: make(map[string]NodeInfo),
		}
	}
	return nil
}