1/* 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 36 "k8s.io/klog" 37) 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) { 136 klog.Fatalf("FromUnstructured unexpected error for %v: error: %v", u, err) 137 } 138 if err == nil && !c.comparison.DeepEqual(obj, newObj) { 139 klog.Fatalf("FromUnstructured mismatch\nobj1: %#v\nobj2: %#v", obj, newObj) 140 } 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) { 427 klog.Fatalf("ToUnstructured unexpected error for %v: error: %v; newErr: %v", obj, err, newErr) 428 } 429 if err == nil && !c.comparison.DeepEqual(u, newUnstr) { 430 klog.Fatalf("ToUnstructured mismatch\nobj1: %#v\nobj2: %#v", u, newUnstr) 431 } 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 { 749 dv.Set(reflect.MakeMapWithSize(mapStringInterfaceType, st.NumField())) 750 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} 806