mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 13:50:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			199 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2020 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package validation
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
 | |
| 	utilvalidation "k8s.io/apimachinery/pkg/util/validation"
 | |
| 	"k8s.io/apimachinery/pkg/util/validation/field"
 | |
| 	"k8s.io/kubernetes/pkg/apis/apiserverinternal"
 | |
| 	apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
 | |
| )
 | |
| 
 | |
| // ValidateStorageVersion validate the storage version object.
 | |
| func ValidateStorageVersion(sv *apiserverinternal.StorageVersion) field.ErrorList {
 | |
| 	var allErrs field.ErrorList
 | |
| 	allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&sv.ObjectMeta, false, ValidateStorageVersionName, field.NewPath("metadata"))...)
 | |
| 	allErrs = append(allErrs, validateStorageVersionStatus(sv.Status, field.NewPath("status"))...)
 | |
| 	return allErrs
 | |
| }
 | |
| 
 | |
| // ValidateStorageVersionName is a ValidateNameFunc for storage version names
 | |
| func ValidateStorageVersionName(name string, prefix bool) []string {
 | |
| 	var allErrs []string
 | |
| 	idx := strings.LastIndex(name, ".")
 | |
| 	if idx < 0 {
 | |
| 		allErrs = append(allErrs, "name must be in the form of <group>.<resource>")
 | |
| 	} else {
 | |
| 		for _, msg := range utilvalidation.IsDNS1123Subdomain(name[:idx]) {
 | |
| 			allErrs = append(allErrs, "the group segment "+msg)
 | |
| 		}
 | |
| 		for _, msg := range utilvalidation.IsDNS1035Label(name[idx+1:]) {
 | |
| 			allErrs = append(allErrs, "the resource segment "+msg)
 | |
| 		}
 | |
| 	}
 | |
| 	return allErrs
 | |
| }
 | |
| 
 | |
| // ValidateStorageVersionUpdate tests if an update to a StorageVersion is valid.
 | |
| func ValidateStorageVersionUpdate(sv, oldSV *apiserverinternal.StorageVersion) field.ErrorList {
 | |
| 	// no error since StorageVersionSpec is an empty spec
 | |
| 	return field.ErrorList{}
 | |
| }
 | |
| 
 | |
| // ValidateStorageVersionStatusUpdate tests if an update to a StorageVersionStatus is valid.
 | |
| func ValidateStorageVersionStatusUpdate(sv, oldSV *apiserverinternal.StorageVersion) field.ErrorList {
 | |
| 	var allErrs field.ErrorList
 | |
| 	allErrs = append(allErrs, validateStorageVersionStatus(sv.Status, field.NewPath("status"))...)
 | |
| 	return allErrs
 | |
| }
 | |
| 
 | |
| func validateStorageVersionStatus(ss apiserverinternal.StorageVersionStatus, fldPath *field.Path) field.ErrorList {
 | |
| 	var allErrs field.ErrorList
 | |
| 	for i, ssv := range ss.StorageVersions {
 | |
| 		allErrs = append(allErrs, validateServerStorageVersion(ssv, fldPath.Child("storageVersions").Index(i))...)
 | |
| 	}
 | |
| 	if err := validateCommonVersion(ss, fldPath); err != nil {
 | |
| 		allErrs = append(allErrs, err)
 | |
| 	}
 | |
| 	allErrs = append(allErrs, validateStorageVersionCondition(ss.Conditions, fldPath)...)
 | |
| 	return allErrs
 | |
| }
 | |
| 
 | |
| func validateServerStorageVersion(ssv apiserverinternal.ServerStorageVersion, fldPath *field.Path) field.ErrorList {
 | |
| 	allErrs := field.ErrorList{}
 | |
| 	for _, msg := range apimachineryvalidation.NameIsDNSSubdomain(ssv.APIServerID, false) {
 | |
| 		allErrs = append(allErrs, field.Invalid(fldPath.Child("apiServerID"), ssv.APIServerID, msg))
 | |
| 	}
 | |
| 	if errs := isValidAPIVersion(ssv.EncodingVersion); len(errs) > 0 {
 | |
| 		allErrs = append(allErrs, field.Invalid(fldPath.Child("encodingVersion"), ssv.EncodingVersion, strings.Join(errs, ",")))
 | |
| 	}
 | |
| 
 | |
| 	found := false
 | |
| 	for i, dv := range ssv.DecodableVersions {
 | |
| 		if errs := isValidAPIVersion(dv); len(errs) > 0 {
 | |
| 			allErrs = append(allErrs, field.Invalid(fldPath.Child("decodableVersions").Index(i), dv, strings.Join(errs, ",")))
 | |
| 		}
 | |
| 		if dv == ssv.EncodingVersion {
 | |
| 			found = true
 | |
| 		}
 | |
| 	}
 | |
| 	if !found {
 | |
| 		allErrs = append(allErrs, field.Invalid(fldPath.Child("decodableVersions"), ssv.DecodableVersions, fmt.Sprintf("decodableVersions must include encodingVersion %s", ssv.EncodingVersion)))
 | |
| 	}
 | |
| 	return allErrs
 | |
| }
 | |
| 
 | |
