Ctrl K
ComunidadGuías…

pkg/k8score/gitops_members_index.go

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

Archivo original ↗Preguntar a KiBot
package k8score

import (
	"strings"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/tools/cache"

	"github.com/skyhook-io/radar/pkg/gitops"
	"github.com/skyhook-io/radar/pkg/resourceid"
)

// GitOpsMembersIndex indexes Argo CD Applications and Flux Kustomizations and
// HelmReleases by the objects their status lists, so the managers recording
// one object are found without reading every manager's list. A manager whose
// list can't be read is filed under UnreadableMembersKey instead.
const GitOpsMembersIndex = "gitopsMembers"

// UnreadableMembersKey is the GitOpsMembersIndex key of the managers whose
// status lists no members Radar can trust, any of which may name any object.
// A manager that applies to another cluster is filed under no key when its
// list can't be read: it can name no object here.
const UnreadableMembersKey = "\x00unreadable"

// MemberPlacement is one way to place a status entry: the namespace its object
// is taken to be in. A built-in kind's scope is fixed, so the index files its
// entries by the object they mean. For any other kind the index can't ask
// discovery the scope — an index function runs when an object changes, not
// when discovery does — so it files an entry under each placement the kind's
// scope could call for, and a reader looks up only the placements the scope
// discovery serves the kind with calls for.
type MemberPlacement byte

const (
	// MemberMeant: the object an entry of a built-in kind names.
	MemberMeant MemberPlacement = 'm'
	// MemberAsRecorded: the namespace the entry records.
	MemberAsRecorded MemberPlacement = 'r'
	// MemberClusterScoped: an entry recording a namespace, read for a
	// cluster-scoped kind, which ignores it.
	MemberClusterScoped MemberPlacement = 'c'
	// MemberInDestination: an Argo CD entry recording no namespace, read for
	// a namespaced kind, which is then in the Application's destination
	// namespace.
	MemberInDestination MemberPlacement = 'd'
)

// MemberKey is the GitOpsMembersIndex key of an entry naming group, kind,
// namespace and name, read r's way.
func MemberKey(r MemberPlacement, group, kind, namespace, name string) string {
	return strings.Join([]string{string(r), group, kind, namespace, name}, "\x00")
}

// gitopsMembersIndexers are the GitOps managers' members indexes, by resource,
// whatever version it is watched at.
var gitopsMembersIndexers = map[schema.GroupResource]cache.Indexers{
	{Group: "argoproj.io", Resource: "applications"}:                   {GitOpsMembersIndex: memberKeys(gitops.ArgoMembers, gitops.ArgoAppliesElsewhere)},
	{Group: "helm.toolkit.fluxcd.io", Resource: "helmreleases"}:        {GitOpsMembersIndex: memberKeys(gitops.FluxMembers, fluxAppliesElsewhere)},
	{Group: "kustomize.toolkit.fluxcd.io", Resource: "kustomizations"}: {GitOpsMembersIndex: memberKeys(gitops.FluxMembers, fluxAppliesElsewhere)},
}

func fluxAppliesElsewhere(obj *unstructured.Unstructured) bool {
	return !gitops.FluxTargetsLocalCluster(obj)
}

// memberKeys files a manager under each entry of its list, in every placement
// the entry's kind's scope could call for.
func memberKeys(members func(*unstructured.Unstructured) (gitops.MemberList, bool), elsewhere func(*unstructured.Unstructured) bool) cache.IndexFunc {
	return func(obj any) ([]string, error) {
		if q, isQuery := obj.(indexQuery); isQuery {
			return q, nil
		}
		u, ok := obj.(*unstructured.Unstructured)
		if !ok {
			return nil, nil
		}
		list, readable := members(u)
		switch {
		case !readable && elsewhere(u):
			return nil, nil
		case !readable:
			return []string{UnreadableMembersKey}, nil
		}
		keys := make([]string, 0, len(list.Entries))
		for _, e := range list.Entries {
			if b, builtin := resourceid.BuiltinForKind(e.Kind); builtin && b.Group == e.Group {
				keys = append(keys, MemberKey(MemberMeant, e.Group, e.Kind, gitops.MemberNamespace(b.Namespaced, e.Namespace, list.DestinationNamespace), e.Name))
				continue
			}
			keys = append(keys, MemberKey(MemberAsRecorded, e.Group, e.Kind, e.Namespace, e.Name))
			switch {
			case e.Namespace != "":
				keys = append(keys, MemberKey(MemberClusterScoped, e.Group, e.Kind, "", e.Name))
			case list.DestinationNamespace != "":
				keys = append(keys, MemberKey(MemberInDestination, e.Group, e.Kind, list.DestinationNamespace, e.Name))
			}
		}
		return keys, nil
	}
}