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) } }) } }