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
+70
View File
@@ -0,0 +1,70 @@
/*
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 testing provides a fake Kubernetes client suitable for use in unit
// tests. The fake client simulates interactions with a kube-apiserver without
// requiring one to be running, making tests fast and self-contained.
//
// # Scope and Limitations
//
// This fake client is intentionally simplified. It does not, and will never,
// fully replicate the behavior of a real kube-apiserver. Many server-side
// behaviors such as field defaulting, validation, status management,
// strategic merge patch, server-side apply semantics, and advanced
// field selectors are not supported by this client and there are no plans
// to add them.
//
// This is by design. Maintaining a high-fidelity mock of the entire
// kube-apiserver API surface would introduce significant complexity that is
// difficult to justify for a test utility.
//
// # When to Use This Package
//
// The fake client works well for unit tests that need to verify how your code
// interacts with the Kubernetes API at a structural level, for example:
//
// - Verifying that the correct API calls are made.
// - Supplying canned responses to drive specific code paths.
//
// # When Not to Use This Package
//
// If your tests depend on the kube-apiserver behaving correctly (e.g.,
// enforcing validation, persisting resources accurately, handling apply
// semantics, or producing realistic watch events), you should write
// integration tests against a real kube-apiserver instead.
//
// # Contributing
//
// Issues requesting that the fake client more closely match kube-apiserver
// behavior should be limiting to bugs in how the fake behaves for unit test
// scenarios it is clearly intended to support. Pull requests that improve the fake
// client will only be accepted when they meet all of the following criteria:
//
// - The change makes the fake client easier to use for common unit testing
// patterns.
// - The change does not introduce significant complexity to the fake client.
// - The use cases motivating the change are clearly better served by a fake
// client than by integration tests against a real kube-apiserver.
//
// We hold a high bar for these changes. If the test scenarios in question can
// be reasonably addressed through integration testing, we will prefer that
// path over expanding the fake client.
//
// We understand this stance may be inconvenient, and we appreciate your
// understanding. Our goal is to keep this package simple, maintainable, and
// honest about what it provides so that it remains a reliable tool for the
// cases it is designed to handle.
package testing
+108 -14
View File
@@ -20,6 +20,7 @@ import (
"fmt"
"reflect"
"sort"
"strconv"
"strings"
"sync"
@@ -288,13 +289,46 @@ type tracker struct {
scheme ObjectScheme
decoder runtime.Decoder
lock sync.RWMutex
objects map[schema.GroupVersionResource]map[types.NamespacedName]runtime.Object
objects map[schema.GroupVersionResource]map[types.NamespacedName]versionedObject
// The value type of watchers is a map of which the key is either a namespace or
// all/non namespace aka "" and its value is list of fake watchers.
// Manipulations on resources will broadcast the notification events into the
// watchers' channel. Note that too many unhandled events (currently 100,
// see apimachinery/pkg/watch.DefaultChanSize) will cause a panic.
watchers map[schema.GroupVersionResource]map[string][]*watch.RaceFreeFakeWatcher
// resourceVersions is the highest resource version of any tracked object with
// a certain gvr. Conceptually it starts at 1 when no objects are stored (0 is
// special in queries) but the map contains no entries in that case.
// The resource version for that set of objects gets bumped before
// storing a new or modified object.
//
// Object content does not get changed to preserve the traditional behavior
// (hence also the versionedObject type instead of storing a runtime.Object
// with modified ResourceVersion).
//
// Resource version support (https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions)
// is very limited. It only supports one particular use case:
// List (no resource version check, returned ListMeta has ResourceVersion set) +
// Watch (Exact match for the ResourceVersion returned by List).
//
// This is sufficient for Reflector.ListAndWatch (https://github.com/kubernetes/kubernetes/blob/b53b9fb5573323484af9a19cf3f5bfe80760abba/staging/src/k8s.io/client-go/tools/cache/reflector.go#L401)
// when setting up informers in an informer factory.
//
// Strictly speaking, this should be by GroupVersion. But objects are
// also tracked by GroupVersionResource instead of GroupVersion, so the
// same is done here to match how List is implemented.
resourceVersions map[schema.GroupVersionResource]int64
}
// versionedObject stores an object together with the resource version that was
// assigned to it by the tracker. The version could be stored inline in the object,
// but this is not how fake client-go has traditionally worked and starting to do
// that now might break tests.
type versionedObject struct {
// resourceVersion is always > 1 for a stored object because 1
// is the initial value for an empty set of objects.
resourceVersion int64
runtime.Object
}
var _ ObjectTracker = &tracker{}
@@ -303,10 +337,11 @@ var _ ObjectTracker = &tracker{}
// of objects for the fake clientset. Mostly useful for unit tests.
func NewObjectTracker(scheme ObjectScheme, decoder runtime.Decoder) ObjectTracker {
return &tracker{
scheme: scheme,
decoder: decoder,
objects: make(map[schema.GroupVersionResource]map[types.NamespacedName]runtime.Object),
watchers: make(map[schema.GroupVersionResource]map[string][]*watch.RaceFreeFakeWatcher),
scheme: scheme,
decoder: decoder,
objects: make(map[schema.GroupVersionResource]map[types.NamespacedName]versionedObject),
watchers: make(map[schema.GroupVersionResource]map[string][]*watch.RaceFreeFakeWatcher),
resourceVersions: make(map[schema.GroupVersionResource]int64),
}
}
@@ -338,15 +373,27 @@ func (t *tracker) List(gvr schema.GroupVersionResource, gvk schema.GroupVersionK
t.lock.RLock()
defer t.lock.RUnlock()
if listMeta, err := meta.ListAccessor(list); err == nil {
resourceVersion, ok := t.resourceVersions[gvr]
if !ok {
resourceVersion = 1
}
listMeta.SetResourceVersion(fmt.Sprintf("%d", resourceVersion))
}
objs, ok := t.objects[gvr]
if !ok {
return list, nil
}
matchingObjs, err := filterByNamespace(objs, ns)
matchingVersionedObjs, err := filterByNamespace(objs, ns)
if err != nil {
return nil, err
}
matchingObjs := make([]runtime.Object, len(matchingVersionedObjs))
for i, obj := range matchingVersionedObjs {
matchingObjs[i] = obj.Object
}
if err := meta.SetList(list, matchingObjs); err != nil {
return nil, err
}
@@ -359,6 +406,27 @@ func (t *tracker) Watch(gvr schema.GroupVersionResource, ns string, opts ...meta
return nil, err
}
// By default, emulate the traditional behavior of the tracker and don't deliver
// *any* existing objects unless list options are provided.
addExisting := false
addFromRV := int64(0)
if len(opts) > 0 {
// Providing options, as the generated client-go fake does, enables support
// for existing objects depending on the resource version.
//
// The default if ResourceVersion is empty is "start at most recent",
// which includes delivering all existing objects. addFromRV == 0
// matches all objects below because all stored objects have addFromRV > 0.
addExisting = true
if opts[0].ResourceVersion != "" {
rv, err := strconv.ParseInt(opts[0].ResourceVersion, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid ResourceVersion %q in ListOptions, must be int64: %w", opts[0].ResourceVersion, err)
}
addFromRV = rv
}
}
t.lock.Lock()
defer t.lock.Unlock()
@@ -368,6 +436,22 @@ func (t *tracker) Watch(gvr schema.GroupVersionResource, ns string, opts ...meta
t.watchers[gvr] = make(map[string][]*watch.RaceFreeFakeWatcher)
}
t.watchers[gvr][ns] = append(t.watchers[gvr][ns], fakewatcher)
// Deliver all objects that match the list options, for example
// between the initial List and the following Watch.
if addExisting {
objs := t.objects[gvr]
matchingObjs, err := filterByNamespace(objs, ns)
if err != nil {
return nil, err
}
for _, obj := range matchingObjs {
if addFromRV < obj.resourceVersion {
fakewatcher.Add(obj.Object)
}
}
}
return fakewatcher, nil
}
@@ -565,17 +649,26 @@ func (t *tracker) add(gvr schema.GroupVersionResource, obj runtime.Object, ns st
_, ok := t.objects[gvr]
if !ok {
t.objects[gvr] = make(map[types.NamespacedName]runtime.Object)
t.objects[gvr] = make(map[types.NamespacedName]versionedObject)
}
// Determine resource version for the new or updated object.
resourceVersion, ok := t.resourceVersions[gvr]
if !ok {
resourceVersion = 1
}
resourceVersion++
namespacedName := types.NamespacedName{Namespace: newMeta.GetNamespace(), Name: newMeta.GetName()}
if _, ok = t.objects[gvr][namespacedName]; ok {
if replaceExisting {
t.resourceVersions[gvr] = resourceVersion
t.objects[gvr][namespacedName] = versionedObject{resourceVersion, obj}
for _, w := range t.getWatches(gvr, ns) {
// To avoid the object from being accidentally modified by watcher
w.Modify(obj.DeepCopyObject())
}
t.objects[gvr][namespacedName] = obj
return nil
}
return apierrors.NewAlreadyExists(gr, newMeta.GetName())
@@ -586,7 +679,8 @@ func (t *tracker) add(gvr schema.GroupVersionResource, obj runtime.Object, ns st
return apierrors.NewNotFound(gr, newMeta.GetName())
}
t.objects[gvr][namespacedName] = obj
t.resourceVersions[gvr] = resourceVersion
t.objects[gvr][namespacedName] = versionedObject{resourceVersion, obj}
for _, w := range t.getWatches(gvr, ns) {
// To avoid the object from being accidentally modified by watcher
@@ -841,11 +935,11 @@ func (d *objectDefaulter) Default(_ runtime.Object) {}
// filterByNamespace returns all objects in the collection that
// match provided namespace. Empty namespace matches
// non-namespaced objects.
func filterByNamespace(objs map[types.NamespacedName]runtime.Object, ns string) ([]runtime.Object, error) {
var res []runtime.Object
func filterByNamespace(objs map[types.NamespacedName]versionedObject, ns string) ([]versionedObject, error) {
var res []versionedObject
for _, obj := range objs {
acc, err := meta.Accessor(obj)
acc, err := meta.Accessor(obj.Object)
if err != nil {
return nil, err
}
@@ -857,8 +951,8 @@ func filterByNamespace(objs map[types.NamespacedName]runtime.Object, ns string)
// Sort res to get deterministic order.
sort.Slice(res, func(i, j int) bool {
acc1, _ := meta.Accessor(res[i])
acc2, _ := meta.Accessor(res[j])
acc1, _ := meta.Accessor(res[i].Object)
acc2, _ := meta.Accessor(res[j].Object)
if acc1.GetNamespace() != acc2.GetNamespace() {
return acc1.GetNamespace() < acc2.GetNamespace()
}