Ctrl K
ComunidadGuías…

pkg/k8score/read_scope_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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")
	}
}