updated vendor

This commit is contained in:
2026-06-16 08:02:19 +02:00
parent 2f7f99d3f0
commit 77299d0c64
1283 changed files with 67302 additions and 208958 deletions
+6
View File
@@ -29,6 +29,12 @@ func MinError[T constraints.Integer](min T) string {
return fmt.Sprintf("must be greater than or equal to %d", min)
}
// MaxError returns a string explanation of a "must be less than or equal"
// validation failure.
func MaxError[T constraints.Integer](max T) string {
return fmt.Sprintf("must be less than or equal to %d", max)
}
// MaxLenError returns a string explanation of a "string too long" validation
// failure.
func MaxLenError(length int) string {
+63
View File
@@ -0,0 +1,63 @@
/*
Copyright 2015 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 content
import (
"fmt"
"strings"
)
// Strings that cannot be used as names specified as path segments (like the
// REST API or etcd store).
var pathSegmentNameMayNotBe = []string{".", ".."}
// Substrings that cannot be used in names specified as path segments (like the
// REST API or etcd store).
var pathSegmentNameMayNotContain = []string{"/", "%"}
// IsPathSegmentName validates the name can be safely encoded as a path
// segment.
//
// Note that, for historical reason, this function does not check for
// empty strings or impose a limit on the length of the name.
func IsPathSegmentName(name string) []string {
for _, illegalName := range pathSegmentNameMayNotBe {
if name == illegalName {
return []string{fmt.Sprintf(`may not be '%s'`, illegalName)}
}
}
return IsPathSegmentPrefix(name)
}
// IsPathSegmentPrefix validates the name can be used as a prefix for a
// name which will be encoded as a path segment It does not check for exact
// matches with disallowed names, since an arbitrary suffix might make the name
// valid.
//
// Note that, for historical reason, this function does not check for
// empty strings or impose a limit on the length of the name.
func IsPathSegmentPrefix(name string) []string {
var errors []string
for _, illegalContent := range pathSegmentNameMayNotContain {
if strings.Contains(name, illegalContent) {
errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
}
}
return errors
}
+71
View File
@@ -0,0 +1,71 @@
/*
Copyright 2025 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 validate
import (
"context"
"k8s.io/apimachinery/pkg/api/operation"
"k8s.io/apimachinery/pkg/util/validation/field"
)
// DiscriminatedRule defines a validation to apply for a specific discriminator value.
type DiscriminatedRule[Tfield any, Tdisc comparable] struct {
Value Tdisc
Validation ValidateFunc[Tfield]
}
// Discriminated validates a member field based on a discriminator value.
// It iterates through the rules and applies the first one that matches the discriminator.
// If no rule matches, it applies the defaultValidation if provided.
//
// It performs ratcheting: if the operation is an Update, and neither the discriminator
// nor the value (checked via equiv) have changed, validation is skipped.
func Discriminated[Tfield any, Tdisc comparable, Tstruct any](ctx context.Context, op operation.Operation, structPath *field.Path,
obj, oldObj *Tstruct, fieldName string, getMemberValue func(*Tstruct) Tfield, getDiscriminator func(*Tstruct) Tdisc,
equiv MatchFunc[Tfield], defaultValidation ValidateFunc[Tfield], rules []DiscriminatedRule[Tfield, Tdisc],
) field.ErrorList {
value := getMemberValue(obj)
discriminator := getDiscriminator(obj)
var oldValue Tfield
var oldDiscriminator Tdisc
if oldObj != nil {
oldValue = getMemberValue(oldObj)
oldDiscriminator = getDiscriminator(oldObj)
}
if op.Type == operation.Update && oldObj != nil && discriminator == oldDiscriminator && equiv(value, oldValue) {
return nil
}
fldPath := structPath.Child(fieldName)
for _, rule := range rules {
if rule.Value == discriminator {
if rule.Validation == nil {
return nil
}
return rule.Validation(ctx, op, fldPath, value, oldValue)
}
}
if defaultValidation != nil {
return defaultValidation(ctx, op, fldPath, value, oldValue)
}
return nil
}
+85 -2
View File
@@ -18,6 +18,8 @@ package validate
import (
"context"
"math"
"unicode/utf8"
"k8s.io/apimachinery/pkg/api/operation"
"k8s.io/apimachinery/pkg/api/validate/constraints"
@@ -31,9 +33,40 @@ func MaxLength[T ~string](_ context.Context, _ operation.Operation, fldPath *fie
if value == nil {
return nil
}
if len(*value) > max {
return field.ErrorList{field.TooLong(fldPath, *value, max).WithOrigin("maxLength")}
// if the length of the value in bytes is less
// than the maximum size then we can confidently
// say that this value is within the bounds
// enforced by the maximum value regardless
// of the actual makeup of characters in the value
byteLength := len(*value)
if byteLength <= max {
return nil
}
// because runes are up to 4 byte characters, if we assume all characters
// in the input are runes, the minimum number of characters that
// are specified is len(value)/4. If the minimum multi-byte
// character count is greater than our enforced maximum, we
// can confidently say that the value is invalid without having
// to actually perform the more expensive rune counting step
minimum := int(math.Ceil(float64(byteLength) / 4.0))
if minimum > max || utf8.RuneCountInString(string(*value)) > max {
return field.ErrorList{field.TooLongCharacters(fldPath, *value, max).WithOrigin("maxLength")}
}
return nil
}
// MaxBytes verifies that the specified value is not longer than max bytes.
func MaxBytes[T ~string](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *T, max int) field.ErrorList {
if value == nil {
return nil
}
if len(*value) > max {
return field.ErrorList{field.TooLong(fldPath, *value, max).WithOrigin("maxBytes")}
}
return nil
}
@@ -45,6 +78,14 @@ func MaxItems[T any](_ context.Context, _ operation.Operation, fldPath *field.Pa
return nil
}
// MinItems verifies that the specified slice is not shorter than min items.
func MinItems[T any](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ []T, min int) field.ErrorList {
if len(value) < min {
return field.ErrorList{field.TooFew(fldPath, len(value), min).WithOrigin("minItems")}
}
return nil
}
// Minimum verifies that the specified value is greater than or equal to min.
func Minimum[T constraints.Integer](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *T, min T) field.ErrorList {
if value == nil {
@@ -55,3 +96,45 @@ func Minimum[T constraints.Integer](_ context.Context, _ operation.Operation, fl
}
return nil
}
// Maximum verifies that the specified value is less than or equal to max.
func Maximum[T constraints.Integer](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *T, max T) field.ErrorList {
if value == nil {
return nil
}
if *value > max {
return field.ErrorList{field.Invalid(fldPath, *value, content.MaxError(max)).WithOrigin("maximum")}
}
return nil
}
// MinLength verifies that the specified value is at least min characters, if non-nil.
func MinLength[T ~string](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *T, min int) field.ErrorList {
if value == nil {
return nil
}
byteLength := len(*value)
// because runes are up to 4 byte characters, if we assume all characters
// in the input are 4 byte runes, the minimum number of characters that
// are specified is len(value)/4. If the minimum multi-byte
// character count is greater than or equal to our enforced minimum, we
// can confidently say that the value is valid without having
// to actually perform the more expensive rune counting step
if int(math.Ceil(float64(byteLength)/4.0)) >= min {
return nil
}
// if the length of the value in bytes is less
// than the minimum size then we can confidently
// say that this value is not within the bounds
// enforced by the maximum value regardless
// of the actual makeup of characters in the value.
// Otherwise, perform a rune count to determine if the
// number of characters is less than the minimum.
if byteLength < min || utf8.RuneCountInString(string(*value)) < min {
return field.ErrorList{field.TooShort(fldPath, *value, min).WithOrigin("minLength")}
}
return nil
}
+17
View File
@@ -133,6 +133,23 @@ func LabelValue[T ~string](_ context.Context, op operation.Operation, fldPath *f
return allErrs
}
// PathSegmentName verifies that the specified value is a valid path segment name.
// A path segment name can be safely encoded as a path segment in URLs and file paths.
// - must not be exactly "." or ".."
// - must not contain "/" (forward slash)
// - must not contain "%" (percent sign)
// - can contain any other characters including mixed case, numbers, dots, hyphens, underscores, and non-ASCII characters
func PathSegmentName[T ~string](_ context.Context, op operation.Operation, fldPath *field.Path, value, _ *T) field.ErrorList {
if value == nil {
return nil
}
var allErrs field.ErrorList
for _, msg := range content.IsPathSegmentName((string)(*value)) {
allErrs = append(allErrs, field.Invalid(fldPath, *value, msg).WithOrigin("format=k8s-path-segment-name"))
}
return allErrs
}
// UUID verifies that the specified value is a valid UUID (RFC 4122).
// - must be 36 characters long
// - must be in the normalized form `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
+15 -4
View File
@@ -19,6 +19,7 @@ package validate
import (
"context"
"fmt"
"reflect"
"strings"
"k8s.io/apimachinery/pkg/api/operation"
@@ -60,6 +61,10 @@ type UnionValidationOptions struct {
// )...)
// return errs
// }
//
// Note that T is "any", rather than "comparable", because union-members can be
// slices, meaning T might be a struct with a slice, meaning it is not
// comparable.
func Union[T any](_ context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj T, union *UnionMembership, isSetFns ...ExtractorFn[T, bool]) field.ErrorList {
options := UnionValidationOptions{
ErrorForEmpty: func(fldPath *field.Path, allFields []string) *field.Error {
@@ -72,7 +77,7 @@ func Union[T any](_ context.Context, op operation.Operation, fldPath *field.Path
},
}
return unionValidate(op, fldPath, obj, oldObj, union, options, isSetFns...)
return unionValidate(op, fldPath, obj, oldObj, union, options, isSetFns...).WithOrigin("union")
}
// DiscriminatedUnion verifies specified union member matches the discriminator.
@@ -98,6 +103,10 @@ func Union[T any](_ context.Context, op operation.Operation, fldPath *field.Path
//
// It is not an error for the discriminatorValue to be unknown. That must be
// validated on its own.
//
// Note that T is "any", rather than "comparable", because union-members can be
// slices, meaning T might be a struct with a slice, meaning it is not
// comparable.
func DiscriminatedUnion[T any, D ~string](_ context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj T, union *UnionMembership, discriminatorExtractor ExtractorFn[T, D], isSetFns ...ExtractorFn[T, bool]) (errs field.ErrorList) {
if len(union.members) != len(isSetFns) {
return field.ErrorList{
@@ -106,6 +115,7 @@ func DiscriminatedUnion[T any, D ~string](_ context.Context, op operation.Operat
len(isSetFns), len(union.members))),
}
}
hasOldValue := !reflect.ValueOf(oldObj).IsZero() // because T is any, rather than comparable
var changed bool
discriminatorValue := discriminatorExtractor(obj)
if op.Type == operation.Update {
@@ -131,10 +141,10 @@ func DiscriminatedUnion[T any, D ~string](_ context.Context, op operation.Operat
}
// If the union discriminator and membership is unchanged, we don't need to
// re-validate.
if op.Type == operation.Update && !changed {
if op.Type == operation.Update && hasOldValue && !changed {
return nil
}
return errs
return errs.WithOrigin("union")
}
// UnionMember represents a member of a union.
@@ -195,6 +205,7 @@ func unionValidate[T any](op operation.Operation, fldPath *field.Path,
}
}
hasOldValue := !reflect.ValueOf(oldObj).IsZero() // because T is any, rather than comparable
var specifiedFields []string
var changed bool
for i, fieldIsSet := range isSetFns {
@@ -209,7 +220,7 @@ func unionValidate[T any](op operation.Operation, fldPath *field.Path,
}
// If the union membership is unchanged, we don't need to re-validate.
if op.Type == operation.Update && !changed {
if op.Type == operation.Update && hasOldValue && !changed {
return nil
}