Ctrl K
ComunidadGuías…

pkg/audit/resilience_test.go

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

Archivo original ↗Preguntar a KiBot
package audit

import (
	"slices"
	"testing"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
)

func resilienceInput(nodes ...string) *CheckInput {
	owner := metav1.OwnerReference{APIVersion: "apps/v1", Kind: "Deployment", Name: "web", UID: "deployment-uid", Controller: ptr(true)}
	input := &CheckInput{Deployments: []*appsv1.Deployment{{ObjectMeta: metav1.ObjectMeta{Name: "web", Namespace: "app", UID: owner.UID}, Spec: appsv1.DeploymentSpec{Replicas: ptr(int32(3)), Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "shared"}}}}}, ReplicaSets: []*appsv1.ReplicaSet{{ObjectMeta: metav1.ObjectMeta{Name: "web-rs", Namespace: "app", UID: "rs-uid", OwnerReferences: []metav1.OwnerReference{owner}}}}, Pods: []*corev1.Pod{}}
	for i, node := range nodes {
		input.Pods = append(input.Pods, &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: string(rune('a' + i)), Namespace: "app", UID: types.UID(string(rune('a' + i))), Labels: map[string]string{"app": "shared"}, OwnerReferences: []metav1.OwnerReference{{APIVersion: "apps/v1", Kind: "ReplicaSet", Name: "web-rs", UID: "rs-uid", Controller: ptr(true)}}}, Spec: corev1.PodSpec{NodeName: node}, Status: corev1.PodStatus{Phase: corev1.PodRunning}})
	}
	return input
}

func TestPodHARiskOwnershipAndCurrentPlacement(t *testing.T) {
	tests := []struct {
		name                string
		mutate              func(*CheckInput)
		findings, evaluated int
		missing             string
	}{
		{"colocated", func(*CheckInput) {}, 1, 1, ""},
		{"distributed", func(i *CheckInput) { i.Pods[1].Spec.NodeName = "node-b" }, 0, 1, ""},
		{"running unready remains placement", func(i *CheckInput) {
			for _, p := range i.Pods {
				p.Status.Conditions = []corev1.PodCondition{{Type: corev1.PodReady, Status: corev1.ConditionFalse}}
			}
		}, 1, 1, ""},
		{"assigned pending excluded", func(i *CheckInput) { i.Pods[1].Status.Phase = corev1.PodPending }, 0, 0, ""},
		{"completed excluded", func(i *CheckInput) { i.Pods[1].Status.Phase = corev1.PodSucceeded }, 0, 0, ""},
		{"failed excluded", func(i *CheckInput) { i.Pods[1].Status.Phase = corev1.PodFailed }, 0, 0, ""},
		{"terminating excluded", func(i *CheckInput) { now := metav1.Now(); i.Pods[1].DeletionTimestamp = &now }, 0, 0, ""},
		{"unassigned excluded", func(i *CheckInput) { i.Pods[1].Spec.NodeName = "" }, 0, 0, ""},
		{"bare matching stranger excluded", func(i *CheckInput) { i.Pods[1].OwnerReferences = nil }, 0, 0, ""},
		{"selector drift cannot erase ownership", func(i *CheckInput) { i.Pods[1].Labels = nil }, 1, 1, ""},
		{"rolling revisions same deployment", func(i *CheckInput) {
			rs := i.ReplicaSets[0].DeepCopy()
			rs.Name = "revision-two"
			rs.UID = "second-rs"
			i.ReplicaSets = append(i.ReplicaSets, rs)
			i.Pods[1].OwnerReferences[0].Name = rs.Name
			i.Pods[1].OwnerReferences[0].UID = rs.UID
		}, 1, 1, ""},
		{"overlapping selector other deployment", func(i *CheckInput) {
			rs := i.ReplicaSets[0].DeepCopy()
			rs.Name = "other-rs"
			rs.UID = "other-rs"
			rs.OwnerReferences[0].UID = "other-deployment"
			rs.OwnerReferences[0].Name = "other"
			i.ReplicaSets = append(i.ReplicaSets, rs)
			i.Pods[1].OwnerReferences[0].Name = rs.Name
			i.Pods[1].OwnerReferences[0].UID = rs.UID
		}, 0, 0, ""},
		{"custom rollout parent excluded", func(i *CheckInput) {
			i.ReplicaSets[0].OwnerReferences[0].Kind = "Rollout"
			i.ReplicaSets[0].OwnerReferences[0].APIVersion = "argoproj.io/v1alpha1"
		}, 0, 0, ""},
		{"recreated deployment UID mismatch", func(i *CheckInput) { i.Deployments[0].UID = "recreated" }, 0, 0, ""},
		{"recreated replicaSet UID mismatch", func(i *CheckInput) { i.ReplicaSets[0].UID = "recreated" }, 0, 0, "replicaset-ownership"},
		{"partial replicaSet namespace inventory", func(i *CheckInput) { i.ReplicaSets = []*appsv1.ReplicaSet{} }, 0, 0, "replicaset-ownership"},
		{"nil replicaSet inventory", func(i *CheckInput) { i.ReplicaSets = nil }, 0, 0, "replicasets"},
		{"nil pod inventory", func(i *CheckInput) { i.Pods = nil }, 0, 0, "pods"},
		{"missing deployment UID", func(i *CheckInput) { i.Deployments[0].UID = "" }, 0, 0, ""},
		{"noncontroller reference excluded", func(i *CheckInput) { i.Pods[1].OwnerReferences[0].Controller = ptr(false) }, 0, 0, ""},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			i := resilienceInput("node-a", "node-a")
			tc.mutate(i)
			r := RunChecks(i)
			count := 0
			for _, f := range r.Findings {
				if f.CheckID == "podHARisk" {
					count++
				}
			}
			if count != tc.findings || r.CheckCounts["podHARisk"].Evaluated != tc.evaluated {
				t.Fatalf("findings=%d counts=%+v", count, r.CheckCounts["podHARisk"])
			}
			if tc.missing != "" && !slices.Contains(r.MissingInputs, tc.missing) {
				t.Fatalf("missing %s: %+v", tc.missing, r.MissingInputs)
			}
			if r.CheckCounts["podHARisk"].Passed != tc.evaluated-tc.findings {
				t.Fatal("incorrect passing count")
			}
		})
	}
}

