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  }