mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 18:54:06 +00:00
Implement cbor.Marshaler and cbor.Unmarshaler for metav1.Time.
This commit is contained in:
parent
d93a9121b8
commit
7b3129e015
@ -19,6 +19,8 @@ package v1
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
cbor "k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Time is a wrapper around time.Time which supports correct
|
// Time is a wrapper around time.Time which supports correct
|
||||||
@ -116,6 +118,25 @@ func (t *Time) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Time) UnmarshalCBOR(b []byte) error {
|
||||||
|
var s *string
|
||||||
|
if err := cbor.Unmarshal(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s == nil {
|
||||||
|
t.Time = time.Time{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := time.Parse(time.RFC3339, *s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Time = parsed.Local()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalQueryParameter converts from a URL query parameter value to an object
|
// UnmarshalQueryParameter converts from a URL query parameter value to an object
|
||||||
func (t *Time) UnmarshalQueryParameter(str string) error {
|
func (t *Time) UnmarshalQueryParameter(str string) error {
|
||||||
if len(str) == 0 {
|
if len(str) == 0 {
|
||||||
@ -151,6 +172,14 @@ func (t Time) MarshalJSON() ([]byte, error) {
|
|||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t Time) MarshalCBOR() ([]byte, error) {
|
||||||
|
if t.IsZero() {
|
||||||
|
return cbor.Marshal(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cbor.Marshal(t.UTC().Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
|
||||||
// ToUnstructured implements the value.UnstructuredConverter interface.
|
// ToUnstructured implements the value.UnstructuredConverter interface.
|
||||||
func (t Time) ToUnstructured() interface{} {
|
func (t Time) ToUnstructured() interface{} {
|
||||||
if t.IsZero() {
|
if t.IsZero() {
|
||||||
|
@ -18,11 +18,16 @@ package v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
cbor "k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
fuzz "github.com/google/gofuzz"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TimeHolder struct {
|
type TimeHolder struct {
|
||||||
@ -100,6 +105,7 @@ func TestTimeUnmarshalJSON(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"{\"t\":null}", Time{}},
|
{"{\"t\":null}", Time{}},
|
||||||
{"{\"t\":\"1998-05-05T05:05:05Z\"}", Time{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC).Local()}},
|
{"{\"t\":\"1998-05-05T05:05:05Z\"}", Time{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC).Local()}},
|
||||||
|
{"{\"t\":\"1998-05-05T05:05:05.123456789Z\"}", Time{Date(1998, time.May, 5, 5, 5, 5, 123456789, time.UTC).Local()}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@ -147,6 +153,62 @@ func TestTimeMarshalJSONUnmarshalYAML(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTimeMarshalCBOR(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
in Time
|
||||||
|
out []byte
|
||||||
|
}{
|
||||||
|
{name: "zero value", in: Time{}, out: []byte{0xf6}}, // null
|
||||||
|
{name: "no fractional seconds", in: Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC), out: []byte("\x541998-05-05T05:05:05Z")}, // '1998-05-05T05:05:05Z'
|
||||||
|
{name: "fractional seconds truncated", in: Date(1998, time.May, 5, 5, 5, 5, 123456789, time.UTC), out: []byte("\x541998-05-05T05:05:05Z")}, // '1998-05-05T05:05:05Z'
|
||||||
|
{name: "epoch", in: Time{Time: time.Unix(0, 0)}, out: []byte("\x541970-01-01T00:00:00Z")}, // '1970-01-01T00:00:00Z'
|
||||||
|
{name: "pre-epoch", in: Date(1960, time.January, 1, 0, 0, 0, 0, time.UTC), out: []byte("\x541960-01-01T00:00:00Z")}, // '1960-01-01T00:00:00Z'
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("%+v", tc.in), func(t *testing.T) {
|
||||||
|
got, err := tc.in.MarshalCBOR()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tc.out, got); diff != "" {
|
||||||
|
t.Errorf("unexpected output:\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeUnmarshalCBOR(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
in []byte
|
||||||
|
out Time
|
||||||
|
errMessage string
|
||||||
|
}{
|
||||||
|
{name: "null", in: []byte{0xf6}, out: Time{}}, // null
|
||||||
|
{name: "no fractional seconds", in: []byte("\x58\x141998-05-05T05:05:05Z"), out: Time{Time: Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC).Local()}}, // '1998-05-05T05:05:05Z'
|
||||||
|
{name: "fractional seconds", in: []byte("\x58\x1e1998-05-05T05:05:05.123456789Z"), out: Time{Time: Date(1998, time.May, 5, 5, 5, 5, 123456789, time.UTC).Local()}}, // '1998-05-05T05:05:05.123456789Z'
|
||||||
|
{name: "invalid cbor type", in: []byte{0x07}, out: Time{}, errMessage: "cbor: cannot unmarshal positive integer into Go value of type string"}, // 7
|
||||||
|
{name: "malformed timestamp", in: []byte("\x45hello"), out: Time{}, errMessage: `parsing time "hello" as "2006-01-02T15:04:05Z07:00": cannot parse "hello" as "2006"`}, // 'hello'
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
var got Time
|
||||||
|
err := got.UnmarshalCBOR(tc.in)
|
||||||
|
if err != nil {
|
||||||
|
if tc.errMessage == "" {
|
||||||
|
t.Fatalf("want nil error, got: %v", err)
|
||||||
|
} else if gotMessage := err.Error(); tc.errMessage != gotMessage {
|
||||||
|
t.Fatalf("want error: %q, got: %q", tc.errMessage, gotMessage)
|
||||||
|
}
|
||||||
|
} else if tc.errMessage != "" {
|
||||||
|
t.Fatalf("got nil error, want: %s", tc.errMessage)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tc.out, got); diff != "" {
|
||||||
|
t.Errorf("unexpected output:\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTimeProto(t *testing.T) {
|
func TestTimeProto(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
input Time
|
input Time
|
||||||
@ -239,3 +301,27 @@ func TestTimeIsZero(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTimeRoundtripCBOR(t *testing.T) {
|
||||||
|
fuzzer := fuzz.New()
|
||||||
|
for i := 0; i < 500; i++ {
|
||||||
|
var initial, final Time
|
||||||
|
fuzzer.Fuzz(&initial)
|
||||||
|
b, err := cbor.Marshal(initial)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error encoding %v: %v", initial, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = cbor.Unmarshal(b, &final)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v: error decoding %v: %v", initial, string(b), err)
|
||||||
|
}
|
||||||
|
if !final.Equal(&initial) {
|
||||||
|
diag, err := cbor.Diagnose(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to produce diagnostic encoding of 0x%x: %v", b, err)
|
||||||
|
}
|
||||||
|
t.Errorf("expected equal: %v, %v (cbor was '%s')", initial, final, diag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user