Introduce GroupVersioner for capturing desired target version
Convert single GV and lists of GVs into an interface that can handle more complex scenarios (everything internal, nothing supported). Pass the interface down into conversion.
This commit is contained in:
@@ -237,7 +237,7 @@ func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, bool,
|
||||
|
||||
gvks, ok := s.typeToGVK[t]
|
||||
if !ok {
|
||||
return nil, false, ¬RegisteredErr{t: t}
|
||||
return nil, false, NewNotRegisteredErr(unversioned.GroupVersionKind{}, t)
|
||||
}
|
||||
_, unversionedType := s.unversionedTypes[t]
|
||||
|
||||
@@ -275,7 +275,7 @@ func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) {
|
||||
if t, exists := s.unversionedKinds[kind.Kind]; exists {
|
||||
return reflect.New(t).Interface().(Object), nil
|
||||
}
|
||||
return nil, ¬RegisteredErr{gvk: kind}
|
||||
return nil, NewNotRegisteredErr(kind, nil)
|
||||
}
|
||||
|
||||
// AddGenericConversionFunc adds a function that accepts the ConversionFunc call pattern
|
||||
@@ -438,23 +438,13 @@ func (s *Scheme) DeepCopy(src interface{}) (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 (s *Scheme) Convert(in, out interface{}) error {
|
||||
inVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
||||
outVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"}
|
||||
if inObj, ok := in.(Object); ok {
|
||||
if gvks, _, err := s.ObjectKinds(inObj); err == nil {
|
||||
inVersion = gvks[0].GroupVersion()
|
||||
}
|
||||
}
|
||||
if outObj, ok := out.(Object); ok {
|
||||
if gvks, _, err := s.ObjectKinds(outObj); err == nil {
|
||||
outVersion = gvks[0].GroupVersion()
|
||||
}
|
||||
}
|
||||
flags, meta := s.generateConvertMeta(inVersion, outVersion, in)
|
||||
// a to test conversion of types that are nested within registered types). The
|
||||
// context interface is passed to the convertor.
|
||||
// TODO: identify whether context should be hidden, or behind a formal context/scope
|
||||
// interface
|
||||
func (s *Scheme) Convert(in, out interface{}, context interface{}) error {
|
||||
flags, meta := s.generateConvertMeta(in)
|
||||
meta.Context = context
|
||||
if flags == 0 {
|
||||
flags = conversion.AllowDifferentFieldTypeNames
|
||||
}
|
||||
@@ -478,73 +468,20 @@ func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string,
|
||||
// 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). Will also
|
||||
// return an error if the conversion does not result in a valid Object being
|
||||
// returned. The serializer handles loading/serializing nested objects.
|
||||
func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
||||
switch in.(type) {
|
||||
case *Unknown, *Unstructured, *UnstructuredList:
|
||||
old := in.GetObjectKind().GroupVersionKind()
|
||||
defer in.GetObjectKind().SetGroupVersionKind(old)
|
||||
setTargetVersion(in, s, outVersion)
|
||||
return in, nil
|
||||
}
|
||||
t := reflect.TypeOf(in)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return nil, fmt.Errorf("only pointer types may be converted: %v", t)
|
||||
}
|
||||
|
||||
t = t.Elem()
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t)
|
||||
}
|
||||
|
||||
var kind unversioned.GroupVersionKind
|
||||
if unversionedKind, ok := s.unversionedTypes[t]; ok {
|
||||
kind = unversionedKind
|
||||
} else {
|
||||
kinds, ok := s.typeToGVK[t]
|
||||
if !ok || len(kinds) == 0 {
|
||||
return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion)
|
||||
}
|
||||
kind = kinds[0]
|
||||
}
|
||||
|
||||
outKind := outVersion.WithKind(kind.Kind)
|
||||
|
||||
inKinds, _, err := s.ObjectKinds(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := s.New(outKind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flags, meta := s.generateConvertMeta(inKinds[0].GroupVersion(), outVersion, in)
|
||||
if err := s.converter.Convert(in, out, flags, meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setTargetVersion(out, s, outVersion)
|
||||
return out, nil
|
||||
// returned. Passes target down to the conversion methods as the Context on the scope.
|
||||
func (s *Scheme) ConvertToVersion(in Object, target GroupVersioner) (Object, error) {
|
||||
return s.convertToVersion(true, in, target)
|
||||
}
|
||||
|
||||
// UnsafeConvertToVersion will convert in to the provided outVersion if such a conversion is possible,
|
||||
// UnsafeConvertToVersion will convert in to the provided target if such a conversion is possible,
|
||||
// but does not guarantee the output object does not share fields with the input object. It attempts to be as
|
||||
// efficient as possible when doing conversion.
|
||||
func (s *Scheme) UnsafeConvertToVersion(in Object, outVersion unversioned.GroupVersion) (Object, error) {
|
||||
switch t := in.(type) {
|
||||
case *Unknown:
|
||||
t.APIVersion = outVersion.String()
|
||||
return t, nil
|
||||
case *Unstructured:
|
||||
t.SetAPIVersion(outVersion.String())
|
||||
return t, nil
|
||||
case *UnstructuredList:
|
||||
t.SetAPIVersion(outVersion.String())
|
||||
return t, nil
|
||||
}
|
||||
func (s *Scheme) UnsafeConvertToVersion(in Object, target GroupVersioner) (Object, error) {
|
||||
return s.convertToVersion(false, in, target)
|
||||
}
|
||||
|
||||
// convertToVersion handles conversion with an optional copy.
|
||||
func (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (Object, error) {
|
||||
// determine the incoming kinds with as few allocations as possible.
|
||||
t := reflect.TypeOf(in)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
@@ -556,64 +493,69 @@ func (s *Scheme) UnsafeConvertToVersion(in Object, outVersion unversioned.GroupV
|
||||
}
|
||||
kinds, ok := s.typeToGVK[t]
|
||||
if !ok || len(kinds) == 0 {
|
||||
return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outVersion)
|
||||
return nil, NewNotRegisteredErr(unversioned.GroupVersionKind{}, t)
|
||||
}
|
||||
|
||||
// if the Go type is also registered to the destination kind, no conversion is necessary
|
||||
for i := range kinds {
|
||||
if kinds[i].Version == outVersion.Version && kinds[i].Group == outVersion.Group {
|
||||
setTargetKind(in, kinds[i])
|
||||
return in, nil
|
||||
gvk, ok := target.KindForGroupVersionKinds(kinds)
|
||||
if !ok {
|
||||
// TODO: should this be a typed error?
|
||||
return nil, fmt.Errorf("%v is not suitable for converting to %q", t, target)
|
||||
}
|
||||
|
||||
// target wants to use the existing type, set kind and return (no conversion necessary)
|
||||
for _, kind := range kinds {
|
||||
if gvk == kind {
|
||||
return copyAndSetTargetKind(copy, s, in, gvk)
|
||||
}
|
||||
}
|
||||
|
||||
// type is unversioned, no conversion necessary
|
||||
// it should be possible to avoid this allocation
|
||||
if unversionedKind, ok := s.unversionedTypes[t]; ok {
|
||||
kind := unversionedKind
|
||||
outKind := outVersion.WithKind(kind.Kind)
|
||||
setTargetKind(in, outKind)
|
||||
return in, nil
|
||||
if gvk, ok := target.KindForGroupVersionKinds([]unversioned.GroupVersionKind{unversionedKind}); ok {
|
||||
return copyAndSetTargetKind(copy, s, in, gvk)
|
||||
}
|
||||
return copyAndSetTargetKind(copy, s, in, unversionedKind)
|
||||
}
|
||||
|
||||
// allocate a new object as the target using the target kind
|
||||
// TODO: this should look in the target group version and find the first kind that matches, rather than the
|
||||
// first kind registered in typeToGVK
|
||||
kind := kinds[0]
|
||||
kind.Version = outVersion.Version
|
||||
kind.Group = outVersion.Group
|
||||
out, err := s.New(kind)
|
||||
out, err := s.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: try to avoid the allocations here - in fast paths we are not likely to need these flags or meta
|
||||
flags, meta := s.converter.DefaultMeta(t)
|
||||
if copy {
|
||||
copied, err := s.Copy(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in = copied
|
||||
}
|
||||
|
||||
flags, meta := s.generateConvertMeta(in)
|
||||
meta.Context = target
|
||||
if err := s.converter.Convert(in, out, flags, meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setTargetKind(out, kind)
|
||||
setTargetKind(out, gvk)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// generateConvertMeta constructs the meta value we pass to Convert.
|
||||
func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
|
||||
func (s *Scheme) generateConvertMeta(in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
|
||||
return s.converter.DefaultMeta(reflect.TypeOf(in))
|
||||
}
|
||||
|
||||
// setTargetVersion is deprecated and should be replaced by use of setTargetKind
|
||||
func setTargetVersion(obj Object, raw *Scheme, gv unversioned.GroupVersion) {
|
||||
if gv.Version == APIVersionInternal {
|
||||
// internal is a special case
|
||||
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
|
||||
return
|
||||
}
|
||||
if gvks, _, _ := raw.ObjectKinds(obj); len(gvks) > 0 {
|
||||
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvks[0].Kind})
|
||||
} else {
|
||||
obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version})
|
||||
// copyAndSetTargetKind performs a conditional copy before returning the object, or an error if copy was not successful.
|
||||
func copyAndSetTargetKind(copy bool, copier ObjectCopier, obj Object, kind unversioned.GroupVersionKind) (Object, error) {
|
||||
if copy {
|
||||
copied, err := copier.Copy(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj = copied
|
||||
}
|
||||
setTargetKind(obj, kind)
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version.
|
||||
|
||||
Reference in New Issue
Block a user