Files
virtual-kubelet/vendor/github.com/vmware/vic/pkg/vsphere/extraconfig/decode.go

478 lines
14 KiB
Go

// Copyright 2016 VMware, Inc. All Rights Reserved.
//
// 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 extraconfig
import (
"encoding/base64"
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
var (
nilValue = reflect.ValueOf(nil)
)
type decoder func(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error)
var (
kindDecoders map[reflect.Kind]decoder
intfDecoders map[reflect.Type]decoder
)
func init() {
kindDecoders = map[reflect.Kind]decoder{
reflect.String: decodeString,
reflect.Struct: decodeStruct,
reflect.Slice: decodeSlice,
reflect.Array: decodeSlice,
reflect.Map: decodeMap,
reflect.Ptr: decodePtr,
reflect.Int: decodePrimitive,
reflect.Int8: decodePrimitive,
reflect.Int16: decodePrimitive,
reflect.Int32: decodePrimitive,
reflect.Int64: decodePrimitive,
reflect.Bool: decodePrimitive,
reflect.Float32: decodePrimitive,
reflect.Float64: decodePrimitive,
}
intfDecoders = map[reflect.Type]decoder{
reflect.TypeOf(time.Time{}): decodeTime,
}
}
// decode is the generic switcher that decides which decoder to use for a field
func decode(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error) {
// if depth has reached zero, we skip decoding entirely
if depth.depth == 0 || depth.skipDecode {
return dest, nil
}
depth.depth--
// obtain the handler from the map, checking for the more specific interfaces first
dec, ok := intfDecoders[dest.Type()]
if ok {
return dec(src, dest, prefix, depth)
}
dec, ok = kindDecoders[dest.Kind()]
if ok {
return dec(src, dest, prefix, depth)
}
logger.Debugf("Skipping unsupported field, interface: %T, kind %s", dest, dest.Kind())
return dest, nil
}
// decodeString is the degenerative case where what we get is what we need
func decodeString(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error) {
v, err := src(prefix)
if err != nil {
logger.Debugf("No value found in data source for string at key %q", prefix)
return nilValue, err
}
return reflect.ValueOf(v), nil
}
// decodePrimitive wraps the fromString primitive decoding in a manner that can be called via decode
func decodePrimitive(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error) {
var this reflect.Value
if !dest.CanAddr() {
logger.Debugf("Making new primitive for %s", prefix)
ptr := reflect.New(dest.Type())
this = ptr.Elem()
} else {
logger.Debugf("Reusing existing struct for %s", prefix)
this = dest
}
// see if there's a value to decode
v, err := src(prefix)
if err != nil {
logger.Debugf("No value available for key to primitive %s", prefix)
return nilValue, err
}
t := this.Type()
this.Set(fromString(reflect.Zero(t), v))
return this, nil
}
func decodePtr(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error) {
// if we're not following pointers, then return immediately
if !depth.follow {
return dest, nil
}
// value representing the run-time data
logger.Debugf("Decoding pointer into object: %#v", dest)
// if the pointer is nil we need to create the destination type
target := dest
if dest.IsNil() {
target = reflect.New(dest.Type().Elem())
}
// check to see if the resulting object is not nil
// If it is nil, then there was nothing to decode and the pointer remains nil
result, err := decode(src, target.Elem(), prefix, depth)
logger.Debugf("target is now %#v, %+q ", target, target.Type())
if !result.IsValid() || err == ErrKeyNotFound {
// leave the pointer as nil if the result is zero type or invalid
return dest, nil
}
// neither pointer, nor zero
// NOTE: if the returned result is not addressable this can panic - that generally
// indicates an incorrect implementation of a decodeX method... those should always
// return addressable Values. See decodeByteSlice as an example - this uses make([]byte)
// rather than built in string(bytes) conversion specifically to get an addressable return
if dest.IsNil() {
dest = target
}
dest.Elem().Set(result)
return dest, nil
}
var typeType = reflect.TypeOf((*reflect.Type)(nil)).Elem()
func decodeStruct(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error) {
// value representing the run-time data
logger.Debugf("Decoding struct into object: %#v, type: %s", dest, dest.Type().Name())
var this reflect.Value
if !dest.CanAddr() {
logger.Debugf("Making new struct for %s", prefix)
ptr := reflect.New(dest.Type())
this = ptr.Elem()
} else {
logger.Debugf("Reusing existing struct for %s", prefix)
this = dest
}
// do we have any data for this struct at all
var valid bool
var err error
noKeysFound := true
// iterate through every field in the struct
for i := 0; i < this.NumField(); i++ {
field := this.Field(i)
key, fdepth := calculateKeyFromField(this.Type().Field(i), prefix, depth)
if key == "" {
// this is either a malformed key or explicitly skipped
continue
}
// Dump what we have so far
logger.Debugf("Key: %s, Kind: %s Value: %s", key, field.Kind(), field.String())
// check to see if the resulting object is not nil
// If it is nil, then there was nothing to decode
var result reflect.Value
result, err = decode(src, field, key, fdepth)
if result.IsValid() {
logger.Debugf("Setting field %s to %#v", this.Type().Field(i).Name, result)
field.Set(result)
valid = true
if err != ErrKeyNotFound {
noKeysFound = false
}
} else {
logger.Debugf("Invalid result for field %s", this.Type().Field(i).Name)
}
}
if !valid || noKeysFound {
logger.Debugf("No valid result, returning nil value")
return nilValue, err
}
logger.Debugf("Return decoded structure for %s: %#v", prefix, this)
return this, nil
}
func decodeByteSlice(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error) {
logger.Debugf("Converting string to []byte")
base, err := src(prefix)
if err != nil {
logger.Debugf("No value found in data source for []byte %q", prefix)
return nilValue, err
}
bytes, err := base64.StdEncoding.DecodeString(base)
if err != nil {
logger.Debugf("Expected base64 encoded string for []byte %q: %s", prefix, err)
return nilValue, err
}
length := len(bytes)
// we don't even try to merge byte arrays - no idea how to get append behaviour
// correct with reflection
// use make([]byte) rather than built in string(bytes) conversion to get an addressable return value
logger.Debugf("Making new slice for %s", prefix)
this := make([]byte, length, length)
copy(this, bytes)
return reflect.ValueOf(this), nil
}
func decodeSlice(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error) {
// value representing the run-time data
logger.Debugf("Decoding struct into object: %#v", dest)
kind := dest.Type().Elem().Kind()
if kind == reflect.Uint8 {
return decodeByteSlice(src, dest, prefix, depth)
}
// do we have any data for this struct at all
length := 0
curLen := 0
// get the length of the array
len, err := src(prefix)
if err != nil || len == "" {
logger.Debugf("No value available for key %s - will create empty array if needed", prefix)
} else {
// if there's any data at all then we can assume we need to be extant
lengthValue := fromString(reflect.ValueOf(0), len)
length = int(lengthValue.Int()) + 1
}
var this reflect.Value
if !dest.IsValid() || dest.IsNil() || length > dest.Cap() {
logger.Debugf("Making new slice for %s", prefix)
this = reflect.MakeSlice(dest.Type(), length, length)
} else {
this = dest
this.SetLen(length)
curLen = this.Len()
}
// determine the key given the array type
if kind == reflect.Struct || isEncodableSliceElemType(dest.Type().Elem()) {
for i := 0; i < length; i++ {
// convert key to name|index format
key := appendToPrefix(prefix, Separator, fmt.Sprintf("%d", i))
// if there's already a struct in the array at this index then we pass that as the current
// value
var cur reflect.Value
if i < curLen {
cur = this.Index(i)
} else {
cur = reflect.Zero(dest.Type().Elem())
}
var result reflect.Value
// #nosec: Errors unhandled.
result, _ = decode(src, cur, key, depth)
if result.IsValid() {
this.Index(i).Set(result)
}
continue
}
return this, nil
}
// convert key to name|index format
key := appendToPrefix(prefix, "", "~")
kval, err := src(key)
if err != nil {
logger.Debugf("No value found in data source for key %q", key)
return this, err
}
// lookup the key and split it
values := strings.Split(kval, Separator)
for i := 0; i < length; i++ {
v := values[i]
t := this.Type().Elem()
k := fromString(reflect.Zero(t), v)
// set the i'th slice item
this.Index(i).Set(k)
}
return this, nil
}
func decodeMap(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error) {
// value representing the run-time data
logger.Debugf("Decoding struct into object: %#v", dest)
// if the value is the zero type, we have to create ourselves
var this reflect.Value
if !dest.IsValid() || dest.IsNil() {
logger.Debugf("Making new maps for %s", prefix)
this = reflect.MakeMap(dest.Type())
} else {
this = dest
}
mapkeys, err := src(prefix)
if mapkeys == "" || err != nil {
logger.Debugf("No value found in data source for maps keys %q", prefix)
return this, err
}
keytype := this.Type().Key()
valtype := this.Type().Elem()
// split the list of map keys and iterate
for _, value := range strings.Split(mapkeys, Separator) {
k := fromString(reflect.Zero(keytype), value)
target := this.MapIndex(k)
if !target.IsValid() {
target = reflect.Zero(valtype)
}
key := appendToPrefix(prefix, Separator, value)
// check to see if the resulting object is not nil
// If it is nil, then there was nothing to decode and the pointer remains nil
// #nosec: Errors unhandled.
result, _ := decode(src, target, key, depth)
if result.IsValid() {
this.SetMapIndex(k, result)
}
}
return this, nil
}
func decodeTime(src DataSource, dest reflect.Value, prefix string, depth recursion) (reflect.Value, error) {
v, err := src(prefix)
if err != nil {
logger.Debugf("No value found in data source for time %q", prefix)
return nilValue, err
}
t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", v)
if err != nil {
logger.Debugf("Failed to convert value %q to time", v)
}
return reflect.ValueOf(t), nil
}
// fromString converts string representation of a basic type to basic type
func fromString(field reflect.Value, value string) reflect.Value {
// handle the zero value
// TODO: can probably handle this more efficiently with a nil pointer return
// as whatever we're populating with primitives will already have their zero
// value.
if value == "" {
return reflect.Zero(field.Type())
}
switch field.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
s, err := strconv.ParseInt(value, 10, 64)
if err != nil {
logger.Errorf("Failed to convert value %#v (%s) to int: %s", value, field.Kind(), err.Error())
return field
}
return reflect.ValueOf(s).Convert(field.Type())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
s, err := strconv.ParseUint(value, 10, 64)
if err != nil {
logger.Errorf("Failed to convert value %#v (%s) to uint: %s", value, field.Kind(), err.Error())
return field
}
return reflect.ValueOf(s).Convert(field.Type())
case reflect.Bool:
s, err := strconv.ParseBool(value)
if err != nil {
logger.Errorf("Failed to convert value %#v (%s) to bool: %s", value, field.Kind(), err.Error())
return field
}
return reflect.ValueOf(s)
case reflect.String:
return reflect.ValueOf(value)
case reflect.Float32, reflect.Float64:
s, err := strconv.ParseFloat(value, 64)
if err != nil {
logger.Errorf("Failed to convert value %#v (%s) to float: %s", value, field.Kind(), err.Error())
return field
}
return reflect.ValueOf(s)
}
logger.Debugf("Invalid Kind: %s (%#v)", field.Kind(), value)
return field
}
// DataSource provides a function that, give a key will return a value
// this is to be used during extraConfig decode to obtain values. Should
// return ErrKeyNotFound if the key does not exist in the data source.
type DataSource func(string) (string, error)
// Decode populates a destination with data from the supplied data source
func Decode(src DataSource, dest interface{}) interface{} {
if src == nil {
logger.Warnf("Decode source is nil - unable to continue")
return dest
}
// #nosec: Errors unhandled.
value, _ := decode(src, reflect.ValueOf(dest), "", Unbounded)
return value.Interface()
}
// DecodeWithPrefix populates a destination with data from the supplied data source, using
// the specified prefix - this allows for decode into substructres.
func DecodeWithPrefix(src DataSource, dest interface{}, prefix string) interface{} {
if src == nil {
logger.Warnf("Decode source is nil - unable to continue")
return dest
}
// #nosec: Errors unhandled.
value, _ := decode(src, reflect.ValueOf(dest), prefix, Unbounded)
return value.Interface()
}
// MapSource takes a key/value map and uses that as the datasource for decoding into
// target structures
func MapSource(src map[string]string) DataSource {
return func(key string) (string, error) {
val, ok := src[key]
if !ok {
return "", ErrKeyNotFound
}
return val, nil
}
}