Remove reflection path in meta.Accessor

Callers are required to implement their interfaces, removes the potential for mistakes. We have a reflective test pkg/api/meta_test.go#TestAccessorImplementations that verifies that all objects registered to the scheme properly implement their interfaces.
parent 5e7c309b
...@@ -29,104 +29,66 @@ import ( ...@@ -29,104 +29,66 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
) )
// errNotList is returned when an object implements the Object style interfaces but not the List style
// interfaces.
var errNotList = fmt.Errorf("object does not implement the List interfaces")
// ListAccessor returns a List interface for the provided object or an error if the object does
// not provide List.
// IMPORTANT: Objects are a superset of lists, so all Objects return List metadata. Do not use this
// check to determine whether an object *is* a List.
// TODO: return bool instead of error
func ListAccessor(obj interface{}) (List, error) { func ListAccessor(obj interface{}) (List, error) {
if listMetaAccessor, ok := obj.(ListMetaAccessor); ok { switch t := obj.(type) {
if om := listMetaAccessor.GetListMeta(); om != nil { case List:
return om, nil return t, nil
case unversioned.List:
return t, nil
case ListMetaAccessor:
if m := t.GetListMeta(); m != nil {
return m, nil
} }
} return nil, errNotList
if listMetaAccessor, ok := obj.(unversioned.ListMetaAccessor); ok { case unversioned.ListMetaAccessor:
if om := listMetaAccessor.GetListMeta(); om != nil { if m := t.GetListMeta(); m != nil {
return om, nil return m, nil
} }
} return nil, errNotList
// we may get passed an object that is directly portable to List case Object:
if list, ok := obj.(List); ok { return t, nil
return list, nil case ObjectMetaAccessor:
} if m := t.GetObjectMeta(); m != nil {
glog.V(4).Infof("Calling ListAccessor on non-internal object: %v", reflect.TypeOf(obj)) return m, nil
// legacy path for objects that do not implement List and ListMetaAccessor via
// reflection - very slow code path.
v, err := conversion.EnforcePtr(obj)
if err != nil {
return nil, err
}
t := v.Type()
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), t, v.Interface())
}
a := &genericAccessor{}
listMeta := v.FieldByName("ListMeta")
if listMeta.IsValid() {
// look for the ListMeta fields
if err := extractFromListMeta(listMeta, a); err != nil {
return nil, fmt.Errorf("unable to find list fields on %#v: %v", listMeta, err)
} }
} else { return nil, errNotList
return nil, fmt.Errorf("unable to find listMeta on %#v", v) default:
return nil, errNotList
} }
return a, nil
} }
// errNotObject is returned when an object implements the List style interfaces but not the Object style
// interfaces.
var errNotObject = fmt.Errorf("object does not implement the Object interfaces")
// Accessor takes an arbitrary object pointer and returns meta.Interface. // Accessor takes an arbitrary object pointer and returns meta.Interface.
// obj must be a pointer to an API type. An error is returned if the minimum // obj must be a pointer to an API type. An error is returned if the minimum
// required fields are missing. Fields that are not required return the default // required fields are missing. Fields that are not required return the default
// value and are a no-op if set. // value and are a no-op if set.
// TODO: return bool instead of error
func Accessor(obj interface{}) (Object, error) { func Accessor(obj interface{}) (Object, error) {
if objectMetaAccessor, ok := obj.(ObjectMetaAccessor); ok { switch t := obj.(type) {
if om := objectMetaAccessor.GetObjectMeta(); om != nil { case Object:
return om, nil return t, nil
} case ObjectMetaAccessor:
} if m := t.GetObjectMeta(); m != nil {
// we may get passed an object that is directly portable to Object return m, nil
if object, ok := obj.(Object); ok {
return object, nil
}
glog.V(4).Infof("Calling Accessor on non-internal object: %v", reflect.TypeOf(obj))
// legacy path for objects that do not implement Object and ObjectMetaAccessor via
// reflection - very slow code path.
v, err := conversion.EnforcePtr(obj)
if err != nil {
return nil, err
}
t := v.Type()
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), t, v.Interface())
}
typeMeta := v.FieldByName("TypeMeta")
if !typeMeta.IsValid() {
return nil, fmt.Errorf("struct %v lacks embedded TypeMeta type", t)
}
a := &genericAccessor{}
if err := extractFromTypeMeta(typeMeta, a); err != nil {
return nil, fmt.Errorf("unable to find type fields on %#v: %v", typeMeta, err)
}
objectMeta := v.FieldByName("ObjectMeta")
if objectMeta.IsValid() {
// look for the ObjectMeta fields
if err := extractFromObjectMeta(objectMeta, a); err != nil {
return nil, fmt.Errorf("unable to find object fields on %#v: %v", objectMeta, err)
}
} else {
listMeta := v.FieldByName("ListMeta")
if listMeta.IsValid() {
// look for the ListMeta fields
if err := extractFromListMeta(listMeta, a); err != nil {
return nil, fmt.Errorf("unable to find list fields on %#v: %v", listMeta, err)
}
} else {
// look for the older TypeMeta with all metadata
if err := extractFromObjectMeta(typeMeta, a); err != nil {
return nil, fmt.Errorf("unable to find object fields on %#v: %v", typeMeta, err)
}
} }
return nil, errNotObject
case List, unversioned.List, ListMetaAccessor, unversioned.ListMetaAccessor:
return nil, errNotObject
default:
return nil, errNotObject
} }
return a, nil
} }
// TypeAccessor returns an interface that allows retrieving and modifying the APIVersion // TypeAccessor returns an interface that allows retrieving and modifying the APIVersion
...@@ -283,7 +245,7 @@ func (resourceAccessor) SetUID(obj runtime.Object, uid types.UID) error { ...@@ -283,7 +245,7 @@ func (resourceAccessor) SetUID(obj runtime.Object, uid types.UID) error {
} }
func (resourceAccessor) SelfLink(obj runtime.Object) (string, error) { func (resourceAccessor) SelfLink(obj runtime.Object) (string, error) {
accessor, err := Accessor(obj) accessor, err := ListAccessor(obj)
if err != nil { if err != nil {
return "", err return "", err
} }
...@@ -291,7 +253,7 @@ func (resourceAccessor) SelfLink(obj runtime.Object) (string, error) { ...@@ -291,7 +253,7 @@ func (resourceAccessor) SelfLink(obj runtime.Object) (string, error) {
} }
func (resourceAccessor) SetSelfLink(obj runtime.Object, selfLink string) error { func (resourceAccessor) SetSelfLink(obj runtime.Object, selfLink string) error {
accessor, err := Accessor(obj) accessor, err := ListAccessor(obj)
if err != nil { if err != nil {
return err return err
} }
...@@ -334,7 +296,7 @@ func (resourceAccessor) SetAnnotations(obj runtime.Object, annotations map[strin ...@@ -334,7 +296,7 @@ func (resourceAccessor) SetAnnotations(obj runtime.Object, annotations map[strin
} }
func (resourceAccessor) ResourceVersion(obj runtime.Object) (string, error) { func (resourceAccessor) ResourceVersion(obj runtime.Object) (string, error) {
accessor, err := Accessor(obj) accessor, err := ListAccessor(obj)
if err != nil { if err != nil {
return "", err return "", err
} }
...@@ -342,7 +304,7 @@ func (resourceAccessor) ResourceVersion(obj runtime.Object) (string, error) { ...@@ -342,7 +304,7 @@ func (resourceAccessor) ResourceVersion(obj runtime.Object) (string, error) {
} }
func (resourceAccessor) SetResourceVersion(obj runtime.Object, version string) error { func (resourceAccessor) SetResourceVersion(obj runtime.Object, version string) error {
accessor, err := Accessor(obj) accessor, err := ListAccessor(obj)
if err != nil { if err != nil {
return err return err
} }
...@@ -603,54 +565,3 @@ func extractFromTypeMeta(v reflect.Value, a *genericAccessor) error { ...@@ -603,54 +565,3 @@ func extractFromTypeMeta(v reflect.Value, a *genericAccessor) error {
} }
return nil return nil
} }
// extractFromObjectMeta extracts pointers to metadata fields from an object
func extractFromObjectMeta(v reflect.Value, a *genericAccessor) error {
if err := runtime.FieldPtr(v, "Namespace", &a.namespace); err != nil {
return err
}
if err := runtime.FieldPtr(v, "Name", &a.name); err != nil {
return err
}
if err := runtime.FieldPtr(v, "GenerateName", &a.generateName); err != nil {
return err
}
if err := runtime.FieldPtr(v, "UID", &a.uid); err != nil {
return err
}
if err := runtime.FieldPtr(v, "ResourceVersion", &a.resourceVersion); err != nil {
return err
}
if err := runtime.FieldPtr(v, "SelfLink", &a.selfLink); err != nil {
return err
}
if err := runtime.FieldPtr(v, "Labels", &a.labels); err != nil {
return err
}
if err := runtime.FieldPtr(v, "Annotations", &a.annotations); err != nil {
return err
}
if err := runtime.FieldPtr(v, "Finalizers", &a.finalizers); err != nil {
return err
}
ownerReferences := v.FieldByName("OwnerReferences")
if !ownerReferences.IsValid() {
return fmt.Errorf("struct %#v lacks OwnerReferences type", v)
}
if ownerReferences.Kind() != reflect.Slice {
return fmt.Errorf("expect %v to be a slice", ownerReferences.Kind())
}
a.ownerReferences = ownerReferences.Addr()
return nil
}
// extractFromObjectMeta extracts pointers to metadata fields from a list object
func extractFromListMeta(v reflect.Value, a *genericAccessor) error {
if err := runtime.FieldPtr(v, "ResourceVersion", &a.resourceVersion); err != nil {
return err
}
if err := runtime.FieldPtr(v, "SelfLink", &a.selfLink); err != nil {
return err
}
return nil
}
...@@ -45,10 +45,6 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) { ...@@ -45,10 +45,6 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) {
// Don't make a reference to a reference. // Don't make a reference to a reference.
return ref, nil return ref, nil
} }
meta, err := meta.Accessor(obj)
if err != nil {
return nil, err
}
gvk := obj.GetObjectKind().GroupVersionKind() gvk := obj.GetObjectKind().GroupVersionKind()
...@@ -64,10 +60,22 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) { ...@@ -64,10 +60,22 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) {
kind = gvks[0].Kind kind = gvks[0].Kind
} }
// An object that implements only List has enough metadata to build a reference
var listMeta meta.List
objectMeta, err := meta.Accessor(obj)
if err != nil {
listMeta, err = meta.ListAccessor(obj)
if err != nil {
return nil, err
}
} else {
listMeta = objectMeta
}
// if the object referenced is actually persisted, we can also get version from meta // if the object referenced is actually persisted, we can also get version from meta
version := gvk.GroupVersion().String() version := gvk.GroupVersion().String()
if len(version) == 0 { if len(version) == 0 {
selfLink := meta.GetSelfLink() selfLink := listMeta.GetSelfLink()
if len(selfLink) == 0 { if len(selfLink) == 0 {
return nil, ErrNoSelfLink return nil, ErrNoSelfLink
} }
...@@ -83,13 +91,22 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) { ...@@ -83,13 +91,22 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) {
version = parts[2] version = parts[2]
} }
// only has list metadata
if objectMeta == nil {
return &ObjectReference{
Kind: kind,
APIVersion: version,
ResourceVersion: listMeta.GetResourceVersion(),
}, nil
}
return &ObjectReference{ return &ObjectReference{
Kind: kind, Kind: kind,
APIVersion: version, APIVersion: version,
Name: meta.GetName(), Name: objectMeta.GetName(),
Namespace: meta.GetNamespace(), Namespace: objectMeta.GetNamespace(),
UID: meta.GetUID(), UID: objectMeta.GetUID(),
ResourceVersion: meta.GetResourceVersion(), ResourceVersion: objectMeta.GetResourceVersion(),
}, nil }, nil
} }
......
...@@ -175,9 +175,9 @@ func (g *GenericEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.Us ...@@ -175,9 +175,9 @@ func (g *GenericEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.Us
if err != nil { if err != nil {
return result, fmt.Errorf("%s: Failed to list %v: %v", g.Name, g.GroupKind(), err) return result, fmt.Errorf("%s: Failed to list %v: %v", g.Name, g.GroupKind(), err)
} }
_, err = meta.Accessor(list) _, err = meta.ListAccessor(list)
if err != nil { if err != nil {
return result, fmt.Errorf("%s: Unable to understand list result %#v", g.Name, list) return result, fmt.Errorf("%s: Unable to understand list result, does not appear to be a list %#v", g.Name, list)
} }
items, err := meta.ExtractList(list) items, err := meta.ExtractList(list)
if err != nil { if err != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment