mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 18:43:34 +00:00
vendor files
This commit is contained in:
50
vendor/k8s.io/apimachinery/pkg/conversion/BUILD
generated
vendored
Normal file
50
vendor/k8s.io/apimachinery/pkg/conversion/BUILD
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"converter_test.go",
|
||||
"helper_test.go",
|
||||
],
|
||||
importpath = "k8s.io/apimachinery/pkg/conversion",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/github.com/google/gofuzz:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"converter.go",
|
||||
"deep_equal.go",
|
||||
"doc.go",
|
||||
"helper.go",
|
||||
],
|
||||
importpath = "k8s.io/apimachinery/pkg/conversion",
|
||||
deps = ["//vendor/k8s.io/apimachinery/third_party/forked/golang/reflect:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/conversion/queryparams:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
898
vendor/k8s.io/apimachinery/pkg/conversion/converter.go
generated
vendored
Normal file
898
vendor/k8s.io/apimachinery/pkg/conversion/converter.go
generated
vendored
Normal file
@ -0,0 +1,898 @@
|
||||
/*
|
||||
Copyright 2014 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 conversion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type typePair struct {
|
||||
source reflect.Type
|
||||
dest reflect.Type
|
||||
}
|
||||
|
||||
type typeNamePair struct {
|
||||
fieldType reflect.Type
|
||||
fieldName string
|
||||
}
|
||||
|
||||
// DebugLogger allows you to get debugging messages if necessary.
|
||||
type DebugLogger interface {
|
||||
Logf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
type NameFunc func(t reflect.Type) string
|
||||
|
||||
var DefaultNameFunc = func(t reflect.Type) string { return t.Name() }
|
||||
|
||||
type GenericConversionFunc func(a, b interface{}, scope Scope) (bool, error)
|
||||
|
||||
// Converter knows how to convert one type to another.
|
||||
type Converter struct {
|
||||
// Map from the conversion pair to a function which can
|
||||
// do the conversion.
|
||||
conversionFuncs ConversionFuncs
|
||||
generatedConversionFuncs ConversionFuncs
|
||||
|
||||
// genericConversions are called during normal conversion to offer a "fast-path"
|
||||
// that avoids all reflection. These methods are not called outside of the .Convert()
|
||||
// method.
|
||||
genericConversions []GenericConversionFunc
|
||||
|
||||
// Set of conversions that should be treated as a no-op
|
||||
ignoredConversions map[typePair]struct{}
|
||||
|
||||
// This is a map from a source field type and name, to a list of destination
|
||||
// field type and name.
|
||||
structFieldDests map[typeNamePair][]typeNamePair
|
||||
|
||||
// Allows for the opposite lookup of structFieldDests. So that SourceFromDest
|
||||
// copy flag also works. So this is a map of destination field name, to potential
|
||||
// source field name and type to look for.
|
||||
structFieldSources map[typeNamePair][]typeNamePair
|
||||
|
||||
// Map from an input type to a function which can apply a key name mapping
|
||||
inputFieldMappingFuncs map[reflect.Type]FieldMappingFunc
|
||||
|
||||
// Map from an input type to a set of default conversion flags.
|
||||
inputDefaultFlags map[reflect.Type]FieldMatchingFlags
|
||||
|
||||
// If non-nil, will be called to print helpful debugging info. Quite verbose.
|
||||
Debug DebugLogger
|
||||
|
||||
// nameFunc is called to retrieve the name of a type; this name is used for the
|
||||
// purpose of deciding whether two types match or not (i.e., will we attempt to
|
||||
// do a conversion). The default returns the go type name.
|
||||
nameFunc func(t reflect.Type) string
|
||||
}
|
||||
|
||||
// NewConverter creates a new Converter object.
|
||||
func NewConverter(nameFn NameFunc) *Converter {
|
||||
c := &Converter{
|
||||
conversionFuncs: NewConversionFuncs(),
|
||||
generatedConversionFuncs: NewConversionFuncs(),
|
||||
ignoredConversions: make(map[typePair]struct{}),
|
||||
nameFunc: nameFn,
|
||||
structFieldDests: make(map[typeNamePair][]typeNamePair),
|
||||
structFieldSources: make(map[typeNamePair][]typeNamePair),
|
||||
|
||||
inputFieldMappingFuncs: make(map[reflect.Type]FieldMappingFunc),
|
||||
inputDefaultFlags: make(map[reflect.Type]FieldMatchingFlags),
|
||||
}
|
||||
c.RegisterConversionFunc(Convert_Slice_byte_To_Slice_byte)
|
||||
return c
|
||||
}
|
||||
|
||||
// AddGenericConversionFunc adds a function that accepts the ConversionFunc call pattern
|
||||
// (for two conversion types) to the converter. These functions are checked first during
|
||||
// a normal conversion, but are otherwise not called. Use AddConversionFuncs when registering
|
||||
// typed conversions.
|
||||
func (c *Converter) AddGenericConversionFunc(fn GenericConversionFunc) {
|
||||
c.genericConversions = append(c.genericConversions, fn)
|
||||
}
|
||||
|
||||
// WithConversions returns a Converter that is a copy of c but with the additional
|
||||
// fns merged on top.
|
||||
func (c *Converter) WithConversions(fns ConversionFuncs) *Converter {
|
||||
copied := *c
|
||||
copied.conversionFuncs = c.conversionFuncs.Merge(fns)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// DefaultMeta returns the conversion FieldMappingFunc and meta for a given type.
|
||||
func (c *Converter) DefaultMeta(t reflect.Type) (FieldMatchingFlags, *Meta) {
|
||||
return c.inputDefaultFlags[t], &Meta{
|
||||
KeyNameMapping: c.inputFieldMappingFuncs[t],
|
||||
}
|
||||
}
|
||||
|
||||
// Convert_Slice_byte_To_Slice_byte prevents recursing into every byte
|
||||
func Convert_Slice_byte_To_Slice_byte(in *[]byte, out *[]byte, s Scope) error {
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
return nil
|
||||
}
|
||||
*out = make([]byte, len(*in))
|
||||
copy(*out, *in)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scope is passed to conversion funcs to allow them to continue an ongoing conversion.
|
||||
// If multiple converters exist in the system, Scope will allow you to use the correct one
|
||||
// from a conversion function--that is, the one your conversion function was called by.
|
||||
type Scope interface {
|
||||
// Call Convert to convert sub-objects. Note that if you call it with your own exact
|
||||
// parameters, you'll run out of stack space before anything useful happens.
|
||||
Convert(src, dest interface{}, flags FieldMatchingFlags) error
|
||||
|
||||
// DefaultConvert performs the default conversion, without calling a conversion func
|
||||
// on the current stack frame. This makes it safe to call from a conversion func.
|
||||
DefaultConvert(src, dest interface{}, flags FieldMatchingFlags) error
|
||||
|
||||
// SrcTags and DestTags contain the struct tags that src and dest had, respectively.
|
||||
// If the enclosing object was not a struct, then these will contain no tags, of course.
|
||||
SrcTag() reflect.StructTag
|
||||
DestTag() reflect.StructTag
|
||||
|
||||
// Flags returns the flags with which the conversion was started.
|
||||
Flags() FieldMatchingFlags
|
||||
|
||||
// Meta returns any information originally passed to Convert.
|
||||
Meta() *Meta
|
||||
}
|
||||
|
||||
// FieldMappingFunc can convert an input field value into different values, depending on
|
||||
// the value of the source or destination struct tags.
|
||||
type FieldMappingFunc func(key string, sourceTag, destTag reflect.StructTag) (source string, dest string)
|
||||
|
||||
func NewConversionFuncs() ConversionFuncs {
|
||||
return ConversionFuncs{fns: make(map[typePair]reflect.Value)}
|
||||
}
|
||||
|
||||
type ConversionFuncs struct {
|
||||
fns map[typePair]reflect.Value
|
||||
}
|
||||
|
||||
// Add adds the provided conversion functions to the lookup table - they must have the signature
|
||||
// `func(type1, type2, Scope) error`. Functions are added in the order passed and will override
|
||||
// previously registered pairs.
|
||||
func (c ConversionFuncs) Add(fns ...interface{}) error {
|
||||
for _, fn := range fns {
|
||||
fv := reflect.ValueOf(fn)
|
||||
ft := fv.Type()
|
||||
if err := verifyConversionFunctionSignature(ft); err != nil {
|
||||
return err
|
||||
}
|
||||
c.fns[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merge returns a new ConversionFuncs that contains all conversions from
|
||||
// both other and c, with other conversions taking precedence.
|
||||
func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs {
|
||||
merged := NewConversionFuncs()
|
||||
for k, v := range c.fns {
|
||||
merged.fns[k] = v
|
||||
}
|
||||
for k, v := range other.fns {
|
||||
merged.fns[k] = v
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// Meta is supplied by Scheme, when it calls Convert.
|
||||
type Meta struct {
|
||||
// KeyNameMapping is an optional function which may map the listed key (field name)
|
||||
// into a source and destination value.
|
||||
KeyNameMapping FieldMappingFunc
|
||||
// Context is an optional field that callers may use to pass info to conversion functions.
|
||||
Context interface{}
|
||||
}
|
||||
|
||||
// scope contains information about an ongoing conversion.
|
||||
type scope struct {
|
||||
converter *Converter
|
||||
meta *Meta
|
||||
flags FieldMatchingFlags
|
||||
|
||||
// srcStack & destStack are separate because they may not have a 1:1
|
||||
// relationship.
|
||||
srcStack scopeStack
|
||||
destStack scopeStack
|
||||
}
|
||||
|
||||
type scopeStackElem struct {
|
||||
tag reflect.StructTag
|
||||
value reflect.Value
|
||||
key string
|
||||
}
|
||||
|
||||
type scopeStack []scopeStackElem
|
||||
|
||||
func (s *scopeStack) pop() {
|
||||
n := len(*s)
|
||||
*s = (*s)[:n-1]
|
||||
}
|
||||
|
||||
func (s *scopeStack) push(e scopeStackElem) {
|
||||
*s = append(*s, e)
|
||||
}
|
||||
|
||||
func (s *scopeStack) top() *scopeStackElem {
|
||||
return &(*s)[len(*s)-1]
|
||||
}
|
||||
|
||||
func (s scopeStack) describe() string {
|
||||
desc := ""
|
||||
if len(s) > 1 {
|
||||
desc = "(" + s[1].value.Type().String() + ")"
|
||||
}
|
||||
for i, v := range s {
|
||||
if i < 2 {
|
||||
// First layer on stack is not real; second is handled specially above.
|
||||
continue
|
||||
}
|
||||
if v.key == "" {
|
||||
desc += fmt.Sprintf(".%v", v.value.Type())
|
||||
} else {
|
||||
desc += fmt.Sprintf(".%v", v.key)
|
||||
}
|
||||
}
|
||||
return desc
|
||||
}
|
||||
|
||||
// Formats src & dest as indices for printing.
|
||||
func (s *scope) setIndices(src, dest int) {
|
||||
s.srcStack.top().key = fmt.Sprintf("[%v]", src)
|
||||
s.destStack.top().key = fmt.Sprintf("[%v]", dest)
|
||||
}
|
||||
|
||||
// Formats src & dest as map keys for printing.
|
||||
func (s *scope) setKeys(src, dest interface{}) {
|
||||
s.srcStack.top().key = fmt.Sprintf(`["%v"]`, src)
|
||||
s.destStack.top().key = fmt.Sprintf(`["%v"]`, dest)
|
||||
}
|
||||
|
||||
// Convert continues a conversion.
|
||||
func (s *scope) Convert(src, dest interface{}, flags FieldMatchingFlags) error {
|
||||
return s.converter.Convert(src, dest, flags, s.meta)
|
||||
}
|
||||
|
||||
// DefaultConvert continues a conversion, performing a default conversion (no conversion func)
|
||||
// for the current stack frame.
|
||||
func (s *scope) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags) error {
|
||||
return s.converter.DefaultConvert(src, dest, flags, s.meta)
|
||||
}
|
||||
|
||||
// SrcTag returns the tag of the struct containing the current source item, if any.
|
||||
func (s *scope) SrcTag() reflect.StructTag {
|
||||
return s.srcStack.top().tag
|
||||
}
|
||||
|
||||
// DestTag returns the tag of the struct containing the current dest item, if any.
|
||||
func (s *scope) DestTag() reflect.StructTag {
|
||||
return s.destStack.top().tag
|
||||
}
|
||||
|
||||
// Flags returns the flags with which the current conversion was started.
|
||||
func (s *scope) Flags() FieldMatchingFlags {
|
||||
return s.flags
|
||||
}
|
||||
|
||||
// Meta returns the meta object that was originally passed to Convert.
|
||||
func (s *scope) Meta() *Meta {
|
||||
return s.meta
|
||||
}
|
||||
|
||||
// describe prints the path to get to the current (source, dest) values.
|
||||
func (s *scope) describe() (src, dest string) {
|
||||
return s.srcStack.describe(), s.destStack.describe()
|
||||
}
|
||||
|
||||
// error makes an error that includes information about where we were in the objects
|
||||
// we were asked to convert.
|
||||
func (s *scope) errorf(message string, args ...interface{}) error {
|
||||
srcPath, destPath := s.describe()
|
||||
where := fmt.Sprintf("converting %v to %v: ", srcPath, destPath)
|
||||
return fmt.Errorf(where+message, args...)
|
||||
}
|
||||
|
||||
// Verifies whether a conversion function has a correct signature.
|
||||
func verifyConversionFunctionSignature(ft reflect.Type) error {
|
||||
if ft.Kind() != reflect.Func {
|
||||
return fmt.Errorf("expected func, got: %v", ft)
|
||||
}
|
||||
if ft.NumIn() != 3 {
|
||||
return fmt.Errorf("expected three 'in' params, got: %v", ft)
|
||||
}
|
||||
if ft.NumOut() != 1 {
|
||||
return fmt.Errorf("expected one 'out' param, got: %v", ft)
|
||||
}
|
||||
if ft.In(0).Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("expected pointer arg for 'in' param 0, got: %v", ft)
|
||||
}
|
||||
if ft.In(1).Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("expected pointer arg for 'in' param 1, got: %v", ft)
|
||||
}
|
||||
scopeType := Scope(nil)
|
||||
if e, a := reflect.TypeOf(&scopeType).Elem(), ft.In(2); e != a {
|
||||
return fmt.Errorf("expected '%v' arg for 'in' param 2, got '%v' (%v)", e, a, ft)
|
||||
}
|
||||
var forErrorType error
|
||||
// This convolution is necessary, otherwise TypeOf picks up on the fact
|
||||
// that forErrorType is nil.
|
||||
errorType := reflect.TypeOf(&forErrorType).Elem()
|
||||
if ft.Out(0) != errorType {
|
||||
return fmt.Errorf("expected error return, got: %v", ft)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterConversionFunc registers a conversion func with the
|
||||
// Converter. conversionFunc must take three parameters: a pointer to the input
|
||||
// type, a pointer to the output type, and a conversion.Scope (which should be
|
||||
// used if recursive conversion calls are desired). It must return an error.
|
||||
//
|
||||
// Example:
|
||||
// c.RegisterConversionFunc(
|
||||
// func(in *Pod, out *v1.Pod, s Scope) error {
|
||||
// // conversion logic...
|
||||
// return nil
|
||||
// })
|
||||
func (c *Converter) RegisterConversionFunc(conversionFunc interface{}) error {
|
||||
return c.conversionFuncs.Add(conversionFunc)
|
||||
}
|
||||
|
||||
// Similar to RegisterConversionFunc, but registers conversion function that were
|
||||
// automatically generated.
|
||||
func (c *Converter) RegisterGeneratedConversionFunc(conversionFunc interface{}) error {
|
||||
return c.generatedConversionFuncs.Add(conversionFunc)
|
||||
}
|
||||
|
||||
// RegisterIgnoredConversion registers a "no-op" for conversion, where any requested
|
||||
// conversion between from and to is ignored.
|
||||
func (c *Converter) RegisterIgnoredConversion(from, to interface{}) error {
|
||||
typeFrom := reflect.TypeOf(from)
|
||||
typeTo := reflect.TypeOf(to)
|
||||
if reflect.TypeOf(from).Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("expected pointer arg for 'from' param 0, got: %v", typeFrom)
|
||||
}
|
||||
if typeTo.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("expected pointer arg for 'to' param 1, got: %v", typeTo)
|
||||
}
|
||||
c.ignoredConversions[typePair{typeFrom.Elem(), typeTo.Elem()}] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsConversionIgnored returns true if the specified objects should be dropped during
|
||||
// conversion.
|
||||
func (c *Converter) IsConversionIgnored(inType, outType reflect.Type) bool {
|
||||
_, found := c.ignoredConversions[typePair{inType, outType}]
|
||||
return found
|
||||
}
|
||||
|
||||
func (c *Converter) HasConversionFunc(inType, outType reflect.Type) bool {
|
||||
_, found := c.conversionFuncs.fns[typePair{inType, outType}]
|
||||
return found
|
||||
}
|
||||
|
||||
func (c *Converter) ConversionFuncValue(inType, outType reflect.Type) (reflect.Value, bool) {
|
||||
value, found := c.conversionFuncs.fns[typePair{inType, outType}]
|
||||
return value, found
|
||||
}
|
||||
|
||||
// SetStructFieldCopy registers a correspondence. Whenever a struct field is encountered
|
||||
// which has a type and name matching srcFieldType and srcFieldName, it wil be copied
|
||||
// into the field in the destination struct matching destFieldType & Name, if such a
|
||||
// field exists.
|
||||
// May be called multiple times, even for the same source field & type--all applicable
|
||||
// copies will be performed.
|
||||
func (c *Converter) SetStructFieldCopy(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error {
|
||||
st := reflect.TypeOf(srcFieldType)
|
||||
dt := reflect.TypeOf(destFieldType)
|
||||
srcKey := typeNamePair{st, srcFieldName}
|
||||
destKey := typeNamePair{dt, destFieldName}
|
||||
c.structFieldDests[srcKey] = append(c.structFieldDests[srcKey], destKey)
|
||||
c.structFieldSources[destKey] = append(c.structFieldSources[destKey], srcKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterInputDefaults registers a field name mapping function, used when converting
|
||||
// from maps to structs. Inputs to the conversion methods are checked for this type and a mapping
|
||||
// applied automatically if the input matches in. A set of default flags for the input conversion
|
||||
// may also be provided, which will be used when no explicit flags are requested.
|
||||
func (c *Converter) RegisterInputDefaults(in interface{}, fn FieldMappingFunc, defaultFlags FieldMatchingFlags) error {
|
||||
fv := reflect.ValueOf(in)
|
||||
ft := fv.Type()
|
||||
if ft.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("expected pointer 'in' argument, got: %v", ft)
|
||||
}
|
||||
c.inputFieldMappingFuncs[ft] = fn
|
||||
c.inputDefaultFlags[ft] = defaultFlags
|
||||
return nil
|
||||
}
|
||||
|
||||
// FieldMatchingFlags contains a list of ways in which struct fields could be
|
||||
// copied. These constants may be | combined.
|
||||
type FieldMatchingFlags int
|
||||
|
||||
const (
|
||||
// Loop through destination fields, search for matching source
|
||||
// field to copy it from. Source fields with no corresponding
|
||||
// destination field will be ignored. If SourceToDest is
|
||||
// specified, this flag is ignored. If neither is specified,
|
||||
// or no flags are passed, this flag is the default.
|
||||
DestFromSource FieldMatchingFlags = 0
|
||||
// Loop through source fields, search for matching dest field
|
||||
// to copy it into. Destination fields with no corresponding
|
||||
// source field will be ignored.
|
||||
SourceToDest FieldMatchingFlags = 1 << iota
|
||||
// Don't treat it as an error if the corresponding source or
|
||||
// dest field can't be found.
|
||||
IgnoreMissingFields
|
||||
// Don't require type names to match.
|
||||
AllowDifferentFieldTypeNames
|
||||
)
|
||||
|
||||
// IsSet returns true if the given flag or combination of flags is set.
|
||||
func (f FieldMatchingFlags) IsSet(flag FieldMatchingFlags) bool {
|
||||
if flag == DestFromSource {
|
||||
// The bit logic doesn't work on the default value.
|
||||
return f&SourceToDest != SourceToDest
|
||||
}
|
||||
return f&flag == flag
|
||||
}
|
||||
|
||||
// Convert will translate src to dest if it knows how. Both must be pointers.
|
||||
// If no conversion func is registered and the default copying mechanism
|
||||
// doesn't work on this type pair, an error will be returned.
|
||||
// Read the comments on the various FieldMatchingFlags constants to understand
|
||||
// what the 'flags' parameter does.
|
||||
// 'meta' is given to allow you to pass information to conversion functions,
|
||||
// it is not used by Convert() other than storing it in the scope.
|
||||
// Not safe for objects with cyclic references!
|
||||
func (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {
|
||||
if len(c.genericConversions) > 0 {
|
||||
// TODO: avoid scope allocation
|
||||
s := &scope{converter: c, flags: flags, meta: meta}
|
||||
for _, fn := range c.genericConversions {
|
||||
if ok, err := fn(src, dest, s); ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.doConversion(src, dest, flags, meta, c.convert)
|
||||
}
|
||||
|
||||
// DefaultConvert will translate src to dest if it knows how. Both must be pointers.
|
||||
// No conversion func is used. If the default copying mechanism
|
||||
// doesn't work on this type pair, an error will be returned.
|
||||
// Read the comments on the various FieldMatchingFlags constants to understand
|
||||
// what the 'flags' parameter does.
|
||||
// 'meta' is given to allow you to pass information to conversion functions,
|
||||
// it is not used by DefaultConvert() other than storing it in the scope.
|
||||
// Not safe for objects with cyclic references!
|
||||
func (c *Converter) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {
|
||||
return c.doConversion(src, dest, flags, meta, c.defaultConvert)
|
||||
}
|
||||
|
||||
type conversionFunc func(sv, dv reflect.Value, scope *scope) error
|
||||
|
||||
func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags, meta *Meta, f conversionFunc) error {
|
||||
dv, err := EnforcePtr(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !dv.CanAddr() && !dv.CanSet() {
|
||||
return fmt.Errorf("can't write to dest")
|
||||
}
|
||||
sv, err := EnforcePtr(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s := &scope{
|
||||
converter: c,
|
||||
flags: flags,
|
||||
meta: meta,
|
||||
}
|
||||
// Leave something on the stack, so that calls to struct tag getters never fail.
|
||||
s.srcStack.push(scopeStackElem{})
|
||||
s.destStack.push(scopeStackElem{})
|
||||
return f(sv, dv, s)
|
||||
}
|
||||
|
||||
// callCustom calls 'custom' with sv & dv. custom must be a conversion function.
|
||||
func (c *Converter) callCustom(sv, dv, custom reflect.Value, scope *scope) error {
|
||||
if !sv.CanAddr() {
|
||||
sv2 := reflect.New(sv.Type())
|
||||
sv2.Elem().Set(sv)
|
||||
sv = sv2
|
||||
} else {
|
||||
sv = sv.Addr()
|
||||
}
|
||||
if !dv.CanAddr() {
|
||||
if !dv.CanSet() {
|
||||
return scope.errorf("can't addr or set dest.")
|
||||
}
|
||||
dvOrig := dv
|
||||
dv := reflect.New(dvOrig.Type())
|
||||
defer func() { dvOrig.Set(dv) }()
|
||||
} else {
|
||||
dv = dv.Addr()
|
||||
}
|
||||
args := []reflect.Value{sv, dv, reflect.ValueOf(scope)}
|
||||
ret := custom.Call(args)[0].Interface()
|
||||
// This convolution is necessary because nil interfaces won't convert
|
||||
// to errors.
|
||||
if ret == nil {
|
||||
return nil
|
||||
}
|
||||
return ret.(error)
|
||||
}
|
||||
|
||||
// convert recursively copies sv into dv, calling an appropriate conversion function if
|
||||
// one is registered.
|
||||
func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error {
|
||||
dt, st := dv.Type(), sv.Type()
|
||||
pair := typePair{st, dt}
|
||||
|
||||
// ignore conversions of this type
|
||||
if _, ok := c.ignoredConversions[pair]; ok {
|
||||
if c.Debug != nil {
|
||||
c.Debug.Logf("Ignoring conversion of '%v' to '%v'", st, dt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert sv to dv.
|
||||
if fv, ok := c.conversionFuncs.fns[pair]; ok {
|
||||
if c.Debug != nil {
|
||||
c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt)
|
||||
}
|
||||
return c.callCustom(sv, dv, fv, scope)
|
||||
}
|
||||
if fv, ok := c.generatedConversionFuncs.fns[pair]; ok {
|
||||
if c.Debug != nil {
|
||||
c.Debug.Logf("Calling generated conversion of '%v' to '%v'", st, dt)
|
||||
}
|
||||
return c.callCustom(sv, dv, fv, scope)
|
||||
}
|
||||
|
||||
return c.defaultConvert(sv, dv, scope)
|
||||
}
|
||||
|
||||
// defaultConvert recursively copies sv into dv. no conversion function is called
|
||||
// for the current stack frame (but conversion functions may be called for nested objects)
|
||||
func (c *Converter) defaultConvert(sv, dv reflect.Value, scope *scope) error {
|
||||
dt, st := dv.Type(), sv.Type()
|
||||
|
||||
if !dv.CanSet() {
|
||||
return scope.errorf("Cannot set dest. (Tried to deep copy something with unexported fields?)")
|
||||
}
|
||||
|
||||
if !scope.flags.IsSet(AllowDifferentFieldTypeNames) && c.nameFunc(dt) != c.nameFunc(st) {
|
||||
return scope.errorf(
|
||||
"type names don't match (%v, %v), and no conversion 'func (%v, %v) error' registered.",
|
||||
c.nameFunc(st), c.nameFunc(dt), st, dt)
|
||||
}
|
||||
|
||||
switch st.Kind() {
|
||||
case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
|
||||
// Don't copy these via assignment/conversion!
|
||||
default:
|
||||
// This should handle all simple types.
|
||||
if st.AssignableTo(dt) {
|
||||
dv.Set(sv)
|
||||
return nil
|
||||
}
|
||||
if st.ConvertibleTo(dt) {
|
||||
dv.Set(sv.Convert(dt))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if c.Debug != nil {
|
||||
c.Debug.Logf("Trying to convert '%v' to '%v'", st, dt)
|
||||
}
|
||||
|
||||
scope.srcStack.push(scopeStackElem{value: sv})
|
||||
scope.destStack.push(scopeStackElem{value: dv})
|
||||
defer scope.srcStack.pop()
|
||||
defer scope.destStack.pop()
|
||||
|
||||
switch dv.Kind() {
|
||||
case reflect.Struct:
|
||||
return c.convertKV(toKVValue(sv), toKVValue(dv), scope)
|
||||
case reflect.Slice:
|
||||
if sv.IsNil() {
|
||||
// Don't make a zero-length slice.
|
||||
dv.Set(reflect.Zero(dt))
|
||||
return nil
|
||||
}
|
||||
dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
|
||||
for i := 0; i < sv.Len(); i++ {
|
||||
scope.setIndices(i, i)
|
||||
if err := c.convert(sv.Index(i), dv.Index(i), scope); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if sv.IsNil() {
|
||||
// Don't copy a nil ptr!
|
||||
dv.Set(reflect.Zero(dt))
|
||||
return nil
|
||||
}
|
||||
dv.Set(reflect.New(dt.Elem()))
|
||||
switch st.Kind() {
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
return c.convert(sv.Elem(), dv.Elem(), scope)
|
||||
default:
|
||||
return c.convert(sv, dv.Elem(), scope)
|
||||
}
|
||||
case reflect.Map:
|
||||
if sv.IsNil() {
|
||||
// Don't copy a nil ptr!
|
||||
dv.Set(reflect.Zero(dt))
|
||||
return nil
|
||||
}
|
||||
dv.Set(reflect.MakeMap(dt))
|
||||
for _, sk := range sv.MapKeys() {
|
||||
dk := reflect.New(dt.Key()).Elem()
|
||||
if err := c.convert(sk, dk, scope); err != nil {
|
||||
return err
|
||||
}
|
||||
dkv := reflect.New(dt.Elem()).Elem()
|
||||
scope.setKeys(sk.Interface(), dk.Interface())
|
||||
// TODO: sv.MapIndex(sk) may return a value with CanAddr() == false,
|
||||
// because a map[string]struct{} does not allow a pointer reference.
|
||||
// Calling a custom conversion function defined for the map value
|
||||
// will panic. Example is PodInfo map[string]ContainerStatus.
|
||||
if err := c.convert(sv.MapIndex(sk), dkv, scope); err != nil {
|
||||
return err
|
||||
}
|
||||
dv.SetMapIndex(dk, dkv)
|
||||
}
|
||||
case reflect.Interface:
|
||||
if sv.IsNil() {
|
||||
// Don't copy a nil interface!
|
||||
dv.Set(reflect.Zero(dt))
|
||||
return nil
|
||||
}
|
||||
tmpdv := reflect.New(sv.Elem().Type()).Elem()
|
||||
if err := c.convert(sv.Elem(), tmpdv, scope); err != nil {
|
||||
return err
|
||||
}
|
||||
dv.Set(reflect.ValueOf(tmpdv.Interface()))
|
||||
return nil
|
||||
default:
|
||||
return scope.errorf("couldn't copy '%v' into '%v'; didn't understand types", st, dt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var stringType = reflect.TypeOf("")
|
||||
|
||||
func toKVValue(v reflect.Value) kvValue {
|
||||
switch v.Kind() {
|
||||
case reflect.Struct:
|
||||
return structAdaptor(v)
|
||||
case reflect.Map:
|
||||
if v.Type().Key().AssignableTo(stringType) {
|
||||
return stringMapAdaptor(v)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// kvValue lets us write the same conversion logic to work with both maps
|
||||
// and structs. Only maps with string keys make sense for this.
|
||||
type kvValue interface {
|
||||
// returns all keys, as a []string.
|
||||
keys() []string
|
||||
// Will just return "" for maps.
|
||||
tagOf(key string) reflect.StructTag
|
||||
// Will return the zero Value if the key doesn't exist.
|
||||
value(key string) reflect.Value
|
||||
// Maps require explicit setting-- will do nothing for structs.
|
||||
// Returns false on failure.
|
||||
confirmSet(key string, v reflect.Value) bool
|
||||
}
|
||||
|
||||
type stringMapAdaptor reflect.Value
|
||||
|
||||
func (a stringMapAdaptor) len() int {
|
||||
return reflect.Value(a).Len()
|
||||
}
|
||||
|
||||
func (a stringMapAdaptor) keys() []string {
|
||||
v := reflect.Value(a)
|
||||
keys := make([]string, v.Len())
|
||||
for i, v := range v.MapKeys() {
|
||||
if v.IsNil() {
|
||||
continue
|
||||
}
|
||||
switch t := v.Interface().(type) {
|
||||
case string:
|
||||
keys[i] = t
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (a stringMapAdaptor) tagOf(key string) reflect.StructTag {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a stringMapAdaptor) value(key string) reflect.Value {
|
||||
return reflect.Value(a).MapIndex(reflect.ValueOf(key))
|
||||
}
|
||||
|
||||
func (a stringMapAdaptor) confirmSet(key string, v reflect.Value) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type structAdaptor reflect.Value
|
||||
|
||||
func (a structAdaptor) len() int {
|
||||
v := reflect.Value(a)
|
||||
return v.Type().NumField()
|
||||
}
|
||||
|
||||
func (a structAdaptor) keys() []string {
|
||||
v := reflect.Value(a)
|
||||
t := v.Type()
|
||||
keys := make([]string, t.NumField())
|
||||
for i := range keys {
|
||||
keys[i] = t.Field(i).Name
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (a structAdaptor) tagOf(key string) reflect.StructTag {
|
||||
v := reflect.Value(a)
|
||||
field, ok := v.Type().FieldByName(key)
|
||||
if ok {
|
||||
return field.Tag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a structAdaptor) value(key string) reflect.Value {
|
||||
v := reflect.Value(a)
|
||||
return v.FieldByName(key)
|
||||
}
|
||||
|
||||
func (a structAdaptor) confirmSet(key string, v reflect.Value) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// convertKV can convert things that consist of key/value pairs, like structs
|
||||
// and some maps.
|
||||
func (c *Converter) convertKV(skv, dkv kvValue, scope *scope) error {
|
||||
if skv == nil || dkv == nil {
|
||||
// TODO: add keys to stack to support really understandable error messages.
|
||||
return fmt.Errorf("Unable to convert %#v to %#v", skv, dkv)
|
||||
}
|
||||
|
||||
lister := dkv
|
||||
if scope.flags.IsSet(SourceToDest) {
|
||||
lister = skv
|
||||
}
|
||||
|
||||
var mapping FieldMappingFunc
|
||||
if scope.meta != nil && scope.meta.KeyNameMapping != nil {
|
||||
mapping = scope.meta.KeyNameMapping
|
||||
}
|
||||
|
||||
for _, key := range lister.keys() {
|
||||
if found, err := c.checkField(key, skv, dkv, scope); found {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
stag := skv.tagOf(key)
|
||||
dtag := dkv.tagOf(key)
|
||||
skey := key
|
||||
dkey := key
|
||||
if mapping != nil {
|
||||
skey, dkey = scope.meta.KeyNameMapping(key, stag, dtag)
|
||||
}
|
||||
|
||||
df := dkv.value(dkey)
|
||||
sf := skv.value(skey)
|
||||
if !df.IsValid() || !sf.IsValid() {
|
||||
switch {
|
||||
case scope.flags.IsSet(IgnoreMissingFields):
|
||||
// No error.
|
||||
case scope.flags.IsSet(SourceToDest):
|
||||
return scope.errorf("%v not present in dest", dkey)
|
||||
default:
|
||||
return scope.errorf("%v not present in src", skey)
|
||||
}
|
||||
continue
|
||||
}
|
||||
scope.srcStack.top().key = skey
|
||||
scope.srcStack.top().tag = stag
|
||||
scope.destStack.top().key = dkey
|
||||
scope.destStack.top().tag = dtag
|
||||
if err := c.convert(sf, df, scope); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkField returns true if the field name matches any of the struct
|
||||
// field copying rules. The error should be ignored if it returns false.
|
||||
func (c *Converter) checkField(fieldName string, skv, dkv kvValue, scope *scope) (bool, error) {
|
||||
replacementMade := false
|
||||
if scope.flags.IsSet(DestFromSource) {
|
||||
df := dkv.value(fieldName)
|
||||
if !df.IsValid() {
|
||||
return false, nil
|
||||
}
|
||||
destKey := typeNamePair{df.Type(), fieldName}
|
||||
// Check each of the potential source (type, name) pairs to see if they're
|
||||
// present in sv.
|
||||
for _, potentialSourceKey := range c.structFieldSources[destKey] {
|
||||
sf := skv.value(potentialSourceKey.fieldName)
|
||||
if !sf.IsValid() {
|
||||
continue
|
||||
}
|
||||
if sf.Type() == potentialSourceKey.fieldType {
|
||||
// Both the source's name and type matched, so copy.
|
||||
scope.srcStack.top().key = potentialSourceKey.fieldName
|
||||
scope.destStack.top().key = fieldName
|
||||
if err := c.convert(sf, df, scope); err != nil {
|
||||
return true, err
|
||||
}
|
||||
dkv.confirmSet(fieldName, df)
|
||||
replacementMade = true
|
||||
}
|
||||
}
|
||||
return replacementMade, nil
|
||||
}
|
||||
|
||||
sf := skv.value(fieldName)
|
||||
if !sf.IsValid() {
|
||||
return false, nil
|
||||
}
|
||||
srcKey := typeNamePair{sf.Type(), fieldName}
|
||||
// Check each of the potential dest (type, name) pairs to see if they're
|
||||
// present in dv.
|
||||
for _, potentialDestKey := range c.structFieldDests[srcKey] {
|
||||
df := dkv.value(potentialDestKey.fieldName)
|
||||
if !df.IsValid() {
|
||||
continue
|
||||
}
|
||||
if df.Type() == potentialDestKey.fieldType {
|
||||
// Both the dest's name and type matched, so copy.
|
||||
scope.srcStack.top().key = fieldName
|
||||
scope.destStack.top().key = potentialDestKey.fieldName
|
||||
if err := c.convert(sf, df, scope); err != nil {
|
||||
return true, err
|
||||
}
|
||||
dkv.confirmSet(potentialDestKey.fieldName, df)
|
||||
replacementMade = true
|
||||
}
|
||||
}
|
||||
return replacementMade, nil
|
||||
}
|
826
vendor/k8s.io/apimachinery/pkg/conversion/converter_test.go
generated
vendored
Normal file
826
vendor/k8s.io/apimachinery/pkg/conversion/converter_test.go
generated
vendored
Normal file
@ -0,0 +1,826 @@
|
||||
/*
|
||||
Copyright 2014 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 conversion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gofuzz"
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
)
|
||||
|
||||
var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
|
||||
|
||||
// Test a weird version/kind embedding format.
|
||||
type MyWeirdCustomEmbeddedVersionKindField struct {
|
||||
ID string `json:"ID,omitempty"`
|
||||
APIVersion string `json:"myVersionKey,omitempty"`
|
||||
ObjectKind string `json:"myKindKey,omitempty"`
|
||||
Z string `json:"Z,omitempty"`
|
||||
Y uint64 `json:"Y,omitempty"`
|
||||
}
|
||||
|
||||
type TestType1 struct {
|
||||
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
C int8 `json:"C,omitempty"`
|
||||
D int16 `json:"D,omitempty"`
|
||||
E int32 `json:"E,omitempty"`
|
||||
F int64 `json:"F,omitempty"`
|
||||
G uint `json:"G,omitempty"`
|
||||
H uint8 `json:"H,omitempty"`
|
||||
I uint16 `json:"I,omitempty"`
|
||||
J uint32 `json:"J,omitempty"`
|
||||
K uint64 `json:"K,omitempty"`
|
||||
L bool `json:"L,omitempty"`
|
||||
M map[string]int `json:"M,omitempty"`
|
||||
N map[string]TestType2 `json:"N,omitempty"`
|
||||
O *TestType2 `json:"O,omitempty"`
|
||||
P []TestType2 `json:"Q,omitempty"`
|
||||
}
|
||||
|
||||
type TestType2 struct {
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
}
|
||||
|
||||
type ExternalTestType2 struct {
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
}
|
||||
type ExternalTestType1 struct {
|
||||
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
|
||||
A string `json:"A,omitempty"`
|
||||
B int `json:"B,omitempty"`
|
||||
C int8 `json:"C,omitempty"`
|
||||
D int16 `json:"D,omitempty"`
|
||||
E int32 `json:"E,omitempty"`
|
||||
F int64 `json:"F,omitempty"`
|
||||
G uint `json:"G,omitempty"`
|
||||
H uint8 `json:"H,omitempty"`
|
||||
I uint16 `json:"I,omitempty"`
|
||||
J uint32 `json:"J,omitempty"`
|
||||
K uint64 `json:"K,omitempty"`
|
||||
L bool `json:"L,omitempty"`
|
||||
M map[string]int `json:"M,omitempty"`
|
||||
N map[string]ExternalTestType2 `json:"N,omitempty"`
|
||||
O *ExternalTestType2 `json:"O,omitempty"`
|
||||
P []ExternalTestType2 `json:"Q,omitempty"`
|
||||
}
|
||||
|
||||
func testLogger(t *testing.T) DebugLogger {
|
||||
// We don't set logger to eliminate rubbish logs in tests.
|
||||
// If you want to switch it, simply switch it to: "return t"
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestConverter_byteSlice(t *testing.T) {
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
src := []byte{1, 2, 3}
|
||||
dest := []byte{}
|
||||
err := c.Convert(&src, &dest, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error")
|
||||
}
|
||||
if e, a := src, dest; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_MismatchedTypes(t *testing.T) {
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
|
||||
err := c.RegisterConversionFunc(
|
||||
func(in *[]string, out *int, s Scope) error {
|
||||
if str, err := strconv.Atoi((*in)[0]); err != nil {
|
||||
return err
|
||||
} else {
|
||||
*out = str
|
||||
return nil
|
||||
}
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
src := []string{"5"}
|
||||
var dest *int
|
||||
err = c.Convert(&src, &dest, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if e, a := 5, *dest; e != a {
|
||||
t.Errorf("expected %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_DefaultConvert(t *testing.T) {
|
||||
type A struct {
|
||||
Foo string
|
||||
Baz int
|
||||
}
|
||||
type B struct {
|
||||
Bar string
|
||||
Baz int
|
||||
}
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
c.nameFunc = func(t reflect.Type) string { return "MyType" }
|
||||
|
||||
// Ensure conversion funcs can call DefaultConvert to get default behavior,
|
||||
// then fixup remaining fields manually
|
||||
err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
if err := s.DefaultConvert(in, out, IgnoreMissingFields); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Bar = in.Foo
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
x := A{"hello, intrepid test reader!", 3}
|
||||
y := B{}
|
||||
|
||||
err = c.Convert(&x, &y, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if e, a := x.Foo, y.Bar; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := x.Baz, y.Baz; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_DeepCopy(t *testing.T) {
|
||||
type A struct {
|
||||
Foo *string
|
||||
Bar []string
|
||||
Baz interface{}
|
||||
Qux map[string]string
|
||||
}
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
|
||||
foo, baz := "foo", "baz"
|
||||
x := A{
|
||||
Foo: &foo,
|
||||
Bar: []string{"bar"},
|
||||
Baz: &baz,
|
||||
Qux: map[string]string{"qux": "qux"},
|
||||
}
|
||||
y := A{}
|
||||
|
||||
if err := c.Convert(&x, &y, 0, nil); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
*x.Foo = "foo2"
|
||||
x.Bar[0] = "bar2"
|
||||
*x.Baz.(*string) = "baz2"
|
||||
x.Qux["qux"] = "qux2"
|
||||
if e, a := *x.Foo, *y.Foo; e == a {
|
||||
t.Errorf("expected difference between %v and %v", e, a)
|
||||
}
|
||||
if e, a := x.Bar, y.Bar; reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected difference between %v and %v", e, a)
|
||||
}
|
||||
if e, a := *x.Baz.(*string), *y.Baz.(*string); e == a {
|
||||
t.Errorf("expected difference between %v and %v", e, a)
|
||||
}
|
||||
if e, a := x.Qux, y.Qux; reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected difference between %v and %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_CallsRegisteredFunctions(t *testing.T) {
|
||||
type A struct {
|
||||
Foo string
|
||||
Baz int
|
||||
}
|
||||
type B struct {
|
||||
Bar string
|
||||
Baz int
|
||||
}
|
||||
type C struct{}
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
out.Bar = in.Foo
|
||||
return s.Convert(&in.Baz, &out.Baz, 0)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
err = c.RegisterConversionFunc(func(in *B, out *A, s Scope) error {
|
||||
out.Foo = in.Bar
|
||||
return s.Convert(&in.Baz, &out.Baz, 0)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
x := A{"hello, intrepid test reader!", 3}
|
||||
y := B{}
|
||||
|
||||
err = c.Convert(&x, &y, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if e, a := x.Foo, y.Bar; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := x.Baz, y.Baz; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
z := B{"all your test are belong to us", 42}
|
||||
w := A{}
|
||||
|
||||
err = c.Convert(&z, &w, 0, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if e, a := z.Bar, w.Foo; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := z.Baz, w.Baz; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
err = c.RegisterConversionFunc(func(in *A, out *C, s Scope) error {
|
||||
return fmt.Errorf("C can't store an A, silly")
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
err = c.Convert(&A{}, &C{}, 0, nil)
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_IgnoredConversion(t *testing.T) {
|
||||
type A struct{}
|
||||
type B struct{}
|
||||
|
||||
count := 0
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
count++
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if err := c.RegisterIgnoredConversion(&A{}, &B{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a := A{}
|
||||
b := B{}
|
||||
if err := c.Convert(&a, &b, 0, nil); err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Errorf("unexpected number of conversion invocations")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_IgnoredConversionNested(t *testing.T) {
|
||||
type C string
|
||||
type A struct {
|
||||
C C
|
||||
}
|
||||
type B struct {
|
||||
C C
|
||||
}
|
||||
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
typed := C("")
|
||||
if err := c.RegisterIgnoredConversion(&typed, &typed); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a := A{C: C("test")}
|
||||
b := B{C: C("other")}
|
||||
if err := c.Convert(&a, &b, AllowDifferentFieldTypeNames, nil); err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
if b.C != C("other") {
|
||||
t.Errorf("expected no conversion of field C: %#v", b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_GeneratedConversionOverriden(t *testing.T) {
|
||||
type A struct{}
|
||||
type B struct{}
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
return fmt.Errorf("generated function should be overriden")
|
||||
}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
a := A{}
|
||||
b := B{}
|
||||
if err := c.Convert(&a, &b, 0, nil); err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_WithConversionOverriden(t *testing.T) {
|
||||
type A struct{}
|
||||
type B struct{}
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
return fmt.Errorf("conversion function should be overriden")
|
||||
}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
return fmt.Errorf("generated function should be overriden")
|
||||
}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
ext := NewConversionFuncs()
|
||||
ext.Add(func(in *A, out *B, s Scope) error {
|
||||
return nil
|
||||
})
|
||||
newc := c.WithConversions(ext)
|
||||
|
||||
a := A{}
|
||||
b := B{}
|
||||
if err := c.Convert(&a, &b, 0, nil); err == nil || err.Error() != "conversion function should be overriden" {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if err := newc.Convert(&a, &b, 0, nil); err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_MapsStringArrays(t *testing.T) {
|
||||
type A struct {
|
||||
Foo string
|
||||
Baz int
|
||||
Other string
|
||||
}
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
|
||||
if len(*input) == 0 {
|
||||
*out = ""
|
||||
}
|
||||
*out = (*input)[0]
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
x := map[string][]string{
|
||||
"Foo": {"bar"},
|
||||
"Baz": {"1"},
|
||||
"Other": {"", "test"},
|
||||
"other": {"wrong"},
|
||||
}
|
||||
y := A{"test", 2, "something"}
|
||||
|
||||
if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames, nil); err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
}
|
||||
|
||||
if err := c.RegisterConversionFunc(func(input *[]string, out *int, s Scope) error {
|
||||
if len(*input) == 0 {
|
||||
*out = 0
|
||||
}
|
||||
str := (*input)[0]
|
||||
i, err := strconv.Atoi(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*out = i
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames, nil); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(y, A{"bar", 1, ""}) {
|
||||
t.Errorf("unexpected result: %#v", y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_MapsStringArraysWithMappingKey(t *testing.T) {
|
||||
type A struct {
|
||||
Foo string `json:"test"`
|
||||
Baz int
|
||||
Other string
|
||||
}
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
|
||||
if len(*input) == 0 {
|
||||
*out = ""
|
||||
}
|
||||
*out = (*input)[0]
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
x := map[string][]string{
|
||||
"Foo": {"bar"},
|
||||
"test": {"baz"},
|
||||
}
|
||||
y := A{"", 0, ""}
|
||||
|
||||
if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames|IgnoreMissingFields, &Meta{}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(y, A{"bar", 0, ""}) {
|
||||
t.Errorf("unexpected result: %#v", y)
|
||||
}
|
||||
|
||||
mapping := func(key string, sourceTag, destTag reflect.StructTag) (source string, dest string) {
|
||||
if s := destTag.Get("json"); len(s) > 0 {
|
||||
return strings.SplitN(s, ",", 2)[0], key
|
||||
}
|
||||
return key, key
|
||||
}
|
||||
|
||||
if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames|IgnoreMissingFields, &Meta{KeyNameMapping: mapping}); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(y, A{"baz", 0, ""}) {
|
||||
t.Errorf("unexpected result: %#v", y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_fuzz(t *testing.T) {
|
||||
// Use the same types from the scheme test.
|
||||
table := []struct {
|
||||
from, to, check interface{}
|
||||
}{
|
||||
{&TestType1{}, &ExternalTestType1{}, &TestType1{}},
|
||||
{&ExternalTestType1{}, &TestType1{}, &ExternalTestType1{}},
|
||||
}
|
||||
|
||||
f := fuzz.New().NilChance(.5).NumElements(0, 100)
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.nameFunc = func(t reflect.Type) string {
|
||||
// Hide the fact that we don't have separate packages for these things.
|
||||
return map[reflect.Type]string{
|
||||
reflect.TypeOf(TestType1{}): "TestType1",
|
||||
reflect.TypeOf(ExternalTestType1{}): "TestType1",
|
||||
reflect.TypeOf(TestType2{}): "TestType2",
|
||||
reflect.TypeOf(ExternalTestType2{}): "TestType2",
|
||||
}[t]
|
||||
}
|
||||
c.Debug = testLogger(t)
|
||||
|
||||
for i, item := range table {
|
||||
for j := 0; j < *fuzzIters; j++ {
|
||||
f.Fuzz(item.from)
|
||||
err := c.Convert(item.from, item.to, 0, nil)
|
||||
if err != nil {
|
||||
t.Errorf("(%v, %v): unexpected error: %v", i, j, err)
|
||||
continue
|
||||
}
|
||||
err = c.Convert(item.to, item.check, 0, nil)
|
||||
if err != nil {
|
||||
t.Errorf("(%v, %v): unexpected error: %v", i, j, err)
|
||||
continue
|
||||
}
|
||||
if e, a := item.from, item.check; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("(%v, %v): unexpected diff: %v", i, j, diff.ObjectDiff(e, a))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_MapElemAddr(t *testing.T) {
|
||||
type Foo struct {
|
||||
A map[int]int
|
||||
}
|
||||
type Bar struct {
|
||||
A map[string]string
|
||||
}
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
err := c.RegisterConversionFunc(
|
||||
func(in *int, out *string, s Scope) error {
|
||||
*out = fmt.Sprintf("%v", *in)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
err = c.RegisterConversionFunc(
|
||||
func(in *string, out *int, s Scope) error {
|
||||
if str, err := strconv.Atoi(*in); err != nil {
|
||||
return err
|
||||
} else {
|
||||
*out = str
|
||||
return nil
|
||||
}
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
f := fuzz.New().NilChance(0).NumElements(3, 3)
|
||||
first := Foo{}
|
||||
second := Bar{}
|
||||
f.Fuzz(&first)
|
||||
err = c.Convert(&first, &second, AllowDifferentFieldTypeNames, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
third := Foo{}
|
||||
err = c.Convert(&second, &third, AllowDifferentFieldTypeNames, nil)
|
||||
if e, a := first, third; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Unexpected diff: %v", diff.ObjectDiff(e, a))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_tags(t *testing.T) {
|
||||
type Foo struct {
|
||||
A string `test:"foo"`
|
||||
}
|
||||
type Bar struct {
|
||||
A string `test:"bar"`
|
||||
}
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
err := c.RegisterConversionFunc(
|
||||
func(in *string, out *string, s Scope) error {
|
||||
if e, a := "foo", s.SrcTag().Get("test"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := "bar", s.DestTag().Get("test"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
err = c.Convert(&Foo{}, &Bar{}, AllowDifferentFieldTypeNames, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_meta(t *testing.T) {
|
||||
type Foo struct{ A string }
|
||||
type Bar struct{ A string }
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
checks := 0
|
||||
err := c.RegisterConversionFunc(
|
||||
func(in *Foo, out *Bar, s Scope) error {
|
||||
if s.Meta() == nil {
|
||||
t.Errorf("Meta did not get passed!")
|
||||
}
|
||||
checks++
|
||||
s.Convert(&in.A, &out.A, 0)
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
err = c.RegisterConversionFunc(
|
||||
func(in *string, out *string, s Scope) error {
|
||||
if s.Meta() == nil {
|
||||
t.Errorf("Meta did not get passed a second time!")
|
||||
}
|
||||
checks++
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
err = c.Convert(&Foo{}, &Bar{}, 0, &Meta{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if checks != 2 {
|
||||
t.Errorf("Registered functions did not get called.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_flags(t *testing.T) {
|
||||
type Foo struct{ A string }
|
||||
type Bar struct{ A string }
|
||||
table := []struct {
|
||||
from, to interface{}
|
||||
flags FieldMatchingFlags
|
||||
shouldSucceed bool
|
||||
}{
|
||||
// Check that DestFromSource allows extra fields only in source.
|
||||
{
|
||||
from: &struct{ A string }{},
|
||||
to: &struct{ A, B string }{},
|
||||
flags: DestFromSource,
|
||||
shouldSucceed: false,
|
||||
}, {
|
||||
from: &struct{ A, B string }{},
|
||||
to: &struct{ A string }{},
|
||||
flags: DestFromSource,
|
||||
shouldSucceed: true,
|
||||
},
|
||||
|
||||
// Check that SourceToDest allows for extra fields only in dest.
|
||||
{
|
||||
from: &struct{ A string }{},
|
||||
to: &struct{ A, B string }{},
|
||||
flags: SourceToDest,
|
||||
shouldSucceed: true,
|
||||
}, {
|
||||
from: &struct{ A, B string }{},
|
||||
to: &struct{ A string }{},
|
||||
flags: SourceToDest,
|
||||
shouldSucceed: false,
|
||||
},
|
||||
|
||||
// Check that IgnoreMissingFields makes the above failure cases pass.
|
||||
{
|
||||
from: &struct{ A string }{},
|
||||
to: &struct{ A, B string }{},
|
||||
flags: DestFromSource | IgnoreMissingFields,
|
||||
shouldSucceed: true,
|
||||
}, {
|
||||
from: &struct{ A, B string }{},
|
||||
to: &struct{ A string }{},
|
||||
flags: SourceToDest | IgnoreMissingFields,
|
||||
shouldSucceed: true,
|
||||
},
|
||||
|
||||
// Check that the field type name must match unless
|
||||
// AllowDifferentFieldTypeNames is specified.
|
||||
{
|
||||
from: &struct{ A, B Foo }{},
|
||||
to: &struct{ A Bar }{},
|
||||
flags: DestFromSource,
|
||||
shouldSucceed: false,
|
||||
}, {
|
||||
from: &struct{ A Foo }{},
|
||||
to: &struct{ A, B Bar }{},
|
||||
flags: SourceToDest,
|
||||
shouldSucceed: false,
|
||||
}, {
|
||||
from: &struct{ A, B Foo }{},
|
||||
to: &struct{ A Bar }{},
|
||||
flags: DestFromSource | AllowDifferentFieldTypeNames,
|
||||
shouldSucceed: true,
|
||||
}, {
|
||||
from: &struct{ A Foo }{},
|
||||
to: &struct{ A, B Bar }{},
|
||||
flags: SourceToDest | AllowDifferentFieldTypeNames,
|
||||
shouldSucceed: true,
|
||||
},
|
||||
}
|
||||
f := fuzz.New().NilChance(.5).NumElements(0, 100)
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
c.Debug = testLogger(t)
|
||||
|
||||
for i, item := range table {
|
||||
for j := 0; j < *fuzzIters; j++ {
|
||||
f.Fuzz(item.from)
|
||||
err := c.Convert(item.from, item.to, item.flags, nil)
|
||||
if item.shouldSucceed && err != nil {
|
||||
t.Errorf("(%v, %v): unexpected error: %v", i, j, err)
|
||||
continue
|
||||
}
|
||||
if !item.shouldSucceed && err == nil {
|
||||
t.Errorf("(%v, %v): unexpected non-error", i, j)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConverter_FieldRename(t *testing.T) {
|
||||
type WeirdMeta struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
type NameMeta struct {
|
||||
Name string
|
||||
}
|
||||
type TypeMeta struct {
|
||||
Type string
|
||||
}
|
||||
type A struct {
|
||||
WeirdMeta
|
||||
}
|
||||
type B struct {
|
||||
TypeMeta
|
||||
NameMeta
|
||||
}
|
||||
|
||||
c := NewConverter(DefaultNameFunc)
|
||||
err := c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", TypeMeta{}, "TypeMeta")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
err = c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", NameMeta{}, "NameMeta")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
err = c.SetStructFieldCopy(TypeMeta{}, "TypeMeta", WeirdMeta{}, "WeirdMeta")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
err = c.SetStructFieldCopy(NameMeta{}, "NameMeta", WeirdMeta{}, "WeirdMeta")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
c.Debug = testLogger(t)
|
||||
|
||||
aVal := &A{
|
||||
WeirdMeta: WeirdMeta{
|
||||
Name: "Foo",
|
||||
Type: "Bar",
|
||||
},
|
||||
}
|
||||
|
||||
bVal := &B{
|
||||
TypeMeta: TypeMeta{"Bar"},
|
||||
NameMeta: NameMeta{"Foo"},
|
||||
}
|
||||
|
||||
table := map[string]struct {
|
||||
from, to, expect interface{}
|
||||
flags FieldMatchingFlags
|
||||
}{
|
||||
"to": {
|
||||
aVal,
|
||||
&B{},
|
||||
bVal,
|
||||
AllowDifferentFieldTypeNames | SourceToDest | IgnoreMissingFields,
|
||||
},
|
||||
"from": {
|
||||
bVal,
|
||||
&A{},
|
||||
aVal,
|
||||
AllowDifferentFieldTypeNames | SourceToDest,
|
||||
},
|
||||
"toDestFirst": {
|
||||
aVal,
|
||||
&B{},
|
||||
bVal,
|
||||
AllowDifferentFieldTypeNames,
|
||||
},
|
||||
"fromDestFirst": {
|
||||
bVal,
|
||||
&A{},
|
||||
aVal,
|
||||
AllowDifferentFieldTypeNames | IgnoreMissingFields,
|
||||
},
|
||||
}
|
||||
|
||||
for name, item := range table {
|
||||
err := c.Convert(item.from, item.to, item.flags, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if e, a := item.expect, item.to; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: unexpected diff: %v", name, diff.ObjectDiff(e, a))
|
||||
}
|
||||
}
|
||||
}
|
36
vendor/k8s.io/apimachinery/pkg/conversion/deep_equal.go
generated
vendored
Normal file
36
vendor/k8s.io/apimachinery/pkg/conversion/deep_equal.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2015 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 conversion
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/third_party/forked/golang/reflect"
|
||||
)
|
||||
|
||||
// The code for this type must be located in third_party, since it forks from
|
||||
// go std lib. But for convenience, we expose the type here, too.
|
||||
type Equalities struct {
|
||||
reflect.Equalities
|
||||
}
|
||||
|
||||
// For convenience, panics on errors
|
||||
func EqualitiesOrDie(funcs ...interface{}) Equalities {
|
||||
e := Equalities{reflect.Equalities{}}
|
||||
if err := e.AddFuncs(funcs...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return e
|
||||
}
|
24
vendor/k8s.io/apimachinery/pkg/conversion/doc.go
generated
vendored
Normal file
24
vendor/k8s.io/apimachinery/pkg/conversion/doc.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright 2014 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 conversion provides go object versioning.
|
||||
//
|
||||
// Specifically, conversion provides a way for you to define multiple versions
|
||||
// of the same object. You may write functions which implement conversion logic,
|
||||
// but for the fields which did not change, copying is automated. This makes it
|
||||
// easy to modify the structures you use in memory without affecting the format
|
||||
// you store on disk or respond to in your external API calls.
|
||||
package conversion // import "k8s.io/apimachinery/pkg/conversion"
|
39
vendor/k8s.io/apimachinery/pkg/conversion/helper.go
generated
vendored
Normal file
39
vendor/k8s.io/apimachinery/pkg/conversion/helper.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright 2014 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 conversion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// EnforcePtr ensures that obj is a pointer of some sort. Returns a reflect.Value
|
||||
// of the dereferenced pointer, ensuring that it is settable/addressable.
|
||||
// Returns an error if this is not possible.
|
||||
func EnforcePtr(obj interface{}) (reflect.Value, error) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
if v.Kind() == reflect.Invalid {
|
||||
return reflect.Value{}, fmt.Errorf("expected pointer, but got invalid kind")
|
||||
}
|
||||
return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type())
|
||||
}
|
||||
if v.IsNil() {
|
||||
return reflect.Value{}, fmt.Errorf("expected pointer, but got nil")
|
||||
}
|
||||
return v.Elem(), nil
|
||||
}
|
38
vendor/k8s.io/apimachinery/pkg/conversion/helper_test.go
generated
vendored
Normal file
38
vendor/k8s.io/apimachinery/pkg/conversion/helper_test.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright 2014 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 conversion
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestInvalidPtrValueKind(t *testing.T) {
|
||||
var simple interface{}
|
||||
switch obj := simple.(type) {
|
||||
default:
|
||||
_, err := EnforcePtr(obj)
|
||||
if err == nil {
|
||||
t.Errorf("Expected error on invalid kind")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnforceNilPtr(t *testing.T) {
|
||||
var nilPtr *struct{}
|
||||
_, err := EnforcePtr(nilPtr)
|
||||
if err == nil {
|
||||
t.Errorf("Expected error on nil pointer")
|
||||
}
|
||||
}
|
40
vendor/k8s.io/apimachinery/pkg/conversion/queryparams/BUILD
generated
vendored
Normal file
40
vendor/k8s.io/apimachinery/pkg/conversion/queryparams/BUILD
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"convert.go",
|
||||
"doc.go",
|
||||
],
|
||||
importpath = "k8s.io/apimachinery/pkg/conversion/queryparams",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = ["convert_test.go"],
|
||||
importpath = "k8s.io/apimachinery/pkg/conversion/queryparams_test",
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/conversion/queryparams:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
195
vendor/k8s.io/apimachinery/pkg/conversion/queryparams/convert.go
generated
vendored
Normal file
195
vendor/k8s.io/apimachinery/pkg/conversion/queryparams/convert.go
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
Copyright 2014 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 queryparams
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Marshaler converts an object to a query parameter string representation
|
||||
type Marshaler interface {
|
||||
MarshalQueryParameter() (string, error)
|
||||
}
|
||||
|
||||
// Unmarshaler converts a string representation to an object
|
||||
type Unmarshaler interface {
|
||||
UnmarshalQueryParameter(string) error
|
||||
}
|
||||
|
||||
func jsonTag(field reflect.StructField) (string, bool) {
|
||||
structTag := field.Tag.Get("json")
|
||||
if len(structTag) == 0 {
|
||||
return "", false
|
||||
}
|
||||
parts := strings.Split(structTag, ",")
|
||||
tag := parts[0]
|
||||
if tag == "-" {
|
||||
tag = ""
|
||||
}
|
||||
omitempty := false
|
||||
parts = parts[1:]
|
||||
for _, part := range parts {
|
||||
if part == "omitempty" {
|
||||
omitempty = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return tag, omitempty
|
||||
}
|
||||
|
||||
func formatValue(value interface{}) string {
|
||||
return fmt.Sprintf("%v", value)
|
||||
}
|
||||
|
||||
func isPointerKind(kind reflect.Kind) bool {
|
||||
return kind == reflect.Ptr
|
||||
}
|
||||
|
||||
func isStructKind(kind reflect.Kind) bool {
|
||||
return kind == reflect.Struct
|
||||
}
|
||||
|
||||
func isValueKind(kind reflect.Kind) bool {
|
||||
switch kind {
|
||||
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, reflect.Float32,
|
||||
reflect.Float64, reflect.Complex64, reflect.Complex128:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func zeroValue(value reflect.Value) bool {
|
||||
return reflect.DeepEqual(reflect.Zero(value.Type()).Interface(), value.Interface())
|
||||
}
|
||||
|
||||
func customMarshalValue(value reflect.Value) (reflect.Value, bool) {
|
||||
// Return unless we implement a custom query marshaler
|
||||
if !value.CanInterface() {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
||||
marshaler, ok := value.Interface().(Marshaler)
|
||||
if !ok {
|
||||
if !isPointerKind(value.Kind()) && value.CanAddr() {
|
||||
marshaler, ok = value.Addr().Interface().(Marshaler)
|
||||
if !ok {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
} else {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
}
|
||||
|
||||
// Don't invoke functions on nil pointers
|
||||
// If the type implements MarshalQueryParameter, AND the tag is not omitempty, AND the value is a nil pointer, "" seems like a reasonable response
|
||||
if isPointerKind(value.Kind()) && zeroValue(value) {
|
||||
return reflect.ValueOf(""), true
|
||||
}
|
||||
|
||||
// Get the custom marshalled value
|
||||
v, err := marshaler.MarshalQueryParameter()
|
||||
if err != nil {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
return reflect.ValueOf(v), true
|
||||
}
|
||||
|
||||
func addParam(values url.Values, tag string, omitempty bool, value reflect.Value) {
|
||||
if omitempty && zeroValue(value) {
|
||||
return
|
||||
}
|
||||
val := ""
|
||||
iValue := fmt.Sprintf("%v", value.Interface())
|
||||
|
||||
if iValue != "<nil>" {
|
||||
val = iValue
|
||||
}
|
||||
values.Add(tag, val)
|
||||
}
|
||||
|
||||
func addListOfParams(values url.Values, tag string, omitempty bool, list reflect.Value) {
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
addParam(values, tag, omitempty, list.Index(i))
|
||||
}
|
||||
}
|
||||
|
||||
// Convert takes an object and converts it to a url.Values object using JSON tags as
|
||||
// parameter names. Only top-level simple values, arrays, and slices are serialized.
|
||||
// Embedded structs, maps, etc. will not be serialized.
|
||||
func Convert(obj interface{}) (url.Values, error) {
|
||||
result := url.Values{}
|
||||
if obj == nil {
|
||||
return result, nil
|
||||
}
|
||||
var sv reflect.Value
|
||||
switch reflect.TypeOf(obj).Kind() {
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
sv = reflect.ValueOf(obj).Elem()
|
||||
default:
|
||||
return nil, fmt.Errorf("expecting a pointer or interface")
|
||||
}
|
||||
st := sv.Type()
|
||||
if !isStructKind(st.Kind()) {
|
||||
return nil, fmt.Errorf("expecting a pointer to a struct")
|
||||
}
|
||||
|
||||
// Check all object fields
|
||||
convertStruct(result, st, sv)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) {
|
||||
for i := 0; i < st.NumField(); i++ {
|
||||
field := sv.Field(i)
|
||||
tag, omitempty := jsonTag(st.Field(i))
|
||||
if len(tag) == 0 {
|
||||
continue
|
||||
}
|
||||
ft := field.Type()
|
||||
|
||||
kind := ft.Kind()
|
||||
if isPointerKind(kind) {
|
||||
ft = ft.Elem()
|
||||
kind = ft.Kind()
|
||||
if !field.IsNil() {
|
||||
field = reflect.Indirect(field)
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case isValueKind(kind):
|
||||
addParam(result, tag, omitempty, field)
|
||||
case kind == reflect.Array || kind == reflect.Slice:
|
||||
if isValueKind(ft.Elem().Kind()) {
|
||||
addListOfParams(result, tag, omitempty, field)
|
||||
}
|
||||
case isStructKind(kind) && !(zeroValue(field) && omitempty):
|
||||
if marshalValue, ok := customMarshalValue(field); ok {
|
||||
addParam(result, tag, omitempty, marshalValue)
|
||||
} else {
|
||||
convertStruct(result, ft, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
215
vendor/k8s.io/apimachinery/pkg/conversion/queryparams/convert_test.go
generated
vendored
Normal file
215
vendor/k8s.io/apimachinery/pkg/conversion/queryparams/convert_test.go
generated
vendored
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
Copyright 2014 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 queryparams_test
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/conversion/queryparams"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type namedString string
|
||||
type namedBool bool
|
||||
|
||||
type bar struct {
|
||||
Float1 float32 `json:"float1"`
|
||||
Float2 float64 `json:"float2"`
|
||||
Int1 int64 `json:"int1,omitempty"`
|
||||
Int2 int32 `json:"int2,omitempty"`
|
||||
Int3 int16 `json:"int3,omitempty"`
|
||||
Str1 string `json:"str1,omitempty"`
|
||||
Ignored int
|
||||
Ignored2 string
|
||||
}
|
||||
|
||||
func (obj *bar) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
|
||||
|
||||
type foo struct {
|
||||
Str string `json:"str"`
|
||||
Integer int `json:"integer,omitempty"`
|
||||
Slice []string `json:"slice,omitempty"`
|
||||
Boolean bool `json:"boolean,omitempty"`
|
||||
NamedStr namedString `json:"namedStr,omitempty"`
|
||||
NamedBool namedBool `json:"namedBool,omitempty"`
|
||||
Foobar bar `json:"foobar,omitempty"`
|
||||
Testmap map[string]string `json:"testmap,omitempty"`
|
||||
}
|
||||
|
||||
func (obj *foo) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
|
||||
|
||||
type baz struct {
|
||||
Ptr *int `json:"ptr"`
|
||||
Bptr *bool `json:"bptr,omitempty"`
|
||||
}
|
||||
|
||||
func (obj *baz) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
|
||||
|
||||
// childStructs tests some of the types we serialize to query params for log API calls
|
||||
// notably, the nested time struct
|
||||
type childStructs struct {
|
||||
Container string `json:"container,omitempty"`
|
||||
Follow bool `json:"follow,omitempty"`
|
||||
Previous bool `json:"previous,omitempty"`
|
||||
SinceSeconds *int64 `json:"sinceSeconds,omitempty"`
|
||||
SinceTime *metav1.Time `json:"sinceTime,omitempty"`
|
||||
EmptyTime *metav1.Time `json:"emptyTime"`
|
||||
NonPointerTime metav1.Time `json:"nonPointerTime"`
|
||||
}
|
||||
|
||||
func (obj *childStructs) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
|
||||
|
||||
func validateResult(t *testing.T, input interface{}, actual, expected url.Values) {
|
||||
local := url.Values{}
|
||||
for k, v := range expected {
|
||||
local[k] = v
|
||||
}
|
||||
for k, v := range actual {
|
||||
if ev, ok := local[k]; !ok || !reflect.DeepEqual(ev, v) {
|
||||
if !ok {
|
||||
t.Errorf("%#v: actual value key %s not found in expected map", input, k)
|
||||
} else {
|
||||
t.Errorf("%#v: values don't match: actual: %#v, expected: %#v", input, v, ev)
|
||||
}
|
||||
}
|
||||
delete(local, k)
|
||||
}
|
||||
if len(local) > 0 {
|
||||
t.Errorf("%#v: expected map has keys that were not found in actual map: %#v", input, local)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
sinceSeconds := int64(123)
|
||||
sinceTime := metav1.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)
|
||||
|
||||
tests := []struct {
|
||||
input interface{}
|
||||
expected url.Values
|
||||
}{
|
||||
{
|
||||
input: &foo{
|
||||
Str: "hello",
|
||||
},
|
||||
expected: url.Values{"str": {"hello"}},
|
||||
},
|
||||
{
|
||||
input: &foo{
|
||||
Str: "test string",
|
||||
Slice: []string{"one", "two", "three"},
|
||||
Integer: 234,
|
||||
Boolean: true,
|
||||
},
|
||||
expected: url.Values{"str": {"test string"}, "slice": {"one", "two", "three"}, "integer": {"234"}, "boolean": {"true"}},
|
||||
},
|
||||
{
|
||||
input: &foo{
|
||||
Str: "named types",
|
||||
NamedStr: "value1",
|
||||
NamedBool: true,
|
||||
},
|
||||
expected: url.Values{"str": {"named types"}, "namedStr": {"value1"}, "namedBool": {"true"}},
|
||||
},
|
||||
{
|
||||
input: &foo{
|
||||
Str: "don't ignore embedded struct",
|
||||
Foobar: bar{
|
||||
Float1: 5.0,
|
||||
},
|
||||
},
|
||||
expected: url.Values{"str": {"don't ignore embedded struct"}, "float1": {"5"}, "float2": {"0"}},
|
||||
},
|
||||
{
|
||||
// Ignore untagged fields
|
||||
input: &bar{
|
||||
Float1: 23.5,
|
||||
Float2: 100.7,
|
||||
Int1: 1,
|
||||
Int2: 2,
|
||||
Int3: 3,
|
||||
Ignored: 1,
|
||||
Ignored2: "ignored",
|
||||
},
|
||||
expected: url.Values{"float1": {"23.5"}, "float2": {"100.7"}, "int1": {"1"}, "int2": {"2"}, "int3": {"3"}},
|
||||
},
|
||||
{
|
||||
// include fields that are not tagged omitempty
|
||||
input: &foo{
|
||||
NamedStr: "named str",
|
||||
},
|
||||
expected: url.Values{"str": {""}, "namedStr": {"named str"}},
|
||||
},
|
||||
{
|
||||
input: &baz{
|
||||
Ptr: intp(5),
|
||||
Bptr: boolp(true),
|
||||
},
|
||||
expected: url.Values{"ptr": {"5"}, "bptr": {"true"}},
|
||||
},
|
||||
{
|
||||
input: &baz{
|
||||
Bptr: boolp(true),
|
||||
},
|
||||
expected: url.Values{"ptr": {""}, "bptr": {"true"}},
|
||||
},
|
||||
{
|
||||
input: &baz{
|
||||
Ptr: intp(5),
|
||||
},
|
||||
expected: url.Values{"ptr": {"5"}},
|
||||
},
|
||||
{
|
||||
input: &childStructs{
|
||||
Container: "mycontainer",
|
||||
Follow: true,
|
||||
Previous: true,
|
||||
SinceSeconds: &sinceSeconds,
|
||||
SinceTime: &sinceTime, // test a custom marshaller
|
||||
EmptyTime: nil, // test a nil custom marshaller without omitempty
|
||||
NonPointerTime: sinceTime,
|
||||
},
|
||||
expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "sinceTime": {"2000-01-01T12:34:56Z"}, "emptyTime": {""}, "nonPointerTime": {"2000-01-01T12:34:56Z"}},
|
||||
},
|
||||
{
|
||||
input: &childStructs{
|
||||
Container: "mycontainer",
|
||||
Follow: true,
|
||||
Previous: true,
|
||||
SinceSeconds: &sinceSeconds,
|
||||
SinceTime: nil, // test a nil custom marshaller with omitempty
|
||||
NonPointerTime: sinceTime,
|
||||
},
|
||||
expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "emptyTime": {""}, "nonPointerTime": {"2000-01-01T12:34:56Z"}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result, err := queryparams.Convert(test.input)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error while converting %#v: %v", test.input, err)
|
||||
}
|
||||
validateResult(t, test.input, result, test.expected)
|
||||
}
|
||||
}
|
||||
|
||||
func intp(n int) *int { return &n }
|
||||
|
||||
func boolp(b bool) *bool { return &b }
|
19
vendor/k8s.io/apimachinery/pkg/conversion/queryparams/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/apimachinery/pkg/conversion/queryparams/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2014 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 queryparams provides conversion from versioned
|
||||
// runtime objects to URL query values
|
||||
package queryparams // import "k8s.io/apimachinery/pkg/conversion/queryparams"
|
Reference in New Issue
Block a user