pkg/k8score/owner_index.go
Rama main · réplica de lectura del repositorio original.
package k8score
import (
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
apiruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/cache"
)
// OwnerUIDIndex indexes every cached object but Events by the UIDs its owner
// references name, so the objects naming one owner are found without scanning
// their kind.
const OwnerUIDIndex = "ownerUID"
var ownerUIDIndexers = cache.Indexers{OwnerUIDIndex: ownerUIDs}
// ownerUIDs returns the UIDs obj's owner references name, whatever their other
// fields say: the garbage collector treats an object as a dependent of every
// UID it names, even when the reference's kind or name is wrong.
func ownerUIDs(obj any) ([]string, error) {
switch o := obj.(type) {
case indexQuery:
return o, nil
case *unstructured.Unstructured:
refs, _, _ := unstructured.NestedFieldNoCopy(o.Object, "metadata", "ownerReferences")
list, _ := refs.([]any)
var uids []string
for _, r := range list {
ref, _ := r.(map[string]any)
if uid, _ := ref["uid"].(string); uid != "" {
uids = append(uids, uid)
}
}
return uids, nil
case metav1.Object:
refs := o.GetOwnerReferences()
if len(refs) == 0 {
return nil, nil
}
uids := make([]string, 0, len(refs))
for _, r := range refs {
if r.UID != "" {
uids = append(uids, string(r.UID))
}
}
return uids, nil
}
return nil, nil
}
// isEventResource reports either API group's Events, which are never indexed.
func isEventResource(gvr schema.GroupVersionResource) bool {
return gvr.Resource == "events" && (gvr.Group == "" || gvr.Group == "events.k8s.io")
}
// ReadableDependents returns the objects of resource in namespaces whose owner
// references name uid, under ReadableObjects' rules: ok is false unless each
// namespace's store is complete now, and nil namespaces means the kind's whole
// scope. The objects are the cached ones: callers must not modify or keep
// them.
func (rc *ResourceCache) ReadableDependents(resource string, namespaces []string, uid string) ([]apiruntime.Object, bool) {
if rc == nil || !rc.isEnabled(resource) || rc.kindStores[resource] == nil {
return nil, false
}
var hits []any
if namespaces == nil {
for _, st := range rc.kindStores[resource] {
if !st.synced() {
return nil, false
}
items, err := st.indexer.ByIndex(OwnerUIDIndex, uid)
if err != nil {
return nil, false
}
hits = append(hits, items...)
}
} else {
covered := rc.KindNamespaces(resource)
isCovered := namespaceSet(covered)
wanted := namespaceSet(namespaces)
read := map[cache.Indexer]bool{}
for _, namespace := range namespaces {
if covered != nil && !isCovered[namespace] {
return nil, false
}
st, ok := rc.storeFor(resource, namespace)
if !ok || !st.synced() {
return nil, false
}
if read[st.indexer] {
continue
}
read[st.indexer] = true
items, err := st.indexer.ByIndex(OwnerUIDIndex, uid)
if err != nil {
return nil, false
}
for _, item := range items {
if m, err := apimeta.Accessor(item); err == nil && wanted[m.GetNamespace()] {
hits = append(hits, item)
}
}
}
}
out := make([]apiruntime.Object, 0, len(hits))
for _, item := range hits {
if obj, ok := item.(apiruntime.Object); ok {
out = append(out, obj)
}
}
return out, true
}
// indexQuery is a read of keys from one of k8score's indexes: their index
// functions answer it with the keys themselves, so Indexer.Index returns the
// objects filed under any of them from one hold of the store's lock.
type indexQuery []string
// ReadableIndexed returns the objects of gvr in namespace that index holds
// under any of keys — for OwnerUIDIndex, those whose owner references name one
// of the UIDs — from the informers serving namespace now, under
// ReadableObject's rules. Each informer's store is read once for all the keys,
// so an object that moves from one key to another during the read is found
// under one of them, and an object filed under several is returned once.
// Several keys need one of k8score's own indexes. namespace "" reads only a
// cluster-wide informer, which holds every namespace of a namespaced
// resource. ok is also false when the serving informers lack the index. The
// objects are the cached ones: callers must not modify or keep them.
func (d *DynamicResourceCache) ReadableIndexed(gvr schema.GroupVersionResource, namespace, index string, keys ...string) (objs []*unstructured.Unstructured, ok bool) {
entries, ok := d.servedEntries(gvr, namespace)
if !ok {
return nil, false
}
for _, e := range entries {
var items []any
var err error
if len(keys) == 1 {
items, err = e.informer.GetIndexer().ByIndex(index, keys[0])
} else {
items, err = e.informer.GetIndexer().Index(index, indexQuery(keys))
}
if err != nil {
return nil, false
}
for _, item := range items {
if u, isUnstructured := item.(*unstructured.Unstructured); isUnstructured && (namespace == "" || u.GetNamespace() == namespace) {
objs = append(objs, u)
}
}
}
return objs, true
}