mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			711 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			711 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2017 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package runtime
 | 
						|
 | 
						|
import (
 | 
						|
	encodingjson "encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"math"
 | 
						|
	"os"
 | 
						|
	"reflect"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
	"sync/atomic"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"k8s.io/apimachinery/pkg/conversion"
 | 
						|
	"k8s.io/apimachinery/pkg/util/json"
 | 
						|
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
						|
	"sigs.k8s.io/structured-merge-diff/v4/value"
 | 
						|
 | 
						|
	"k8s.io/klog/v2"
 | 
						|
)
 | 
						|
 | 
						|
// UnstructuredConverter is an interface for converting between interface{}
 | 
						|
// and map[string]interface representation.
 | 
						|
type UnstructuredConverter interface {
 | 
						|
	ToUnstructured(obj interface{}) (map[string]interface{}, error)
 | 
						|
	FromUnstructured(u map[string]interface{}, obj interface{}) error
 | 
						|
}
 | 
						|
 | 
						|
type structField struct {
 | 
						|
	structType reflect.Type
 | 
						|
	field      int
 | 
						|
}
 | 
						|
 | 
						|
type fieldInfo struct {
 | 
						|
	name      string
 | 
						|
	nameValue reflect.Value
 | 
						|
	omitempty bool
 | 
						|
}
 | 
						|
 | 
						|
type fieldsCacheMap map[structField]*fieldInfo
 | 
						|
 | 
						|
type fieldsCache struct {
 | 
						|
	sync.Mutex
 | 
						|
	value atomic.Value
 | 
						|
}
 | 
						|
 | 
						|
func newFieldsCache() *fieldsCache {
 | 
						|
	cache := &fieldsCache{}
 | 
						|
	cache.value.Store(make(fieldsCacheMap))
 | 
						|
	return cache
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
 | 
						|
	stringType             = reflect.TypeOf(string(""))
 | 
						|
	fieldCache             = newFieldsCache()
 | 
						|
 | 
						|
	// DefaultUnstructuredConverter performs unstructured to Go typed object conversions.
 | 
						|
	DefaultUnstructuredConverter = &unstructuredConverter{
 | 
						|
		mismatchDetection: parseBool(os.Getenv("KUBE_PATCH_CONVERSION_DETECTOR")),
 | 
						|
		comparison: conversion.EqualitiesOrDie(
 | 
						|
			func(a, b time.Time) bool {
 | 
						|
				return a.UTC() == b.UTC()
 | 
						|
			},
 | 
						|
		),
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
func parseBool(key string) bool {
 | 
						|
	if len(key) == 0 {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	value, err := strconv.ParseBool(key)
 | 
						|
	if err != nil {
 | 
						|
		utilruntime.HandleError(fmt.Errorf("couldn't parse '%s' as bool for unstructured mismatch detection", key))
 | 
						|
	}
 | 
						|
	return value
 | 
						|
}
 | 
						|
 | 
						|
// unstructuredConverter knows how to convert between interface{} and
 | 
						|
// Unstructured in both ways.
 | 
						|
type unstructuredConverter struct {
 | 
						|
	// If true, we will be additionally running conversion via json
 | 
						|
	// to ensure that the result is true.
 | 
						|
	// This is supposed to be set only in tests.
 | 
						|
	mismatchDetection bool
 | 
						|
	// comparison is the default test logic used to compare
 | 
						|
	comparison conversion.Equalities
 | 
						|
}
 | 
						|
 | 
						|
// NewTestUnstructuredConverter creates an UnstructuredConverter that accepts JSON typed maps and translates them
 | 
						|
// to Go types via reflection. It performs mismatch detection automatically and is intended for use by external
 | 
						|
// test tools. Use DefaultUnstructuredConverter if you do not explicitly need mismatch detection.
 | 
						|
func NewTestUnstructuredConverter(comparison conversion.Equalities) UnstructuredConverter {
 | 
						|
	return &unstructuredConverter{
 | 
						|
		mismatchDetection: true,
 | 
						|
		comparison:        comparison,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// FromUnstructured converts an object from map[string]interface{} representation into a concrete type.
 | 
						|
// It uses encoding/json/Unmarshaler if object implements it or reflection if not.
 | 
						|
func (c *unstructuredConverter) FromUnstructured(u map[string]interface{}, obj interface{}) error {
 | 
						|
	t := reflect.TypeOf(obj)
 | 
						|
	value := reflect.ValueOf(obj)
 | 
						|
	if t.Kind() != reflect.Ptr || value.IsNil() {
 | 
						|
		return fmt.Errorf("FromUnstructured requires a non-nil pointer to an object, got %v", t)
 | 
						|
	}
 | 
						|
	err := fromUnstructured(reflect.ValueOf(u), value.Elem())
 | 
						|
	if c.mismatchDetection {
 | 
						|
		newObj := reflect.New(t.Elem()).Interface()
 | 
						|
		newErr := fromUnstructuredViaJSON(u, newObj)
 | 
						|
		if (err != nil) != (newErr != nil) {
 | 
						|
			klog.Fatalf("FromUnstructured unexpected error for %v: error: %v", u, err)
 | 
						|
		}
 | 
						|
		if err == nil && !c.comparison.DeepEqual(obj, newObj) {
 | 
						|
			klog.Fatalf("FromUnstructured mismatch\nobj1: %#v\nobj2: %#v", obj, newObj)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func fromUnstructuredViaJSON(u map[string]interface{}, obj interface{}) error {
 | 
						|
	data, err := json.Marshal(u)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return json.Unmarshal(data, obj)
 | 
						|
}
 | 
						|
 | 
						|
func fromUnstructured(sv, dv reflect.Value) error {
 | 
						|
	sv = unwrapInterface(sv)
 | 
						|
	if !sv.IsValid() {
 | 
						|
		dv.Set(reflect.Zero(dv.Type()))
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	st, dt := sv.Type(), dv.Type()
 | 
						|
 | 
						|
	switch dt.Kind() {
 | 
						|
	case reflect.Map, reflect.Slice, reflect.Ptr, reflect.Struct, reflect.Interface:
 | 
						|
		// Those require non-trivial conversion.
 | 
						|
	default:
 | 
						|
		// This should handle all simple types.
 | 
						|
		if st.AssignableTo(dt) {
 | 
						|
			dv.Set(sv)
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
		// We cannot simply use "ConvertibleTo", as JSON doesn't support conversions
 | 
						|
		// between those four groups: bools, integers, floats and string. We need to
 | 
						|
		// do the same.
 | 
						|
		if st.ConvertibleTo(dt) {
 | 
						|
			switch st.Kind() {
 | 
						|
			case reflect.String:
 | 
						|
				switch dt.Kind() {
 | 
						|
				case reflect.String:
 | 
						|
					dv.Set(sv.Convert(dt))
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
			case reflect.Bool:
 | 
						|
				switch dt.Kind() {
 | 
						|
				case reflect.Bool:
 | 
						|
					dv.Set(sv.Convert(dt))
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
 | 
						|
				reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
				switch dt.Kind() {
 | 
						|
				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
 | 
						|
					reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
					dv.Set(sv.Convert(dt))
 | 
						|
					return nil
 | 
						|
				case reflect.Float32, reflect.Float64:
 | 
						|
					dv.Set(sv.Convert(dt))
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
			case reflect.Float32, reflect.Float64:
 | 
						|
				switch dt.Kind() {
 | 
						|
				case reflect.Float32, reflect.Float64:
 | 
						|
					dv.Set(sv.Convert(dt))
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
				if sv.Float() == math.Trunc(sv.Float()) {
 | 
						|
					dv.Set(sv.Convert(dt))
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return fmt.Errorf("cannot convert %s to %s", st.String(), dt.String())
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Check if the object has a custom JSON marshaller/unmarshaller.
 | 
						|
	entry := value.TypeReflectEntryOf(dv.Type())
 | 
						|
	if entry.CanConvertFromUnstructured() {
 | 
						|
		return entry.FromUnstructured(sv, dv)
 | 
						|
	}
 | 
						|
 | 
						|
	switch dt.Kind() {
 | 
						|
	case reflect.Map:
 | 
						|
		return mapFromUnstructured(sv, dv)
 | 
						|
	case reflect.Slice:
 | 
						|
		return sliceFromUnstructured(sv, dv)
 | 
						|
	case reflect.Ptr:
 | 
						|
		return pointerFromUnstructured(sv, dv)
 | 
						|
	case reflect.Struct:
 | 
						|
		return structFromUnstructured(sv, dv)
 | 
						|
	case reflect.Interface:
 | 
						|
		return interfaceFromUnstructured(sv, dv)
 | 
						|
	default:
 | 
						|
		return fmt.Errorf("unrecognized type: %v", dt.Kind())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func fieldInfoFromField(structType reflect.Type, field int) *fieldInfo {
 | 
						|
	fieldCacheMap := fieldCache.value.Load().(fieldsCacheMap)
 | 
						|
	if info, ok := fieldCacheMap[structField{structType, field}]; ok {
 | 
						|
		return info
 | 
						|
	}
 | 
						|
 | 
						|
	// Cache miss - we need to compute the field name.
 | 
						|
	info := &fieldInfo{}
 | 
						|
	typeField := structType.Field(field)
 | 
						|
	jsonTag := typeField.Tag.Get("json")
 | 
						|
	if len(jsonTag) == 0 {
 | 
						|
		// Make the first character lowercase.
 | 
						|
		if typeField.Name == "" {
 | 
						|
			info.name = typeField.Name
 | 
						|
		} else {
 | 
						|
			info.name = strings.ToLower(typeField.Name[:1]) + typeField.Name[1:]
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		items := strings.Split(jsonTag, ",")
 | 
						|
		info.name = items[0]
 | 
						|
		for i := range items {
 | 
						|
			if items[i] == "omitempty" {
 | 
						|
				info.omitempty = true
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	info.nameValue = reflect.ValueOf(info.name)
 | 
						|
 | 
						|
	fieldCache.Lock()
 | 
						|
	defer fieldCache.Unlock()
 | 
						|
	fieldCacheMap = fieldCache.value.Load().(fieldsCacheMap)
 | 
						|
	newFieldCacheMap := make(fieldsCacheMap)
 | 
						|
	for k, v := range fieldCacheMap {
 | 
						|
		newFieldCacheMap[k] = v
 | 
						|
	}
 | 
						|
	newFieldCacheMap[structField{structType, field}] = info
 | 
						|
	fieldCache.value.Store(newFieldCacheMap)
 | 
						|
	return info
 | 
						|
}
 | 
						|
 | 
						|
func unwrapInterface(v reflect.Value) reflect.Value {
 | 
						|
	for v.Kind() == reflect.Interface {
 | 
						|
		v = v.Elem()
 | 
						|
	}
 | 
						|
	return v
 | 
						|
}
 | 
						|
 | 
						|
func mapFromUnstructured(sv, dv reflect.Value) error {
 | 
						|
	st, dt := sv.Type(), dv.Type()
 | 
						|
	if st.Kind() != reflect.Map {
 | 
						|
		return fmt.Errorf("cannot restore map from %v", st.Kind())
 | 
						|
	}
 | 
						|
 | 
						|
	if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
 | 
						|
		return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
 | 
						|
	}
 | 
						|
 | 
						|
	if sv.IsNil() {
 | 
						|
		dv.Set(reflect.Zero(dt))
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	dv.Set(reflect.MakeMap(dt))
 | 
						|
	for _, key := range sv.MapKeys() {
 | 
						|
		value := reflect.New(dt.Elem()).Elem()
 | 
						|
		if val := unwrapInterface(sv.MapIndex(key)); val.IsValid() {
 | 
						|
			if err := fromUnstructured(val, value); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			value.Set(reflect.Zero(dt.Elem()))
 | 
						|
		}
 | 
						|
		if st.Key().AssignableTo(dt.Key()) {
 | 
						|
			dv.SetMapIndex(key, value)
 | 
						|
		} else {
 | 
						|
			dv.SetMapIndex(key.Convert(dt.Key()), value)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func sliceFromUnstructured(sv, dv reflect.Value) error {
 | 
						|
	st, dt := sv.Type(), dv.Type()
 | 
						|
	if st.Kind() == reflect.String && dt.Elem().Kind() == reflect.Uint8 {
 | 
						|
		// We store original []byte representation as string.
 | 
						|
		// This conversion is allowed, but we need to be careful about
 | 
						|
		// marshaling data appropriately.
 | 
						|
		if len(sv.Interface().(string)) > 0 {
 | 
						|
			marshalled, err := json.Marshal(sv.Interface())
 | 
						|
			if err != nil {
 | 
						|
				return fmt.Errorf("error encoding %s to json: %v", st, err)
 | 
						|
			}
 | 
						|
			// TODO: Is this Unmarshal needed?
 | 
						|
			var data []byte
 | 
						|
			err = json.Unmarshal(marshalled, &data)
 | 
						|
			if err != nil {
 | 
						|
				return fmt.Errorf("error decoding from json: %v", err)
 | 
						|
			}
 | 
						|
			dv.SetBytes(data)
 | 
						|
		} else {
 | 
						|
			dv.Set(reflect.Zero(dt))
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	if st.Kind() != reflect.Slice {
 | 
						|
		return fmt.Errorf("cannot restore slice from %v", st.Kind())
 | 
						|
	}
 | 
						|
 | 
						|
	if sv.IsNil() {
 | 
						|
		dv.Set(reflect.Zero(dt))
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
 | 
						|
	for i := 0; i < sv.Len(); i++ {
 | 
						|
		if err := fromUnstructured(sv.Index(i), dv.Index(i)); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func pointerFromUnstructured(sv, dv reflect.Value) error {
 | 
						|
	st, dt := sv.Type(), dv.Type()
 | 
						|
 | 
						|
	if st.Kind() == reflect.Ptr && sv.IsNil() {
 | 
						|
		dv.Set(reflect.Zero(dt))
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	dv.Set(reflect.New(dt.Elem()))
 | 
						|
	switch st.Kind() {
 | 
						|
	case reflect.Ptr, reflect.Interface:
 | 
						|
		return fromUnstructured(sv.Elem(), dv.Elem())
 | 
						|
	default:
 | 
						|
		return fromUnstructured(sv, dv.Elem())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func structFromUnstructured(sv, dv reflect.Value) error {
 | 
						|
	st, dt := sv.Type(), dv.Type()
 | 
						|
	if st.Kind() != reflect.Map {
 | 
						|
		return fmt.Errorf("cannot restore struct from: %v", st.Kind())
 | 
						|
	}
 | 
						|
 | 
						|
	for i := 0; i < dt.NumField(); i++ {
 | 
						|
		fieldInfo := fieldInfoFromField(dt, i)
 | 
						|
		fv := dv.Field(i)
 | 
						|
 | 
						|
		if len(fieldInfo.name) == 0 {
 | 
						|
			// This field is inlined.
 | 
						|
			if err := fromUnstructured(sv, fv); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			value := unwrapInterface(sv.MapIndex(fieldInfo.nameValue))
 | 
						|
			if value.IsValid() {
 | 
						|
				if err := fromUnstructured(value, fv); err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				fv.Set(reflect.Zero(fv.Type()))
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func interfaceFromUnstructured(sv, dv reflect.Value) error {
 | 
						|
	// TODO: Is this conversion safe?
 | 
						|
	dv.Set(sv)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// ToUnstructured converts an object into map[string]interface{} representation.
 | 
						|
// It uses encoding/json/Marshaler if object implements it or reflection if not.
 | 
						|
func (c *unstructuredConverter) ToUnstructured(obj interface{}) (map[string]interface{}, error) {
 | 
						|
	var u map[string]interface{}
 | 
						|
	var err error
 | 
						|
	if unstr, ok := obj.(Unstructured); ok {
 | 
						|
		u = unstr.UnstructuredContent()
 | 
						|
	} else {
 | 
						|
		t := reflect.TypeOf(obj)
 | 
						|
		value := reflect.ValueOf(obj)
 | 
						|
		if t.Kind() != reflect.Ptr || value.IsNil() {
 | 
						|
			return nil, fmt.Errorf("ToUnstructured requires a non-nil pointer to an object, got %v", t)
 | 
						|
		}
 | 
						|
		u = map[string]interface{}{}
 | 
						|
		err = toUnstructured(value.Elem(), reflect.ValueOf(&u).Elem())
 | 
						|
	}
 | 
						|
	if c.mismatchDetection {
 | 
						|
		newUnstr := map[string]interface{}{}
 | 
						|
		newErr := toUnstructuredViaJSON(obj, &newUnstr)
 | 
						|
		if (err != nil) != (newErr != nil) {
 | 
						|
			klog.Fatalf("ToUnstructured unexpected error for %v: error: %v; newErr: %v", obj, err, newErr)
 | 
						|
		}
 | 
						|
		if err == nil && !c.comparison.DeepEqual(u, newUnstr) {
 | 
						|
			klog.Fatalf("ToUnstructured mismatch\nobj1: %#v\nobj2: %#v", u, newUnstr)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return u, nil
 | 
						|
}
 | 
						|
 | 
						|
// DeepCopyJSON deep copies the passed value, assuming it is a valid JSON representation i.e. only contains
 | 
						|
// types produced by json.Unmarshal() and also int64.
 | 
						|
// bool, int64, float64, string, []interface{}, map[string]interface{}, json.Number and nil
 | 
						|
func DeepCopyJSON(x map[string]interface{}) map[string]interface{} {
 | 
						|
	return DeepCopyJSONValue(x).(map[string]interface{})
 | 
						|
}
 | 
						|
 | 
						|
// DeepCopyJSONValue deep copies the passed value, assuming it is a valid JSON representation i.e. only contains
 | 
						|
// types produced by json.Unmarshal() and also int64.
 | 
						|
// bool, int64, float64, string, []interface{}, map[string]interface{}, json.Number and nil
 | 
						|
func DeepCopyJSONValue(x interface{}) interface{} {
 | 
						|
	switch x := x.(type) {
 | 
						|
	case map[string]interface{}:
 | 
						|
		if x == nil {
 | 
						|
			// Typed nil - an interface{} that contains a type map[string]interface{} with a value of nil
 | 
						|
			return x
 | 
						|
		}
 | 
						|
		clone := make(map[string]interface{}, len(x))
 | 
						|
		for k, v := range x {
 | 
						|
			clone[k] = DeepCopyJSONValue(v)
 | 
						|
		}
 | 
						|
		return clone
 | 
						|
	case []interface{}:
 | 
						|
		if x == nil {
 | 
						|
			// Typed nil - an interface{} that contains a type []interface{} with a value of nil
 | 
						|
			return x
 | 
						|
		}
 | 
						|
		clone := make([]interface{}, len(x))
 | 
						|
		for i, v := range x {
 | 
						|
			clone[i] = DeepCopyJSONValue(v)
 | 
						|
		}
 | 
						|
		return clone
 | 
						|
	case string, int64, bool, float64, nil, encodingjson.Number:
 | 
						|
		return x
 | 
						|
	default:
 | 
						|
		panic(fmt.Errorf("cannot deep copy %T", x))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func toUnstructuredViaJSON(obj interface{}, u *map[string]interface{}) error {
 | 
						|
	data, err := json.Marshal(obj)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return json.Unmarshal(data, u)
 | 
						|
}
 | 
						|
 | 
						|
func toUnstructured(sv, dv reflect.Value) error {
 | 
						|
	// Check if the object has a custom string converter.
 | 
						|
	entry := value.TypeReflectEntryOf(sv.Type())
 | 
						|
	if entry.CanConvertToUnstructured() {
 | 
						|
		v, err := entry.ToUnstructured(sv)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if v != nil {
 | 
						|
			dv.Set(reflect.ValueOf(v))
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	st := sv.Type()
 | 
						|
	switch st.Kind() {
 | 
						|
	case reflect.String:
 | 
						|
		dv.Set(reflect.ValueOf(sv.String()))
 | 
						|
		return nil
 | 
						|
	case reflect.Bool:
 | 
						|
		dv.Set(reflect.ValueOf(sv.Bool()))
 | 
						|
		return nil
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		dv.Set(reflect.ValueOf(sv.Int()))
 | 
						|
		return nil
 | 
						|
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
		uVal := sv.Uint()
 | 
						|
		if uVal > math.MaxInt64 {
 | 
						|
			return fmt.Errorf("unsigned value %d does not fit into int64 (overflow)", uVal)
 | 
						|
		}
 | 
						|
		dv.Set(reflect.ValueOf(int64(uVal)))
 | 
						|
		return nil
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		dv.Set(reflect.ValueOf(sv.Float()))
 | 
						|
		return nil
 | 
						|
	case reflect.Map:
 | 
						|
		return mapToUnstructured(sv, dv)
 | 
						|
	case reflect.Slice:
 | 
						|
		return sliceToUnstructured(sv, dv)
 | 
						|
	case reflect.Ptr:
 | 
						|
		return pointerToUnstructured(sv, dv)
 | 
						|
	case reflect.Struct:
 | 
						|
		return structToUnstructured(sv, dv)
 | 
						|
	case reflect.Interface:
 | 
						|
		return interfaceToUnstructured(sv, dv)
 | 
						|
	default:
 | 
						|
		return fmt.Errorf("unrecognized type: %v", st.Kind())
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func mapToUnstructured(sv, dv reflect.Value) error {
 | 
						|
	st, dt := sv.Type(), dv.Type()
 | 
						|
	if sv.IsNil() {
 | 
						|
		dv.Set(reflect.Zero(dt))
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
 | 
						|
		if st.Key().Kind() == reflect.String {
 | 
						|
			switch st.Elem().Kind() {
 | 
						|
			// TODO It should be possible to reuse the slice for primitive types.
 | 
						|
			// However, it is panicing in the following form.
 | 
						|
			// case reflect.String, reflect.Bool,
 | 
						|
			// 	reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
 | 
						|
			// 	reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
			// 	sv.Set(sv)
 | 
						|
			// 	return nil
 | 
						|
			default:
 | 
						|
				// We need to do a proper conversion.
 | 
						|
			}
 | 
						|
		}
 | 
						|
		dv.Set(reflect.MakeMap(mapStringInterfaceType))
 | 
						|
		dv = dv.Elem()
 | 
						|
		dt = dv.Type()
 | 
						|
	}
 | 
						|
	if dt.Kind() != reflect.Map {
 | 
						|
		return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
 | 
						|
	}
 | 
						|
 | 
						|
	if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
 | 
						|
		return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
 | 
						|
	}
 | 
						|
 | 
						|
	for _, key := range sv.MapKeys() {
 | 
						|
		value := reflect.New(dt.Elem()).Elem()
 | 
						|
		if err := toUnstructured(sv.MapIndex(key), value); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if st.Key().AssignableTo(dt.Key()) {
 | 
						|
			dv.SetMapIndex(key, value)
 | 
						|
		} else {
 | 
						|
			dv.SetMapIndex(key.Convert(dt.Key()), value)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func sliceToUnstructured(sv, dv reflect.Value) error {
 | 
						|
	st, dt := sv.Type(), dv.Type()
 | 
						|
	if sv.IsNil() {
 | 
						|
		dv.Set(reflect.Zero(dt))
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	if st.Elem().Kind() == reflect.Uint8 {
 | 
						|
		dv.Set(reflect.New(stringType))
 | 
						|
		data, err := json.Marshal(sv.Bytes())
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		var result string
 | 
						|
		if err = json.Unmarshal(data, &result); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		dv.Set(reflect.ValueOf(result))
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
 | 
						|
		switch st.Elem().Kind() {
 | 
						|
		// TODO It should be possible to reuse the slice for primitive types.
 | 
						|
		// However, it is panicing in the following form.
 | 
						|
		// case reflect.String, reflect.Bool,
 | 
						|
		// 	reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
 | 
						|
		// 	reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
		// 	sv.Set(sv)
 | 
						|
		// 	return nil
 | 
						|
		default:
 | 
						|
			// We need to do a proper conversion.
 | 
						|
			dv.Set(reflect.MakeSlice(reflect.SliceOf(dt), sv.Len(), sv.Cap()))
 | 
						|
			dv = dv.Elem()
 | 
						|
			dt = dv.Type()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if dt.Kind() != reflect.Slice {
 | 
						|
		return fmt.Errorf("cannot convert slice to: %v", dt.Kind())
 | 
						|
	}
 | 
						|
	for i := 0; i < sv.Len(); i++ {
 | 
						|
		if err := toUnstructured(sv.Index(i), dv.Index(i)); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func pointerToUnstructured(sv, dv reflect.Value) error {
 | 
						|
	if sv.IsNil() {
 | 
						|
		// We're done - we don't need to store anything.
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	return toUnstructured(sv.Elem(), dv)
 | 
						|
}
 | 
						|
 | 
						|
func isZero(v reflect.Value) bool {
 | 
						|
	switch v.Kind() {
 | 
						|
	case reflect.Array, reflect.String:
 | 
						|
		return v.Len() == 0
 | 
						|
	case reflect.Bool:
 | 
						|
		return !v.Bool()
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		return v.Int() == 0
 | 
						|
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
		return v.Uint() == 0
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		return v.Float() == 0
 | 
						|
	case reflect.Map, reflect.Slice:
 | 
						|
		// TODO: It seems that 0-len maps are ignored in it.
 | 
						|
		return v.IsNil() || v.Len() == 0
 | 
						|
	case reflect.Ptr, reflect.Interface:
 | 
						|
		return v.IsNil()
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func structToUnstructured(sv, dv reflect.Value) error {
 | 
						|
	st, dt := sv.Type(), dv.Type()
 | 
						|
	if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
 | 
						|
		dv.Set(reflect.MakeMapWithSize(mapStringInterfaceType, st.NumField()))
 | 
						|
		dv = dv.Elem()
 | 
						|
		dt = dv.Type()
 | 
						|
	}
 | 
						|
	if dt.Kind() != reflect.Map {
 | 
						|
		return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
 | 
						|
	}
 | 
						|
	realMap := dv.Interface().(map[string]interface{})
 | 
						|
 | 
						|
	for i := 0; i < st.NumField(); i++ {
 | 
						|
		fieldInfo := fieldInfoFromField(st, i)
 | 
						|
		fv := sv.Field(i)
 | 
						|
 | 
						|
		if fieldInfo.name == "-" {
 | 
						|
			// This field should be skipped.
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if fieldInfo.omitempty && isZero(fv) {
 | 
						|
			// omitempty fields should be ignored.
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if len(fieldInfo.name) == 0 {
 | 
						|
			// This field is inlined.
 | 
						|
			if err := toUnstructured(fv, dv); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		switch fv.Type().Kind() {
 | 
						|
		case reflect.String:
 | 
						|
			realMap[fieldInfo.name] = fv.String()
 | 
						|
		case reflect.Bool:
 | 
						|
			realMap[fieldInfo.name] = fv.Bool()
 | 
						|
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
			realMap[fieldInfo.name] = fv.Int()
 | 
						|
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
			realMap[fieldInfo.name] = fv.Uint()
 | 
						|
		case reflect.Float32, reflect.Float64:
 | 
						|
			realMap[fieldInfo.name] = fv.Float()
 | 
						|
		default:
 | 
						|
			subv := reflect.New(dt.Elem()).Elem()
 | 
						|
			if err := toUnstructured(fv, subv); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			dv.SetMapIndex(fieldInfo.nameValue, subv)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func interfaceToUnstructured(sv, dv reflect.Value) error {
 | 
						|
	if !sv.IsValid() || sv.IsNil() {
 | 
						|
		dv.Set(reflect.Zero(dv.Type()))
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	return toUnstructured(sv.Elem(), dv)
 | 
						|
}
 |