diff --git a/controller/cache/cluster.go b/controller/cache/cluster.go index 3bb41321d6..42c10a7c34 100644 --- a/controller/cache/cluster.go +++ b/controller/cache/cluster.go @@ -169,7 +169,7 @@ func (c *clusterInfo) getChildren(obj *unstructured.Unstructured) []appv1.Resour nsNodes := c.nsIndex[obj.GetNamespace()] for _, child := range nsNodes { if objInfo.isParentOf(child) { - children = append(children, child.childResourceNodes(nsNodes)) + children = append(children, child.childResourceNodes(nsNodes, map[kube.ResourceKey]bool{objInfo.resourceKey(): true})) } } } diff --git a/controller/cache/cluster_test.go b/controller/cache/cluster_test.go index dab19dce83..031a6eebb9 100644 --- a/controller/cache/cluster_test.go +++ b/controller/cache/cluster_test.go @@ -258,3 +258,19 @@ func TestUpdateAppResource(t *testing.T) { assert.Equal(t, []string{"helm-guestbook"}, updatesReceived) } + +func TestCircularReference(t *testing.T) { + dep := testDeploy.DeepCopy() + dep.SetOwnerReferences([]metav1.OwnerReference{{ + Name: testPod.GetName(), + Kind: testPod.GetKind(), + APIVersion: testPod.GetAPIVersion(), + }}) + cluster := newCluster(testPod, testRS, dep) + err := cluster.ensureSynced() + + assert.Nil(t, err) + + children := cluster.getChildren(dep) + assert.Len(t, children, 1) +} diff --git a/controller/cache/node.go b/controller/cache/node.go index 2c1fe22fde..20c7a3abb0 100644 --- a/controller/cache/node.go +++ b/controller/cache/node.go @@ -1,8 +1,11 @@ package cache import ( + log "github.com/sirupsen/logrus" + appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" "github.com/argoproj/argo-cd/util/kube" + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -57,11 +60,27 @@ func (n *node) getApp(ns map[kube.ResourceKey]*node) string { return "" } -func (n *node) childResourceNodes(ns map[kube.ResourceKey]*node) appv1.ResourceNode { +func newResourceKeySet(set map[kube.ResourceKey]bool, keys ...kube.ResourceKey) map[kube.ResourceKey]bool { + newSet := make(map[kube.ResourceKey]bool) + for k, v := range set { + newSet[k] = v + } + for i := range keys { + newSet[keys[i]] = true + } + return newSet +} + +func (n *node) childResourceNodes(ns map[kube.ResourceKey]*node, parents map[kube.ResourceKey]bool) appv1.ResourceNode { children := make([]appv1.ResourceNode, 0) - for key := range ns { - if n.isParentOf(ns[key]) { - children = append(children, ns[key].childResourceNodes(ns)) + for childKey := range ns { + if n.isParentOf(ns[childKey]) { + if parents[childKey] { + key := n.resourceKey() + log.Warnf("Circular dependency detected. %s is child and parent of %s", childKey.String(), key.String()) + } else { + children = append(children, ns[childKey].childResourceNodes(ns, newResourceKeySet(parents, n.resourceKey()))) + } } } gv, err := schema.ParseGroupVersion(n.ref.APIVersion)