conversion

package
v0.9.1-0...-c65f83f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 27, 2015 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package conversion provides go object versioning and encoding/decoding mechanisms.

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.

The second offering of this package is automated encoding/decoding. The version and type of the object is recorded in the output, so it can be recreated upon reading. Currently, conversion writes JSON output, and interprets both JSON and YAML input.

In the future, we plan to more explicitly separate the above two mechanisms, and add more serialization options, such as gob.

Index

Constants

This section is empty.

Variables

View Source
var DefaultMetaFactory = SimpleMetaFactory{KindField: "Kind", VersionField: "APIVersion"}

DefaultMetaFactory is a default factory for versioning objects in JSON/YAML. The object in memory and in the default JSON serialization will use the "kind" and "apiVersion" fields.

Functions

func EnforcePtr

func EnforcePtr(obj interface{}) (reflect.Value, error)

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 IsNotRegisteredError

func IsNotRegisteredError(err error) bool

IsNotRegisteredError returns true if the error indicates the provided object or input data is not registered.

func UpdateVersionAndKind

func UpdateVersionAndKind(baseFields []string, versionField, version, kindField, kind string, obj interface{}) error

UpdateVersionAndKind uses reflection to find and set the versionField and kindField fields on a pointer to a struct to version and kind. Provided as a convenience for others implementing MetaFactory. Pass an array to baseFields to check one or more nested structs for the named fields. The version field is treated as optional if it is not present in the struct.

Types

type Converter

type Converter struct {

	// 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
	// contains filtered or unexported fields
}

Converter knows how to convert one type to another.

func NewConverter

func NewConverter() *Converter

NewConverter creates a new Converter object.

func (*Converter) Convert