func TestPodHARiskStaleOutsiderCannotMaskColocation(t *testing.T) {
	for _, phase := range []corev1.PodPhase{corev1.PodPending, corev1.PodSucceeded, corev1.PodFailed} {
		i := resilienceInput("node-a", "node-a", "node-b")
		i.Pods[2].Status.Phase = phase
		r := RunChecks(i)
		if r.CheckCounts["podHARisk"] != (CheckCount{Evaluated: 1, Passed: 0}) {
			t.Fatalf("phase%s masks colocated running pods: %+v", phase, r.CheckCounts)
		}
	}
}

func TestPodHARiskUnknownOwnershipIsNamespaceScopedAndPreservesOtherChecks(t *testing.T) {
	i := resilienceInput("node-a", "node-a")
	unknown := i.Pods[0].DeepCopy()
	unknown.Name = "unknown"
	unknown.Namespace = "other"
	i.Pods = append(i.Pods, unknown)
	r := RunChecks(i)
	if r.CheckCounts["podHARisk"].Evaluated != 1 {
		t.Fatal("other namespace contaminated evaluated deployment")
	}
	unknown.Namespace = "app"
	unknown.OwnerReferences[0].UID = "missing"
	r = RunChecks(i)
	if r.CheckCounts["podHARisk"].Evaluated != 0 || !slices.Contains(r.MissingInputs, "replicaset-ownership") {
		t.Fatal("unknown was counted as passing")
	}
	found := false
	for _, f := range r.Findings {
		if f.CheckID == "missingTopologySpread" {
			found = true
		}
	}
	if !found {
		t.Fatal("independent topology policy finding lost")
	}
}

