Ctrl K
ComunidadGuías…

internal/gitops/ownership_test.go

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

Archivo original ↗Preguntar a KiBot
package gitops

import (
	"slices"
	"strings"
	"testing"

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

	"github.com/sophotechlabs/spinoza/internal/api"
)

func fieldsEntry(manager, fields string) metav1.ManagedFieldsEntry {
	return metav1.ManagedFieldsEntry{
		Manager:    manager,
		Operation:  metav1.ManagedFieldsOperationApply,
		FieldsType: "FieldsV1",
		FieldsV1:   metav1.NewFieldsV1(fields),
	}
}

func ssaObject(entries ...metav1.ManagedFieldsEntry) *unstructured.Unstructured {
	live := &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "apps/v1",
		"kind":       "Deployment",
		"metadata":   map[string]any{"name": "web", "namespace": "shop"},
		"spec":       map[string]any{"replicas": int64(3)},
	}}
	live.SetManagedFields(entries)
	return live
}

func ownersOf(t *testing.T, live *unstructured.Unstructured, controller string) map[string]string {
	t.Helper()
	found, _ := Ownership(live, controller)
	out := map[string]string{}
	for _, one := range found {
		out[one.Path] = one.Declared + " -> " + one.Live
	}
	return out
}

func TestOwnershipNamesAFieldAnotherManagerTookFromArgo(t *testing.T) {
	live := ssaObject(
		fieldsEntry("argocd-controller", `{"f:spec":{"f:paused":{}}}`),
		fieldsEntry("kubectl-edit", `{"f:spec":{"f:replicas":{}}}`),
	)

	got := ownersOf(t, live, api.ControllerArgo)

	if got["spec.replicas"] != "argocd-controller -> kubectl-edit" {
		t.Fatalf("ownership = %v, want spec.replicas taken by kubectl-edit", got)
	}
	if len(got) != 1 {
		t.Fatalf("ownership = %v, want only what another writer holds", got)
	}
}

func TestOwnershipNamesAFieldTakenFromFlux(t *testing.T) {
	live := ssaObject(
		fieldsEntry("kustomize-controller", `{"f:spec":{"f:replicas":{}}}`),
		fieldsEntry("kubectl-edit", `{"f:spec":{"f:replicas":{}}}`),
	)

	got := ownersOf(t, live, api.ControllerFlux)

	if got["spec.replicas"] != "kustomize-controller -> kubectl-edit" {
		t.Fatalf("ownership = %v", got)
	}
}

func TestOwnershipReadsTheApplicationControllerNameToo(t *testing.T) {
	live := ssaObject(
		fieldsEntry("argocd-application-controller", `{"f:spec":{"f:replicas":{}}}`),
		fieldsEntry("kubectl-edit", `{"f:spec":{"f:replicas":{}}}`),
	)

	if got := ownersOf(t, live, api.ControllerArgo); len(got) != 1 {
		t.Fatalf("ownership = %v, want the statefulset controller understood", got)
	}
}

func TestOwnershipNamesASpecFieldTheControllerDoesNotHoldAtAll(t *testing.T) {
	live := ssaObject(
		fieldsEntry("argocd-controller", `{"f:spec":{"f:replicas":{}}}`),
		fieldsEntry("some-operator", `{"f:spec":{"f:template":{"f:metadata":{"f:labels":{}}}}}`),
	)

	got := ownersOf(t, live, api.ControllerArgo)

	want := "argocd-controller -> some-operator"
	if got["spec.template.metadata.labels"] != want {
		t.Fatalf("ownership = %v, want the field the controller does not hold", got)
	}
}

func TestOwnershipReadsWhatARealApiServerWrote(t *testing.T) {
	argo := `{"f:spec":{"f:selector":{},"f:template":{"f:spec":{"f:containers":` +
		`{"k:{\"name\":\"pause\"}":{".":{},"f:image":{},"f:name":{}}}}}}}`
	live := ssaObject(
		fieldsEntry("argocd-controller", argo),
		fieldsEntry("kubectl-edit", `{"f:spec":{"f:replicas":{}}}`),
	)

	got := ownersOf(t, live, api.ControllerArgo)

	if len(got) != 1 {
		t.Fatalf("ownership = %v, want the one field the other writer took", got)
	}
	if got["spec.replicas"] != "argocd-controller -> kubectl-edit" {
		t.Fatalf("ownership = %v", got)
	}
}

func TestOwnershipSaysSoWhenNothingHasMoved(t *testing.T) {
	live := ssaObject(fieldsEntry("argocd-controller", `{"f:spec":{"f:replicas":{}}}`))

	found, note := Ownership(live, api.ControllerArgo)

	if found != nil {
		t.Fatalf("ownership = %v, want none", found)
	}
	if !strings.HasPrefix(note, heldNote) {
		t.Fatalf("note = %q, want it to say nothing else holds a spec field", note)
	}
	if !strings.HasSuffix(note, "argocd-controller") {
		t.Fatalf("note = %q, want the manager named", note)
	}
}

func TestOwnershipFallsBackToTheOldNoteWhenNoGitopsManagerIsThere(t *testing.T) {
	live := ssaObject(fieldsEntry("kubectl-client-side-apply", `{"f:spec":{"f:replicas":{}}}`))

	found, note := Ownership(live, api.ControllerArgo)

	if found != nil {
		t.Fatalf("ownership = %v, want none", found)
	}
	if note != noDeclaration {
		t.Fatalf("note = %q, want the note that says there is nothing to compare", note)
	}
}

