package ownership_test import ( "fmt" "testing" "github.com/skyhook-io/radar/pkg/ownership" "github.com/skyhook-io/radar/pkg/ownership/ownershiptest" "github.com/skyhook-io/radar/pkg/resourceid" ) const ( trackingID = "argocd.argoproj.io/tracking-id" instanceLabel = "argocd.argoproj.io/instance" helmRelease = "meta.helm.sh/release-name" helmNamespace = "meta.helm.sh/release-namespace" ) func app(name string) resourceid.Ref { return resourceid.Ref{Group: "argoproj.io", Kind: "Application", Namespace: "argocd", Name: name} } // checkOf returns the check of the claim naming manager. func checkOf(e ownership.ManagerEvidence, manager resourceid.Ref) ownership.ClaimCheck { for _, c := range e.Claims { if c.Manager == manager { return c.Check } } return "" } func TestManagerStates(t *testing.T) { recorded := ownership.MembersRecorded for _, tc := range []struct { name string build func(b *ownershiptest.Builder) resourceid.Ref state ownership.ManagerState lead *resourceid.Ref check func(t *testing.T, a ownership.ManagerAnswer) }{ { name: "listed: a list names it, its metadata names no one", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Add("", "ConfigMap", "shop", "settings") b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop", cm.Ref) return cm.Ref }, state: ownership.ManagerListed, lead: ref(app("shop")), }, { name: "agree: its tracking ID names the manager whose list names it", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/settings"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop", cm.Ref) return cm.Ref }, state: ownership.ManagerAgree, lead: ref(app("shop")), }, { // A label without a namespace matches the Application of its name // whose list names the object. name: "agree: an instance label without a namespace", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), map[string]string{instanceLabel: "shop"}, nil) b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop", cm.Ref) return cm.Ref }, state: ownership.ManagerAgree, lead: ref(app("shop")), }, { name: "disagree: its label names a manager whose readable list omits it", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{trackingID: "argocd_old:/ConfigMap:shop/settings"}) b.Manager(ownership.ArgoApplication, "argocd", "old", recorded, "shop") b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop", cm.Ref) return cm.Ref }, state: ownership.ManagerDisagree, check: func(t *testing.T, a ownership.ManagerAnswer) { if got := checkOf(a.Direct, app("old")); got != ownership.ClaimOmitted { t.Errorf("claim of old: %q, want omitted", got) } }, }, { // The claimed manager's list can't be read; still a disagreement. name: "disagree: a list names it and its label names an unreadable manager", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{trackingID: "argocd_old:/ConfigMap:shop/settings"}) b.Manager(ownership.ArgoApplication, "argocd", "old", ownership.MembersUnreadable, "shop") b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop", cm.Ref) return cm.Ref }, state: ownership.ManagerDisagree, check: func(t *testing.T, a ownership.ManagerAnswer) { if got := checkOf(a.Direct, app("old")); got != ownership.ClaimUnreadable { t.Errorf("claim of old: %q, want unreadable", got) } }, }, { name: "shared: two lists name it", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Add("", "ConfigMap", "shop", "settings") b.Manager(ownership.ArgoApplication, "argocd", "a", recorded, "shop", cm.Ref) b.Manager(ownership.ArgoApplication, "argocd", "b", recorded, "shop", cm.Ref) return cm.Ref }, state: ownership.ManagerShared, }, { name: "shared: the claimed one of two leads", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{trackingID: "argocd_b:/ConfigMap:shop/settings"}) b.Manager(ownership.ArgoApplication, "argocd", "a", recorded, "shop", cm.Ref) b.Manager(ownership.ArgoApplication, "argocd", "b", recorded, "shop", cm.Ref) return cm.Ref }, state: ownership.ManagerShared, lead: ref(app("b")), }, { name: "claimed: the claimed manager's list can't be read", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/settings"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", ownership.MembersUnreadable, "shop") return cm.Ref }, state: ownership.ManagerClaimed, lead: ref(app("shop")), check: func(t *testing.T, a ownership.ManagerAnswer) { if got := checkOf(a.Direct, app("shop")); got != ownership.ClaimUnreadable { t.Errorf("check %q, want unreadable", got) } }, }, { name: "claimed: the claimed manager applies elsewhere", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/settings"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", ownership.MembersRemote, "shop", cm.Ref) return cm.Ref }, state: ownership.ManagerClaimed, lead: ref(app("shop")), check: func(t *testing.T, a ownership.ManagerAnswer) { if got := checkOf(a.Direct, app("shop")); got != ownership.ClaimRemote { t.Errorf("check %q, want remote", got) } }, }, { // A readable list that omits it, and no other: the claim stands // unconfirmed rather than turning into none. name: "claimed: the claimed manager's readable list omits it", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/settings"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop") return cm.Ref }, state: ownership.ManagerClaimed, lead: ref(app("shop")), check: func(t *testing.T, a ownership.ManagerAnswer) { if got := checkOf(a.Direct, app("shop")); got != ownership.ClaimOmitted { t.Errorf("check %q, want omitted", got) } }, }, { name: "claimed: no such manager", build: func(b *ownershiptest.Builder) resourceid.Ref { return b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{trackingID: "argocd_gone:/ConfigMap:shop/settings"}).Ref }, state: ownership.ManagerClaimed, lead: ref(app("gone")), check: func(t *testing.T, a ownership.ManagerAnswer) { if got := checkOf(a.Direct, app("gone")); got != ownership.ClaimMissing { t.Errorf("check %q, want missing", got) } }, }, { name: "claimed: a native Helm release", build: func(b *ownershiptest.Builder) resourceid.Ref { return b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{helmRelease: "web", helmNamespace: "shop"}).Ref }, state: ownership.ManagerClaimed, lead: &resourceid.Ref{Kind: "HelmRelease", Namespace: "shop", Name: "web"}, }, { // Flux's helm-controller and Argo CD installs carry the Helm // release's annotations; they don't make a second manager. name: "listed: Helm annotations beside a list naming it", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{helmRelease: "web", helmNamespace: "shop"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop", cm.Ref) return cm.Ref }, state: ownership.ManagerListed, lead: ref(app("shop")), }, { // A tracking ID and an instance label name one Application, the // ID saying its namespace. name: "claimed: a tracking ID and an instance label naming one Application", build: func(b *ownershiptest.Builder) resourceid.Ref { return b.Meta(b.Add("", "ConfigMap", "shop", "settings"), map[string]string{instanceLabel: "shop"}, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/settings"}).Ref }, state: ownership.ManagerClaimed, lead: ref(app("shop")), }, { name: "claimed: two claims naming different managers, no list", build: func(b *ownershiptest.Builder) resourceid.Ref { return b.Meta(b.Add("", "ConfigMap", "shop", "settings"), map[string]string{"kustomize.toolkit.fluxcd.io/name": "apps", "kustomize.toolkit.fluxcd.io/namespace": "flux-system"}, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/settings"}).Ref }, state: ownership.ManagerClaimed, }, { name: "none: copied tracking ID only, everything read", build: func(b *ownershiptest.Builder) resourceid.Ref { return b.Meta(b.Add("", "ConfigMap", "shop", "copy"), nil, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/settings"}).Ref }, state: ownership.ManagerNone, }, { name: "unknown: no evidence, and a list Radar can't read", build: func(b *ownershiptest.Builder) resourceid.Ref { cm := b.Add("", "ConfigMap", "shop", "settings") b.Manager(ownership.ArgoApplication, "argocd", "broken", ownership.MembersUnreadable, "shop") return cm.Ref }, state: ownership.ManagerUnknown, }, { name: "none: no evidence anywhere", build: func(b *ownershiptest.Builder) resourceid.Ref { return b.Add("", "ConfigMap", "shop", "settings").Ref }, state: ownership.ManagerNone, }, { // The fleet's commonest case: the Pod inherits its template's // instance label; only its Deployment is listed. name: "via: a Pod whose Deployment a list names", build: func(b *ownershiptest.Builder) resourceid.Ref { d := b.Add("apps", "Deployment", "shop", "web") rs := b.Add("apps", "ReplicaSet", "shop", "web-1", d.OwnerRef(true)) pod := b.Meta(b.Add("", "Pod", "shop", "web-1-a", rs.OwnerRef(true)), map[string]string{instanceLabel: "shop"}, nil) b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop", d.Ref) return pod.Ref }, state: ownership.ManagerListed, lead: ref(app("shop")), check: func(t *testing.T, a ownership.ManagerAnswer) { if a.Via == nil || a.Via.Object.Kind != "Deployment" { t.Fatalf("via %+v, want the Deployment", a.Via) } if len(a.Direct.Claims) != 1 || !a.Direct.Claims[0].Weak { t.Errorf("direct claims %+v, want the Pod's label kept as weak", a.Direct.Claims) } }, }, { // A Deployment copies its annotations, a Helm release's among them, // onto its ReplicaSets; the list naming the Deployment answers. name: "via: a ReplicaSet with its Deployment's Helm annotations", build: func(b *ownershiptest.Builder) resourceid.Ref { helm := map[string]string{helmRelease: "web", helmNamespace: "shop"} d := b.Meta(b.Add("apps", "Deployment", "shop", "web"), nil, helm) rs := b.Meta(b.Add("apps", "ReplicaSet", "shop", "web-1", d.OwnerRef(true)), nil, helm) b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop", d.Ref) return rs.Ref }, state: ownership.ManagerListed, lead: ref(app("shop")), }, { // A self-referencing tracking ID on the object is its own answer, // whatever manages its controller. name: "direct: an object's own tracking ID stops the walk", build: func(b *ownershiptest.Builder) resourceid.Ref { d := b.Add("apps", "Deployment", "shop", "web") rs := b.Meta(b.Add("apps", "ReplicaSet", "shop", "web-1", d.OwnerRef(true)), nil, map[string]string{trackingID: "argocd_other:apps/ReplicaSet:shop/web-1"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", recorded, "shop", d.Ref) b.Manager(ownership.ArgoApplication, "argocd", "other", ownership.MembersUnreadable, "shop") return rs.Ref }, state: ownership.ManagerClaimed, lead: ref(app("other")), check: func(t *testing.T, a ownership.ManagerAnswer) { if a.Via != nil { t.Errorf("via %+v, want none", a.Via) } }, }, { // A gone controller ends the chain with an answer: nothing above // it was missed. name: "none: the walk stops at a controller that's gone", build: func(b *ownershiptest.Builder) resourceid.Ref { gone := ownershiptest.Object{Ref: resourceid.Ref{Group: "apps", Kind: "ReplicaSet", Namespace: "shop", Name: "web-1"}, APIVersion: "apps/v1", UID: "uid-gone"} return b.Add("", "Pod", "shop", "web-1-a", gone.OwnerRef(true)).Ref }, state: ownership.ManagerNone, check: func(t *testing.T, a ownership.ManagerAnswer) { if a.ChainEnd != ownership.ChainEndUnfollowed { t.Errorf("chain end %q, want unfollowed", a.ChainEnd) } }, }, { // Each manager answers for what its own list names: the parent // for the child Application, not for the child's members. name: "app of apps: a child Application is managed by its parent", build: func(b *ownershiptest.Builder) resourceid.Ref { child := b.Manager(ownership.ArgoApplication, "argocd", "child", recorded, "shop") b.Manager(ownership.ArgoApplication, "argocd", "parent", recorded, "argocd", child.Ref) return child.Ref }, state: ownership.ManagerListed, lead: ref(app("parent")), }, } { t.Run(tc.name, func(t *testing.T) { b := ownershiptest.NewBuilder() subject := tc.build(b) got := view(t, b.World(), ownership.Unrestricted()).Manager(subject) if got.State != tc.state { t.Fatalf("state %q, want %q (direct %+v, via %+v)", got.State, tc.state, got.Direct, got.Via) } if (got.Lead == nil) != (tc.lead == nil) || got.Lead != nil && *got.Lead != *tc.lead { t.Fatalf("lead %v, want %v", got.Lead, tc.lead) } if got.Narrowed { t.Error("narrowed for an unrestricted caller") } if tc.check != nil { tc.check(t, got) } }) } } // A caller who may not read Applications sees the claim, but nothing of the // manager it names, and an answer marked narrowed. func TestManagerForACallerWhoMayNotReadManagers(t *testing.T) { b := ownershiptest.NewBuilder() cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), nil, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/settings"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", ownership.MembersRecorded, "shop", cm.Ref) got := view(t, b.World(), access{denied: []resourceid.GroupKind{ownership.ArgoApplication}}).Manager(cm.Ref) if got.State != ownership.ManagerClaimed || len(got.Direct.Recorders) != 0 || !got.Narrowed { t.Fatalf("answer %+v; want claimed, no recorders, narrowed", got) } if c := checkOf(got.Direct, app("shop")); c != ownership.ClaimWithheld { t.Fatalf("check %q, want withheld", c) } } func ref(r resourceid.Ref) *resourceid.Ref { return &r } // Every claim must name the one recorder for agreement: one that names // another manager makes a disagreement, whichever source it comes from. func TestManagerAgreementNeedsEveryClaim(t *testing.T) { b := ownershiptest.NewBuilder() cm := b.Meta(b.Add("", "ConfigMap", "shop", "settings"), map[string]string{"kustomize.toolkit.fluxcd.io/name": "apps", "kustomize.toolkit.fluxcd.io/namespace": "flux-system"}, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/settings"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", ownership.MembersRecorded, "shop", cm.Ref) if got := view(t, b.World(), ownership.Unrestricted()).Manager(cm.Ref); got.State != ownership.ManagerDisagree || got.Lead != nil { t.Fatalf("answer %+v; want a disagreement with no lead", got) } } // Weak claims at each hop let the walk go on, and are kept on the way. func TestManagerKeepsWeakEvidenceAlongTheWalk(t *testing.T) { b := ownershiptest.NewBuilder() d := b.Add("apps", "Deployment", "shop", "web") rs := b.Meta(b.Add("apps", "ReplicaSet", "shop", "web-1", d.OwnerRef(true)), map[string]string{instanceLabel: "other"}, nil) pod := b.Add("", "Pod", "shop", "web-1-a", rs.OwnerRef(true)) b.Manager(ownership.ArgoApplication, "argocd", "shop", ownership.MembersRecorded, "shop", d.Ref) got := view(t, b.World(), ownership.Unrestricted()).Manager(pod.Ref) if got.State != ownership.ManagerListed || got.Via == nil || got.Via.Object != d.Ref { t.Fatalf("answer %+v; want listed via the Deployment", got) } if len(got.Between) != 1 || got.Between[0].Object != rs.Ref || !got.Between[0].Claims[0].Weak { t.Fatalf("between %+v; want the ReplicaSet's weak claim", got.Between) } } // When no object settles it, a claim the walk passed answers: the Pod is // tracked through its ReplicaSet, not unmanaged. func TestManagerFallsBackToAWeakClaimOnTheWay(t *testing.T) { b := ownershiptest.NewBuilder() d := b.Add("apps", "Deployment", "shop", "web") rs := b.Meta(b.Add("apps", "ReplicaSet", "shop", "web-1", d.OwnerRef(true)), map[string]string{instanceLabel: "argocd_shop"}, nil) pod := b.Add("", "Pod", "shop", "web-1-a", rs.OwnerRef(true)) got := view(t, b.World(), ownership.Unrestricted()).Manager(pod.Ref) if got.State != ownership.ManagerClaimed || got.Via == nil || got.Via.Object != rs.Ref || len(got.Between) != 0 { t.Fatalf("answer %+v; want claimed via the ReplicaSet", got) } } // A controller the caller may not read stops the walk: the answer says so, // and nothing of the controller. func TestManagerStopsAtAWithheldController(t *testing.T) { b := ownershiptest.NewBuilder() d := b.Add("apps", "Deployment", "shop", "web") rs := b.Add("apps", "ReplicaSet", "shop", "web-1", d.OwnerRef(true)) pod := b.Add("", "Pod", "shop", "web-1-a", rs.OwnerRef(true)) b.Manager(ownership.ArgoApplication, "argocd", "shop", ownership.MembersRecorded, "shop", d.Ref) got := view(t, b.World(), access{denied: []resourceid.GroupKind{{Group: "apps", Kind: "ReplicaSet"}}}).Manager(pod.Ref) if got.State != ownership.ManagerUnknown || got.ChainEnd != ownership.ChainEndUnfollowed || got.Stop == nil || got.Stop.Owner.State != ownership.OwnerWithheld || got.Via != nil { t.Fatalf("answer %+v; want unknown, stopped at a withheld controller", got) } } // Radar couldn't tell whether the caller may read Applications everywhere // they are loading: no evidence is unknown, not none. func TestManagerWithUndecidedAccessIsUnknown(t *testing.T) { b := ownershiptest.NewBuilder() cm := b.Add("", "ConfigMap", "shop", "settings") w := b.World() c := w.Collection[ownership.ArgoApplication] c.State = ownershiptest.Loading w.Collection[ownership.ArgoApplication] = c got := view(t, w, wholeKindUndecided{ownership.Unrestricted()}).Manager(cm.Ref) if got.State != ownership.ManagerUnknown || !got.Direct.Undecided { t.Fatalf("answer %+v; want unknown with undecided access", got) } } // A controller of a kind discovery can't map stops the walk; the stop keeps // the reference that names it, from the metadata of an object the caller reads. func TestManagerStopKeepsAnUnmappedControllersReference(t *testing.T) { b := ownershiptest.NewBuilder() parent := ownershiptest.Object{Ref: resourceid.Ref{Group: "missing.example.io", Kind: "Parent", Namespace: "shop", Name: "controller-x"}, APIVersion: "missing.example.io/v1", UID: "uid-parent"} pod := b.Add("", "Pod", "shop", "web-1-a", parent.OwnerRef(true)) got := view(t, b.World(), ownership.Unrestricted()).Manager(pod.Ref) if got.Stop == nil || got.Stop.Owner.State != ownership.OwnerUnknown || got.Stop.Reference.Kind != "Parent" || got.Stop.Reference.Name != "controller-x" { t.Fatalf("stop %+v; want the unmapped Parent's reference", got.Stop) } } // Checking a stale claim against a large manager reads that manager's list, // not every object in it. func TestManagerChecksAClaimWithoutReadingItsManagersMembers(t *testing.T) { b := ownershiptest.NewBuilder() var members []resourceid.Ref for i := range 50 { members = append(members, b.Add("", "ConfigMap", "shop", fmt.Sprint("listed-", i)).Ref) } cm := b.Meta(b.Add("", "ConfigMap", "shop", "stale"), nil, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/stale"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", ownership.MembersRecorded, "shop", members...) s, err := ownershiptest.Scan(b.World()).Begin() if err != nil { t.Fatal(err) } counted := &objectCounter{ReadSession: s} got := ownership.NewView(counted, ownership.Unrestricted()).Manager(cm.Ref) if got.State != ownership.ManagerClaimed || checkOf(got.Direct, app("shop")) != ownership.ClaimOmitted { t.Fatalf("answer %+v; want the claim checked as omitted", got) } if counted.objects > 3 { t.Fatalf("%d object reads; want the subject and the manager, not the manager's members", counted.objects) } } type objectCounter struct { ownership.ReadSession objects int memberships int } func (s *objectCounter) Object(ref resourceid.Ref) ownership.ObjectRead { s.objects++ return s.ReadSession.Object(ref) } func (s *objectCounter) Membership(ref resourceid.Ref) ownership.MembersRead { s.memberships++ return s.ReadSession.Membership(ref) } func TestManagerSharesClaimMembershipWithinAView(t *testing.T) { b := ownershiptest.NewBuilder() a := b.Meta(b.Add("", "ConfigMap", "shop", "a"), nil, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/a"}) bb := b.Meta(b.Add("", "ConfigMap", "shop", "b"), nil, map[string]string{trackingID: "argocd_shop:/ConfigMap:shop/b"}) b.Manager(ownership.ArgoApplication, "argocd", "shop", ownership.MembersRecorded, "shop") s, err := ownershiptest.Scan(b.World()).Begin() if err != nil { t.Fatal(err) } counted := &objectCounter{ReadSession: s} for request := range 2 { v := ownership.NewView(counted, ownership.Unrestricted()) for _, row := range []resourceid.Ref{a.Ref, bb.Ref} { answer := v.Manager(row) if answer.State != ownership.ManagerClaimed || checkOf(answer.Direct, app("shop")) != ownership.ClaimOmitted { t.Fatalf("answer %+v; want a checked omitted claim", answer) } } if counted.memberships != request+1 { t.Fatalf("membership reads %d; want one per request", counted.memberships) } } } // A Pod whose template carries the Flux origin labels of an outer // Kustomization is managed by the HelmRelease that records its Deployment, the // nearer manager; without that record its own labels still answer. func TestManagerWalksPastFluxLabelsAControllerCopied(t *testing.T) { outer := map[string]string{"kustomize.toolkit.fluxcd.io/name": "flux-system", "kustomize.toolkit.fluxcd.io/namespace": "flux-system"} for _, recordedByRelease := range []bool{true, false} { b := ownershiptest.NewBuilder() d := b.Add("apps", "Deployment", "apps", "web") rs := b.Meta(b.Add("apps", "ReplicaSet", "apps", "web-1", d.OwnerRef(true)), outer, nil) pod := b.Meta(b.Add("", "Pod", "apps", "web-1-a", rs.OwnerRef(true)), outer, nil) b.Manager(ownership.FluxKustomization, "flux-system", "flux-system", ownership.MembersRecorded, "") var members []resourceid.Ref if recordedByRelease { members = append(members, d.Ref) } b.Manager(ownership.FluxHelmRelease, "apps", "web", ownership.MembersRecorded, "", members...) got := view(t, b.World(), ownership.Unrestricted()).Manager(pod.Ref) release := resourceid.Ref{Group: "helm.toolkit.fluxcd.io", Kind: "HelmRelease", Namespace: "apps", Name: "web"} kustomization := resourceid.Ref{Group: "kustomize.toolkit.fluxcd.io", Kind: "Kustomization", Namespace: "flux-system", Name: "flux-system"} switch { case recordedByRelease && (got.State != ownership.ManagerListed || got.Lead == nil || *got.Lead != release || got.Via == nil || got.Via.Object != d.Ref): t.Fatalf("recorded: answer %+v; want listed via the Deployment, led by its HelmRelease", got) case !recordedByRelease && (got.State != ownership.ManagerClaimed || got.Lead == nil || *got.Lead != kustomization): t.Fatalf("unrecorded: answer %+v; want the Pod's own claim", got) } } } // A Pod Flux labelled and a ReplicaSet later adopted: the HelmRelease records // no inventory, so nothing says the labels were copied, and they answer. func TestManagerKeepsFluxLabelsWhoseListCannotBeRead(t *testing.T) { b := ownershiptest.NewBuilder() d := b.Add("apps", "Deployment", "apps", "web") rs := b.Add("apps", "ReplicaSet", "apps", "web-1", d.OwnerRef(true)) pod := b.Meta(b.Add("", "Pod", "apps", "adopted", rs.OwnerRef(true)), map[string]string{"helm.toolkit.fluxcd.io/name": "legacy", "helm.toolkit.fluxcd.io/namespace": "apps"}, nil) b.Manager(ownership.FluxHelmRelease, "apps", "legacy", ownership.MembersUnreadable, "") b.Manager(ownership.FluxKustomization, "flux-system", "apps", ownership.MembersRecorded, "", d.Ref) got := view(t, b.World(), ownership.Unrestricted()).Manager(pod.Ref) legacy := resourceid.Ref{Group: "helm.toolkit.fluxcd.io", Kind: "HelmRelease", Namespace: "apps", Name: "legacy"} if got.Via != nil || len(got.Direct.Claims) != 1 || got.Direct.Claims[0].Weak || got.Lead == nil || *got.Lead != legacy { t.Fatalf("answer %+v; want the Pod's own unreadable-list claim to answer", got) } }