8cadff2b79
* CNI: bump to 1.0.1 This commit bumps the declared version of CNI in the Kilo manifests to 1.0.1. This is possible with no changes to the configuration lists because our simple configuration is not affected by any of the deprecations, and there was effectively no change between 0.4.0 and 1.0.0, other than the declaration of a stable API. Similarly, this commit also bumps the version of the CNI library and the plugins package. Bumping to CNI 1.0.0 will help ensure that Kilo stays compatible with container runtimes in the future. Signed-off-by: Lucas Servén Marín <lserven@gmail.com> * vendor: revendor Signed-off-by: Lucas Servén Marín <lserven@gmail.com>
376 lines
11 KiB
Go
376 lines
11 KiB
Go
// Copyright 2013 Dario Castañé. All rights reserved.
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Based on src/pkg/reflect/deepequal.go from official
|
|
// golang's stdlib.
|
|
|
|
package mergo
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
func hasMergeableFields(dst reflect.Value) (exported bool) {
|
|
for i, n := 0, dst.NumField(); i < n; i++ {
|
|
field := dst.Type().Field(i)
|
|
if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
|
|
exported = exported || hasMergeableFields(dst.Field(i))
|
|
} else if isExportedComponent(&field) {
|
|
exported = exported || len(field.PkgPath) == 0
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func isExportedComponent(field *reflect.StructField) bool {
|
|
pkgPath := field.PkgPath
|
|
if len(pkgPath) > 0 {
|
|
return false
|
|
}
|
|
c := field.Name[0]
|
|
if 'a' <= c && c <= 'z' || c == '_' {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
type Config struct {
|
|
Overwrite bool
|
|
AppendSlice bool
|
|
TypeCheck bool
|
|
Transformers Transformers
|
|
overwriteWithEmptyValue bool
|
|
overwriteSliceWithEmptyValue bool
|
|
sliceDeepCopy bool
|
|
debug bool
|
|
}
|
|
|
|
type Transformers interface {
|
|
Transformer(reflect.Type) func(dst, src reflect.Value) error
|
|
}
|
|
|
|
// Traverses recursively both values, assigning src's fields values to dst.
|
|
// The map argument tracks comparisons that have already been seen, which allows
|
|
// short circuiting on recursive types.
|
|
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
|
|
overwrite := config.Overwrite
|
|
typeCheck := config.TypeCheck
|
|
overwriteWithEmptySrc := config.overwriteWithEmptyValue
|
|
overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue
|
|
sliceDeepCopy := config.sliceDeepCopy
|
|
|
|
if !src.IsValid() {
|
|
return
|
|
}
|
|
if dst.CanAddr() {
|
|
addr := dst.UnsafeAddr()
|
|
h := 17 * addr
|
|
seen := visited[h]
|
|
typ := dst.Type()
|
|
for p := seen; p != nil; p = p.next {
|
|
if p.ptr == addr && p.typ == typ {
|
|
return nil
|
|
}
|
|
}
|
|
// Remember, remember...
|
|
visited[h] = &visit{addr, typ, seen}
|
|
}
|
|
|
|
if config.Transformers != nil && !isEmptyValue(dst) {
|
|
if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
|
|
err = fn(dst, src)
|
|
return
|
|
}
|
|
}
|
|
|
|
switch dst.Kind() {
|
|
case reflect.Struct:
|
|
if hasMergeableFields(dst) {
|
|
for i, n := 0, dst.NumField(); i < n; i++ {
|
|
if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
if (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) {
|
|
dst.Set(src)
|
|
}
|
|
}
|
|
case reflect.Map:
|
|
if dst.IsNil() && !src.IsNil() {
|
|
dst.Set(reflect.MakeMap(dst.Type()))
|
|
}
|
|
|
|
if src.Kind() != reflect.Map {
|
|
if overwrite {
|
|
dst.Set(src)
|
|
}
|
|
return
|
|
}
|
|
|
|
for _, key := range src.MapKeys() {
|
|
srcElement := src.MapIndex(key)
|
|
if !srcElement.IsValid() {
|
|
continue
|
|
}
|
|
dstElement := dst.MapIndex(key)
|
|
switch srcElement.Kind() {
|
|
case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
|
|
if srcElement.IsNil() {
|
|
if overwrite {
|
|
dst.SetMapIndex(key, srcElement)
|
|
}
|
|
continue
|
|
}
|
|
fallthrough
|
|
default:
|
|
if !srcElement.CanInterface() {
|
|
continue
|
|
}
|
|
switch reflect.TypeOf(srcElement.Interface()).Kind() {
|
|
case reflect.Struct:
|
|
fallthrough
|
|
case reflect.Ptr:
|
|
fallthrough
|
|
case reflect.Map:
|
|
srcMapElm := srcElement
|
|
dstMapElm := dstElement
|
|
if srcMapElm.CanInterface() {
|
|
srcMapElm = reflect.ValueOf(srcMapElm.Interface())
|
|
if dstMapElm.IsValid() {
|
|
dstMapElm = reflect.ValueOf(dstMapElm.Interface())
|
|
}
|
|
}
|
|
if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
case reflect.Slice:
|
|
srcSlice := reflect.ValueOf(srcElement.Interface())
|
|
|
|
var dstSlice reflect.Value
|
|
if !dstElement.IsValid() || dstElement.IsNil() {
|
|
dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
|
|
} else {
|
|
dstSlice = reflect.ValueOf(dstElement.Interface())
|
|
}
|
|
|
|
if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
|
|
if typeCheck && srcSlice.Type() != dstSlice.Type() {
|
|
return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
|
|
}
|
|
dstSlice = srcSlice
|
|
} else if config.AppendSlice {
|
|
if srcSlice.Type() != dstSlice.Type() {
|
|
return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
|
|
}
|
|
dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
|
|
} else if sliceDeepCopy {
|
|
i := 0
|
|
for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ {
|
|
srcElement := srcSlice.Index(i)
|
|
dstElement := dstSlice.Index(i)
|
|
|
|
if srcElement.CanInterface() {
|
|
srcElement = reflect.ValueOf(srcElement.Interface())
|
|
}
|
|
if dstElement.CanInterface() {
|
|
dstElement = reflect.ValueOf(dstElement.Interface())
|
|
}
|
|
|
|
if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
}
|
|
dst.SetMapIndex(key, dstSlice)
|
|
}
|
|
}
|
|
if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
|
|
continue
|
|
}
|
|
|
|
if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) {
|
|
if dst.IsNil() {
|
|
dst.Set(reflect.MakeMap(dst.Type()))
|
|
}
|
|
dst.SetMapIndex(key, srcElement)
|
|
}
|
|
}
|
|
case reflect.Slice:
|
|
if !dst.CanSet() {
|
|
break
|
|
}
|
|
if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy {
|
|
dst.Set(src)
|
|
} else if config.AppendSlice {
|
|
if src.Type() != dst.Type() {
|
|
return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
|
|
}
|
|
dst.Set(reflect.AppendSlice(dst, src))
|
|
} else if sliceDeepCopy {
|
|
for i := 0; i < src.Len() && i < dst.Len(); i++ {
|
|
srcElement := src.Index(i)
|
|
dstElement := dst.Index(i)
|
|
if srcElement.CanInterface() {
|
|
srcElement = reflect.ValueOf(srcElement.Interface())
|
|
}
|
|
if dstElement.CanInterface() {
|
|
dstElement = reflect.ValueOf(dstElement.Interface())
|
|
}
|
|
|
|
if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
case reflect.Ptr:
|
|
fallthrough
|
|
case reflect.Interface:
|
|
if isReflectNil(src) {
|
|
if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) {
|
|
dst.Set(src)
|
|
}
|
|
break
|
|
}
|
|
|
|
if src.Kind() != reflect.Interface {
|
|
if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
|
|
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
|
|
dst.Set(src)
|
|
}
|
|
} else if src.Kind() == reflect.Ptr {
|
|
if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
} else if dst.Elem().Type() == src.Type() {
|
|
if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
return ErrDifferentArgumentsTypes
|
|
}
|
|
break
|
|
}
|
|
|
|
if dst.IsNil() || overwrite {
|
|
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
|
|
dst.Set(src)
|
|
}
|
|
break
|
|
}
|
|
|
|
if dst.Elem().Kind() == src.Elem().Kind() {
|
|
if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
break
|
|
}
|
|
default:
|
|
mustSet := (isEmptyValue(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc)
|
|
if mustSet {
|
|
if dst.CanSet() {
|
|
dst.Set(src)
|
|
} else {
|
|
dst = src
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Merge will fill any empty for value type attributes on the dst struct using corresponding
|
|
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
|
|
// and dst must be a pointer to struct.
|
|
// It won't merge unexported (private) fields and will do recursively any exported field.
|
|
func Merge(dst, src interface{}, opts ...func(*Config)) error {
|
|
return merge(dst, src, opts...)
|
|
}
|
|
|
|
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
|
|
// non-empty src attribute values.
|
|
// Deprecated: use Merge(…) with WithOverride
|
|
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
|
|
return merge(dst, src, append(opts, WithOverride)...)
|
|
}
|
|
|
|
// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
|
|
func WithTransformers(transformers Transformers) func(*Config) {
|
|
return func(config *Config) {
|
|
config.Transformers = transformers
|
|
}
|
|
}
|
|
|
|
// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
|
|
func WithOverride(config *Config) {
|
|
config.Overwrite = true
|
|
}
|
|
|
|
// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
|
|
func WithOverwriteWithEmptyValue(config *Config) {
|
|
config.Overwrite = true
|
|
config.overwriteWithEmptyValue = true
|
|
}
|
|
|
|
// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
|
|
func WithOverrideEmptySlice(config *Config) {
|
|
config.overwriteSliceWithEmptyValue = true
|
|
}
|
|
|
|
// WithAppendSlice will make merge append slices instead of overwriting it.
|
|
func WithAppendSlice(config *Config) {
|
|
config.AppendSlice = true
|
|
}
|
|
|
|
// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
|
|
func WithTypeCheck(config *Config) {
|
|
config.TypeCheck = true
|
|
}
|
|
|
|
// WithSliceDeepCopy will merge slice element one by one with Overwrite flag.
|
|
func WithSliceDeepCopy(config *Config) {
|
|
config.sliceDeepCopy = true
|
|
config.Overwrite = true
|
|
}
|
|
|
|
func merge(dst, src interface{}, opts ...func(*Config)) error {
|
|
if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
|
|
return ErrNonPointerAgument
|
|
}
|
|
var (
|
|
vDst, vSrc reflect.Value
|
|
err error
|
|
)
|
|
|
|
config := &Config{}
|
|
|
|
for _, opt := range opts {
|
|
opt(config)
|
|
}
|
|
|
|
if vDst, vSrc, err = resolveValues(dst, src); err != nil {
|
|
return err
|
|
}
|
|
if vDst.Type() != vSrc.Type() {
|
|
return ErrDifferentArgumentsTypes
|
|
}
|
|
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
|
|
}
|
|
|
|
// IsReflectNil is the reflect value provided nil
|
|
func isReflectNil(v reflect.Value) bool {
|
|
k := v.Kind()
|
|
switch k {
|
|
case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
|
|
// Both interface and slice are nil if first word is 0.
|
|
// Both are always bigger than a word; assume flagIndir.
|
|
return v.IsNil()
|
|
default:
|
|
return false
|
|
}
|
|
}
|