func TestOwnershipIgnoresAStatusWriter(t *testing.T) {
	entries := []metav1.ManagedFieldsEntry{
		fieldsEntry("argocd-controller", `{"f:spec":{"f:replicas":{}}}`),
		fieldsEntry("kube-controller-manager", `{"f:spec":{"f:replicas":{}}}`),
	}
	entries[1].Subresource = "status"
	live := ssaObject(entries...)

	if got := ownersOf(t, live, api.ControllerArgo); len(got) != 0 {
		t.Fatalf("ownership = %v, want a status writer left out of it", got)
	}
}

func TestOwnershipSkipsTheControllersOwnStatusEntry(t *testing.T) {
	entries := []metav1.ManagedFieldsEntry{
		fieldsEntry("argocd-controller", `{"f:status":{"f:replicas":{}}}`),
		fieldsEntry("argocd-controller", `{"f:spec":{"f:paused":{}}}`),
		fieldsEntry("kubectl-edit", `{"f:spec":{"f:replicas":{}}}`),
	}
	entries[0].Subresource = "status"
	live := ssaObject(entries...)

	if got := ownersOf(t, live, api.ControllerArgo); len(got) != 1 {
		t.Fatalf("ownership = %v, want the spec entry used, not the status one", got)
	}
}

func TestOwnershipCountsAFieldOnceWhenTwoWritersHoldIt(t *testing.T) {
	live := ssaObject(
		fieldsEntry("argocd-controller", `{"f:spec":{"f:paused":{}}}`),
		fieldsEntry("kubectl-edit", `{"f:spec":{"f:replicas":{}}}`),
		fieldsEntry("some-operator", `{"f:spec":{"f:replicas":{}}}`),
	)

	found, _ := Ownership(live, api.ControllerArgo)

	if len(found) != 1 {
		t.Fatalf("ownership = %+v, want one row for one field", found)
	}
	if found[0].Live != "kubectl-edit" {
		t.Fatalf("taken by = %q, want the first writer that holds it", found[0].Live)
	}
}

func TestFieldPathsReadTheShapesTheApiServerWrites(t *testing.T) {
	cases := []struct {
		name   string
		fields string
		want   string
	}{
		{
			name:   "a plain field",
			fields: `{"f:spec":{"f:replicas":{}}}`,
			want:   "spec.replicas",
		},
		{
			name:   "a nested field",
			fields: `{"f:spec":{"f:template":{"f:spec":{"f:serviceAccountName":{}}}}}`,
			want:   "spec.template.spec.serviceAccountName",
		},
		{
			name:   "a list entry keyed by name",
			fields: `{"f:spec":{"f:template":{"f:spec":{"f:containers":{"k:{\"name\":\"web\"}":{"f:image":{}}}}}}}`,
			want:   "spec.template.spec.containers[web].image",
		},
		{
			name:   "a list entry keyed by something else",
			fields: `{"f:spec":{"f:ports":{"k:{\"port\":80,\"protocol\":\"TCP\"}":{"f:targetPort":{}}}}}`,
			want:   "spec.ports[port=80,protocol=TCP].targetPort",
		},
		{
			name:   "a set entry",
			fields: `{"f:spec":{"f:finalizers":{"v:\"keep\"":{}}}}`,
			want:   "spec.finalizers[keep]",
		},
		{
			name:   "an indexed entry",
			fields: `{"f:spec":{"f:rules":{"i:0":{}}}}`,
			want:   "spec.rules[0]",
		},
		{
			name:   "a key the api server did not prefix",
			fields: `{"f:spec":{"odd":{}}}`,
			want:   "spec.odd",
		},
		{
			name:   "a malformed field value",
			fields: `{"f:spec":{"f:replicas":true}}`,
			want:   "spec.replicas",
		},
		{
			name:   "a key that is not readable json",
			fields: `{"f:spec":{"f:ports":{"k:not-json":{}}}}`,
			want:   "spec.ports[not-json]",
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			entry := fieldsEntry("argocd-controller", tc.fields)

			paths := specPathsOf(&entry)

			if !paths[tc.want] {
				t.Fatalf("paths = %v, want %q among them", sorted(paths), tc.want)
			}
		})
	}
}

func TestFieldPathsSurviveAnEntryThatSaysNothing(t *testing.T) {
	cases := []struct {
		name  string
		entry metav1.ManagedFieldsEntry
	}{
		{name: "no fields at all", entry: metav1.ManagedFieldsEntry{Manager: "argocd-controller"}},
		{name: "fields that are not json", entry: fieldsEntry("argocd-controller", `not json`)},
		{name: "no spec in them", entry: fieldsEntry("argocd-controller", `{"f:metadata":{"f:labels":{}}}`)},
		{name: "a spec that is not a map", entry: fieldsEntry("argocd-controller", `{"f:spec":"nope"}`)},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if paths := specPathsOf(&tc.entry); len(paths) != 0 {
				t.Fatalf("paths = %v, want none", sorted(paths))
			}
		})
	}
}

func TestOwnershipStopsAfterTwentyFieldsAndCountsTheRest(t *testing.T) {
	owned := map[string]any{}
	for at := range 25 {
		owned["f:field"+string(rune('a'+at))] = map[string]any{}
	}
	encoded := `{"f:spec":{`
	parts := make([]string, 0, len(owned))
	for key := range owned {
		parts = append(parts, `"`+key+`":{}`)
	}
	slices.Sort(parts)
	encoded += strings.Join(parts, ",") + `}}`
	live := ssaObject(fieldsEntry("argocd-controller", encoded), fieldsEntry("kubectl-edit", encoded))

	found, note := Ownership(live, api.ControllerArgo)

	if len(found) != maxDriftFields {
		t.Fatalf("ownership = %d rows, want %d", len(found), maxDriftFields)
	}
	if !strings.HasSuffix(note, "and 5 more") {
		t.Fatalf("note = %q, want the rest counted", note)
	}
}