pkg/k8score/read_scope_test.go
Rama main · réplica de lectura del repositorio original.
package k8score
import (
"slices"
"testing"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
)
func TestAssessKindReadScopeAndReadiness(t *testing.T) {
ready := true
rc := &ResourceCache{
enabledResources: map[string]bool{Pods: true},
config: CacheConfig{ResourceScopes: map[string]ResourceScope{Pods: {Enabled: true, Namespace: "alpha"}}, ResourceScopeNamespaces: map[string][]string{Pods: {"alpha", "beta"}}},
informerStatuses: []InformerSyncStatus{{Key: Pods}},
informerHasSynced: []func() bool{func() bool { return ready }},
}
partial := rc.AssessKindRead(Pods, []string{"alpha", "gamma"})
if partial.Complete || !slices.Equal(partial.Read, []string{"alpha"}) || !slices.Equal(partial.Uncovered, []string{"gamma"}) {
t.Fatalf("assessment = %+v", partial)
}
if !rc.AssessKindRead(Pods, []string{"beta"}).Complete {
t.Fatal("covered target should be complete")
}
empty := rc.AssessKindRead(Pods, []string{})
if !empty.Complete || empty.Requested == nil || empty.Read == nil || empty.AllNamespaces {
t.Fatalf("empty finite target became unrestricted: %+v", empty)
}
all := rc.AssessKindRead(Pods, nil)
if all.Complete || all.AllNamespaces || len(all.Read) != 2 {
t.Fatalf("unscoped assessment = %+v", all)
}
// Without per-namespace stores the kind's readiness is every namespace's.
ready = false
pending := rc.AssessKindRead(Pods, []string{"alpha", "gamma"})
if pending.Readiness != KindPending || len(pending.Read) != 0 || pending.Read == nil || pending.Complete ||
!slices.Equal(pending.Loading, []string{"alpha"}) || !slices.Equal(pending.Uncovered, []string{"gamma"}) {
t.Fatalf("pending scope = %+v", pending)
}
rc.deferredFailed.Store(true)
rc.deferredSynced = map[string]bool{Pods: false}
failed := rc.AssessKindRead(Pods, []string{"alpha", "gamma"})
if failed.Readiness != KindFailed || len(failed.Read) != 0 || failed.Complete ||
!slices.Equal(failed.Failed, []string{"alpha"}) || !slices.Equal(failed.Uncovered, []string{"gamma"}) {
t.Fatalf("failed scope = %+v", failed)
}
if unavailable := rc.AssessKindRead(Services, nil); unavailable.Complete || unavailable.AllNamespaces {
t.Fatalf("unavailable = %+v", unavailable)
}
var absent *ResourceCache
if absent.AssessKindRead(Pods, nil).Complete {
t.Fatal("nil cache established completeness")
}
ready = true
rc.config.ResourceScopes[Pods] = ResourceScope{Enabled: true}
cluster := rc.AssessKindRead(Pods, nil)
if !cluster.Complete || !cluster.AllNamespaces || cluster.Read != nil {
t.Fatalf("cluster = %+v", cluster)
}
// An empty target is complete on either kind of cache, loaded or not.
ready = false
if loading := rc.AssessKindRead(Pods, []string{}); !loading.Complete || loading.TargetReadiness() != KindReady {
t.Fatalf("empty target on a loading cluster-wide cache = %+v", loading)
}
// The deadline set above still applies: a named target takes the kind's state.
if named := rc.AssessKindRead(Pods, []string{"alpha"}); named.Complete || named.TargetReadiness() != KindFailed {
t.Fatalf("named target on a loading cluster-wide cache = %+v", named)
}
}
func TestAssessKindReadRequestedScope(t *testing.T) {
rc := &ResourceCache{
enabledResources: map[string]bool{Pods: true},
config: CacheConfig{ResourceScopes: map[string]ResourceScope{Pods: {Enabled: true, Namespace: "alpha"}}, ResourceScopeNamespaces: map[string][]string{Pods: {"alpha", "beta"}}},
informerStatuses: []InformerSyncStatus{{Key: Pods}},
informerHasSynced: []func() bool{func() bool { return true }},
}
for _, tt := range []struct {
name string
requested, read, uncovered []string
complete bool
}{
{"all", nil, []string{"alpha", "beta"}, nil, false},
{"explicit order", []string{"beta", "alpha"}, []string{"beta", "alpha"}, nil, true},
{"partial", []string{"beta", "gamma"}, []string{"beta"}, []string{"gamma"}, false},
{"zero overlap", []string{"gamma"}, []string{}, []string{"gamma"}, false},
{"no target", []string{}, []string{}, nil, true},
} {
t.Run(tt.name, func(t *testing.T) {
got := rc.AssessKindRead(Pods, tt.requested)
if got.Readiness != KindReady || got.Complete != tt.complete || !slices.Equal(got.Read, tt.read) || !slices.Equal(got.Uncovered, tt.uncovered) {
t.Fatalf("assessment = %+v", got)
}
if got.Read == nil {
t.Fatal("a namespace-scoped collector's read became all namespaces")
}
})
}
rc.config.ResourceScopes[Pods] = ResourceScope{Enabled: true}
if got := rc.AssessKindRead(Pods, []string{}); !got.Complete || !got.AllNamespaces || got.Read == nil || len(got.Read) != 0 {
t.Fatalf("cluster-wide no-target = %+v", got)
}
rc.informerHasSynced = []func() bool{func() bool { return false }}
if got := rc.AssessKindRead(Pods, []string{"alpha"}); got.Readiness != KindPending || !got.AllNamespaces || len(got.Read) != 0 || !slices.Equal(got.Loading, []string{"alpha"}) || got.Complete {
t.Fatalf("cluster-wide pending = %+v", got)
}
}
func podStore(t *testing.T, synced *bool, pods ...*corev1.Pod) kindStore {
t.Helper()
idx := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
for _, p := range pods {
if err := idx.Add(p); err != nil {
t.Fatal(err)
}
}
return kindStore{indexer: idx, synced: func() bool { return *synced }}
}
func pod(namespace, name string) *corev1.Pod {
return &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: name}}
}
// A kind watched namespace by namespace is readable in a namespace whose own
// informer has synced while another namespace is still loading — the kind as
// a whole is not ready until every namespace is.
func TestAssessKindReadIsPerNamespace(t *testing.T) {
alphaSynced, gammaSynced := true, false
rc := &ResourceCache{
enabledResources: map[string]bool{ConfigMaps: true},
config: CacheConfig{
ResourceScopes: map[string]ResourceScope{ConfigMaps: {Enabled: true, Namespace: "alpha"}},
ResourceScopeNamespaces: map[string][]string{ConfigMaps: {"alpha", "gamma"}},
},
informerStatuses: []InformerSyncStatus{{Key: ConfigMaps}},
informerHasSynced: []func() bool{func() bool { return alphaSynced && gammaSynced }},
kindStores: map[string]map[string]kindStore{ConfigMaps: {
"alpha": podStore(t, &alphaSynced, pod("alpha", "a1"), pod("alpha", "a2")),
"gamma": podStore(t, &gammaSynced, pod("gamma", "g1")),
}},
}
got := rc.AssessKindRead(ConfigMaps, []string{"alpha"})
if got.Readiness != KindPending || !got.Complete || !slices.Equal(got.Read, []string{"alpha"}) || got.TargetReadiness() != KindReady {
t.Fatalf("alpha while gamma loads = %+v", got)
}
all := rc.AssessKindRead(ConfigMaps, nil)
if all.Complete || !slices.Equal(all.Read, []string{"alpha"}) || !slices.Equal(all.Loading, []string{"gamma"}) || all.TargetReadiness() != KindPending {
t.Fatalf("unscoped while gamma loads = %+v", all)
}
if objs, ok := rc.ReadableObjects(ConfigMaps, []string{"alpha"}); !ok || len(objs) != 2 {
t.Fatalf("alpha objects = %d, %v", len(objs), ok)
}
if n, ok := rc.ReadableCount(ConfigMaps, []string{"alpha"}); !ok || n != 2 {
t.Fatalf("alpha count = %d, %v", n, ok)
}
if _, ok := rc.ReadableObjects(ConfigMaps, []string{"alpha", "gamma"}); ok {
t.Fatal("a loading namespace was readable")
}
if _, ok := rc.ReadableObjects(ConfigMaps, nil); ok {
t.Fatal("the whole kind was readable while gamma loads")
}
if _, found, ok := rc.ReadableObject(ConfigMaps, "alpha", "a1"); !ok || !found {
t.Fatal("a1 should be found in synced alpha")
}
if _, found, ok := rc.ReadableObject(ConfigMaps, "alpha", "missing"); !ok || found {
t.Fatal("a miss in synced alpha is a confirmed absence")
}
if _, _, ok := rc.ReadableObject(ConfigMaps, "gamma", "g1"); ok {
t.Fatal("a read in loading gamma must not be authoritative")
}
if _, _, ok := rc.ReadableObject(ConfigMaps, "delta", "x"); ok {
t.Fatal("an uncovered namespace must not be readable")
}
// The deferred deadline marks only the namespaces still loading.
rc.deferredFailed.Store(true)
rc.deferredSynced = map[string]bool{ConfigMaps: false}
failed := rc.AssessKindRead(ConfigMaps, []string{"alpha", "gamma"})
if failed.Readiness != KindFailed || !slices.Equal(failed.Read, []string{"alpha"}) || !slices.Equal(failed.Failed, []string{"gamma"}) || failed.TargetReadiness() != KindFailed {
t.Fatalf("after the deadline = %+v", failed)
}
if alpha := rc.AssessKindRead(ConfigMaps, []string{"alpha"}); alpha.TargetReadiness() != KindReady {
t.Fatalf("a loaded target is ready while another namespace failed: %+v", alpha)
}
// Without a partition the target is the whole kind.
if whole := (KindReadAssessment{Readiness: KindPending, AllNamespaces: true}); whole.TargetReadiness() != KindPending {
t.Fatalf("whole-kind target = %v", whole.TargetReadiness())
}
// Background kinds have no deadline.
rc.backgroundKeys = map[string]bool{ConfigMaps: true}
if bg := rc.AssessKindRead(ConfigMaps, []string{"gamma"}); !slices.Equal(bg.Loading, []string{"gamma"}) {
t.Fatalf("background kind = %+v", bg)
}
gammaSynced = true
if whole, ok := rc.ReadableObjects(ConfigMaps, nil); !ok || len(whole) != 3 {
t.Fatalf("whole kind = %d, %v", len(whole), ok)
}
}
func TestCachedObjectFoundBeforeSync(t *testing.T) {
synced := false
rc := &ResourceCache{enabledResources: map[string]bool{Pods: true}, kindStores: map[string]map[string]kindStore{Pods: {"": podStore(t, &synced, pod("alpha", "present"))}}}
obj, found := rc.CachedObject(Pods, "alpha", "present")
if !found || obj.(*corev1.Pod).Name != "present" {
t.Fatalf("hit before sync: %v %v", obj, found)
}
obj.(*corev1.Pod).Name = "changed"
if again, _ := rc.CachedObject(Pods, "alpha", "present"); again.(*corev1.Pod).Name != "present" {
t.Fatal("lookup exposed the store object")
}
if _, found := rc.CachedObject(Pods, "alpha", "missing"); found {
t.Fatal("unexpected hit")
}
if _, _, ok := rc.ReadableObject(Pods, "alpha", "missing"); ok {
t.Fatal("miss before sync became authoritative")
}
synced = true
if _, found, ok := rc.ReadableObject(Pods, "alpha", "missing"); found || !ok {
t.Fatal("miss after sync must establish absence")
}
}