Add Object type to runtime, make runtime test pass.

This commit is contained in:
Daniel Smith 2014-09-05 16:11:30 -07:00
parent 0c6adde92e
commit 77edb91032
12 changed files with 212 additions and 141 deletions

View File

@ -21,21 +21,21 @@ import (
)
func init() {
runtime.AddKnownTypes("",
PodList{},
Pod{},
ReplicationControllerList{},
ReplicationController{},
ServiceList{},
Service{},
MinionList{},
Minion{},
Status{},
ServerOpList{},
ServerOp{},
ContainerManifestList{},
Endpoints{},
EndpointsList{},
Binding{},
runtime.DefaultScheme.AddKnownTypes("",
&PodList{},
&Pod{},
&ReplicationControllerList{},
&ReplicationController{},
&ServiceList{},
&Service{},
&MinionList{},
&Minion{},
&Status{},
&ServerOpList{},
&ServerOp{},
&ContainerManifestList{},
&Endpoints{},
&EndpointsList{},
&Binding{},
)
}

View File

@ -65,6 +65,8 @@ type ContainerManifestList struct {
Items []ContainerManifest `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*ContainerManifestList) IsAnAPIObject() {}
// Volume represents a named volume in a pod that may be accessed by any containers in the pod.
type Volume struct {
// Required: This must be a DNS_LABEL. Each volume in a pod must have
@ -287,6 +289,8 @@ type PodList struct {
Items []Pod `json:"items" yaml:"items,omitempty"`
}
func (*PodList) IsAnAPIObject() {}
// Pod is a collection of containers, used as either input (create, update) or as output (list, get).
type Pod struct {
JSONBase `json:",inline" yaml:",inline"`
@ -295,6 +299,8 @@ type Pod struct {
CurrentState PodState `json:"currentState,omitempty" yaml:"currentState,omitempty"`
}
func (*Pod) IsAnAPIObject() {}
// ReplicationControllerState is the state of a replication controller, either input (create, update) or as output (list, get).
type ReplicationControllerState struct {
Replicas int `json:"replicas" yaml:"replicas"`
@ -308,6 +314,8 @@ type ReplicationControllerList struct {
Items []ReplicationController `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*ReplicationControllerList) IsAnAPIObject() {}
// ReplicationController represents the configuration of a replication controller.
type ReplicationController struct {
JSONBase `json:",inline" yaml:",inline"`
@ -315,6 +323,8 @@ type ReplicationController struct {
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
func (*ReplicationController) IsAnAPIObject() {}
// PodTemplate holds the information used for creating pods.
type PodTemplate struct {
DesiredState PodState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"`
@ -327,6 +337,8 @@ type ServiceList struct {
Items []Service `json:"items" yaml:"items"`
}
func (*ServiceList) IsAnAPIObject() {}
// Service is a named abstraction of software service (for example, mysql) consisting of local port
// (for example 3306) that the proxy listens on, and the selector that determines which pods
// will answer requests sent through the proxy.
@ -346,6 +358,8 @@ type Service struct {
ContainerPort util.IntOrString `json:"containerPort,omitempty" yaml:"containerPort,omitempty"`
}
func (*Service) IsAnAPIObject() {}
// Endpoints is a collection of endpoints that implement the actual service, for example:
// Name: "mysql", Endpoints: ["10.10.1.1:1909", "10.10.2.2:8834"]
type Endpoints struct {
@ -353,12 +367,16 @@ type Endpoints struct {
Endpoints []string `json:"endpoints,omitempty" yaml:"endpoints,omitempty"`
}
func (*Endpoints) IsAnAPIObject() {}
// EndpointsList is a list of endpoints.
type EndpointsList struct {
JSONBase `json:",inline" yaml:",inline"`
Items []Endpoints `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*EndpointsList) IsAnAPIObject() {}
// Minion is a worker node in Kubernetenes.
// The name of the minion according to etcd is in JSONBase.ID.
type Minion struct {
@ -367,12 +385,16 @@ type Minion struct {
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"`
}
func (*Minion) IsAnAPIObject() {}
// MinionList is a list of minions.
type MinionList struct {
JSONBase `json:",inline" yaml:",inline"`
Items []Minion `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*MinionList) IsAnAPIObject() {}
// Binding is written by a scheduler to cause a pod to be bound to a host.
type Binding struct {
JSONBase `json:",inline" yaml:",inline"`
@ -380,6 +402,8 @@ type Binding struct {
Host string `json:"host" yaml:"host"`
}
func (*Binding) IsAnAPIObject() {}
// Status is a return value for calls that don't return other objects.
// TODO: this could go in apiserver, but I'm including it here so clients needn't
// import both.
@ -403,6 +427,8 @@ type Status struct {
Code int `json:"code,omitempty" yaml:"code,omitempty"`
}
func (*Status) IsAnAPIObject() {}
// StatusDetails is a set of additional properties that MAY be set by the
// server to provide additional information about a response. The Reason
// field of a Status object defines what attributes will be set. Clients
@ -539,12 +565,16 @@ type ServerOp struct {
JSONBase `yaml:",inline" json:",inline"`
}
func (*ServerOp) IsAnAPIObject() {}
// ServerOpList is a list of operations, as delivered to API clients.
type ServerOpList struct {
JSONBase `yaml:",inline" json:",inline"`
Items []ServerOp `yaml:"items,omitempty" json:"items,omitempty"`
}
func (*ServerOpList) IsAnAPIObject() {}
// WatchEvent objects are streamed from the api server in response to a watch request.
type WatchEvent struct {
// The type of the watch event; added, modified, or deleted.

View File

@ -24,7 +24,7 @@ import (
)
func init() {
runtime.AddConversionFuncs(
runtime.DefaultScheme.AddConversionFuncs(
// EnvVar's Key is deprecated in favor of Name.
func(in *newer.EnvVar, out *EnvVar, s conversion.Scope) error {
out.Value = in.Value

View File

@ -21,21 +21,21 @@ import (
)
func init() {
runtime.AddKnownTypes("v1beta1",
PodList{},
Pod{},
ReplicationControllerList{},
ReplicationController{},
ServiceList{},
Service{},
MinionList{},
Minion{},
Status{},
ServerOpList{},
ServerOp{},
ContainerManifestList{},
Endpoints{},
EndpointsList{},
Binding{},
runtime.DefaultScheme.AddKnownTypes("v1beta1",
&PodList{},
&Pod{},
&ReplicationControllerList{},
&ReplicationController{},
&ServiceList{},
&Service{},
&MinionList{},
&Minion{},
&Status{},
&ServerOpList{},
&ServerOp{},
&ContainerManifestList{},
&Endpoints{},
&EndpointsList{},
&Binding{},
)
}

View File

@ -65,6 +65,8 @@ type ContainerManifestList struct {
Items []ContainerManifest `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*ContainerManifestList) IsAnAPIObject() {}
// Volume represents a named volume in a pod that may be accessed by any containers in the pod.
type Volume struct {
// Required: This must be a DNS_LABEL. Each volume in a pod must have
@ -243,6 +245,8 @@ type JSONBase struct {
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
}
func (*JSONBase) IsAnAPIObject() {}
// PodStatus represents a status of a pod.
type PodStatus string
@ -298,6 +302,8 @@ type PodList struct {
Items []Pod `json:"items" yaml:"items,omitempty"`
}
func (*PodList) IsAnAPIObject() {}
// Pod is a collection of containers, used as either input (create, update) or as output (list, get).
type Pod struct {
JSONBase `json:",inline" yaml:",inline"`
@ -306,6 +312,8 @@ type Pod struct {
CurrentState PodState `json:"currentState,omitempty" yaml:"currentState,omitempty"`
}
func (*Pod) IsAnAPIObject() {}
// ReplicationControllerState is the state of a replication controller, either input (create, update) or as output (list, get).
type ReplicationControllerState struct {
Replicas int `json:"replicas" yaml:"replicas"`
@ -319,6 +327,8 @@ type ReplicationControllerList struct {
Items []ReplicationController `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*ReplicationControllerList) IsAnAPIObject() {}
// ReplicationController represents the configuration of a replication controller.
type ReplicationController struct {
JSONBase `json:",inline" yaml:",inline"`
@ -326,6 +336,8 @@ type ReplicationController struct {
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
func (*ReplicationController) IsAnAPIObject() {}
// PodTemplate holds the information used for creating pods.
type PodTemplate struct {
DesiredState PodState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"`
@ -338,6 +350,8 @@ type ServiceList struct {
Items []Service `json:"items" yaml:"items"`
}
func (*ServiceList) IsAnAPIObject() {}
// Service is a named abstraction of software service (for example, mysql) consisting of local port
// (for example 3306) that the proxy listens on, and the selector that determines which pods
// will answer requests sent through the proxy.
@ -357,6 +371,8 @@ type Service struct {
ContainerPort util.IntOrString `json:"containerPort,omitempty" yaml:"containerPort,omitempty"`
}
func (*Service) IsAnAPIObject() {}
// Endpoints is a collection of endpoints that implement the actual service, for example:
// Name: "mysql", Endpoints: ["10.10.1.1:1909", "10.10.2.2:8834"]
type Endpoints struct {
@ -364,12 +380,16 @@ type Endpoints struct {
Endpoints []string `json:"endpoints,omitempty" yaml:"endpoints,omitempty"`
}
func (*Endpoints) IsAnAPIObject() {}
// EndpointsList is a list of endpoints.
type EndpointsList struct {
JSONBase `json:",inline" yaml:",inline"`
Items []Endpoints `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*EndpointsList) IsAnAPIObject() {}
// Minion is a worker node in Kubernetenes.
// The name of the minion according to etcd is in JSONBase.ID.
type Minion struct {
@ -378,6 +398,8 @@ type Minion struct {
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"`
}
func (*Minion) IsAnAPIObject() {}
// MinionList is a list of minions.
type MinionList struct {
JSONBase `json:",inline" yaml:",inline"`
@ -387,6 +409,8 @@ type MinionList struct {
Items []Minion `json:"items,omitempty" yaml:"items,omitempty"`
}
func (*MinionList) IsAnAPIObject() {}
// Binding is written by a scheduler to cause a pod to be bound to a host.
type Binding struct {
JSONBase `json:",inline" yaml:",inline"`
@ -394,6 +418,8 @@ type Binding struct {
Host string `json:"host" yaml:"host"`
}
func (*Binding) IsAnAPIObject() {}
// Status is a return value for calls that don't return other objects.
// TODO: this could go in apiserver, but I'm including it here so clients needn't
// import both.
@ -417,6 +443,8 @@ type Status struct {
Code int `json:"code,omitempty" yaml:"code,omitempty"`
}
func (*Status) IsAnAPIObject() {}
// StatusDetails is a set of additional properties that MAY be set by the
// server to provide additional information about a response. The Reason
// field of a Status object defines what attributes will be set. Clients
@ -540,12 +568,16 @@ type ServerOp struct {
JSONBase `yaml:",inline" json:",inline"`
}
func (*ServerOp) IsAnAPIObject() {}
// ServerOpList is a list of operations, as delivered to API clients.
type ServerOpList struct {
JSONBase `yaml:",inline" json:",inline"`
Items []ServerOp `yaml:"items,omitempty" json:"items,omitempty"`
}
func (*ServerOpList) IsAnAPIObject() {}
// WatchEvent objects are streamed from the api server in response to a watch request.
type WatchEvent struct {
// The type of the watch event; added, modified, or deleted.

View File

@ -33,7 +33,7 @@ func (a *EmbeddedObject) UnmarshalJSON(b []byte) error {
return nil
}
obj, err := Decode(b)
obj, err := Codec.Decode(b)
if err != nil {
return err
}
@ -48,7 +48,7 @@ func (a EmbeddedObject) MarshalJSON() ([]byte, error) {
return []byte("null"), nil
}
return Encode(a.Object)
return Codec.Encode(a.Object)
}
// SetYAML implements the yaml.Setter interface.
@ -67,7 +67,7 @@ func (a *EmbeddedObject) SetYAML(tag string, value interface{}) bool {
if err != nil {
panic("yaml can't reverse its own object")
}
obj, err := Decode(b)
obj, err := Codec.Decode(b)
if err != nil {
return false
}
@ -82,7 +82,7 @@ func (a EmbeddedObject) GetYAML() (tag string, value interface{}) {
return
}
// Encode returns JSON, which is conveniently a subset of YAML.
v, err := Encode(a.Object)
v, err := Codec.Encode(a.Object)
if err != nil {
panic("impossible to encode API object!")
}

View File

@ -22,14 +22,19 @@ import (
"testing"
)
type EmbeddedTest struct {
JSONBase `yaml:",inline" json:",inline"`
Object EmbeddedObject `yaml:"object,omitempty" json:"object,omitempty"`
EmptyObject EmbeddedObject `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"`
}
func (*EmbeddedTest) IsAnAPIObject() {}
func TestEmbeddedObject(t *testing.T) {
type EmbeddedTest struct {
JSONBase `yaml:",inline" json:",inline"`
Object EmbeddedObject `yaml:"object,omitempty" json:"object,omitempty"`
EmptyObject EmbeddedObject `yaml:"emptyObject,omitempty" json:"emptyObject,omitempty"`
}
AddKnownTypes("", EmbeddedTest{})
AddKnownTypes("v1beta1", EmbeddedTest{})
// TODO(dbsmith) fix EmbeddedObject to not use DefaultScheme.
s := DefaultScheme
s.AddKnownTypes("", &EmbeddedTest{})
s.AddKnownTypes("v1beta1", &EmbeddedTest{})
outer := &EmbeddedTest{
JSONBase: JSONBase{ID: "outer"},
@ -40,14 +45,14 @@ func TestEmbeddedObject(t *testing.T) {
},
}
wire, err := Encode(outer)
wire, err := s.Encode(outer)
if err != nil {
t.Fatalf("Unexpected encode error '%v'", err)
}
t.Logf("Wire format is:\n%v\n", string(wire))
decoded, err := Decode(wire)
decoded, err := s.Decode(wire)
if err != nil {
t.Fatalf("Unexpected decode error %v", err)
}

View File

@ -27,38 +27,56 @@ import (
// codec defines methods for serializing and deserializing API
// objects.
type codec interface {
Encode(obj interface{}) (data []byte, err error)
Decode(data []byte) (interface{}, error)
DecodeInto(data []byte, obj interface{}) error
Encode(obj Object) (data []byte, err error)
Decode(data []byte) (Object, error)
DecodeInto(data []byte, obj Object) error
}
// resourceVersioner provides methods for setting and retrieving
// the resource version from an API object.
type resourceVersioner interface {
SetResourceVersion(obj interface{}, version uint64) error
ResourceVersion(obj interface{}) (uint64, error)
SetResourceVersion(obj Object, version uint64) error
ResourceVersion(obj Object) (uint64, error)
}
var ResourceVersioner resourceVersioner = NewJSONBaseResourceVersioner()
var conversionScheme = conversion.NewScheme()
var Codec codec = conversionScheme
var DefaultScheme = NewScheme("", "v1beta1")
var Codec codec = DefaultScheme
func init() {
conversionScheme.InternalVersion = ""
conversionScheme.ExternalVersion = "v1beta1"
conversionScheme.MetaInsertionFactory = metaInsertion{}
// Scheme defines methods for serializing and deserializing API objects. It
// is an adaptation of conversion's Scheme for our API objects.
type Scheme struct {
raw *conversion.Scheme
}
// NewScheme creates a new Scheme. A default scheme is provided and accessible
// as the "DefaultScheme" variable.
func NewScheme(internalVersion, externalVersion string) *Scheme {
s := &Scheme{conversion.NewScheme()}
s.raw.InternalVersion = internalVersion
s.raw.ExternalVersion = externalVersion
s.raw.MetaInsertionFactory = metaInsertion{}
return s
}
// AddKnownTypes registers the types of the arguments to the marshaller of the package api.
// Encode() refuses the object unless its type is registered with AddKnownTypes.
func AddKnownTypes(version string, types ...interface{}) {
conversionScheme.AddKnownTypes(version, types...)
func (s *Scheme) AddKnownTypes(version string, types ...Object) {
interfaces := make([]interface{}, len(types))
for i := range types {
interfaces[i] = types[i]
}
s.raw.AddKnownTypes(version, interfaces...)
}
// New returns a new API object of the given version ("" for internal
// representation) and name, or an error if it hasn't been registered.
func New(versionName, typeName string) (interface{}, error) {
return conversionScheme.NewObject(versionName, typeName)
func (s *Scheme) New(versionName, typeName string) (Object, error) {
obj, err := s.raw.NewObject(versionName, typeName)
if err != nil {
return nil, err
}
return obj.(Object), nil
}
// AddConversionFuncs adds a function to the list of conversion functions. The given
@ -73,20 +91,20 @@ func New(versionName, typeName string) (interface{}, error) {
// sanely copy fields that have the same names. It's OK if the destination type has
// extra fields, but it must not remove any. So you only need to add a conversion
// function for things with changed/removed fields.
func AddConversionFuncs(conversionFuncs ...interface{}) error {
return conversionScheme.AddConversionFuncs(conversionFuncs...)
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
return s.raw.AddConversionFuncs(conversionFuncs...)
}
// Convert will attempt to convert in into out. Both must be pointers to API objects.
// Convert will attempt to convert in into out. Both must be pointers.
// For easy testing of conversion functions. Returns an error if the conversion isn't
// possible.
func Convert(in, out interface{}) error {
return conversionScheme.Convert(in, out)
func (s *Scheme) Convert(in, out interface{}) error {
return s.raw.Convert(in, out)
}
// FindJSONBase takes an arbitary api type, returns pointer to its JSONBase field.
// obj must be a pointer to an api type.
func FindJSONBase(obj interface{}) (JSONBaseInterface, error) {
func FindJSONBase(obj Object) (JSONBaseInterface, error) {
v, err := enforcePtr(obj)
if err != nil {
return nil, err
@ -108,8 +126,8 @@ func FindJSONBase(obj interface{}) (JSONBaseInterface, error) {
}
// EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests.
func EncodeOrDie(obj interface{}) string {
return conversionScheme.EncodeOrDie(obj)
func (s *Scheme) EncodeOrDie(obj Object) string {
return s.raw.EncodeOrDie(obj)
}
// Encode turns the given api object into an appropriate JSON string.
@ -146,14 +164,14 @@ func EncodeOrDie(obj interface{}) string {
// default will be needed, to allow operating in clusters that haven't yet
// upgraded.
//
func Encode(obj interface{}) (data []byte, err error) {
return conversionScheme.Encode(obj)
func (s *Scheme) Encode(obj Object) (data []byte, err error) {
return s.raw.Encode(obj)
}
// enforcePtr ensures that obj is a pointer of some sort. Returns a reflect.Value of the
// dereferenced pointer, ensuring that it is settable/addressable.
// Returns an error if this is not possible.
func enforcePtr(obj interface{}) (reflect.Value, error) {
func enforcePtr(obj Object) (reflect.Value, error) {
v := reflect.ValueOf(obj)
if v.Kind() != reflect.Ptr {
return reflect.Value{}, fmt.Errorf("expected pointer, but got %v", v.Type().Name())
@ -181,8 +199,12 @@ func VersionAndKind(data []byte) (version, kind string, err error) {
// Deduces the type based upon the APIVersion and Kind fields, which are set
// by Encode. Only versioned objects (APIVersion != "") are accepted. The object
// will be converted into the in-memory unversioned type before being returned.
func Decode(data []byte) (interface{}, error) {
return conversionScheme.Decode(data)
func (s *Scheme) Decode(data []byte) (Object, error) {
obj, err := s.raw.Decode(data)
if err != nil {
return nil, err
}
return obj.(Object), nil
}
// DecodeInto parses a YAML or JSON string and stores it in obj. Returns an error
@ -190,22 +212,22 @@ func Decode(data []byte) (interface{}, 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.
func DecodeInto(data []byte, obj interface{}) error {
return conversionScheme.DecodeInto(data, obj)
func (s *Scheme) DecodeInto(data []byte, obj Object) error {
return s.raw.DecodeInto(data, obj)
}
// Does a deep copy of an API object. Useful mostly for tests.
// TODO(dbsmith): implement directly instead of via Encode/Decode
func Copy(obj interface{}) (interface{}, error) {
data, err := Encode(obj)
func (s *Scheme) Copy(obj Object) (Object, error) {
data, err := s.Encode(obj)
if err != nil {
return nil, err
}
return Decode(data)
return s.Decode(data)
}
func CopyOrDie(obj interface{}) interface{} {
newObj, err := Copy(obj)
func (s *Scheme) CopyOrDie(obj Object) Object {
newObj, err := s.Copy(obj)
if err != nil {
panic(err)
}

View File

@ -25,31 +25,13 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
func TestEncode_NonPtr(t *testing.T) {
pod := api.Pod{
Labels: map[string]string{"name": "foo"},
}
obj := interface{}(pod)
data, err := runtime.Encode(obj)
obj2, err2 := runtime.Decode(data)
if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v'", err, err2)
}
if _, ok := obj2.(*api.Pod); !ok {
t.Fatalf("Got wrong type")
}
if !reflect.DeepEqual(obj2, &pod) {
t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2)
}
}
func TestEncode_Ptr(t *testing.T) {
func TestEncode(t *testing.T) {
pod := &api.Pod{
Labels: map[string]string{"name": "foo"},
}
obj := interface{}(pod)
data, err := runtime.Encode(obj)
obj2, err2 := runtime.Decode(data)
obj := runtime.Object(pod)
data, err := runtime.DefaultScheme.Encode(obj)
obj2, err2 := runtime.DefaultScheme.Decode(data)
if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v'", err, err2)
}
@ -63,11 +45,11 @@ func TestEncode_Ptr(t *testing.T) {
func TestBadJSONRejection(t *testing.T) {
badJSONMissingKind := []byte(`{ }`)
if _, err := runtime.Decode(badJSONMissingKind); err == nil {
if _, err := runtime.DefaultScheme.Decode(badJSONMissingKind); err == nil {
t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
}
badJSONUnknownType := []byte(`{"kind": "bar"}`)
if _, err1 := runtime.Decode(badJSONUnknownType); err1 == nil {
if _, err1 := runtime.DefaultScheme.Decode(badJSONUnknownType); err1 == nil {
t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
}
/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)

View File

@ -29,7 +29,7 @@ func NewJSONBaseResourceVersioner() resourceVersioner {
type jsonBaseResourceVersioner struct{}
func (v jsonBaseResourceVersioner) ResourceVersion(obj interface{}) (uint64, error) {
func (v jsonBaseResourceVersioner) ResourceVersion(obj Object) (uint64, error) {
json, err := FindJSONBase(obj)
if err != nil {
return 0, err
@ -37,7 +37,7 @@ func (v jsonBaseResourceVersioner) ResourceVersion(obj interface{}) (uint64, err
return json.ResourceVersion(), nil
}
func (v jsonBaseResourceVersioner) SetResourceVersion(obj interface{}, version uint64) error {
func (v jsonBaseResourceVersioner) SetResourceVersion(obj Object, version uint64) error {
json, err := FindJSONBase(obj)
if err != nil {
return err

View File

@ -77,20 +77,20 @@ func TestGenericJSONBase(t *testing.T) {
}
}
type MyAPIObject struct {
JSONBase `yaml:",inline" json:",inline"`
}
func (*MyAPIObject) IsAnAPIObject() {}
type MyIncorrectlyMarkedAsAPIObject struct {
}
func (*MyIncorrectlyMarkedAsAPIObject) IsAnAPIObject() {}
func TestResourceVersionerOfAPI(t *testing.T) {
type JSONBase struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
ID string `json:"id,omitempty" yaml:"id,omitempty"`
CreationTimestamp util.Time `json:"creationTimestamp,omitempty" yaml:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty" yaml:"selfLink,omitempty"`
ResourceVersion uint64 `json:"resourceVersion,omitempty" yaml:"resourceVersion,omitempty"`
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
}
type MyAPIObject struct {
JSONBase `yaml:",inline" json:",inline"`
}
type T struct {
Object interface{}
Object
Expected uint64
}
testCases := map[string]T{
@ -110,10 +110,10 @@ func TestResourceVersionerOfAPI(t *testing.T) {
}
failingCases := map[string]struct {
Object interface{}
Object
Expected uint64
}{
"not a valid object to try": {JSONBase{ResourceVersion: 1}, 1},
"not a valid object to try": {&MyIncorrectlyMarkedAsAPIObject{}, 1},
}
for key, testCase := range failingCases {
_, err := versioning.ResourceVersion(testCase.Object)
@ -123,7 +123,7 @@ func TestResourceVersionerOfAPI(t *testing.T) {
}
setCases := map[string]struct {
Object interface{}
Object
Expected uint64
}{
"pointer to api object with version": {&MyAPIObject{JSONBase: JSONBase{ResourceVersion: 1}}, 1},
@ -140,17 +140,4 @@ func TestResourceVersionerOfAPI(t *testing.T) {
t.Errorf("%s: expected %d, got %d", key, 5, actual)
}
}
failingSetCases := map[string]struct {
Object interface{}
Expected uint64
}{
"empty api object": {MyAPIObject{}, 0},
"api object with version": {MyAPIObject{JSONBase: JSONBase{ResourceVersion: 1}}, 1},
}
for key, testCase := range failingSetCases {
if err := versioning.SetResourceVersion(testCase.Object, 5); err == nil {
t.Errorf("%s: unexpected non-error", key)
}
}
}

View File

@ -20,6 +20,15 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
// All api types must support the Object interface. It's deliberately tiny so that this is not an onerous
// burden. Implement it with a pointer reciever; this will allow us to use the go compiler to check the
// one thing about our objects that it's capable of checking for us.
type Object interface {
// This function is used only to enforce membership. It's never called.
// TODO: Consider mass rename in the future to make it do something useful.
IsAnAPIObject()
}
// 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.
@ -29,8 +38,9 @@ import (
// runtime.JSONBase `yaml:",inline" json:",inline"`
// ... // other fields
// }
// func (*MyAwesomeAPIObject) IsAnAPIObject() {}
//
// JSONBase is provided here for convenience. You may use it directlly from this package or define
// JSONBase is provided here for convenience. You may use it directly from this package or define
// your own with the same fields.
//
type JSONBase struct {
@ -43,17 +53,16 @@ type JSONBase struct {
}
// EmbeddedObject has appropriate encoder and decoder functions, such that on the wire, it's
// stored as a []byte, but in memory, the contained object is accessable as an interface{}
// via the Get() function. Only objects having a JSONBase may be stored via Object.
// stored as a []byte, but in memory, the contained object is accessable as an Object
// via the Get() function. Only valid API objects may be stored via EmbeddedObject.
// The purpose of this is to allow an API object of type known only at runtime to be
// embedded within other API objects.
//
// Note that object assumes that you've registered all of your api types with the api package.
//
// Note that objects will be serialized into the api package's default external versioned type;
// this should be fixed in the future to use the version of the current Codec instead.
// TODO(dbsmith): Stop using runtime.Codec, use the codec appropriate for the conversion (I have a plan).
type EmbeddedObject struct {
Object interface{}
Object
}
// Extension allows api objects with unknown types to be passed-through. This can be used
@ -61,4 +70,8 @@ type EmbeddedObject struct {
// JSONBase features-- kind, version, resourceVersion, etc.
// TODO: Not implemented yet
type Extension struct {
JSONBase `yaml:",inline" json:",inline"`
// RawJSON to go here.
}
func (*Extension) IsAnAPIObject() {}