313 lines
9.1 KiB
Go
313 lines
9.1 KiB
Go
package kubedog
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/werf/kubedog/pkg/tracker/daemonset"
|
|
"github.com/werf/kubedog/pkg/tracker/deployment"
|
|
"github.com/werf/kubedog/pkg/tracker/indicators"
|
|
"github.com/werf/kubedog/pkg/tracker/job"
|
|
"github.com/werf/kubedog/pkg/tracker/pod"
|
|
"github.com/werf/kubedog/pkg/tracker/statefulset"
|
|
"github.com/werf/kubedog/pkg/utils"
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
var statusProgressTableRatio = []float64{.58, .11, .12, .19}
|
|
var statusProgressSubTableRatio = []float64{.40, .15, .20, .25}
|
|
|
|
func writeOut(out io.Writer, s string) {
|
|
_, _ = fmt.Fprint(out, s)
|
|
}
|
|
|
|
func displayDeploymentStatusProgress(out io.Writer, resourceCaption string, status deployment.DeploymentStatus, prevStatus *deployment.DeploymentStatus) {
|
|
t := utils.NewTable(statusProgressTableRatio...)
|
|
t.SetWidth(termWidth())
|
|
|
|
showProgress := status.StatusGeneration > prevStatus.StatusGeneration
|
|
|
|
replicas := "-"
|
|
if status.ReplicasIndicator != nil {
|
|
replicas = status.ReplicasIndicator.FormatTableElem(prevStatus.ReplicasIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
WithTargetValue: true,
|
|
})
|
|
}
|
|
available := "-"
|
|
if status.AvailableIndicator != nil {
|
|
available = status.AvailableIndicator.FormatTableElem(prevStatus.AvailableIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
})
|
|
}
|
|
uptodate := "-"
|
|
if status.UpToDateIndicator != nil {
|
|
uptodate = status.UpToDateIndicator.FormatTableElem(prevStatus.UpToDateIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
})
|
|
}
|
|
|
|
t.Header("DEPLOYMENT", "REPLICAS", "AVAILABLE", "UP-TO-DATE")
|
|
|
|
args := []interface{}{resourceCaption, replicas, available, uptodate}
|
|
if status.IsFailed {
|
|
args = append(args, formatResourceError(status.FailedReason))
|
|
}
|
|
t.Row(args...)
|
|
|
|
displayChildPodsAndWaiting(&t, prevStatus.Pods, status.Pods, status.NewPodsNames, status.WaitingForMessages)
|
|
|
|
writeOut(out, t.Render())
|
|
}
|
|
|
|
func displayStatefulSetStatusProgress(out io.Writer, resourceCaption string, status statefulset.StatefulSetStatus, prevStatus *statefulset.StatefulSetStatus) {
|
|
t := utils.NewTable(statusProgressTableRatio...)
|
|
t.SetWidth(termWidth())
|
|
|
|
showProgress := status.StatusGeneration > prevStatus.StatusGeneration
|
|
|
|
replicas := "-"
|
|
if status.ReplicasIndicator != nil {
|
|
replicas = status.ReplicasIndicator.FormatTableElem(prevStatus.ReplicasIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
WithTargetValue: true,
|
|
})
|
|
}
|
|
ready := "-"
|
|
if status.ReadyIndicator != nil {
|
|
ready = status.ReadyIndicator.FormatTableElem(prevStatus.ReadyIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
})
|
|
}
|
|
uptodate := "-"
|
|
if status.UpToDateIndicator != nil {
|
|
uptodate = status.UpToDateIndicator.FormatTableElem(prevStatus.UpToDateIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
})
|
|
}
|
|
|
|
t.Header("STATEFULSET", "REPLICAS", "READY", "UP-TO-DATE")
|
|
|
|
args := []interface{}{resourceCaption, replicas, ready, uptodate}
|
|
if status.IsFailed {
|
|
args = append(args, formatResourceError(status.FailedReason))
|
|
} else {
|
|
for _, w := range status.WarningMessages {
|
|
args = append(args, formatResourceWarning(w))
|
|
}
|
|
}
|
|
t.Row(args...)
|
|
|
|
displayChildPodsAndWaiting(&t, prevStatus.Pods, status.Pods, status.NewPodsNames, status.WaitingForMessages)
|
|
|
|
writeOut(out, t.Render())
|
|
}
|
|
|
|
func displayDaemonSetStatusProgress(out io.Writer, resourceCaption string, status daemonset.DaemonSetStatus, prevStatus *daemonset.DaemonSetStatus) {
|
|
t := utils.NewTable(statusProgressTableRatio...)
|
|
t.SetWidth(termWidth())
|
|
|
|
showProgress := status.StatusGeneration > prevStatus.StatusGeneration
|
|
|
|
replicas := "-"
|
|
if status.ReplicasIndicator != nil {
|
|
replicas = status.ReplicasIndicator.FormatTableElem(prevStatus.ReplicasIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
WithTargetValue: true,
|
|
})
|
|
}
|
|
available := "-"
|
|
if status.AvailableIndicator != nil {
|
|
available = status.AvailableIndicator.FormatTableElem(prevStatus.AvailableIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
})
|
|
}
|
|
uptodate := "-"
|
|
if status.UpToDateIndicator != nil {
|
|
uptodate = status.UpToDateIndicator.FormatTableElem(prevStatus.UpToDateIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
})
|
|
}
|
|
|
|
t.Header("DAEMONSET", "REPLICAS", "AVAILABLE", "UP-TO-DATE")
|
|
|
|
args := []interface{}{resourceCaption, replicas, available, uptodate}
|
|
if status.IsFailed {
|
|
args = append(args, formatResourceError(status.FailedReason))
|
|
}
|
|
t.Row(args...)
|
|
|
|
displayChildPodsAndWaiting(&t, prevStatus.Pods, status.Pods, status.NewPodsNames, status.WaitingForMessages)
|
|
|
|
writeOut(out, t.Render())
|
|
}
|
|
|
|
func displayJobStatusProgress(out io.Writer, resourceCaption string, status job.JobStatus, prevStatus *job.JobStatus) {
|
|
t := utils.NewTable(statusProgressTableRatio...)
|
|
t.SetWidth(termWidth())
|
|
|
|
showProgress := status.StatusGeneration > prevStatus.StatusGeneration
|
|
|
|
succeeded := "-"
|
|
if status.SucceededIndicator != nil {
|
|
succeeded = status.SucceededIndicator.FormatTableElem(prevStatus.SucceededIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
})
|
|
}
|
|
|
|
t.Header("JOB", "ACTIVE", "DURATION", "SUCCEEDED/FAILED")
|
|
|
|
var active interface{} = "-"
|
|
if status.Active != 0 {
|
|
active = status.Active
|
|
}
|
|
failed := fmt.Sprintf("%d", status.Failed)
|
|
|
|
args := []interface{}{resourceCaption, active, status.Age, strings.Join([]string{succeeded, failed}, "/")}
|
|
if status.IsFailed {
|
|
args = append(args, formatResourceError(status.FailedReason))
|
|
}
|
|
t.Row(args...)
|
|
|
|
if len(status.Pods) > 0 {
|
|
st := displayChildPodsStatusProgress(&t, prevStatus.Pods, status.Pods, nil, showProgress)
|
|
extraMsg := ""
|
|
if len(status.WaitingForMessages) > 0 {
|
|
extraMsg += "---\n"
|
|
extraMsg += utils.BlueF("Waiting for: %s", strings.Join(status.WaitingForMessages, ", "))
|
|
}
|
|
st.Commit(extraMsg)
|
|
}
|
|
|
|
writeOut(out, t.Render())
|
|
}
|
|
|
|
func displayChildPodsAndWaiting(t *utils.Table, prevPods, pods map[string]pod.PodStatus, newPodsNames []string, waitingForMessages []string) {
|
|
if len(pods) > 0 {
|
|
st := displayChildPodsStatusProgress(t, prevPods, pods, newPodsNames, true)
|
|
extraMsg := ""
|
|
if len(waitingForMessages) > 0 {
|
|
extraMsg += "---\n"
|
|
extraMsg += utils.BlueF("Waiting for: %s", strings.Join(waitingForMessages, ", "))
|
|
}
|
|
st.Commit(extraMsg)
|
|
}
|
|
}
|
|
|
|
func displayChildPodsStatusProgress(t *utils.Table, prevPods, pods map[string]pod.PodStatus, newPodsNames []string, showProgress bool) *utils.Table {
|
|
subT := t.SubTable(statusProgressSubTableRatio...)
|
|
st := &subT
|
|
|
|
st.Header("POD", "READY", "RESTARTS", "STATUS")
|
|
|
|
podsNames := make([]string, 0, len(pods))
|
|
for podName := range pods {
|
|
podsNames = append(podsNames, podName)
|
|
}
|
|
sort.Strings(podsNames)
|
|
|
|
var podRows [][]interface{}
|
|
|
|
newPodSet := make(map[string]struct{}, len(newPodsNames))
|
|
for _, name := range newPodsNames {
|
|
newPodSet[name] = struct{}{}
|
|
}
|
|
|
|
for _, podName := range podsNames {
|
|
var podRow []interface{}
|
|
|
|
_, isPodNew := newPodSet[podName]
|
|
|
|
prevPodStatus := prevPods[podName]
|
|
podStatus := pods[podName]
|
|
|
|
isReady := false
|
|
if podStatus.StatusIndicator != nil {
|
|
isReady = podStatus.StatusIndicator.IsReady()
|
|
}
|
|
|
|
resource := formatPodResourceCaption(podName, isReady, podStatus.IsFailed, isPodNew)
|
|
ready := fmt.Sprintf("%d/%d", podStatus.ReadyContainers, podStatus.TotalContainers)
|
|
|
|
status := "-"
|
|
if podStatus.StatusIndicator != nil {
|
|
status = podStatus.StatusIndicator.FormatTableElem(prevPodStatus.StatusIndicator, indicators.FormatTableElemOptions{
|
|
ShowProgress: showProgress,
|
|
IsResourceNew: isPodNew,
|
|
})
|
|
}
|
|
|
|
podRow = append(podRow, resource, ready, podStatus.Restarts, status)
|
|
if podStatus.IsFailed {
|
|
podRow = append(podRow, formatResourceError(podStatus.FailedReason))
|
|
}
|
|
|
|
podRows = append(podRows, podRow)
|
|
}
|
|
|
|
st.Rows(podRows...)
|
|
|
|
return st
|
|
}
|
|
|
|
func formatResourceCaption(caption string, isReady, isFailed bool) string {
|
|
switch {
|
|
case isReady:
|
|
return utils.GreenF("%s", caption)
|
|
case isFailed:
|
|
return utils.RedF("%s", caption)
|
|
default:
|
|
return utils.YellowF("%s", caption)
|
|
}
|
|
}
|
|
|
|
func formatPodResourceCaption(podName string, isReady, isFailed, isNew bool) string {
|
|
if !isNew {
|
|
return podName
|
|
}
|
|
return formatResourceCaption(podName, isReady, isFailed)
|
|
}
|
|
|
|
func formatResourceError(reason string) string {
|
|
return utils.RedF("error: %s", reason)
|
|
}
|
|
|
|
func formatResourceWarning(reason string) string {
|
|
return utils.YellowF("warning: %s", reason)
|
|
}
|
|
|
|
func termWidth() int {
|
|
if w, _, err := term.GetSize(int(os.Stderr.Fd())); err == nil && w > 0 {
|
|
return w
|
|
}
|
|
return 140
|
|
}
|
|
|
|
func displayCanaryStatus(out io.Writer, resourceCaption string, status CanaryStatusView) {
|
|
var parts []string
|
|
if status.Phase != "" {
|
|
parts = append(parts, fmt.Sprintf("phase %s", status.Phase))
|
|
}
|
|
if status.Age != "" {
|
|
parts = append(parts, fmt.Sprintf("age %s", status.Age))
|
|
}
|
|
msg := fmt.Sprintf("%s: %s", resourceCaption, strings.Join(parts, ", "))
|
|
if status.IsFailed {
|
|
msg = utils.RedF("%s", msg)
|
|
}
|
|
_, _ = fmt.Fprintln(out, msg)
|
|
}
|
|
|
|
type CanaryStatusView struct {
|
|
Phase string
|
|
Age string
|
|
IsFailed bool
|
|
}
|
|
|
|
func statusOutput() io.Writer {
|
|
return os.Stderr
|
|
}
|