applied native sidecar fix (#1914)

This commit is contained in:
Benjamin Yang
2025-11-04 11:30:42 -06:00
committed by GitHub
parent 05a7a2092e
commit cf2db49f86
2 changed files with 309 additions and 1 deletions

View File

@@ -25,6 +25,26 @@ const (
PodStatusReasonInitCrashLoopBackOff PodStatusReason = "Init:CrashLoopBackOff"
)
// isNativeSidecar checks if an init container is a native sidecar.
// Native sidecars are init containers with restartPolicy: Always (Kubernetes 1.28+).
// They run continuously alongside main containers, unlike traditional init containers
// which must complete before main containers start.
func isNativeSidecar(pod *corev1.Pod, initContainerIndex int) bool {
// Bounds check - ensure the index is valid
if initContainerIndex >= len(pod.Spec.InitContainers) {
return false
}
initContainer := pod.Spec.InitContainers[initContainerIndex]
// Check if RestartPolicy is set to Always
if initContainer.RestartPolicy != nil && *initContainer.RestartPolicy == corev1.ContainerRestartPolicyAlways {
return true
}
return false
}
// reference: https://github.com/kubernetes/kubernetes/blob/e8fcd0de98d50f4019561a6b7a0287f5c059267a/pkg/printers/internalversion/printers.go#L741
func GetPodStatusReason(pod *corev1.Pod) (string, string) {
reason := string(pod.Status.Phase)
@@ -55,6 +75,11 @@ func GetPodStatusReason(pod *corev1.Pod) (string, string) {
case container.State.Waiting != nil && len(container.State.Waiting.Reason) > 0 && container.State.Waiting.Reason != "PodInitializing":
reason = "Init:" + container.State.Waiting.Reason
initializing = true
case isNativeSidecar(pod, i) && container.State.Running != nil:
// Native sidecar running - this is expected, not stuck initializing.
// Native sidecars (init containers with restartPolicy: Always) are designed
// to run continuously, so a Running state means successful initialization.
continue
default:
reason = fmt.Sprintf("Init:%d/%d", i, len(pod.Spec.InitContainers))
initializing = true

View File

@@ -1,9 +1,11 @@
package k8sutil
import (
"testing"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestIsPodUnhealthy(t *testing.T) {
@@ -99,3 +101,284 @@ func TestIsPodUnhealthy(t *testing.T) {
})
}
}
// TestGetPodStatusReason_HealthyNativeSidecar tests that a pod with a running native sidecar
// is correctly reported as "Running" and not stuck initializing.
func TestGetPodStatusReason_HealthyNativeSidecar(t *testing.T) {
startedTrue := true
restartPolicyAlways := corev1.ContainerRestartPolicyAlways
pod := &corev1.Pod{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "istio-proxy",
Image: "istio/proxyv2:1.20",
RestartPolicy: &restartPolicyAlways, // Native sidecar!
},
},
Containers: []corev1.Container{
{
Name: "app",
Image: "myapp:latest",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionTrue,
},
},
InitContainerStatuses: []corev1.ContainerStatus{
{
Name: "istio-proxy",
Ready: true,
Started: &startedTrue,
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{
StartedAt: metav1.Now(),
},
},
},
},
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "app",
Ready: true,
Started: &startedTrue,
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{
StartedAt: metav1.Now(),
},
},
},
},
},
}
reason, message := GetPodStatusReason(pod)
// Should report as Running, not Init:0/1
if reason != "Running" {
t.Errorf("Expected reason 'Running', got '%s'", reason)
}
if message != "" {
t.Errorf("Expected empty message, got '%s'", message)
}
}
// TestIsPodUnhealthy_HealthyNativeSidecar tests that a pod with a healthy running native sidecar
// is not marked as unhealthy.
func TestIsPodUnhealthy_HealthyNativeSidecar(t *testing.T) {
startedTrue := true
restartPolicyAlways := corev1.ContainerRestartPolicyAlways
pod := &corev1.Pod{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "istio-proxy",
RestartPolicy: &restartPolicyAlways,
},
},
Containers: []corev1.Container{
{
Name: "app",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionTrue,
},
},
InitContainerStatuses: []corev1.ContainerStatus{
{
Name: "istio-proxy",
Ready: true,
Started: &startedTrue,
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{},
},
},
},
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "app",
Ready: true,
Started: &startedTrue,
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{},
},
},
},
},
}
unhealthy := IsPodUnhealthy(pod)
if unhealthy {
t.Error("Pod with healthy native sidecar should not be marked as unhealthy")
}
}
// TestGetPodStatusReason_TraditionalInitAndNativeSidecar tests that a pod with both
// a completed traditional init container and a running native sidecar is reported as "Running".
func TestGetPodStatusReason_TraditionalInitAndNativeSidecar(t *testing.T) {
startedTrue := true
restartPolicyAlways := corev1.ContainerRestartPolicyAlways
pod := &corev1.Pod{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "init-setup",
// No RestartPolicy = traditional init container
},
{
Name: "istio-proxy",
RestartPolicy: &restartPolicyAlways, // Native sidecar
},
},
Containers: []corev1.Container{
{
Name: "app",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodRunning,
Conditions: []corev1.PodCondition{
{
Type: corev1.PodReady,
Status: corev1.ConditionTrue,
},
},
InitContainerStatuses: []corev1.ContainerStatus{
{
Name: "init-setup",
State: corev1.ContainerState{
Terminated: &corev1.ContainerStateTerminated{
ExitCode: 0,
Reason: "Completed",
},
},
},
{
Name: "istio-proxy",
Ready: true,
Started: &startedTrue,
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{},
},
},
},
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "app",
Ready: true,
Started: &startedTrue,
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{},
},
},
},
},
}
reason, _ := GetPodStatusReason(pod)
if reason != "Running" {
t.Errorf("Expected reason 'Running', got '%s'", reason)
}
}
// TestGetPodStatusReason_NativeSidecarCrashLoopBackOff tests that a native sidecar
// in CrashLoopBackOff is still correctly detected as an error.
func TestGetPodStatusReason_NativeSidecarCrashLoopBackOff(t *testing.T) {
restartPolicyAlways := corev1.ContainerRestartPolicyAlways
pod := &corev1.Pod{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "istio-proxy",
RestartPolicy: &restartPolicyAlways,
},
},
Containers: []corev1.Container{
{
Name: "app",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodPending,
InitContainerStatuses: []corev1.ContainerStatus{
{
Name: "istio-proxy",
Ready: false,
State: corev1.ContainerState{
Waiting: &corev1.ContainerStateWaiting{
Reason: "CrashLoopBackOff",
Message: "Back-off 5m0s restarting failed container",
},
},
},
},
},
}
reason, _ := GetPodStatusReason(pod)
// Should still catch the error
if reason != "Init:CrashLoopBackOff" {
t.Errorf("Expected reason 'Init:CrashLoopBackOff', got '%s'", reason)
}
}
// TestGetPodStatusReason_TraditionalInitStuck tests that a traditional init container
// that is stuck running is still correctly detected as stuck initializing.
func TestGetPodStatusReason_TraditionalInitStuck(t *testing.T) {
pod := &corev1.Pod{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "init-setup",
// No RestartPolicy = traditional init
},
},
Containers: []corev1.Container{
{
Name: "app",
},
},
},
Status: corev1.PodStatus{
Phase: corev1.PodPending,
InitContainerStatuses: []corev1.ContainerStatus{
{
Name: "init-setup",
Ready: false,
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{},
},
},
},
},
}
reason, _ := GetPodStatusReason(pod)
// Traditional init running = stuck
if reason != "Init:0/1" {
t.Errorf("Expected reason 'Init:0/1', got '%s'", reason)
}
}