fix(dashboard): fall back to .firstTimestamp in Event Time column

core/v1 Events populate .lastTimestamp and .firstTimestamp but leave
.eventTime null; events.k8s.io/v1 Events do the opposite. The previous
column bound to .eventTime alone and rendered 'Invalid Date' for every
Helm-generated event.

Extend createTimestampColumn with an optional second jsonPath that is
encoded as a nested reqsJsonPath fallback in the template, and use
.eventTime → .firstTimestamp for Event Time and .lastTimestamp →
.eventTime for Last Seen so both APIs render correctly.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
This commit is contained in:
Aleksei Sviridkin 2026-04-16 01:10:10 +03:00
parent d369b64d71
commit 9cc5deeabe
No known key found for this signature in database
GPG key ID: 7988329FDF395282
2 changed files with 29 additions and 7 deletions

View file

@ -650,12 +650,31 @@ func createStringColumn(name, jsonPath string) map[string]any {
}
}
// createTimestampColumn creates a timestamp column with custom formatting
func createTimestampColumn(name, jsonPath string) map[string]any {
// createTimestampColumn creates a timestamp column with custom formatting.
// Extra jsonPaths act as ordered fallbacks: the first non-null path wins,
// and `-` is used only when every path is null. Depth is capped at two
// because the template parser's non-greedy matcher cannot track more than
// one level of alternating quotes in a nested reqsJsonPath fallback.
func createTimestampColumn(name string, jsonPaths ...string) map[string]any {
if len(jsonPaths) == 0 {
panic("createTimestampColumn requires at least one jsonPath")
}
if len(jsonPaths) > 2 {
panic("createTimestampColumn supports at most two jsonPaths (primary + one fallback)")
}
var text string
switch len(jsonPaths) {
case 1:
text = "{reqsJsonPath[0]['" + jsonPaths[0] + "']['-']}"
case 2:
text = "{reqsJsonPath[0]['" + jsonPaths[0] + "'][\"{reqsJsonPath[0]['" + jsonPaths[1] + "']['-']}\"]}"
}
return map[string]any{
"name": name,
"type": "factory",
"jsonPath": jsonPath,
"jsonPath": jsonPaths[0],
"customProps": map[string]any{
"disableEventBubbling": true,
"items": []any{
@ -673,7 +692,7 @@ func createTimestampColumn(name, jsonPath string) map[string]any {
"data": map[string]any{
"formatter": "timestamp",
"id": "time-value",
"text": "{reqsJsonPath[0]['" + jsonPath + "']['-']}",
"text": text,
},
},
},

View file

@ -224,10 +224,13 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
createStringColumn("Name", ".name"),
}),
// Factory details events
// Factory details events. Event Time falls back to `.firstTimestamp`
// because core/v1 Events (Helm, controller-runtime) populate only the
// legacy timestamps while events.k8s.io/v1 Events populate only
// `.eventTime`; taking the first non-null of the two covers both.
createCustomColumnsOverride("factory-details-events", []any{
createTimestampColumn("Last Seen", ".lastTimestamp"),
createTimestampColumn("Event Time", ".eventTime"),
createTimestampColumn("Last Seen", ".lastTimestamp", ".eventTime"),
createTimestampColumn("Event Time", ".eventTime", ".firstTimestamp"),
createStringColumn("Type", ".type"),
createStringColumn("Reason", ".reason"),
createStringColumn("Object", ".involvedObject.kind"),