1
16
17 package runtime
18
19 import (
20 encodingjson "encoding/json"
21 "fmt"
22 "math"
23 "os"
24 "reflect"
25 "sort"
26 "strconv"
27 "strings"
28 "sync"
29 "sync/atomic"
30 "time"
31
32 "k8s.io/apimachinery/pkg/conversion"
33 "k8s.io/apimachinery/pkg/util/json"
34 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
35 "sigs.k8s.io/structured-merge-diff/v4/value"
36
37 "k8s.io/klog/v2"
38 )
39
40
41
42 type UnstructuredConverter interface {
43 ToUnstructured(obj interface{}) (map[string]interface{}, error)
44 FromUnstructured(u map[string]interface{}, obj interface{}) error
45 }
46
47 type structField struct {
48 structType reflect.Type
49 field int
50 }
51
52 type fieldInfo struct {
53 name string
54 nameValue reflect.Value
55 omitempty bool
56 }
57
58 type fieldsCacheMap map[structField]*fieldInfo
59
60 type fieldsCache struct {
61 sync.Mutex
62 value atomic.Value
63 }
64
65 func newFieldsCache() *fieldsCache {
66 cache := &fieldsCache{}
67 cache.value.Store(make(fieldsCacheMap))
68 return cache
69 }
70
71 var (
72 mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
73 stringType = reflect.TypeOf(string(""))
74 fieldCache = newFieldsCache()
75
76
77 DefaultUnstructuredConverter = &unstructuredConverter{
78 mismatchDetection: parseBool(os.Getenv("KUBE_PATCH_CONVERSION_DETECTOR")),
79 comparison: conversion.EqualitiesOrDie(
80 func(a, b time.Time) bool {
81 return a.UTC() == b.UTC()
82 },
83 ),
84 }
85 )
86
87 func parseBool(key string) bool {
88 if len(key) == 0 {
89 return false
90 }
91 value, err := strconv.ParseBool(key)
92 if err != nil {
93 utilruntime.HandleError(fmt.Errorf("couldn't parse '%s' as bool for unstructured mismatch detection", key))
94 }
95 return value
96 }
97
98
99
100 type unstructuredConverter struct {
101
102
103
104 mismatchDetection bool
105
106 comparison conversion.Equalities
107 }
108
109
110
111
112 func NewTestUnstructuredConverter(comparison conversion.Equalities) UnstructuredConverter {
113 return NewTestUnstructuredConverterWithValidation(comparison)
114 }
115
116
117
118 func NewTestUnstructuredConverterWithValidation(comparison conversion.Equalities) *unstructuredConverter {
119 return &unstructuredConverter{
120 mismatchDetection: true,
121 comparison: comparison,
122 }
123 }
124
125
126
127 type fromUnstructuredContext struct {
128
129
130
131
132 isInlined bool
133
134
135
136
137 matchedKeys []map[string]struct{}
138
139
140
141
142 parentPath []string
143
144
145
146 returnUnknownFields bool
147
148
149
150 unknownFieldErrors []error
151 }
152
153
154
155
156 func (c *fromUnstructuredContext) pushMatchedKeyTracker() {
157 if !c.returnUnknownFields {
158 return
159 }
160
161 c.matchedKeys = append(c.matchedKeys, nil)
162 }
163
164
165
166
167 func (c *fromUnstructuredContext) recordMatchedKey(key string) {
168 if !c.returnUnknownFields {
169 return
170 }
171
172 last := len(c.matchedKeys) - 1
173 if c.matchedKeys[last] == nil {
174 c.matchedKeys[last] = map[string]struct{}{}
175 }
176 c.matchedKeys[last][key] = struct{}{}
177 }
178
179
180
181
182
183
184
185
186 func (c *fromUnstructuredContext) popAndVerifyMatchedKeys(mapValue reflect.Value) {
187 if !c.returnUnknownFields {
188 return
189 }
190
191 last := len(c.matchedKeys) - 1
192 curMatchedKeys := c.matchedKeys[last]
193 c.matchedKeys[last] = nil
194 c.matchedKeys = c.matchedKeys[:last]
195 for _, key := range mapValue.MapKeys() {
196 if _, ok := curMatchedKeys[key.String()]; !ok {
197 c.recordUnknownField(key.String())
198 }
199 }
200 }
201
202 func (c *fromUnstructuredContext) recordUnknownField(field string) {
203 if !c.returnUnknownFields {
204 return
205 }
206
207 pathLen := len(c.parentPath)
208 c.pushKey(field)
209 errPath := strings.Join(c.parentPath, "")
210 c.parentPath = c.parentPath[:pathLen]
211 c.unknownFieldErrors = append(c.unknownFieldErrors, fmt.Errorf(`unknown field "%s"`, errPath))
212 }
213
214 func (c *fromUnstructuredContext) pushIndex(index int) {
215 if !c.returnUnknownFields {
216 return
217 }
218
219 c.parentPath = append(c.parentPath, "[", strconv.Itoa(index), "]")
220 }
221
222 func (c *fromUnstructuredContext) pushKey(key string) {
223 if !c.returnUnknownFields {
224 return
225 }
226
227 if len(c.parentPath) > 0 {
228 c.parentPath = append(c.parentPath, ".")
229 }
230 c.parentPath = append(c.parentPath, key)
231
232 }
233
234
235
236
237 func (c *unstructuredConverter) FromUnstructuredWithValidation(u map[string]interface{}, obj interface{}, returnUnknownFields bool) error {
238 t := reflect.TypeOf(obj)
239 value := reflect.ValueOf(obj)
240 if t.Kind() != reflect.Pointer || value.IsNil() {
241 return fmt.Errorf("FromUnstructured requires a non-nil pointer to an object, got %v", t)
242 }
243
244 fromUnstructuredContext := &fromUnstructuredContext{
245 returnUnknownFields: returnUnknownFields,
246 }
247 err := fromUnstructured(reflect.ValueOf(u), value.Elem(), fromUnstructuredContext)
248 if c.mismatchDetection {
249 newObj := reflect.New(t.Elem()).Interface()
250 newErr := fromUnstructuredViaJSON(u, newObj)
251 if (err != nil) != (newErr != nil) {
252 klog.Fatalf("FromUnstructured unexpected error for %v: error: %v", u, err)
253 }
254 if err == nil && !c.comparison.DeepEqual(obj, newObj) {
255 klog.Fatalf("FromUnstructured mismatch\nobj1: %#v\nobj2: %#v", obj, newObj)
256 }
257 }
258 if err != nil {
259 return err
260 }
261 if returnUnknownFields && len(fromUnstructuredContext.unknownFieldErrors) > 0 {
262 sort.Slice(fromUnstructuredContext.unknownFieldErrors, func(i, j int) bool {
263 return fromUnstructuredContext.unknownFieldErrors[i].Error() <
264 fromUnstructuredContext.unknownFieldErrors[j].Error()
265 })
266 return NewStrictDecodingError(fromUnstructuredContext.unknownFieldErrors)
267 }
268 return nil
269 }
270
271
272
273 func (c *unstructuredConverter) FromUnstructured(u map[string]interface{}, obj interface{}) error {
274 return c.FromUnstructuredWithValidation(u, obj, false)
275 }
276
277 func fromUnstructuredViaJSON(u map[string]interface{}, obj interface{}) error {
278 data, err := json.Marshal(u)
279 if err != nil {
280 return err
281 }
282 return json.Unmarshal(data, obj)
283 }
284
285 func fromUnstructured(sv, dv reflect.Value, ctx *fromUnstructuredContext) error {
286 sv = unwrapInterface(sv)
287 if !sv.IsValid() {
288 dv.Set(reflect.Zero(dv.Type()))
289 return nil
290 }
291 st, dt := sv.Type(), dv.Type()
292
293 switch dt.Kind() {
294 case reflect.Map, reflect.Slice, reflect.Pointer, reflect.Struct, reflect.Interface:
295
296 default:
297
298 if st.AssignableTo(dt) {
299 dv.Set(sv)
300 return nil
301 }
302
303
304
305 if st.ConvertibleTo(dt) {
306 switch st.Kind() {
307 case reflect.String:
308 switch dt.Kind() {
309 case reflect.String:
310 dv.Set(sv.Convert(dt))
311 return nil
312 }
313 case reflect.Bool:
314 switch dt.Kind() {
315 case reflect.Bool:
316 dv.Set(sv.Convert(dt))
317 return nil
318 }
319 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
320 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
321 switch dt.Kind() {
322 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
323 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
324 dv.Set(sv.Convert(dt))
325 return nil
326 case reflect.Float32, reflect.Float64:
327 dv.Set(sv.Convert(dt))
328 return nil
329 }
330 case reflect.Float32, reflect.Float64:
331 switch dt.Kind() {
332 case reflect.Float32, reflect.Float64:
333 dv.Set(sv.Convert(dt))
334 return nil
335 }
336 if sv.Float() == math.Trunc(sv.Float()) {
337 dv.Set(sv.Convert(dt))
338 return nil
339 }
340 }
341 return fmt.Errorf("cannot convert %s to %s", st.String(), dt.String())
342 }
343 }
344
345
346 entry := value.TypeReflectEntryOf(dv.Type())
347 if entry.CanConvertFromUnstructured() {
348 return entry.FromUnstructured(sv, dv)
349 }
350
351 switch dt.Kind() {
352 case reflect.Map:
353 return mapFromUnstructured(sv, dv, ctx)
354 case reflect.Slice:
355 return sliceFromUnstructured(sv, dv, ctx)
356 case reflect.Pointer:
357 return pointerFromUnstructured(sv, dv, ctx)
358 case reflect.Struct:
359 return structFromUnstructured(sv, dv, ctx)
360 case reflect.Interface:
361 return interfaceFromUnstructured(sv, dv)
362 default:
363 return fmt.Errorf("unrecognized type: %v", dt.Kind())
364 }
365
366 }
367
368 func fieldInfoFromField(structType reflect.Type, field int) *fieldInfo {
369 fieldCacheMap := fieldCache.value.Load().(fieldsCacheMap)
370 if info, ok := fieldCacheMap[structField{structType, field}]; ok {
371 return info
372 }
373
374
375 info := &fieldInfo{}
376 typeField := structType.Field(field)
377 jsonTag := typeField.Tag.Get("json")
378 if len(jsonTag) == 0 {
379
380 if typeField.Name == "" {
381 info.name = typeField.Name
382 } else {
383 info.name = strings.ToLower(typeField.Name[:1]) + typeField.Name[1:]
384 }
385 } else {
386 items := strings.Split(jsonTag, ",")
387 info.name = items[0]
388 for i := range items {
389 if items[i] == "omitempty" {
390 info.omitempty = true
391 break
392 }
393 }
394 }
395 info.nameValue = reflect.ValueOf(info.name)
396
397 fieldCache.Lock()
398 defer fieldCache.Unlock()
399 fieldCacheMap = fieldCache.value.Load().(fieldsCacheMap)
400 newFieldCacheMap := make(fieldsCacheMap)
401 for k, v := range fieldCacheMap {
402 newFieldCacheMap[k] = v
403 }
404 newFieldCacheMap[structField{structType, field}] = info
405 fieldCache.value.Store(newFieldCacheMap)
406 return info
407 }
408
409 func unwrapInterface(v reflect.Value) reflect.Value {
410 for v.Kind() == reflect.Interface {
411 v = v.Elem()
412 }
413 return v
414 }
415
416 func mapFromUnstructured(sv, dv reflect.Value, ctx *fromUnstructuredContext) error {
417 st, dt := sv.Type(), dv.Type()
418 if st.Kind() != reflect.Map {
419 return fmt.Errorf("cannot restore map from %v", st.Kind())
420 }
421
422 if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
423 return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
424 }
425
426 if sv.IsNil() {
427 dv.Set(reflect.Zero(dt))
428 return nil
429 }
430 dv.Set(reflect.MakeMap(dt))
431 for _, key := range sv.MapKeys() {
432 value := reflect.New(dt.Elem()).Elem()
433 if val := unwrapInterface(sv.MapIndex(key)); val.IsValid() {
434 if err := fromUnstructured(val, value, ctx); err != nil {
435 return err
436 }
437 } else {
438 value.Set(reflect.Zero(dt.Elem()))
439 }
440 if st.Key().AssignableTo(dt.Key()) {
441 dv.SetMapIndex(key, value)
442 } else {
443 dv.SetMapIndex(key.Convert(dt.Key()), value)
444 }
445 }
446 return nil
447 }
448
449 func sliceFromUnstructured(sv, dv reflect.Value, ctx *fromUnstructuredContext) error {
450 st, dt := sv.Type(), dv.Type()
451 if st.Kind() == reflect.String && dt.Elem().Kind() == reflect.Uint8 {
452
453
454
455 if len(sv.Interface().(string)) > 0 {
456 marshalled, err := json.Marshal(sv.Interface())
457 if err != nil {
458 return fmt.Errorf("error encoding %s to json: %v", st, err)
459 }
460
461 var data []byte
462 err = json.Unmarshal(marshalled, &data)
463 if err != nil {
464 return fmt.Errorf("error decoding from json: %v", err)
465 }
466 dv.SetBytes(data)
467 } else {
468 dv.Set(reflect.MakeSlice(dt, 0, 0))
469 }
470 return nil
471 }
472 if st.Kind() != reflect.Slice {
473 return fmt.Errorf("cannot restore slice from %v", st.Kind())
474 }
475
476 if sv.IsNil() {
477 dv.Set(reflect.Zero(dt))
478 return nil
479 }
480 dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
481
482 pathLen := len(ctx.parentPath)
483 defer func() {
484 ctx.parentPath = ctx.parentPath[:pathLen]
485 }()
486 for i := 0; i < sv.Len(); i++ {
487 ctx.pushIndex(i)
488 if err := fromUnstructured(sv.Index(i), dv.Index(i), ctx); err != nil {
489 return err
490 }
491 ctx.parentPath = ctx.parentPath[:pathLen]
492 }
493 return nil
494 }
495
496 func pointerFromUnstructured(sv, dv reflect.Value, ctx *fromUnstructuredContext) error {
497 st, dt := sv.Type(), dv.Type()
498
499 if st.Kind() == reflect.Pointer && sv.IsNil() {
500 dv.Set(reflect.Zero(dt))
501 return nil
502 }
503 dv.Set(reflect.New(dt.Elem()))
504 switch st.Kind() {
505 case reflect.Pointer, reflect.Interface:
506 return fromUnstructured(sv.Elem(), dv.Elem(), ctx)
507 default:
508 return fromUnstructured(sv, dv.Elem(), ctx)
509 }
510 }
511
512 func structFromUnstructured(sv, dv reflect.Value, ctx *fromUnstructuredContext) error {
513 st, dt := sv.Type(), dv.Type()
514 if st.Kind() != reflect.Map {
515 return fmt.Errorf("cannot restore struct from: %v", st.Kind())
516 }
517
518 pathLen := len(ctx.parentPath)
519 svInlined := ctx.isInlined
520 defer func() {
521 ctx.parentPath = ctx.parentPath[:pathLen]
522 ctx.isInlined = svInlined
523 }()
524 if !svInlined {
525 ctx.pushMatchedKeyTracker()
526 }
527 for i := 0; i < dt.NumField(); i++ {
528 fieldInfo := fieldInfoFromField(dt, i)
529 fv := dv.Field(i)
530
531 if len(fieldInfo.name) == 0 {
532
533
534 ctx.isInlined = true
535 if err := fromUnstructured(sv, fv, ctx); err != nil {
536 return err
537 }
538 ctx.isInlined = svInlined
539 } else {
540
541
542
543
544
545 ctx.recordMatchedKey(fieldInfo.name)
546 value := unwrapInterface(sv.MapIndex(fieldInfo.nameValue))
547 if value.IsValid() {
548 ctx.isInlined = false
549 ctx.pushKey(fieldInfo.name)
550 if err := fromUnstructured(value, fv, ctx); err != nil {
551 return err
552 }
553 ctx.parentPath = ctx.parentPath[:pathLen]
554 ctx.isInlined = svInlined
555 } else {
556 fv.Set(reflect.Zero(fv.Type()))
557 }
558 }
559 }
560 if !svInlined {
561 ctx.popAndVerifyMatchedKeys(sv)
562 }
563 return nil
564 }
565
566 func interfaceFromUnstructured(sv, dv reflect.Value) error {
567
568 dv.Set(sv)
569 return nil
570 }
571
572
573
574 func (c *unstructuredConverter) ToUnstructured(obj interface{}) (map[string]interface{}, error) {
575 var u map[string]interface{}
576 var err error
577 if unstr, ok := obj.(Unstructured); ok {
578 u = unstr.UnstructuredContent()
579 } else {
580 t := reflect.TypeOf(obj)
581 value := reflect.ValueOf(obj)
582 if t.Kind() != reflect.Pointer || value.IsNil() {
583 return nil, fmt.Errorf("ToUnstructured requires a non-nil pointer to an object, got %v", t)
584 }
585 u = map[string]interface{}{}
586 err = toUnstructured(value.Elem(), reflect.ValueOf(&u).Elem())
587 }
588 if c.mismatchDetection {
589 newUnstr := map[string]interface{}{}
590 newErr := toUnstructuredViaJSON(obj, &newUnstr)
591 if (err != nil) != (newErr != nil) {
592 klog.Fatalf("ToUnstructured unexpected error for %v: error: %v; newErr: %v", obj, err, newErr)
593 }
594 if err == nil && !c.comparison.DeepEqual(u, newUnstr) {
595 klog.Fatalf("ToUnstructured mismatch\nobj1: %#v\nobj2: %#v", u, newUnstr)
596 }
597 }
598 if err != nil {
599 return nil, err
600 }
601 return u, nil
602 }
603
604
605
606
607 func DeepCopyJSON(x map[string]interface{}) map[string]interface{} {
608 return DeepCopyJSONValue(x).(map[string]interface{})
609 }
610
611
612
613
614 func DeepCopyJSONValue(x interface{}) interface{} {
615 switch x := x.(type) {
616 case map[string]interface{}:
617 if x == nil {
618
619 return x
620 }
621 clone := make(map[string]interface{}, len(x))
622 for k, v := range x {
623 clone[k] = DeepCopyJSONValue(v)
624 }
625 return clone
626 case []interface{}:
627 if x == nil {
628
629 return x
630 }
631 clone := make([]interface{}, len(x))
632 for i, v := range x {
633 clone[i] = DeepCopyJSONValue(v)
634 }
635 return clone
636 case string, int64, bool, float64, nil, encodingjson.Number:
637 return x
638 default:
639 panic(fmt.Errorf("cannot deep copy %T", x))
640 }
641 }
642
643 func toUnstructuredViaJSON(obj interface{}, u *map[string]interface{}) error {
644 data, err := json.Marshal(obj)
645 if err != nil {
646 return err
647 }
648 return json.Unmarshal(data, u)
649 }
650
651 func toUnstructured(sv, dv reflect.Value) error {
652
653 entry := value.TypeReflectEntryOf(sv.Type())
654 if entry.CanConvertToUnstructured() {
655 v, err := entry.ToUnstructured(sv)
656 if err != nil {
657 return err
658 }
659 if v != nil {
660 dv.Set(reflect.ValueOf(v))
661 }
662 return nil
663 }
664 st := sv.Type()
665 switch st.Kind() {
666 case reflect.String:
667 dv.Set(reflect.ValueOf(sv.String()))
668 return nil
669 case reflect.Bool:
670 dv.Set(reflect.ValueOf(sv.Bool()))
671 return nil
672 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
673 dv.Set(reflect.ValueOf(sv.Int()))
674 return nil
675 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
676 uVal := sv.Uint()
677 if uVal > math.MaxInt64 {
678 return fmt.Errorf("unsigned value %d does not fit into int64 (overflow)", uVal)
679 }
680 dv.Set(reflect.ValueOf(int64(uVal)))
681 return nil
682 case reflect.Float32, reflect.Float64:
683 dv.Set(reflect.ValueOf(sv.Float()))
684 return nil
685 case reflect.Map:
686 return mapToUnstructured(sv, dv)
687 case reflect.Slice:
688 return sliceToUnstructured(sv, dv)
689 case reflect.Pointer:
690 return pointerToUnstructured(sv, dv)
691 case reflect.Struct:
692 return structToUnstructured(sv, dv)
693 case reflect.Interface:
694 return interfaceToUnstructured(sv, dv)
695 default:
696 return fmt.Errorf("unrecognized type: %v", st.Kind())
697 }
698 }
699
700 func mapToUnstructured(sv, dv reflect.Value) error {
701 st, dt := sv.Type(), dv.Type()
702 if sv.IsNil() {
703 dv.Set(reflect.Zero(dt))
704 return nil
705 }
706 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
707 if st.Key().Kind() == reflect.String {
708 dv.Set(reflect.MakeMap(mapStringInterfaceType))
709 dv = dv.Elem()
710 dt = dv.Type()
711 }
712 }
713 if dt.Kind() != reflect.Map {
714 return fmt.Errorf("cannot convert map to: %v", dt.Kind())
715 }
716
717 if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
718 return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
719 }
720
721 for _, key := range sv.MapKeys() {
722 value := reflect.New(dt.Elem()).Elem()
723 if err := toUnstructured(sv.MapIndex(key), value); err != nil {
724 return err
725 }
726 if st.Key().AssignableTo(dt.Key()) {
727 dv.SetMapIndex(key, value)
728 } else {
729 dv.SetMapIndex(key.Convert(dt.Key()), value)
730 }
731 }
732 return nil
733 }
734
735 func sliceToUnstructured(sv, dv reflect.Value) error {
736 st, dt := sv.Type(), dv.Type()
737 if sv.IsNil() {
738 dv.Set(reflect.Zero(dt))
739 return nil
740 }
741 if st.Elem().Kind() == reflect.Uint8 {
742 dv.Set(reflect.New(stringType))
743 data, err := json.Marshal(sv.Bytes())
744 if err != nil {
745 return err
746 }
747 var result string
748 if err = json.Unmarshal(data, &result); err != nil {
749 return err
750 }
751 dv.Set(reflect.ValueOf(result))
752 return nil
753 }
754 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
755 dv.Set(reflect.MakeSlice(reflect.SliceOf(dt), sv.Len(), sv.Cap()))
756 dv = dv.Elem()
757 dt = dv.Type()
758 }
759 if dt.Kind() != reflect.Slice {
760 return fmt.Errorf("cannot convert slice to: %v", dt.Kind())
761 }
762 for i := 0; i < sv.Len(); i++ {
763 if err := toUnstructured(sv.Index(i), dv.Index(i)); err != nil {
764 return err
765 }
766 }
767 return nil
768 }
769
770 func pointerToUnstructured(sv, dv reflect.Value) error {
771 if sv.IsNil() {
772
773 return nil
774 }
775 return toUnstructured(sv.Elem(), dv)
776 }
777
778 func isZero(v reflect.Value) bool {
779 switch v.Kind() {
780 case reflect.Array, reflect.String:
781 return v.Len() == 0
782 case reflect.Bool:
783 return !v.Bool()
784 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
785 return v.Int() == 0
786 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
787 return v.Uint() == 0
788 case reflect.Float32, reflect.Float64:
789 return v.Float() == 0
790 case reflect.Map, reflect.Slice:
791
792 return v.IsNil() || v.Len() == 0
793 case reflect.Pointer, reflect.Interface:
794 return v.IsNil()
795 }
796 return false
797 }
798
799 func structToUnstructured(sv, dv reflect.Value) error {
800 st, dt := sv.Type(), dv.Type()
801 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
802 dv.Set(reflect.MakeMapWithSize(mapStringInterfaceType, st.NumField()))
803 dv = dv.Elem()
804 dt = dv.Type()
805 }
806 if dt.Kind() != reflect.Map {
807 return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
808 }
809 realMap := dv.Interface().(map[string]interface{})
810
811 for i := 0; i < st.NumField(); i++ {
812 fieldInfo := fieldInfoFromField(st, i)
813 fv := sv.Field(i)
814
815 if fieldInfo.name == "-" {
816
817 continue
818 }
819 if fieldInfo.omitempty && isZero(fv) {
820
821 continue
822 }
823 if len(fieldInfo.name) == 0 {
824
825 if err := toUnstructured(fv, dv); err != nil {
826 return err
827 }
828 continue
829 }
830 switch fv.Type().Kind() {
831 case reflect.String:
832 realMap[fieldInfo.name] = fv.String()
833 case reflect.Bool:
834 realMap[fieldInfo.name] = fv.Bool()
835 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
836 realMap[fieldInfo.name] = fv.Int()
837 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
838 realMap[fieldInfo.name] = fv.Uint()
839 case reflect.Float32, reflect.Float64:
840 realMap[fieldInfo.name] = fv.Float()
841 default:
842 subv := reflect.New(dt.Elem()).Elem()
843 if err := toUnstructured(fv, subv); err != nil {
844 return err
845 }
846 dv.SetMapIndex(fieldInfo.nameValue, subv)
847 }
848 }
849 return nil
850 }
851
852 func interfaceToUnstructured(sv, dv reflect.Value) error {
853 if !sv.IsValid() || sv.IsNil() {
854 dv.Set(reflect.Zero(dv.Type()))
855 return nil
856 }
857 return toUnstructured(sv.Elem(), dv)
858 }
859
View as plain text