Skip to content

Commit 6253e16

Browse files
benluddyk8s-publishing-bot
authored andcommitted
Implement cbor.Marshaler and cbor.Unmarshaler for metav1.MicroTime.
Kubernetes-commit: 14367eee5a2516a3ab7a960a828e95bafb1ff0e2
1 parent b19cb35 commit 6253e16

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

pkg/apis/meta/v1/micro_time.go

+28
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package v1
1919
import (
2020
"encoding/json"
2121
"time"
22+
23+
cbor "k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct"
2224
)
2325

2426
const RFC3339Micro = "2006-01-02T15:04:05.000000Z07:00"
@@ -129,6 +131,25 @@ func (t *MicroTime) UnmarshalJSON(b []byte) error {
129131
return nil
130132
}
131133

134+
func (t *MicroTime) UnmarshalCBOR(b []byte) error {
135+
var s *string
136+
if err := cbor.Unmarshal(b, &s); err != nil {
137+
return err
138+
}
139+
if s == nil {
140+
t.Time = time.Time{}
141+
return nil
142+
}
143+
144+
parsed, err := time.Parse(RFC3339Micro, *s)
145+
if err != nil {
146+
return err
147+
}
148+
149+
t.Time = parsed.Local()
150+
return nil
151+
}
152+
132153
// UnmarshalQueryParameter converts from a URL query parameter value to an object
133154
func (t *MicroTime) UnmarshalQueryParameter(str string) error {
134155
if len(str) == 0 {
@@ -160,6 +181,13 @@ func (t MicroTime) MarshalJSON() ([]byte, error) {
160181
return json.Marshal(t.UTC().Format(RFC3339Micro))
161182
}
162183

184+
func (t MicroTime) MarshalCBOR() ([]byte, error) {
185+
if t.IsZero() {
186+
return cbor.Marshal(nil)
187+
}
188+
return cbor.Marshal(t.UTC().Format(RFC3339Micro))
189+
}
190+
163191
// OpenAPISchemaType is used by the kube-openapi generator when constructing
164192
// the OpenAPI spec of this type.
165193
//

pkg/apis/meta/v1/micro_time_test.go

+82
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,16 @@ package v1
1818

1919
import (
2020
"encoding/json"
21+
"fmt"
2122
"reflect"
2223
"testing"
2324
"time"
2425

26+
cbor "k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct"
2527
"sigs.k8s.io/yaml"
28+
29+
"github.com/google/go-cmp/cmp"
30+
fuzz "github.com/google/gofuzz"
2631
)
2732

2833
type MicroTimeHolder struct {
@@ -113,6 +118,59 @@ func TestMicroTimeUnmarshalJSON(t *testing.T) {
113118
}
114119
}
115120

121+
func TestMicroTimeMarshalCBOR(t *testing.T) {
122+
for _, tc := range []struct {
123+
name string
124+
in MicroTime
125+
out []byte
126+
}{
127+
{name: "zero value", in: MicroTime{}, out: []byte{0xf6}}, // null
128+
{name: "no fractional seconds", in: DateMicro(1998, time.May, 5, 5, 5, 5, 0, time.UTC), out: []byte("\x58\x1b1998-05-05T05:05:05.000000Z")}, // '1998-05-05T05:05:05.000000Z'
129+
{name: "nanoseconds truncated", in: DateMicro(1998, time.May, 5, 5, 5, 5, 5050, time.UTC), out: []byte("\x58\x1b1998-05-05T05:05:05.000005Z")}, // '1998-05-05T05:05:05.000005Z'
130+
} {
131+
t.Run(fmt.Sprintf("%+v", tc.in), func(t *testing.T) {
132+
got, err := tc.in.MarshalCBOR()
133+
if err != nil {
134+
t.Fatal(err)
135+
}
136+
if diff := cmp.Diff(tc.out, got); diff != "" {
137+
t.Errorf("unexpected output:\n%s", diff)
138+
}
139+
})
140+
}
141+
}
142+
143+
func TestMicroTimeUnmarshalCBOR(t *testing.T) {
144+
for _, tc := range []struct {
145+
name string
146+
in []byte
147+
out MicroTime
148+
errMessage string
149+
}{
150+
{name: "null", in: []byte{0xf6}, out: MicroTime{}}, // null
151+
{name: "valid", in: []byte("\x58\x1b1998-05-05T05:05:05.000000Z"), out: MicroTime{Time: Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC).Local()}}, // '1998-05-05T05:05:05.000000Z'
152+
{name: "invalid cbor type", in: []byte{0x07}, out: MicroTime{}, errMessage: "cbor: cannot unmarshal positive integer into Go value of type string"}, // 7
153+
{name: "malformed timestamp", in: []byte("\x45hello"), out: MicroTime{}, errMessage: `parsing time "hello" as "2006-01-02T15:04:05.000000Z07:00": cannot parse "hello" as "2006"`}, // 'hello'
154+
} {
155+
t.Run(tc.name, func(t *testing.T) {
156+
var got MicroTime
157+
err := got.UnmarshalCBOR(tc.in)
158+
if err != nil {
159+
if tc.errMessage == "" {
160+
t.Fatalf("want nil error, got: %v", err)
161+
} else if gotMessage := err.Error(); tc.errMessage != gotMessage {
162+
t.Fatalf("want error: %q, got: %q", tc.errMessage, gotMessage)
163+
}
164+
} else if tc.errMessage != "" {
165+
t.Fatalf("got nil error, want: %s", tc.errMessage)
166+
}
167+
if diff := cmp.Diff(tc.out, got); diff != "" {
168+
t.Errorf("unexpected output:\n%s", diff)
169+
}
170+
})
171+
}
172+
}
173+
116174
func TestMicroTimeProto(t *testing.T) {
117175
cases := []struct {
118176
input MicroTime
@@ -318,3 +376,27 @@ func TestMicroTimeProtoUnmarshalRaw(t *testing.T) {
318376
}
319377

320378
}
379+
380+
func TestMicroTimeRoundtripCBOR(t *testing.T) {
381+
fuzzer := fuzz.New()
382+
for i := 0; i < 500; i++ {
383+
var initial, final MicroTime
384+
fuzzer.Fuzz(&initial)
385+
b, err := cbor.Marshal(initial)
386+
if err != nil {
387+
t.Errorf("error encoding %v: %v", initial, err)
388+
continue
389+
}
390+
err = cbor.Unmarshal(b, &final)
391+
if err != nil {
392+
t.Errorf("%v: error decoding %v: %v", initial, string(b), err)
393+
}
394+
if !final.Equal(&initial) {
395+
diag, err := cbor.Diagnose(b)
396+
if err != nil {
397+
t.Logf("failed to produce diagnostic encoding of 0x%x: %v", b, err)
398+
}
399+
t.Errorf("expected equal: %v, %v (cbor was '%s')", initial, final, diag)
400+
}
401+
}
402+
}

0 commit comments

Comments
 (0)