blob: 80343081f52d137b91ef6e49f743b1bd1e8db389 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
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 "bytes"
21 encodingjson "encoding/json"
22 "fmt"
23 "math"
24 "os"
25 "reflect"
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
David Bainbridge86971522019-09-26 22:09:39 +000036 "k8s.io/klog"
Zack Williamse940c7a2019-08-21 14:25:39 -070037)
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 marshalerType = reflect.TypeOf(new(encodingjson.Marshaler)).Elem()
72 unmarshalerType = reflect.TypeOf(new(encodingjson.Unmarshaler)).Elem()
73 mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
74 stringType = reflect.TypeOf(string(""))
75 int64Type = reflect.TypeOf(int64(0))
76 float64Type = reflect.TypeOf(float64(0))
77 boolType = reflect.TypeOf(bool(false))
78 fieldCache = newFieldsCache()
79
80 // DefaultUnstructuredConverter performs unstructured to Go typed object conversions.
81 DefaultUnstructuredConverter = &unstructuredConverter{
82 mismatchDetection: parseBool(os.Getenv("KUBE_PATCH_CONVERSION_DETECTOR")),
83 comparison: conversion.EqualitiesOrDie(
84 func(a, b time.Time) bool {
85 return a.UTC() == b.UTC()
86 },
87 ),
88 }
89)
90
91func parseBool(key string) bool {
92 if len(key) == 0 {
93 return false
94 }
95 value, err := strconv.ParseBool(key)
96 if err != nil {
97 utilruntime.HandleError(fmt.Errorf("Couldn't parse '%s' as bool for unstructured mismatch detection", key))
98 }
99 return value
100}
101
102// unstructuredConverter knows how to convert between interface{} and
103// Unstructured in both ways.
104type unstructuredConverter struct {
105 // If true, we will be additionally running conversion via json
106 // to ensure that the result is true.
107 // This is supposed to be set only in tests.
108 mismatchDetection bool
109 // comparison is the default test logic used to compare
110 comparison conversion.Equalities
111}
112
113// NewTestUnstructuredConverter creates an UnstructuredConverter that accepts JSON typed maps and translates them
114// to Go types via reflection. It performs mismatch detection automatically and is intended for use by external
115// test tools. Use DefaultUnstructuredConverter if you do not explicitly need mismatch detection.
116func NewTestUnstructuredConverter(comparison conversion.Equalities) UnstructuredConverter {
117 return &unstructuredConverter{
118 mismatchDetection: true,
119 comparison: comparison,
120 }
121}
122
123// FromUnstructured converts an object from map[string]interface{} representation into a concrete type.
124// It uses encoding/json/Unmarshaler if object implements it or reflection if not.
125func (c *unstructuredConverter) FromUnstructured(u map[string]interface{}, obj interface{}) error {
126 t := reflect.TypeOf(obj)
127 value := reflect.ValueOf(obj)
128 if t.Kind() != reflect.Ptr || value.IsNil() {
129 return fmt.Errorf("FromUnstructured requires a non-nil pointer to an object, got %v", t)
130 }
131 err := fromUnstructured(reflect.ValueOf(u), value.Elem())
132 if c.mismatchDetection {
133 newObj := reflect.New(t.Elem()).Interface()
134 newErr := fromUnstructuredViaJSON(u, newObj)
135 if (err != nil) != (newErr != nil) {
David Bainbridge86971522019-09-26 22:09:39 +0000136 klog.Fatalf("FromUnstructured unexpected error for %v: error: %v", u, err)
Zack Williamse940c7a2019-08-21 14:25:39 -0700137 }
138 if err == nil && !c.comparison.DeepEqual(obj, newObj) {
David Bainbridge86971522019-09-26 22:09:39 +0000139 klog.Fatalf("FromUnstructured mismatch\nobj1: %#v\nobj2: %#v", obj, newObj)
Zack Williamse940c7a2019-08-21 14:25:39 -0700140 }
141 }
142 return err
143}
144
145func fromUnstructuredViaJSON(u map[string]interface{}, obj interface{}) error {
146 data, err := json.Marshal(u)
147 if err != nil {
148 return err
149 }
150 return json.Unmarshal(data, obj)
151}
152
153func fromUnstructured(sv, dv reflect.Value) error {
154 sv = unwrapInterface(sv)
155 if !sv.IsValid() {
156 dv.Set(reflect.Zero(dv.Type()))
157 return nil
158 }
159 st, dt := sv.Type(), dv.Type()
160
161 switch dt.Kind() {
162 case reflect.Map, reflect.Slice, reflect.Ptr, reflect.Struct, reflect.Interface:
163 // Those require non-trivial conversion.
164 default:
165 // This should handle all simple types.
166 if st.AssignableTo(dt) {
167 dv.Set(sv)
168 return nil
169 }
170 // We cannot simply use "ConvertibleTo", as JSON doesn't support conversions
171 // between those four groups: bools, integers, floats and string. We need to
172 // do the same.
173 if st.ConvertibleTo(dt) {
174 switch st.Kind() {
175 case reflect.String:
176 switch dt.Kind() {
177 case reflect.String:
178 dv.Set(sv.Convert(dt))
179 return nil
180 }
181 case reflect.Bool:
182 switch dt.Kind() {
183 case reflect.Bool:
184 dv.Set(sv.Convert(dt))
185 return nil
186 }
187 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
188 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
189 switch dt.Kind() {
190 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
191 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
192 dv.Set(sv.Convert(dt))
193 return nil
194 }
195 case reflect.Float32, reflect.Float64:
196 switch dt.Kind() {
197 case reflect.Float32, reflect.Float64:
198 dv.Set(sv.Convert(dt))
199 return nil
200 }
201 if sv.Float() == math.Trunc(sv.Float()) {
202 dv.Set(sv.Convert(dt))
203 return nil
204 }
205 }
206 return fmt.Errorf("cannot convert %s to %s", st.String(), dt.String())
207 }
208 }
209
210 // Check if the object has a custom JSON marshaller/unmarshaller.
211 if reflect.PtrTo(dt).Implements(unmarshalerType) {
212 data, err := json.Marshal(sv.Interface())
213 if err != nil {
214 return fmt.Errorf("error encoding %s to json: %v", st.String(), err)
215 }
216 unmarshaler := dv.Addr().Interface().(encodingjson.Unmarshaler)
217 return unmarshaler.UnmarshalJSON(data)
218 }
219
220 switch dt.Kind() {
221 case reflect.Map:
222 return mapFromUnstructured(sv, dv)
223 case reflect.Slice:
224 return sliceFromUnstructured(sv, dv)
225 case reflect.Ptr:
226 return pointerFromUnstructured(sv, dv)
227 case reflect.Struct:
228 return structFromUnstructured(sv, dv)
229 case reflect.Interface:
230 return interfaceFromUnstructured(sv, dv)
231 default:
232 return fmt.Errorf("unrecognized type: %v", dt.Kind())
233 }
234}
235
236func fieldInfoFromField(structType reflect.Type, field int) *fieldInfo {
237 fieldCacheMap := fieldCache.value.Load().(fieldsCacheMap)
238 if info, ok := fieldCacheMap[structField{structType, field}]; ok {
239 return info
240 }
241
242 // Cache miss - we need to compute the field name.
243 info := &fieldInfo{}
244 typeField := structType.Field(field)
245 jsonTag := typeField.Tag.Get("json")
246 if len(jsonTag) == 0 {
247 // Make the first character lowercase.
248 if typeField.Name == "" {
249 info.name = typeField.Name
250 } else {
251 info.name = strings.ToLower(typeField.Name[:1]) + typeField.Name[1:]
252 }
253 } else {
254 items := strings.Split(jsonTag, ",")
255 info.name = items[0]
256 for i := range items {
257 if items[i] == "omitempty" {
258 info.omitempty = true
259 }
260 }
261 }
262 info.nameValue = reflect.ValueOf(info.name)
263
264 fieldCache.Lock()
265 defer fieldCache.Unlock()
266 fieldCacheMap = fieldCache.value.Load().(fieldsCacheMap)
267 newFieldCacheMap := make(fieldsCacheMap)
268 for k, v := range fieldCacheMap {
269 newFieldCacheMap[k] = v
270 }
271 newFieldCacheMap[structField{structType, field}] = info
272 fieldCache.value.Store(newFieldCacheMap)
273 return info
274}
275
276func unwrapInterface(v reflect.Value) reflect.Value {
277 for v.Kind() == reflect.Interface {
278 v = v.Elem()
279 }
280 return v
281}
282
283func mapFromUnstructured(sv, dv reflect.Value) error {
284 st, dt := sv.Type(), dv.Type()
285 if st.Kind() != reflect.Map {
286 return fmt.Errorf("cannot restore map from %v", st.Kind())
287 }
288
289 if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
290 return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
291 }
292
293 if sv.IsNil() {
294 dv.Set(reflect.Zero(dt))
295 return nil
296 }
297 dv.Set(reflect.MakeMap(dt))
298 for _, key := range sv.MapKeys() {
299 value := reflect.New(dt.Elem()).Elem()
300 if val := unwrapInterface(sv.MapIndex(key)); val.IsValid() {
301 if err := fromUnstructured(val, value); err != nil {
302 return err
303 }
304 } else {
305 value.Set(reflect.Zero(dt.Elem()))
306 }
307 if st.Key().AssignableTo(dt.Key()) {
308 dv.SetMapIndex(key, value)
309 } else {
310 dv.SetMapIndex(key.Convert(dt.Key()), value)
311 }
312 }
313 return nil
314}
315
316func sliceFromUnstructured(sv, dv reflect.Value) error {
317 st, dt := sv.Type(), dv.Type()
318 if st.Kind() == reflect.String && dt.Elem().Kind() == reflect.Uint8 {
319 // We store original []byte representation as string.
320 // This conversion is allowed, but we need to be careful about
321 // marshaling data appropriately.
322 if len(sv.Interface().(string)) > 0 {
323 marshalled, err := json.Marshal(sv.Interface())
324 if err != nil {
325 return fmt.Errorf("error encoding %s to json: %v", st, err)
326 }
327 // TODO: Is this Unmarshal needed?
328 var data []byte
329 err = json.Unmarshal(marshalled, &data)
330 if err != nil {
331 return fmt.Errorf("error decoding from json: %v", err)
332 }
333 dv.SetBytes(data)
334 } else {
335 dv.Set(reflect.Zero(dt))
336 }
337 return nil
338 }
339 if st.Kind() != reflect.Slice {
340 return fmt.Errorf("cannot restore slice from %v", st.Kind())
341 }
342
343 if sv.IsNil() {
344 dv.Set(reflect.Zero(dt))
345 return nil
346 }
347 dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
348 for i := 0; i < sv.Len(); i++ {
349 if err := fromUnstructured(sv.Index(i), dv.Index(i)); err != nil {
350 return err
351 }
352 }
353 return nil
354}
355
356func pointerFromUnstructured(sv, dv reflect.Value) error {
357 st, dt := sv.Type(), dv.Type()
358
359 if st.Kind() == reflect.Ptr && sv.IsNil() {
360 dv.Set(reflect.Zero(dt))
361 return nil
362 }
363 dv.Set(reflect.New(dt.Elem()))
364 switch st.Kind() {
365 case reflect.Ptr, reflect.Interface:
366 return fromUnstructured(sv.Elem(), dv.Elem())
367 default:
368 return fromUnstructured(sv, dv.Elem())
369 }
370}
371
372func structFromUnstructured(sv, dv reflect.Value) error {
373 st, dt := sv.Type(), dv.Type()
374 if st.Kind() != reflect.Map {
375 return fmt.Errorf("cannot restore struct from: %v", st.Kind())
376 }
377
378 for i := 0; i < dt.NumField(); i++ {
379 fieldInfo := fieldInfoFromField(dt, i)
380 fv := dv.Field(i)
381
382 if len(fieldInfo.name) == 0 {
383 // This field is inlined.
384 if err := fromUnstructured(sv, fv); err != nil {
385 return err
386 }
387 } else {
388 value := unwrapInterface(sv.MapIndex(fieldInfo.nameValue))
389 if value.IsValid() {
390 if err := fromUnstructured(value, fv); err != nil {
391 return err
392 }
393 } else {
394 fv.Set(reflect.Zero(fv.Type()))
395 }
396 }
397 }
398 return nil
399}
400
401func interfaceFromUnstructured(sv, dv reflect.Value) error {
402 // TODO: Is this conversion safe?
403 dv.Set(sv)
404 return nil
405}
406
407// ToUnstructured converts an object into map[string]interface{} representation.
408// It uses encoding/json/Marshaler if object implements it or reflection if not.
409func (c *unstructuredConverter) ToUnstructured(obj interface{}) (map[string]interface{}, error) {
410 var u map[string]interface{}
411 var err error
412 if unstr, ok := obj.(Unstructured); ok {
413 u = unstr.UnstructuredContent()
414 } else {
415 t := reflect.TypeOf(obj)
416 value := reflect.ValueOf(obj)
417 if t.Kind() != reflect.Ptr || value.IsNil() {
418 return nil, fmt.Errorf("ToUnstructured requires a non-nil pointer to an object, got %v", t)
419 }
420 u = map[string]interface{}{}
421 err = toUnstructured(value.Elem(), reflect.ValueOf(&u).Elem())
422 }
423 if c.mismatchDetection {
424 newUnstr := map[string]interface{}{}
425 newErr := toUnstructuredViaJSON(obj, &newUnstr)
426 if (err != nil) != (newErr != nil) {
David Bainbridge86971522019-09-26 22:09:39 +0000427 klog.Fatalf("ToUnstructured unexpected error for %v: error: %v; newErr: %v", obj, err, newErr)
Zack Williamse940c7a2019-08-21 14:25:39 -0700428 }
429 if err == nil && !c.comparison.DeepEqual(u, newUnstr) {
David Bainbridge86971522019-09-26 22:09:39 +0000430 klog.Fatalf("ToUnstructured mismatch\nobj1: %#v\nobj2: %#v", u, newUnstr)
Zack Williamse940c7a2019-08-21 14:25:39 -0700431 }
432 }
433 if err != nil {
434 return nil, err
435 }
436 return u, nil
437}
438
439// DeepCopyJSON deep copies the passed value, assuming it is a valid JSON representation i.e. only contains
440// types produced by json.Unmarshal() and also int64.
441// bool, int64, float64, string, []interface{}, map[string]interface{}, json.Number and nil
442func DeepCopyJSON(x map[string]interface{}) map[string]interface{} {
443 return DeepCopyJSONValue(x).(map[string]interface{})
444}
445
446// DeepCopyJSONValue deep copies the passed value, assuming it is a valid JSON representation i.e. only contains
447// types produced by json.Unmarshal() and also int64.
448// bool, int64, float64, string, []interface{}, map[string]interface{}, json.Number and nil
449func DeepCopyJSONValue(x interface{}) interface{} {
450 switch x := x.(type) {
451 case map[string]interface{}:
452 if x == nil {
453 // Typed nil - an interface{} that contains a type map[string]interface{} with a value of nil
454 return x
455 }
456 clone := make(map[string]interface{}, len(x))
457 for k, v := range x {
458 clone[k] = DeepCopyJSONValue(v)
459 }
460 return clone
461 case []interface{}:
462 if x == nil {
463 // Typed nil - an interface{} that contains a type []interface{} with a value of nil
464 return x
465 }
466 clone := make([]interface{}, len(x))
467 for i, v := range x {
468 clone[i] = DeepCopyJSONValue(v)
469 }
470 return clone
471 case string, int64, bool, float64, nil, encodingjson.Number:
472 return x
473 default:
474 panic(fmt.Errorf("cannot deep copy %T", x))
475 }
476}
477
478func toUnstructuredViaJSON(obj interface{}, u *map[string]interface{}) error {
479 data, err := json.Marshal(obj)
480 if err != nil {
481 return err
482 }
483 return json.Unmarshal(data, u)
484}
485
486var (
487 nullBytes = []byte("null")
488 trueBytes = []byte("true")
489 falseBytes = []byte("false")
490)
491
492func getMarshaler(v reflect.Value) (encodingjson.Marshaler, bool) {
493 // Check value receivers if v is not a pointer and pointer receivers if v is a pointer
494 if v.Type().Implements(marshalerType) {
495 return v.Interface().(encodingjson.Marshaler), true
496 }
497 // Check pointer receivers if v is not a pointer
498 if v.Kind() != reflect.Ptr && v.CanAddr() {
499 v = v.Addr()
500 if v.Type().Implements(marshalerType) {
501 return v.Interface().(encodingjson.Marshaler), true
502 }
503 }
504 return nil, false
505}
506
507func toUnstructured(sv, dv reflect.Value) error {
508 // Check if the object has a custom JSON marshaller/unmarshaller.
509 if marshaler, ok := getMarshaler(sv); ok {
510 if sv.Kind() == reflect.Ptr && sv.IsNil() {
511 // We're done - we don't need to store anything.
512 return nil
513 }
514
515 data, err := marshaler.MarshalJSON()
516 if err != nil {
517 return err
518 }
519 switch {
520 case len(data) == 0:
521 return fmt.Errorf("error decoding from json: empty value")
522
523 case bytes.Equal(data, nullBytes):
524 // We're done - we don't need to store anything.
525
526 case bytes.Equal(data, trueBytes):
527 dv.Set(reflect.ValueOf(true))
528
529 case bytes.Equal(data, falseBytes):
530 dv.Set(reflect.ValueOf(false))
531
532 case data[0] == '"':
533 var result string
534 err := json.Unmarshal(data, &result)
535 if err != nil {
536 return fmt.Errorf("error decoding string from json: %v", err)
537 }
538 dv.Set(reflect.ValueOf(result))
539
540 case data[0] == '{':
541 result := make(map[string]interface{})
542 err := json.Unmarshal(data, &result)
543 if err != nil {
544 return fmt.Errorf("error decoding object from json: %v", err)
545 }
546 dv.Set(reflect.ValueOf(result))
547
548 case data[0] == '[':
549 result := make([]interface{}, 0)
550 err := json.Unmarshal(data, &result)
551 if err != nil {
552 return fmt.Errorf("error decoding array from json: %v", err)
553 }
554 dv.Set(reflect.ValueOf(result))
555
556 default:
557 var (
558 resultInt int64
559 resultFloat float64
560 err error
561 )
562 if err = json.Unmarshal(data, &resultInt); err == nil {
563 dv.Set(reflect.ValueOf(resultInt))
564 } else if err = json.Unmarshal(data, &resultFloat); err == nil {
565 dv.Set(reflect.ValueOf(resultFloat))
566 } else {
567 return fmt.Errorf("error decoding number from json: %v", err)
568 }
569 }
570
571 return nil
572 }
573
574 st, dt := sv.Type(), dv.Type()
575 switch st.Kind() {
576 case reflect.String:
577 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
578 dv.Set(reflect.New(stringType))
579 }
580 dv.Set(reflect.ValueOf(sv.String()))
581 return nil
582 case reflect.Bool:
583 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
584 dv.Set(reflect.New(boolType))
585 }
586 dv.Set(reflect.ValueOf(sv.Bool()))
587 return nil
588 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
589 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
590 dv.Set(reflect.New(int64Type))
591 }
592 dv.Set(reflect.ValueOf(sv.Int()))
593 return nil
594 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
595 uVal := sv.Uint()
596 if uVal > math.MaxInt64 {
597 return fmt.Errorf("unsigned value %d does not fit into int64 (overflow)", uVal)
598 }
599 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
600 dv.Set(reflect.New(int64Type))
601 }
602 dv.Set(reflect.ValueOf(int64(uVal)))
603 return nil
604 case reflect.Float32, reflect.Float64:
605 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
606 dv.Set(reflect.New(float64Type))
607 }
608 dv.Set(reflect.ValueOf(sv.Float()))
609 return nil
610 case reflect.Map:
611 return mapToUnstructured(sv, dv)
612 case reflect.Slice:
613 return sliceToUnstructured(sv, dv)
614 case reflect.Ptr:
615 return pointerToUnstructured(sv, dv)
616 case reflect.Struct:
617 return structToUnstructured(sv, dv)
618 case reflect.Interface:
619 return interfaceToUnstructured(sv, dv)
620 default:
621 return fmt.Errorf("unrecognized type: %v", st.Kind())
622 }
623}
624
625func mapToUnstructured(sv, dv reflect.Value) error {
626 st, dt := sv.Type(), dv.Type()
627 if sv.IsNil() {
628 dv.Set(reflect.Zero(dt))
629 return nil
630 }
631 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
632 if st.Key().Kind() == reflect.String {
633 switch st.Elem().Kind() {
634 // TODO It should be possible to reuse the slice for primitive types.
635 // However, it is panicing in the following form.
636 // case reflect.String, reflect.Bool,
637 // reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
638 // reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
639 // sv.Set(sv)
640 // return nil
641 default:
642 // We need to do a proper conversion.
643 }
644 }
645 dv.Set(reflect.MakeMap(mapStringInterfaceType))
646 dv = dv.Elem()
647 dt = dv.Type()
648 }
649 if dt.Kind() != reflect.Map {
650 return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
651 }
652
653 if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
654 return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
655 }
656
657 for _, key := range sv.MapKeys() {
658 value := reflect.New(dt.Elem()).Elem()
659 if err := toUnstructured(sv.MapIndex(key), value); err != nil {
660 return err
661 }
662 if st.Key().AssignableTo(dt.Key()) {
663 dv.SetMapIndex(key, value)
664 } else {
665 dv.SetMapIndex(key.Convert(dt.Key()), value)
666 }
667 }
668 return nil
669}
670
671func sliceToUnstructured(sv, dv reflect.Value) error {
672 st, dt := sv.Type(), dv.Type()
673 if sv.IsNil() {
674 dv.Set(reflect.Zero(dt))
675 return nil
676 }
677 if st.Elem().Kind() == reflect.Uint8 {
678 dv.Set(reflect.New(stringType))
679 data, err := json.Marshal(sv.Bytes())
680 if err != nil {
681 return err
682 }
683 var result string
684 if err = json.Unmarshal(data, &result); err != nil {
685 return err
686 }
687 dv.Set(reflect.ValueOf(result))
688 return nil
689 }
690 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
691 switch st.Elem().Kind() {
692 // TODO It should be possible to reuse the slice for primitive types.
693 // However, it is panicing in the following form.
694 // case reflect.String, reflect.Bool,
695 // reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
696 // reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
697 // sv.Set(sv)
698 // return nil
699 default:
700 // We need to do a proper conversion.
701 dv.Set(reflect.MakeSlice(reflect.SliceOf(dt), sv.Len(), sv.Cap()))
702 dv = dv.Elem()
703 dt = dv.Type()
704 }
705 }
706 if dt.Kind() != reflect.Slice {
707 return fmt.Errorf("cannot convert slice to: %v", dt.Kind())
708 }
709 for i := 0; i < sv.Len(); i++ {
710 if err := toUnstructured(sv.Index(i), dv.Index(i)); err != nil {
711 return err
712 }
713 }
714 return nil
715}
716
717func pointerToUnstructured(sv, dv reflect.Value) error {
718 if sv.IsNil() {
719 // We're done - we don't need to store anything.
720 return nil
721 }
722 return toUnstructured(sv.Elem(), dv)
723}
724
725func isZero(v reflect.Value) bool {
726 switch v.Kind() {
727 case reflect.Array, reflect.String:
728 return v.Len() == 0
729 case reflect.Bool:
730 return !v.Bool()
731 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
732 return v.Int() == 0
733 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
734 return v.Uint() == 0
735 case reflect.Float32, reflect.Float64:
736 return v.Float() == 0
737 case reflect.Map, reflect.Slice:
738 // TODO: It seems that 0-len maps are ignored in it.
739 return v.IsNil() || v.Len() == 0
740 case reflect.Ptr, reflect.Interface:
741 return v.IsNil()
742 }
743 return false
744}
745
746func structToUnstructured(sv, dv reflect.Value) error {
747 st, dt := sv.Type(), dv.Type()
748 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
David Bainbridge86971522019-09-26 22:09:39 +0000749 dv.Set(reflect.MakeMapWithSize(mapStringInterfaceType, st.NumField()))
Zack Williamse940c7a2019-08-21 14:25:39 -0700750 dv = dv.Elem()
751 dt = dv.Type()
752 }
753 if dt.Kind() != reflect.Map {
754 return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
755 }
756 realMap := dv.Interface().(map[string]interface{})
757
758 for i := 0; i < st.NumField(); i++ {
759 fieldInfo := fieldInfoFromField(st, i)
760 fv := sv.Field(i)
761
762 if fieldInfo.name == "-" {
763 // This field should be skipped.
764 continue
765 }
766 if fieldInfo.omitempty && isZero(fv) {
767 // omitempty fields should be ignored.
768 continue
769 }
770 if len(fieldInfo.name) == 0 {
771 // This field is inlined.
772 if err := toUnstructured(fv, dv); err != nil {
773 return err
774 }
775 continue
776 }
777 switch fv.Type().Kind() {
778 case reflect.String:
779 realMap[fieldInfo.name] = fv.String()
780 case reflect.Bool:
781 realMap[fieldInfo.name] = fv.Bool()
782 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
783 realMap[fieldInfo.name] = fv.Int()
784 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
785 realMap[fieldInfo.name] = fv.Uint()
786 case reflect.Float32, reflect.Float64:
787 realMap[fieldInfo.name] = fv.Float()
788 default:
789 subv := reflect.New(dt.Elem()).Elem()
790 if err := toUnstructured(fv, subv); err != nil {
791 return err
792 }
793 dv.SetMapIndex(fieldInfo.nameValue, subv)
794 }
795 }
796 return nil
797}
798
799func interfaceToUnstructured(sv, dv reflect.Value) error {
800 if !sv.IsValid() || sv.IsNil() {
801 dv.Set(reflect.Zero(dv.Type()))
802 return nil
803 }
804 return toUnstructured(sv.Elem(), dv)
805}