Ctrl K
ComunidadGuías…

pkg/envresolve/resolver_test.go

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

Archivo original ↗Preguntar a KiBot
package envresolve

import (
	"strings"
	"testing"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestResolvePodPrecedenceAndSecretTaint(t *testing.T) {
	pod := &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "shop", Labels: map[string]string{"tier": "api"}},
		Spec: corev1.PodSpec{NodeName: "worker-1", ServiceAccountName: "api", Containers: []corev1.Container{{
			Name: "app",
			EnvFrom: []corev1.EnvFromSource{
				{ConfigMapRef: &corev1.ConfigMapEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "base"}}},
				{SecretRef: &corev1.SecretEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "auth"}}},
			},
			Env: []corev1.EnvVar{
				{Name: "HOST", Value: "override"},
				{Name: "DSN", Value: "postgres://$(PASSWORD)@$(HOST)"},
				{Name: "TIER", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.labels['tier']"}}},
				{Name: "HOST_IPS", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.hostIPs"}}},
				{Name: "POD_IP", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.podIP"}}},
				{Name: "POD_IPS", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.podIPs"}}},
				{Name: "NODE", ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{FieldPath: "spec.nodeName"}}},
			},
		}}},
		Status: corev1.PodStatus{HostIPs: []corev1.HostIP{{IP: "192.0.2.1"}, {IP: "2001:db8::1"}}, PodIP: "10.0.0.2", PodIPs: []corev1.PodIP{{IP: "10.0.0.2"}, {IP: "fd00::2"}}},
	}
	sources := map[SourceID]SourceData{
		{Kind: "ConfigMap", Name: "base"}: {Kind: "ConfigMap", Name: "base", State: SourceAvailable, Keys: []string{"HOST"}, Values: map[string]string{"HOST": "db"}},
		{Kind: "Secret", Name: "auth"}:    {Kind: "Secret", Name: "auth", State: SourceAvailable, Keys: []string{"PASSWORD"}, Sensitive: true},
	}
	got := ResolvePod(pod, sources, NodeData{})
	rows := rowsByName(got.Containers[0].Rows)
	if rows["HOST"].Value != "override" || len(rows["HOST"].ShadowedSources) != 1 {
		t.Fatalf("HOST = %+v", rows["HOST"])
	}
	if !rows["DSN"].Sensitive || rows["DSN"].State != ValueMasked || rows["DSN"].Value != "" {
		t.Fatalf("DSN = %+v", rows["DSN"])
	}
	foundSecretDependency := false
	for _, dependency := range rows["DSN"].Dependencies {
		foundSecretDependency = foundSecretDependency || dependency.Kind == "Secret" && dependency.Name == "auth" && dependency.Key == "PASSWORD"
	}
	if !foundSecretDependency {
		t.Fatalf("DSN lost Secret provenance: %+v", rows["DSN"])
	}
	if rows["TIER"].Value != "api" || !rows["TIER"].CurrentPodValue {
		t.Fatalf("TIER = %+v", rows["TIER"])
	}
	if rows["HOST_IPS"].Value != "192.0.2.1,2001:db8::1" || rows["POD_IP"].Value != "10.0.0.2" || rows["POD_IPS"].Value != "10.0.0.2,fd00::2" || rows["NODE"].Value != "worker-1" {
		t.Fatalf("field refs: HOST_IPS=%+v POD_IP=%+v POD_IPS=%+v NODE=%+v", rows["HOST_IPS"], rows["POD_IP"], rows["POD_IPS"], rows["NODE"])
	}
}

func TestResolvePodSinglePassAndRuntimeDependency(t *testing.T) {
	pod := &corev1.Pod{Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "app", Env: []corev1.EnvVar{
		{Name: "A", Value: "$(IMAGE_VAR)"},
		{Name: "B", Value: "$(A)"},
		{Name: "C", Value: "$$(A)"},
	}}}}}
	rows := rowsByName(ResolvePod(pod, nil, NodeData{}).Containers[0].Rows)
	if !rows["A"].RuntimeDependent || rows["A"].State != ValueUnavailable {
		t.Fatalf("A = %+v", rows["A"])
	}
	if !rows["B"].RuntimeDependent || rows["B"].Value != "$(IMAGE_VAR)" || rows["B"].State != ValueUnavailable {
		t.Fatalf("B = %+v", rows["B"])
	}
	if rows["C"].Value != "$(A)" {
		t.Fatalf("C = %+v", rows["C"])
	}
}

func TestResolvePodDeniedEnvFromDoesNotDiscloseKeys(t *testing.T) {
	pod := &corev1.Pod{Spec: corev1.PodSpec{Containers: []corev1.Container{{
		Name: "app",
		EnvFrom: []corev1.EnvFromSource{{
			SecretRef: &corev1.SecretEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "auth"}},
		}},
	}}}}
	got := ResolvePod(pod, map[SourceID]SourceData{
		{Kind: "Secret", Name: "auth"}: {Kind: "Secret", Name: "auth", State: SourceDenied, Sensitive: true},
	}, NodeData{})
	if len(got.Containers[0].Rows) != 1 || !got.Containers[0].Rows[0].Placeholder || got.Containers[0].Rows[0].State != ValueDenied {
		t.Fatalf("rows = %+v", got.Containers[0].Rows)
	}
}

