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() { func init() {
runtime.AddKnownTypes("", runtime.DefaultScheme.AddKnownTypes("",
PodList{}, &PodList{},
Pod{}, &Pod{},
ReplicationControllerList{}, &ReplicationControllerList{},
ReplicationController{}, &ReplicationController{},
ServiceList{}, &ServiceList{},
Service{}, &Service{},
MinionList{}, &MinionList{},
Minion{}, &Minion{},
Status{}, &Status{},
ServerOpList{}, &ServerOpList{},
ServerOp{}, &ServerOp{},
ContainerManifestList{}, &ContainerManifestList{},
Endpoints{}, &Endpoints{},
EndpointsList{}, &EndpointsList{},
Binding{}, &Binding{},
) )
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -77,20 +77,20 @@ func TestGenericJSONBase(t *testing.T) {
} }
} }
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 { type MyAPIObject struct {
JSONBase `yaml:",inline" json:",inline"` JSONBase `yaml:",inline" json:",inline"`
} }
func (*MyAPIObject) IsAnAPIObject() {}
type MyIncorrectlyMarkedAsAPIObject struct {
}
func (*MyIncorrectlyMarkedAsAPIObject) IsAnAPIObject() {}
func TestResourceVersionerOfAPI(t *testing.T) {
type T struct { type T struct {
Object interface{} Object
Expected uint64 Expected uint64
} }
testCases := map[string]T{ testCases := map[string]T{
@ -110,10 +110,10 @@ func TestResourceVersionerOfAPI(t *testing.T) {
} }
failingCases := map[string]struct { failingCases := map[string]struct {
Object interface{} Object
Expected uint64 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 { for key, testCase := range failingCases {
_, err := versioning.ResourceVersion(testCase.Object) _, err := versioning.ResourceVersion(testCase.Object)
@ -123,7 +123,7 @@ func TestResourceVersionerOfAPI(t *testing.T) {
} }
setCases := map[string]struct { setCases := map[string]struct {
Object interface{} Object
Expected uint64 Expected uint64
}{ }{
"pointer to api object with version": {&MyAPIObject{JSONBase: JSONBase{ResourceVersion: 1}}, 1}, "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) 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" "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 // 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. // safe to use from within all versions of every API object.
@ -29,8 +38,9 @@ import (
// runtime.JSONBase `yaml:",inline" json:",inline"` // runtime.JSONBase `yaml:",inline" json:",inline"`
// ... // other fields // ... // 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. // your own with the same fields.
// //
type JSONBase struct { type JSONBase struct {
@ -43,17 +53,16 @@ type JSONBase struct {
} }
// EmbeddedObject has appropriate encoder and decoder functions, such that on the wire, it's // 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{} // stored as a []byte, but in memory, the contained object is accessable as an Object
// via the Get() function. Only objects having a JSONBase may be stored via 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 // The purpose of this is to allow an API object of type known only at runtime to be
// embedded within other API objects. // embedded within other API objects.
// //
// Note that object assumes that you've registered all of your api types with the api package. // 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; // TODO(dbsmith): Stop using runtime.Codec, use the codec appropriate for the conversion (I have a plan).
// this should be fixed in the future to use the version of the current Codec instead.
type EmbeddedObject struct { type EmbeddedObject struct {
Object interface{} Object
} }
// Extension allows api objects with unknown types to be passed-through. This can be used // 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. // JSONBase features-- kind, version, resourceVersion, etc.
// TODO: Not implemented yet // TODO: Not implemented yet
type Extension struct { type Extension struct {
JSONBase `yaml:",inline" json:",inline"`
// RawJSON to go here.
} }
func (*Extension) IsAnAPIObject() {}