diff --git a/pkg/api/meta/meta.go b/pkg/api/meta/meta.go index 12d9ce3c044..a4570987f5a 100644 --- a/pkg/api/meta/meta.go +++ b/pkg/api/meta/meta.go @@ -24,9 +24,10 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) -// FindAccessor takes an arbitary api type, returns pointer to its TypeMeta field. -// obj must be a pointer to an api type. -func FindAccessor(obj runtime.Object) (Accessor, error) { +// FindAccessor takes an arbitary type and returns an Accessor interfaces. +// obj must be a pointer to an api type. An error is returned if the minimum +// required fields are missing. +func FindAccessor(obj interface{}) (Accessor, error) { v, err := conversion.EnforcePtr(obj) if err != nil { return nil, err @@ -36,27 +37,51 @@ func FindAccessor(obj runtime.Object) (Accessor, error) { if v.Kind() != reflect.Struct { return nil, fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), name, v.Interface()) } + typeMeta := v.FieldByName("TypeMeta") if !typeMeta.IsValid() { return nil, fmt.Errorf("struct %v lacks embedded TypeMeta type", name) } - g, err := newGenericTypeMeta(typeMeta) - if err != nil { - return nil, err + + a := &genericAccessor{} + if err := findTypeMeta(typeMeta, a); err != nil { + return nil, fmt.Errorf("unable to find type fields on %#v", typeMeta) } - return g, nil + + objectMeta := v.FieldByName("ObjectMeta") + if objectMeta.IsValid() { + // look for the ObjectMeta fields + if err := findObjectMeta(objectMeta, a); err != nil { + return nil, fmt.Errorf("unable to find object fields on %#v", objectMeta) + } + } else { + listMeta := v.FieldByName("ListMeta") + if listMeta.IsValid() { + // look for the ListMeta fields + if err := findListMeta(listMeta, a); err != nil { + return nil, fmt.Errorf("unable to find list fields on %#v", listMeta) + } + } else { + // look for the older TypeMeta with all metadata + if err := findObjectMeta(typeMeta, a); err != nil { + return nil, fmt.Errorf("unable to find object fields on %#v", typeMeta) + } + } + } + + return a, nil } // NewResourceVersioner returns a ResourceVersioner that can set or // retrieve ResourceVersion on objects derived from TypeMeta. func NewResourceVersioner() runtime.ResourceVersioner { - return typeMetaModifier{} + return resourceAccessor{} } -// typeMetaModifier implements ResourceVersioner and SelfLinker. -type typeMetaModifier struct{} +// resourceAccessor implements ResourceVersioner and SelfLinker. +type resourceAccessor struct{} -func (v typeMetaModifier) ResourceVersion(obj runtime.Object) (string, error) { +func (v resourceAccessor) ResourceVersion(obj runtime.Object) (string, error) { accessor, err := FindAccessor(obj) if err != nil { return "", err @@ -64,7 +89,7 @@ func (v typeMetaModifier) ResourceVersion(obj runtime.Object) (string, error) { return accessor.ResourceVersion(), nil } -func (v typeMetaModifier) SetResourceVersion(obj runtime.Object, version string) error { +func (v resourceAccessor) SetResourceVersion(obj runtime.Object, version string) error { accessor, err := FindAccessor(obj) if err != nil { return err @@ -73,7 +98,7 @@ func (v typeMetaModifier) SetResourceVersion(obj runtime.Object, version string) return nil } -func (v typeMetaModifier) Name(obj runtime.Object) (string, error) { +func (v resourceAccessor) Name(obj runtime.Object) (string, error) { accessor, err := FindAccessor(obj) if err != nil { return "", err @@ -81,7 +106,7 @@ func (v typeMetaModifier) Name(obj runtime.Object) (string, error) { return accessor.Name(), nil } -func (v typeMetaModifier) SelfLink(obj runtime.Object) (string, error) { +func (v resourceAccessor) SelfLink(obj runtime.Object) (string, error) { accessor, err := FindAccessor(obj) if err != nil { return "", err @@ -89,7 +114,7 @@ func (v typeMetaModifier) SelfLink(obj runtime.Object) (string, error) { return accessor.SelfLink(), nil } -func (v typeMetaModifier) SetSelfLink(obj runtime.Object, selfLink string) error { +func (v resourceAccessor) SetSelfLink(obj runtime.Object, selfLink string) error { accessor, err := FindAccessor(obj) if err != nil { return err @@ -100,7 +125,7 @@ func (v typeMetaModifier) SetSelfLink(obj runtime.Object, selfLink string) error // NewSelfLinker returns a SelfLinker that works on all TypeMeta SelfLink fields. func NewSelfLinker() runtime.SelfLinker { - return typeMetaModifier{} + return resourceAccessor{} } // Accessor lets you work with object metadata from any of the versioned or @@ -120,7 +145,9 @@ type Accessor interface { SetSelfLink(selfLink string) } -type genericTypeMeta struct { +// genericAccessor contains pointers to strings that can modify an arbitrary +// struct and implements the Accessor interface. +type genericAccessor struct { name *string uid *string apiVersion *string @@ -129,52 +156,64 @@ type genericTypeMeta struct { selfLink *string } -func (g genericTypeMeta) Name() string { - return *g.name +func (a genericAccessor) Name() string { + if a.name == nil { + return "" + } + return *a.name } -func (g genericTypeMeta) SetName(name string) { - *g.name = name +func (a genericAccessor) SetName(name string) { + if a.name == nil { + return + } + *a.name = name } -func (g genericTypeMeta) UID() string { - return *g.uid +func (a genericAccessor) UID() string { + if a.uid == nil { + return "" + } + return *a.uid } -func (g genericTypeMeta) SetUID(uid string) { - *g.uid = uid +func (a genericAccessor) SetUID(uid string) { + if a.uid == nil { + return + } + *a.uid = uid } -func (g genericTypeMeta) APIVersion() string { - return *g.apiVersion +func (a genericAccessor) APIVersion() string { + return *a.apiVersion } -func (g genericTypeMeta) SetAPIVersion(version string) { - *g.apiVersion = version +func (a genericAccessor) SetAPIVersion(version string) { + *a.apiVersion = version } -func (g genericTypeMeta) Kind() string { - return *g.kind +func (a genericAccessor) Kind() string { + return *a.kind } -func (g genericTypeMeta) SetKind(kind string) { - *g.kind = kind +func (a genericAccessor) SetKind(kind string) { + *a.kind = kind } -func (g genericTypeMeta) ResourceVersion() string { - return *g.resourceVersion +func (a genericAccessor) ResourceVersion() string { + return *a.resourceVersion } -func (g genericTypeMeta) SetResourceVersion(version string) { - *g.resourceVersion = version +func (a genericAccessor) SetResourceVersion(version string) { + *a.resourceVersion = version } -func (g genericTypeMeta) SelfLink() string { - return *g.selfLink +func (a genericAccessor) SelfLink() string { + return *a.selfLink } -func (g genericTypeMeta) SetSelfLink(selfLink string) { - *g.selfLink = selfLink +func (a genericAccessor) SetSelfLink(selfLink string) { + *a.selfLink = selfLink } // fieldPtr puts the address of fieldName, which must be a member of v, @@ -202,28 +241,41 @@ func fieldPtr(v reflect.Value, fieldName string, dest interface{}) error { return fmt.Errorf("Couldn't assign/convert %v to %v", field.Type(), v.Type()) } -// newGenericTypeMeta creates a new generic TypeMeta from v, which must be an -// addressable/setable reflect.Value having the same fields as api.TypeMeta. -// Returns an error if this isn't the case. -func newGenericTypeMeta(v reflect.Value) (genericTypeMeta, error) { - g := genericTypeMeta{} - if err := fieldPtr(v, "Name", &g.name); err != nil { - return g, err +// findTypeMeta extracts pointers to version and kind fields from an object +func findTypeMeta(v reflect.Value, a *genericAccessor) error { + if err := fieldPtr(v, "APIVersion", &a.apiVersion); err != nil { + return err } - if err := fieldPtr(v, "UID", &g.uid); err != nil { - return g, err + if err := fieldPtr(v, "Kind", &a.kind); err != nil { + return err } - if err := fieldPtr(v, "APIVersion", &g.apiVersion); err != nil { - return g, err - } - if err := fieldPtr(v, "Kind", &g.kind); err != nil { - return g, err - } - if err := fieldPtr(v, "ResourceVersion", &g.resourceVersion); err != nil { - return g, err - } - if err := fieldPtr(v, "SelfLink", &g.selfLink); err != nil { - return g, err - } - return g, nil + return nil +} + +// findObjectMeta extracts pointers to metadata fields from an object +func findObjectMeta(v reflect.Value, a *genericAccessor) error { + if err := fieldPtr(v, "Name", &a.name); err != nil { + return err + } + if err := fieldPtr(v, "UID", &a.uid); err != nil { + return err + } + if err := fieldPtr(v, "ResourceVersion", &a.resourceVersion); err != nil { + return err + } + if err := fieldPtr(v, "SelfLink", &a.selfLink); err != nil { + return err + } + return nil +} + +// findObjectMeta extracts pointers to metadata fields from a list object +func findListMeta(v reflect.Value, a *genericAccessor) error { + if err := fieldPtr(v, "ResourceVersion", &a.resourceVersion); err != nil { + return err + } + if err := fieldPtr(v, "SelfLink", &a.selfLink); err != nil { + return err + } + return nil } diff --git a/pkg/api/meta/meta_test.go b/pkg/api/meta/meta_test.go index 1acb7ebf65f..094b231f015 100644 --- a/pkg/api/meta/meta_test.go +++ b/pkg/api/meta/meta_test.go @@ -17,7 +17,6 @@ limitations under the License. package meta import ( - "reflect" "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" @@ -34,47 +33,50 @@ func TestGenericTypeMeta(t *testing.T) { ResourceVersion string `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` } - j := TypeMeta{ - Name: "foo", - UID: "uid", - APIVersion: "a", - Kind: "b", - ResourceVersion: "1", - SelfLink: "some/place/only/we/know", + type Object struct { + TypeMeta `json:",inline" yaml:",inline"` } - g, err := newGenericTypeMeta(reflect.ValueOf(&j).Elem()) + j := Object{ + TypeMeta{ + Name: "foo", + UID: "uid", + APIVersion: "a", + Kind: "b", + ResourceVersion: "1", + SelfLink: "some/place/only/we/know", + }, + } + accessor, err := FindAccessor(&j) if err != nil { t.Fatalf("new err: %v", err) } - // Prove g supports Accessor. - jbi := Accessor(g) - if e, a := "foo", jbi.Name(); e != a { + if e, a := "foo", accessor.Name(); e != a { t.Errorf("expected %v, got %v", e, a) } - if e, a := "uid", jbi.UID(); e != a { + if e, a := "uid", accessor.UID(); e != a { t.Errorf("expected %v, got %v", e, a) } - if e, a := "a", jbi.APIVersion(); e != a { + if e, a := "a", accessor.APIVersion(); e != a { t.Errorf("expected %v, got %v", e, a) } - if e, a := "b", jbi.Kind(); e != a { + if e, a := "b", accessor.Kind(); e != a { t.Errorf("expected %v, got %v", e, a) } - if e, a := "1", jbi.ResourceVersion(); e != a { + if e, a := "1", accessor.ResourceVersion(); e != a { t.Errorf("expected %v, got %v", e, a) } - if e, a := "some/place/only/we/know", jbi.SelfLink(); e != a { + if e, a := "some/place/only/we/know", accessor.SelfLink(); e != a { t.Errorf("expected %v, got %v", e, a) } - jbi.SetName("bar") - jbi.SetUID("other") - jbi.SetAPIVersion("c") - jbi.SetKind("d") - jbi.SetResourceVersion("2") - jbi.SetSelfLink("google.com") + accessor.SetName("bar") + accessor.SetUID("other") + accessor.SetAPIVersion("c") + accessor.SetKind("d") + accessor.SetResourceVersion("2") + accessor.SetSelfLink("google.com") - // Prove that jbi changes the original object. + // Prove that accessor changes the original object. if e, a := "bar", j.Name; e != a { t.Errorf("expected %v, got %v", e, a) } @@ -95,6 +97,153 @@ func TestGenericTypeMeta(t *testing.T) { } } +func TestGenericObjectMeta(t *testing.T) { + type TypeMeta struct { + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + } + type ObjectMeta struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + UID string `json:"uid,omitempty" yaml:"uid,omitempty"` + CreationTimestamp util.Time `json:"creationTimestamp,omitempty" yaml:"creationTimestamp,omitempty"` + SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"` + ResourceVersion string `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"` + } + type Object struct { + TypeMeta `json:",inline" yaml:",inline"` + ObjectMeta `json:"metadata" yaml:"metadata"` + } + j := Object{ + TypeMeta{ + APIVersion: "a", + Kind: "b", + }, + ObjectMeta{ + Name: "foo", + UID: "uid", + ResourceVersion: "1", + SelfLink: "some/place/only/we/know", + }, + } + accessor, err := FindAccessor(&j) + if err != nil { + t.Fatalf("new err: %v", err) + } + if e, a := "foo", accessor.Name(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "uid", accessor.UID(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + 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) + } + if e, a := "1", accessor.ResourceVersion(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "some/place/only/we/know", accessor.SelfLink(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + + accessor.SetName("bar") + accessor.SetUID("other") + accessor.SetAPIVersion("c") + accessor.SetKind("d") + accessor.SetResourceVersion("2") + accessor.SetSelfLink("google.com") + + // Prove that accessor changes the original object. + if e, a := "bar", j.Name; e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "other", j.UID; e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "c", j.APIVersion; e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "d", j.Kind; e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "2", j.ResourceVersion; e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "google.com", j.SelfLink; e != a { + t.Errorf("expected %v, got %v", e, a) + } +} + +func TestGenericListMeta(t *testing.T) { + type TypeMeta struct { + Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` + } + type ListMeta struct { + SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"` + ResourceVersion string `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"` + } + type Object struct { + TypeMeta `json:",inline" yaml:",inline"` + ListMeta `json:"metadata" yaml:"metadata"` + } + j := Object{ + TypeMeta{ + APIVersion: "a", + Kind: "b", + }, + ListMeta{ + ResourceVersion: "1", + SelfLink: "some/place/only/we/know", + }, + } + accessor, err := FindAccessor(&j) + if err != nil { + t.Fatalf("new err: %v", err) + } + if e, a := "", accessor.Name(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "", accessor.UID(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + 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) + } + if e, a := "1", accessor.ResourceVersion(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "some/place/only/we/know", accessor.SelfLink(); e != a { + t.Errorf("expected %v, got %v", e, a) + } + + accessor.SetName("bar") + accessor.SetUID("other") + accessor.SetAPIVersion("c") + accessor.SetKind("d") + accessor.SetResourceVersion("2") + accessor.SetSelfLink("google.com") + + // Prove that accessor changes the original object. + if e, a := "c", j.APIVersion; e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "d", j.Kind; e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "2", j.ResourceVersion; e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := "google.com", j.SelfLink; e != a { + t.Errorf("expected %v, got %v", e, a) + } +} + type MyAPIObject struct { runtime.TypeMeta `yaml:",inline" json:",inline"` }