Files
virtual-kubelet/vendor/github.com/vmware/vic/pkg/vsphere/extraconfig/decode.go
Loc Nguyen 513cebe7b7 VMware vSphere Integrated Containers provider (#206)
* Add Virtual Kubelet provider for VIC

Initial virtual kubelet provider for VMware VIC.  This provider currently
handles creating and starting of a pod VM via the VIC portlayer and persona
server.  Image store handling via the VIC persona server.  This provider
currently requires the feature/wolfpack branch of VIC.

* Added pod stop and delete.  Also added node capacity.

Added the ability to stop and delete pod VMs via VIC.  Also retrieve
node capacity information from the VCH.

* Cleanup and readme file

Some file clean up and added a Readme.md markdown file for the VIC
provider.

* Cleaned up errors, added function comments, moved operation code

1. Cleaned up error handling.  Set standard for creating errors.
2. Added method prototype comments for all interface functions.
3. Moved PodCreator, PodStarter, PodStopper, and PodDeleter to a new folder.

* Add mocking code and unit tests for podcache, podcreator, and podstarter

Used the unit test framework used in VIC to handle assertions in the provider's
unit test.  Mocking code generated using OSS project mockery, which is compatible
with the testify assertion framework.

* Vendored packages for the VIC provider

Requires feature/wolfpack branch of VIC and a few specific commit sha of
projects used within VIC.

* Implementation of POD Stopper and Deleter unit tests (#4)

* Updated files for initial PR
2018-06-04 15:41:32 -07:00

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 {
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), DefaultPrefix, 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
}
}