Files
argo-cd/gitops-engine/pkg/cache/resource.go
Leonardo Luz Almeida bcc0243f1e prepare repo for migration to ArgoCD repo
Signed-off-by: Leonardo Luz Almeida <leonardo_almeida@intuit.com>
2025-09-23 10:05:42 -04:00

108 lines
3.2 KiB
Go

package cache
import (
"fmt"
"k8s.io/apimachinery/pkg/types"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
)
// Resource holds the information about Kubernetes resource, ownership references and optional information
type Resource struct {
// ResourceVersion holds most recent observed resource version
ResourceVersion string
// Resource reference
Ref corev1.ObjectReference
// References to resource owners
OwnerRefs []metav1.OwnerReference
// Optional creation timestamp of the resource
CreationTimestamp *metav1.Time
// Optional additional information about the resource
Info any
// Optional whole resource manifest
Resource *unstructured.Unstructured
// answers if resource is inferred parent of provided resource
isInferredParentOf func(key kube.ResourceKey) bool
}
func (r *Resource) ResourceKey() kube.ResourceKey {
return kube.NewResourceKey(r.Ref.GroupVersionKind().Group, r.Ref.Kind, r.Ref.Namespace, r.Ref.Name)
}
func (r *Resource) isParentOf(child *Resource) bool {
for i, ownerRef := range child.OwnerRefs {
// backfill UID of inferred owner child references
if ownerRef.UID == "" && r.Ref.Kind == ownerRef.Kind && r.Ref.APIVersion == ownerRef.APIVersion && r.Ref.Name == ownerRef.Name {
ownerRef.UID = r.Ref.UID
child.OwnerRefs[i] = ownerRef
return true
}
if r.Ref.UID == ownerRef.UID {
return true
}
}
return false
}
// setOwnerRef adds or removes specified owner reference
func (r *Resource) setOwnerRef(ref metav1.OwnerReference, add bool) {
index := -1
for i, item := range r.OwnerRefs {
if item.UID == ref.UID {
index = i
break
}
}
added := index > -1
if add != added {
if add {
r.OwnerRefs = append(r.OwnerRefs, ref)
} else {
r.OwnerRefs = append(r.OwnerRefs[:index], r.OwnerRefs[index+1:]...)
}
}
}
func (r *Resource) toOwnerRef() metav1.OwnerReference {
return metav1.OwnerReference{UID: r.Ref.UID, Name: r.Ref.Name, Kind: r.Ref.Kind, APIVersion: r.Ref.APIVersion}
}
// iterateChildrenV2 is a depth-first traversal of the graph of resources starting from the current resource.
func (r *Resource) iterateChildrenV2(graph map[kube.ResourceKey]map[types.UID]*Resource, ns map[kube.ResourceKey]*Resource, visited map[kube.ResourceKey]int, action func(err error, child *Resource, namespaceResources map[kube.ResourceKey]*Resource) bool) {
key := r.ResourceKey()
if visited[key] == 2 {
return
}
// this indicates that we've started processing this node's children
visited[key] = 1
defer func() {
// this indicates that we've finished processing this node's children
visited[key] = 2
}()
children, ok := graph[key]
if !ok || children == nil {
return
}
for _, c := range children {
childKey := c.ResourceKey()
child := ns[childKey]
switch visited[childKey] {
case 1:
// Since we encountered a node that we're currently processing, we know we have a circular dependency.
_ = action(fmt.Errorf("circular dependency detected. %s is child and parent of %s", childKey.String(), key.String()), child, ns)
case 0:
if action(nil, child, ns) {
child.iterateChildrenV2(graph, ns, visited, action)
}
}
}
}