func (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error

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 (*Converter) DefaultConvert

func (c *Converter) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error

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 (*Converter) Register

func (c *Converter) Register(conversionFunc interface{}) error

Register 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.Register(func(in *Pod, out *v1beta1.Pod, s Scope) error { ... return nil })

func (*Converter) SetStructFieldCopy

func (c *Converter) SetStructFieldCopy(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error

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.

type DebugLogger

type DebugLogger interface {
	Logf(format string, args ...interface{})
}

DebugLogger allows you to get debugging messages if necessary.

type Equalities

type Equalities map[reflect.Type]reflect.Value

Equalities is a map from type to a function comparing two values of that type.

func EqualitiesOrDie

func EqualitiesOrDie(funcs ...interface{}) Equalities

For convenience, panics on errrors

func (Equalities) AddFunc

func (e Equalities) AddFunc(eqFunc interface{}) error

AddFunc uses func as an equality function: it must take two parameters of the same type, and return a boolean.

func (Equalities) AddFuncs

func (e Equalities) AddFuncs(funcs ...interface{}) error

AddFuncs is a shortcut for multiple calls to AddFunc.

func (Equalities) DeepEqual

func (e Equalities) DeepEqual(a1, a2 interface{}) bool

DeepEqual is like reflect.DeepEqual, but focused on semantic equality instead of memory equality.

It will use e's equality functions if it finds types that match.

An empty slice *is* equal to a nil slice for our purposes; same for maps.

Unexported field members are not compared.

func (Equalities) Equal

func (e Equalities) Equal(a, b interface{}) bool

Equal return true if a matching equality function thinks a == b; if there is no matching equality function, it calls the standard go == operator.

type FieldMatchingFlags

type FieldMatchingFlags int

FieldMatchingFlags contains a list of ways in which struct fields could be copied. These constants may be | combined.

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 niether 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
)

func (FieldMatchingFlags) IsSet

IsSet returns true if the given flag or combination of flags is set.

type Meta

type Meta struct {
	SrcVersion  string
	DestVersion string
}

Meta is supplied by Scheme, when it calls Convert.

type MetaFactory

type MetaFactory interface {
	// Update sets the given version and kind onto the object.
	Update(version, kind string, obj interface{}) error
	// Interpret should return the version and kind of the wire-format of
	// the object.
	Interpret(data []byte) (version, kind string, err error)
}

MetaFactory is used to store and retrieve the version and kind information for all objects in a scheme.

type Scheme

type Scheme struct {

	// Indent will cause the JSON output from Encode to be indented, iff it is true.
	Indent bool

	// InternalVersion is the default internal version. It is recommended that
	// you use "" for the internal version.
	InternalVersion string

	// MetaInsertionFactory is used to create an object to store and retrieve
	// the version and kind information for all objects. The default uses the
	// keys "apiVersion" and "kind" respectively.
	MetaFactory MetaFactory
	// contains filtered or unexported fields
}

Scheme defines an entire encoding and decoding scheme.

func NewScheme

func NewScheme() *Scheme

NewScheme manufactures a new scheme.

func (*Scheme) AddConversionFuncs

func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error

AddConversionFuncs adds functions to the list of conversion functions. The given functions should know how to convert between two of your API objects, or their sub-objects. We deduce how to call these functions from the types of their two parameters; see the comment for Converter.Register.

Note that, if you need to copy sub-objects that didn't change, you can use the conversion.Scope object that will be passed to your conversion function. Additionally, all conversions started by Scheme will set the SrcVersion and DestVersion fields on the Meta object. Example:

s.AddConversionFuncs(

func(in *InternalObject, out *ExternalObject, scope conversion.Scope) error {
	// You can depend on Meta() being non-nil, and this being set to
	// the source version, e.g., ""
	s.Meta().SrcVersion
	// You can depend on this being set to the destination version,
	// e.g., "v1beta1".
	s.Meta().DestVersion
	// Call scope.Convert to copy sub-fields.
	s.Convert(&in.SubFieldThatMoved, &out.NewLocation.NewName, 0)
	return nil
},

)

(For more detail about conversion functions, see Converter.Register's comment.)

Also note that the default behavior, if you don't add a conversion function, is to sanely copy fields that have the same names and same type names. It's OK if the destination type has extra fields, but it must not remove any. So you only need to add conversion functions for things with changed/removed fields.

func (*Scheme) AddKnownTypeWithName

func (s *Scheme) AddKnownTypeWithName(version, kind string, obj interface{})

AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should be encoded as. Useful for testing when you don't want to make multiple packages to define your structs.

func (*Scheme) AddKnownTypes

func (s *Scheme) AddKnownTypes(version string, types ...interface{})

AddKnownTypes registers all types passed in 'types' as being members of version 'version. Encode() will refuse objects unless their type has been registered with AddKnownTypes. All objects passed to types should be pointers to structs. The name that go reports for the struct becomes the "kind" field when encoding.

func (*Scheme) AddStructFieldConversion

func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error

AddStructFieldConversion allows you to specify a mechanical copy for a moved or renamed struct field without writing an entire conversion function. See the comment in Converter.SetStructFieldCopy for parameter details. Call as many times as needed, even on the same fields.

func (*Scheme) Convert

func (s *Scheme) Convert(in, out interface{}) error

Convert will attempt to convert in into out. Both must be pointers. For easy testing of conversion functions. Returns an error if the conversion isn't possible. You can call this with types that haven't been registered (for example, a to test conversion of types that are nested within registered types), but in that case, the conversion.Scope object passed to your conversion functions won't have SrcVersion or DestVersion fields set correctly in Meta().

func (*Scheme) ConvertToVersion

func (s *Scheme) ConvertToVersion(in interface{}, outVersion string) (interface{}, error)

ConvertToVersion attempts to convert an input object to its matching Kind in another version within this scheme. Will return an error if the provided version does not contain the inKind (or a mapping by name defined with AddKnownTypeWithName).

func (*Scheme) DataVersionAndKind

func (s *Scheme) DataVersionAndKind(data []byte) (version, kind string, err error)

DataVersionAndKind will return the APIVersion and Kind of the given wire-format encoding of an API Object, or an error.

func (*Scheme) Decode

func (s *Scheme) Decode(data []byte) (interface{}, error)

Decode converts a YAML or JSON string back into a pointer to an api object. Deduces the type based upon the fields added by the MetaInsertionFactory technique. The object will be converted, if necessary, into the s.InternalVersion type before being returned. Decode will not decode objects without version set unless InternalVersion is also "".

func (*Scheme) DecodeInto

func (s *Scheme) DecodeInto(data []byte, obj interface{}) error

DecodeInto parses a YAML or JSON string and stores it in obj. Returns an error if data.Kind is set and doesn't match the type of obj. Obj should be a pointer to an api type. If obj's version doesn't match that in data, an attempt will be made to convert data into obj's version.

func (*Scheme) EncodeToVersion

func (s *Scheme) EncodeToVersion(obj interface{}, destVersion string) (data []byte, err error)

EncodeToVersion turns the given api object into an appropriate JSON string. Obj may be a pointer to a struct, or a struct. If a struct, a copy will be made, therefore it's recommended to pass a pointer to a struct. The type must have been registered.

Memory/wire format differences:

  • Having to keep track of the Kind and Version fields makes tests very annoying, so the rule is that they are set only in wire format (json), not when in native (memory) format. This is possible because both pieces of information are implicit in the go typed object.
  • An exception: note that, if there are embedded API objects of known type, for example, PodList{... Items []Pod ...}, these embedded objects must be of the same version of the object they are embedded within, and their Version and Kind must both be empty.
  • Note that the exception does not apply to a generic APIObject type which recursively does Encode()/Decode(), and is capable of expressing any API object.
  • Only versioned objects should be encoded. This means that, if you pass a native object, Encode will convert it to a versioned object. For example, an api.Pod will get converted to a v1beta1.Pod. However, if you pass in an object that's already versioned (v1beta1.Pod), Encode will not modify it.

The purpose of the above complex conversion behavior is to allow us to change the memory format yet not break compatibility with any stored objects, whether they be in our storage layer (e.g., etcd), or in user's config files.

func (*Scheme) KnownTypes

func (s *Scheme) KnownTypes(version string) map[string]reflect.Type

KnownTypes returns an array of the types that are known for a particular version.

func (*Scheme) Log

func (s *Scheme) Log(l DebugLogger)

Log sets a logger on the scheme. For test purposes only

func (*Scheme) NewObject

func (s *Scheme) NewObject(versionName, kind string) (interface{}, error)

NewObject returns a new object of the given version and name, or an error if it hasn't been registered.

func (*Scheme) ObjectVersionAndKind

func (s *Scheme) ObjectVersionAndKind(obj interface{}) (apiVersion, kind string, err error)

ObjectVersionAndKind returns the API version and kind of the go object, or an error if it's not a pointer or is unregistered.

func (*Scheme) SetVersionAndKind

func (s *Scheme) SetVersionAndKind(version, kind string, obj interface{}) error

SetVersionAndKind sets the version and kind fields (with help from MetaInsertionFactory). Returns an error if this isn't possible. obj must be a pointer.

type Scope

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
}

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 SimpleMetaFactory

type SimpleMetaFactory struct {
	// The name of the API version field in memory of the struct
	VersionField string
	// The name of the kind field in memory of the struct.
	KindField string
	// Optional, if set will look in the named inline structs to find the fields to set.
	BaseFields []string
}

SimpleMetaFactory provides default methods for retrieving the type and version of objects that are identified with an "apiVersion" and "kind" fields in their JSON/YAML serialization. It may be parameterized with the names of the fields in memory, or an optional list of base structs to search for those fields in memory.

func (SimpleMetaFactory) Interpret

func (SimpleMetaFactory) Interpret(data []byte) (version, kind string, err error)

Interpret will return the APIVersion and Kind of the JSON/YAML wire-format encoding of an object, or an error.

func (SimpleMetaFactory) Update

func (f SimpleMetaFactory) Update(version, kind string, obj interface{}) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL