Skip to content

Commit 438c406

Browse files
committed
fix panic when calling ToUnstructured on nil metav1.Time
1 parent cf09e71 commit 438c406

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

value/reflectcache.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,18 +184,18 @@ func (e TypeReflectCacheEntry) ToUnstructured(sv reflect.Value) (interface{}, er
184184
// This is based on https://github.com/kubernetes/kubernetes/blob/82c9e5c814eb7acc6cc0a090c057294d0667ad66/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L505
185185
// and is intended to replace it.
186186

187+
// Check if the object is a nil pointer.
188+
if sv.Kind() == reflect.Ptr && sv.IsNil() {
189+
// We're done - we don't need to store anything.
190+
return nil, nil
191+
}
187192
// Check if the object has a custom string converter and use it if available, since it is much more efficient
188193
// than round tripping through json.
189194
if converter, ok := e.getUnstructuredConverter(sv); ok {
190195
return converter.ToUnstructured(), nil
191196
}
192197
// Check if the object has a custom JSON marshaller/unmarshaller.
193198
if marshaler, ok := e.getJsonMarshaler(sv); ok {
194-
if sv.Kind() == reflect.Ptr && sv.IsNil() {
195-
// We're done - we don't need to store anything.
196-
return nil, nil
197-
}
198-
199199
data, err := marshaler.MarshalJSON()
200200
if err != nil {
201201
return nil, err

value/reflectcache_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package value
1919
import (
2020
"reflect"
2121
"testing"
22+
"time"
2223
)
2324

2425
type CustomValue struct {
@@ -39,6 +40,21 @@ func (c *CustomPointer) MarshalJSON() ([]byte, error) {
3940
return c.data, nil
4041
}
4142

43+
// Mimics https://github.com/kubernetes/apimachinery/blob/master/pkg/apis/meta/v1/time.go.
44+
type Time struct {
45+
time.Time
46+
}
47+
48+
// ToUnstructured implements the value.UnstructuredConverter interface.
49+
func (t Time) ToUnstructured() interface{} {
50+
if t.IsZero() {
51+
return nil
52+
}
53+
buf := make([]byte, 0, len(time.RFC3339))
54+
buf = t.UTC().AppendFormat(buf, time.RFC3339)
55+
return string(buf)
56+
}
57+
4258
func TestToUnstructured(t *testing.T) {
4359
testcases := []struct {
4460
Data string
@@ -63,6 +79,7 @@ func TestToUnstructured(t *testing.T) {
6379
CustomValue{data: []byte(tc.Data)},
6480
&CustomValue{data: []byte(tc.Data)},
6581
&CustomPointer{data: []byte(tc.Data)},
82+
&CustomPointer{nil},
6683
}
6784
for _, custom := range custom {
6885
rv := reflect.ValueOf(custom)
@@ -78,6 +95,39 @@ func TestToUnstructured(t *testing.T) {
7895
}
7996
}
8097

98+
func timePtr(t time.Time) *time.Time { return &t }
99+
100+
func TestTimeToUnstructured(t *testing.T) {
101+
testcases := []struct {
102+
Name string
103+
Time *time.Time
104+
Expected interface{}
105+
}{
106+
{Name: "nil", Time: nil, Expected: nil},
107+
{Name: "zero", Time: &time.Time{}, Expected: nil},
108+
{Name: "1", Time: timePtr(time.Time{}.Add(time.Second)), Expected: "0001-01-01T00:00:01Z"},
109+
}
110+
111+
for _, tc := range testcases {
112+
tc := tc
113+
t.Run(tc.Name, func(t *testing.T) {
114+
t.Parallel()
115+
var time *Time
116+
rv := reflect.ValueOf(time)
117+
if tc.Time != nil {
118+
rv = reflect.ValueOf(Time{Time: *tc.Time})
119+
}
120+
result, err := TypeReflectEntryOf(rv.Type()).ToUnstructured(rv)
121+
if err != nil {
122+
t.Fatal(err)
123+
}
124+
if !reflect.DeepEqual(result, tc.Expected) {
125+
t.Errorf("expected %#v but got %#v", tc.Expected, result)
126+
}
127+
})
128+
}
129+
}
130+
81131
func TestTypeReflectEntryOf(t *testing.T) {
82132
testString := ""
83133
tests := map[string]struct {

0 commit comments

Comments
 (0)