apiserver/pkg/apis/apiserverinternal/validation/validation.go

129 lines
5.3 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"
"k8s.io/apimachinery/pkg/util/validation"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/apis/apiserverinternal"
)
// ValidateStorageVersion validate the storage version object.
func ValidateStorageVersion(sv *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 := utilvalidation.IsDNS1035Label(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 := utilvalidation.IsDNS1035Label(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
for _, msg := range validation.IsQualifiedName(string(condition.Type)) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("type"), string(condition.Type), msg))
}
for _, msg := range validation.IsQualifiedName(string(condition.Status)) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("status"), string(condition.Type), msg))
}
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
}