Skip to content

Commit

Permalink
Add TypeAccessor to api/meta for objects without Object/ListMeta
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
smarterclayton committed Jan 20, 2015
1 parent eeb712d commit 5f6caab
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 6 deletions.
15 changes: 11 additions & 4 deletions pkg/api/meta/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
26 changes: 26 additions & 0 deletions pkg/api/meta/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 20 additions & 0 deletions pkg/api/meta/meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions pkg/api/serialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions pkg/runtime/scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down

0 comments on commit 5f6caab

Please sign in to comment.