Ctrl K
ComunidadGuías…

pkg/ownership/view_golden_test.go

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

Archivo original ↗Preguntar a KiBot
package ownership_test

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"slices"
	"testing"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

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

var updateGolden = flag.Bool("update-ownership-golden", false, "rewrite the ownership view golden")

// The golden records every answer a View gives about every object of the
// ownership fixture, for a caller who may read everything and for one who
// may read only alpha and argocd. It was checked by hand when written; a
// change that alters an answer regenerates it with -update-ownership-golden
// and justifies each changed line.
func TestViewGolden(t *testing.T) {
	w := ownershiptest.Fixture()
	refs := make([]resourceid.Ref, 0, len(w.Objects))
	for _, o := range w.Objects {
		refs = append(refs, o.Ref)
	}
	slices.SortFunc(refs, func(a, b resourceid.Ref) int { return compareRef(a, b) })
	record := map[string][]goldenAnswer{}
	for name, caller := range map[string]ownership.Access{
		"everything":       ownership.Unrestricted(),
		"alpha and argocd": access{namespaces: []string{"alpha", "argocd"}},
	} {
		v := view(t, w, caller)
		for _, ref := range refs {
			record[name] = append(record[name], answer(v, ref))
		}
	}
	got, err := json.MarshalIndent(record, "", "  ")
	if err != nil {
		t.Fatal(err)
	}
	got = append(got, '\n')
	path := filepath.Join("testdata", "view-fixture.json")
	if *updateGolden {
		if err := os.MkdirAll("testdata", 0o755); err != nil {
			t.Fatal(err)
		}
		if err := os.WriteFile(path, got, 0o644); err != nil {
			t.Fatal(err)
		}
		return
	}
	want, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("%v (run with -update-ownership-golden to create it)", err)
	}
	if !bytes.Equal(got, want) {
		t.Fatalf("view answers differ from %s; rerun with -update-ownership-golden and justify each changed line", path)
	}
}

func compareRef(a, b resourceid.Ref) int {
	for _, c := range [][2]string{{a.Group, b.Group}, {a.Kind, b.Kind}, {a.Namespace, b.Namespace}, {a.Name, b.Name}} {
		if c[0] != c[1] {
			if c[0] < c[1] {
				return -1
			}
			return 1
		}
	}
	return 0
}

type goldenAnswer struct {
	Object     string   `json:"object"`
	Subject    string   `json:"subject"`
	References []string `json:"references,omitempty"`
	Owner      string   `json:"owner,omitempty"`
	Children   []string `json:"children,omitempty"`
	Coverage   []string `json:"childrenCoverage,omitempty"`
	Chain      string   `json:"controllerChain,omitempty"`
	Members    []string `json:"members,omitempty"`
	Membership string   `json:"membership,omitempty"`
}

func resolutionText(r ownership.OwnerResolution) string {
	s := r.State.String()
	if r.Ref != (resourceid.Ref{}) {
		s += " " + r.Ref.String()
	}
	if r.Gap.Reason != "" {
		s += fmt.Sprintf(" (%s %s/%s)", r.Gap.Reason, r.Gap.Kind, r.Gap.Namespace)
	}
	return s
}

func answer(v *ownership.View, ref resourceid.Ref) goldenAnswer {
	a := goldenAnswer{Object: ref.String()}
	subject, refs := v.References(ref)
	a.Subject = subject.Access.String()
	if subject.Access == ownership.Allowed {
		a.Subject = subject.Read.State.String()
		if subject.Read.State == ownership.Unavailable {
			a.Subject += " " + string(subject.Read.Gap.Reason)
		}
	}
	if !subject.Present() {
		return a
	}
	for _, r := range refs {
		a.References = append(a.References, resolutionText(r))
	}
	switch owner := v.Owner(ref); {
	case owner.Several:
		a.Owner = "several, none a controller"
	case owner.Reference != nil:
		a.Owner = fmt.Sprintf("reference %d: %s", referenceIndex(owner), resolutionText(owner.Resolution))
	}
	children := v.Children(ref)
	for _, c := range children.Children {
		s := c.Ref.String()
		if slices.ContainsFunc(c.OwnerReferences, func(r metav1.OwnerReference) bool {
			return r.UID == subject.Read.Object.UID && isControllerRef(r)
		}) {
			s += " (controller)"
		}
		a.Children = append(a.Children, s)
	}
	for _, g := range children.Gaps {
		a.Coverage = append(a.Coverage, fmt.Sprintf("not read: %s %s/%s", g.Reason, g.Kind, g.Namespace))
	}
	if children.Narrowed {
		a.Coverage = append(a.Coverage, "narrowed to what the caller may read")
	}
	chain := v.ControllerChain(ref)
	end := chain.Chain[len(chain.Chain)-1]
	switch {
	case chain.Cycle:
		a.Chain = "cycle after " + end.String()
	case chain.Truncated:
		a.Chain = "truncated at " + end.String()
	case chain.UnfollowedReference != nil:
		a.Chain = fmt.Sprintf("%s, stopped by %s", end, resolutionText(*chain.UnfollowedReference))
	case end != ref:
		a.Chain = end.String()
	}
	if ownership.IsRecordingManager(ref.GroupKind()) {
		m := v.Members(ref)
		a.Membership = fmt.Sprintf("%s; %d missing, %d withheld, %d unknown", m.Membership, m.Missing, m.Withheld, m.Unknown)
		for _, o := range m.Members {
			a.Members = append(a.Members, o.Ref.String())
		}
		for _, g := range m.Gaps {
			a.Membership += fmt.Sprintf("; not read: %s %s/%s", g.Reason, g.Kind, g.Namespace)
		}
	}
	return a
}