mirror of https://github.com/k3s-io/k3s
Print container statuses in `kubectl get pods`
`kubectl get pod` already prints one container per line. This change fills in the status for each container listed. This aims to help users quickly identify unhealthy pods (e.g. in a crash loop) at a glance. - The first row of every pod would display the pod information and status - Each row of the subsequent rows corresponds to a container in that pod: * STATUS refers to the container status (Running, Waiting, Terminated). * CREATED refers to the elapsed time since the last start time of the container. * MESSAGE is a string which explains the last termination reason, and/or the reason behind the waiting status.pull/6/head
parent
23e806604d
commit
06125f37d3
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
|
||||||
"github.com/docker/docker/pkg/units"
|
"github.com/docker/docker/pkg/units"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
|
@ -243,7 +244,7 @@ func (h *HumanReadablePrinter) HandledResources() []string {
|
||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
var podColumns = []string{"POD", "IP", "CONTAINER(S)", "IMAGE(S)", "HOST", "LABELS", "STATUS", "CREATED"}
|
var podColumns = []string{"POD", "IP", "CONTAINER(S)", "IMAGE(S)", "HOST", "LABELS", "STATUS", "CREATED", "MESSAGE"}
|
||||||
var replicationControllerColumns = []string{"CONTROLLER", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "REPLICAS"}
|
var replicationControllerColumns = []string{"CONTROLLER", "CONTAINER(S)", "IMAGE(S)", "SELECTOR", "REPLICAS"}
|
||||||
var serviceColumns = []string{"NAME", "LABELS", "SELECTOR", "IP", "PORT(S)"}
|
var serviceColumns = []string{"NAME", "LABELS", "SELECTOR", "IP", "PORT(S)"}
|
||||||
var endpointColumns = []string{"NAME", "ENDPOINTS"}
|
var endpointColumns = []string{"NAME", "ENDPOINTS"}
|
||||||
|
@ -339,32 +340,97 @@ func podHostString(host, ip string) string {
|
||||||
return host + "/" + ip
|
return host + "/" + ip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// translateTimestamp returns the elapsed time since timestamp in
|
||||||
|
// human-readable approximation.
|
||||||
|
func translateTimestamp(timestamp util.Time) string {
|
||||||
|
return units.HumanDuration(time.Now().Sub(timestamp.Time))
|
||||||
|
}
|
||||||
|
|
||||||
|
// interpretContainerStatus interprets the container status and returns strings
|
||||||
|
// associated with columns "STATUS", "CREATED", and "MESSAGE".
|
||||||
|
// The meaning of MESSAGE varies based on the context of STATUS:
|
||||||
|
// STATUS: Waiting; MESSAGE: reason for waiting
|
||||||
|
// STATUS: Running; MESSAGE: reason for the last termination
|
||||||
|
// STATUS: Terminated; MESSAGE: reason for this termination
|
||||||
|
func interpretContainerStatus(status *api.ContainerStatus) (string, string, string, error) {
|
||||||
|
// Helper function to compose a meaning message from terminate state.
|
||||||
|
getTermMsg := func(state *api.ContainerStateTerminated) string {
|
||||||
|
var message string
|
||||||
|
if state != nil {
|
||||||
|
message = fmt.Sprintf("exit code %d", state.ExitCode)
|
||||||
|
if state.Reason != "" {
|
||||||
|
message = fmt.Sprintf("%s, reason: %s", state.Reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
state := &status.State
|
||||||
|
if state.Waiting != nil {
|
||||||
|
return "Waiting", "", state.Waiting.Reason, nil
|
||||||
|
} else if state.Running != nil {
|
||||||
|
// Get the information of the last termination state. This is useful if
|
||||||
|
// a container is stuck in a crash loop.
|
||||||
|
message := getTermMsg(status.LastTerminationState.Termination)
|
||||||
|
if message != "" {
|
||||||
|
message = "last termination: " + message
|
||||||
|
}
|
||||||
|
return "Running", translateTimestamp(state.Running.StartedAt), message, nil
|
||||||
|
} else if state.Termination != nil {
|
||||||
|
return "Terminated", translateTimestamp(state.Termination.StartedAt), getTermMsg(state.Termination), nil
|
||||||
|
}
|
||||||
|
return "", "", "", fmt.Errorf("unknown container state %#v", *state)
|
||||||
|
}
|
||||||
|
|
||||||
func printPod(pod *api.Pod, w io.Writer) error {
|
func printPod(pod *api.Pod, w io.Writer) error {
|
||||||
// TODO: remove me when pods are converted
|
// TODO: remove me when pods are converted
|
||||||
spec := &api.PodSpec{}
|
spec := &api.PodSpec{}
|
||||||
if err := api.Scheme.Convert(&pod.Spec, spec); err != nil {
|
if err := api.Scheme.Convert(&pod.Spec, spec); err != nil {
|
||||||
glog.Errorf("Unable to convert pod manifest: %v", err)
|
glog.Errorf("Unable to convert pod manifest: %v", err)
|
||||||
}
|
}
|
||||||
containers := spec.Containers
|
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||||
var firstContainer api.Container
|
|
||||||
if len(containers) > 0 {
|
|
||||||
firstContainer, containers = containers[0], containers[1:]
|
|
||||||
}
|
|
||||||
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
|
||||||
pod.Name,
|
pod.Name,
|
||||||
pod.Status.PodIP,
|
pod.Status.PodIP,
|
||||||
firstContainer.Name,
|
"", "",
|
||||||
firstContainer.Image,
|
|
||||||
podHostString(pod.Spec.Host, pod.Status.HostIP),
|
podHostString(pod.Spec.Host, pod.Status.HostIP),
|
||||||
formatLabels(pod.Labels),
|
formatLabels(pod.Labels),
|
||||||
pod.Status.Phase,
|
pod.Status.Phase,
|
||||||
units.HumanDuration(time.Now().Sub(pod.CreationTimestamp.Time)))
|
translateTimestamp(pod.CreationTimestamp),
|
||||||
|
pod.Status.Message,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Lay out all the other containers on separate lines.
|
// Lay out all containers on separate lines.
|
||||||
for _, container := range containers {
|
statuses := pod.Status.ContainerStatuses
|
||||||
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "", "", container.Name, container.Image, "", "", "", "")
|
if len(statuses) == 0 {
|
||||||
|
// Container status has not been reported yet. Print basic information
|
||||||
|
// of the containers and exit the function.
|
||||||
|
for _, container := range pod.Spec.Containers {
|
||||||
|
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||||
|
"", "", container.Name, container.Image, "", "", "", "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the actual container statuses.
|
||||||
|
for _, status := range statuses {
|
||||||
|
state, created, message, err := interpretContainerStatus(&status)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
|
||||||
|
"", "",
|
||||||
|
status.Name,
|
||||||
|
status.Image,
|
||||||
|
"", "",
|
||||||
|
state,
|
||||||
|
created,
|
||||||
|
message,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,6 +336,8 @@ func (dm *DockerManager) GetPodStatus(pod *api.Pod) (*api.PodStatus, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var containerStatus api.ContainerStatus
|
var containerStatus api.ContainerStatus
|
||||||
|
containerStatus.Name = container.Name
|
||||||
|
containerStatus.Image = container.Image
|
||||||
if oldStatus, found := oldStatuses[container.Name]; found {
|
if oldStatus, found := oldStatuses[container.Name]; found {
|
||||||
// Some states may be lost due to GC; apply the last observed
|
// Some states may be lost due to GC; apply the last observed
|
||||||
// values if possible.
|
// values if possible.
|
||||||
|
|
Loading…
Reference in New Issue