github.com/openshift/installer@v1.4.17/pkg/types/aws/validation/machinepool.go (about)

     1  package validation
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"k8s.io/apimachinery/pkg/util/sets"
     8  	"k8s.io/apimachinery/pkg/util/validation/field"
     9  
    10  	"github.com/openshift/installer/pkg/types"
    11  	"github.com/openshift/installer/pkg/types/aws"
    12  )
    13  
    14  var (
    15  	validArchitectures = map[types.Architecture]bool{
    16  		types.ArchitectureAMD64: true,
    17  		types.ArchitectureARM64: true,
    18  	}
    19  
    20  	// validArchitectureValues lists the supported arches for AWS
    21  	validArchitectureValues = func() []string {
    22  		v := make([]string, 0, len(validArchitectures))
    23  		for m := range validArchitectures {
    24  			v = append(v, string(m))
    25  		}
    26  		return v
    27  	}()
    28  
    29  	validMetadataAuthValues = sets.NewString("Required", "Optional")
    30  )
    31  
    32  // AWS has a limit of 16 security groups. See:
    33  // https://docs.aws.amazon.com/vpc/latest/userguide/amazon-vpc-limits.html
    34  // We set a user limit of 10 and reserve 6 for use by OpenShift.
    35  const maxUserSecurityGroupsCount = 10
    36  
    37  // ValidateMachinePool checks that the specified machine pool is valid.
    38  func ValidateMachinePool(platform *aws.Platform, p *aws.MachinePool, fldPath *field.Path) field.ErrorList {
    39  	allErrs := field.ErrorList{}
    40  	for i, zone := range p.Zones {
    41  		if !strings.HasPrefix(zone, platform.Region) {
    42  			allErrs = append(allErrs, field.Invalid(fldPath.Child("zones").Index(i), zone, fmt.Sprintf("Zone not in configured region (%s)", platform.Region)))
    43  		}
    44  	}
    45  
    46  	if p.EC2RootVolume.Type != "" {
    47  		allErrs = append(allErrs, validateVolumeSize(p, fldPath)...)
    48  		allErrs = append(allErrs, validateIOPS(p, fldPath)...)
    49  	}
    50  
    51  	if p.EC2Metadata.Authentication != "" && !validMetadataAuthValues.Has(p.EC2Metadata.Authentication) {
    52  		allErrs = append(allErrs, field.Invalid(fldPath.Child("authentication"), p.EC2Metadata.Authentication, "must be either Required or Optional"))
    53  	}
    54  
    55  	allErrs = append(allErrs, validateSecurityGroups(platform, p, fldPath)...)
    56  
    57  	return allErrs
    58  }
    59  
    60  func validateSecurityGroups(platform *aws.Platform, p *aws.MachinePool, fldPath *field.Path) field.ErrorList {
    61  	allErrs := field.ErrorList{}
    62  
    63  	if len(p.AdditionalSecurityGroupIDs) > 0 && len(platform.Subnets) == 0 {
    64  		allErrs = append(allErrs, field.Required(fldPath.Child("platform.subnets"), "subnets must be provided when additional security groups are present"))
    65  	}
    66  
    67  	// The installer also creates a security group: `${var.cluster_id}-master-sg/${var.cluster_id}-worker-sg`
    68  	if count := len(p.AdditionalSecurityGroupIDs); count > maxUserSecurityGroupsCount {
    69  		allErrs = append(allErrs, field.TooMany(fldPath, count, maxUserSecurityGroupsCount))
    70  	}
    71  
    72  	return allErrs
    73  }
    74  
    75  func validateVolumeSize(p *aws.MachinePool, fldPath *field.Path) field.ErrorList {
    76  	allErrs := field.ErrorList{}
    77  	volumeSize := p.EC2RootVolume.Size
    78  
    79  	if volumeSize <= 0 {
    80  		allErrs = append(allErrs, field.Invalid(fldPath.Child("size"), volumeSize, "volume size value must be a positive number"))
    81  	}
    82  
    83  	return allErrs
    84  }
    85  
    86  func validateIOPS(p *aws.MachinePool, fldPath *field.Path) field.ErrorList {
    87  	allErrs := field.ErrorList{}
    88  	volumeType := strings.ToLower(p.EC2RootVolume.Type)
    89  	iops := p.EC2RootVolume.IOPS
    90  
    91  	switch volumeType {
    92  	case "io1", "io2":
    93  		if iops <= 0 {
    94  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iops"), iops, "iops must be a positive number"))
    95  		}
    96  	case "gp3":
    97  		if iops < 0 {
    98  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iops"), iops, "iops must be a positive number"))
    99  		}
   100  	case "gp2", "st1", "sc1", "standard":
   101  		if iops != 0 {
   102  			allErrs = append(allErrs, field.Invalid(fldPath.Child("iops"), iops, fmt.Sprintf("iops not supported for type %s", volumeType)))
   103  		}
   104  	default:
   105  		allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), volumeType, fmt.Sprintf("failed to find volume type %s", volumeType)))
   106  	}
   107  
   108  	return allErrs
   109  }
   110  
   111  // ValidateAMIID check the AMI ID is set for a machine pool.
   112  func ValidateAMIID(platform *aws.Platform, p *aws.MachinePool, fldPath *field.Path) field.ErrorList {
   113  	allErrs := field.ErrorList{}
   114  	pool := &aws.MachinePool{AMIID: platform.AMIID}
   115  	pool.Set(platform.DefaultMachinePlatform)
   116  	pool.Set(p)
   117  
   118  	// regions is a list of regions for which the user should set AMI ID as copying the AMI to these regions
   119  	// is known to not be supported.
   120  	regions := sets.NewString("us-iso-east-1", "us-isob-east-1", "us-iso-west-1", "cn-north-1", "cn-northwest-1")
   121  	if pool.AMIID == "" && regions.Has(platform.Region) {
   122  		allErrs = append(allErrs, field.Required(fldPath, fmt.Sprintf("AMI ID must be provided for regions %s", strings.Join(regions.List(), ", "))))
   123  	}
   124  	return allErrs
   125  }
   126  
   127  // ValidateMachinePoolArchitecture checks that a valid architecture is set for a machine pool.
   128  func ValidateMachinePoolArchitecture(pool *types.MachinePool, fldPath *field.Path) field.ErrorList {
   129  	allErrs := field.ErrorList{}
   130  
   131  	if !validArchitectures[pool.Architecture] {
   132  		allErrs = append(allErrs, field.NotSupported(fldPath, pool.Architecture, validArchitectureValues))
   133  	}
   134  	return allErrs
   135  }