func TestResolvePodMarksOptionalMissingKey(t *testing.T) {
	optional := true
	pod := &corev1.Pod{Spec: corev1.PodSpec{Containers: []corev1.Container{{
		Name: "app",
		Env: []corev1.EnvVar{{Name: "FEATURE", ValueFrom: &corev1.EnvVarSource{ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
			LocalObjectReference: corev1.LocalObjectReference{Name: "settings"}, Key: "feature", Optional: &optional,
		}}}},
	}}}}
	rows := ResolvePod(pod, map[SourceID]SourceData{
		{Kind: "ConfigMap", Name: "settings"}: {Kind: "ConfigMap", Name: "settings", State: SourceAvailable},
	}, NodeData{}).Containers[0].Rows
	if len(rows) != 1 || rows[0].State != ValueMissing || !rows[0].Optional {
		t.Fatalf("optional missing key = %+v", rows)
	}
	if rows[0].MissingImpact != "" {
		t.Fatalf("optional missing key must not carry failure impact: %+v", rows[0])
	}
}

func TestResolvePodDoesNotTreatOptionalMissingDependencyAsBlocking(t *testing.T) {
	optional := true
	pod := &corev1.Pod{
		Spec: corev1.PodSpec{Containers: []corev1.Container{{
			Name: "app",
			Env: []corev1.EnvVar{
				{Name: "FEATURE", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
					LocalObjectReference: corev1.LocalObjectReference{Name: "settings"}, Key: "feature", Optional: &optional,
				}}},
				{Name: "MESSAGE", Value: "feature=$(FEATURE)"},
			},
		}}},
		Status: corev1.PodStatus{ContainerStatuses: []corev1.ContainerStatus{{
			Name: "app", State: corev1.ContainerState{Running: &corev1.ContainerStateRunning{}},
		}}},
	}
	rows := rowsByName(ResolvePod(pod, map[SourceID]SourceData{
		{Kind: "Secret", Name: "settings"}: {Kind: "Secret", Name: "settings", State: SourceAvailable, Sensitive: true},
	}, NodeData{}).Containers[0].Rows)
	if rows["MESSAGE"].State != ValueResolved || rows["MESSAGE"].Value != "feature=$(FEATURE)" || rows["MESSAGE"].Sensitive || len(rows["MESSAGE"].Dependencies) != 0 || rows["MESSAGE"].MissingImpact != "" {
		t.Fatalf("dependent value did not preserve optional missing reference: %+v", rows["MESSAGE"])
	}
}

func TestResolvePodClassifiesRequiredMissingImpact(t *testing.T) {
	pod := &corev1.Pod{Spec: corev1.PodSpec{Containers: []corev1.Container{{
		Name: "app",
		Env: []corev1.EnvVar{{Name: "SETTING", ValueFrom: &corev1.EnvVarSource{ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
			LocalObjectReference: corev1.LocalObjectReference{Name: "settings"}, Key: "required",
		}}}},
	}}}}
	sources := map[SourceID]SourceData{
		{Kind: "ConfigMap", Name: "settings"}: {Kind: "ConfigMap", Name: "settings", State: SourceAvailable},
	}

	pod.Status.ContainerStatuses = []corev1.ContainerStatus{{
		Name: "app", ContainerID: "containerd://running", State: corev1.ContainerState{Running: &corev1.ContainerStateRunning{}},
	}}
	running := ResolvePod(pod, sources, NodeData{}).Containers[0].Rows[0]
	if running.MissingImpact != MissingImpactRestartBlocked || !strings.Contains(running.Message, "cannot start again") {
		t.Fatalf("running container impact = %+v", running)
	}

	pod.Status.ContainerStatuses[0] = corev1.ContainerStatus{
		Name: "app", State: corev1.ContainerState{Waiting: &corev1.ContainerStateWaiting{Reason: "CreateContainerConfigError"}},
	}
	blocked := ResolvePod(pod, sources, NodeData{}).Containers[0].Rows[0]
	if blocked.MissingImpact != MissingImpactStartupBlocked || !strings.Contains(blocked.Message, "prevents the container from starting") {
		t.Fatalf("blocked container impact = %+v", blocked)
	}

	pod.Status.ContainerStatuses[0] = corev1.ContainerStatus{
		Name:                 "app",
		State:                corev1.ContainerState{Waiting: &corev1.ContainerStateWaiting{Reason: "CrashLoopBackOff"}},
		LastTerminationState: corev1.ContainerState{Terminated: &corev1.ContainerStateTerminated{ExitCode: 1}},
	}
	down := ResolvePod(pod, sources, NodeData{}).Containers[0].Rows[0]
	if down.MissingImpact != MissingImpactStartupBlocked || !strings.Contains(down.Message, "prevents the container from starting") {
		t.Fatalf("down container impact = %+v", down)
	}
}

