Merge pull request #1969 from smarterclayton/new_typemeta

Allow Meta lookup to work across ObjectType and ListMeta
This commit is contained in:
Daniel Smith 2014-10-23 15:29:57 -07:00
commit e46af6e37f
19 changed files with 719 additions and 469 deletions

View File

@ -31,6 +31,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubecfg"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -372,11 +373,11 @@ func executeAPIRequest(ctx api.Context, method string, c *client.Client) bool {
if err != nil {
glog.Fatalf("error obtaining resource version for update: %v", err)
}
jsonBase, err := runtime.FindTypeMeta(obj)
meta, err := meta.Accessor(obj)
if err != nil {
glog.Fatalf("error finding json base for update: %v", err)
}
version = jsonBase.ResourceVersion()
version = meta.ResourceVersion()
verb = "PUT"
setBody = true
if !validStorage || !hasSuffix {
@ -408,7 +409,7 @@ func executeAPIRequest(ctx api.Context, method string, c *client.Client) bool {
if err != nil {
glog.Fatalf("error setting resource version: %v", err)
}
jsonBase, err := runtime.FindTypeMeta(obj)
jsonBase, err := meta.Accessor(obj)
if err != nil {
glog.Fatalf("error setting resource version: %v", err)
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -47,13 +48,13 @@ var Codec = v1beta1.Codec
// ResourceVersioner describes a default versioner that can handle all types
// of versioning.
// TODO: when versioning changes, make this part of each API definition.
var ResourceVersioner = runtime.NewTypeMetaResourceVersioner()
var ResourceVersioner = meta.NewResourceVersioner()
// SelfLinker can set or get the SelfLink field of all API types.
// TODO: when versioning changes, make this part of each API definition.
// TODO(lavalamp): Combine SelfLinker & ResourceVersioner interfaces, force all uses
// to go through the InterfacesFor method below.
var SelfLinker = runtime.NewTypeMetaSelfLinker()
var SelfLinker = meta.NewSelfLinker()
// VersionInterfaces contains the interfaces one should use for dealing with types of a particular version.
type VersionInterfaces struct {

19
pkg/api/meta/doc.go Normal file
View File

@ -0,0 +1,19 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package meta provides functions for retrieving API metadata from objects
// belonging to the Kubernetes API
package meta

281
pkg/api/meta/meta.go Normal file
View File

@ -0,0 +1,281 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package meta
import (
"fmt"
"reflect"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
// Interface lets you work with object metadata from any of the versioned or
// internal API objects.
type Interface interface {
Name() string
SetName(name string)
UID() string
SetUID(uid string)
APIVersion() string
SetAPIVersion(version string)
Kind() string
SetKind(kind string)
ResourceVersion() string
SetResourceVersion(version string)
SelfLink() string
SetSelfLink(selfLink string)
}
// Accessor takes an arbitary object pointer and returns meta.Interface.
// 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
// value and are a no-op if set.
func Accessor(obj interface{}) (Interface, 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", typeMeta)
}
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", objectMeta)
}
} 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", listMeta)
}
} 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", typeMeta)
}
}
}
return a, nil
}
// NewResourceVersioner returns a ResourceVersioner that can set or
// retrieve ResourceVersion on objects derived from TypeMeta.
func NewResourceVersioner() runtime.ResourceVersioner {
return resourceAccessor{}
}
// resourceAccessor implements ResourceVersioner and SelfLinker.
type resourceAccessor struct{}
func (v resourceAccessor) ResourceVersion(obj runtime.Object) (string, error) {
accessor, err := Accessor(obj)
if err != nil {
return "", err
}
return accessor.ResourceVersion(), nil
}
func (v resourceAccessor) SetResourceVersion(obj runtime.Object, version string) error {
accessor, err := Accessor(obj)
if err != nil {
return err
}
accessor.SetResourceVersion(version)
return nil
}
func (v resourceAccessor) Name(obj runtime.Object) (string, error) {
accessor, err := Accessor(obj)
if err != nil {
return "", err
}
return accessor.Name(), nil
}
func (v resourceAccessor) SelfLink(obj runtime.Object) (string, error) {
accessor, err := Accessor(obj)
if err != nil {
return "", err
}
return accessor.SelfLink(), nil
}
func (v resourceAccessor) SetSelfLink(obj runtime.Object, selfLink string) error {
accessor, err := Accessor(obj)
if err != nil {
return err
}
accessor.SetSelfLink(selfLink)
return nil
}
// NewSelfLinker returns a SelfLinker that works on all TypeMeta SelfLink fields.
func NewSelfLinker() runtime.SelfLinker {
return resourceAccessor{}
}
// 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
kind *string
resourceVersion *string
selfLink *string
}
func (a genericAccessor) Name() string {
if a.name == nil {
return ""
}
return *a.name
}
func (a genericAccessor) SetName(name string) {
if a.name == nil {
return
}
*a.name = name
}
func (a genericAccessor) UID() string {
if a.uid == nil {
return ""
}
return *a.uid
}
func (a genericAccessor) SetUID(uid string) {
if a.uid == nil {
return
}
*a.uid = uid
}
func (a genericAccessor) APIVersion() string {
return *a.apiVersion
}
func (a genericAccessor) SetAPIVersion(version string) {
*a.apiVersion = version
}
func (a genericAccessor) Kind() string {
return *a.kind
}
func (a genericAccessor) SetKind(kind string) {
*a.kind = kind
}
func (a genericAccessor) ResourceVersion() string {
return *a.resourceVersion
}
func (a genericAccessor) SetResourceVersion(version string) {
*a.resourceVersion = version
}
func (a genericAccessor) SelfLink() string {
return *a.selfLink
}
func (a genericAccessor) SetSelfLink(selfLink string) {
*a.selfLink = selfLink
}
// fieldPtr puts the address of fieldName, which must be a member of v,
// into dest, which must be an address of a variable to which this field's
// address can be assigned.
func fieldPtr(v reflect.Value, fieldName string, dest interface{}) error {
field := v.FieldByName(fieldName)
if !field.IsValid() {
return fmt.Errorf("Couldn't find %v field in %#v", fieldName, v.Interface())
}
v = reflect.ValueOf(dest)
if v.Kind() != reflect.Ptr {
return fmt.Errorf("dest should be ptr")
}
v = v.Elem()
field = field.Addr()
if field.Type().AssignableTo(v.Type()) {
v.Set(field)
return nil
}
if field.Type().ConvertibleTo(v.Type()) {
v.Set(field.Convert(v.Type()))
return nil
}
return fmt.Errorf("Couldn't assign/convert %v to %v", field.Type(), v.Type())
}
// extractFromTypeMeta extracts pointers to version and kind fields from an object
func extractFromTypeMeta(v reflect.Value, a *genericAccessor) error {
if err := fieldPtr(v, "APIVersion", &a.apiVersion); err != nil {
return err
}
if err := fieldPtr(v, "Kind", &a.kind); err != nil {
return err
}
return nil
}
// extractFromObjectMeta extracts pointers to metadata fields from an object
func extractFromObjectMeta(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
}
// extractFromObjectMeta extracts pointers to metadata fields from a list object
func extractFromListMeta(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
}

