blob: 871e4c8c46e8c2b72e1857b50794a15b4b395e3f [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
2Copyright 2017 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package runtime
18
19import (
20 encodingjson "encoding/json"
21 "fmt"
22 "math"
23 "os"
24 "reflect"
25 "strconv"
26 "strings"
27 "sync"
28 "sync/atomic"
29 "time"
30
31 "k8s.io/apimachinery/pkg/conversion"
32 "k8s.io/apimachinery/pkg/util/json"
33 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
34 "sigs.k8s.io/structured-merge-diff/v4/value"
35
36 "k8s.io/klog/v2"
37)
38
39// UnstructuredConverter is an interface for converting between interface{}
40// and map[string]interface representation.
41type UnstructuredConverter interface {
42 ToUnstructured(obj interface{}) (map[string]interface{}, error)
43 FromUnstructured(u map[string]interface{}, obj interface{}) error
44}
45
46type structField struct {
47 structType reflect.Type
48 field int
49}
50
51type fieldInfo struct {
52 name string
53 nameValue reflect.Value
54 omitempty bool
55}
56
57type fieldsCacheMap map[structField]*fieldInfo
58
59type fieldsCache struct {
60 sync.Mutex
61 value atomic.Value
62}
63
64func newFieldsCache() *fieldsCache {
65 cache := &fieldsCache{}
66 cache.value.Store(make(fieldsCacheMap))
67 return cache
68}
69
70var (
71 mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
72 stringType = reflect.TypeOf(string(""))
73 fieldCache = newFieldsCache()
74
75 // DefaultUnstructuredConverter performs unstructured to Go typed object conversions.
76 DefaultUnstructuredConverter = &unstructuredConverter{
77 mismatchDetection: parseBool(os.Getenv("KUBE_PATCH_CONVERSION_DETECTOR")),
78 comparison: conversion.EqualitiesOrDie(
79 func(a, b time.Time) bool {
80 return a.UTC() == b.UTC()
81 },
82 ),
83 }
84)
85
86func parseBool(key string) bool {
87 if len(key) == 0 {
88 return false
89 }
90 value, err := strconv.ParseBool(key)
91 if err != nil {
92 utilruntime.HandleError(fmt.Errorf("couldn't parse '%s' as bool for unstructured mismatch detection", key))
93 }
94 return value
95}
96
97// unstructuredConverter knows how to convert between interface{} and
98// Unstructured in both ways.
99type unstructuredConverter struct {
100 // If true, we will be additionally running conversion via json
101 // to ensure that the result is true.
102 // This is supposed to be set only in tests.
103 mismatchDetection bool
104 // comparison is the default test logic used to compare
105 comparison conversion.Equalities
106}
107
108// NewTestUnstructuredConverter creates an UnstructuredConverter that accepts JSON typed maps and translates them
109// to Go types via reflection. It performs mismatch detection automatically and is intended for use by external
110// test tools. Use DefaultUnstructuredConverter if you do not explicitly need mismatch detection.
111func NewTestUnstructuredConverter(comparison conversion.Equalities) UnstructuredConverter {
112 return &unstructuredConverter{
113 mismatchDetection: true,
114 comparison: comparison,
115 }
116}
117
118// FromUnstructured converts an object from map[string]interface{} representation into a concrete type.
119// It uses encoding/json/Unmarshaler if object implements it or reflection if not.
120func (c *unstructuredConverter) FromUnstructured(u map[string]interface{}, obj interface{}) error {
121 t := reflect.TypeOf(obj)
122 value := reflect.ValueOf(obj)
123 if t.Kind() != reflect.Ptr || value.IsNil() {
124 return fmt.Errorf("FromUnstructured requires a non-nil pointer to an object, got %v", t)
125 }
126 err := fromUnstructured(reflect.ValueOf(u), value.Elem())
127 if c.mismatchDetection {
128 newObj := reflect.New(t.Elem()).Interface()
129 newErr := fromUnstructuredViaJSON(u, newObj)
130 if (err != nil) != (newErr != nil) {
131 klog.Fatalf("FromUnstructured unexpected error for %v: error: %v", u, err)
132 }
133 if err == nil && !c.comparison.DeepEqual(obj, newObj) {
134 klog.Fatalf("FromUnstructured mismatch\nobj1: %#v\nobj2: %#v", obj, newObj)
135 }
136 }
137 return err
138}
139
140func fromUnstructuredViaJSON(u map[string]interface{}, obj interface{}) error {
141 data, err := json.Marshal(u)
142 if err != nil {
143 return err
144 }
145 return json.Unmarshal(data, obj)
146}
147
148func fromUnstructured(sv, dv reflect.Value) error {
149 sv = unwrapInterface(sv)
150 if !sv.IsValid() {
151 dv.Set(reflect.Zero(dv.Type()))
152 return nil
153 }
154 st, dt := sv.Type(), dv.Type()
155
156 switch dt.Kind() {
157 case reflect.Map, reflect.Slice, reflect.Ptr, reflect.Struct, reflect.Interface:
158 // Those require non-trivial conversion.
159 default:
160 // This should handle all simple types.
161 if st.AssignableTo(dt) {
162 dv.Set(sv)
163 return nil
164 }
165 // We cannot simply use "ConvertibleTo", as JSON doesn't support conversions
166 // between those four groups: bools, integers, floats and string. We need to
167 // do the same.
168 if st.ConvertibleTo(dt) {
169 switch st.Kind() {
170 case reflect.String:
171 switch dt.Kind() {
172 case reflect.String:
173 dv.Set(sv.Convert(dt))
174 return nil
175 }
176 case reflect.Bool:
177 switch dt.Kind() {
178 case reflect.Bool:
179 dv.Set(sv.Convert(dt))
180 return nil
181 }
182 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
183 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
184 switch dt.Kind() {
185 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
186 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
187 dv.Set(sv.Convert(dt))
188 return nil
189 }
190 case reflect.Float32, reflect.Float64:
191 switch dt.Kind() {
192 case reflect.Float32, reflect.Float64:
193 dv.Set(sv.Convert(dt))
194 return nil
195 }
196 if sv.Float() == math.Trunc(sv.Float()) {
197 dv.Set(sv.Convert(dt))
198 return nil
199 }
200 }
201 return fmt.Errorf("cannot convert %s to %s", st.String(), dt.String())
202 }
203 }
204
205 // Check if the object has a custom JSON marshaller/unmarshaller.
206 entry := value.TypeReflectEntryOf(dv.Type())
207 if entry.CanConvertFromUnstructured() {
208 return entry.FromUnstructured(sv, dv)
209 }
210
211 switch dt.Kind() {
212 case reflect.Map:
213 return mapFromUnstructured(sv, dv)
214 case reflect.Slice:
215 return sliceFromUnstructured(sv, dv)
216 case reflect.Ptr:
217 return pointerFromUnstructured(sv, dv)
218 case reflect.Struct:
219 return structFromUnstructured(sv, dv)
220 case reflect.Interface:
221 return interfaceFromUnstructured(sv, dv)
222 default:
223 return fmt.Errorf("unrecognized type: %v", dt.Kind())
224 }
225}
226
227func fieldInfoFromField(structType reflect.Type, field int) *fieldInfo {
228 fieldCacheMap := fieldCache.value.Load().(fieldsCacheMap)
229 if info, ok := fieldCacheMap[structField{structType, field}]; ok {
230 return info
231 }
232
233 // Cache miss - we need to compute the field name.
234 info := &fieldInfo{}
235 typeField := structType.Field(field)
236 jsonTag := typeField.Tag.Get("json")
237 if len(jsonTag) == 0 {
238 // Make the first character lowercase.
239 if typeField.Name == "" {
240 info.name = typeField.Name
241 } else {
242 info.name = strings.ToLower(typeField.Name[:1]) + typeField.Name[1:]
243 }
244 } else {
245 items := strings.Split(jsonTag, ",")
246 info.name = items[0]
247 for i := range items {
248 if items[i] == "omitempty" {
249 info.omitempty = true
250 break
251 }
252 }
253 }
254 info.nameValue = reflect.ValueOf(info.name)
255
256 fieldCache.Lock()
257 defer fieldCache.Unlock()
258 fieldCacheMap = fieldCache.value.Load().(fieldsCacheMap)
259 newFieldCacheMap := make(fieldsCacheMap)
260 for k, v := range fieldCacheMap {
261 newFieldCacheMap[k] = v
262 }
263 newFieldCacheMap[structField{structType, field}] = info
264 fieldCache.value.Store(newFieldCacheMap)
265 return info
266}
267
268func unwrapInterface(v reflect.Value) reflect.Value {
269 for v.Kind() == reflect.Interface {
270 v = v.Elem()
271 }
272 return v
273}
274
275func mapFromUnstructured(sv, dv reflect.Value) error {
276 st, dt := sv.Type(), dv.Type()
277 if st.Kind() != reflect.Map {
278 return fmt.Errorf("cannot restore map from %v", st.Kind())
279 }
280
281 if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
282 return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
283 }
284
285 if sv.IsNil() {
286 dv.Set(reflect.Zero(dt))
287 return nil
288 }
289 dv.Set(reflect.MakeMap(dt))
290 for _, key := range sv.MapKeys() {
291 value := reflect.New(dt.Elem()).Elem()
292 if val := unwrapInterface(sv.MapIndex(key)); val.IsValid() {
293 if err := fromUnstructured(val, value); err != nil {
294 return err
295 }
296 } else {
297 value.Set(reflect.Zero(dt.Elem()))
298 }
299 if st.Key().AssignableTo(dt.Key()) {
300 dv.SetMapIndex(key, value)
301 } else {
302 dv.SetMapIndex(key.Convert(dt.Key()), value)
303 }
304 }
305 return nil
306}
307
308func sliceFromUnstructured(sv, dv reflect.Value) error {
309 st, dt := sv.Type(), dv.Type()
310 if st.Kind() == reflect.String && dt.Elem().Kind() == reflect.Uint8 {
311 // We store original []byte representation as string.
312 // This conversion is allowed, but we need to be careful about
313 // marshaling data appropriately.
314 if len(sv.Interface().(string)) > 0 {
315 marshalled, err := json.Marshal(sv.Interface())
316 if err != nil {
317 return fmt.Errorf("error encoding %s to json: %v", st, err)
318 }
319 // TODO: Is this Unmarshal needed?
320 var data []byte
321 err = json.Unmarshal(marshalled, &data)
322 if err != nil {
323 return fmt.Errorf("error decoding from json: %v", err)
324 }
325 dv.SetBytes(data)
326 } else {
327 dv.Set(reflect.Zero(dt))
328 }
329 return nil
330 }
331 if st.Kind() != reflect.Slice {
332 return fmt.Errorf("cannot restore slice from %v", st.Kind())
333 }
334
335 if sv.IsNil() {
336 dv.Set(reflect.Zero(dt))
337 return nil
338 }
339 dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
340 for i := 0; i < sv.Len(); i++ {
341 if err := fromUnstructured(sv.Index(i), dv.Index(i)); err != nil {
342 return err
343 }
344 }
345 return nil
346}
347
348func pointerFromUnstructured(sv, dv reflect.Value) error {
349 st, dt := sv.Type(), dv.Type()
350
351 if st.Kind() == reflect.Ptr && sv.IsNil() {
352 dv.Set(reflect.Zero(dt))
353 return nil
354 }
355 dv.Set(reflect.New(dt.Elem()))
356 switch st.Kind() {
357 case reflect.Ptr, reflect.Interface:
358 return fromUnstructured(sv.Elem(), dv.Elem())
359 default:
360 return fromUnstructured(sv, dv.Elem())
361 }
362}
363
364func structFromUnstructured(sv, dv reflect.Value) error {
365 st, dt := sv.Type(), dv.Type()
366 if st.Kind() != reflect.Map {
367 return fmt.Errorf("cannot restore struct from: %v", st.Kind())
368 }
369
370 for i := 0; i < dt.NumField(); i++ {
371 fieldInfo := fieldInfoFromField(dt, i)
372 fv := dv.Field(i)
373
374 if len(fieldInfo.name) == 0 {
375 // This field is inlined.
376 if err := fromUnstructured(sv, fv); err != nil {
377 return err
378 }
379 } else {
380 value := unwrapInterface(sv.MapIndex(fieldInfo.nameValue))
381 if value.IsValid() {
382 if err := fromUnstructured(value, fv); err != nil {
383 return err
384 }
385 } else {
386 fv.Set(reflect.Zero(fv.Type()))
387 }
388 }
389 }
390 return nil
391}
392
393func interfaceFromUnstructured(sv, dv reflect.Value) error {
394 // TODO: Is this conversion safe?
395 dv.Set(sv)
396 return nil
397}
398
399// ToUnstructured converts an object into map[string]interface{} representation.
400// It uses encoding/json/Marshaler if object implements it or reflection if not.
401func (c *unstructuredConverter) ToUnstructured(obj interface{}) (map[string]interface{}, error) {
402 var u map[string]interface{}
403 var err error
404 if unstr, ok := obj.(Unstructured); ok {
405 u = unstr.UnstructuredContent()
406 } else {
407 t := reflect.TypeOf(obj)
408 value := reflect.ValueOf(obj)
409 if t.Kind() != reflect.Ptr || value.IsNil() {
410 return nil, fmt.Errorf("ToUnstructured requires a non-nil pointer to an object, got %v", t)
411 }
412 u = map[string]interface{}{}
413 err = toUnstructured(value.Elem(), reflect.ValueOf(&u).Elem())
414 }
415 if c.mismatchDetection {
416 newUnstr := map[string]interface{}{}
417 newErr := toUnstructuredViaJSON(obj, &newUnstr)
418 if (err != nil) != (newErr != nil) {
419 klog.Fatalf("ToUnstructured unexpected error for %v: error: %v; newErr: %v", obj, err, newErr)
420 }
421 if err == nil && !c.comparison.DeepEqual(u, newUnstr) {
422 klog.Fatalf("ToUnstructured mismatch\nobj1: %#v\nobj2: %#v", u, newUnstr)
423 }
424 }
425 if err != nil {
426 return nil, err
427 }
428 return u, nil
429}
430
431// DeepCopyJSON deep copies the passed value, assuming it is a valid JSON representation i.e. only contains
432// types produced by json.Unmarshal() and also int64.
433// bool, int64, float64, string, []interface{}, map[string]interface{}, json.Number and nil
434func DeepCopyJSON(x map[string]interface{}) map[string]interface{} {
435 return DeepCopyJSONValue(x).(map[string]interface{})
436}
437
438// DeepCopyJSONValue deep copies the passed value, assuming it is a valid JSON representation i.e. only contains
439// types produced by json.Unmarshal() and also int64.
440// bool, int64, float64, string, []interface{}, map[string]interface{}, json.Number and nil
441func DeepCopyJSONValue(x interface{}) interface{} {
442 switch x := x.(type) {
443 case map[string]interface{}:
444 if x == nil {
445 // Typed nil - an interface{} that contains a type map[string]interface{} with a value of nil
446 return x
447 }
448 clone := make(map[string]interface{}, len(x))
449 for k, v := range x {
450 clone[k] = DeepCopyJSONValue(v)
451 }
452 return clone
453 case []interface{}:
454 if x == nil {
455 // Typed nil - an interface{} that contains a type []interface{} with a value of nil
456 return x
457 }
458 clone := make([]interface{}, len(x))
459 for i, v := range x {
460 clone[i] = DeepCopyJSONValue(v)
461 }
462 return clone
463 case string, int64, bool, float64, nil, encodingjson.Number:
464 return x
465 default:
466 panic(fmt.Errorf("cannot deep copy %T", x))
467 }
468}
469
470func toUnstructuredViaJSON(obj interface{}, u *map[string]interface{}) error {
471 data, err := json.Marshal(obj)
472 if err != nil {
473 return err
474 }
475 return json.Unmarshal(data, u)
476}
477
478func toUnstructured(sv, dv reflect.Value) error {
479 // Check if the object has a custom string converter.
480 entry := value.TypeReflectEntryOf(sv.Type())
481 if entry.CanConvertToUnstructured() {
482 v, err := entry.ToUnstructured(sv)
483 if err != nil {
484 return err
485 }
486 if v != nil {
487 dv.Set(reflect.ValueOf(v))
488 }
489 return nil
490 }
491 st := sv.Type()
492 switch st.Kind() {
493 case reflect.String:
494 dv.Set(reflect.ValueOf(sv.String()))
495 return nil
496 case reflect.Bool:
497 dv.Set(reflect.ValueOf(sv.Bool()))
498 return nil
499 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
500 dv.Set(reflect.ValueOf(sv.Int()))
501 return nil
502 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
503 uVal := sv.Uint()
504 if uVal > math.MaxInt64 {
505 return fmt.Errorf("unsigned value %d does not fit into int64 (overflow)", uVal)
506 }
507 dv.Set(reflect.ValueOf(int64(uVal)))
508 return nil
509 case reflect.Float32, reflect.Float64:
510 dv.Set(reflect.ValueOf(sv.Float()))
511 return nil
512 case reflect.Map:
513 return mapToUnstructured(sv, dv)
514 case reflect.Slice:
515 return sliceToUnstructured(sv, dv)
516 case reflect.Ptr:
517 return pointerToUnstructured(sv, dv)
518 case reflect.Struct:
519 return structToUnstructured(sv, dv)
520 case reflect.Interface:
521 return interfaceToUnstructured(sv, dv)
522 default:
523 return fmt.Errorf("unrecognized type: %v", st.Kind())
524 }
525}
526
527func mapToUnstructured(sv, dv reflect.Value) error {
528 st, dt := sv.Type(), dv.Type()
529 if sv.IsNil() {
530 dv.Set(reflect.Zero(dt))
531 return nil
532 }
533 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
534 if st.Key().Kind() == reflect.String {
535 switch st.Elem().Kind() {
536 // TODO It should be possible to reuse the slice for primitive types.
537 // However, it is panicing in the following form.
538 // case reflect.String, reflect.Bool,
539 // reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
540 // reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
541 // sv.Set(sv)
542 // return nil
543 default:
544 // We need to do a proper conversion.
545 }
546 }
547 dv.Set(reflect.MakeMap(mapStringInterfaceType))
548 dv = dv.Elem()
549 dt = dv.Type()
550 }
551 if dt.Kind() != reflect.Map {
552 return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
553 }
554
555 if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
556 return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
557 }
558
559 for _, key := range sv.MapKeys() {
560 value := reflect.New(dt.Elem()).Elem()
561 if err := toUnstructured(sv.MapIndex(key), value); err != nil {
562 return err
563 }
564 if st.Key().AssignableTo(dt.Key()) {
565 dv.SetMapIndex(key, value)
566 } else {
567 dv.SetMapIndex(key.Convert(dt.Key()), value)
568 }
569 }
570 return nil
571}
572
573func sliceToUnstructured(sv, dv reflect.Value) error {
574 st, dt := sv.Type(), dv.Type()
575 if sv.IsNil() {
576 dv.Set(reflect.Zero(dt))
577 return nil
578 }
579 if st.Elem().Kind() == reflect.Uint8 {
580 dv.Set(reflect.New(stringType))
581 data, err := json.Marshal(sv.Bytes())
582 if err != nil {
583 return err
584 }
585 var result string
586 if err = json.Unmarshal(data, &result); err != nil {
587 return err
588 }
589 dv.Set(reflect.ValueOf(result))
590 return nil
591 }
592 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
593 switch st.Elem().Kind() {
594 // TODO It should be possible to reuse the slice for primitive types.
595 // However, it is panicing in the following form.
596 // case reflect.String, reflect.Bool,
597 // reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
598 // reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
599 // sv.Set(sv)
600 // return nil
601 default:
602 // We need to do a proper conversion.
603 dv.Set(reflect.MakeSlice(reflect.SliceOf(dt), sv.Len(), sv.Cap()))
604 dv = dv.Elem()
605 dt = dv.Type()
606 }
607 }
608 if dt.Kind() != reflect.Slice {
609 return fmt.Errorf("cannot convert slice to: %v", dt.Kind())
610 }
611 for i := 0; i < sv.Len(); i++ {
612 if err := toUnstructured(sv.Index(i), dv.Index(i)); err != nil {
613 return err
614 }
615 }
616 return nil
617}
618
619func pointerToUnstructured(sv, dv reflect.Value) error {
620 if sv.IsNil() {
621 // We're done - we don't need to store anything.
622 return nil
623 }
624 return toUnstructured(sv.Elem(), dv)
625}
626
627func isZero(v reflect.Value) bool {
628 switch v.Kind() {
629 case reflect.Array, reflect.String:
630 return v.Len() == 0
631 case reflect.Bool:
632 return !v.Bool()
633 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
634 return v.Int() == 0
635 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
636 return v.Uint() == 0
637 case reflect.Float32, reflect.Float64:
638 return v.Float() == 0
639 case reflect.Map, reflect.Slice:
640 // TODO: It seems that 0-len maps are ignored in it.
641 return v.IsNil() || v.Len() == 0
642 case reflect.Ptr, reflect.Interface:
643 return v.IsNil()
644 }
645 return false
646}
647
648func structToUnstructured(sv, dv reflect.Value) error {
649 st, dt := sv.Type(), dv.Type()
650 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
651 dv.Set(reflect.MakeMapWithSize(mapStringInterfaceType, st.NumField()))
652 dv = dv.Elem()
653 dt = dv.Type()
654 }
655 if dt.Kind() != reflect.Map {
656 return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
657 }
658 realMap := dv.Interface().(map[string]interface{})
659
660 for i := 0; i < st.NumField(); i++ {
661 fieldInfo := fieldInfoFromField(st, i)
662 fv := sv.Field(i)
663
664 if fieldInfo.name == "-" {
665 // This field should be skipped.
666 continue
667 }
668 if fieldInfo.omitempty && isZero(fv) {
669 // omitempty fields should be ignored.
670 continue
671 }
672 if len(fieldInfo.name) == 0 {
673 // This field is inlined.
674 if err := toUnstructured(fv, dv); err != nil {
675 return err
676 }
677 continue
678 }
679 switch fv.Type().Kind() {
680 case reflect.String:
681 realMap[fieldInfo.name] = fv.String()
682 case reflect.Bool:
683 realMap[fieldInfo.name] = fv.Bool()
684 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
685 realMap[fieldInfo.name] = fv.Int()
686 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
687 realMap[fieldInfo.name] = fv.Uint()
688 case reflect.Float32, reflect.Float64:
689 realMap[fieldInfo.name] = fv.Float()
690 default:
691 subv := reflect.New(dt.Elem()).Elem()
692 if err := toUnstructured(fv, subv); err != nil {
693 return err
694 }
695 dv.SetMapIndex(fieldInfo.nameValue, subv)
696 }
697 }
698 return nil
699}
700
701func interfaceToUnstructured(sv, dv reflect.Value) error {
702 if !sv.IsValid() || sv.IsNil() {
703 dv.Set(reflect.Zero(dv.Type()))
704 return nil
705 }
706 return toUnstructured(sv.Elem(), dv)
707}