Merge pull request #2786 from smarterclayton/load_opaque_objects

Allow runtime.Object to be encoded as runtime.RawExtension
pull/6/head
Daniel Smith 2014-12-10 16:24:05 -08:00
commit f81ec248d0
20 changed files with 384 additions and 58 deletions

View File

@ -25,7 +25,9 @@ import (
internal "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
docker "github.com/fsouza/go-dockerclient"
fuzz "github.com/google/gofuzz"
)
@ -83,6 +85,26 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
// only replicas round trips
j.Replicas = int(c.RandUint64())
},
func(j *internal.List, c fuzz.Continue) {
c.Fuzz(&j.ListMeta)
c.Fuzz(&j.Items)
if j.Items == nil {
j.Items = []runtime.Object{}
}
},
func(j *runtime.Object, c fuzz.Continue) {
if c.RandBool() {
*j = &runtime.Unknown{
TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"},
RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`),
}
} else {
types := []runtime.Object{&internal.Pod{}, &internal.ReplicationController{}}
t := types[c.Rand.Intn(len(types))]
c.Fuzz(t)
*j = t
}
},
func(intstr *util.IntOrString, c fuzz.Continue) {
// util.IntOrString will panic if its kind is set wrong.
if c.RandBool() {

View File

@ -45,6 +45,7 @@ func init() {
&ContainerManifestList{},
&BoundPod{},
&BoundPods{},
&List{},
)
// Legacy names are supported
Scheme.AddKnownTypeWithName("", "Minion", &Node{})
@ -71,3 +72,4 @@ func (*ContainerManifest) IsAnAPIObject() {}
func (*ContainerManifestList) IsAnAPIObject() {}
func (*BoundPod) IsAnAPIObject() {}
func (*BoundPods) IsAnAPIObject() {}
func (*List) IsAnAPIObject() {}

View File

@ -48,15 +48,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()
j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 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
@ -99,6 +90,26 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
// only replicas round trips
j.Replicas = int(c.RandUint64())
},
func(j *api.List, c fuzz.Continue) {
c.Fuzz(&j.ListMeta)
c.Fuzz(&j.Items)
if j.Items == nil {
j.Items = []runtime.Object{}
}
},
func(j *runtime.Object, c fuzz.Continue) {
if c.RandBool() {
*j = &runtime.Unknown{
TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown"},
RawJSON: []byte(`{"apiVersion":"unknown","kind":"Something","someKey":"someValue"}`),
}
} else {
types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}}
t := types[c.Rand.Intn(len(types))]
c.Fuzz(t)
*j = t
}
},
func(intstr *util.IntOrString, c fuzz.Continue) {
// util.IntOrString will panic if its kind is set wrong.
if c.RandBool() {
@ -154,7 +165,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
obj2, err := codec.Decode(data)
if err != nil {
t.Errorf("%v: %v", name, err)
t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), source)
return
}
if !reflect.DeepEqual(source, obj2) {
@ -188,7 +199,21 @@ func TestSpecificKind(t *testing.T) {
api.Scheme.Log(nil)
}
func TestList(t *testing.T) {
api.Scheme.Log(t)
kind := "List"
item, err := api.Scheme.New("", kind)
if err != nil {
t.Errorf("Couldn't make a %v? %v", kind, err)
return
}
runTest(t, v1beta1.Codec, item)
runTest(t, v1beta2.Codec, item)
api.Scheme.Log(nil)
}
var nonRoundTrippableTypes = util.NewStringSet("ContainerManifest")
var nonInternalRoundTrippableTypes = util.NewStringSet("List")
func TestRoundTripTypes(t *testing.T) {
for kind := range api.Scheme.KnownTypes("") {
@ -206,10 +231,12 @@ func TestRoundTripTypes(t *testing.T) {
}
runTest(t, v1beta1.Codec, item)
runTest(t, v1beta2.Codec, item)
if !nonInternalRoundTrippableTypes.Has(kind) {
runTest(t, api.Codec, item)
}
}
}
}
func TestEncode_Ptr(t *testing.T) {
pod := &api.Pod{

View File

@ -19,6 +19,7 @@ package api
import (
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
@ -1000,3 +1001,11 @@ type BoundPods struct {
// Items is the list of all pods bound to a given host.
Items []BoundPod `json:"items"`
}
// List holds a list of objects, which may not be known by the server.
type List struct {
TypeMeta `json:",inline"`
ListMeta `json:"metadata,omitempty"`
Items []runtime.Object `json:"items"`
}

View File

@ -50,6 +50,7 @@ func init() {
&ContainerManifestList{},
&BoundPod{},
&BoundPods{},
&List{},
)
// Future names are supported
api.Scheme.AddKnownTypeWithName("v1beta1", "Node", &Minion{})
@ -76,3 +77,4 @@ func (*ContainerManifest) IsAnAPIObject() {}
func (*ContainerManifestList) IsAnAPIObject() {}
func (*BoundPod) IsAnAPIObject() {}
func (*BoundPods) IsAnAPIObject() {}
func (*List) IsAnAPIObject() {}

View File

@ -19,6 +19,7 @@ package v1beta1
import (
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
@ -782,3 +783,9 @@ type BoundPods struct {
// Items is the list of all pods bound to a given host.
Items []BoundPod `json:"items" description:"list of all pods bound to a given host"`
}
// List holds a list of objects, which may not be known by the server.
type List struct {
TypeMeta `json:",inline"`
Items []runtime.RawExtension `json:"items" description:"list of objects"`
}

View File

@ -50,6 +50,7 @@ func init() {
&ContainerManifestList{},
&BoundPod{},
&BoundPods{},
&List{},
)
// Future names are supported
api.Scheme.AddKnownTypeWithName("v1beta2", "Node", &Minion{})
@ -76,3 +77,4 @@ func (*ContainerManifest) IsAnAPIObject() {}
func (*ContainerManifestList) IsAnAPIObject() {}
func (*BoundPod) IsAnAPIObject() {}
func (*BoundPods) IsAnAPIObject() {}
func (*List) IsAnAPIObject() {}

View File

@ -19,6 +19,7 @@ package v1beta2
import (
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
@ -783,3 +784,9 @@ type BoundPods struct {
// Items is the list of all pods bound to a given host.
Items []BoundPod `json:"items" description:"list of all pods bound to a given host"`
}
// List holds a list of objects, which may not be known by the server.
type List struct {
TypeMeta `json:",inline"`
Items []runtime.RawExtension `json:"items" description:"list of objects"`
}

View File

@ -46,6 +46,7 @@ func init() {
&OperationList{},
&Event{},
&EventList{},
&List{},
)
// Legacy names are supported
api.Scheme.AddKnownTypeWithName("v1beta3", "Minion", &Node{})
@ -72,3 +73,4 @@ func (*Operation) IsAnAPIObject() {}
func (*OperationList) IsAnAPIObject() {}
func (*Event) IsAnAPIObject() {}
func (*EventList) IsAnAPIObject() {}
func (*List) IsAnAPIObject() {}

View File

@ -19,6 +19,7 @@ package v1beta3
import (
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
@ -556,9 +557,14 @@ type ReplicationControllerSpec struct {
// Selector is a label query over pods that should match the Replicas count.
Selector map[string]string `json:"selector,omitempty"`
// Template is a reference to an object that describes the pod that will be created if
// TemplateRef is a reference to an object that describes the pod that will be created if
// insufficient replicas are detected.
Template ObjectReference `json:"template,omitempty"`
TemplateRef *ObjectReference `json:"templateRef,omitempty"`
// Template is the object that describes the pod that will be created if
// insufficient replicas are detected. This takes precedence over a
// TemplateRef.
Template *PodTemplateSpec `json:"template,omitempty"`
}
// ReplicationControllerStatus represents the current status of a replication
@ -944,3 +950,11 @@ type EventList struct {
Items []Event `json:"items"`
}
// List holds a list of objects, which may not be known by the server.
type List struct {
TypeMeta `json:",inline"`
ListMeta `json:"metadata,omitempty"`
Items []runtime.RawExtension `json:"items" description:"list of objects"`
}

View File

@ -49,15 +49,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()
j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 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

View File

@ -112,7 +112,7 @@ func (s *Scheme) DecodeInto(data []byte, obj interface{}) error {
} else {
external, err := s.NewObject(dataVersion, dataKind)
if err != nil {
return fmt.Errorf("unable to create new object of type ('%s', '%s')", dataVersion, dataKind)
return err
}
// yaml is a superset of json, so we use it to decode here. That way,
// we understand both.

51
pkg/conversion/error.go Normal file
View File

@ -0,0 +1,51 @@
/*
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 conversion
import (
"fmt"
"reflect"
)
type notRegisteredErr struct {
kind string
version string
t reflect.Type
}
func (k *notRegisteredErr) Error() string {
if k.t != nil {
return fmt.Sprintf("no kind is registered for the type %v", k.t)
}
if len(k.kind) == 0 {
return fmt.Sprintf("no version %q has been registered", k.version)
}
if len(k.version) == 0 {
return fmt.Sprintf("no kind %q is registered for the default version", k.kind)
}
return fmt.Sprintf("no kind %q is registered for version %q", k.kind, k.version)
}
// IsNotRegisteredError returns true if the error indicates the provided
// object or input data is not registered.
func IsNotRegisteredError(err error) bool {
if err == nil {
return false
}
_, ok := err.(*notRegisteredErr)
return ok
}

View File

@ -145,14 +145,14 @@ func (s *Scheme) KnownTypes(version string) map[string]reflect.Type {
// NewObject returns a new object of the given version and name,
// or an error if it hasn't been registered.
func (s *Scheme) NewObject(versionName, typeName string) (interface{}, error) {
func (s *Scheme) NewObject(versionName, kind string) (interface{}, error) {
if types, ok := s.versionMap[versionName]; ok {
if t, ok := types[typeName]; ok {
if t, ok := types[kind]; ok {
return reflect.New(t).Interface(), nil
}
return nil, fmt.Errorf("no type '%v' for version '%v'", typeName, versionName)
return nil, &notRegisteredErr{kind: kind, version: versionName}
}
return nil, fmt.Errorf("no version '%v'", versionName)
return nil, &notRegisteredErr{kind: kind, version: versionName}
}
// AddConversionFuncs adds functions to the list of conversion functions. The given
@ -187,8 +187,7 @@ func (s *Scheme) NewObject(versionName, typeName string) (interface{}, error) {
// add conversion functions for things with changed/removed fields.
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
for _, f := range conversionFuncs {
err := s.converter.Register(f)
if err != nil {
if err := s.converter.Register(f); err != nil {
return err
}
}
@ -286,7 +285,7 @@ func (s *Scheme) ObjectVersionAndKind(obj interface{}) (apiVersion, kind string,
version, vOK := s.typeToVersion[t]
kinds, kOK := s.typeToKind[t]
if !vOK || !kOK {
return "", "", fmt.Errorf("unregistered type: %v", t)
return "", "", &notRegisteredErr{t: t}
}
apiVersion = version
kind = kinds[0]

View File

@ -22,16 +22,14 @@ import (
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
var scheme = runtime.NewScheme()
var Codec = runtime.CodecFor(scheme, "v1test")
type EmbeddedTest struct {
runtime.TypeMeta `json:",inline"`
ID string `json:"id,omitempty"`
Object runtime.EmbeddedObject `json:"object,omitempty"`
EmptyObject runtime.EmbeddedObject `json:"emptyObject,omitempty"`
runtime.TypeMeta
ID string
Object runtime.EmbeddedObject
EmptyObject runtime.EmbeddedObject
}
type EmbeddedTestExternal struct {
@ -41,20 +39,90 @@ type EmbeddedTestExternal struct {
EmptyObject runtime.RawExtension `json:"emptyObject,omitempty"`
}
type ObjectTest struct {
runtime.TypeMeta
ID string
Items []runtime.Object
}
type ObjectTestExternal struct {
runtime.TypeMeta `yaml:",inline" json:",inline"`
ID string `json:"id,omitempty"`
Items []runtime.RawExtension `json:"items,omitempty"`
}
func (*ObjectTest) IsAnAPIObject() {}
func (*ObjectTestExternal) IsAnAPIObject() {}
func (*EmbeddedTest) IsAnAPIObject() {}
func (*EmbeddedTestExternal) IsAnAPIObject() {}
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
s := runtime.NewScheme()
s.AddKnownTypes("", &ObjectTest{})
s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{})
_, err := s.Decode([]byte(`{"kind":"ObjectTest","apiVersion":"v1test","items":[{}]}`))
if err == nil {
t.Fatalf("unexpected non-error")
}
}
func TestArrayOfRuntimeObject(t *testing.T) {
s := runtime.NewScheme()
s.AddKnownTypes("", &EmbeddedTest{})
s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{})
s.AddKnownTypes("", &ObjectTest{})
s.AddKnownTypeWithName("v1test", "ObjectTest", &ObjectTestExternal{})
internal := &ObjectTest{
Items: []runtime.Object{
&EmbeddedTest{ID: "foo"},
&EmbeddedTest{ID: "bar"},
// TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization
&runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown","foo":"bar","kind":"OtherTest"}`)},
&ObjectTest{
Items: []runtime.Object{
&EmbeddedTest{ID: "baz"},
},
},
},
}
wire, err := s.EncodeToVersion(internal, "v1test")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
t.Logf("Wire format is:\n%s\n", string(wire))
obj := &ObjectTestExternal{}
if err := json.Unmarshal(wire, obj); err != nil {
t.Fatalf("unexpected error: %v", err)
}
t.Logf("exact wire is: %#v", string(obj.Items[0].RawJSON))
decoded, err := s.Decode(wire)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
internal.Items[2].(*runtime.Unknown).Kind = "OtherTest"
internal.Items[2].(*runtime.Unknown).APIVersion = "unknown"
if e, a := internal, decoded; !reflect.DeepEqual(e, a) {
t.Log(string(decoded.(*ObjectTest).Items[2].(*runtime.Unknown).RawJSON))
t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a))
}
}
func TestEmbeddedObject(t *testing.T) {
s := scheme
s := runtime.NewScheme()
s.AddKnownTypes("", &EmbeddedTest{})
s.AddKnownTypeWithName("v1test", "EmbeddedTest", &EmbeddedTestExternal{})
outer := &EmbeddedTest{
TypeMeta: runtime.TypeMeta{Name: "outer"},
ID: "outer",
Object: runtime.EmbeddedObject{
&EmbeddedTest{
TypeMeta: runtime.TypeMeta{Name: "inner"},
ID: "inner",
},
},
@ -83,7 +151,7 @@ func TestEmbeddedObject(t *testing.T) {
if err != nil {
t.Fatalf("Unexpected decode error %v", err)
}
if externalViaJSON.Kind == "" || externalViaJSON.APIVersion == "" || externalViaJSON.Name != "outer" {
if externalViaJSON.Kind == "" || externalViaJSON.APIVersion == "" || externalViaJSON.ID != "outer" {
t.Errorf("Expected objects to have type info set, got %#v", externalViaJSON)
}
if !reflect.DeepEqual(externalViaJSON.EmptyObject.RawJSON, []byte("null")) || len(externalViaJSON.Object.RawJSON) == 0 {

27
pkg/runtime/error.go Normal file
View File

@ -0,0 +1,27 @@
/*
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 (
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
)
// IsNotRegisteredError returns true if the error indicates the provided
// object or input data is not registered.
func IsNotRegisteredError(err error) bool {
return conversion.IsNotRegisteredError(err)
}

View File

@ -26,6 +26,8 @@ func (re *RawExtension) UnmarshalJSON(in []byte) error {
return nil
}
func (re *RawExtension) MarshalJSON() ([]byte, error) {
// Marshal may get called on pointers or values, so implement MarshalJSON on value.
// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go
func (re RawExtension) MarshalJSON() ([]byte, error) {
return re.RawJSON, nil
}

View File

@ -0,0 +1,39 @@
/*
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_test
import (
"encoding/json"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
func TestEmbeddedRawExtensionMarshal(t *testing.T) {
type test struct {
Ext runtime.RawExtension
}
extension := test{Ext: runtime.RawExtension{RawJSON: []byte(`{"foo":"bar"}`)}}
data, err := json.Marshal(extension)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(data) != `{"Ext":{"foo":"bar"}}` {
t.Errorf("unexpected data: %s", string(data))
}
}

View File

@ -140,15 +140,77 @@ func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *Embedded
return nil
}
// runtimeObjectToRawExtensionArray takes a list of objects and encodes them as RawExtension in the output version
// defined by the conversion.Scope. If objects must be encoded to different schema versions you should set them as
// runtime.Unknown in the internal version instead.
func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExtension, s conversion.Scope) error {
src := *in
dest := make([]RawExtension, len(src))
_, outVersion, scheme := self.fromScope(s)
for i := range src {
switch t := src[i].(type) {
case *Unknown:
dest[i].RawJSON = t.RawJSON
default:
data, err := scheme.EncodeToVersion(src[i], outVersion)
if err != nil {
return err
}
dest[i].RawJSON = data
}
}
*out = dest
return nil
}
// rawExtensionToRuntimeObjectArray attempts to decode objects from the array - if they are unrecognized objects,
// they are added as Unknown.
func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[]Object, s conversion.Scope) error {
src := *in
dest := make([]Object, len(src))
_, _, scheme := self.fromScope(s)
for i := range src {
data := src[i].RawJSON
obj, err := scheme.Decode(data)
if err != nil {
if !IsNotRegisteredError(err) {
return err
}
version, kind, err := scheme.raw.DataVersionAndKind(data)
if err != nil {
return err
}
obj = &Unknown{
TypeMeta: TypeMeta{
APIVersion: version,
Kind: kind,
},
RawJSON: data,
}
}
dest[i] = obj
}
*out = dest
return nil
}
// NewScheme creates a new Scheme. This scheme is pluggable by default.
func NewScheme() *Scheme {
s := &Scheme{conversion.NewScheme()}
s.raw.InternalVersion = ""
s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"}
s.raw.AddConversionFuncs(
if err := s.raw.AddConversionFuncs(
s.embeddedObjectToRawExtension,
s.rawExtensionToEmbeddedObject,
)
s.runtimeObjectToRawExtensionArray,
s.rawExtensionToRuntimeObjectArray,
); err != nil {
panic(err)
}
return s
}
@ -304,6 +366,8 @@ func (s *Scheme) DecodeInto(data []byte, obj Object) error {
// Copy does a deep copy of an API object. Useful mostly for tests.
// TODO(dbsmith): implement directly instead of via Encode/Decode
// TODO(claytonc): Copy cannot be used for objects which do not encode type information, such
// as lists of runtime.Objects
func (s *Scheme) Copy(obj Object) (Object, error) {
data, err := s.EncodeToVersion(obj, "")
if err != nil {

View File

@ -16,9 +16,7 @@ limitations under the License.
package runtime
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
import ()
// Note that the types provided in this file are not versioned and are intended to be
// safe to use from within all versions of every API object.
@ -35,15 +33,8 @@ import (
// your own with the same fields.
//
type TypeMeta struct {
APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty"`
Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty"`
UID string `json:"uid,omitempty"`
CreationTimestamp util.Time `json:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
}
// PluginBase is like TypeMeta, but it's intended for plugin objects that won't ever be encoded