1
0
mirror of https://github.com/rancher/steve.git synced 2025-08-11 03:01:38 +00:00

changing ParseHumanDuration to support timestamp values too (#684)

This commit is contained in:
Felipe Gehrke 2025-06-18 14:57:49 -03:00 committed by GitHub
parent b3539616e0
commit 4212386e13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 34 additions and 10 deletions

View File

@ -6,11 +6,20 @@ import (
"time" "time"
) )
func ParseHumanReadableDuration(s string) (time.Duration, error) { // ParseTimestampOrHumanReadableDuration can do one of three things with an incoming string:
// 1. Recognize it's an absolute timestamp and calculate a relative `time.Duration`
// 2. Recognize it's a human-readable duration (like 3m) and convert to a relative `time.Duration`
// 3. Return an error because it doesn't recognize the input
func ParseTimestampOrHumanReadableDuration(s string) (time.Duration, error) {
var total time.Duration var total time.Duration
var val int var val int
var unit byte var unit byte
parsedTime, err := time.Parse(time.RFC3339, s)
if err == nil {
return time.Since(parsedTime), nil
}
r := strings.NewReader(s) r := strings.NewReader(s)
for r.Len() > 0 { for r.Len() > 0 {
if _, err := fmt.Fscanf(r, "%d%c", &val, &unit); err != nil { if _, err := fmt.Fscanf(r, "%d%c", &val, &unit); err != nil {

View File

@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestParseHumanDuration(t *testing.T) { func TestParseHumanReadableDuration(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
input string input string
@ -63,7 +63,7 @@ func TestParseHumanDuration(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
output, err := ParseHumanReadableDuration(tc.input) output, err := ParseTimestampOrHumanReadableDuration(tc.input)
if tc.expectedErr { if tc.expectedErr {
assert.Error(t, err) assert.Error(t, err)
} else { } else {
@ -71,4 +71,8 @@ func TestParseHumanDuration(t *testing.T) {
} }
}) })
} }
// Testing timestamp parsing separately
_, err := ParseTimestampOrHumanReadableDuration("2024-02-12T15:19:21Z")
assert.NoError(t, err)
} }

View File

@ -247,29 +247,37 @@ func convertMetadataTimestampFields(request *types.APIRequest, gvk schema2.Group
curValue, got, err := unstructured.NestedSlice(unstr.Object, "metadata", "fields") curValue, got, err := unstructured.NestedSlice(unstr.Object, "metadata", "fields")
if err != nil { if err != nil {
logrus.Errorf("failed to get metadata.fields slice from unstr.Object: %s", err.Error()) logrus.Warnf("failed to get metadata.fields slice from unstr.Object: %s", err.Error())
return
} }
if !got { if !got {
logrus.Debugf("couldn't find metadata.fields at unstr.Object") logrus.Warnf("couldn't find metadata.fields at unstr.Object")
return return
} }
timeValue, ok := curValue[index].(string) timeValue, ok := curValue[index].(string)
if !ok { if !ok {
logrus.Debugf("time field isn't a string") logrus.Warnf("time field isn't a string")
return return
} }
millis, err := strconv.ParseInt(timeValue, 10, 64) millis, err := strconv.ParseInt(timeValue, 10, 64)
if err != nil { if err != nil {
logrus.Warnf("failed to convert timestamp value: %s", err.Error()) logrus.Warnf("convert timestamp value: %s failed with error: %s", timeValue, err.Error())
return return
} }
timestamp := time.Unix(0, millis*int64(time.Millisecond)) timestamp := time.Unix(0, millis*int64(time.Millisecond))
dur := time.Since(timestamp) dur := time.Since(timestamp)
curValue[index] = duration.HumanDuration(dur) humanDuration := duration.HumanDuration(dur)
if humanDuration == "<invalid>" {
logrus.Warnf("couldn't convert value %d into human duration for column %s", int64(dur), col.Name)
return
}
curValue[index] = humanDuration
if err := unstructured.SetNestedSlice(unstr.Object, curValue, "metadata", "fields"); err != nil { if err := unstructured.SetNestedSlice(unstr.Object, curValue, "metadata", "fields"); err != nil {
logrus.Errorf("failed to set value back to metadata.fields slice: %s", err.Error()) logrus.Errorf("failed to set value back to metadata.fields slice: %s", err.Error())
return return

View File

@ -11,6 +11,7 @@ import (
"github.com/rancher/steve/pkg/resources/virtual/clusters" "github.com/rancher/steve/pkg/resources/virtual/clusters"
"github.com/rancher/steve/pkg/resources/virtual/common" "github.com/rancher/steve/pkg/resources/virtual/common"
"github.com/rancher/steve/pkg/resources/virtual/events" "github.com/rancher/steve/pkg/resources/virtual/events"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
@ -64,9 +65,11 @@ func (t *TransformBuilder) GetTransformFunc(gvk schema.GroupVersionKind, columns
if !cast { if !cast {
return nil, fmt.Errorf("could not cast metadata.fields %d to string", index) return nil, fmt.Errorf("could not cast metadata.fields %d to string", index)
} }
duration, err := rescommon.ParseHumanReadableDuration(value)
duration, err := rescommon.ParseTimestampOrHumanReadableDuration(value)
if err != nil { if err != nil {
return nil, err logrus.Errorf("parse timestamp %s, failed with error: %s", value, err)
return obj, nil
} }
curValue[index] = fmt.Sprintf("%d", now.Add(-duration).UnixMilli()) curValue[index] = fmt.Sprintf("%d", now.Add(-duration).UnixMilli())