pkg/k8score/dynamic_cache_synced_test.go
Rama main · réplica de lectura del repositorio original.
package k8score
import (
"errors"
"testing"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
dynamicfake "k8s.io/client-go/dynamic/fake"
k8stesting "k8s.io/client-go/testing"
)
// IsSynced answers "is SOME informer for this GVR synced", which is true as soon
// as any one namespace has been watched. A caller reading a different namespace
// that passes that gate starts a fresh informer on its first List and reads an
// empty indexer with no error — an absence the cache never established. For a
// screen that says "no cluster uses this catalog", that is the whole bug.
func TestIsNamespaceSynced_DoesNotBorrowAnotherNamespacesInformer(t *testing.T) {
gvr := schema.GroupVersionResource{Group: "example.com", Version: "v1", Resource: "widgets"}
dyn := fakeDynamicForListAccess(t, map[schema.GroupVersionResource]string{
gvr: "WidgetList",
}, func(schema.GroupVersionResource, string) bool { return true })
d, err := NewDynamicResourceCache(DynamicCacheConfig{DynamicClient: dyn})
if err != nil {
t.Fatalf("NewDynamicResourceCache failed: %v", err)
}
defer d.Stop()
if err := d.startWatching(gvr, "ns-a"); err != nil {
t.Fatalf("startWatching(ns-a): %v", err)
}
if !d.WaitForSync(gvr, 2*time.Second) {
t.Fatal("ns-a informer never synced")
}
// The state that makes the difference visible: one namespace watched.
if !d.IsSynced(gvr) {
t.Fatal("IsSynced should be true once any informer synced; test premise broken")
}
if !d.IsNamespaceSynced(gvr, "ns-a") {
t.Error("the namespace that IS watched must report synced")
}
if d.IsNamespaceSynced(gvr, "ns-b") {
t.Error("ns-b has no informer, so the cache cannot answer for it — reporting synced " +
"lets a caller read an empty list and call it an absence")
}
// Cluster-wide is a stronger claim still: namespace-scoped coverage is not it.
if d.IsNamespaceSynced(gvr, "") {
t.Error("a namespace-scoped cache cannot license a cluster-wide absence")
}
}
// A read served by several namespace informers waits once, not once per
// informer: three stalled informers must not triple the caller's timeout.
func TestListBlocking_TimeoutCoversEveryInformer(t *testing.T) {
gvr := schema.GroupVersionResource{Group: "example.com", Version: "v1", Resource: "widgets"}
dyn := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(
runtime.NewScheme(),
map[schema.GroupVersionResource]string{gvr: "WidgetList"},
)
dyn.PrependReactor("list", "widgets", func(action k8stesting.Action) (bool, runtime.Object, error) {
if action.GetNamespace() == "" {
return true, nil, apierrors.NewForbidden(gvr.GroupResource(), "", errors.New("denied"))
}
return true, nil, errors.New("apiserver hiccup")
})
d, err := NewDynamicResourceCache(DynamicCacheConfig{DynamicClient: dyn, NamespaceFallbacks: []string{"x", "y", "z"}})
if err != nil {
t.Fatalf("NewDynamicResourceCache failed: %v", err)
}
defer d.Stop()
const timeout = 500 * time.Millisecond
start := time.Now()
_, _ = d.ListBlocking(gvr, "", timeout)
if elapsed := time.Since(start); elapsed > 2*timeout {
t.Fatalf("ListBlocking took %v with a %v timeout over three stalled informers", elapsed, timeout)
}
if d.IsNamespaceSynced(gvr, "x") {
t.Fatal("test premise broken: the x informer synced")
}
}
// A fallback walk that stopped early leaves namespaces with no informer, so an
// all-namespaces read is incomplete even when every started informer synced.
// A namespace-scoped install never walks, so it never reports this.
func TestAllNamespacesFanoutIncomplete(t *testing.T) {
gvr := schema.GroupVersionResource{Group: "example.com", Version: "v1", Resource: "widgets"}
d := &DynamicResourceCache{fanoutIncomplete: map[schema.GroupVersionResource]bool{gvr: true}}
if !d.AllNamespacesFanoutIncomplete(gvr) {
t.Error("walk ended early: want incomplete")
}
d.fanoutIncomplete[gvr] = false
if d.AllNamespacesFanoutIncomplete(gvr) {
t.Error("walk finished: want complete")
}
scoped := &DynamicResourceCache{config: DynamicCacheConfig{NamespaceScoped: true, Namespace: "team"},
fanoutIncomplete: map[schema.GroupVersionResource]bool{gvr: true}}
if scoped.AllNamespacesFanoutIncomplete(gvr) {
t.Error("namespace-scoped install: want never incomplete")
}
var nilCache *DynamicResourceCache
if nilCache.AllNamespacesFanoutIncomplete(gvr) {
t.Error("nil cache: want false")
}
}