k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/apis/discovery/validation/validation.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package validation 18 19 import ( 20 "fmt" 21 22 corev1 "k8s.io/api/core/v1" 23 apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" 24 metavalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" 25 "k8s.io/apimachinery/pkg/util/sets" 26 "k8s.io/apimachinery/pkg/util/validation" 27 "k8s.io/apimachinery/pkg/util/validation/field" 28 api "k8s.io/kubernetes/pkg/apis/core" 29 apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" 30 "k8s.io/kubernetes/pkg/apis/discovery" 31 ) 32 33 var ( 34 supportedAddressTypes = sets.NewString( 35 string(discovery.AddressTypeIPv4), 36 string(discovery.AddressTypeIPv6), 37 string(discovery.AddressTypeFQDN), 38 ) 39 supportedPortProtocols = sets.NewString( 40 string(api.ProtocolTCP), 41 string(api.ProtocolUDP), 42 string(api.ProtocolSCTP), 43 ) 44 maxTopologyLabels = 16 45 maxAddresses = 100 46 maxPorts = 20000 47 maxEndpoints = 1000 48 maxZoneHints = 8 49 ) 50 51 // ValidateEndpointSliceName can be used to check whether the given endpoint 52 // slice name is valid. Prefix indicates this name will be used as part of 53 // generation, in which case trailing dashes are allowed. 54 var ValidateEndpointSliceName = apimachineryvalidation.NameIsDNSSubdomain 55 56 // ValidateEndpointSlice validates an EndpointSlice. 57 func ValidateEndpointSlice(endpointSlice *discovery.EndpointSlice) field.ErrorList { 58 allErrs := apivalidation.ValidateObjectMeta(&endpointSlice.ObjectMeta, true, ValidateEndpointSliceName, field.NewPath("metadata")) 59 allErrs = append(allErrs, validateAddressType(endpointSlice.AddressType)...) 60 allErrs = append(allErrs, validateEndpoints(endpointSlice.Endpoints, endpointSlice.AddressType, field.NewPath("endpoints"))...) 61 allErrs = append(allErrs, validatePorts(endpointSlice.Ports, field.NewPath("ports"))...) 62 63 return allErrs 64 } 65 66 // ValidateEndpointSliceCreate validates an EndpointSlice when it is created. 67 func ValidateEndpointSliceCreate(endpointSlice *discovery.EndpointSlice) field.ErrorList { 68 return ValidateEndpointSlice(endpointSlice) 69 } 70 71 // ValidateEndpointSliceUpdate validates an EndpointSlice when it is updated. 72 func ValidateEndpointSliceUpdate(newEndpointSlice, oldEndpointSlice *discovery.EndpointSlice) field.ErrorList { 73 allErrs := ValidateEndpointSlice(newEndpointSlice) 74 allErrs = append(allErrs, apivalidation.ValidateImmutableField(newEndpointSlice.AddressType, oldEndpointSlice.AddressType, field.NewPath("addressType"))...) 75 76 return allErrs 77 } 78 79 func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.AddressType, fldPath *field.Path) field.ErrorList { 80 allErrs := field.ErrorList{} 81 82 if len(endpoints) > maxEndpoints { 83 allErrs = append(allErrs, field.TooMany(fldPath, len(endpoints), maxEndpoints)) 84 return allErrs 85 } 86 87 for i, endpoint := range endpoints { 88 idxPath := fldPath.Index(i) 89 addressPath := idxPath.Child("addresses") 90 91 if len(endpoint.Addresses) == 0 { 92 allErrs = append(allErrs, field.Required(addressPath, "must contain at least 1 address")) 93 } else if len(endpoint.Addresses) > maxAddresses { 94 allErrs = append(allErrs, field.TooMany(addressPath, len(endpoint.Addresses), maxAddresses)) 95 } 96 97 for i, address := range endpoint.Addresses { 98 // This validates known address types, unknown types fall through 99 // and do not get validated. 100 switch addrType { 101 case discovery.AddressTypeIPv4: 102 allErrs = append(allErrs, validation.IsValidIPv4Address(addressPath.Index(i), address)...) 103 allErrs = append(allErrs, apivalidation.ValidateNonSpecialIP(address, addressPath.Index(i))...) 104 case discovery.AddressTypeIPv6: 105 allErrs = append(allErrs, validation.IsValidIPv6Address(addressPath.Index(i), address)...) 106 allErrs = append(allErrs, apivalidation.ValidateNonSpecialIP(address, addressPath.Index(i))...) 107 case discovery.AddressTypeFQDN: 108 allErrs = append(allErrs, validation.IsFullyQualifiedDomainName(addressPath.Index(i), address)...) 109 } 110 } 111 112 if endpoint.NodeName != nil { 113 nnPath := idxPath.Child("nodeName") 114 for _, msg := range apivalidation.ValidateNodeName(*endpoint.NodeName, false) { 115 allErrs = append(allErrs, field.Invalid(nnPath, *endpoint.NodeName, msg)) 116 } 117 } 118 119 topologyPath := idxPath.Child("deprecatedTopology") 120 if len(endpoint.DeprecatedTopology) > maxTopologyLabels { 121 allErrs = append(allErrs, field.TooMany(topologyPath, len(endpoint.DeprecatedTopology), maxTopologyLabels)) 122 } 123 allErrs = append(allErrs, metavalidation.ValidateLabels(endpoint.DeprecatedTopology, topologyPath)...) 124 if _, found := endpoint.DeprecatedTopology[corev1.LabelTopologyZone]; found { 125 allErrs = append(allErrs, field.InternalError(topologyPath.Key(corev1.LabelTopologyZone), fmt.Errorf("reserved key was not removed in conversion"))) 126 } 127 128 if endpoint.Hostname != nil { 129 allErrs = append(allErrs, apivalidation.ValidateDNS1123Label(*endpoint.Hostname, idxPath.Child("hostname"))...) 130 } 131 132 if endpoint.Hints != nil { 133 allErrs = append(allErrs, validateHints(endpoint.Hints, idxPath.Child("hints"))...) 134 } 135 } 136 137 return allErrs 138 } 139 140 func validatePorts(endpointPorts []discovery.EndpointPort, fldPath *field.Path) field.ErrorList { 141 allErrs := field.ErrorList{} 142 143 if len(endpointPorts) > maxPorts { 144 allErrs = append(allErrs, field.TooMany(fldPath, len(endpointPorts), maxPorts)) 145 return allErrs 146 } 147 148 portNames := sets.String{} 149 for i, endpointPort := range endpointPorts { 150 idxPath := fldPath.Index(i) 151 152 if len(*endpointPort.Name) > 0 { 153 allErrs = append(allErrs, apivalidation.ValidateDNS1123Label(*endpointPort.Name, idxPath.Child("name"))...) 154 } 155 156 if portNames.Has(*endpointPort.Name) { 157 allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), endpointPort.Name)) 158 } else { 159 portNames.Insert(*endpointPort.Name) 160 } 161 162 if endpointPort.Protocol == nil { 163 allErrs = append(allErrs, field.Required(idxPath.Child("protocol"), "")) 164 } else if !supportedPortProtocols.Has(string(*endpointPort.Protocol)) { 165 allErrs = append(allErrs, field.NotSupported(idxPath.Child("protocol"), *endpointPort.Protocol, supportedPortProtocols.List())) 166 } 167 168 if endpointPort.AppProtocol != nil { 169 allErrs = append(allErrs, apivalidation.ValidateQualifiedName(*endpointPort.AppProtocol, idxPath.Child("appProtocol"))...) 170 } 171 } 172 173 return allErrs 174 } 175 176 func validateAddressType(addressType discovery.AddressType) field.ErrorList { 177 allErrs := field.ErrorList{} 178 179 if addressType == "" { 180 allErrs = append(allErrs, field.Required(field.NewPath("addressType"), "")) 181 } else if !supportedAddressTypes.Has(string(addressType)) { 182 allErrs = append(allErrs, field.NotSupported(field.NewPath("addressType"), addressType, supportedAddressTypes.List())) 183 } 184 185 return allErrs 186 } 187 188 func validateHints(endpointHints *discovery.EndpointHints, fldPath *field.Path) field.ErrorList { 189 allErrs := field.ErrorList{} 190 191 fzPath := fldPath.Child("forZones") 192 if len(endpointHints.ForZones) > maxZoneHints { 193 allErrs = append(allErrs, field.TooMany(fzPath, len(endpointHints.ForZones), maxZoneHints)) 194 return allErrs 195 } 196 197 zoneNames := sets.String{} 198 for i, forZone := range endpointHints.ForZones { 199 zonePath := fzPath.Index(i).Child("name") 200 if zoneNames.Has(forZone.Name) { 201 allErrs = append(allErrs, field.Duplicate(zonePath, forZone.Name)) 202 } else { 203 zoneNames.Insert(forZone.Name) 204 } 205 206 for _, msg := range validation.IsValidLabelValue(forZone.Name) { 207 allErrs = append(allErrs, field.Invalid(zonePath, forZone.Name, msg)) 208 } 209 } 210 211 return allErrs 212 }