mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-02-14 18:29:53 +00:00
applied native sidecar fix (#1914)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user