diff --git a/pkg/api/meta/interfaces.go b/pkg/api/meta/interfaces.go index 02c8ab512f..17161d1e47 100644 --- a/pkg/api/meta/interfaces.go +++ b/pkg/api/meta/interfaces.go @@ -32,17 +32,16 @@ type VersionInterfaces struct { // 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 // a default value. +// TODO: rename to ObjectInterface when we clear up these interfaces. type Interface interface { + TypeInterface + Namespace() string SetNamespace(namespace string) Name() string SetName(name string) UID() types.UID SetUID(uid types.UID) - APIVersion() string - SetAPIVersion(version string) - Kind() string - SetKind(kind string) ResourceVersion() string SetResourceVersion(version string) SelfLink() string @@ -53,6 +52,14 @@ type Interface interface { 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 // 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 diff --git a/pkg/api/meta/meta.go b/pkg/api/meta/meta.go index b16e3a192e..1353083474 100644 --- a/pkg/api/meta/meta.go +++ b/pkg/api/meta/meta.go @@ -74,6 +74,32 @@ func Accessor(obj interface{}) (Interface, error) { 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 // or manipulate resource version on objects derived from core API // metadata concepts. diff --git a/pkg/api/meta/meta_test.go b/pkg/api/meta/meta_test.go index d8b3f0fe82..b357fca60d 100644 --- a/pkg/api/meta/meta_test.go +++ b/pkg/api/meta/meta_test.go @@ -79,6 +79,17 @@ func TestGenericTypeMeta(t *testing.T) { 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.SetName("bar") accessor.SetUID("other") @@ -109,6 +120,15 @@ func TestGenericTypeMeta(t *testing.T) { if e, a := "google.com", j.SelfLink; 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 { diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index be5da5005b..85b212d569 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -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 { fuzzerFor(t, forVersion, rand.NewSource(seed)).Fuzz(item) - j, err := meta.Accessor(item) + j, err := meta.TypeAccessor(item) if err != nil { t.Fatalf("Unexpected error %v for %#v", err, item) } @@ -264,7 +264,7 @@ func TestRoundTripTypes(t *testing.T) { if err != nil { 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) } roundTripSame(t, item) diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 0a3f8d040a..447ad523f6 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -360,6 +360,9 @@ func (s *Scheme) Decode(data []byte) (Object, error) { // pointer to an api type. // If obj's APIVersion doesn't match that in data, an attempt will be made to convert // 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 { return s.raw.DecodeInto(data, obj) }