Add TypeAccessor to api/meta for objects without Object/ListMeta

Adding objects that have TypeMeta (use runtime.Scheme) but do not
expose ObjectMeta/ListMeta (because they are not Kube API objects)
and wanted to get the simpler access path for in memory objects.
This commit is contained in:
Clayton Coleman 2015-01-18 21:22:55 -05:00
parent eeb712d163
commit 5f6caaba2e
5 changed files with 62 additions and 6 deletions

View File

@ -32,17 +32,16 @@ type VersionInterfaces struct {
// internal API objects. Attempting to set or retrieve a field on an object that does // internal API objects. Attempting to set or retrieve a field on an object that does
// not support that field (Name, UID, Namespace on lists) will be a no-op and return // not support that field (Name, UID, Namespace on lists) will be a no-op and return
// a default value. // a default value.
// TODO: rename to ObjectInterface when we clear up these interfaces.
type Interface interface { type Interface interface {
TypeInterface
Namespace() string Namespace() string
SetNamespace(namespace string) SetNamespace(namespace string)
Name() string Name() string
SetName(name string) SetName(name string)
UID() types.UID UID() types.UID
SetUID(uid types.UID) SetUID(uid types.UID)
APIVersion() string
SetAPIVersion(version string)
Kind() string
SetKind(kind string)
ResourceVersion() string ResourceVersion() string
SetResourceVersion(version string) SetResourceVersion(version string)
SelfLink() string SelfLink() string
@ -53,6 +52,14 @@ type Interface interface {
SetAnnotations(annotations map[string]string) SetAnnotations(annotations map[string]string)
} }
// TypeInterface exposes the type and APIVersion of versioned or internal API objects.
type TypeInterface interface {
APIVersion() string
SetAPIVersion(version string)
Kind() string
SetKind(kind string)
}
// MetadataAccessor lets you work with object and list metadata from any of the versioned or // MetadataAccessor lets you work with object and list metadata from any of the versioned or
// internal API objects. Attempting to set or retrieve a field on an object that does // internal API objects. Attempting to set or retrieve a field on an object that does
// not support that field (Name, UID, Namespace on lists) will be a no-op and return // not support that field (Name, UID, Namespace on lists) will be a no-op and return

View File

@ -74,6 +74,32 @@ func Accessor(obj interface{}) (Interface, error) {
return a, nil return a, nil
} }
// TypeAccessor returns an interface that allows retrieving and modifying the APIVersion
// and Kind of an in-memory internal object.
// TODO: this interface is used to test code that does not have ObjectMeta or ListMeta
// in round tripping (objects which can use apiVersion/kind, but do not fit the Kube
// api conventions).
func TypeAccessor(obj interface{}) (TypeInterface, error) {
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)
}
return a, nil
}
// NewAccessor returns a MetadataAccessor that can retrieve // NewAccessor returns a MetadataAccessor that can retrieve
// or manipulate resource version on objects derived from core API // or manipulate resource version on objects derived from core API
// metadata concepts. // metadata concepts.

View File

@ -79,6 +79,17 @@ func TestGenericTypeMeta(t *testing.T) {
t.Errorf("expected %v, got %v", e, a) t.Errorf("expected %v, got %v", e, a)
} }
typeAccessor, err := TypeAccessor(&j)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if e, a := "a", accessor.APIVersion(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "b", accessor.Kind(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
accessor.SetNamespace("baz") accessor.SetNamespace("baz")
accessor.SetName("bar") accessor.SetName("bar")
accessor.SetUID("other") accessor.SetUID("other")
@ -109,6 +120,15 @@ func TestGenericTypeMeta(t *testing.T) {
if e, a := "google.com", j.SelfLink; e != a { if e, a := "google.com", j.SelfLink; e != a {
t.Errorf("expected %v, got %v", e, a) t.Errorf("expected %v, got %v", e, a)
} }
typeAccessor.SetAPIVersion("d")
typeAccessor.SetKind("e")
if e, a := "d", j.APIVersion; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "e", j.Kind; e != a {
t.Errorf("expected %v, got %v", e, a)
}
} }
type InternalTypeMeta struct { type InternalTypeMeta struct {

View File

@ -163,7 +163,7 @@ func fuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, seed int64) runtime.Object { func fuzzInternalObject(t *testing.T, forVersion string, item runtime.Object, seed int64) runtime.Object {
fuzzerFor(t, forVersion, rand.NewSource(seed)).Fuzz(item) fuzzerFor(t, forVersion, rand.NewSource(seed)).Fuzz(item)
j, err := meta.Accessor(item) j, err := meta.TypeAccessor(item)
if err != nil { if err != nil {
t.Fatalf("Unexpected error %v for %#v", err, item) t.Fatalf("Unexpected error %v for %#v", err, item)
} }
@ -264,7 +264,7 @@ func TestRoundTripTypes(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Couldn't make a %v? %v", kind, err) t.Fatalf("Couldn't make a %v? %v", kind, err)
} }
if _, err := meta.Accessor(item); err != nil { if _, err := meta.TypeAccessor(item); err != nil {
t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err) t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err)
} }
roundTripSame(t, item) roundTripSame(t, item)

View File

@ -360,6 +360,9 @@ func (s *Scheme) Decode(data []byte) (Object, error) {
// pointer to an api type. // pointer to an api type.
// If obj's APIVersion doesn't match that in data, an attempt will be made to convert // If obj's APIVersion doesn't match that in data, an attempt will be made to convert
// data into obj's version. // data into obj's version.
// TODO: allow Decode/DecodeInto to take a default apiVersion and a default kind, to
// be applied if the provided object does not have either field (integrate external
// apis into the decoding scheme).
func (s *Scheme) DecodeInto(data []byte, obj Object) error { func (s *Scheme) DecodeInto(data []byte, obj Object) error {
return s.raw.DecodeInto(data, obj) return s.raw.DecodeInto(data, obj)
} }