355
pkg/api/meta/meta_test.go Normal file
View File

@ -0,0 +1,355 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package meta
import (
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
func TestGenericTypeMeta(t *testing.T) {
type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
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"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
}
type Object struct {
TypeMeta `json:",inline" yaml:",inline"`
}
j := Object{
TypeMeta{
Name: "foo",
UID: "uid",
APIVersion: "a",
Kind: "b",
ResourceVersion: "1",
SelfLink: "some/place/only/we/know",
},
}
accessor, err := Accessor(&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 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 := Accessor(&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 := Accessor(&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"`
}
func (*MyAPIObject) IsAnAPIObject() {}
type MyIncorrectlyMarkedAsAPIObject struct {
}
func (*MyIncorrectlyMarkedAsAPIObject) IsAnAPIObject() {}
func TestResourceVersionerOfAPI(t *testing.T) {
type T struct {
runtime.Object
Expected string
}
testCases := map[string]T{
"empty api object": {&MyAPIObject{}, ""},
"api object with version": {&MyAPIObject{TypeMeta: runtime.TypeMeta{ResourceVersion: "1"}}, "1"},
"pointer to api object with version": {&MyAPIObject{TypeMeta: runtime.TypeMeta{ResourceVersion: "1"}}, "1"},
}
versioning := NewResourceVersioner()
for key, testCase := range testCases {
actual, err := versioning.ResourceVersion(testCase.Object)
if err != nil {
t.Errorf("%s: unexpected error %#v", key, err)
}
if actual != testCase.Expected {
t.Errorf("%s: expected %v, got %v", key, testCase.Expected, actual)
}
}
failingCases := map[string]struct {
runtime.Object
Expected string
}{
"not a valid object to try": {&MyIncorrectlyMarkedAsAPIObject{}, "1"},
}
for key, testCase := range failingCases {
_, err := versioning.ResourceVersion(testCase.Object)
if err == nil {
t.Errorf("%s: expected error, got nil", key)
}
}
setCases := map[string]struct {
runtime.Object
Expected string
}{
"pointer to api object with version": {&MyAPIObject{TypeMeta: runtime.TypeMeta{ResourceVersion: "1"}}, "1"},
}
for key, testCase := range setCases {
if err := versioning.SetResourceVersion(testCase.Object, "5"); err != nil {
t.Errorf("%s: unexpected error %#v", key, err)
}
actual, err := versioning.ResourceVersion(testCase.Object)
if err != nil {
t.Errorf("%s: unexpected error %#v", key, err)
}
if actual != "5" {
t.Errorf("%s: expected %v, got %v", key, "5", actual)
}
}
}
func TestTypeMetaSelfLinker(t *testing.T) {
table := map[string]struct {
obj runtime.Object
expect string
try string
succeed bool
}{
"normal": {
obj: &MyAPIObject{TypeMeta: runtime.TypeMeta{SelfLink: "foobar"}},
expect: "foobar",
try: "newbar",
succeed: true,
},
"fail": {
obj: &MyIncorrectlyMarkedAsAPIObject{},
succeed: false,
},
}
linker := NewSelfLinker()
for name, item := range table {
got, err := linker.SelfLink(item.obj)
if e, a := item.succeed, err == nil; e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
if e, a := item.expect, got; item.succeed && e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
err = linker.SetSelfLink(item.obj, item.try)
if e, a := item.succeed, err == nil; e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
if item.succeed {
got, err := linker.SelfLink(item.obj)
if err != nil {
t.Errorf("%v: expected no err, got %v", name, err)
}
if e, a := item.try, got; e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
}
}
}

View File

@ -21,6 +21,7 @@ import (
"fmt"
"regexp"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
@ -35,7 +36,7 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) {
if obj == nil {
return nil, ErrNilObject
}
jsonBase, err := runtime.FindTypeMeta(obj)
meta, err := meta.Accessor(obj)
if err != nil {
return nil, err
}
@ -43,16 +44,16 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) {
if err != nil {
return nil, err
}
version := versionFromSelfLink.FindStringSubmatch(jsonBase.SelfLink())
version := versionFromSelfLink.FindStringSubmatch(meta.SelfLink())
if len(version) < 2 {
return nil, fmt.Errorf("unexpected self link format: %v", jsonBase.SelfLink())
return nil, fmt.Errorf("unexpected self link format: %v", meta.SelfLink())
}
return &ObjectReference{
Kind: kind,
APIVersion: version[1],
// TODO: correct Name and UID when TypeMeta makes a distinction
Name: jsonBase.ID(),
UID: jsonBase.ID(),
ResourceVersion: jsonBase.ResourceVersion(),
Name: meta.Name(),
UID: meta.UID(),
ResourceVersion: meta.ResourceVersion(),
}, nil
}

View File

@ -37,6 +37,7 @@ func TestGetReference(t *testing.T) {
obj: &Pod{
TypeMeta: TypeMeta{
Name: "foo",
UID: "bar",
ResourceVersion: "42",
SelfLink: "/api/v1beta1/pods/foo",
},
@ -45,7 +46,7 @@ func TestGetReference(t *testing.T) {
Kind: "Pod",
APIVersion: "v1beta1",
Name: "foo",
UID: "foo",
UID: "bar",
ResourceVersion: "42",
},
},
@ -53,6 +54,7 @@ func TestGetReference(t *testing.T) {
obj: &ServiceList{
TypeMeta: TypeMeta{
Name: "foo",
UID: "bar",
ResourceVersion: "42",
SelfLink: "/api/v1beta2/services",
},
@ -61,7 +63,7 @@ func TestGetReference(t *testing.T) {
Kind: "ServiceList",
APIVersion: "v1beta2",
Name: "foo",
UID: "foo",
UID: "bar",
ResourceVersion: "42",
},
},

View File

@ -26,6 +26,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -46,17 +47,6 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
// APIVersion and Kind must remain blank in memory.
j.APIVersion = ""
j.Kind = ""
j.Name = c.RandString()
// TODO: Fix JSON/YAML packages and/or write custom encoding
// for uint64's. Somehow the LS *byte* of this is lost, but
// only when all 8 bytes are set.
j.ResourceVersion = strconv.FormatUint(c.RandUint64()>>8, 10)
j.SelfLink = c.RandString()
var sec, nsec int64
c.Fuzz(&sec)
c.Fuzz(&nsec)
j.CreationTimestamp = util.Unix(sec, nsec).Rfc3339Copy()
},
func(j *api.TypeMeta, c fuzz.Continue) {
// We have to customize the randomization of TypeMetas because their
@ -111,7 +101,7 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
name := reflect.TypeOf(source).Elem().Name()
apiObjectFuzzer.Fuzz(source)
j, err := runtime.FindTypeMeta(source)
j, err := meta.Accessor(source)
if err != nil {
t.Fatalf("Unexpected error %v for %#v", err, source)
}
@ -170,7 +160,7 @@ func TestTypes(t *testing.T) {
t.Errorf("Couldn't make a %v? %v", kind, err)
continue
}
if _, err := runtime.FindTypeMeta(item); err != nil {
if _, err := meta.Accessor(item); err != nil {
t.Logf("%s is not a TypeMeta and cannot be round tripped: %v", kind, err)
continue
}

View File

@ -564,11 +564,11 @@ func TestParseTimeout(t *testing.T) {
type setTestSelfLinker struct {
t *testing.T
expectedSet string
id string
name string
called bool
}
func (s *setTestSelfLinker) ID(runtime.Object) (string, error) { return s.id, nil }
func (s *setTestSelfLinker) Name(runtime.Object) (string, error) { return s.name, nil }
func (*setTestSelfLinker) SelfLink(runtime.Object) (string, error) { return "", nil }
func (s *setTestSelfLinker) SetSelfLink(obj runtime.Object, selfLink string) error {
if e, a := s.expectedSet, selfLink; e != a {
@ -587,7 +587,7 @@ func TestSyncCreate(t *testing.T) {
}
selfLinker := &setTestSelfLinker{
t: t,
id: "bar",
name: "bar",
expectedSet: "/prefix/version/foo/bar",
}
handler := Handle(map[string]RESTStorage{
@ -684,7 +684,7 @@ func TestAsyncCreateError(t *testing.T) {
}
selfLinker := &setTestSelfLinker{
t: t,
id: "bar",
name: "bar",
expectedSet: "/prefix/version/foo/bar",
}
handler := Handle(map[string]RESTStorage{"foo": &storage}, codec, "/prefix/version", selfLinker)

View File

@ -64,14 +64,14 @@ func (h *RESTHandler) setSelfLink(obj runtime.Object, req *http.Request) error {
return h.selfLinker.SetSelfLink(obj, newURL.String())
}
// Like setSelfLink, but appends the object's id.
func (h *RESTHandler) setSelfLinkAddID(obj runtime.Object, req *http.Request) error {
id, err := h.selfLinker.ID(obj)
// Like setSelfLink, but appends the object's name.
func (h *RESTHandler) setSelfLinkAddName(obj runtime.Object, req *http.Request) error {
name, err := h.selfLinker.Name(obj)
if err != nil {
return err
}
newURL := *req.URL
newURL.Path = path.Join(h.canonicalPrefix, req.URL.Path, id)
newURL.Path = path.Join(h.canonicalPrefix, req.URL.Path, name)
newURL.RawQuery = ""
newURL.Fragment = ""
return h.selfLinker.SetSelfLink(obj, newURL.String())
@ -171,7 +171,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt
errorJSON(err, h.codec, w)
return
}
op := h.createOperation(out, sync, timeout, curry(h.setSelfLinkAddID, req))
op := h.createOperation(out, sync, timeout, curry(h.setSelfLinkAddName, req))
h.finishReq(op, req, w)
case "DELETE":

View File

@ -23,6 +23,7 @@ import (
"time"
apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@ -78,12 +79,12 @@ func (r *Reflector) listAndWatch() {
glog.Errorf("Failed to list %v: %v", r.expectedType, err)
return
}
jsonBase, err := runtime.FindTypeMeta(list)
meta, err := meta.Accessor(list)
if err != nil {
glog.Errorf("Unable to understand list result %#v", list)
return
}
resourceVersion = jsonBase.ResourceVersion()
resourceVersion = meta.ResourceVersion()
items, err := runtime.ExtractList(list)
if err != nil {
glog.Errorf("Unable to understand list result %#v (%v)", list, err)
@ -112,11 +113,11 @@ func (r *Reflector) listAndWatch() {
func (r *Reflector) syncWith(items []runtime.Object) error {
found := map[string]interface{}{}
for _, item := range items {
jsonBase, err := runtime.FindTypeMeta(item)
meta, err := meta.Accessor(item)
if err != nil {
return fmt.Errorf("unexpected item in list: %v", err)
}
found[jsonBase.ID()] = item
found[meta.Name()] = item
}
r.store.Replace(found)
@ -139,25 +140,25 @@ func (r *Reflector) watchHandler(w watch.Interface, resourceVersion *string) err
glog.Errorf("expected type %v, but watch event object had type %v", e, a)
continue
}
jsonBase, err := runtime.FindTypeMeta(event.Object)
meta, err := meta.Accessor(event.Object)
if err != nil {
glog.Errorf("unable to understand watch event %#v", event)
continue
}
switch event.Type {
case watch.Added:
r.store.Add(jsonBase.ID(), event.Object)
r.store.Add(meta.Name(), event.Object)
case watch.Modified:
r.store.Update(jsonBase.ID(), event.Object)
r.store.Update(meta.Name(), event.Object)
case watch.Deleted:
// TODO: Will any consumers need access to the "last known
// state", which is passed in event.Object? If so, may need
// to change this.
r.store.Delete(jsonBase.ID())
r.store.Delete(meta.Name())
default:
glog.Errorf("unable to understand watch event %#v", event)
}
*resourceVersion = jsonBase.ResourceVersion()
*resourceVersion = meta.ResourceVersion()
eventCount++
}

View File

@ -57,6 +57,7 @@ func TestEventf(t *testing.T) {
TypeMeta: api.TypeMeta{
SelfLink: "/api/v1beta1/pods/foo",
Name: "foo",
UID: "bar",
},
},
fieldPath: "desiredState.manifest.containers[2]",
@ -68,7 +69,7 @@ func TestEventf(t *testing.T) {
InvolvedObject: api.ObjectReference{
Kind: "Pod",
Name: "foo",
UID: "foo",
UID: "bar",
APIVersion: "v1beta1",
FieldPath: "desiredState.manifest.containers[2]",
},
@ -77,7 +78,7 @@ func TestEventf(t *testing.T) {
Message: "some verbose message: 1",
Source: "eventTest",
},
expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"", Name:"foo", UID:"foo", APIVersion:"v1beta1", ResourceVersion:"", FieldPath:"desiredState.manifest.containers[2]"}): status: 'running', reason: 'started' some verbose message: 1`,
expectLog: `Event(api.ObjectReference{Kind:"Pod", Namespace:"", Name:"foo", UID:"bar", APIVersion:"v1beta1", ResourceVersion:"", FieldPath:"desiredState.manifest.containers[2]"}): status: 'running', reason: 'started' some verbose message: 1`,
},
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
"io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
@ -109,11 +110,11 @@ func doUpdate(c *client.RESTClient, resource string, obj runtime.Object) (string
// Update the object we are trying to send to the server with the
// correct resource version.
typeMeta, err := runtime.FindTypeMeta(obj)
meta, err := meta.Accessor(obj)
if err != nil {
return "", err
}
typeMeta.SetResourceVersion(version)
meta.SetResourceVersion(version)
// Convert object with updated resourceVersion to data for PUT.
data, err := c.Codec.Encode(obj)
@ -149,17 +150,17 @@ func doDelete(c *client.RESTClient, resource string, obj runtime.Object) (string
}
func getIDFromObj(obj runtime.Object) (string, error) {
typeMeta, err := runtime.FindTypeMeta(obj)
meta, err := meta.Accessor(obj)
if err != nil {
return "", err
}
return typeMeta.ID(), nil
return meta.Name(), nil
}
func getResourceVersionFromObj(obj runtime.Object) (string, error) {
typeMeta, err := runtime.FindTypeMeta(obj)
meta, err := meta.Accessor(obj)
if err != nil {
return "", err
}
return typeMeta.ResourceVersion(), nil
return meta.ResourceVersion(), nil
}

View File

@ -29,12 +29,14 @@ var Codec = runtime.CodecFor(scheme, "v1test")
type EmbeddedTest struct {
runtime.TypeMeta `yaml:",inline" json:",inline"`
ID string `yaml:"id,omitempty" json:"id,omitempty"`
Object runtime.EmbeddedObject `yaml:"object,omitempty" json:"object,omitempty"`
EmptyObject runtime.EmbeddedObject `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"`
}
type EmbeddedTestExternal struct {
runtime.TypeMeta `yaml:",inline" json:",inline"`
ID string `yaml:"id,omitempty" json:"id,omitempty"`
Object runtime.RawExtension `yaml:"object,omitempty" json:"object,omitempty"`
EmptyObject runtime.RawExtension `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"`
}
@ -49,9 +51,11 @@ func TestEmbeddedObject(t *testing.T) {
outer := &EmbeddedTest{
TypeMeta: runtime.TypeMeta{Name: "outer"},
ID: "outer",
Object: runtime.EmbeddedObject{
&EmbeddedTest{
TypeMeta: runtime.TypeMeta{Name: "inner"},
ID: "inner",
},
},
}

View File

@ -45,8 +45,8 @@ type SelfLinker interface {
SetSelfLink(obj Object, selfLink string) error
SelfLink(obj Object) (string, error)
// Knowing ID is sometimes necessary to use a SelfLinker.
ID(obj Object) (string, error)
// Knowing Name is sometimes necessary to use a SelfLinker.
Name(obj Object) (string, error)
}
// All api types must support the Object interface. It's deliberately tiny so that this is not an onerous

View File

@ -1,214 +0,0 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package runtime
import (
"fmt"
"reflect"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
)
// FindTypeMeta takes an arbitary api type, returns pointer to its TypeMeta field.
// obj must be a pointer to an api type.
func FindTypeMeta(obj Object) (TypeMetaInterface, error) {
v, err := conversion.EnforcePtr(obj)
if err != nil {
return nil, err
}
t := v.Type()
name := t.Name()
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 JSON type", name)
}
g, err := newGenericTypeMeta(typeMeta)
if err != nil {
return nil, err
}
return g, nil
}
// NewTypeMetaResourceVersioner returns a ResourceVersioner that can set or
// retrieve ResourceVersion on objects derived from TypeMeta.
func NewTypeMetaResourceVersioner() ResourceVersioner {
return jsonBaseModifier{}
}
// jsonBaseModifier implements ResourceVersioner and SelfLinker.
type jsonBaseModifier struct{}
func (v jsonBaseModifier) ResourceVersion(obj Object) (string, error) {
json, err := FindTypeMeta(obj)
if err != nil {
return "", err
}
return json.ResourceVersion(), nil
}
func (v jsonBaseModifier) SetResourceVersion(obj Object, version string) error {
json, err := FindTypeMeta(obj)
if err != nil {
return err
}
json.SetResourceVersion(version)
return nil
}
func (v jsonBaseModifier) ID(obj Object) (string, error) {
json, err := FindTypeMeta(obj)
if err != nil {
return "", err
}
return json.ID(), nil
}
func (v jsonBaseModifier) SelfLink(obj Object) (string, error) {
json, err := FindTypeMeta(obj)
if err != nil {
return "", err
}
return json.SelfLink(), nil
}
func (v jsonBaseModifier) SetSelfLink(obj Object, selfLink string) error {
json, err := FindTypeMeta(obj)
if err != nil {
return err
}
json.SetSelfLink(selfLink)
return nil
}
// NewTypeMetaSelfLinker returns a SelfLinker that works on all TypeMeta SelfLink fields.
func NewTypeMetaSelfLinker() SelfLinker {
return jsonBaseModifier{}
}
// TypeMetaInterface lets you work with a TypeMeta from any of the versioned or
// internal APIObjects.
type TypeMetaInterface interface {
ID() string
SetID(ID string)
APIVersion() string
SetAPIVersion(version string)
Kind() string
SetKind(kind string)
ResourceVersion() string
SetResourceVersion(version string)
SelfLink() string
SetSelfLink(selfLink string)
}
type genericTypeMeta struct {
id *string
apiVersion *string
kind *string
resourceVersion *string
selfLink *string
}
func (g genericTypeMeta) ID() string {
return *g.id
}
func (g genericTypeMeta) SetID(id string) {
*g.id = id
}
func (g genericTypeMeta) APIVersion() string {
return *g.apiVersion
}
func (g genericTypeMeta) SetAPIVersion(version string) {
*g.apiVersion = version
}
func (g genericTypeMeta) Kind() string {
return *g.kind
}
func (g genericTypeMeta) SetKind(kind string) {
*g.kind = kind
}
func (g genericTypeMeta) ResourceVersion() string {
return *g.resourceVersion
}
func (g genericTypeMeta) SetResourceVersion(version string) {
*g.resourceVersion = version
}
func (g genericTypeMeta) SelfLink() string {
return *g.selfLink
}
func (g genericTypeMeta) SetSelfLink(selfLink string) {
*g.selfLink = selfLink
}
// fieldPtr puts the address of fieldName, which must be a member of v,
// into dest, which must be an address of a variable to which this field's
// address can be assigned.
func fieldPtr(v reflect.Value, fieldName string, dest interface{}) error {
field := v.FieldByName(fieldName)
if !field.IsValid() {
return fmt.Errorf("Couldn't find %v field in %#v", fieldName, v.Interface())
}
v = reflect.ValueOf(dest)
if v.Kind() != reflect.Ptr {
return fmt.Errorf("dest should be ptr")
}
v = v.Elem()
field = field.Addr()
if field.Type().AssignableTo(v.Type()) {
v.Set(field)
return nil
}
if field.Type().ConvertibleTo(v.Type()) {
v.Set(field.Convert(v.Type()))
return nil
}
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.id); err != nil {
return g, 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
}

View File

@ -1,196 +0,0 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package runtime
import (
"reflect"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
func TestGenericTypeMeta(t *testing.T) {
type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
Name string `json:"name,omitempty" yaml:"name,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"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
}
j := TypeMeta{
Name: "foo",
APIVersion: "a",
Kind: "b",
ResourceVersion: "1",
SelfLink: "some/place/only/we/know",
}
g, err := newGenericTypeMeta(reflect.ValueOf(&j).Elem())
if err != nil {
t.Fatalf("new err: %v", err)
}
// Prove g supports TypeMetaInterface.
jbi := TypeMetaInterface(g)
if e, a := "foo", jbi.ID(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "a", jbi.APIVersion(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "b", jbi.Kind(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "1", jbi.ResourceVersion(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "some/place/only/we/know", jbi.SelfLink(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
jbi.SetID("bar")
jbi.SetAPIVersion("c")
jbi.SetKind("d")
jbi.SetResourceVersion("2")
jbi.SetSelfLink("google.com")
// Prove that jbi changes the original object.
if e, a := "bar", j.Name; 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)
}
}
type MyAPIObject struct {
TypeMeta `yaml:",inline" json:",inline"`
}
func (*MyAPIObject) IsAnAPIObject() {}
type MyIncorrectlyMarkedAsAPIObject struct {
}
func (*MyIncorrectlyMarkedAsAPIObject) IsAnAPIObject() {}
func TestResourceVersionerOfAPI(t *testing.T) {
type T struct {
Object
Expected string
}
testCases := map[string]T{
"empty api object": {&MyAPIObject{}, ""},
"api object with version": {&MyAPIObject{TypeMeta: TypeMeta{ResourceVersion: "1"}}, "1"},
"pointer to api object with version": {&MyAPIObject{TypeMeta: TypeMeta{ResourceVersion: "1"}}, "1"},
}
versioning := NewTypeMetaResourceVersioner()
for key, testCase := range testCases {
actual, err := versioning.ResourceVersion(testCase.Object)
if err != nil {
t.Errorf("%s: unexpected error %#v", key, err)
}
if actual != testCase.Expected {
t.Errorf("%s: expected %v, got %v", key, testCase.Expected, actual)
}
}
failingCases := map[string]struct {
Object
Expected string
}{
"not a valid object to try": {&MyIncorrectlyMarkedAsAPIObject{}, "1"},
}
for key, testCase := range failingCases {
_, err := versioning.ResourceVersion(testCase.Object)
if err == nil {
t.Errorf("%s: expected error, got nil", key)
}
}
setCases := map[string]struct {
Object
Expected string
}{
"pointer to api object with version": {&MyAPIObject{TypeMeta: TypeMeta{ResourceVersion: "1"}}, "1"},
}
for key, testCase := range setCases {
if err := versioning.SetResourceVersion(testCase.Object, "5"); err != nil {
t.Errorf("%s: unexpected error %#v", key, err)
}
actual, err := versioning.ResourceVersion(testCase.Object)
if err != nil {
t.Errorf("%s: unexpected error %#v", key, err)
}
if actual != "5" {
t.Errorf("%s: expected %v, got %v", key, "5", actual)
}
}
}
func TestTypeMetaSelfLinker(t *testing.T) {
table := map[string]struct {
obj Object
expect string
try string
succeed bool
}{
"normal": {
obj: &MyAPIObject{TypeMeta: TypeMeta{SelfLink: "foobar"}},
expect: "foobar",
try: "newbar",
succeed: true,
},
"fail": {
obj: &MyIncorrectlyMarkedAsAPIObject{},
succeed: false,
},
}
linker := NewTypeMetaSelfLinker()
for name, item := range table {
got, err := linker.SelfLink(item.obj)
if e, a := item.succeed, err == nil; e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
if e, a := item.expect, got; item.succeed && e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
err = linker.SetSelfLink(item.obj, item.try)
if e, a := item.succeed, err == nil; e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
if item.succeed {
got, err := linker.SelfLink(item.obj)
if err != nil {
t.Errorf("%v: expected no err, got %v", name, err)
}
if e, a := item.try, got; e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
}
}
}

View File

@ -26,8 +26,8 @@ import (
// TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type,
// like this:
// type MyAwesomeAPIObject struct {
// runtime.TypeMeta `yaml:",inline" json:",inline"`
// ... // other fields
// runtime.TypeMeta `yaml:",inline" json:",inline"`
// ... // other fields
// }
// func (*MyAwesomeAPIObject) IsAnAPIObject() {}
//
@ -35,12 +35,14 @@ import (
// your own with the same fields.
//
type TypeMeta struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
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"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
}
// PluginBase is like TypeMeta, but it's intended for plugin objects that won't ever be encoded

View File

@ -25,6 +25,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/coreos/go-etcd/etcd"
@ -44,7 +45,7 @@ func (*TestResource) IsAnAPIObject() {}
var scheme *runtime.Scheme
var codec runtime.Codec
var versioner = RuntimeVersionAdapter{runtime.NewTypeMetaResourceVersioner()}
var versioner = RuntimeVersionAdapter{meta.NewResourceVersioner()}
func init() {
scheme = runtime.NewScheme()