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
+3
View File
@@ -40,6 +40,9 @@ var Semantic = conversion.EqualitiesOrDie(
func(a, b metav1.Time) bool {
return a.UTC() == b.UTC()
},
func(a, b metav1.FieldsV1) bool {
return a.Equal(b)
},
func(a, b labels.Selector) bool {
return a.String() == b.String()
},
@@ -1,26 +0,0 @@
//go:build kubernetes_protomessage_one_more_release
// +build kubernetes_protomessage_one_more_release
/*
Copyright 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.
*/
// Code generated by go-to-protobuf. DO NOT EDIT.
package resource
func (*Quantity) ProtoMessage() {}
func (*QuantityValue) ProtoMessage() {}
+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
}
-38
View File
@@ -1,38 +0,0 @@
/*
Copyright 2023 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 internalversion
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// SetListOptionsDefaults sets defaults on the provided ListOptions if applicable.
//
// TODO(#115478): once the watch-list fg is always on we register this function in the scheme (via AddTypeDefaultingFunc).
// TODO(#115478): when the function is registered in the scheme remove all callers of this method.
func SetListOptionsDefaults(obj *ListOptions, isWatchListFeatureEnabled bool) {
if !isWatchListFeatureEnabled {
return
}
if obj.SendInitialEvents != nil || len(obj.ResourceVersionMatch) != 0 {
return
}
legacy := obj.ResourceVersion == "" || obj.ResourceVersion == "0"
if obj.Watch && legacy {
turnOnInitialEvents := true
obj.SendInitialEvents = &turnOnInitialEvents
obj.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan
}
}
-20
View File
@@ -1,20 +0,0 @@
/*
Copyright 2016 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.
*/
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=k8s.io/apimachinery/pkg/apis/meta/v1
package internalversion
-88
View File
@@ -1,88 +0,0 @@
/*
Copyright 2017 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 internalversion
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name for this API.
const GroupName = "meta.k8s.io"
var (
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// addToGroupVersion registers common meta types into schemas.
func addToGroupVersion(scheme *runtime.Scheme) error {
if err := scheme.AddIgnoredConversionType(&metav1.TypeMeta{}, &metav1.TypeMeta{}); err != nil {
return err
}
// ListOptions is the only options struct which needs conversion (it exposes labels and fields
// as selectors for convenience). The other types have only a single representation today.
scheme.AddKnownTypes(SchemeGroupVersion,
&ListOptions{},
&metav1.GetOptions{},
&metav1.DeleteOptions{},
&metav1.CreateOptions{},
&metav1.UpdateOptions{},
)
scheme.AddKnownTypes(SchemeGroupVersion,
&metav1.Table{},
&metav1.TableOptions{},
&metav1beta1.PartialObjectMetadata{},
&metav1beta1.PartialObjectMetadataList{},
)
if err := metav1beta1.AddMetaToScheme(scheme); err != nil {
return err
}
if err := metav1.AddMetaToScheme(scheme); err != nil {
return err
}
// Allow delete options to be decoded across all version in this scheme (we may want to be more clever than this)
scheme.AddUnversionedTypes(SchemeGroupVersion,
&metav1.DeleteOptions{},
&metav1.CreateOptions{},
&metav1.UpdateOptions{})
metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion)
if err := metav1beta1.RegisterConversions(scheme); err != nil {
return err
}
return nil
}
// Unlike other API groups, meta internal knows about all meta external versions, but keeps
// the logic for conversion private.
func init() {
localSchemeBuilder.Register(addToGroupVersion)
}
-17
View File
@@ -1,17 +0,0 @@
/*
Copyright 2016 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 scheme
@@ -1,39 +0,0 @@
/*
Copyright 2017 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 scheme
import (
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
// Scheme is the registry for any type that adheres to the meta API spec.
var Scheme = runtime.NewScheme()
// Codecs provides access to encoding and decoding for the scheme.
var Codecs = serializer.NewCodecFactory(Scheme)
// ParameterCodec handles versioning of objects that are converted to query parameters.
var ParameterCodec = runtime.NewParameterCodec(Scheme)
// Unlike other API groups, meta internal knows about all meta external versions, but keeps
// the logic for conversion private.
func init() {
utilruntime.Must(internalversion.AddToScheme(Scheme))
}
-103
View File
@@ -1,103 +0,0 @@
/*
Copyright 2017 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 internalversion
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ListOptions is the query options to a standard REST list call.
type ListOptions struct {
metav1.TypeMeta
// A selector based on labels
LabelSelector labels.Selector
// A selector based on fields
FieldSelector fields.Selector
// If true, watch for changes to this list
Watch bool
// allowWatchBookmarks requests watch events with type "BOOKMARK".
// Servers that do not implement bookmarks may ignore this flag and
// bookmarks are sent at the server's discretion. Clients should not
// assume bookmarks are returned at any specific interval, nor may they
// assume the server will send any BOOKMARK event during a session.
// If this is not a watch, this field is ignored.
AllowWatchBookmarks bool
// resourceVersion sets a constraint on what resource versions a request may be served from.
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for
// details.
ResourceVersion string
// resourceVersionMatch determines how resourceVersion is applied to list calls.
// It is highly recommended that resourceVersionMatch be set for list calls where
// resourceVersion is set.
// See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for
// details.
ResourceVersionMatch metav1.ResourceVersionMatch
// Timeout for the list/watch call.
TimeoutSeconds *int64
// Limit specifies the maximum number of results to return from the server. The server may
// not support this field on all resource types, but if it does and more results remain it
// will set the continue field on the returned list object.
Limit int64
// Continue is a token returned by the server that lets a client retrieve chunks of results
// from the server by specifying limit. The server may reject requests for continuation tokens
// it does not recognize and will return a 410 error if the token can no longer be used because
// it has expired.
Continue string
// `sendInitialEvents=true` may be set together with `watch=true`.
// In that case, the watch stream will begin with synthetic events to
// produce the current state of objects in the collection. Once all such
// events have been sent, a synthetic "Bookmark" event will be sent.
// The bookmark will report the ResourceVersion (RV) corresponding to the
// set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation.
// Afterwards, the watch stream will proceed as usual, sending watch events
// corresponding to changes (subsequent to the RV) to objects watched.
//
// When `sendInitialEvents` option is set, we require `resourceVersionMatch`
// option to also be set. The semantic of the watch request is as following:
// - `resourceVersionMatch` = NotOlderThan
// is interpreted as "data at least as new as the provided `resourceVersion`"
// and the bookmark event is send when the state is synced
// to a `resourceVersion` at least as fresh as the one provided by the ListOptions.
// If `resourceVersion` is unset, this is interpreted as "consistent read" and the
// bookmark event is send when the state is synced at least to the moment
// when request started being processed.
// - `resourceVersionMatch` set to any other value or unset
// Invalid error is returned.
//
// Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward
// compatibility reasons) and to false otherwise.
SendInitialEvents *bool
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// List holds a list of objects, which may not be known by the server.
type List struct {
metav1.TypeMeta
// +optional
metav1.ListMeta
Items []runtime.Object
}
@@ -1,76 +0,0 @@
/*
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"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
)
// ValidateListOptions returns all validation errors found while validating the ListOptions.
func ValidateListOptions(options *internalversion.ListOptions, isWatchListFeatureEnabled bool) field.ErrorList {
if options.Watch {
return validateWatchOptions(options, isWatchListFeatureEnabled)
}
allErrs := field.ErrorList{}
if match := options.ResourceVersionMatch; len(match) > 0 {
if len(options.ResourceVersion) == 0 {
allErrs = append(allErrs, field.Forbidden(field.NewPath("resourceVersionMatch"), "resourceVersionMatch is forbidden unless resourceVersion is provided"))
}
if len(options.Continue) > 0 {
allErrs = append(allErrs, field.Forbidden(field.NewPath("resourceVersionMatch"), "resourceVersionMatch is forbidden when continue is provided"))
}
if match != metav1.ResourceVersionMatchExact && match != metav1.ResourceVersionMatchNotOlderThan {
allErrs = append(allErrs, field.NotSupported(field.NewPath("resourceVersionMatch"), match, []string{string(metav1.ResourceVersionMatchExact), string(metav1.ResourceVersionMatchNotOlderThan), ""}))
}
if match == metav1.ResourceVersionMatchExact && options.ResourceVersion == "0" {
allErrs = append(allErrs, field.Forbidden(field.NewPath("resourceVersionMatch"), "resourceVersionMatch \"exact\" is forbidden for resourceVersion \"0\""))
}
}
if options.SendInitialEvents != nil {
allErrs = append(allErrs, field.Forbidden(field.NewPath("sendInitialEvents"), "sendInitialEvents is forbidden for list"))
}
return allErrs
}
func validateWatchOptions(options *internalversion.ListOptions, isWatchListFeatureEnabled bool) field.ErrorList {
allErrs := field.ErrorList{}
match := options.ResourceVersionMatch
if options.SendInitialEvents != nil {
if match != metav1.ResourceVersionMatchNotOlderThan {
allErrs = append(allErrs, field.Forbidden(field.NewPath("resourceVersionMatch"), fmt.Sprintf("sendInitialEvents requires setting resourceVersionMatch to %s", metav1.ResourceVersionMatchNotOlderThan)))
}
if !isWatchListFeatureEnabled {
allErrs = append(allErrs, field.Forbidden(field.NewPath("sendInitialEvents"), "sendInitialEvents is forbidden for watch unless the WatchList feature gate is enabled"))
}
}
if len(match) > 0 {
if options.SendInitialEvents == nil {
allErrs = append(allErrs, field.Forbidden(field.NewPath("resourceVersionMatch"), "resourceVersionMatch is forbidden for watch unless sendInitialEvents is provided"))
}
if match != metav1.ResourceVersionMatchNotOlderThan {
allErrs = append(allErrs, field.NotSupported(field.NewPath("resourceVersionMatch"), match, []string{string(metav1.ResourceVersionMatchNotOlderThan)}))
}
if len(options.Continue) > 0 {
allErrs = append(allErrs, field.Forbidden(field.NewPath("resourceVersionMatch"), "resourceVersionMatch is forbidden when continue is provided"))
}
}
return allErrs
}
@@ -1,148 +0,0 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by conversion-gen. DO NOT EDIT.
package internalversion
import (
unsafe "unsafe"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*List)(nil), (*v1.List)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_internalversion_List_To_v1_List(a.(*List), b.(*v1.List), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.List)(nil), (*List)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_List_To_internalversion_List(a.(*v1.List), b.(*List), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*ListOptions)(nil), (*v1.ListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_internalversion_ListOptions_To_v1_ListOptions(a.(*ListOptions), b.(*v1.ListOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.ListOptions)(nil), (*ListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_ListOptions_To_internalversion_ListOptions(a.(*v1.ListOptions), b.(*ListOptions), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_internalversion_List_To_v1_List(in *List, out *v1.List, s conversion.Scope) error {
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]runtime.RawExtension, len(*in))
for i := range *in {
if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
// Convert_internalversion_List_To_v1_List is an autogenerated conversion function.
func Convert_internalversion_List_To_v1_List(in *List, out *v1.List, s conversion.Scope) error {
return autoConvert_internalversion_List_To_v1_List(in, out, s)
}
func autoConvert_v1_List_To_internalversion_List(in *v1.List, out *List, s conversion.Scope) error {
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]runtime.Object, len(*in))
for i := range *in {
if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
// Convert_v1_List_To_internalversion_List is an autogenerated conversion function.
func Convert_v1_List_To_internalversion_List(in *v1.List, out *List, s conversion.Scope) error {
return autoConvert_v1_List_To_internalversion_List(in, out, s)
}
func autoConvert_internalversion_ListOptions_To_v1_ListOptions(in *ListOptions, out *v1.ListOptions, s conversion.Scope) error {
if err := v1.Convert_labels_Selector_To_string(&in.LabelSelector, &out.LabelSelector, s); err != nil {
return err
}
if err := v1.Convert_fields_Selector_To_string(&in.FieldSelector, &out.FieldSelector, s); err != nil {
return err
}
out.Watch = in.Watch
out.AllowWatchBookmarks = in.AllowWatchBookmarks
out.ResourceVersion = in.ResourceVersion
out.ResourceVersionMatch = v1.ResourceVersionMatch(in.ResourceVersionMatch)
out.TimeoutSeconds = (*int64)(unsafe.Pointer(in.TimeoutSeconds))
out.Limit = in.Limit
out.Continue = in.Continue
out.SendInitialEvents = (*bool)(unsafe.Pointer(in.SendInitialEvents))
return nil
}
// Convert_internalversion_ListOptions_To_v1_ListOptions is an autogenerated conversion function.
func Convert_internalversion_ListOptions_To_v1_ListOptions(in *ListOptions, out *v1.ListOptions, s conversion.Scope) error {
return autoConvert_internalversion_ListOptions_To_v1_ListOptions(in, out, s)
}
func autoConvert_v1_ListOptions_To_internalversion_ListOptions(in *v1.ListOptions, out *ListOptions, s conversion.Scope) error {
if err := v1.Convert_string_To_labels_Selector(&in.LabelSelector, &out.LabelSelector, s); err != nil {
return err
}
if err := v1.Convert_string_To_fields_Selector(&in.FieldSelector, &out.FieldSelector, s); err != nil {
return err
}
out.Watch = in.Watch
out.AllowWatchBookmarks = in.AllowWatchBookmarks
out.ResourceVersion = in.ResourceVersion
out.ResourceVersionMatch = v1.ResourceVersionMatch(in.ResourceVersionMatch)
out.TimeoutSeconds = (*int64)(unsafe.Pointer(in.TimeoutSeconds))
out.Limit = in.Limit
out.Continue = in.Continue
out.SendInitialEvents = (*bool)(unsafe.Pointer(in.SendInitialEvents))
return nil
}
// Convert_v1_ListOptions_To_internalversion_ListOptions is an autogenerated conversion function.
func Convert_v1_ListOptions_To_internalversion_ListOptions(in *v1.ListOptions, out *ListOptions, s conversion.Scope) error {
return autoConvert_v1_ListOptions_To_internalversion_ListOptions(in, out, s)
}
@@ -1,102 +0,0 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package internalversion
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *List) DeepCopyInto(out *List) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]runtime.Object, len(*in))
for i := range *in {
if (*in)[i] != nil {
(*out)[i] = (*in)[i].DeepCopyObject()
}
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new List.
func (in *List) DeepCopy() *List {
if in == nil {
return nil
}
out := new(List)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *List) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ListOptions) DeepCopyInto(out *ListOptions) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.LabelSelector != nil {
out.LabelSelector = in.LabelSelector.DeepCopySelector()
}
if in.FieldSelector != nil {
out.FieldSelector = in.FieldSelector.DeepCopySelector()
}
if in.TimeoutSeconds != nil {
in, out := &in.TimeoutSeconds, &out.TimeoutSeconds
*out = new(int64)
**out = **in
}
if in.SendInitialEvents != nil {
in, out := &in.SendInitialEvents, &out.SendInitialEvents
*out = new(bool)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ListOptions.
func (in *ListOptions) DeepCopy() *ListOptions {
if in == nil {
return nil
}
out := new(ListOptions)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ListOptions) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
+170
View File
@@ -0,0 +1,170 @@
/*
Copyright 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 v1
import (
"fmt"
"io"
)
func (FieldsV1) SwaggerDoc() map[string]string {
return map[string]string{
"": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:<name>', where <name> is the name of a field in a struct, or key in a map 'v:<value>', where <value> is the exact json formatted value of a list item 'i:<index>', where <index> is position of a item in a list 'k:<keys>', where <keys> is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
}
}
type FieldsV1Reader interface {
io.Reader
io.ReaderAt
// Size returns the original byte length of the underlying data. Size is the number of bytes available for reading via ReadAt.
Size() int64
}
func (f *FieldsV1) DeepCopy() *FieldsV1 {
if f == nil {
return nil
}
out := new(FieldsV1)
f.DeepCopyInto(out)
return out
}
func (f *FieldsV1) Marshal() (dAtA []byte, err error) {
size := f.Size()
dAtA = make([]byte, size)
n, err := f.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (f *FieldsV1) MarshalTo(dAtA []byte) (int, error) {
size := f.Size()
return f.MarshalToSizedBuffer(dAtA[:size])
}
func (f *FieldsV1) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
rawBytes := f.GetRawBytes()
if len(rawBytes) > 0 {
i -= len(rawBytes)
copy(dAtA[i:], rawBytes)
i = encodeVarintGenerated(dAtA, i, uint64(len(rawBytes)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (f *FieldsV1) Size() (n int) {
if f == nil {
return 0
}
var l int
_ = l
if l := int(f.GetRawReader().Size()); l > 0 {
n += 1 + l + sovGenerated(uint64(l))
}
return n
}
func (f *FieldsV1) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: FieldsV1: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: FieldsV1: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Raw", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
f.SetRawBytes(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
+105
View File
@@ -0,0 +1,105 @@
//go:build !fieldsv1string
/*
Copyright 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 v1
import (
"bytes"
)
// FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.
//
// Each key is either a '.' representing the field itself, and will always map to an empty set,
// or a string representing a sub-field or item. The string will follow one of these four formats:
// 'f:<name>', where <name> is the name of a field in a struct, or key in a map
// 'v:<value>', where <value> is the exact json formatted value of a list item
// 'i:<index>', where <index> is position of a item in a list
// 'k:<keys>', where <keys> is a map of a list item's key fields to their unique values
// If a key maps to an empty Fields value, the field that key represents is part of the set.
//
// The exact format is defined in sigs.k8s.io/structured-merge-diff
// +k8s:deepcopy-gen=false
// +protobuf.options.marshal=false
// +protobuf.options.(gogoproto.goproto_stringer)=false
type FieldsV1 struct {
// Raw is the underlying serialization of this object.
//
// Deprecated: Direct access to this field is deprecated. Use GetRawBytes, GetRawString, SetRawBytes, SetRawString, GetRawReader, NewFieldsV1 instead.
Raw []byte `json:"-" protobuf:"bytes,1,opt,name=Raw"`
}
func (f FieldsV1) String() string {
return string(f.Raw)
}
func (f FieldsV1) Equal(f2 FieldsV1) bool {
return bytes.Equal(f.Raw, f2.Raw)
}
func (f *FieldsV1) GetRawReader() FieldsV1Reader {
if f == nil || len(f.Raw) == 0 {
return bytes.NewReader(nil)
}
return bytes.NewReader(f.Raw)
}
// GetRawBytes returns the raw bytes.
// These may or may not be a copy of the underlying bytes.
// If mutating the underlying bytes is desired, the returned bytes may be mutated and then passed to SetRawBytes().
// If mutating the underlying bytes is not desired, make a copy of the returned bytes.
func (f *FieldsV1) GetRawBytes() []byte {
if f == nil {
return nil
}
return f.Raw
}
// GetRawString returns the raw data as a string.
func (f *FieldsV1) GetRawString() string {
if f == nil {
return ""
}
return string(f.Raw)
}
// SetRawBytes sets the raw bytes. It does not retain the passed-in byte slice.
func (f *FieldsV1) SetRawBytes(b []byte) {
if f != nil {
f.Raw = bytes.Clone(b)
}
}
// SetRawString sets the raw data from a string.
func (f *FieldsV1) SetRawString(s string) {
if f != nil {
f.Raw = []byte(s)
}
}
func NewFieldsV1(raw string) *FieldsV1 {
return &FieldsV1{Raw: []byte(raw)}
}
func (f *FieldsV1) DeepCopyInto(out *FieldsV1) {
*out = *f
if f.Raw != nil {
in, out := &f.Raw, &out.Raw
*out = make([]byte, len(*in))
copy(*out, *in)
}
}
+113
View File
@@ -0,0 +1,113 @@
//go:build fieldsv1string
/*
Copyright 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 v1
import (
"strings"
"unique"
)
// FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.
//
// Each key is either a '.' representing the field itself, and will always map to an empty set,
// or a string representing a sub-field or item. The string will follow one of these four formats:
// 'f:<name>', where <name> is the name of a field in a struct, or key in a map
// 'v:<value>', where <value> is the exact json formatted value of a list item
// 'i:<index>', where <index> is position of a item in a list
// 'k:<keys>', where <keys> is a map of a list item's key fields to their unique values
// If a key maps to an empty Fields value, the field that key represents is part of the set.
//
// The exact format is defined in sigs.k8s.io/structured-merge-diff
// +k8s:deepcopy-gen=false
// +protobuf.options.marshal=false
// +protobuf.options.(gogoproto.goproto_stringer)=false
type FieldsV1 struct {
// The zero value of a unique.Handle[string] has an uninitialized underlying pointer.
// Calling .Value() on it panics. We must explicitly check for this uninitialized
// state (f.handle == unique.Handle[string]{}) across accessors to safely support
// uninitialized metav1.FieldsV1{} objects.
// See ongoing golang discussion related to this here: https://github.com/golang/go/issues/73344
handle unique.Handle[string]
}
func (f FieldsV1) String() string {
if f.handle == (unique.Handle[string]{}) {
return ""
}
return f.handle.Value()
}
func (f FieldsV1) Equal(f2 FieldsV1) bool {
if f.handle == f2.handle {
return true
}
// An uninitialized FieldsV1 compared to an explicitly empty
// FieldsV1 (unique.Make("") will fail the handle check above.
// Evaluate string contents directly as well to maintain parity with legacy
// bytes.Equal(nil, []byte{}) == true behavior.
return f.GetRawString() == f2.GetRawString()
}
func (f *FieldsV1) GetRawReader() FieldsV1Reader {
if f == nil || f.handle == (unique.Handle[string]{}) {
return strings.NewReader("")
}
return strings.NewReader(f.handle.Value())
}
// GetRawBytes returns the raw bytes.
// These may or may not be a copy of the underlying bytes.
// If mutating the underlying bytes is desired, the returned bytes may be mutated and then passed to SetRawBytes().
// If mutating the underlying bytes is not desired, make a copy of the returned bytes.
func (f *FieldsV1) GetRawBytes() []byte {
if f == nil || f.handle == (unique.Handle[string]{}) {
return nil
}
return []byte(f.handle.Value())
}
// GetRawString returns the raw data as a string.
func (f *FieldsV1) GetRawString() string {
if f == nil || f.handle == (unique.Handle[string]{}) {
return ""
}
return f.handle.Value()
}
// SetRawBytes sets the raw bytes. It does not retain the passed-in byte slice.
func (f *FieldsV1) SetRawBytes(b []byte) {
if f != nil {
f.handle = unique.Make(string(b))
}
}
// SetRawString sets the raw data from a string.
func (f *FieldsV1) SetRawString(s string) {
if f != nil {
f.handle = unique.Make(s)
}
}
func NewFieldsV1(raw string) *FieldsV1 {
return &FieldsV1{handle: unique.Make(raw)}
}
func (f *FieldsV1) DeepCopyInto(out *FieldsV1) {
*out = *f
}
+226 -127
View File
@@ -105,6 +105,8 @@ func (m *RootPaths) Reset() { *m = RootPaths{} }
func (m *ServerAddressByClientCIDR) Reset() { *m = ServerAddressByClientCIDR{} }
func (m *ShardInfo) Reset() { *m = ShardInfo{} }
func (m *Status) Reset() { *m = Status{} }
func (m *StatusCause) Reset() { *m = StatusCause{} }
@@ -694,36 +696,6 @@ func (m *FieldSelectorRequirement) MarshalToSizedBuffer(dAtA []byte) (int, error
return len(dAtA) - i, nil
}
func (m *FieldsV1) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *FieldsV1) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *FieldsV1) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.Raw != nil {
i -= len(m.Raw)
copy(dAtA[i:], m.Raw)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Raw)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *GetOptions) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -1130,6 +1102,18 @@ func (m *ListMeta) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.ShardInfo != nil {
{
size, err := m.ShardInfo.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x2a
}
if m.RemainingItemCount != nil {
i = encodeVarintGenerated(dAtA, i, uint64(*m.RemainingItemCount))
i--
@@ -1173,6 +1157,11 @@ func (m *ListOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
i -= len(m.ShardSelector)
copy(dAtA[i:], m.ShardSelector)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.ShardSelector)))
i--
dAtA[i] = 0x7a
if m.SendInitialEvents != nil {
i--
if *m.SendInitialEvents {
@@ -1797,6 +1786,34 @@ func (m *ServerAddressByClientCIDR) MarshalToSizedBuffer(dAtA []byte) (int, erro
return len(dAtA) - i, nil
}
func (m *ShardInfo) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ShardInfo) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ShardInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
i -= len(m.Selector)
copy(dAtA[i:], m.Selector)
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Selector)))
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *Status) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -2405,19 +2422,6 @@ func (m *FieldSelectorRequirement) Size() (n int) {
return n
}
func (m *FieldsV1) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Raw != nil {
l = len(m.Raw)
n += 1 + l + sovGenerated(uint64(l))
}
return n
}
func (m *GetOptions) Size() (n int) {
if m == nil {
return 0
@@ -2585,6 +2589,10 @@ func (m *ListMeta) Size() (n int) {
if m.RemainingItemCount != nil {
n += 1 + sovGenerated(uint64(*m.RemainingItemCount))
}
if m.ShardInfo != nil {
l = m.ShardInfo.Size()
n += 1 + l + sovGenerated(uint64(l))
}
return n
}
@@ -2613,6 +2621,8 @@ func (m *ListOptions) Size() (n int) {
if m.SendInitialEvents != nil {
n += 2
}
l = len(m.ShardSelector)
n += 1 + l + sovGenerated(uint64(l))
return n
}
@@ -2835,6 +2845,17 @@ func (m *ServerAddressByClientCIDR) Size() (n int) {
return n
}
func (m *ShardInfo) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Selector)
n += 1 + l + sovGenerated(uint64(l))
return n
}
func (m *Status) Size() (n int) {
if m == nil {
return 0
@@ -3217,6 +3238,7 @@ func (this *ListMeta) String() string {
`ResourceVersion:` + fmt.Sprintf("%v", this.ResourceVersion) + `,`,
`Continue:` + fmt.Sprintf("%v", this.Continue) + `,`,
`RemainingItemCount:` + valueToStringGenerated(this.RemainingItemCount) + `,`,
`ShardInfo:` + strings.Replace(this.ShardInfo.String(), "ShardInfo", "ShardInfo", 1) + `,`,
`}`,
}, "")
return s
@@ -3236,6 +3258,7 @@ func (this *ListOptions) String() string {
`AllowWatchBookmarks:` + fmt.Sprintf("%v", this.AllowWatchBookmarks) + `,`,
`ResourceVersionMatch:` + fmt.Sprintf("%v", this.ResourceVersionMatch) + `,`,
`SendInitialEvents:` + valueToStringGenerated(this.SendInitialEvents) + `,`,
`ShardSelector:` + fmt.Sprintf("%v", this.ShardSelector) + `,`,
`}`,
}, "")
return s
@@ -3405,6 +3428,16 @@ func (this *ServerAddressByClientCIDR) String() string {
}, "")
return s
}
func (this *ShardInfo) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&ShardInfo{`,
`Selector:` + fmt.Sprintf("%v", this.Selector) + `,`,
`}`,
}, "")
return s
}
func (this *Status) String() string {
if this == nil {
return "nil"
@@ -5314,90 +5347,6 @@ func (m *FieldSelectorRequirement) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *FieldsV1) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: FieldsV1: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: FieldsV1: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Raw", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Raw = append(m.Raw[:0], dAtA[iNdEx:postIndex]...)
if m.Raw == nil {
m.Raw = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *GetOptions) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@@ -6847,6 +6796,42 @@ func (m *ListMeta) Unmarshal(dAtA []byte) error {
}
}
m.RemainingItemCount = &v
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ShardInfo", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.ShardInfo == nil {
m.ShardInfo = &ShardInfo{}
}
if err := m.ShardInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -7157,6 +7142,38 @@ func (m *ListOptions) Unmarshal(dAtA []byte) error {
}
b := bool(v != 0)
m.SendInitialEvents = &b
case 15:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ShardSelector", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ShardSelector = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -9113,6 +9130,88 @@ func (m *ServerAddressByClientCIDR) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *ShardInfo) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ShardInfo: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ShardInfo: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Selector", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Selector = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Status) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
+56
View File
@@ -369,9 +369,13 @@ message FieldSelectorRequirement {
// If a key maps to an empty Fields value, the field that key represents is part of the set.
//
// The exact format is defined in sigs.k8s.io/structured-merge-diff
// +k8s:deepcopy-gen=false
// +protobuf.options.marshal=false
// +protobuf.options.(gogoproto.goproto_stringer)=false
message FieldsV1 {
// Raw is the underlying serialization of this object.
//
// Deprecated: Direct access to this field is deprecated. Use GetRawBytes, GetRawString, SetRawBytes, SetRawString, GetRawReader, NewFieldsV1 instead.
optional bytes Raw = 1;
}
@@ -533,6 +537,17 @@ message ListMeta {
// should not rely on the remainingItemCount to be set or to be exact.
// +optional
optional int64 remainingItemCount = 4;
// shardInfo is set when the list is a filtered subset of the full collection,
// as selected by a shard selector on the request. It echoes back the selector
// so clients can verify which shard they received and merge sharded responses.
// Clients should not cache sharded list responses as a full representation
// of the collection.
//
// This is an alpha field and requires enabling the ShardedListAndWatch feature gate.
// +featureGate=ShardedListAndWatch
// +optional
optional ShardInfo shardInfo = 5;
}
// ListOptions is the query options to a standard REST list call.
@@ -643,6 +658,38 @@ message ListOptions {
// compatibility reasons) and to false otherwise.
// +optional
optional bool sendInitialEvents = 11;
// shardSelector restricts the list of returned objects using a CEL-based
// shard selector expression. The format uses the shardRange() function
// combined with || (logical OR) to specify one or more hash ranges:
//
// shardRange(object.metadata.uid, '0x0', '0x8000000000000000')
// shardRange(object.metadata.uid, '0x0', '0x8000000000000000') || shardRange(object.metadata.uid, '0x8000000000000000', '0x10000000000000000')
//
// Field paths use CEL-style object-rooted syntax (e.g. "object.metadata.uid"),
// NOT the fieldSelector format ("metadata.uid"). Currently supported paths:
// - object.metadata.uid
// - object.metadata.namespace
//
// hexStart and hexEnd are single-quoted CEL string literals with a '0x' prefix,
// defining the inclusive lower and exclusive upper bounds over the 64-bit FNV-1a
// hash space. The full range is [0x0, 0x10000000000000000), where the exclusive
// upper bound equals 2^64.
//
// Examples:
// 2-shard split:
// shard 0: shardRange(object.metadata.uid, '0x0000000000000000', '0x8000000000000000')
// shard 1: shardRange(object.metadata.uid, '0x8000000000000000', '0x10000000000000000')
// 4-shard split:
// shard 0: shardRange(object.metadata.uid, '0x0000000000000000', '0x4000000000000000')
// shard 1: shardRange(object.metadata.uid, '0x4000000000000000', '0x8000000000000000')
// shard 2: shardRange(object.metadata.uid, '0x8000000000000000', '0xc000000000000000')
// shard 3: shardRange(object.metadata.uid, '0xc000000000000000', '0x10000000000000000')
//
// This is an alpha field and requires enabling the ShardedListAndWatch feature gate.
// +featureGate=ShardedListAndWatch
// +optional
optional string shardSelector = 15;
}
// ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource
@@ -1009,6 +1056,15 @@ message ServerAddressByClientCIDR {
optional string serverAddress = 2;
}
// ShardInfo describes the shard selector that was applied to produce a list response.
// Its presence on a list response indicates the list is a filtered subset.
message ShardInfo {
// selector is the shard selector string from the request, echoed back so clients
// can verify which shard they received and merge responses from multiple shards.
// +required
optional string selector = 1;
}
// Status is a return value for calls that don't return other objects.
message Status {
// Standard list metadata.
@@ -1,112 +0,0 @@
//go:build kubernetes_protomessage_one_more_release
// +build kubernetes_protomessage_one_more_release
/*
Copyright 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.
*/
// Code generated by go-to-protobuf. DO NOT EDIT.
package v1
func (*APIGroup) ProtoMessage() {}
func (*APIGroupList) ProtoMessage() {}
func (*APIResource) ProtoMessage() {}
func (*APIResourceList) ProtoMessage() {}
func (*APIVersions) ProtoMessage() {}
func (*ApplyOptions) ProtoMessage() {}
func (*Condition) ProtoMessage() {}
func (*CreateOptions) ProtoMessage() {}
func (*DeleteOptions) ProtoMessage() {}
func (*Duration) ProtoMessage() {}
func (*FieldSelectorRequirement) ProtoMessage() {}
func (*FieldsV1) ProtoMessage() {}
func (*GetOptions) ProtoMessage() {}
func (*GroupKind) ProtoMessage() {}
func (*GroupResource) ProtoMessage() {}
func (*GroupVersion) ProtoMessage() {}
func (*GroupVersionForDiscovery) ProtoMessage() {}
func (*GroupVersionKind) ProtoMessage() {}
func (*GroupVersionResource) ProtoMessage() {}
func (*LabelSelector) ProtoMessage() {}
func (*LabelSelectorRequirement) ProtoMessage() {}
func (*List) ProtoMessage() {}
func (*ListMeta) ProtoMessage() {}
func (*ListOptions) ProtoMessage() {}
func (*ManagedFieldsEntry) ProtoMessage() {}
func (*MicroTime) ProtoMessage() {}
func (*ObjectMeta) ProtoMessage() {}
func (*OwnerReference) ProtoMessage() {}
func (*PartialObjectMetadata) ProtoMessage() {}
func (*PartialObjectMetadataList) ProtoMessage() {}
func (*Patch) ProtoMessage() {}
func (*PatchOptions) ProtoMessage() {}
func (*Preconditions) ProtoMessage() {}
func (*RootPaths) ProtoMessage() {}
func (*ServerAddressByClientCIDR) ProtoMessage() {}
func (*Status) ProtoMessage() {}
func (*StatusCause) ProtoMessage() {}
func (*StatusDetails) ProtoMessage() {}
func (*TableOptions) ProtoMessage() {}
func (*Time) ProtoMessage() {}
func (*Timestamp) ProtoMessage() {}
func (*TypeMeta) ProtoMessage() {}
func (*UpdateOptions) ProtoMessage() {}
func (*Verbs) ProtoMessage() {}
func (*WatchEvent) ProtoMessage() {}
+17 -10
View File
@@ -279,17 +279,18 @@ func ResetObjectMetaForStatus(meta, existingMeta Object) {
// MarshalJSON may get called on pointers or values, so implement MarshalJSON on value.
// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go
func (f FieldsV1) MarshalJSON() ([]byte, error) {
if f.Raw == nil {
raw := f.GetRawBytes()
if len(raw) == 0 {
return []byte("null"), nil
}
if f.getContentType() == fieldsV1InvalidOrValidCBORObject {
var u map[string]interface{}
if err := cbor.Unmarshal(f.Raw, &u); err != nil {
if err := cbor.Unmarshal(raw, &u); err != nil {
return nil, fmt.Errorf("metav1.FieldsV1 cbor invalid: %w", err)
}
return utiljson.Marshal(u)
}
return f.Raw, nil
return raw, nil
}
// UnmarshalJSON implements json.Unmarshaler
@@ -298,7 +299,7 @@ func (f *FieldsV1) UnmarshalJSON(b []byte) error {
return errors.New("metav1.FieldsV1: UnmarshalJSON on nil pointer")
}
if !bytes.Equal(b, []byte("null")) {
f.Raw = append(f.Raw[0:0], b...)
f.SetRawBytes(b)
}
return nil
}
@@ -307,17 +308,18 @@ var _ json.Marshaler = FieldsV1{}
var _ json.Unmarshaler = &FieldsV1{}
func (f FieldsV1) MarshalCBOR() ([]byte, error) {
if f.Raw == nil {
raw := f.GetRawBytes()
if len(raw) == 0 {
return cbor.Marshal(nil)
}
if f.getContentType() == fieldsV1InvalidOrValidJSONObject {
var u map[string]interface{}
if err := utiljson.Unmarshal(f.Raw, &u); err != nil {
if err := utiljson.Unmarshal(raw, &u); err != nil {
return nil, fmt.Errorf("metav1.FieldsV1 json invalid: %w", err)
}
return cbor.Marshal(u)
}
return f.Raw, nil
return raw, nil
}
var cborNull = []byte{0xf6}
@@ -327,7 +329,7 @@ func (f *FieldsV1) UnmarshalCBOR(b []byte) error {
return errors.New("metav1.FieldsV1: UnmarshalCBOR on nil pointer")
}
if !bytes.Equal(b, cborNull) {
f.Raw = append(f.Raw[0:0], b...)
f.SetRawBytes(b)
}
return nil
}
@@ -362,8 +364,13 @@ const (
// or, if a tag-enclosed map, an initial byte with major type "tag" (0xf6, 0xa0...0xbf, or
// 0xc6...0xdb). The two sets of valid initial bytes don't intersect.
func (f FieldsV1) getContentType() int {
if len(f.Raw) > 0 {
p := f.Raw[0]
reader := f.GetRawReader()
if reader.Size() > 0 {
var buf [1]byte
if _, err := reader.Read(buf[:]); err != nil {
return fieldsV1InvalidOrEmpty
}
p := buf[0]
switch p {
case 'n', '{', '\t', '\r', '\n', ' ':
return fieldsV1InvalidOrValidJSONObject
+9
View File
@@ -94,6 +94,13 @@ type ListInterface interface {
SetRemainingItemCount(c *int64)
}
// ShardedListInterface can be implemented by list types to indicate that they
// represent a sharded subset of the full collection rather than the complete list.
type ShardedListInterface interface {
GetShardInfo() *ShardInfo
SetShardInfo(*ShardInfo)
}
// Type exposes the type and APIVersion of versioned or internal API objects.
// TODO: move this, and TypeMeta and ListMeta, to a different package
type Type interface {
@@ -113,6 +120,8 @@ func (meta *ListMeta) GetContinue() string { return meta.Continue
func (meta *ListMeta) SetContinue(c string) { meta.Continue = c }
func (meta *ListMeta) GetRemainingItemCount() *int64 { return meta.RemainingItemCount }
func (meta *ListMeta) SetRemainingItemCount(c *int64) { meta.RemainingItemCount = c }
func (meta *ListMeta) GetShardInfo() *ShardInfo { return meta.ShardInfo }
func (meta *ListMeta) SetShardInfo(s *ShardInfo) { meta.ShardInfo = s }
func (obj *TypeMeta) GetObjectKind() schema.ObjectKind { return obj }
-1
View File
@@ -1,5 +1,4 @@
//go:build !notest
// +build !notest
/*
Copyright 2020 The Kubernetes Authors.
-1
View File
@@ -1,5 +1,4 @@
//go:build !notest
// +build !notest
/*
Copyright 2020 The Kubernetes Authors.
+52 -21
View File
@@ -92,6 +92,26 @@ type ListMeta struct {
// should not rely on the remainingItemCount to be set or to be exact.
// +optional
RemainingItemCount *int64 `json:"remainingItemCount,omitempty" protobuf:"bytes,4,opt,name=remainingItemCount"`
// shardInfo is set when the list is a filtered subset of the full collection,
// as selected by a shard selector on the request. It echoes back the selector
// so clients can verify which shard they received and merge sharded responses.
// Clients should not cache sharded list responses as a full representation
// of the collection.
//
// This is an alpha field and requires enabling the ShardedListAndWatch feature gate.
// +featureGate=ShardedListAndWatch
// +optional
ShardInfo *ShardInfo `json:"shardInfo,omitempty" protobuf:"bytes,5,opt,name=shardInfo"`
}
// ShardInfo describes the shard selector that was applied to produce a list response.
// Its presence on a list response indicates the list is a filtered subset.
type ShardInfo struct {
// selector is the shard selector string from the request, echoed back so clients
// can verify which shard they received and merge responses from multiple shards.
// +required
Selector string `json:"selector" protobuf:"bytes,1,opt,name=selector"`
}
// Field path constants that are specific to the internal API
@@ -430,6 +450,38 @@ type ListOptions struct {
// compatibility reasons) and to false otherwise.
// +optional
SendInitialEvents *bool `json:"sendInitialEvents,omitempty" protobuf:"varint,11,opt,name=sendInitialEvents"`
// shardSelector restricts the list of returned objects using a CEL-based
// shard selector expression. The format uses the shardRange() function
// combined with || (logical OR) to specify one or more hash ranges:
//
// shardRange(object.metadata.uid, '0x0', '0x8000000000000000')
// shardRange(object.metadata.uid, '0x0', '0x8000000000000000') || shardRange(object.metadata.uid, '0x8000000000000000', '0x10000000000000000')
//
// Field paths use CEL-style object-rooted syntax (e.g. "object.metadata.uid"),
// NOT the fieldSelector format ("metadata.uid"). Currently supported paths:
// - object.metadata.uid
// - object.metadata.namespace
//
// hexStart and hexEnd are single-quoted CEL string literals with a '0x' prefix,
// defining the inclusive lower and exclusive upper bounds over the 64-bit FNV-1a
// hash space. The full range is [0x0, 0x10000000000000000), where the exclusive
// upper bound equals 2^64.
//
// Examples:
// 2-shard split:
// shard 0: shardRange(object.metadata.uid, '0x0000000000000000', '0x8000000000000000')
// shard 1: shardRange(object.metadata.uid, '0x8000000000000000', '0x10000000000000000')
// 4-shard split:
// shard 0: shardRange(object.metadata.uid, '0x0000000000000000', '0x4000000000000000')
// shard 1: shardRange(object.metadata.uid, '0x4000000000000000', '0x8000000000000000')
// shard 2: shardRange(object.metadata.uid, '0x8000000000000000', '0xc000000000000000')
// shard 3: shardRange(object.metadata.uid, '0xc000000000000000', '0x10000000000000000')
//
// This is an alpha field and requires enabling the ShardedListAndWatch feature gate.
// +featureGate=ShardedListAndWatch
// +optional
ShardSelector string `json:"shardSelector,omitempty" protobuf:"bytes,15,opt,name=shardSelector"`
}
const (
@@ -1384,27 +1436,6 @@ const (
ManagedFieldsOperationUpdate ManagedFieldsOperationType = "Update"
)
// FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.
//
// Each key is either a '.' representing the field itself, and will always map to an empty set,
// or a string representing a sub-field or item. The string will follow one of these four formats:
// 'f:<name>', where <name> is the name of a field in a struct, or key in a map
// 'v:<value>', where <value> is the exact json formatted value of a list item
// 'i:<index>', where <index> is position of a item in a list
// 'k:<keys>', where <keys> is a map of a list item's key fields to their unique values
// If a key maps to an empty Fields value, the field that key represents is part of the set.
//
// The exact format is defined in sigs.k8s.io/structured-merge-diff
// +protobuf.options.(gogoproto.goproto_stringer)=false
type FieldsV1 struct {
// Raw is the underlying serialization of this object.
Raw []byte `json:"-" protobuf:"bytes,1,opt,name=Raw"`
}
func (f FieldsV1) String() string {
return string(f.Raw)
}
// TODO: Table does not generate to protobuf because of the interface{} - fix protobuf
// generation to support a meta type that can accept any valid JSON. This can be introduced
// in a v1 because clients a) receive an error if they try to access proto today, and b)
+11 -8
View File
@@ -147,14 +147,6 @@ func (FieldSelectorRequirement) SwaggerDoc() map[string]string {
return map_FieldSelectorRequirement
}
var map_FieldsV1 = map[string]string{
"": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:<name>', where <name> is the name of a field in a struct, or key in a map 'v:<value>', where <value> is the exact json formatted value of a list item 'i:<index>', where <index> is position of a item in a list 'k:<keys>', where <keys> is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
}
func (FieldsV1) SwaggerDoc() map[string]string {
return map_FieldsV1
}
var map_GetOptions = map[string]string{
"": "GetOptions is the standard query options to the standard REST get call.",
"resourceVersion": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset",
@@ -211,6 +203,7 @@ var map_ListMeta = map[string]string{
"resourceVersion": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency",
"continue": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.",
"remainingItemCount": "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.",
"shardInfo": "shardInfo is set when the list is a filtered subset of the full collection, as selected by a shard selector on the request. It echoes back the selector so clients can verify which shard they received and merge sharded responses. Clients should not cache sharded list responses as a full representation of the collection.\n\nThis is an alpha field and requires enabling the ShardedListAndWatch feature gate.",
}
func (ListMeta) SwaggerDoc() map[string]string {
@@ -229,6 +222,7 @@ var map_ListOptions = map[string]string{
"limit": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.",
"continue": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.",
"sendInitialEvents": "`sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic \"Bookmark\" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `\"k8s.io/initial-events-end\": \"true\"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.",
"shardSelector": "shardSelector restricts the list of returned objects using a CEL-based shard selector expression. The format uses the shardRange() function combined with || (logical OR) to specify one or more hash ranges:\n\n shardRange(object.metadata.uid, '0x0', '0x8000000000000000')\n shardRange(object.metadata.uid, '0x0', '0x8000000000000000') || shardRange(object.metadata.uid, '0x8000000000000000', '0x10000000000000000')\n\nField paths use CEL-style object-rooted syntax (e.g. \"object.metadata.uid\"), NOT the fieldSelector format (\"metadata.uid\"). Currently supported paths:\n - object.metadata.uid\n - object.metadata.namespace\n\nhexStart and hexEnd are single-quoted CEL string literals with a '0x' prefix, defining the inclusive lower and exclusive upper bounds over the 64-bit FNV-1a hash space. The full range is [0x0, 0x10000000000000000), where the exclusive upper bound equals 2^64.\n\nExamples:\n 2-shard split:\n shard 0: shardRange(object.metadata.uid, '0x0000000000000000', '0x8000000000000000')\n shard 1: shardRange(object.metadata.uid, '0x8000000000000000', '0x10000000000000000')\n 4-shard split:\n shard 0: shardRange(object.metadata.uid, '0x0000000000000000', '0x4000000000000000')\n shard 1: shardRange(object.metadata.uid, '0x4000000000000000', '0x8000000000000000')\n shard 2: shardRange(object.metadata.uid, '0x8000000000000000', '0xc000000000000000')\n shard 3: shardRange(object.metadata.uid, '0xc000000000000000', '0x10000000000000000')\n\nThis is an alpha field and requires enabling the ShardedListAndWatch feature gate.",
}
func (ListOptions) SwaggerDoc() map[string]string {
@@ -355,6 +349,15 @@ func (ServerAddressByClientCIDR) SwaggerDoc() map[string]string {
return map_ServerAddressByClientCIDR
}
var map_ShardInfo = map[string]string{
"": "ShardInfo describes the shard selector that was applied to produce a list response. Its presence on a list response indicates the list is a filtered subset.",
"selector": "selector is the shard selector string from the request, echoed back so clients can verify which shard they received and merge responses from multiple shards.",
}
func (ShardInfo) SwaggerDoc() map[string]string {
return map_ShardInfo
}
var map_Status = map[string]string{
"": "Status is a return value for calls that don't return other objects.",
"metadata": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
@@ -440,6 +440,13 @@ func autoConvert_url_Values_To_v1_ListOptions(in *url.Values, out *ListOptions,
} else {
out.SendInitialEvents = nil
}
if values, ok := map[string][]string(*in)["shardSelector"]; ok && len(values) > 0 {
if err := runtime.Convert_Slice_string_To_string(&values, &out.ShardSelector, s); err != nil {
return err
}
} else {
out.ShardSelector = ""
}
return nil
}
+22 -23
View File
@@ -353,27 +353,6 @@ func (in *FieldSelectorRequirement) DeepCopy() *FieldSelectorRequirement {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FieldsV1) DeepCopyInto(out *FieldsV1) {
*out = *in
if in.Raw != nil {
in, out := &in.Raw, &out.Raw
*out = make([]byte, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FieldsV1.
func (in *FieldsV1) DeepCopy() *FieldsV1 {
if in == nil {
return nil
}
out := new(FieldsV1)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GetOptions) DeepCopyInto(out *GetOptions) {
*out = *in
@@ -606,6 +585,11 @@ func (in *ListMeta) DeepCopyInto(out *ListMeta) {
*out = new(int64)
**out = **in
}
if in.ShardInfo != nil {
in, out := &in.ShardInfo, &out.ShardInfo
*out = new(ShardInfo)
**out = **in
}
return
}
@@ -663,8 +647,7 @@ func (in *ManagedFieldsEntry) DeepCopyInto(out *ManagedFieldsEntry) {
}
if in.FieldsV1 != nil {
in, out := &in.FieldsV1, &out.FieldsV1
*out = new(FieldsV1)
(*in).DeepCopyInto(*out)
*out = (*in).DeepCopy()
}
return
}
@@ -947,6 +930,22 @@ func (in *ServerAddressByClientCIDR) DeepCopy() *ServerAddressByClientCIDR {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ShardInfo) DeepCopyInto(out *ShardInfo) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShardInfo.
func (in *ShardInfo) DeepCopy() *ShardInfo {
if in == nil {
return nil
}
out := new(ShardInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Status) DeepCopyInto(out *Status) {
*out = *in
@@ -201,6 +201,11 @@ func (in ServerAddressByClientCIDR) OpenAPIModelName() string {
return "io.k8s.apimachinery.pkg.apis.meta.v1.ServerAddressByClientCIDR"
}
// OpenAPIModelName returns the OpenAPI model name for this type.
func (in ShardInfo) OpenAPIModelName() string {
return "io.k8s.apimachinery.pkg.apis.meta.v1.ShardInfo"
}
// OpenAPIModelName returns the OpenAPI model name for this type.
func (in Status) OpenAPIModelName() string {
return "io.k8s.apimachinery.pkg.apis.meta.v1.Status"
-46
View File
@@ -1,46 +0,0 @@
/*
Copyright 2017 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 v1beta1
import (
"unsafe"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
)
// Convert_Slice_string_To_v1beta1_IncludeObjectPolicy allows converting a URL query parameter value
func Convert_Slice_string_To_v1beta1_IncludeObjectPolicy(in *[]string, out *IncludeObjectPolicy, s conversion.Scope) error {
if len(*in) > 0 {
*out = IncludeObjectPolicy((*in)[0])
}
return nil
}
// Convert_v1beta1_PartialObjectMetadataList_To_v1_PartialObjectMetadataList allows converting PartialObjectMetadataList between versions
func Convert_v1beta1_PartialObjectMetadataList_To_v1_PartialObjectMetadataList(in *PartialObjectMetadataList, out *v1.PartialObjectMetadataList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]v1.PartialObjectMetadata)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_v1_PartialObjectMetadataList_To_v1beta1_PartialObjectMetadataList allows converting PartialObjectMetadataList between versions
func Convert_v1_PartialObjectMetadataList_To_v1beta1_PartialObjectMetadataList(in *v1.PartialObjectMetadataList, out *PartialObjectMetadataList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]v1.PartialObjectMetadata)(unsafe.Pointer(&in.Items))
return nil
}
-17
View File
@@ -1,17 +0,0 @@
/*
Copyright 2017 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 v1beta1
-24
View File
@@ -1,24 +0,0 @@
/*
Copyright 2017 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.
*/
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta
// +k8s:openapi-model-package=io.k8s.apimachinery.pkg.apis.meta.v1beta1
// +groupName=meta.k8s.io
package v1beta1
-341
View File
@@ -1,341 +0,0 @@
/*
Copyright 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.
*/
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: k8s.io/apimachinery/pkg/apis/meta/v1beta1/generated.proto
package v1beta1
import (
fmt "fmt"
io "io"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
math_bits "math/bits"
reflect "reflect"
strings "strings"
)
func (m *PartialObjectMetadataList) Reset() { *m = PartialObjectMetadataList{} }
func (m *PartialObjectMetadataList) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PartialObjectMetadataList) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PartialObjectMetadataList) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
{
size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
if len(m.Items) > 0 {
for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
offset -= sovGenerated(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *PartialObjectMetadataList) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Items) > 0 {
for _, e := range m.Items {
l = e.Size()
n += 1 + l + sovGenerated(uint64(l))
}
}
l = m.ListMeta.Size()
n += 1 + l + sovGenerated(uint64(l))
return n
}
func sovGenerated(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozGenerated(x uint64) (n int) {
return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (this *PartialObjectMetadataList) String() string {
if this == nil {
return "nil"
}
repeatedStringForItems := "[]PartialObjectMetadata{"
for _, f := range this.Items {
repeatedStringForItems += fmt.Sprintf("%v", f) + ","
}
repeatedStringForItems += "}"
s := strings.Join([]string{`&PartialObjectMetadataList{`,
`Items:` + repeatedStringForItems + `,`,
`ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`,
`}`,
}, "")
return s
}
func valueToStringGenerated(v interface{}) string {
rv := reflect.ValueOf(v)
if rv.IsNil() {
return "nil"
}
pv := reflect.Indirect(rv).Interface()
return fmt.Sprintf("*%v", pv)
}
func (m *PartialObjectMetadataList) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: PartialObjectMetadataList: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: PartialObjectMetadataList: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Items = append(m.Items, v1.PartialObjectMetadata{})
if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowGenerated
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthGenerated
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupGenerated
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthGenerated
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupGenerated = fmt.Errorf("proto: unexpected end of group")
)
-41
View File
@@ -1,41 +0,0 @@
/*
Copyright 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.
*/
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
syntax = "proto2";
package k8s.io.apimachinery.pkg.apis.meta.v1beta1;
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "k8s.io/apimachinery/pkg/apis/meta/v1beta1";
// PartialObjectMetadataList contains a list of objects containing only their metadata.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
message PartialObjectMetadataList {
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
optional .k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 2;
// items contains each of the included items.
repeated .k8s.io.apimachinery.pkg.apis.meta.v1.PartialObjectMetadata items = 1;
}
@@ -1,24 +0,0 @@
//go:build kubernetes_protomessage_one_more_release
// +build kubernetes_protomessage_one_more_release
/*
Copyright 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.
*/
// Code generated by go-to-protobuf. DO NOT EDIT.
package v1beta1
func (*PartialObjectMetadataList) ProtoMessage() {}
-62
View File
@@ -1,62 +0,0 @@
/*
Copyright 2017 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 v1beta1
import (
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name for this API.
const GroupName = "meta.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// AddMetaToScheme registers base meta types into schemas.
func AddMetaToScheme(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Table{},
&TableOptions{},
&PartialObjectMetadata{},
&PartialObjectMetadataList{},
)
return nil
}
// RegisterConversions adds conversion functions to the given scheme.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*PartialObjectMetadataList)(nil), (*v1.PartialObjectMetadataList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_PartialObjectMetadataList_To_v1_PartialObjectMetadataList(a.(*PartialObjectMetadataList), b.(*v1.PartialObjectMetadataList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.PartialObjectMetadataList)(nil), (*PartialObjectMetadataList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_PartialObjectMetadataList_To_v1beta1_PartialObjectMetadataList(a.(*v1.PartialObjectMetadataList), b.(*PartialObjectMetadataList), scope)
}); err != nil {
return err
}
return nil
}
-85
View File
@@ -1,85 +0,0 @@
/*
Copyright 2017 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 v1beta1 is alpha objects from meta that will be introduced.
package v1beta1
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Table is a tabular representation of a set of API resources. The server transforms the
// object into a set of preferred columns for quickly reviewing the objects.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +protobuf=false
type Table = v1.Table
// TableColumnDefinition contains information about a column returned in the Table.
// +protobuf=false
type TableColumnDefinition = v1.TableColumnDefinition
// TableRow is an individual row in a table.
// +protobuf=false
type TableRow = v1.TableRow
// TableRowCondition allows a row to be marked with additional information.
// +protobuf=false
type TableRowCondition = v1.TableRowCondition
type RowConditionType = v1.RowConditionType
type ConditionStatus = v1.ConditionStatus
type IncludeObjectPolicy = v1.IncludeObjectPolicy
// TableOptions are used when a Table is requested by the caller.
// +k8s:conversion-gen:explicit-from=net/url.Values
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type TableOptions = v1.TableOptions
// PartialObjectMetadata is a generic representation of any object with ObjectMeta. It allows clients
// to get access to a particular ObjectMeta schema without knowing the details of the version.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type PartialObjectMetadata = v1.PartialObjectMetadata
// IMPORTANT: PartialObjectMetadataList has different protobuf field ids in v1beta1 than
// v1 because ListMeta was accidentally omitted prior to 1.15. Therefore this type must
// remain independent of v1.PartialObjectMetadataList to preserve mappings.
// PartialObjectMetadataList contains a list of objects containing only their metadata.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type PartialObjectMetadataList struct {
v1.TypeMeta `json:",inline"`
// Standard list metadata.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
// +optional
v1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,2,opt,name=metadata"`
// items contains each of the included items.
Items []v1.PartialObjectMetadata `json:"items" protobuf:"bytes,1,rep,name=items"`
}
const (
RowCompleted = v1.RowCompleted
ConditionTrue = v1.ConditionTrue
ConditionFalse = v1.ConditionFalse
ConditionUnknown = v1.ConditionUnknown
IncludeNone = v1.IncludeNone
IncludeMetadata = v1.IncludeMetadata
IncludeObject = v1.IncludeObject
)
@@ -1,40 +0,0 @@
/*
Copyright 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 v1beta1
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//
// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if
// they are on one line! For multiple line or blocks that you want to ignore use ---.
// Any context after a --- is ignored.
//
// Those methods can be generated by using hack/update-codegen.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_PartialObjectMetadataList = map[string]string{
"": "PartialObjectMetadataList contains a list of objects containing only their metadata.",
"metadata": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"items": "items contains each of the included items.",
}
func (PartialObjectMetadataList) SwaggerDoc() map[string]string {
return map_PartialObjectMetadataList
}
// AUTO-GENERATED FUNCTIONS END HERE
@@ -1,60 +0,0 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1beta1
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PartialObjectMetadataList) DeepCopyInto(out *PartialObjectMetadataList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]v1.PartialObjectMetadata, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PartialObjectMetadataList.
func (in *PartialObjectMetadataList) DeepCopy() *PartialObjectMetadataList {
if in == nil {
return nil
}
out := new(PartialObjectMetadataList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *PartialObjectMetadataList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
@@ -1,33 +0,0 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by defaulter-gen. DO NOT EDIT.
package v1beta1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
return nil
}
@@ -1,27 +0,0 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by openapi-gen. DO NOT EDIT.
package v1beta1
// OpenAPIModelName returns the OpenAPI model name for this type.
func (in PartialObjectMetadataList) OpenAPIModelName() string {
return "io.k8s.apimachinery.pkg.apis.meta.v1beta1.PartialObjectMetadataList"
}
-28
View File
@@ -1,28 +0,0 @@
//go:build kubernetes_protomessage_one_more_release
// +build kubernetes_protomessage_one_more_release
/*
Copyright 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.
*/
// Code generated by go-to-protobuf. DO NOT EDIT.
package runtime
func (*RawExtension) ProtoMessage() {}
func (*TypeMeta) ProtoMessage() {}
func (*Unknown) ProtoMessage() {}
@@ -1,22 +0,0 @@
//go:build kubernetes_protomessage_one_more_release
// +build kubernetes_protomessage_one_more_release
/*
Copyright 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.
*/
// Code generated by go-to-protobuf. DO NOT EDIT.
package schema
@@ -58,12 +58,18 @@ var decode cbor.DecMode = func() cbor.DecMode {
// with or without tagging. If a tag number is present, it must be valid.
TimeTag: cbor.DecTagOptional,
// Observed depth up to 16 in fuzzed batch/v1 CronJobList. JSON implementation limit
// is 10000.
MaxNestedLevels: 64,
// MaxNestedLevels is set to the same value used in the JSON implementation.
MaxNestedLevels: 10000,
MaxArrayElements: 1024,
MaxMapPairs: 1024,
// MaxArrayElements is set to the maximum allowed by the cbor library. We rely on
// the library initial wellformedness scan and on the api max request limit to
// prevent preallocating very large slices during decoding.
MaxArrayElements: 2147483647,
// MaxMapPairs specifies the maximum number of key-value pairs allowed in a map.
// We selected this value as it is large enough so that in practice the API server
// decoder will always hit the request body limit before the limit here is reached.
MaxMapPairs: 2097152,
// Indefinite-length sequences aren't produced by this serializer, but other
// implementations can.
+2 -2
View File
@@ -50,7 +50,7 @@ var rawTypeTranscodeFuncs = map[reflect.Type]func(reflect.Value) error{
return nil
}
fields := rv.Addr().Interface().(*metav1.FieldsV1)
if fields.Raw == nil {
if fields.GetRawReader().Size() == 0 {
// When Raw is nil it encodes to null. Don't change nil Raw values during
// transcoding, they would have unmarshalled from JSON as nil too.
return nil
@@ -59,7 +59,7 @@ var rawTypeTranscodeFuncs = map[reflect.Type]func(reflect.Value) error{
if err != nil {
return fmt.Errorf("failed to transcode FieldsV1 to JSON: %w", err)
}
fields.Raw = j
fields.SetRawBytes(j)
return nil
},
}
-202
View File
@@ -1,202 +0,0 @@
/*
Copyright 2019 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 cache
import (
"container/heap"
"sync"
"time"
"k8s.io/utils/clock"
)
// NewExpiring returns an initialized expiring cache.
func NewExpiring() *Expiring {
return NewExpiringWithClock(clock.RealClock{})
}
// NewExpiringWithClock is like NewExpiring but allows passing in a custom
// clock for testing.
func NewExpiringWithClock(clock clock.Clock) *Expiring {
return &Expiring{
clock: clock,
cache: make(map[interface{}]entry),
}
}
// Expiring is a map whose entries expire after a per-entry timeout.
type Expiring struct {
// AllowExpiredGet causes the expiration check to be skipped on Get.
// It should only be used when a key always corresponds to the exact same value.
// Thus when this field is true, expired keys are considered valid
// until the next call to Set (which causes the GC to run).
// It may not be changed concurrently with calls to Get.
AllowExpiredGet bool
clock clock.Clock
// mu protects the below fields
mu sync.RWMutex
// cache is the internal map that backs the cache.
cache map[interface{}]entry
// generation is used as a cheap resource version for cache entries. Cleanups
// are scheduled with a key and generation. When the cleanup runs, it first
// compares its generation with the current generation of the entry. It
// deletes the entry iff the generation matches. This prevents cleanups
// scheduled for earlier versions of an entry from deleting later versions of
// an entry when Set() is called multiple times with the same key.
//
// The integer value of the generation of an entry is meaningless.
generation uint64
heap expiringHeap
}
type entry struct {
val interface{}
expiry time.Time
generation uint64
}
// Get looks up an entry in the cache.
func (c *Expiring) Get(key interface{}) (val interface{}, ok bool) {
c.mu.RLock()
defer c.mu.RUnlock()
e, ok := c.cache[key]
if !ok {
return nil, false
}
if !c.AllowExpiredGet && !c.clock.Now().Before(e.expiry) {
return nil, false
}
return e.val, true
}
// Set sets a key/value/expiry entry in the map, overwriting any previous entry
// with the same key. The entry expires at the given expiry time, but its TTL
// may be lengthened or shortened by additional calls to Set(). Garbage
// collection of expired entries occurs during calls to Set(), however calls to
// Get() will not return expired entries that have not yet been garbage
// collected.
func (c *Expiring) Set(key interface{}, val interface{}, ttl time.Duration) {
now := c.clock.Now()
expiry := now.Add(ttl)
c.mu.Lock()
defer c.mu.Unlock()
c.generation++
c.cache[key] = entry{
val: val,
expiry: expiry,
generation: c.generation,
}
// Run GC inline before pushing the new entry.
c.gc(now)
heap.Push(&c.heap, &expiringHeapEntry{
key: key,
expiry: expiry,
generation: c.generation,
})
}
// Delete deletes an entry in the map.
func (c *Expiring) Delete(key interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
c.del(key, 0)
}
// del deletes the entry for the given key. The generation argument is the
// generation of the entry that should be deleted. If the generation has been
// changed (e.g. if a set has occurred on an existing element but the old
// cleanup still runs), this is a noop. If the generation argument is 0, the
// entry's generation is ignored and the entry is deleted.
//
// del must be called under the write lock.
func (c *Expiring) del(key interface{}, generation uint64) {
e, ok := c.cache[key]
if !ok {
return
}
if generation != 0 && generation != e.generation {
return
}
delete(c.cache, key)
}
// Len returns the number of items in the cache.
func (c *Expiring) Len() int {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.cache)
}
func (c *Expiring) gc(now time.Time) {
for {
// Return from gc if the heap is empty or the next element is not yet
// expired.
//
// heap[0] is a peek at the next element in the heap, which is not obvious
// from looking at the (*expiringHeap).Pop() implementation below.
// heap.Pop() swaps the first entry with the last entry of the heap, then
// calls (*expiringHeap).Pop() which returns the last element.
if len(c.heap) == 0 || now.Before(c.heap[0].expiry) {
return
}
cleanup := heap.Pop(&c.heap).(*expiringHeapEntry)
c.del(cleanup.key, cleanup.generation)
}
}
type expiringHeapEntry struct {
key interface{}
expiry time.Time
generation uint64
}
// expiringHeap is a min-heap ordered by expiration time of its entries. The
// expiring cache uses this as a priority queue to efficiently organize entries
// which will be garbage collected once they expire.
type expiringHeap []*expiringHeapEntry
var _ heap.Interface = &expiringHeap{}
func (cq expiringHeap) Len() int {
return len(cq)
}
func (cq expiringHeap) Less(i, j int) bool {
return cq[i].expiry.Before(cq[j].expiry)
}
func (cq expiringHeap) Swap(i, j int) {
cq[i], cq[j] = cq[j], cq[i]
}
func (cq *expiringHeap) Push(c interface{}) {
*cq = append(*cq, c.(*expiringHeapEntry))
}
func (cq *expiringHeap) Pop() interface{} {
c := (*cq)[cq.Len()-1]
*cq = (*cq)[:cq.Len()-1]
return c
}
-173
View File
@@ -1,173 +0,0 @@
/*
Copyright 2016 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 cache
import (
"container/list"
"sync"
"time"
)
// Clock defines an interface for obtaining the current time
type Clock interface {
Now() time.Time
}
// realClock implements the Clock interface by calling time.Now()
type realClock struct{}
func (realClock) Now() time.Time { return time.Now() }
// LRUExpireCache is a cache that ensures the mostly recently accessed keys are returned with
// a ttl beyond which keys are forcibly expired.
type LRUExpireCache struct {
// clock is used to obtain the current time
clock Clock
lock sync.Mutex
maxSize int
evictionList list.List
entries map[interface{}]*list.Element
}
// NewLRUExpireCache creates an expiring cache with the given size
func NewLRUExpireCache(maxSize int) *LRUExpireCache {
return NewLRUExpireCacheWithClock(maxSize, realClock{})
}
// NewLRUExpireCacheWithClock creates an expiring cache with the given size, using the specified clock to obtain the current time.
func NewLRUExpireCacheWithClock(maxSize int, clock Clock) *LRUExpireCache {
if maxSize <= 0 {
panic("maxSize must be > 0")
}
return &LRUExpireCache{
clock: clock,
maxSize: maxSize,
entries: map[interface{}]*list.Element{},
}
}
type cacheEntry struct {
key interface{}
value interface{}
expireTime time.Time
}
// Add adds the value to the cache at key with the specified maximum duration.
func (c *LRUExpireCache) Add(key interface{}, value interface{}, ttl time.Duration) {
c.lock.Lock()
defer c.lock.Unlock()
// Key already exists
oldElement, ok := c.entries[key]
if ok {
c.evictionList.MoveToFront(oldElement)
oldElement.Value.(*cacheEntry).value = value
oldElement.Value.(*cacheEntry).expireTime = c.clock.Now().Add(ttl)
return
}
// Make space if necessary
if c.evictionList.Len() >= c.maxSize {
toEvict := c.evictionList.Back()
c.evictionList.Remove(toEvict)
delete(c.entries, toEvict.Value.(*cacheEntry).key)
}
// Add new entry
entry := &cacheEntry{
key: key,
value: value,
expireTime: c.clock.Now().Add(ttl),
}
element := c.evictionList.PushFront(entry)
c.entries[key] = element
}
// Get returns the value at the specified key from the cache if it exists and is not
// expired, or returns false.
func (c *LRUExpireCache) Get(key interface{}) (interface{}, bool) {
c.lock.Lock()
defer c.lock.Unlock()
element, ok := c.entries[key]
if !ok {
return nil, false
}
if c.clock.Now().After(element.Value.(*cacheEntry).expireTime) {
c.evictionList.Remove(element)
delete(c.entries, key)
return nil, false
}
c.evictionList.MoveToFront(element)
return element.Value.(*cacheEntry).value, true
}
// Remove removes the specified key from the cache if it exists
func (c *LRUExpireCache) Remove(key interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
element, ok := c.entries[key]
if !ok {
return
}
c.evictionList.Remove(element)
delete(c.entries, key)
}
// RemoveAll removes all keys that match predicate.
func (c *LRUExpireCache) RemoveAll(predicate func(key any) bool) {
c.lock.Lock()
defer c.lock.Unlock()
for key, element := range c.entries {
if predicate(key) {
c.evictionList.Remove(element)
delete(c.entries, key)
}
}
}
// Keys returns all unexpired keys in the cache.
//
// Keep in mind that subsequent calls to Get() for any of the returned keys
// might return "not found".
//
// Keys are returned ordered from least recently used to most recently used.
func (c *LRUExpireCache) Keys() []interface{} {
c.lock.Lock()
defer c.lock.Unlock()
now := c.clock.Now()
val := make([]interface{}, 0, c.evictionList.Len())
for element := c.evictionList.Back(); element != nil; element = element.Prev() {
// Only return unexpired keys
if !now.After(element.Value.(*cacheEntry).expireTime) {
val = append(val, element.Value.(*cacheEntry).key)
}
}
return val
}
-31
View File
@@ -1,31 +0,0 @@
//go:build usegocmp
// +build usegocmp
/*
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 diff
import (
"github.com/google/go-cmp/cmp" //nolint:depguard
)
// Diff returns a string representation of the difference between two objects.
// When built with the usegocmp tag, it uses go-cmp/cmp to generate a diff
// between the objects.
func Diff(a, b any) string {
return cmp.Diff(a, b)
}
-62
View File
@@ -1,62 +0,0 @@
//go:build !usegocmp
// +build !usegocmp
/*
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 diff
import (
"encoding/json"
"fmt"
"github.com/pmezard/go-difflib/difflib"
"k8s.io/apimachinery/pkg/util/dump"
)
// Diff returns a string representation of the difference between two objects.
// When built without the usegocmp tag, it uses go-difflib/difflib to generate a
// unified diff of the objects. It attempts to use JSON serialization first,
// falling back to an object dump via the dump package if JSON marshaling fails.
func Diff(a, b any) string {
aStr, aErr := toPrettyJSON(a)
bStr, bErr := toPrettyJSON(b)
if aErr != nil || bErr != nil {
aStr = dump.Pretty(a)
bStr = dump.Pretty(b)
}
diff := difflib.UnifiedDiff{
A: difflib.SplitLines(aStr),
B: difflib.SplitLines(bStr),
Context: 3,
}
diffstr, err := difflib.GetUnifiedDiffString(diff)
if err != nil {
return fmt.Sprintf("error generating diff: %v", err)
}
return diffstr
}
// toPrettyJSON converts an object to a pretty-printed JSON string.
func toPrettyJSON(data any) (string, error) {
jsonData, err := json.MarshalIndent(data, "", " ")
return string(jsonData), err
}
-67
View File
@@ -1,67 +0,0 @@
/*
Copyright 2014 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 diff
import (
"bytes"
"fmt"
"strings"
"text/tabwriter"
"k8s.io/apimachinery/pkg/util/dump"
)
// ObjectGoPrintSideBySide prints a and b as textual dumps side by side,
// enabling easy visual scanning for mismatches.
func ObjectGoPrintSideBySide(a, b interface{}) string {
sA := dump.Pretty(a)
sB := dump.Pretty(b)
linesA := strings.Split(sA, "\n")
linesB := strings.Split(sB, "\n")
width := 0
for _, s := range linesA {
l := len(s)
if l > width {
width = l
}
}
for _, s := range linesB {
l := len(s)
if l > width {
width = l
}
}
buf := &bytes.Buffer{}
w := tabwriter.NewWriter(buf, width, 0, 1, ' ', 0)
max := len(linesA)
if len(linesB) > max {
max = len(linesB)
}
for i := 0; i < max; i++ {
var a, b string
if i < len(linesA) {
a = linesA[i]
}
if i < len(linesB) {
b = linesB[i]
}
_, _ = fmt.Fprintf(w, "%s\t%s\n", a, b)
}
_ = w.Flush()
return buf.String()
}
-54
View File
@@ -1,54 +0,0 @@
/*
Copyright 2021 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 dump
import (
"github.com/davecgh/go-spew/spew"
)
var prettyPrintConfig = &spew.ConfigState{
Indent: " ",
DisableMethods: true,
DisablePointerAddresses: true,
DisableCapacities: true,
}
// The config MUST NOT be changed because that could change the result of a hash operation
var prettyPrintConfigForHash = &spew.ConfigState{
Indent: " ",
SortKeys: true,
DisableMethods: true,
SpewKeys: true,
DisablePointerAddresses: true,
DisableCapacities: true,
}
// Pretty wrap the spew.Sdump with Indent, and disabled methods like error() and String()
// The output may change over time, so for guaranteed output please take more direct control
func Pretty(a interface{}) string {
return prettyPrintConfig.Sdump(a)
}
// ForHash keeps the original Spew.Sprintf format to ensure the same checksum
func ForHash(a interface{}) string {
return prettyPrintConfigForHash.Sprintf("%#v", a)
}
// OneLine outputs the object in one line
func OneLine(a interface{}) string {
return prettyPrintConfig.Sprintf("%#v", a)
}
@@ -1,24 +0,0 @@
//go:build kubernetes_protomessage_one_more_release
// +build kubernetes_protomessage_one_more_release
/*
Copyright 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.
*/
// Code generated by go-to-protobuf. DO NOT EDIT.
package intstr
func (*IntOrString) ProtoMessage() {}
-1
View File
@@ -1,5 +1,4 @@
//go:build !notest
// +build !notest
/*
Copyright 2020 The Kubernetes Authors.
-160
View File
@@ -1,160 +0,0 @@
/*
Copyright 2017 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 jsonmergepatch
import (
"fmt"
"reflect"
"gopkg.in/evanphx/json-patch.v4"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/mergepatch"
)
// Create a 3-way merge patch based-on JSON merge patch.
// Calculate addition-and-change patch between current and modified.
// Calculate deletion patch between original and modified.
func CreateThreeWayJSONMergePatch(original, modified, current []byte, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
if len(original) == 0 {
original = []byte(`{}`)
}
if len(modified) == 0 {
modified = []byte(`{}`)
}
if len(current) == 0 {
current = []byte(`{}`)
}
addAndChangePatch, err := jsonpatch.CreateMergePatch(current, modified)
if err != nil {
return nil, err
}
// Only keep addition and changes
addAndChangePatch, addAndChangePatchObj, err := keepOrDeleteNullInJsonPatch(addAndChangePatch, false)
if err != nil {
return nil, err
}
deletePatch, err := jsonpatch.CreateMergePatch(original, modified)
if err != nil {
return nil, err
}
// Only keep deletion
deletePatch, deletePatchObj, err := keepOrDeleteNullInJsonPatch(deletePatch, true)
if err != nil {
return nil, err
}
hasConflicts, err := mergepatch.HasConflicts(addAndChangePatchObj, deletePatchObj)
if err != nil {
return nil, err
}
if hasConflicts {
return nil, mergepatch.NewErrConflict(mergepatch.ToYAMLOrError(addAndChangePatchObj), mergepatch.ToYAMLOrError(deletePatchObj))
}
patch, err := jsonpatch.MergePatch(deletePatch, addAndChangePatch)
if err != nil {
return nil, err
}
var patchMap map[string]interface{}
err = json.Unmarshal(patch, &patchMap)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal patch for precondition check: %s", patch)
}
meetPreconditions, err := meetPreconditions(patchMap, fns...)
if err != nil {
return nil, err
}
if !meetPreconditions {
return nil, mergepatch.NewErrPreconditionFailed(patchMap)
}
return patch, nil
}
// keepOrDeleteNullInJsonPatch takes a json-encoded byte array and a boolean.
// It returns a filtered object and its corresponding json-encoded byte array.
// It is a wrapper of func keepOrDeleteNullInObj
func keepOrDeleteNullInJsonPatch(patch []byte, keepNull bool) ([]byte, map[string]interface{}, error) {
var patchMap map[string]interface{}
err := json.Unmarshal(patch, &patchMap)
if err != nil {
return nil, nil, err
}
filteredMap, err := keepOrDeleteNullInObj(patchMap, keepNull)
if err != nil {
return nil, nil, err
}
o, err := json.Marshal(filteredMap)
return o, filteredMap, err
}
// keepOrDeleteNullInObj will keep only the null value and delete all the others,
// if keepNull is true. Otherwise, it will delete all the null value and keep the others.
func keepOrDeleteNullInObj(m map[string]interface{}, keepNull bool) (map[string]interface{}, error) {
filteredMap := make(map[string]interface{})
var err error
for key, val := range m {
switch {
case keepNull && val == nil:
filteredMap[key] = nil
case val != nil:
switch typedVal := val.(type) {
case map[string]interface{}:
// Explicitly-set empty maps are treated as values instead of empty patches
if len(typedVal) == 0 {
if !keepNull {
filteredMap[key] = typedVal
}
continue
}
var filteredSubMap map[string]interface{}
filteredSubMap, err = keepOrDeleteNullInObj(typedVal, keepNull)
if err != nil {
return nil, err
}
// If the returned filtered submap was empty, this is an empty patch for the entire subdict, so the key
// should not be set
if len(filteredSubMap) != 0 {
filteredMap[key] = filteredSubMap
}
case []interface{}, string, float64, bool, int64, nil:
// Lists are always replaced in Json, no need to check each entry in the list.
if !keepNull {
filteredMap[key] = val
}
default:
return nil, fmt.Errorf("unknown type: %v", reflect.TypeOf(typedVal))
}
}
}
return filteredMap, nil
}
func meetPreconditions(patchObj map[string]interface{}, fns ...mergepatch.PreconditionFunc) (bool, error) {
// Apply the preconditions to the patch, and return an error if any of them fail.
for _, fn := range fns {
if !fn(patchObj) {
return false, fmt.Errorf("precondition failed for: %v", patchObj)
}
}
return true, nil
}
+1 -2
View File
@@ -17,7 +17,6 @@ limitations under the License.
package managedfields
import (
"bytes"
"fmt"
"sigs.k8s.io/structured-merge-diff/v6/fieldpath"
@@ -65,7 +64,7 @@ func ExtractInto(object runtime.Object, objectType typed.ParseableType, fieldMan
return nil
}
fieldset := &fieldpath.Set{}
err = fieldset.FromJSON(bytes.NewReader(fieldsEntry.FieldsV1.Raw))
err = fieldset.FromJSON(fieldsEntry.FieldsV1.GetRawReader())
if err != nil {
return fmt.Errorf("error marshalling FieldsV1 to JSON: %w", err)
}
+3 -4
View File
@@ -17,8 +17,6 @@ limitations under the License.
package internal
import (
"bytes"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/structured-merge-diff/v6/fieldpath"
@@ -36,12 +34,13 @@ var EmptyFields = func() metav1.FieldsV1 {
// FieldsToSet creates a set paths from an input trie of fields
func FieldsToSet(f metav1.FieldsV1) (s fieldpath.Set, err error) {
err = s.FromJSON(bytes.NewReader(f.Raw))
err = s.FromJSON(f.GetRawReader())
return s, err
}
// SetToFields creates a trie of fields from an input set of paths
func SetToFields(s fieldpath.Set) (f metav1.FieldsV1, err error) {
f.Raw, err = s.ToJSON()
raw, err := s.ToJSON()
f.SetRawBytes(raw)
return f, err
}
+1 -1
View File
@@ -20,7 +20,7 @@ import (
"fmt"
"reflect"
"k8s.io/apimachinery/pkg/util/dump"
"k8s.io/utils/dump"
"sigs.k8s.io/yaml"
)
+5
View File
@@ -132,9 +132,11 @@ func SetTransportDefaults(t *http.Transport) *http.Transport {
t = SetOldTransportDefaults(t)
// Allow clients to disable http2 if needed.
if s := os.Getenv("DISABLE_HTTP2"); len(s) > 0 {
//nolint:logcheck // Should be rare, not worth converting.
klog.Info("HTTP2 has been explicitly disabled")
} else if allowsHTTP2(t) {
if err := configureHTTP2Transport(t); err != nil {
//nolint:logcheck // Should be rare, not worth converting.
klog.Warningf("Transport failed http2 configuration: %v", err)
}
}
@@ -148,6 +150,7 @@ func readIdleTimeoutSeconds() int {
if s := os.Getenv("HTTP2_READ_IDLE_TIMEOUT_SECONDS"); len(s) > 0 {
i, err := strconv.Atoi(s)
if err != nil {
//nolint:logcheck // Should be rare, not worth converting.
klog.Warningf("Illegal HTTP2_READ_IDLE_TIMEOUT_SECONDS(%q): %v."+
" Default value %d is used", s, err, ret)
return ret
@@ -162,6 +165,7 @@ func pingTimeoutSeconds() int {
if s := os.Getenv("HTTP2_PING_TIMEOUT_SECONDS"); len(s) > 0 {
i, err := strconv.Atoi(s)
if err != nil {
//nolint:logcheck // Should be rare, not worth converting.
klog.Warningf("Illegal HTTP2_PING_TIMEOUT_SECONDS(%q): %v."+
" Default value %d is used", s, err, ret)
return ret
@@ -256,6 +260,7 @@ func CloseIdleConnectionsFor(transport http.RoundTripper) {
case RoundTripperWrapper:
CloseIdleConnectionsFor(transport.WrappedRoundTripper())
default:
//nolint:logcheck // Should be rare, not worth converting.
klog.Warningf("unknown transport type: %T", transport)
}
}
+72 -40
View File
@@ -201,12 +201,12 @@ func parseIP(str string, family AddressFamily) (net.IP, error) {
return net.IP(bytes), nil
}
func isInterfaceUp(intf *net.Interface) bool {
func isInterfaceUp(logger klog.Logger, intf *net.Interface) bool {
if intf == nil {
return false
}
if intf.Flags&net.FlagUp != 0 {
klog.V(4).Infof("Interface %v is up", intf.Name)
logger.V(4).Info("Interface is up", "interface", intf.Name)
return true
}
return false
@@ -218,23 +218,23 @@ func isLoopbackOrPointToPoint(intf *net.Interface) bool {
// getMatchingGlobalIP returns the first valid global unicast address of the given
// 'family' from the list of 'addrs'.
func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
func getMatchingGlobalIP(logger klog.Logger, addrs []net.Addr, family AddressFamily) (net.IP, error) {
if len(addrs) > 0 {
for i := range addrs {
klog.V(4).Infof("Checking addr %s.", addrs[i].String())
logger.V(4).Info("Checking for matching global IP", "address", addrs[i])
ip, _, err := netutils.ParseCIDRSloppy(addrs[i].String())
if err != nil {
return nil, err
}
if memberOf(ip, family) {
if ip.IsGlobalUnicast() {
klog.V(4).Infof("IP found %v", ip)
logger.V(4).Info("IP found", "IP", ip)
return ip, nil
} else {
klog.V(4).Infof("Non-global unicast address found %v", ip)
logger.V(4).Info("Non-global unicast address found", "IP", ip)
}
} else {
klog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
logger.V(4).Info("IP address has wrong version", "IP", ip, "IPVersion", int(family))
}
}
@@ -244,23 +244,23 @@ func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error)
// getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The
// interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
func getIPFromInterface(logger klog.Logger, intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
intf, err := nw.InterfaceByName(intfName)
if err != nil {
return nil, err
}
if isInterfaceUp(intf) {
if isInterfaceUp(logger, intf) {
addrs, err := nw.Addrs(intf)
if err != nil {
return nil, err
}
klog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
logger.V(4).Info("Found addresses for interface", "interface", intfName, "numAddresses", len(addrs), "addresses", addrs)
matchingIP, err := getMatchingGlobalIP(logger, addrs, forFamily)
if err != nil {
return nil, err
}
if matchingIP != nil {
klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
logger.V(4).Info("Found valid address", "IPVersion", int(forFamily), "IP", matchingIP, "interface", intfName)
return matchingIP, nil
}
}
@@ -269,13 +269,13 @@ func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInte
// getIPFromLoopbackInterface gets the IPs on a loopback interface and returns a global unicast address, if any.
// The loopback interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
func getIPFromLoopbackInterface(forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
func getIPFromLoopbackInterface(logger klog.Logger, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
intfs, err := nw.Interfaces()
if err != nil {
return nil, err
}
for _, intf := range intfs {
if !isInterfaceUp(&intf) {
if !isInterfaceUp(logger, &intf) {
continue
}
if intf.Flags&(net.FlagLoopback) != 0 {
@@ -283,13 +283,13 @@ func getIPFromLoopbackInterface(forFamily AddressFamily, nw networkInterfacer) (
if err != nil {
return nil, err
}
klog.V(4).Infof("Interface %q has %d addresses :%v.", intf.Name, len(addrs), addrs)
matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
logger.V(4).Info("Found addresses for interface", "interface", intf.Name, "numAddresses", len(addrs), "addresses", addrs)
matchingIP, err := getMatchingGlobalIP(logger, addrs, forFamily)
if err != nil {
return nil, err
}
if matchingIP != nil {
klog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intf.Name)
logger.V(4).Info("Found valid address", "IPVersion", int(forFamily), "IP", matchingIP, "interface", intf.Name)
return matchingIP, nil
}
}
@@ -309,7 +309,7 @@ func memberOf(ip net.IP, family AddressFamily) bool {
// chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
// has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
// addressFamilies determines whether it prefers IPv4 or IPv6
func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
func chooseIPFromHostInterfaces(logger klog.Logger, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
intfs, err := nw.Interfaces()
if err != nil {
return nil, err
@@ -318,14 +318,14 @@ func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFam
return nil, fmt.Errorf("no interfaces found on host.")
}
for _, family := range addressFamilies {
klog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
logger.V(4).Info("Looking for system interface with a global address", "IPVersion", uint(family))
for _, intf := range intfs {
if !isInterfaceUp(&intf) {
klog.V(4).Infof("Skipping: down interface %q", intf.Name)
if !isInterfaceUp(logger, &intf) {
logger.V(4).Info("Skipping: interface is down", "interface", intf.Name)
continue
}
if isLoopbackOrPointToPoint(&intf) {
klog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
logger.V(4).Info("Skipping: is LB or P2P", "interface", intf.Name)
continue
}
addrs, err := nw.Addrs(&intf)
@@ -333,7 +333,7 @@ func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFam
return nil, err
}
if len(addrs) == 0 {
klog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
logger.V(4).Info("Skipping: no addresses", "interface", intf.Name)
continue
}
for _, addr := range addrs {
@@ -342,15 +342,15 @@ func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFam
return nil, fmt.Errorf("unable to parse CIDR for interface %q: %s", intf.Name, err)
}
if !memberOf(ip, family) {
klog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
logger.V(4).Info("Skipping: no address family match", "IP", ip, "interface", intf.Name)
continue
}
// TODO: Decide if should open up to allow IPv6 LLAs in future.
if !ip.IsGlobalUnicast() {
klog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
logger.V(4).Info("Skipping: non-global address", "IP", ip, "interface", intf.Name)
continue
}
klog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
logger.V(4).Info("Found global unicast address", "IP", ip, "interface", intf.Name)
return ip, nil
}
}
@@ -363,20 +363,31 @@ func chooseIPFromHostInterfaces(nw networkInterfacer, addressFamilies AddressFam
// interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
// IP of the interface with a gateway on it (with priority given to IPv4). For a node
// with no internet connection, it returns error.
//
//logcheck:context // [ChooseHostInterfaceWithLogger] should be used instead of ChooseHostInterface in code which supports contextual logging.
func ChooseHostInterface() (net.IP, error) {
return chooseHostInterface(preferIPv4)
return ChooseHostInterfaceWithLogger(klog.Background())
}
func chooseHostInterface(addressFamilies AddressFamilyPreference) (net.IP, error) {
// ChooseHostInterfaceWithLogger is a method used fetch an IP for a daemon.
// If there is no routing info file, it will choose a global IP from the system
// interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
// IP of the interface with a gateway on it (with priority given to IPv4). For a node
// with no internet connection, it returns error.
func ChooseHostInterfaceWithLogger(logger klog.Logger) (net.IP, error) {
return chooseHostInterface(logger, preferIPv4)
}
func chooseHostInterface(logger klog.Logger, addressFamilies AddressFamilyPreference) (net.IP, error) {
var nw networkInterfacer = networkInterface{}
if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
return chooseIPFromHostInterfaces(nw, addressFamilies)
return chooseIPFromHostInterfaces(logger, nw, addressFamilies)
}
routes, err := getAllDefaultRoutes()
if err != nil {
return nil, err
}
return chooseHostInterfaceFromRoute(routes, nw, addressFamilies)
return chooseHostInterfaceFromRoute(logger, routes, nw, addressFamilies)
}
// networkInterfacer defines an interface for several net library functions. Production
@@ -427,36 +438,36 @@ func getAllDefaultRoutes() ([]Route, error) {
// global IP address from the interface for the route. If there are routes but no global
// address is obtained from the interfaces, it checks if the loopback interface has a global address.
// addressFamilies determines whether it prefers IPv4 or IPv6
func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
func chooseHostInterfaceFromRoute(logger klog.Logger, routes []Route, nw networkInterfacer, addressFamilies AddressFamilyPreference) (net.IP, error) {
for _, family := range addressFamilies {
klog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
logger.V(4).Info("Looking for default routes with IP addresses", "IPVersion", uint(family))
for _, route := range routes {
if route.Family != family {
continue
}
klog.V(4).Infof("Default route transits interface %q", route.Interface)
finalIP, err := getIPFromInterface(route.Interface, family, nw)
logger.V(4).Info("Default route transits interface", "interface", route.Interface)
finalIP, err := getIPFromInterface(logger, route.Interface, family, nw)
if err != nil {
return nil, err
}
if finalIP != nil {
klog.V(4).Infof("Found active IP %v ", finalIP)
logger.V(4).Info("Found active IP", "IP", finalIP)
return finalIP, nil
}
// In case of network setups where default routes are present, but network
// interfaces use only link-local addresses (e.g. as described in RFC5549).
// the global IP is assigned to the loopback interface, and we should use it
loopbackIP, err := getIPFromLoopbackInterface(family, nw)
loopbackIP, err := getIPFromLoopbackInterface(logger, family, nw)
if err != nil {
return nil, err
}
if loopbackIP != nil {
klog.V(4).Infof("Found active IP %v on Loopback interface", loopbackIP)
logger.V(4).Info("Found active IP on Loopback interface", "IP", loopbackIP)
return loopbackIP, nil
}
}
}
klog.V(4).Infof("No active IP found by looking at default routes")
logger.V(4).Info("No active IP found by looking at default routes")
return nil, fmt.Errorf("unable to select an IP from default routes.")
}
@@ -465,14 +476,25 @@ func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer, addressF
// If bindAddress is unspecified or loopback, it returns the default IP of the same
// address family as bindAddress.
// Otherwise, it just returns bindAddress.
//
//logcheck:context // [ResolveBindAddressWithLogger] should be used instead of ResolveBindAddress in code which supports contextual logging.
func ResolveBindAddress(bindAddress net.IP) (net.IP, error) {
return ResolveBindAddressWithLogger(klog.Background(), bindAddress)
}
// ResolveBindAddressWithLogger returns the IP address of a daemon, based on the given bindAddress:
// If bindAddress is unset, it returns the host's default IP, as with ChooseHostInterface().
// If bindAddress is unspecified or loopback, it returns the default IP of the same
// address family as bindAddress.
// Otherwise, it just returns bindAddress.
func ResolveBindAddressWithLogger(logger klog.Logger, bindAddress net.IP) (net.IP, error) {
addressFamilies := preferIPv4
if bindAddress != nil && memberOf(bindAddress, familyIPv6) {
addressFamilies = preferIPv6
}
if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
hostIP, err := chooseHostInterface(addressFamilies)
hostIP, err := chooseHostInterface(logger, addressFamilies)
if err != nil {
return nil, err
}
@@ -485,10 +507,20 @@ func ResolveBindAddress(bindAddress net.IP) (net.IP, error) {
// This is required in case of network setups where default routes are present, but network
// interfaces use only link-local addresses (e.g. as described in RFC5549).
// e.g when using BGP to announce a host IP over link-local ip addresses and this ip address is attached to the lo interface.
//
//logcheck:context // [ChooseBindAddressForInterfaceWithLogger] should be used instead of ChooseBindAddressForInterface in code which supports contextual logging.
func ChooseBindAddressForInterface(intfName string) (net.IP, error) {
return ChooseBindAddressForInterfaceWithLogger(klog.Background(), intfName)
}
// ChooseBindAddressForInterfaceWithLogger choose a global IP for a specific interface, with priority given to IPv4.
// This is required in case of network setups where default routes are present, but network
// interfaces use only link-local addresses (e.g. as described in RFC5549).
// e.g when using BGP to announce a host IP over link-local ip addresses and this ip address is attached to the lo interface.
func ChooseBindAddressForInterfaceWithLogger(logger klog.Logger, intfName string) (net.IP, error) {
var nw networkInterfacer = networkInterface{}
for _, family := range preferIPv4 {
ip, err := getIPFromInterface(intfName, family, nw)
ip, err := getIPFromInterface(logger, intfName, family, nw)
if err != nil {
return nil, err
}
+32 -1
View File
@@ -17,14 +17,17 @@ limitations under the License.
package runtime
import (
"bytes"
"context"
"fmt"
"net/http"
"runtime"
"strings"
"sync"
"time"
"k8s.io/klog/v2"
"k8s.io/klog/v2/textlogger"
)
var (
@@ -55,7 +58,6 @@ func HandleCrash(additionalHandlers ...func(interface{})) {
if r := recover(); r != nil {
additionalHandlersWithContext := make([]func(context.Context, interface{}), len(additionalHandlers))
for i, handler := range additionalHandlers {
handler := handler // capture loop variable
additionalHandlersWithContext[i] = func(_ context.Context, r interface{}) {
handler(r)
}
@@ -155,8 +157,37 @@ var ErrorHandlers = []ErrorHandler{
backoffError(1 * time.Millisecond),
}
// ErrorHandler is called indirectly through [HandleError], [HandleErrorWithContext] or [HandleErrorWithLogger].
// It is passed the same parameters that a structured logging backend needs to log a problem.
// It follows the semantic described for [HandleErrorWithContext] and [logr.Logger.Error]:
// - err is optional and may be nil
// - msg is string that describes the problem
// - keysAndValues contains additional information that varies between different occurrences of the problem
//
// [ErrorToString] can be used to convert these parameters into a single string, using the klog text output.
type ErrorHandler func(ctx context.Context, err error, msg string, keysAndValues ...interface{})
// ErrorToString takes the parameters passed to [ErrorHandler] and
// formats them as a string using the klog text output.
//
// If any of the values is a multi-line string, then the resulting
// string also uses line breaks and indention for the sake of readability.
// Does not include a trailing newline.
//
// Use errors.New if an error instead of a string is needed.
func ErrorToString(err error, msg string, keysAndValues ...interface{}) string {
var buffer bytes.Buffer
config := textlogger.NewConfig(
textlogger.Output(&buffer),
textlogger.WithHeader(false),
)
logger := textlogger.NewLogger(config)
logger.Error(err, msg, keysAndValues...)
result := buffer.String()
result = strings.TrimSpace(result)
return result
}
// HandlerError is a method to invoke when a non-user facing piece of code cannot
// return an error and needs to indicate it has been ignored. Invoking this method
// is preferable to logging the error - the default behavior is to log but the
+2 -6
View File
@@ -1827,9 +1827,7 @@ func (ss SortableSliceOfMaps) Less(i, j int) bool {
}
func (ss SortableSliceOfMaps) Swap(i, j int) {
tmp := ss.s[i]
ss.s[i] = ss.s[j]
ss.s[j] = tmp
ss.s[i], ss.s[j] = ss.s[j], ss.s[i]
}
func deduplicateAndSortScalars(s []interface{}) []interface{} {
@@ -1875,9 +1873,7 @@ func (ss SortableSliceOfScalars) Less(i, j int) bool {
}
func (ss SortableSliceOfScalars) Swap(i, j int) {
tmp := ss.s[i]
ss.s[i] = ss.s[j]
ss.s[j] = tmp
ss.s[i], ss.s[j] = ss.s[j], ss.s[i]
}
// Returns the type of the elements of N slice(s). If the type is different,
+36 -4
View File
@@ -40,10 +40,12 @@ type ErrorMatcher struct {
matchField bool
// TODO(thockin): consider whether value could be assumed - if the
// "want" error has a nil value, don't match on value.
matchValue bool
matchOrigin bool
matchDetail func(want, got string) bool
requireOriginWhenInvalid bool
matchValue bool
matchOrigin bool
matchDetail func(want, got string) bool
requireOriginWhenInvalid bool
matchValidationStabilityLevel bool
matchSource bool
// normalizationRules holds the pre-compiled regex patterns for path normalization.
normalizationRules []NormalizationRule
}
@@ -86,6 +88,14 @@ func (m ErrorMatcher) Matches(want, got *Error) bool {
if m.matchDetail != nil && !m.matchDetail(want.Detail, got.Detail) {
return false
}
if m.matchValidationStabilityLevel && want.ValidationStabilityLevel != got.ValidationStabilityLevel {
return false
}
if m.matchSource && want.FromImperative != got.FromImperative {
return false
}
return true
}
@@ -148,6 +158,14 @@ func (m ErrorMatcher) Render(e *Error) string {
comma()
buf.WriteString(fmt.Sprintf("Detail=%q", e.Detail))
}
if m.matchValidationStabilityLevel {
comma()
buf.WriteString(fmt.Sprintf("ValidationStabilityLevel=%s", e.ValidationStabilityLevel))
}
if m.matchSource {
comma()
buf.WriteString(fmt.Sprintf("FromImperative=%t", e.FromImperative))
}
return "{" + buf.String() + "}"
}
@@ -224,6 +242,20 @@ func (m ErrorMatcher) RequireOriginWhenInvalid() ErrorMatcher {
return m
}
// BySource returns a derived ErrorMatcher which also matches by the error origination
// value of field errors.
func (m ErrorMatcher) BySource() ErrorMatcher {
m.matchSource = true
return m
}
// ByValidationStabilityLevel returns a derived ErrorMatcher which also matches by the validation stability level
// value of field errors.
func (m ErrorMatcher) ByValidationStabilityLevel() ErrorMatcher {
m.matchValidationStabilityLevel = true
return m
}
// ByDetailExact returns a derived ErrorMatcher which also matches errors by
// the exact detail string.
func (m ErrorMatcher) ByDetailExact() ErrorMatcher {
+219 -13
View File
@@ -42,7 +42,7 @@ type Error struct {
// The value should be either:
// - A simple camelCase identifier (e.g., "maximum", "maxItems")
// - A structured format using "format=<dash-style-identifier>" for validation errors related to specific formats
// (e.g., "format=dns-label", "format=qualified-name")
// (e.g. "format=k8s-short-name")
//
// If the Origin corresponds to an existing declarative validation tag or JSON Schema keyword,
// use that same name for consistency.
@@ -55,10 +55,46 @@ type Error struct {
// validation. This field is to identify errors from imperative validation
// that should also be caught by declarative validation.
CoveredByDeclarative bool
// FromImperative denotes these errors are originating from the hand written validations.
FromImperative bool
// ValidationStabilityLevel denotes the validation stability level of the declarative validation from this error is returned. This should be used in the declarative validations only.
ValidationStabilityLevel ValidationStabilityLevel
}
// ValidationStabilityLevel denotes the stability level of a validation.
type ValidationStabilityLevel int
const (
stabilityLevelUnknown ValidationStabilityLevel = iota
stabilityLevelAlpha
stabilityLevelBeta
)
func (v ValidationStabilityLevel) String() string {
switch v {
case stabilityLevelAlpha:
return "alpha"
case stabilityLevelBeta:
return "beta"
default:
return "unknown"
}
}
var _ error = &Error{}
// IsAlpha returns true if the error is an alpha validation error.
func (e *Error) IsAlpha() bool {
return e.ValidationStabilityLevel == stabilityLevelAlpha
}
// IsBeta returns true if the error is a beta validation error.
func (e *Error) IsBeta() bool {
return e.ValidationStabilityLevel == stabilityLevelBeta
}
// Error implements the error interface.
func (e *Error) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.ErrorBody())
@@ -73,10 +109,10 @@ var omitValue = OmitValueType{}
func (e *Error) ErrorBody() string {
var s string
switch e.Type {
case ErrorTypeRequired, ErrorTypeForbidden, ErrorTypeTooLong, ErrorTypeInternal:
case ErrorTypeRequired, ErrorTypeForbidden, ErrorTypeTooLong, ErrorTypeTooShort, ErrorTypeInternal:
s = e.Type.String()
case ErrorTypeInvalid, ErrorTypeTypeInvalid, ErrorTypeNotSupported,
ErrorTypeNotFound, ErrorTypeDuplicate, ErrorTypeTooMany:
ErrorTypeNotFound, ErrorTypeDuplicate, ErrorTypeTooMany, ErrorTypeTooFew:
if e.BadValue == omitValue {
s = e.Type.String()
break
@@ -113,6 +149,7 @@ func (e *Error) ErrorBody() string {
if len(e.Detail) != 0 {
s += fmt.Sprintf(": %s", e.Detail)
}
return s
}
@@ -164,11 +201,18 @@ const (
// report that a given list has too many items. This is similar to FieldValueTooLong,
// but the error indicates quantity instead of length.
ErrorTypeTooMany ErrorType = "FieldValueTooMany"
// ErrorTypeTooFew is used to report "too few". This is used to
// report that a given list has too few items. This is similar to FieldValueTooLong,
// but the error indicates quantity instead of length.
ErrorTypeTooFew ErrorType = "FieldValueTooFew"
// ErrorTypeInternal is used to report other errors that are not related
// to user input. See InternalError().
ErrorTypeInternal ErrorType = "InternalError"
// ErrorTypeTypeInvalid is for the value did not match the schema type for that field
ErrorTypeTypeInvalid ErrorType = "FieldValueTypeInvalid"
// ErrorTypeTooShort is used to report that the given value is too short.
// This is similar to ErrorTypeInvalid. See TooShort().
ErrorTypeTooShort ErrorType = "FieldValueTooShort"
)
// String converts a ErrorType into its corresponding canonical error message.
@@ -190,10 +234,14 @@ func (t ErrorType) String() string {
return "Too long"
case ErrorTypeTooMany:
return "Too many"
case ErrorTypeTooFew:
return "Too few"
case ErrorTypeInternal:
return "Internal error"
case ErrorTypeTypeInvalid:
return "Invalid value"
case ErrorTypeTooShort:
return "Too short"
default:
return fmt.Sprintf("<unknown error %q>", string(t))
}
@@ -201,32 +249,56 @@ func (t ErrorType) String() string {
// TypeInvalid returns a *Error indicating "type is invalid"
func TypeInvalid(field *Path, value interface{}, detail string) *Error {
return &Error{ErrorTypeTypeInvalid, field.String(), value, detail, "", false}
return &Error{
Type: ErrorTypeTypeInvalid,
Field: field.String(),
BadValue: value,
Detail: detail,
}
}
// NotFound returns a *Error indicating "value not found". This is
// used to report failure to find a requested value (e.g. looking up an ID).
func NotFound(field *Path, value interface{}) *Error {
return &Error{ErrorTypeNotFound, field.String(), value, "", "", false}
return &Error{
Type: ErrorTypeNotFound,
Field: field.String(),
BadValue: value,
}
}
// Required returns a *Error indicating "value required". This is used
// to report required values that are not provided (e.g. empty strings, null
// values, or empty arrays).
func Required(field *Path, detail string) *Error {
return &Error{ErrorTypeRequired, field.String(), "", detail, "", false}
return &Error{
Type: ErrorTypeRequired,
Field: field.String(),
Detail: detail,
BadValue: "",
}
}
// Duplicate returns a *Error indicating "duplicate value". This is
// used to report collisions of values that must be unique (e.g. names or IDs).
func Duplicate(field *Path, value interface{}) *Error {
return &Error{ErrorTypeDuplicate, field.String(), value, "", "", false}
return &Error{
Type: ErrorTypeDuplicate,
Field: field.String(),
BadValue: value,
}
}
// Invalid returns a *Error indicating "invalid value". This is used
// to report malformed values (e.g. failed regex match, too long, out of bounds).
func Invalid(field *Path, value interface{}, detail string) *Error {
return &Error{ErrorTypeInvalid, field.String(), value, detail, "", false}
return &Error{
Type: ErrorTypeInvalid,
Field: field.String(),
BadValue: value,
Detail: detail,
}
}
// NotSupported returns a *Error indicating "unsupported value".
@@ -241,7 +313,12 @@ func NotSupported[T ~string](field *Path, value interface{}, validValues []T) *E
}
detail = "supported values: " + strings.Join(quotedValues, ", ")
}
return &Error{ErrorTypeNotSupported, field.String(), value, detail, "", false}
return &Error{
Type: ErrorTypeNotSupported,
Field: field.String(),
BadValue: value,
Detail: detail,
}
}
// Forbidden returns a *Error indicating "forbidden". This is used to
@@ -249,7 +326,12 @@ func NotSupported[T ~string](field *Path, value interface{}, validValues []T) *E
// some conditions, but which are not permitted by current conditions (e.g.
// security policy).
func Forbidden(field *Path, detail string) *Error {
return &Error{ErrorTypeForbidden, field.String(), "", detail, "", false}
return &Error{
Type: ErrorTypeForbidden,
Field: field.String(),
Detail: detail,
BadValue: "",
}
}
// TooLong returns a *Error indicating "too long". This is used to report that
@@ -267,7 +349,35 @@ func TooLong(field *Path, _ interface{}, maxLength int) *Error {
} else {
msg = "value is too long"
}
return &Error{ErrorTypeTooLong, field.String(), "<value omitted>", msg, "", false}
return &Error{
Type: ErrorTypeTooLong,
Field: field.String(),
BadValue: "<value omitted>",
Detail: msg,
}
}
// TooLongCharacters returns a *Error indicating "too long". This is used to report that
// the given value is too long in characters (including multi-byte characters).
// This is similar to Invalid, but the returned error will not include the too-long value.
// If maxLength is negative, it will be included in the message. The value argument is not used.
func TooLongCharacters[T ~string](field *Path, _ T, maxLength int) *Error {
var msg string
if maxLength >= 0 {
bs := "characters"
if maxLength == 1 {
bs = "character"
}
msg = fmt.Sprintf("may not be more than %d %s", maxLength, bs)
} else {
msg = "value is too long"
}
return &Error{
Type: ErrorTypeTooLong,
Field: field.String(),
BadValue: "<value omitted>",
Detail: msg,
}
}
// TooLongMaxLength returns a *Error indicating "too long".
@@ -299,14 +409,46 @@ func TooMany(field *Path, actualQuantity, maxQuantity int) *Error {
actual = omitValue
}
return &Error{ErrorTypeTooMany, field.String(), actual, msg, "", false}
return &Error{
Type: ErrorTypeTooMany,
Field: field.String(),
BadValue: actual,
Detail: msg,
}
}
// InternalError returns a *Error indicating "internal error". This is used
// to signal that an error was found that was not directly related to user
// input. The err argument must be non-nil.
func InternalError(field *Path, err error) *Error {
return &Error{ErrorTypeInternal, field.String(), nil, err.Error(), "", false}
return &Error{
Type: ErrorTypeInternal,
Field: field.String(),
BadValue: err,
Detail: err.Error(),
}
}
// TooShort returns a *Error indicating "too short". This is used to report that
// the given value is too short in characters. This is similar to Invalid.
// If minLength is non-negative, it will be included in the message.
func TooShort[T ~string](field *Path, value T, minLength int) *Error {
var msg string
if minLength >= 0 {
bs := "characters"
if minLength == 1 {
bs = "character"
}
msg = fmt.Sprintf("must be at least %d %s", minLength, bs)
} else {
msg = "value is too short"
}
return &Error{
Type: ErrorTypeTooShort,
Field: field.String(),
BadValue: value,
Detail: msg,
}
}
// ErrorList holds a set of Errors. It is plausible that we might one day have
@@ -397,6 +539,46 @@ func (list ErrorList) ExtractCoveredByDeclarative() ErrorList {
return newList
}
// MarkAlpha marks the error as an alpha validation error.
func (e *Error) MarkAlpha() *Error {
e.ValidationStabilityLevel = stabilityLevelAlpha
return e
}
// MarkAlpha marks the errors as alpha validation errors.
func (list ErrorList) MarkAlpha() ErrorList {
for _, err := range list {
err.ValidationStabilityLevel = stabilityLevelAlpha
}
return list
}
// MarkBeta marks the error as a beta validation error.
func (e *Error) MarkBeta() *Error {
e.ValidationStabilityLevel = stabilityLevelBeta
return e
}
// MarkBeta marks the errors as beta validation errors.
func (list ErrorList) MarkBeta() ErrorList {
for _, err := range list {
err.ValidationStabilityLevel = stabilityLevelBeta
}
return list
}
func (e *Error) MarkFromImperative() *Error {
e.FromImperative = true
return e
}
func (list ErrorList) MarkFromImperative() ErrorList {
for _, err := range list {
err.FromImperative = true
}
return list
}
// RemoveCoveredByDeclarative returns a new ErrorList containing only the errors that should not be covered by declarative validation.
func (list ErrorList) RemoveCoveredByDeclarative() ErrorList {
newList := ErrorList{}
@@ -407,3 +589,27 @@ func (list ErrorList) RemoveCoveredByDeclarative() ErrorList {
}
return newList
}
// TooFew returns a *Error indicating "too few". This is used to
// report that a given list has too few items. This is similar to TooLong,
// but the returned error indicates quantity instead of length.
func TooFew(field *Path, actualQuantity, minQuantity int) *Error {
var msg string
if minQuantity >= 0 {
is := "items"
if minQuantity == 1 {
is = "item"
}
msg = fmt.Sprintf("must have at least %d %s", minQuantity, is)
} else {
msg = "has too few items"
}
return &Error{
Type: ErrorTypeTooFew,
Field: field.String(),
BadValue: actualQuantity,
Detail: msg,
}
}
+2 -2
View File
@@ -115,7 +115,7 @@ func GetWarningsForIP(fldPath *field.Path, value string) []string {
// ParseAddr() doesn't) or IPv4-mapped IPv6 (.Is4In6()). Either way,
// re-stringifying the net.IP value will give the preferred form.
return []string{
fmt.Sprintf("%s: non-standard IP address %q will be considered invalid in a future Kubernetes release: use %q", fldPath, value, ip.String()),
fmt.Sprintf("%s: non-standard IP address %q is invalid: use %q", fldPath, value, ip.String()),
}
}
@@ -233,7 +233,7 @@ func GetWarningsForCIDR(fldPath *field.Path, value string) []string {
// ParsePrefix() doesn't) or IPv4-mapped IPv6 (.Is4In6()). Either way,
// re-stringifying the net.IPNet value will give the preferred form.
warnings = append(warnings,
fmt.Sprintf("%s: non-standard CIDR value %q will be considered invalid in a future Kubernetes release: use %q", fldPath, value, ipnet.String()),
fmt.Sprintf("%s: non-standard CIDR value %q is invalid: use %q", fldPath, value, ipnet.String()),
)
}