83 lines
2.9 KiB
Go
83 lines
2.9 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
|
|
"felis.lolicon.best/internal/apis/felis/v1alpha1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
)
|
|
|
|
type StartupStatus struct {
|
|
Stage string `json:"stage"`
|
|
Reason string `json:"reason,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
StartedAt string `json:"startedAt,omitempty"`
|
|
LogsAvailable bool `json:"logsAvailable"`
|
|
}
|
|
|
|
func startupStatus(ms *v1alpha1.MinecraftServer, pods []corev1.Pod) *StartupStatus {
|
|
s := &StartupStatus{Stage: "creating"}
|
|
if ms.Status.Phase == v1alpha1.PhaseFailed {
|
|
s.Stage = "failed"
|
|
}
|
|
if ms.Status.StartRequestedAt != nil {
|
|
s.StartedAt = ms.Status.StartRequestedAt.UTC().Format("2006-01-02T15:04:05Z")
|
|
}
|
|
if c := meta.FindStatusCondition(ms.Status.Conditions, v1alpha1.ConditionReady); c != nil {
|
|
s.Reason, s.Message = c.Reason, c.Message
|
|
}
|
|
var p *corev1.Pod
|
|
for i := range pods {
|
|
if pods[i].DeletionTimestamp == nil && (p == nil || pods[i].CreationTimestamp.After(p.CreationTimestamp.Time)) {
|
|
p = &pods[i]
|
|
}
|
|
}
|
|
if p == nil {
|
|
if ms.Status.Phase == v1alpha1.PhaseRunning {
|
|
s.Stage, s.Reason, s.Message = "failed", "GamePodMissing", "game Pod is missing; recorded Running status is no longer confirmed"
|
|
}
|
|
return s
|
|
}
|
|
s.Stage = "preparing"
|
|
if p.Status.Phase == corev1.PodFailed {
|
|
s.Stage, s.Reason, s.Message = "failed", p.Status.Reason, p.Status.Message
|
|
}
|
|
for _, c := range p.Status.Conditions {
|
|
if c.Type == corev1.PodReady && (c.Status == corev1.ConditionUnknown || (ms.Status.Phase == v1alpha1.PhaseRunning && c.Status == corev1.ConditionFalse && p.Status.Phase == corev1.PodRunning)) {
|
|
s.Stage, s.Reason, s.Message = "failed", c.Reason, c.Message
|
|
return s
|
|
}
|
|
if c.Type == corev1.PodScheduled && c.Status == corev1.ConditionFalse {
|
|
s.Stage, s.Reason, s.Message = "scheduling", c.Reason, c.Message
|
|
return s
|
|
}
|
|
}
|
|
s.LogsAvailable = p.Status.Phase == corev1.PodRunning
|
|
for _, c := range slices.Concat(p.Status.InitContainerStatuses, p.Status.ContainerStatuses) {
|
|
if c.State.Waiting != nil {
|
|
switch c.State.Waiting.Reason {
|
|
case "CrashLoopBackOff", "ImagePullBackOff", "ErrImagePull", "CreateContainerConfigError", "RunContainerError":
|
|
s.Stage, s.Reason, s.Message = "failed", c.State.Waiting.Reason, c.State.Waiting.Message
|
|
default:
|
|
if s.Stage != "failed" {
|
|
s.Reason, s.Message = c.State.Waiting.Reason, c.State.Waiting.Message
|
|
}
|
|
}
|
|
}
|
|
if c.State.Terminated != nil && (c.State.Terminated.ExitCode != 0 || c.Name == serverLogContainer) {
|
|
s.Stage, s.Reason, s.Message = "failed", c.State.Terminated.Reason, c.State.Terminated.Message
|
|
if s.Message == "" {
|
|
s.Message = fmt.Sprintf("Container %s exited with code %d", c.Name, c.State.Terminated.ExitCode)
|
|
}
|
|
}
|
|
if c.Name == serverLogContainer && c.State.Running != nil {
|
|
if s.Stage != "failed" {
|
|
s.Stage = "booting"
|
|
}
|
|
}
|
|
}
|
|
return s
|
|
}
|