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 }