2 Copyright 2017 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
21 encodingjson "encoding/json"
32 "k8s.io/apimachinery/pkg/conversion"
33 "k8s.io/apimachinery/pkg/util/json"
34 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
39 // UnstructuredConverter is an interface for converting between interface{}
40 // and map[string]interface representation.
41 type UnstructuredConverter interface {
42 ToUnstructured(obj interface{}) (map[string]interface{}, error)
43 FromUnstructured(u map[string]interface{}, obj interface{}) error
46 type structField struct {
47 structType reflect.Type
51 type fieldInfo struct {
53 nameValue reflect.Value
57 type fieldsCacheMap map[structField]*fieldInfo
59 type fieldsCache struct {
64 func newFieldsCache() *fieldsCache {
65 cache := &fieldsCache{}
66 cache.value.Store(make(fieldsCacheMap))
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()
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()
91 func parseBool(key string) bool {
95 value, err := strconv.ParseBool(key)
97 utilruntime.HandleError(fmt.Errorf("Couldn't parse '%s' as bool for unstructured mismatch detection", key))
102 // unstructuredConverter knows how to convert between interface{} and
103 // Unstructured in both ways.
104 type 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
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.
116 func NewTestUnstructuredConverter(comparison conversion.Equalities) UnstructuredConverter {
117 return &unstructuredConverter{
118 mismatchDetection: true,
119 comparison: comparison,
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.
125 func (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)
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) {
136 klog.Fatalf("FromUnstructured unexpected error for %v: error: %v", u, err)
138 if err == nil && !c.comparison.DeepEqual(obj, newObj) {
139 klog.Fatalf("FromUnstructured mismatch\nobj1: %#v\nobj2: %#v", obj, newObj)
145 func fromUnstructuredViaJSON(u map[string]interface{}, obj interface{}) error {
146 data, err := json.Marshal(u)
150 return json.Unmarshal(data, obj)
153 func fromUnstructured(sv, dv reflect.Value) error {
154 sv = unwrapInterface(sv)
156 dv.Set(reflect.Zero(dv.Type()))
159 st, dt := sv.Type(), dv.Type()
162 case reflect.Map, reflect.Slice, reflect.Ptr, reflect.Struct, reflect.Interface:
163 // Those require non-trivial conversion.
165 // This should handle all simple types.
166 if st.AssignableTo(dt) {
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
173 if st.ConvertibleTo(dt) {
178 dv.Set(sv.Convert(dt))
184 dv.Set(sv.Convert(dt))
187 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
188 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
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))
195 case reflect.Float32, reflect.Float64:
197 case reflect.Float32, reflect.Float64:
198 dv.Set(sv.Convert(dt))
201 if sv.Float() == math.Trunc(sv.Float()) {
202 dv.Set(sv.Convert(dt))
206 return fmt.Errorf("cannot convert %s to %s", st.String(), dt.String())
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())
214 return fmt.Errorf("error encoding %s to json: %v", st.String(), err)
216 unmarshaler := dv.Addr().Interface().(encodingjson.Unmarshaler)
217 return unmarshaler.UnmarshalJSON(data)
222 return mapFromUnstructured(sv, dv)
224 return sliceFromUnstructured(sv, dv)
226 return pointerFromUnstructured(sv, dv)
228 return structFromUnstructured(sv, dv)
229 case reflect.Interface:
230 return interfaceFromUnstructured(sv, dv)
232 return fmt.Errorf("unrecognized type: %v", dt.Kind())
236 func fieldInfoFromField(structType reflect.Type, field int) *fieldInfo {
237 fieldCacheMap := fieldCache.value.Load().(fieldsCacheMap)
238 if info, ok := fieldCacheMap[structField{structType, field}]; ok {
242 // Cache miss - we need to compute the field name.
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
251 info.name = strings.ToLower(typeField.Name[:1]) + typeField.Name[1:]
254 items := strings.Split(jsonTag, ",")
256 for i := range items {
257 if items[i] == "omitempty" {
258 info.omitempty = true
262 info.nameValue = reflect.ValueOf(info.name)
265 defer fieldCache.Unlock()
266 fieldCacheMap = fieldCache.value.Load().(fieldsCacheMap)
267 newFieldCacheMap := make(fieldsCacheMap)
268 for k, v := range fieldCacheMap {
269 newFieldCacheMap[k] = v
271 newFieldCacheMap[structField{structType, field}] = info
272 fieldCache.value.Store(newFieldCacheMap)
276 func unwrapInterface(v reflect.Value) reflect.Value {
277 for v.Kind() == reflect.Interface {
283 func 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())
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())
294 dv.Set(reflect.Zero(dt))
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 {
305 value.Set(reflect.Zero(dt.Elem()))
307 if st.Key().AssignableTo(dt.Key()) {
308 dv.SetMapIndex(key, value)
310 dv.SetMapIndex(key.Convert(dt.Key()), value)
316 func 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())
325 return fmt.Errorf("error encoding %s to json: %v", st, err)
327 // TODO: Is this Unmarshal needed?
329 err = json.Unmarshal(marshalled, &data)
331 return fmt.Errorf("error decoding from json: %v", err)
335 dv.Set(reflect.Zero(dt))
339 if st.Kind() != reflect.Slice {
340 return fmt.Errorf("cannot restore slice from %v", st.Kind())
344 dv.Set(reflect.Zero(dt))
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 {
356 func pointerFromUnstructured(sv, dv reflect.Value) error {
357 st, dt := sv.Type(), dv.Type()
359 if st.Kind() == reflect.Ptr && sv.IsNil() {
360 dv.Set(reflect.Zero(dt))
363 dv.Set(reflect.New(dt.Elem()))
365 case reflect.Ptr, reflect.Interface:
366 return fromUnstructured(sv.Elem(), dv.Elem())
368 return fromUnstructured(sv, dv.Elem())
372 func 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())
378 for i := 0; i < dt.NumField(); i++ {
379 fieldInfo := fieldInfoFromField(dt, i)
382 if len(fieldInfo.name) == 0 {
383 // This field is inlined.
384 if err := fromUnstructured(sv, fv); err != nil {
388 value := unwrapInterface(sv.MapIndex(fieldInfo.nameValue))
390 if err := fromUnstructured(value, fv); err != nil {
394 fv.Set(reflect.Zero(fv.Type()))
401 func interfaceFromUnstructured(sv, dv reflect.Value) error {
402 // TODO: Is this conversion safe?
407 // ToUnstructured converts an object into map[string]interface{} representation.
408 // It uses encoding/json/Marshaler if object implements it or reflection if not.
409 func (c *unstructuredConverter) ToUnstructured(obj interface{}) (map[string]interface{}, error) {
410 var u map[string]interface{}
412 if unstr, ok := obj.(Unstructured); ok {
413 u = unstr.UnstructuredContent()
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)
420 u = map[string]interface{}{}
421 err = toUnstructured(value.Elem(), reflect.ValueOf(&u).Elem())
423 if c.mismatchDetection {
424 newUnstr := map[string]interface{}{}
425 newErr := toUnstructuredViaJSON(obj, &newUnstr)
426 if (err != nil) != (newErr != nil) {
427 klog.Fatalf("ToUnstructured unexpected error for %v: error: %v; newErr: %v", obj, err, newErr)
429 if err == nil && !c.comparison.DeepEqual(u, newUnstr) {
430 klog.Fatalf("ToUnstructured mismatch\nobj1: %#v\nobj2: %#v", u, newUnstr)
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
442 func DeepCopyJSON(x map[string]interface{}) map[string]interface{} {
443 return DeepCopyJSONValue(x).(map[string]interface{})
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
449 func DeepCopyJSONValue(x interface{}) interface{} {
450 switch x := x.(type) {
451 case map[string]interface{}:
453 // Typed nil - an interface{} that contains a type map[string]interface{} with a value of nil
456 clone := make(map[string]interface{}, len(x))
457 for k, v := range x {
458 clone[k] = DeepCopyJSONValue(v)
463 // Typed nil - an interface{} that contains a type []interface{} with a value of nil
466 clone := make([]interface{}, len(x))
467 for i, v := range x {
468 clone[i] = DeepCopyJSONValue(v)
471 case string, int64, bool, float64, nil, encodingjson.Number:
474 panic(fmt.Errorf("cannot deep copy %T", x))
478 func toUnstructuredViaJSON(obj interface{}, u *map[string]interface{}) error {
479 data, err := json.Marshal(obj)
483 return json.Unmarshal(data, u)
487 nullBytes = []byte("null")
488 trueBytes = []byte("true")
489 falseBytes = []byte("false")
492 func 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
497 // Check pointer receivers if v is not a pointer
498 if v.Kind() != reflect.Ptr && v.CanAddr() {
500 if v.Type().Implements(marshalerType) {
501 return v.Interface().(encodingjson.Marshaler), true
507 func 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.
515 data, err := marshaler.MarshalJSON()
521 return fmt.Errorf("error decoding from json: empty value")
523 case bytes.Equal(data, nullBytes):
524 // We're done - we don't need to store anything.
526 case bytes.Equal(data, trueBytes):
527 dv.Set(reflect.ValueOf(true))
529 case bytes.Equal(data, falseBytes):
530 dv.Set(reflect.ValueOf(false))
534 err := json.Unmarshal(data, &result)
536 return fmt.Errorf("error decoding string from json: %v", err)
538 dv.Set(reflect.ValueOf(result))
541 result := make(map[string]interface{})
542 err := json.Unmarshal(data, &result)
544 return fmt.Errorf("error decoding object from json: %v", err)
546 dv.Set(reflect.ValueOf(result))
549 result := make([]interface{}, 0)
550 err := json.Unmarshal(data, &result)
552 return fmt.Errorf("error decoding array from json: %v", err)
554 dv.Set(reflect.ValueOf(result))
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))
567 return fmt.Errorf("error decoding number from json: %v", err)
574 st, dt := sv.Type(), dv.Type()
577 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
578 dv.Set(reflect.New(stringType))
580 dv.Set(reflect.ValueOf(sv.String()))
583 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
584 dv.Set(reflect.New(boolType))
586 dv.Set(reflect.ValueOf(sv.Bool()))
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))
592 dv.Set(reflect.ValueOf(sv.Int()))
594 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
596 if uVal > math.MaxInt64 {
597 return fmt.Errorf("unsigned value %d does not fit into int64 (overflow)", uVal)
599 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
600 dv.Set(reflect.New(int64Type))
602 dv.Set(reflect.ValueOf(int64(uVal)))
604 case reflect.Float32, reflect.Float64:
605 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
606 dv.Set(reflect.New(float64Type))
608 dv.Set(reflect.ValueOf(sv.Float()))
611 return mapToUnstructured(sv, dv)
613 return sliceToUnstructured(sv, dv)
615 return pointerToUnstructured(sv, dv)
617 return structToUnstructured(sv, dv)
618 case reflect.Interface:
619 return interfaceToUnstructured(sv, dv)
621 return fmt.Errorf("unrecognized type: %v", st.Kind())
625 func mapToUnstructured(sv, dv reflect.Value) error {
626 st, dt := sv.Type(), dv.Type()
628 dv.Set(reflect.Zero(dt))
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:
642 // We need to do a proper conversion.
645 dv.Set(reflect.MakeMap(mapStringInterfaceType))
649 if dt.Kind() != reflect.Map {
650 return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
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())
657 for _, key := range sv.MapKeys() {
658 value := reflect.New(dt.Elem()).Elem()
659 if err := toUnstructured(sv.MapIndex(key), value); err != nil {
662 if st.Key().AssignableTo(dt.Key()) {
663 dv.SetMapIndex(key, value)
665 dv.SetMapIndex(key.Convert(dt.Key()), value)
671 func sliceToUnstructured(sv, dv reflect.Value) error {
672 st, dt := sv.Type(), dv.Type()
674 dv.Set(reflect.Zero(dt))
677 if st.Elem().Kind() == reflect.Uint8 {
678 dv.Set(reflect.New(stringType))
679 data, err := json.Marshal(sv.Bytes())
684 if err = json.Unmarshal(data, &result); err != nil {
687 dv.Set(reflect.ValueOf(result))
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:
700 // We need to do a proper conversion.
701 dv.Set(reflect.MakeSlice(reflect.SliceOf(dt), sv.Len(), sv.Cap()))
706 if dt.Kind() != reflect.Slice {
707 return fmt.Errorf("cannot convert slice to: %v", dt.Kind())
709 for i := 0; i < sv.Len(); i++ {
710 if err := toUnstructured(sv.Index(i), dv.Index(i)); err != nil {
717 func pointerToUnstructured(sv, dv reflect.Value) error {
719 // We're done - we don't need to store anything.
722 return toUnstructured(sv.Elem(), dv)
725 func isZero(v reflect.Value) bool {
727 case reflect.Array, reflect.String:
731 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
733 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
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:
746 func structToUnstructured(sv, dv reflect.Value) error {
747 st, dt := sv.Type(), dv.Type()
748 if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
749 dv.Set(reflect.MakeMap(mapStringInterfaceType))
753 if dt.Kind() != reflect.Map {
754 return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
756 realMap := dv.Interface().(map[string]interface{})
758 for i := 0; i < st.NumField(); i++ {
759 fieldInfo := fieldInfoFromField(st, i)
762 if fieldInfo.name == "-" {
763 // This field should be skipped.
766 if fieldInfo.omitempty && isZero(fv) {
767 // omitempty fields should be ignored.
770 if len(fieldInfo.name) == 0 {
771 // This field is inlined.
772 if err := toUnstructured(fv, dv); err != nil {
777 switch fv.Type().Kind() {
779 realMap[fieldInfo.name] = fv.String()
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()
789 subv := reflect.New(dt.Elem()).Elem()
790 if err := toUnstructured(fv, subv); err != nil {
793 dv.SetMapIndex(fieldInfo.nameValue, subv)
799 func interfaceToUnstructured(sv, dv reflect.Value) error {
800 if !sv.IsValid() || sv.IsNil() {
801 dv.Set(reflect.Zero(dv.Type()))
804 return toUnstructured(sv.Elem(), dv)