...

Source file src/k8s.io/apimachinery/pkg/runtime/converter.go

Documentation: k8s.io/apimachinery/pkg/runtime

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     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
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    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.
    15  */
    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  // UnstructuredConverter is an interface for converting between interface{}
    41  // and map[string]interface representation.
    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  	// DefaultUnstructuredConverter performs unstructured to Go typed object conversions.
    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  // unstructuredConverter knows how to convert between interface{} and
    99  // Unstructured in both ways.
   100  type unstructuredConverter struct {
   101  	// If true, we will be additionally running conversion via json
   102  	// to ensure that the result is true.
   103  	// This is supposed to be set only in tests.
   104  	mismatchDetection bool
   105  	// comparison is the default test logic used to compare
   106  	comparison conversion.Equalities
   107  }
   108  
   109  // NewTestUnstructuredConverter creates an UnstructuredConverter that accepts JSON typed maps and translates them
   110  // to Go types via reflection. It performs mismatch detection automatically and is intended for use by external
   111  // test tools. Use DefaultUnstructuredConverter if you do not explicitly need mismatch detection.
   112  func NewTestUnstructuredConverter(comparison conversion.Equalities) UnstructuredConverter {
   113  	return NewTestUnstructuredConverterWithValidation(comparison)
   114  }
   115  
   116  // NewTestUnstrucutredConverterWithValidation allows for access to
   117  // FromUnstructuredWithValidation from within tests.
   118  func NewTestUnstructuredConverterWithValidation(comparison conversion.Equalities) *unstructuredConverter {
   119  	return &unstructuredConverter{
   120  		mismatchDetection: true,
   121  		comparison:        comparison,
   122  	}
   123  }
   124  
   125  // fromUnstructuredContext provides options for informing the converter
   126  // the state of its recursive walk through the conversion process.
   127  type fromUnstructuredContext struct {
   128  	// isInlined indicates whether the converter is currently in
   129  	// an inlined field or not to determine whether it should
   130  	// validate the matchedKeys yet or only collect them.
   131  	// This should only be set from `structFromUnstructured`
   132  	isInlined bool
   133  	// matchedKeys is a stack of the set of all fields that exist in the
   134  	// concrete go type of the object being converted into.
   135  	// This should only be manipulated via `pushMatchedKeyTracker`,
   136  	// `recordMatchedKey`, or `popAndVerifyMatchedKeys`
   137  	matchedKeys []map[string]struct{}
   138  	// parentPath collects the path that the conversion
   139  	// takes as it traverses the unstructured json map.
   140  	// It is used to report the full path to any unknown
   141  	// fields that the converter encounters.
   142  	parentPath []string
   143  	// returnUnknownFields indicates whether or not
   144  	// unknown field errors should be collected and
   145  	// returned to the caller
   146  	returnUnknownFields bool
   147  	// unknownFieldErrors are the collection of
   148  	// the full path to each unknown field in the
   149  	// object.
   150  	unknownFieldErrors []error
   151  }
   152  
   153  // pushMatchedKeyTracker adds a placeholder set for tracking
   154  // matched keys for the given level. This should only be
   155  // called from `structFromUnstructured`.
   156  func (c *fromUnstructuredContext) pushMatchedKeyTracker() {
   157  	if !c.returnUnknownFields {
   158  		return
   159  	}
   160  
   161  	c.matchedKeys = append(c.matchedKeys, nil)
   162  }
   163  
   164  // recordMatchedKey initializes the last element of matchedKeys
   165  // (if needed) and sets 'key'. This should only be called from
   166  // `structFromUnstructured`.
   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  // popAndVerifyMatchedKeys pops the last element of matchedKeys,
   180  // checks the matched keys against the data, and adds unknown
   181  // field errors for any matched keys.
   182  // `mapValue` is the value of sv containing all of the keys that exist at this level
   183  // (ie. sv.MapKeys) in the source data.
   184  // `matchedKeys` are all the keys found for that level in the destination object.
   185  // This should only be called from `structFromUnstructured`.
   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  // FromUnstructuredWithValidation converts an object from map[string]interface{} representation into a concrete type.
   235  // It uses encoding/json/Unmarshaler if object implements it or reflection if not.
   236  // It takes a validationDirective that indicates how to behave when it encounters unknown fields.
   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  // FromUnstructured converts an object from map[string]interface{} representation into a concrete type.
   272  // It uses encoding/json/Unmarshaler if object implements it or reflection if not.
   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  		// Those require non-trivial conversion.
   296  	default:
   297  		// This should handle all simple types.
   298  		if st.AssignableTo(dt) {
   299  			dv.Set(sv)
   300  			return nil
   301  		}
   302  		// We cannot simply use "ConvertibleTo", as JSON doesn't support conversions
   303  		// between those four groups: bools, integers, floats and string. We need to
   304  		// do the same.
   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  	// Check if the object has a custom JSON marshaller/unmarshaller.
   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  	// Cache miss - we need to compute the field name.
   375  	info := &fieldInfo{}
   376  	typeField := structType.Field(field)
   377  	jsonTag := typeField.Tag.Get("json")
   378  	if len(jsonTag) == 0 {
   379  		// Make the first character lowercase.
   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  		// We store original []byte representation as string.
   453  		// This conversion is allowed, but we need to be careful about
   454  		// marshaling data appropriately.
   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  			// TODO: Is this Unmarshal needed?
   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  			// This field is inlined, recurse into fromUnstructured again
   533  			// with the same set of matched keys.
   534  			ctx.isInlined = true
   535  			if err := fromUnstructured(sv, fv, ctx); err != nil {
   536  				return err
   537  			}
   538  			ctx.isInlined = svInlined
   539  		} else {
   540  			// This field is not inlined so we recurse into
   541  			// child field of sv corresponding to field i of
   542  			// dv, with a new set of matchedKeys and updating
   543  			// the parentPath to indicate that we are one level
   544  			// deeper.
   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  	// TODO: Is this conversion safe?
   568  	dv.Set(sv)
   569  	return nil
   570  }
   571  
   572  // ToUnstructured converts an object into map[string]interface{} representation.
   573  // It uses encoding/json/Marshaler if object implements it or reflection if not.
   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  // DeepCopyJSON deep copies the passed value, assuming it is a valid JSON representation i.e. only contains
   605  // types produced by json.Unmarshal() and also int64.
   606  // bool, int64, float64, string, []interface{}, map[string]interface{}, json.Number and nil
   607  func DeepCopyJSON(x map[string]interface{}) map[string]interface{} {
   608  	return DeepCopyJSONValue(x).(map[string]interface{})
   609  }
   610  
   611  // DeepCopyJSONValue deep copies the passed value, assuming it is a valid JSON representation i.e. only contains
   612  // types produced by json.Unmarshal() and also int64.
   613  // bool, int64, float64, string, []interface{}, map[string]interface{}, json.Number and nil
   614  func DeepCopyJSONValue(x interface{}) interface{} {
   615  	switch x := x.(type) {
   616  	case map[string]interface{}:
   617  		if x == nil {
   618  			// Typed nil - an interface{} that contains a type map[string]interface{} with a value of nil
   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  			// Typed nil - an interface{} that contains a type []interface{} with a value of nil
   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  	// Check if the object has a custom string converter.
   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  		// We're done - we don't need to store anything.
   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  		// TODO: It seems that 0-len maps are ignored in it.
   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  			// This field should be skipped.
   817  			continue
   818  		}
   819  		if fieldInfo.omitempty && isZero(fv) {
   820  			// omitempty fields should be ignored.
   821  			continue
   822  		}
   823  		if len(fieldInfo.name) == 0 {
   824  			// This field is inlined.
   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