Improve the output of kubectl get events

Events have long shown the most data of the core objects in their output, but that data is of varying use
to a user. Following the principle that events are intended for the system to communicate information back
to the user, and that Message is the primary human readable field, this commit alters the default columns
to ensure event is shown with the most width.

1. Events are no longer sorted in the printer (this was a bug and was broken with paging and server side
   rendering)
2. Only the last seen, type, reason, kind, and message fields are shown by default, which makes the
   message prominent
3. Source, subobject, count, and first seen are only shown under `-o wide`
4. The duration fields were changed to be the more precise output introduced for job duration (2-3 sig figs)
This commit is contained in:
Clayton Coleman 2018-07-26 01:14:39 -04:00
parent 845a55dbbd
commit 2f275b72b2
No known key found for this signature in database
GPG Key ID: 3D16906B4F1C5CB3
4 changed files with 53 additions and 26 deletions

View File

@ -21,7 +21,6 @@ import (
"fmt"
"io"
"net"
"sort"
"strconv"
"strings"
"time"
@ -44,7 +43,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/duration"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/api/events"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
@ -236,15 +234,15 @@ func AddHandlers(h printers.PrintHandler) {
eventColumnDefinitions := []metav1beta1.TableColumnDefinition{
{Name: "Last Seen", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["lastTimestamp"]},
{Name: "First Seen", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["firstTimestamp"]},
{Name: "Count", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["count"]},
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
{Name: "Kind", Type: "string", Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["kind"]},
{Name: "Subobject", Type: "string", Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["fieldPath"]},
{Name: "Type", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["type"]},
{Name: "Reason", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["reason"]},
{Name: "Source", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["source"]},
{Name: "Kind", Type: "string", Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["kind"]},
{Name: "Source", Type: "string", Priority: 1, Description: apiv1.Event{}.SwaggerDoc()["source"]},
{Name: "Message", Type: "string", Description: apiv1.Event{}.SwaggerDoc()["message"]},
{Name: "Subobject", Type: "string", Priority: 1, Description: apiv1.Event{}.InvolvedObject.SwaggerDoc()["fieldPath"]},
{Name: "First Seen", Type: "string", Priority: 1, Description: apiv1.Event{}.SwaggerDoc()["firstTimestamp"]},
{Name: "Count", Type: "string", Priority: 1, Description: apiv1.Event{}.SwaggerDoc()["count"]},
{Name: "Name", Type: "string", Priority: 1, Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
}
h.TableHandler(eventColumnDefinitions, printEvent)
h.TableHandler(eventColumnDefinitions, printEventList)
@ -515,7 +513,7 @@ func translateTimestampSince(timestamp metav1.Time) string {
return "<unknown>"
}
return duration.ShortHumanDuration(time.Since(timestamp.Time))
return duration.HumanDuration(time.Since(timestamp.Time))
}
// translateTimestampUntil returns the elapsed time until timestamp in
@ -525,7 +523,7 @@ func translateTimestampUntil(timestamp metav1.Time) string {
return "<unknown>"
}
return duration.ShortHumanDuration(time.Until(timestamp.Time))
return duration.HumanDuration(time.Until(timestamp.Time))
}
var (
@ -1328,25 +1326,42 @@ func printEvent(obj *api.Event, options printers.PrintOptions) ([]metav1beta1.Ta
Object: runtime.RawExtension{Object: obj},
}
// While watching event, we should print absolute time.
var FirstTimestamp, LastTimestamp string
var firstTimestamp, lastTimestamp string
if options.AbsoluteTimestamps {
FirstTimestamp = obj.FirstTimestamp.String()
LastTimestamp = obj.LastTimestamp.String()
firstTimestamp = obj.FirstTimestamp.String()
lastTimestamp = obj.LastTimestamp.String()
} else {
FirstTimestamp = translateTimestampSince(obj.FirstTimestamp)
LastTimestamp = translateTimestampSince(obj.LastTimestamp)
firstTimestamp = translateTimestampSince(obj.FirstTimestamp)
lastTimestamp = translateTimestampSince(obj.LastTimestamp)
}
if options.Wide {
row.Cells = append(row.Cells,
lastTimestamp,
obj.Type,
obj.Reason,
obj.InvolvedObject.Kind,
formatEventSource(obj.Source),
strings.TrimSpace(obj.Message),
obj.InvolvedObject.FieldPath,
firstTimestamp,
int64(obj.Count),
obj.Name,
)
} else {
row.Cells = append(row.Cells,
lastTimestamp,
obj.Type,
obj.Reason,
obj.InvolvedObject.Kind,
strings.TrimSpace(obj.Message),
)
}
row.Cells = append(row.Cells, LastTimestamp, FirstTimestamp,
int64(obj.Count), obj.Name, obj.InvolvedObject.Kind,
obj.InvolvedObject.FieldPath, obj.Type, obj.Reason,
formatEventSource(obj.Source), obj.Message)
return []metav1beta1.TableRow{row}, nil
}
// Sorts and prints the EventList in a human-friendly format.
func printEventList(list *api.EventList, options printers.PrintOptions) ([]metav1beta1.TableRow, error) {
sort.Sort(events.SortableEvents(list.Items))
rows := make([]metav1beta1.TableRow, 0, len(list.Items))
for i := range list.Items {
r, err := printEvent(&list.Items[i], options)

View File

@ -1927,7 +1927,7 @@ func TestTranslateTimestampSince(t *testing.T) {
{"unknown", translateTimestampSince(metav1.Time{}), "<unknown>"},
{"30 seconds ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e10)}), "30s"},
{"5 minutes ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-3e11)}), "5m"},
{"an hour ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-6e12)}), "1h"},
{"an hour ago", translateTimestampSince(metav1.Time{Time: time.Now().Add(-6e12)}), "100m"},
{"2 days ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -2)}), "2d"},
{"months ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, -90)}), "90d"},
{"10 years ago", translateTimestampSince(metav1.Time{Time: time.Now().UTC().AddDate(-10, 0, 0)}), "10y"},
@ -1952,7 +1952,7 @@ func TestTranslateTimestampUntil(t *testing.T) {
{"unknown", translateTimestampUntil(metav1.Time{}), "<unknown>"},
{"in 30 seconds", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e10 + buf)}), "30s"},
{"in 5 minutes", translateTimestampUntil(metav1.Time{Time: time.Now().Add(3e11 + buf)}), "5m"},
{"in an hour", translateTimestampUntil(metav1.Time{Time: time.Now().Add(6e12 + buf)}), "1h"},
{"in an hour", translateTimestampUntil(metav1.Time{Time: time.Now().Add(6e12 + buf)}), "100m"},
{"in 2 days", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 2).Add(buf)}), "2d"},
{"in months", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(0, 0, 90).Add(buf)}), "90d"},
{"in 10 years", translateTimestampUntil(metav1.Time{Time: time.Now().UTC().AddDate(10, 0, 0).Add(buf)}), "10y"},

View File

@ -463,7 +463,7 @@ func TestConvertToTableList(t *testing.T) {
out: &metav1beta1.Table{
ColumnDefinitions: columns,
Rows: []metav1beta1.TableRow{
{Cells: []interface{}{"foo", "1/2", "Pending", int64(10), "1y", "10.1.2.3", "test-node", "nominated-node"}, Object: runtime.RawExtension{Object: pod1}},
{Cells: []interface{}{"foo", "1/2", "Pending", int64(10), "370d", "10.1.2.3", "test-node", "nominated-node"}, Object: runtime.RawExtension{Object: pod1}},
},
},
},

View File

@ -57,17 +57,29 @@ func HumanDuration(d time.Duration) string {
}
minutes := int(d / time.Minute)
if minutes < 10 {
return fmt.Sprintf("%dm%ds", minutes, int(d/time.Second)%60)
s := int(d/time.Second) % 60
if s == 0 {
return fmt.Sprintf("%dm", minutes)
}
return fmt.Sprintf("%dm%ds", minutes, s)
} else if minutes < 60*3 {
return fmt.Sprintf("%dm", minutes)
}
hours := int(d / time.Hour)
if hours < 8 {
return fmt.Sprintf("%dh%dm", hours, int(d/time.Minute)%60)
m := int(d/time.Minute) % 60
if m == 0 {
return fmt.Sprintf("%dh", hours)
}
return fmt.Sprintf("%dh%dm", hours, m)
} else if hours < 48 {
return fmt.Sprintf("%dh", hours)
} else if hours < 24*8 {
return fmt.Sprintf("%dd%dh", hours/24, hours%24)
h := hours % 24
if h == 0 {
return fmt.Sprintf("%dd", hours/24)
}
return fmt.Sprintf("%dd%dh", hours/24, h)
} else if hours < 24*365*2 {
return fmt.Sprintf("%dd", hours/24)
} else if hours < 24*365*8 {