Rainbond/worker/util/pod.go
2020-07-18 22:16:11 +08:00

143 lines
4.2 KiB
Go

package util
import (
"fmt"
"sort"
"strings"
k8sutil "github.com/goodrain/rainbond/util/k8s"
"github.com/goodrain/rainbond/worker/server/pb"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes"
)
// PodStatusAdvice -
type PodStatusAdvice string
// String converts PodStatusAdvice to string
func (p PodStatusAdvice) String() string {
return string(p)
}
// PodStatusAdviceOOM -
var PodStatusAdviceOOM PodStatusAdvice = "OutOfMemory"
// PodStatusAdviceUnhealthy -
var PodStatusAdviceUnhealthy PodStatusAdvice = "Unhealthy"
// PodStatusAdviceInitiating -
var PodStatusAdviceInitiating PodStatusAdvice = "Initiating"
var podStatusTbl = map[string]pb.PodStatus_Type{
string(corev1.PodPending): pb.PodStatus_INITIATING,
string(corev1.PodRunning): pb.PodStatus_RUNNING,
string(corev1.PodSucceeded): pb.PodStatus_ABNORMAL,
string(corev1.PodFailed): pb.PodStatus_ABNORMAL,
string(corev1.PodUnknown): pb.PodStatus_UNKNOWN,
string(corev1.PodReady): pb.PodStatus_NOTREADY,
string(corev1.PodInitialized): pb.PodStatus_INITIATING,
string(corev1.PodScheduled): pb.PodStatus_SCHEDULING,
string(corev1.ContainersReady): pb.PodStatus_NOTREADY,
}
// DescribePodStatus -
func DescribePodStatus(clientset kubernetes.Interface, pod *corev1.Pod, podStatus *pb.PodStatus, f k8sutil.ListEventsByPod) {
defer func() {
podStatus.TypeStr = podStatus.Type.String()
}()
if pod.DeletionTimestamp != nil {
podStatus.Type = pb.PodStatus_TEMINATING
podStatus.Message = fmt.Sprintf("Termination Grace Period:\t%ds", *pod.DeletionGracePeriodSeconds)
} else if len(pod.Status.Conditions) == 0 {
podStatus.Type = podStatusTbl[string(pod.Status.Phase)]
if len(pod.Status.Reason) > 0 {
podStatus.Reason = pod.Status.Reason
}
if len(pod.Status.Message) > 0 {
podStatus.Message = pod.Status.Message
}
} else {
// schedule, ready, init
podStatus.Type = pb.PodStatus_RUNNING
sort.Sort(SortableConditionType(pod.Status.Conditions))
for _, condition := range pod.Status.Conditions {
if condition.Status == corev1.ConditionTrue {
continue
}
podStatus.Type = podStatusTbl[string(condition.Type)] // find the latest not ready condition
podStatus.Reason = condition.Reason
podStatus.Message = condition.Message
}
}
if podStatus.Type == pb.PodStatus_INITIATING {
podStatus.Advice = PodStatusAdviceInitiating.String()
// if all main container ready
if len(pod.Status.ContainerStatuses) > 0 {
allMainCReady := true
for _, mainC := range pod.Status.ContainerStatuses {
if !mainC.Ready {
allMainCReady = false
break
}
}
if allMainCReady {
podStatus.Type = pb.PodStatus_RUNNING
return
}
}
return
}
if podStatus.Type == pb.PodStatus_NOTREADY {
for _, cstatus := range pod.Status.ContainerStatuses {
if !cstatus.Ready && cstatus.State.Terminated != nil {
podStatus.Type = pb.PodStatus_ABNORMAL
if cstatus.State.Terminated.Reason == "OOMKilled" {
podStatus.Advice = PodStatusAdviceOOM.String()
}
return
}
if !cstatus.Ready {
events := f(clientset, pod)
if events != nil {
for _, evt := range events.Items {
if strings.Contains(evt.Message, "Liveness probe failed") || strings.Contains(evt.Message, "Readiness probe failed") {
podStatus.Type = pb.PodStatus_UNHEALTHY
podStatus.Advice = PodStatusAdviceUnhealthy.String()
return
}
}
}
}
if !cstatus.Ready && cstatus.State.Waiting != nil {
w := cstatus.State.Waiting
if w.Reason != "PodInitializing" && w.Reason != "ContainerCreating" {
podStatus.Type = pb.PodStatus_ABNORMAL
return
}
}
}
}
}
// SortableConditionType implements sort.Interface for []PodCondition based on
// the Type field.
type SortableConditionType []corev1.PodCondition
var podConditionTbl = map[corev1.PodConditionType]int{
corev1.PodScheduled: 0,
corev1.PodInitialized: 1,
corev1.PodReady: 2,
corev1.ContainersReady: 3,
}
func (s SortableConditionType) Len() int {
return len(s)
}
func (s SortableConditionType) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s SortableConditionType) Less(i, j int) bool {
return podConditionTbl[s[i].Type] > podConditionTbl[s[j].Type]
}