func TestPodHARiskUnknownOwnershipKeepsProvableSpread(t *testing.T) {
	for _, tc := range []struct {
		name    string
		nodes   []string
		want    CheckCount
		missing bool
	}{
		{"verified spread", []string{"node-a", "node-b"}, CheckCount{Evaluated: 1, Passed: 1}, false},
		{"verified colocation", []string{"node-a", "node-a"}, CheckCount{}, true},
		{"one verified placement", []string{"node-a"}, CheckCount{}, true},
		{"no verified placements", nil, CheckCount{}, true},
	} {
		t.Run(tc.name, func(t *testing.T) {
			i := resilienceInput(tc.nodes...)
			unknown := resilienceInput("node-c").Pods[0]
			unknown.Name = "unknown"
			unknown.UID = "unknown-pod"
			unknown.OwnerReferences[0].UID = "unresolved-rs"
			i.Pods = append(i.Pods, unknown)
			r := RunChecks(i)
			if got := r.CheckCounts["podHARisk"]; got != tc.want {
				t.Fatalf("counts=%+v, want %+v", got, tc.want)
			}
			if got := slices.Contains(r.MissingInputs, "replicaset-ownership"); got != tc.missing {
				t.Fatalf("ownership missing=%v, want %v", got, tc.missing)
			}
			for _, f := range r.Findings {
				if f.CheckID == "podHARisk" {
					t.Fatalf("unknown ownership created colocation warning: %+v", f)
				}
			}
		})
	}
}

func TestPodHARiskProvableSpreadDoesNotHideSkippedOrIndependentWorkloads(t *testing.T) {
	for _, sameNamespace := range []bool{true, false} {
		t.Run(map[bool]string{true: "same namespace skipped", false: "other namespace warning preserved"}[sameNamespace], func(t *testing.T) {
			i := resilienceInput("node-a", "node-b")
			unknown := i.Pods[0].DeepCopy()
			unknown.Name, unknown.UID = "unknown", "unknown-pod"
			unknown.OwnerReferences[0].UID = "unresolved-rs"
			i.Pods = append(i.Pods, unknown)
			other := resilienceInput("node-a", "node-a")
			d, rs := other.Deployments[0], other.ReplicaSets[0]
			d.Name, d.UID = "other", "other-deployment"
			rs.Name, rs.UID = "other-rs", "other-rs"
			rs.OwnerReferences[0].Name, rs.OwnerReferences[0].UID = d.Name, d.UID
			if !sameNamespace {
				d.Namespace, rs.Namespace = "other", "other"
			}
			for _, pod := range other.Pods {
				pod.Name = "other-" + pod.Name
				pod.UID = types.UID("other-" + string(pod.UID))
				pod.Namespace = d.Namespace
				pod.OwnerReferences[0].Name, pod.OwnerReferences[0].UID = rs.Name, rs.UID
			}
			i.Deployments = append(i.Deployments, d)
			i.ReplicaSets = append(i.ReplicaSets, rs)
			i.Pods = append(i.Pods, other.Pods...)
			r := RunChecks(i)
			want := CheckCount{Evaluated: 2, Passed: 1}
			if sameNamespace {
				want.Evaluated = 1
			}
			if got := r.CheckCounts["podHARisk"]; got != want {
				t.Fatalf("counts=%+v, want %+v", got, want)
			}
			if got := slices.Contains(r.MissingInputs, "replicaset-ownership"); got != sameNamespace {
				t.Fatalf("missing ownership=%v, want %v", got, sameNamespace)
			}
			var findings []Finding
			for _, f := range r.Findings {
				if f.CheckID == "podHARisk" {
					findings = append(findings, f)
				}
			}
			if sameNamespace && len(findings) != 0 {
				t.Fatalf("skipped workload emitted finding: %+v", findings)
			}
			if !sameNamespace && (len(findings) != 1 || findings[0].Name != d.Name || findings[0].Namespace != d.Namespace) {
				t.Fatalf("independent colocation warning lost: %+v", findings)
			}
		})
	}
}