| func commonVersion(ssv []apiserverinternal.ServerStorageVersion) *string {
 | |
| 	if len(ssv) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	commonVersion := ssv[0].EncodingVersion
 | |
| 	for _, v := range ssv[1:] {
 | |
| 		if v.EncodingVersion != commonVersion {
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| 	return &commonVersion
 | |
| }
 | |
| 
 | |
| func validateCommonVersion(svs apiserverinternal.StorageVersionStatus, fldPath *field.Path) *field.Error {
 | |
| 	actualCommonVersion := commonVersion(svs.StorageVersions)
 | |
| 	if actualCommonVersion == nil && svs.CommonEncodingVersion == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if actualCommonVersion == nil && svs.CommonEncodingVersion != nil {
 | |
| 		return field.Invalid(fldPath.Child("commonEncodingVersion"), *svs.CommonEncodingVersion, "should be nil if servers do not agree on the same encoding version, or if there is no server reporting the supported versions yet")
 | |
| 	}
 | |
| 	if actualCommonVersion != nil && svs.CommonEncodingVersion == nil {
 | |
| 		return field.Invalid(fldPath.Child("commonEncodingVersion"), svs.CommonEncodingVersion, fmt.Sprintf("the common encoding version is %s", *actualCommonVersion))
 | |
| 	}
 | |
| 	if actualCommonVersion != nil && svs.CommonEncodingVersion != nil && *actualCommonVersion != *svs.CommonEncodingVersion {
 | |
| 		return field.Invalid(fldPath.Child("commonEncodingVersion"), *svs.CommonEncodingVersion, fmt.Sprintf("the actual common encoding version is %s", *actualCommonVersion))
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func validateStorageVersionCondition(conditions []apiserverinternal.StorageVersionCondition, fldPath *field.Path) field.ErrorList {
 | |
| 	allErrs := field.ErrorList{}
 | |
| 	// We do not verify that the condition type or the condition status is
 | |
| 	// a predefined one because we might add more type or status later.
 | |
| 	seenType := make(map[apiserverinternal.StorageVersionConditionType]int)
 | |
| 	for i, condition := range conditions {
 | |
| 		if ii, ok := seenType[condition.Type]; ok {
 | |
| 			allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("type"), string(condition.Type),
 | |
| 				fmt.Sprintf("the type of the condition is not unique, it also appears in conditions[%d]", ii)))
 | |
| 		}
 | |
| 		seenType[condition.Type] = i
 | |
| 		allErrs = append(allErrs, apivalidation.ValidateQualifiedName(string(condition.Type), fldPath.Index(i).Child("type"))...)
 | |
| 		allErrs = append(allErrs, apivalidation.ValidateQualifiedName(string(condition.Status), fldPath.Index(i).Child("status"))...)
 | |
| 		if condition.Reason == "" {
 | |
| 			allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("reason"), "reason cannot be empty"))
 | |
| 		}
 | |
| 		if condition.Message == "" {
 | |
| 			allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("message"), "message cannot be empty"))
 | |
| 		}
 | |
| 	}
 | |
| 	return allErrs
 | |
| }
 | |
| 
 | |
| const dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?"
 | |
| const dns1035LabelErrMsg string = "a DNS-1035 label, which must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character"
 | |
| 
 | |
| // isValidAPIVersion tests whether the value passed is a valid apiVersion. A
 | |
| // valid apiVersion contains a version string that matches DNS_LABEL format,
 | |
| // with an optional group/ prefix, where the group string matches DNS_SUBDOMAIN
 | |
| // format. If the value is not valid, a list of error strings is returned.
 | |
| // Otherwise an empty list (or nil) is returned.
 | |
| func isValidAPIVersion(apiVersion string) []string {
 | |
| 	var errs []string
 | |
| 	parts := strings.Split(apiVersion, "/")
 | |
| 	var version string
 | |
| 	switch len(parts) {
 | |
| 	case 1:
 | |
| 		version = parts[0]
 | |
| 	case 2:
 | |
| 		var group string
 | |
| 		group, version = parts[0], parts[1]
 | |
| 		if len(group) == 0 {
 | |
| 			errs = append(errs, "group part: "+utilvalidation.EmptyError())
 | |
| 		} else if msgs := utilvalidation.IsDNS1123Subdomain(group); len(msgs) != 0 {
 | |
| 			errs = append(errs, prefixEach(msgs, "group part: ")...)
 | |
| 		}
 | |
| 	default:
 | |
| 		return append(errs, "an apiVersion is "+utilvalidation.RegexError(dns1035LabelErrMsg, dns1035LabelFmt, "my-name", "abc-123")+
 | |
| 			" with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyVersion')")
 | |
| 	}
 | |
| 
 | |
| 	if len(version) == 0 {
 | |
| 		errs = append(errs, "version part: "+utilvalidation.EmptyError())
 | |
| 	} else if msgs := utilvalidation.IsDNS1035Label(version); len(msgs) != 0 {
 | |
| 		errs = append(errs, prefixEach(msgs, "version part: ")...)
 | |
| 	}
 | |
| 	return errs
 | |
| }
 | |
| 
 | |
| func prefixEach(msgs []string, prefix string) []string {
 | |
| 	for i := range msgs {
 | |
| 		msgs[i] = prefix + msgs[i]
 | |
| 	}
 | |
| 	return msgs
 | |
| }
 |