mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-08-27 00:37:20 +00:00
385 lines
9.1 KiB
Go
385 lines
9.1 KiB
Go
package k8sutil
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
)
|
|
|
|
func TestIsPodUnhealthy(t *testing.T) {
|
|
andTrue := true
|
|
tests := []struct {
|
|
name string
|
|
pod *corev1.Pod
|
|
want bool
|
|
}{
|
|
{
|
|
name: "healthy pod with init containers",
|
|
want: false,
|
|
pod: &corev1.Pod{
|
|
Status: corev1.PodStatus{
|
|
Phase: "Running",
|
|
Conditions: []corev1.PodCondition{
|
|
// ignored here
|
|
},
|
|
InitContainerStatuses: []corev1.ContainerStatus{
|
|
{
|
|
Name: "init",
|
|
State: corev1.ContainerState{
|
|
Terminated: &corev1.ContainerStateTerminated{
|
|
ExitCode: 0,
|
|
Reason: "Completed",
|
|
},
|
|
},
|
|
Ready: true,
|
|
RestartCount: 2,
|
|
Image: "projectcontour/contour:v1.11.0",
|
|
ImageID: "docker://sha256:12878e02b6f969de1456b51b0093f289fd195db956837ccd75aa679e9dac24d9",
|
|
ContainerID: "docker://50fc0794fa1402b1a48521e2fd380a92521a69db70ad25c9699c91fccd839d70",
|
|
},
|
|
},
|
|
ContainerStatuses: []corev1.ContainerStatus{
|
|
{
|
|
Name: "contour",
|
|
State: corev1.ContainerState{
|
|
Running: &corev1.ContainerStateRunning{},
|
|
},
|
|
Ready: true,
|
|
RestartCount: 5,
|
|
Image: "projectcontour/contour:v1.11.0",
|
|
ImageID: "docker://sha256:12878e02b6f969de1456b51b0093f289fd195db956837ccd75aa679e9dac24d9",
|
|
ContainerID: "docker://50fc0794fa1402b1a48521e2fd380a92521a69db70ad25c9699c91fccd839d70",
|
|
Started: &andTrue,
|
|
},
|
|
},
|
|
QOSClass: corev1.PodQOSBestEffort,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "running pod with one unhealthy container",
|
|
want: true,
|
|
pod: &corev1.Pod{
|
|
Status: corev1.PodStatus{
|
|
Phase: "Running",
|
|
Conditions: []corev1.PodCondition{
|
|
// ignored here
|
|
},
|
|
ContainerStatuses: []corev1.ContainerStatus{
|
|
{
|
|
Name: "envoy",
|
|
State: corev1.ContainerState{
|
|
Running: &corev1.ContainerStateRunning{},
|
|
},
|
|
Ready: false,
|
|
RestartCount: 5,
|
|
Started: &andTrue,
|
|
},
|
|
{
|
|
Name: "shutdown-manager",
|
|
State: corev1.ContainerState{
|
|
Running: &corev1.ContainerStateRunning{},
|
|
},
|
|
Ready: true,
|
|
RestartCount: 5,
|
|
Started: &andTrue,
|
|
},
|
|
},
|
|
QOSClass: corev1.PodQOSBestEffort,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
req := require.New(t)
|
|
|
|
got := IsPodUnhealthy(tt.pod)
|
|
req.Equal(tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|