mirror of
https://github.com/kubernetes/sample-controller.git
synced 2026-04-12 00:00:26 +08:00
Merge pull request #55650 from smarterclayton/make_unstructured_conversion
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Move unstructured conversion into pkg/runtime Scheme conversion should support unstructured conversion natively to allow going from unstructured to typed and back. It is not a higher level responsibility to do that conversion because the scheme is the only one who knows what types it supports. @liggitt @kubernetes/sig-cli-api-reviews I am going to make Scheme support unstructured in ConvertToVersion and Convert, which means resource.Builder and the CLI can get simpler for all existing use cases where versioned and unstructured need to coexist. Kubernetes-commit: a67abac7654cc3e05618d41191d71730cf9565ac
This commit is contained in:
+119
-25
@@ -217,19 +217,22 @@ func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type {
|
||||
return s.gvkToType
|
||||
}
|
||||
|
||||
// ObjectKind returns the group,version,kind of the go object and true if this object
|
||||
// is considered unversioned, or an error if it's not a pointer or is unregistered.
|
||||
func (s *Scheme) ObjectKind(obj Object) (schema.GroupVersionKind, bool, error) {
|
||||
gvks, unversionedType, err := s.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return schema.GroupVersionKind{}, false, err
|
||||
}
|
||||
return gvks[0], unversionedType, nil
|
||||
}
|
||||
|
||||
// ObjectKinds returns all possible group,version,kind of the go object, true if the
|
||||
// object is considered unversioned, or an error if it's not a pointer or is unregistered.
|
||||
func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error) {
|
||||
// Unstructured objects are always considered to have their declared GVK
|
||||
if _, ok := obj.(Unstructured); ok {
|
||||
// we require that the GVK be populated in order to recognize the object
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
if len(gvk.Kind) == 0 {
|
||||
return nil, false, NewMissingKindErr("unstructured object has no kind")
|
||||
}
|
||||
if len(gvk.Version) == 0 {
|
||||
return nil, false, NewMissingVersionErr("unstructured object has no version")
|
||||
}
|
||||
return []schema.GroupVersionKind{gvk}, false, nil
|
||||
}
|
||||
|
||||
v, err := conversion.EnforcePtr(obj)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
@@ -338,7 +341,7 @@ func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Similar to AddConversionFuncs, but registers conversion functions that were
|
||||
// AddGeneratedConversionFuncs registers conversion functions that were
|
||||
// automatically generated.
|
||||
func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) error {
|
||||
for _, f := range conversionFuncs {
|
||||
@@ -396,10 +399,68 @@ func (s *Scheme) Default(src Object) {
|
||||
// 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). The
|
||||
// context interface is passed to the convertor.
|
||||
// TODO: identify whether context should be hidden, or behind a formal context/scope
|
||||
// interface
|
||||
// context interface is passed to the convertor. Convert also supports Unstructured
|
||||
// types and will convert them intelligently.
|
||||
func (s *Scheme) Convert(in, out interface{}, context interface{}) error {
|
||||
unstructuredIn, okIn := in.(Unstructured)
|
||||
unstructuredOut, okOut := out.(Unstructured)
|
||||
switch {
|
||||
case okIn && okOut:
|
||||
// converting unstructured input to an unstructured output is a straight copy - unstructured
|
||||
// is a "smart holder" and the contents are passed by reference between the two objects
|
||||
unstructuredOut.SetUnstructuredContent(unstructuredIn.UnstructuredContent())
|
||||
return nil
|
||||
|
||||
case okOut:
|
||||
// if the output is an unstructured object, use the standard Go type to unstructured
|
||||
// conversion. The object must not be internal.
|
||||
obj, ok := in.(Object)
|
||||
if !ok {
|
||||
return fmt.Errorf("unable to convert object type %T to Unstructured, must be a runtime.Object", in)
|
||||
}
|
||||
gvks, unversioned, err := s.ObjectKinds(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gvk := gvks[0]
|
||||
|
||||
// if no conversion is necessary, convert immediately
|
||||
if unversioned || gvk.Version != APIVersionInternal {
|
||||
content, err := DefaultUnstructuredConverter.ToUnstructured(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
unstructuredOut.SetUnstructuredContent(content)
|
||||
return nil
|
||||
}
|
||||
|
||||
// attempt to convert the object to an external version first.
|
||||
target, ok := context.(GroupVersioner)
|
||||
if !ok {
|
||||
return fmt.Errorf("unable to convert the internal object type %T to Unstructured without providing a preferred version to convert to", in)
|
||||
}
|
||||
// Convert is implicitly unsafe, so we don't need to perform a safe conversion
|
||||
versioned, err := s.UnsafeConvertToVersion(obj, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content, err := DefaultUnstructuredConverter.ToUnstructured(versioned)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
unstructuredOut.SetUnstructuredContent(content)
|
||||
return nil
|
||||
|
||||
case okIn:
|
||||
// converting an unstructured object to any type is modeled by first converting
|
||||
// the input to a versioned type, then running standard conversions
|
||||
typed, err := s.unstructuredToTyped(unstructuredIn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in = typed
|
||||
}
|
||||
|
||||
flags, meta := s.generateConvertMeta(in)
|
||||
meta.Context = context
|
||||
if flags == 0 {
|
||||
@@ -408,8 +469,8 @@ func (s *Scheme) Convert(in, out interface{}, context interface{}) error {
|
||||
return s.converter.Convert(in, out, flags, meta)
|
||||
}
|
||||
|
||||
// Converts the given field label and value for an kind field selector from
|
||||
// versioned representation to an unversioned one.
|
||||
// ConvertFieldLabel alters the given field label and value for an kind field selector from
|
||||
// versioned representation to an unversioned one or returns an error.
|
||||
func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
|
||||
if s.fieldLabelConversionFuncs[version] == nil {
|
||||
return DefaultMetaV1FieldSelectorConversion(label, value)
|
||||
@@ -439,15 +500,30 @@ func (s *Scheme) UnsafeConvertToVersion(in Object, target GroupVersioner) (Objec
|
||||
|
||||
// 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 {
|
||||
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 t reflect.Type
|
||||
|
||||
if u, ok := in.(Unstructured); ok {
|
||||
typed, err := s.unstructuredToTyped(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
in = typed
|
||||
// unstructuredToTyped returns an Object, which must be a pointer to a struct.
|
||||
t = reflect.TypeOf(in).Elem()
|
||||
|
||||
} else {
|
||||
// determine the incoming kinds with as few allocations as possible.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
kinds, ok := s.typeToGVK[t]
|
||||
if !ok || len(kinds) == 0 {
|
||||
return nil, NewNotRegisteredErrForType(t)
|
||||
@@ -463,7 +539,6 @@ func (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (
|
||||
}
|
||||
return copyAndSetTargetKind(copy, in, unversionedKind)
|
||||
}
|
||||
|
||||
return nil, NewNotRegisteredErrForTarget(t, target)
|
||||
}
|
||||
|
||||
@@ -501,6 +576,25 @@ func (s *Scheme) convertToVersion(copy bool, in Object, target GroupVersioner) (
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// unstructuredToTyped attempts to transform an unstructured object to a typed
|
||||
// object if possible. It will return an error if conversion is not possible, or the versioned
|
||||
// Go form of the object. Note that this conversion will lose fields.
|
||||
func (s *Scheme) unstructuredToTyped(in Unstructured) (Object, error) {
|
||||
// the type must be something we recognize
|
||||
gvks, _, err := s.ObjectKinds(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typed, err := s.New(gvks[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := DefaultUnstructuredConverter.FromUnstructured(in.UnstructuredContent(), typed); err != nil {
|
||||
return nil, fmt.Errorf("unable to convert unstructured object to %v: %v", gvks[0], err)
|
||||
}
|
||||
return typed, nil
|
||||
}
|
||||
|
||||
// generateConvertMeta constructs the meta value we pass to Convert.
|
||||
func (s *Scheme) generateConvertMeta(in interface{}) (conversion.FieldMatchingFlags, *conversion.Meta) {
|
||||
return s.converter.DefaultMeta(reflect.TypeOf(in))
|
||||
|
||||
Reference in New Issue
Block a user