func TestResolvePodKeepsUnavailableEnvFromSourcesSeparate(t *testing.T) {
	pod := &corev1.Pod{Spec: corev1.PodSpec{Containers: []corev1.Container{{
		Name: "app",
		EnvFrom: []corev1.EnvFromSource{
			{ConfigMapRef: &corev1.ConfigMapEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "first"}}},
			{ConfigMapRef: &corev1.ConfigMapEnvSource{LocalObjectReference: corev1.LocalObjectReference{Name: "second"}}},
		},
	}}}}
	got := ResolvePod(pod, map[SourceID]SourceData{
		{Kind: "ConfigMap", Name: "first"}:  {Kind: "ConfigMap", Name: "first", State: SourceMissing},
		{Kind: "ConfigMap", Name: "second"}: {Kind: "ConfigMap", Name: "second", State: SourceDenied},
	}, NodeData{})
	if len(got.Containers[0].Rows) != 2 || !got.Containers[0].Rows[0].Placeholder || !got.Containers[0].Rows[1].Placeholder {
		t.Fatalf("unavailable sources collapsed: %+v", got.Containers[0].Rows)
	}
}

func TestResolvePodPropagatesDeniedDependency(t *testing.T) {
	pod := &corev1.Pod{Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "app", Env: []corev1.EnvVar{
		{Name: "PASSWORD", ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: "db"}, Key: "password"}}},
		{Name: "DSN", Value: "postgres://$(PASSWORD)"},
	}}}}}
	rows := rowsByName(ResolvePod(pod, map[SourceID]SourceData{
		{Kind: "Secret", Name: "db"}: {Kind: "Secret", Name: "db", State: SourceDenied, Sensitive: true},
	}, NodeData{}).Containers[0].Rows)
	if rows["DSN"].State != ValueDenied || !rows["DSN"].Sensitive {
		t.Fatalf("DSN did not inherit denied Secret state: %+v", rows["DSN"])
	}
}

func TestResolvePodUsesNodeAllocatableForMissingLimit(t *testing.T) {
	pod := &corev1.Pod{Spec: corev1.PodSpec{NodeName: "worker-1", Containers: []corev1.Container{{
		Name: "app",
		Env: []corev1.EnvVar{{Name: "CPU_LIMIT", ValueFrom: &corev1.EnvVarSource{ResourceFieldRef: &corev1.ResourceFieldSelector{
			Resource: "limits.cpu", Divisor: resource.MustParse("1m"),
		}}}},
	}}}}
	node := NodeData{State: SourceAvailable, Allocatable: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("2")}}

	rows := rowsByName(ResolvePod(pod, nil, node).Containers[0].Rows)
	if rows["CPU_LIMIT"].Value != "2000" || !rows["CPU_LIMIT"].CurrentPodValue {
		t.Fatalf("CPU_LIMIT = %+v", rows["CPU_LIMIT"])
	}

	denied := rowsByName(ResolvePod(pod, nil, NodeData{State: SourceDenied}).Containers[0].Rows)
	if denied["CPU_LIMIT"].State != ValueDenied {
		t.Fatalf("denied CPU_LIMIT = %+v", denied["CPU_LIMIT"])
	}
}

func TestResolvePodUsesResourceFieldRefContainerName(t *testing.T) {
	pod := &corev1.Pod{Spec: corev1.PodSpec{NodeName: "worker-1", Containers: []corev1.Container{
		{Name: "producer", Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("250m")}}},
		{Name: "consumer", Resources: corev1.ResourceRequirements{Limits: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1")}}, Env: []corev1.EnvVar{
			{Name: "PRODUCER_CPU", ValueFrom: &corev1.EnvVarSource{ResourceFieldRef: &corev1.ResourceFieldSelector{
				ContainerName: "producer", Resource: "limits.cpu", Divisor: resource.MustParse("1m"),
			}}},
		}},
	}}}
	if PodEnvironmentNeedsNode(pod) {
		t.Fatal("declared target-container limit should not require Node data")
	}
	rows := rowsByName(ResolvePod(pod, nil, NodeData{}).Containers[1].Rows)
	if rows["PRODUCER_CPU"].Value != "250" {
		t.Fatalf("PRODUCER_CPU = %+v", rows["PRODUCER_CPU"])
	}

	delete(pod.Spec.Containers[0].Resources.Limits, corev1.ResourceCPU)
	if !PodEnvironmentNeedsNode(pod) {
		t.Fatal("missing target-container limit should require Node data")
	}
	rows = rowsByName(ResolvePod(pod, nil, NodeData{
		State: SourceAvailable, Allocatable: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("2")},
	}).Containers[1].Rows)
	if rows["PRODUCER_CPU"].Value != "2000" {
		t.Fatalf("PRODUCER_CPU with Node fallback = %+v", rows["PRODUCER_CPU"])
	}
}

func rowsByName(rows []Row) map[string]Row {
	out := make(map[string]Row, len(rows))
	for _, row := range rows {
		out[row.Name] = row
	}
	return out
}