diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index 36f1d004b9a..14fe09fdd33 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -179,7 +179,7 @@ func runReplicationControllerTest(c *client.Client) { } glog.Infof("Creating replication controllers") - if _, err := c.CreateReplicationController(controllerRequest); err != nil { + if _, err := c.CreateReplicationController(&controllerRequest); err != nil { glog.Fatalf("Unexpected error: %#v", err) } glog.Infof("Done creating replication controllers") @@ -194,7 +194,7 @@ func runReplicationControllerTest(c *client.Client) { if err != nil { glog.Fatalf("FAILED: unable to get pods to list: %v", err) } - if err := wait.Poll(time.Second, time.Second*10, podsOnMinions(c, pods)); err != nil { + if err := wait.Poll(time.Second, time.Second*10, podsOnMinions(c, *pods)); err != nil { glog.Fatalf("FAILED: pods never started running %v", err) } @@ -204,7 +204,7 @@ func runReplicationControllerTest(c *client.Client) { func runAtomicPutTest(c *client.Client) { var svc api.Service err := c.Post().Path("services").Body( - api.Service{ + &api.Service{ JSONBase: api.JSONBase{ID: "atomicservice", APIVersion: "v1beta1"}, Port: 12345, Labels: map[string]string{ diff --git a/cmd/kubecfg/kubecfg.go b/cmd/kubecfg/kubecfg.go index 4e32da40a95..7cd590f3a07 100644 --- a/cmd/kubecfg/kubecfg.go +++ b/cmd/kubecfg/kubecfg.go @@ -58,11 +58,11 @@ var ( imageName = flag.String("image", "", "Image used when updating a replicationController. Will apply to the first container in the pod template.") ) -var parser = kubecfg.NewParser(map[string]interface{}{ - "pods": api.Pod{}, - "services": api.Service{}, - "replicationControllers": api.ReplicationController{}, - "minions": api.Minion{}, +var parser = kubecfg.NewParser(map[string]runtime.Object{ + "pods": &api.Pod{}, + "services": &api.Service{}, + "replicationControllers": &api.ReplicationController{}, + "minions": &api.Minion{}, }) func usage() { @@ -266,7 +266,7 @@ func executeAPIRequest(method string, c *client.Client) bool { if setBody { if version != 0 { data := readConfig(storage) - obj, err := runtime.Decode(data) + obj, err := runtime.DefaultCodec.Decode(data) if err != nil { glog.Fatalf("error setting resource version: %v", err) } @@ -275,7 +275,7 @@ func executeAPIRequest(method string, c *client.Client) bool { glog.Fatalf("error setting resource version: %v", err) } jsonBase.SetResourceVersion(version) - data, err = runtime.Encode(obj) + data, err = runtime.DefaultCodec.Encode(obj) if err != nil { glog.Fatalf("error setting resource version: %v", err) } diff --git a/examples/examples_test.go b/examples/examples_test.go index 2747dc46cf3..3abb905cf3b 100644 --- a/examples/examples_test.go +++ b/examples/examples_test.go @@ -31,7 +31,7 @@ import ( "github.com/golang/glog" ) -func validateObject(obj interface{}) (errors []error) { +func validateObject(obj runtime.Object) (errors []error) { switch t := obj.(type) { case *api.ReplicationController: errors = validation.ValidateManifest(&t.DesiredState.PodTemplate.DesiredState.Manifest) @@ -85,7 +85,7 @@ func walkJSONFiles(inDir string, fn func(name, path string, data []byte)) error } func TestApiExamples(t *testing.T) { - expected := map[string]interface{}{ + expected := map[string]runtime.Object{ "controller": &api.ReplicationController{}, "controller-list": &api.ReplicationControllerList{}, "pod": &api.Pod{}, @@ -103,7 +103,7 @@ func TestApiExamples(t *testing.T) { return } tested += 1 - if err := runtime.DecodeInto(data, expectedType); err != nil { + if err := runtime.DefaultCodec.DecodeInto(data, expectedType); err != nil { t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data)) return } @@ -120,7 +120,7 @@ func TestApiExamples(t *testing.T) { } func TestExamples(t *testing.T) { - expected := map[string]interface{}{ + expected := map[string]runtime.Object{ "frontend-controller": &api.ReplicationController{}, "redis-slave-controller": &api.ReplicationController{}, "redis-master": &api.Pod{}, @@ -137,7 +137,7 @@ func TestExamples(t *testing.T) { return } tested += 1 - if err := runtime.DecodeInto(data, expectedType); err != nil { + if err := runtime.DefaultCodec.DecodeInto(data, expectedType); err != nil { t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data)) return } @@ -168,14 +168,14 @@ func TestReadme(t *testing.T) { } for _, json := range match[1:] { expectedType := &api.Pod{} - if err := runtime.DecodeInto([]byte(json), expectedType); err != nil { + if err := runtime.DefaultCodec.DecodeInto([]byte(json), expectedType); err != nil { t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data)) return } if errors := validateObject(expectedType); len(errors) > 0 { t.Errorf("%s did not validate correctly: %v", path, errors) } - encoded, err := runtime.Encode(expectedType) + encoded, err := runtime.DefaultCodec.Encode(expectedType) if err != nil { t.Errorf("Could not encode object: %v", err) continue diff --git a/pkg/api/register.go b/pkg/api/register.go index 54bd719bddb..909bb906d2f 100644 --- a/pkg/api/register.go +++ b/pkg/api/register.go @@ -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{}, ) } diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 985b595272e..b1c89045b5f 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -85,7 +85,7 @@ var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs( }, ) -func objDiff(a, b interface{}) string { +func objDiff(a, b runtime.Object) string { ab, err := json.Marshal(a) if err != nil { panic("a") @@ -105,7 +105,7 @@ func objDiff(a, b interface{}) string { ) } -func runTest(t *testing.T, source interface{}) { +func runTest(t *testing.T, source runtime.Object) { name := reflect.TypeOf(source).Elem().Name() apiObjectFuzzer.Fuzz(source) j, err := runtime.FindJSONBase(source) @@ -115,13 +115,13 @@ func runTest(t *testing.T, source interface{}) { j.SetKind("") j.SetAPIVersion("") - data, err := runtime.Encode(source) + data, err := runtime.DefaultCodec.Encode(source) if err != nil { t.Errorf("%v: %v (%#v)", name, err, source) return } - obj2, err := runtime.Decode(data) + obj2, err := runtime.DefaultCodec.Decode(data) if err != nil { t.Errorf("%v: %v", name, err) return @@ -131,8 +131,8 @@ func runTest(t *testing.T, source interface{}) { return } } - obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface() - err = runtime.DecodeInto(data, obj3) + obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface().(runtime.Object) + err = runtime.DefaultCodec.DecodeInto(data, obj3) if err != nil { t.Errorf("2: %v: %v", name, err) return @@ -145,7 +145,7 @@ func runTest(t *testing.T, source interface{}) { } func TestTypes(t *testing.T) { - table := []interface{}{ + table := []runtime.Object{ &api.PodList{}, &api.Pod{}, &api.ServiceList{}, @@ -169,31 +169,13 @@ func TestTypes(t *testing.T) { } } -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) { 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.DefaultCodec.Encode(obj) + obj2, err2 := runtime.DefaultCodec.Decode(data) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } @@ -207,11 +189,11 @@ func TestEncode_Ptr(t *testing.T) { func TestBadJSONRejection(t *testing.T) { badJSONMissingKind := []byte(`{ }`) - if _, err := runtime.Decode(badJSONMissingKind); err == nil { + if _, err := runtime.DefaultCodec.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.DefaultCodec.Decode(badJSONUnknownType); err1 == nil { t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) } /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) diff --git a/pkg/api/types.go b/pkg/api/types.go index 76ebc5edae2..365c0c94a44 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -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. diff --git a/pkg/api/v1beta1/conversion.go b/pkg/api/v1beta1/conversion.go index 20c42e999a3..d83c001c2d9 100644 --- a/pkg/api/v1beta1/conversion.go +++ b/pkg/api/v1beta1/conversion.go @@ -19,22 +19,20 @@ package v1beta1 import ( // Alias this so it can be easily changed when we cut the next version. newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) func init() { - // Shortcut for sub-conversions. TODO: This should possibly be refactored - // such that this convert function is passed to each conversion func. - Convert := runtime.Convert - runtime.AddConversionFuncs( + runtime.DefaultScheme.AddConversionFuncs( // EnvVar's Key is deprecated in favor of Name. - func(in *newer.EnvVar, out *EnvVar) error { + func(in *newer.EnvVar, out *EnvVar, s conversion.Scope) error { out.Value = in.Value out.Key = in.Name out.Name = in.Name return nil }, - func(in *EnvVar, out *newer.EnvVar) error { + func(in *EnvVar, out *newer.EnvVar, s conversion.Scope) error { out.Value = in.Value if in.Name != "" { out.Name = in.Name @@ -45,7 +43,7 @@ func init() { }, // Path & MountType are deprecated. - func(in *newer.VolumeMount, out *VolumeMount) error { + func(in *newer.VolumeMount, out *VolumeMount, s conversion.Scope) error { out.Name = in.Name out.ReadOnly = in.ReadOnly out.MountPath = in.MountPath @@ -53,7 +51,7 @@ func init() { out.MountType = "" // MountType is ignored. return nil }, - func(in *VolumeMount, out *newer.VolumeMount) error { + func(in *VolumeMount, out *newer.VolumeMount, s conversion.Scope) error { out.Name = in.Name out.ReadOnly = in.ReadOnly if in.MountPath == "" { @@ -65,18 +63,18 @@ func init() { }, // MinionList.Items had a wrong name in v1beta1 - func(in *newer.MinionList, out *MinionList) error { - Convert(&in.JSONBase, &out.JSONBase) - Convert(&in.Items, &out.Items) + func(in *newer.MinionList, out *MinionList, s conversion.Scope) error { + s.Convert(&in.JSONBase, &out.JSONBase, 0) + s.Convert(&in.Items, &out.Items, 0) out.Minions = out.Items return nil }, - func(in *MinionList, out *newer.MinionList) error { - Convert(&in.JSONBase, &out.JSONBase) + func(in *MinionList, out *newer.MinionList, s conversion.Scope) error { + s.Convert(&in.JSONBase, &out.JSONBase, 0) if len(in.Items) == 0 { - Convert(&in.Minions, &out.Items) + s.Convert(&in.Minions, &out.Items, 0) } else { - Convert(&in.Items, &out.Items) + s.Convert(&in.Items, &out.Items, 0) } return nil }, diff --git a/pkg/api/v1beta1/conversion_test.go b/pkg/api/v1beta1/conversion_test.go index 8a2f3b85fed..2c151445578 100644 --- a/pkg/api/v1beta1/conversion_test.go +++ b/pkg/api/v1beta1/conversion_test.go @@ -25,7 +25,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) -var Convert = runtime.Convert +var Convert = runtime.DefaultScheme.Convert func TestEnvConversion(t *testing.T) { nonCanonical := []v1beta1.EnvVar{ diff --git a/pkg/api/v1beta1/register.go b/pkg/api/v1beta1/register.go index c5768bac8aa..7dd8f83c559 100644 --- a/pkg/api/v1beta1/register.go +++ b/pkg/api/v1beta1/register.go @@ -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{}, ) } diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 1bd2cccd5fe..74d576d8049 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -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. diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index d08f5dbe006..d6e13a6991d 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -27,17 +27,11 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz" "github.com/GoogleCloudPlatform/kubernetes/pkg/httplog" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/version" "github.com/golang/glog" ) -// 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 -} - // mux is an object that can register http handlers. type mux interface { Handle(pattern string, handler http.Handler) @@ -53,7 +47,7 @@ type defaultAPIServer struct { // Handle returns a Handler function that expose the provided storage interfaces // as RESTful resources at prefix, serialized by codec, and also includes the support // http resources. -func Handle(storage map[string]RESTStorage, codec Codec, prefix string) http.Handler { +func Handle(storage map[string]RESTStorage, codec runtime.Codec, prefix string) http.Handler { group := NewAPIGroup(storage, codec) mux := http.NewServeMux() @@ -78,7 +72,7 @@ type APIGroup struct { // This is a helper method for registering multiple sets of REST handlers under different // prefixes onto a server. // TODO: add multitype codec serialization -func NewAPIGroup(storage map[string]RESTStorage, codec Codec) *APIGroup { +func NewAPIGroup(storage map[string]RESTStorage, codec runtime.Codec) *APIGroup { return &APIGroup{RESTHandler{ storage: storage, codec: codec, @@ -147,7 +141,7 @@ func handleVersion(w http.ResponseWriter, req *http.Request) { } // writeJSON renders an object as JSON to the response. -func writeJSON(statusCode int, codec Codec, object interface{}, w http.ResponseWriter) { +func writeJSON(statusCode int, codec runtime.Codec, object runtime.Object, w http.ResponseWriter) { output, err := codec.Encode(object) if err != nil { errorJSON(err, codec, w) @@ -159,7 +153,7 @@ func writeJSON(statusCode int, codec Codec, object interface{}, w http.ResponseW } // errorJSON renders an error to the response. -func errorJSON(err error, codec Codec, w http.ResponseWriter) { +func errorJSON(err error, codec runtime.Codec, w http.ResponseWriter) { status := errToAPIStatus(err) writeJSON(status.Code, codec, status, w) } diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index 75bbbab51ab..12d8ae3585c 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -38,15 +38,15 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" ) -func convert(obj interface{}) (interface{}, error) { +func convert(obj runtime.Object) (runtime.Object, error) { return obj, nil } -var codec = runtime.Codec +var codec = runtime.DefaultCodec func init() { - runtime.AddKnownTypes("", Simple{}, SimpleList{}) - runtime.AddKnownTypes("v1beta1", Simple{}, SimpleList{}) + runtime.DefaultScheme.AddKnownTypes("", &Simple{}, &SimpleList{}) + runtime.DefaultScheme.AddKnownTypes("v1beta1", &Simple{}, &SimpleList{}) } type Simple struct { @@ -54,11 +54,15 @@ type Simple struct { Name string `yaml:"name,omitempty" json:"name,omitempty"` } +func (*Simple) IsAnAPIObject() {} + type SimpleList struct { api.JSONBase `yaml:",inline" json:",inline"` Items []Simple `yaml:"items,omitempty" json:"items,omitempty"` } +func (*SimpleList) IsAnAPIObject() {} + type SimpleRESTStorage struct { errors map[string]error list []Simple @@ -78,43 +82,43 @@ type SimpleRESTStorage struct { // If non-nil, called inside the WorkFunc when answering update, delete, create. // obj receives the original input to the update, delete, or create call. - injectedFunction func(obj interface{}) (returnObj interface{}, err error) + injectedFunction func(obj runtime.Object) (returnObj runtime.Object, err error) } -func (storage *SimpleRESTStorage) List(labels.Selector) (interface{}, error) { +func (storage *SimpleRESTStorage) List(labels.Selector) (runtime.Object, error) { result := &SimpleList{ Items: storage.list, } return result, storage.errors["list"] } -func (storage *SimpleRESTStorage) Get(id string) (interface{}, error) { - return storage.item, storage.errors["get"] +func (storage *SimpleRESTStorage) Get(id string) (runtime.Object, error) { + return runtime.DefaultScheme.CopyOrDie(&storage.item), storage.errors["get"] } -func (storage *SimpleRESTStorage) Delete(id string) (<-chan interface{}, error) { +func (storage *SimpleRESTStorage) Delete(id string) (<-chan runtime.Object, error) { storage.deleted = id if err := storage.errors["delete"]; err != nil { return nil, err } - return MakeAsync(func() (interface{}, error) { + return MakeAsync(func() (runtime.Object, error) { if storage.injectedFunction != nil { - return storage.injectedFunction(id) + return storage.injectedFunction(&Simple{JSONBase: api.JSONBase{ID: id}}) } return &api.Status{Status: api.StatusSuccess}, nil }), nil } -func (storage *SimpleRESTStorage) New() interface{} { +func (storage *SimpleRESTStorage) New() runtime.Object { return &Simple{} } -func (storage *SimpleRESTStorage) Create(obj interface{}) (<-chan interface{}, error) { +func (storage *SimpleRESTStorage) Create(obj runtime.Object) (<-chan runtime.Object, error) { storage.created = obj.(*Simple) if err := storage.errors["create"]; err != nil { return nil, err } - return MakeAsync(func() (interface{}, error) { + return MakeAsync(func() (runtime.Object, error) { if storage.injectedFunction != nil { return storage.injectedFunction(obj) } @@ -122,12 +126,12 @@ func (storage *SimpleRESTStorage) Create(obj interface{}) (<-chan interface{}, e }), nil } -func (storage *SimpleRESTStorage) Update(obj interface{}) (<-chan interface{}, error) { +func (storage *SimpleRESTStorage) Update(obj runtime.Object) (<-chan runtime.Object, error) { storage.updated = obj.(*Simple) if err := storage.errors["update"]; err != nil { return nil, err } - return MakeAsync(func() (interface{}, error) { + return MakeAsync(func() (runtime.Object, error) { if storage.injectedFunction != nil { return storage.injectedFunction(obj) } @@ -156,7 +160,7 @@ func (storage *SimpleRESTStorage) ResourceLocation(id string) (string, error) { return id, nil } -func extractBody(response *http.Response, object interface{}) (string, error) { +func extractBody(response *http.Response, object runtime.Object) (string, error) { defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { @@ -398,7 +402,7 @@ func TestUpdate(t *testing.T) { handler := Handle(storage, codec, "/prefix/version") server := httptest.NewServer(handler) - item := Simple{ + item := &Simple{ Name: "bar", } body, err := codec.Encode(item) @@ -428,7 +432,7 @@ func TestUpdateMissing(t *testing.T) { handler := Handle(storage, codec, "/prefix/version") server := httptest.NewServer(handler) - item := Simple{ + item := &Simple{ Name: "bar", } body, err := codec.Encode(item) @@ -457,7 +461,7 @@ func TestCreate(t *testing.T) { server := httptest.NewServer(handler) client := http.Client{} - simple := Simple{ + simple := &Simple{ Name: "foo", } data, _ := codec.Encode(simple) @@ -497,7 +501,7 @@ func TestCreateNotFound(t *testing.T) { server := httptest.NewServer(handler) client := http.Client{} - simple := Simple{Name: "foo"} + simple := &Simple{Name: "foo"} data, _ := codec.Encode(simple) request, err := http.NewRequest("POST", server.URL+"/prefix/version/simple", bytes.NewBuffer(data)) if err != nil { @@ -528,7 +532,7 @@ func TestParseTimeout(t *testing.T) { func TestSyncCreate(t *testing.T) { storage := SimpleRESTStorage{ - injectedFunction: func(obj interface{}) (interface{}, error) { + injectedFunction: func(obj runtime.Object) (runtime.Object, error) { time.Sleep(5 * time.Millisecond) return obj, nil }, @@ -539,7 +543,7 @@ func TestSyncCreate(t *testing.T) { server := httptest.NewServer(handler) client := http.Client{} - simple := Simple{ + simple := &Simple{ Name: "foo", } data, _ := codec.Encode(simple) @@ -566,7 +570,7 @@ func TestSyncCreate(t *testing.T) { t.Errorf("unexpected error: %v", err) } - if !reflect.DeepEqual(itemOut, simple) { + if !reflect.DeepEqual(&itemOut, simple) { t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body)) } if response.StatusCode != http.StatusOK { @@ -600,7 +604,7 @@ func expectApiStatus(t *testing.T, method, url string, data []byte, code int) *a func TestAsyncDelayReturnsError(t *testing.T) { storage := SimpleRESTStorage{ - injectedFunction: func(obj interface{}) (interface{}, error) { + injectedFunction: func(obj runtime.Object) (runtime.Object, error) { return nil, apierrs.NewAlreadyExists("foo", "bar") }, } @@ -617,7 +621,7 @@ func TestAsyncDelayReturnsError(t *testing.T) { func TestAsyncCreateError(t *testing.T) { ch := make(chan struct{}) storage := SimpleRESTStorage{ - injectedFunction: func(obj interface{}) (interface{}, error) { + injectedFunction: func(obj runtime.Object) (runtime.Object, error) { <-ch return nil, apierrs.NewAlreadyExists("foo", "bar") }, @@ -626,7 +630,7 @@ func TestAsyncCreateError(t *testing.T) { handler.(*defaultAPIServer).group.handler.asyncOpWait = 0 server := httptest.NewServer(handler) - simple := Simple{Name: "foo"} + simple := &Simple{Name: "foo"} data, _ := codec.Encode(simple) status := expectApiStatus(t, "POST", fmt.Sprintf("%s/prefix/version/foo", server.URL), data, http.StatusAccepted) @@ -662,18 +666,21 @@ func TestAsyncCreateError(t *testing.T) { } } +type UnregisteredAPIObject struct { + Value string +} + +func (*UnregisteredAPIObject) IsAnAPIObject() {} + func TestWriteJSONDecodeError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - type T struct { - Value string - } - writeJSON(http.StatusOK, runtime.Codec, &T{"Undecodable"}, w) + writeJSON(http.StatusOK, runtime.DefaultCodec, &UnregisteredAPIObject{"Undecodable"}, w) })) status := expectApiStatus(t, "GET", server.URL, nil, http.StatusInternalServerError) if status.Reason != api.StatusReasonUnknown { t.Errorf("unexpected reason %#v", status) } - if !strings.Contains(status.Message, "type apiserver.T is not registered") { + if !strings.Contains(status.Message, "type apiserver.UnregisteredAPIObject is not registered") { t.Errorf("unexpected message %#v", status) } } @@ -705,7 +712,7 @@ func TestSyncCreateTimeout(t *testing.T) { testOver := make(chan struct{}) defer close(testOver) storage := SimpleRESTStorage{ - injectedFunction: func(obj interface{}) (interface{}, error) { + injectedFunction: func(obj runtime.Object) (runtime.Object, error) { // Eliminate flakes by ensuring the create operation takes longer than this test. <-testOver return obj, nil @@ -716,7 +723,7 @@ func TestSyncCreateTimeout(t *testing.T) { }, codec, "/prefix/version") server := httptest.NewServer(handler) - simple := Simple{Name: "foo"} + simple := &Simple{Name: "foo"} data, _ := codec.Encode(simple) itemOut := expectApiStatus(t, "POST", server.URL+"/prefix/version/foo?sync=true&timeout=4ms", data, http.StatusAccepted) if itemOut.Status != api.StatusWorking || itemOut.Details == nil || itemOut.Details.ID == "" { diff --git a/pkg/apiserver/async.go b/pkg/apiserver/async.go index a87c9766a1d..97a5919d070 100644 --- a/pkg/apiserver/async.go +++ b/pkg/apiserver/async.go @@ -17,18 +17,19 @@ limitations under the License. package apiserver import ( + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) // WorkFunc is used to perform any time consuming work for an api call, after // the input has been validated. Pass one of these to MakeAsync to create an // appropriate return value for the Update, Delete, and Create methods. -type WorkFunc func() (result interface{}, err error) +type WorkFunc func() (result runtime.Object, err error) // MakeAsync takes a function and executes it, delivering the result in the way required // by RESTStorage's Update, Delete, and Create methods. -func MakeAsync(fn WorkFunc) <-chan interface{} { - channel := make(chan interface{}) +func MakeAsync(fn WorkFunc) <-chan runtime.Object { + channel := make(chan runtime.Object) go func() { defer util.HandleCrash() obj, err := fn() diff --git a/pkg/apiserver/interfaces.go b/pkg/apiserver/interfaces.go index 201436a5e9b..c2a4f2c5751 100644 --- a/pkg/apiserver/interfaces.go +++ b/pkg/apiserver/interfaces.go @@ -18,6 +18,7 @@ package apiserver import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" ) @@ -25,25 +26,25 @@ import ( // Resources which are exported to the RESTful API of apiserver need to implement this interface. type RESTStorage interface { // New returns an empty object that can be used with Create and Update after request data has been put into it. - // This object must be a pointer type for use with Codec.DecodeInto([]byte, interface{}) - New() interface{} + // This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object) + New() runtime.Object // List selects resources in the storage which match to the selector. // TODO: add field selector in addition to label selector. - List(labels.Selector) (interface{}, error) + List(labels.Selector) (runtime.Object, error) // Get finds a resource in the storage by id and returns it. // Although it can return an arbitrary error value, IsNotFound(err) is true for the // returned error value err when the specified resource is not found. - Get(id string) (interface{}, error) + Get(id string) (runtime.Object, error) // Delete finds a resource in the storage and deletes it. // Although it can return an arbitrary error value, IsNotFound(err) is true for the // returned error value err when the specified resource is not found. - Delete(id string) (<-chan interface{}, error) + Delete(id string) (<-chan runtime.Object, error) - Create(interface{}) (<-chan interface{}, error) - Update(interface{}) (<-chan interface{}, error) + Create(runtime.Object) (<-chan runtime.Object, error) + Update(runtime.Object) (<-chan runtime.Object, error) } // ResourceWatcher should be implemented by all RESTStorage objects that diff --git a/pkg/apiserver/operation.go b/pkg/apiserver/operation.go index 34cd12c9260..e0a3e9a461f 100644 --- a/pkg/apiserver/operation.go +++ b/pkg/apiserver/operation.go @@ -25,12 +25,13 @@ import ( "time" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) type OperationHandler struct { ops *Operations - codec Codec + codec runtime.Codec } func (h *OperationHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { @@ -63,8 +64,8 @@ func (h *OperationHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Operation represents an ongoing action which the server is performing. type Operation struct { ID string - result interface{} - awaiting <-chan interface{} + result runtime.Object + awaiting <-chan runtime.Object finished *time.Time lock sync.Mutex notify chan struct{} @@ -90,7 +91,7 @@ func NewOperations() *Operations { } // NewOperation adds a new operation. It is lock-free. -func (ops *Operations) NewOperation(from <-chan interface{}) *Operation { +func (ops *Operations) NewOperation(from <-chan runtime.Object) *Operation { id := atomic.AddInt64(&ops.lastID, 1) op := &Operation{ ID: strconv.FormatInt(id, 10), @@ -110,7 +111,7 @@ func (ops *Operations) insert(op *Operation) { } // List lists operations for an API client. -func (ops *Operations) List() api.ServerOpList { +func (ops *Operations) List() *api.ServerOpList { ops.lock.Lock() defer ops.lock.Unlock() @@ -119,7 +120,7 @@ func (ops *Operations) List() api.ServerOpList { ids = append(ids, id) } sort.StringSlice(ids).Sort() - ol := api.ServerOpList{} + ol := &api.ServerOpList{} for _, id := range ids { ol.Items = append(ol.Items, api.ServerOp{JSONBase: api.JSONBase{ID: id}}) } @@ -185,7 +186,7 @@ func (op *Operation) expired(limitTime time.Time) bool { // StatusOrResult returns status information or the result of the operation if it is complete, // with a bool indicating true in the latter case. -func (op *Operation) StatusOrResult() (description interface{}, finished bool) { +func (op *Operation) StatusOrResult() (description runtime.Object, finished bool) { op.lock.Lock() defer op.lock.Unlock() diff --git a/pkg/apiserver/operation_test.go b/pkg/apiserver/operation_test.go index 6e32aa4a727..856fe764389 100644 --- a/pkg/apiserver/operation_test.go +++ b/pkg/apiserver/operation_test.go @@ -28,12 +28,13 @@ import ( // TODO: remove dependency on api, apiserver should be generic "github.com/GoogleCloudPlatform/kubernetes/pkg/api" _ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) func TestOperation(t *testing.T) { ops := NewOperations() - c := make(chan interface{}) + c := make(chan runtime.Object) op := ops.NewOperation(c) // Allow context switch, so that op's ID can get added to the map and Get will work. // This is just so we can test Get. Ordinary users have no need to call Get immediately @@ -41,7 +42,7 @@ func TestOperation(t *testing.T) { time.Sleep(time.Millisecond) go func() { time.Sleep(500 * time.Millisecond) - c <- "All done" + c <- &Simple{JSONBase: api.JSONBase{ID: "All done"}} }() if op.expired(time.Now().Add(-time.Minute)) { @@ -89,7 +90,7 @@ func TestOperation(t *testing.T) { t.Errorf("expire failed to remove the operation %#v", ops) } - if op.result.(string) != "All done" { + if op.result.(*Simple).ID != "All done" { t.Errorf("Got unexpected result: %#v", op.result) } } @@ -98,7 +99,7 @@ func TestOperationsList(t *testing.T) { testOver := make(chan struct{}) defer close(testOver) simpleStorage := &SimpleRESTStorage{ - injectedFunction: func(obj interface{}) (interface{}, error) { + injectedFunction: func(obj runtime.Object) (runtime.Object, error) { // Eliminate flakes by ensuring the create operation takes longer than this test. <-testOver return obj, nil @@ -111,7 +112,7 @@ func TestOperationsList(t *testing.T) { server := httptest.NewServer(handler) client := http.Client{} - simple := Simple{ + simple := &Simple{ Name: "foo", } data, err := codec.Encode(simple) @@ -154,7 +155,7 @@ func TestOpGet(t *testing.T) { testOver := make(chan struct{}) defer close(testOver) simpleStorage := &SimpleRESTStorage{ - injectedFunction: func(obj interface{}) (interface{}, error) { + injectedFunction: func(obj runtime.Object) (runtime.Object, error) { // Eliminate flakes by ensuring the create operation takes longer than this test. <-testOver return obj, nil @@ -167,7 +168,7 @@ func TestOpGet(t *testing.T) { server := httptest.NewServer(handler) client := http.Client{} - simple := Simple{ + simple := &Simple{ Name: "foo", } data, err := codec.Encode(simple) diff --git a/pkg/apiserver/redirect.go b/pkg/apiserver/redirect.go index be0a9136327..57aff02d3d1 100644 --- a/pkg/apiserver/redirect.go +++ b/pkg/apiserver/redirect.go @@ -20,11 +20,12 @@ import ( "net/http" "github.com/GoogleCloudPlatform/kubernetes/pkg/httplog" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) type RedirectHandler struct { storage map[string]RESTStorage - codec Codec + codec runtime.Codec } func (r *RedirectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 0e99c1dba0b..09251b5bc40 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -23,11 +23,12 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/httplog" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) type RESTHandler struct { storage map[string]RESTStorage - codec Codec + codec runtime.Codec ops *Operations asyncOpWait time.Duration } @@ -158,7 +159,7 @@ func (h *RESTHandler) handleRESTStorage(parts []string, req *http.Request, w htt } // createOperation creates an operation to process a channel response. -func (h *RESTHandler) createOperation(out <-chan interface{}, sync bool, timeout time.Duration) *Operation { +func (h *RESTHandler) createOperation(out <-chan runtime.Object, sync bool, timeout time.Duration) *Operation { op := h.ops.NewOperation(out) if sync { op.WaitFor(timeout) @@ -175,11 +176,6 @@ func (h *RESTHandler) finishReq(op *Operation, w http.ResponseWriter) { if complete { status := http.StatusOK switch stat := obj.(type) { - case api.Status: - httplog.LogOf(w).Addf("programmer error: use *api.Status as a result, not api.Status.") - if stat.Code != 0 { - status = stat.Code - } case *api.Status: if stat.Code != 0 { status = stat.Code diff --git a/pkg/apiserver/watch.go b/pkg/apiserver/watch.go index a4d349e663f..7cd047093b7 100644 --- a/pkg/apiserver/watch.go +++ b/pkg/apiserver/watch.go @@ -32,7 +32,7 @@ import ( type WatchHandler struct { storage map[string]RESTStorage - codec Codec + codec runtime.Codec } func getWatchParams(query url.Values) (label, field labels.Selector, resourceVersion uint64) { diff --git a/pkg/apiserver/watch_test.go b/pkg/apiserver/watch_test.go index 09f7952f7d9..70027c1d44a 100644 --- a/pkg/apiserver/watch_test.go +++ b/pkg/apiserver/watch_test.go @@ -26,12 +26,13 @@ import ( "code.google.com/p/go.net/websocket" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" ) var watchTestTable = []struct { t watch.EventType - obj interface{} + obj runtime.Object }{ {watch.Added, &Simple{Name: "A Name"}}, {watch.Modified, &Simple{Name: "Another Name"}}, @@ -56,7 +57,7 @@ func TestWatchWebsocket(t *testing.T) { t.Errorf("unexpected error: %v", err) } - try := func(action watch.EventType, object interface{}) { + try := func(action watch.EventType, object runtime.Object) { // Send simpleStorage.fakeWatch.Action(action, object) // Test receive @@ -113,7 +114,7 @@ func TestWatchHTTP(t *testing.T) { decoder := json.NewDecoder(response.Body) - try := func(action watch.EventType, object interface{}) { + try := func(action watch.EventType, object runtime.Object) { // Send simpleStorage.fakeWatch.Action(action, object) // Test receive diff --git a/pkg/client/client.go b/pkg/client/client.go index e9b377636cb..63519708b20 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -46,36 +46,36 @@ type Interface interface { // PodInterface has methods to work with Pod resources. type PodInterface interface { - ListPods(selector labels.Selector) (api.PodList, error) - GetPod(id string) (api.Pod, error) + ListPods(selector labels.Selector) (*api.PodList, error) + GetPod(id string) (*api.Pod, error) DeletePod(id string) error - CreatePod(api.Pod) (api.Pod, error) - UpdatePod(api.Pod) (api.Pod, error) + CreatePod(*api.Pod) (*api.Pod, error) + UpdatePod(*api.Pod) (*api.Pod, error) } // ReplicationControllerInterface has methods to work with ReplicationController resources. type ReplicationControllerInterface interface { - ListReplicationControllers(selector labels.Selector) (api.ReplicationControllerList, error) - GetReplicationController(id string) (api.ReplicationController, error) - CreateReplicationController(api.ReplicationController) (api.ReplicationController, error) - UpdateReplicationController(api.ReplicationController) (api.ReplicationController, error) + ListReplicationControllers(selector labels.Selector) (*api.ReplicationControllerList, error) + GetReplicationController(id string) (*api.ReplicationController, error) + CreateReplicationController(*api.ReplicationController) (*api.ReplicationController, error) + UpdateReplicationController(*api.ReplicationController) (*api.ReplicationController, error) DeleteReplicationController(string) error WatchReplicationControllers(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) } // ServiceInterface has methods to work with Service resources. type ServiceInterface interface { - ListServices(selector labels.Selector) (api.ServiceList, error) - GetService(id string) (api.Service, error) - CreateService(api.Service) (api.Service, error) - UpdateService(api.Service) (api.Service, error) + ListServices(selector labels.Selector) (*api.ServiceList, error) + GetService(id string) (*api.Service, error) + CreateService(*api.Service) (*api.Service, error) + UpdateService(*api.Service) (*api.Service, error) DeleteService(string) error WatchServices(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) } // EndpointsInterface has methods to work with Endpoints resources type EndpointsInterface interface { - ListEndpoints(selector labels.Selector) (api.EndpointsList, error) + ListEndpoints(selector labels.Selector) (*api.EndpointsList, error) WatchEndpoints(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) } @@ -85,7 +85,7 @@ type VersionInterface interface { } type MinionInterface interface { - ListMinions() (api.MinionList, error) + ListMinions() (*api.MinionList, error) } // Client is the actual implementation of a Kubernetes client. @@ -222,7 +222,7 @@ func (c *RESTClient) doRequest(request *http.Request) ([]byte, error) { // Did the server give us a status response? isStatusResponse := false var status api.Status - if err := runtime.DecodeInto(body, &status); err == nil && status.Status != "" { + if err := runtime.DefaultCodec.DecodeInto(body, &status); err == nil && status.Status != "" { isStatusResponse = true } @@ -247,14 +247,16 @@ func (c *RESTClient) doRequest(request *http.Request) ([]byte, error) { } // ListPods takes a selector, and returns the list of pods that match that selector. -func (c *Client) ListPods(selector labels.Selector) (result api.PodList, err error) { - err = c.Get().Path("pods").SelectorParam("labels", selector).Do().Into(&result) +func (c *Client) ListPods(selector labels.Selector) (result *api.PodList, err error) { + result = &api.PodList{} + err = c.Get().Path("pods").SelectorParam("labels", selector).Do().Into(result) return } // GetPod takes the id of the pod, and returns the corresponding Pod object, and an error if it occurs -func (c *Client) GetPod(id string) (result api.Pod, err error) { - err = c.Get().Path("pods").Path(id).Do().Into(&result) +func (c *Client) GetPod(id string) (result *api.Pod, err error) { + result = &api.Pod{} + err = c.Get().Path("pods").Path(id).Do().Into(result) return } @@ -264,46 +266,52 @@ func (c *Client) DeletePod(id string) error { } // CreatePod takes the representation of a pod. Returns the server's representation of the pod, and an error, if it occurs. -func (c *Client) CreatePod(pod api.Pod) (result api.Pod, err error) { - err = c.Post().Path("pods").Body(pod).Do().Into(&result) +func (c *Client) CreatePod(pod *api.Pod) (result *api.Pod, err error) { + result = &api.Pod{} + err = c.Post().Path("pods").Body(pod).Do().Into(result) return } // UpdatePod takes the representation of a pod to update. Returns the server's representation of the pod, and an error, if it occurs. -func (c *Client) UpdatePod(pod api.Pod) (result api.Pod, err error) { +func (c *Client) UpdatePod(pod *api.Pod) (result *api.Pod, err error) { + result = &api.Pod{} if pod.ResourceVersion == 0 { err = fmt.Errorf("invalid update object, missing resource version: %v", pod) return } - err = c.Put().Path("pods").Path(pod.ID).Body(pod).Do().Into(&result) + err = c.Put().Path("pods").Path(pod.ID).Body(pod).Do().Into(result) return } // ListReplicationControllers takes a selector, and returns the list of replication controllers that match that selector. -func (c *Client) ListReplicationControllers(selector labels.Selector) (result api.ReplicationControllerList, err error) { - err = c.Get().Path("replicationControllers").SelectorParam("labels", selector).Do().Into(&result) +func (c *Client) ListReplicationControllers(selector labels.Selector) (result *api.ReplicationControllerList, err error) { + result = &api.ReplicationControllerList{} + err = c.Get().Path("replicationControllers").SelectorParam("labels", selector).Do().Into(result) return } // GetReplicationController returns information about a particular replication controller. -func (c *Client) GetReplicationController(id string) (result api.ReplicationController, err error) { - err = c.Get().Path("replicationControllers").Path(id).Do().Into(&result) +func (c *Client) GetReplicationController(id string) (result *api.ReplicationController, err error) { + result = &api.ReplicationController{} + err = c.Get().Path("replicationControllers").Path(id).Do().Into(result) return } // CreateReplicationController creates a new replication controller. -func (c *Client) CreateReplicationController(controller api.ReplicationController) (result api.ReplicationController, err error) { - err = c.Post().Path("replicationControllers").Body(controller).Do().Into(&result) +func (c *Client) CreateReplicationController(controller *api.ReplicationController) (result *api.ReplicationController, err error) { + result = &api.ReplicationController{} + err = c.Post().Path("replicationControllers").Body(controller).Do().Into(result) return } // UpdateReplicationController updates an existing replication controller. -func (c *Client) UpdateReplicationController(controller api.ReplicationController) (result api.ReplicationController, err error) { +func (c *Client) UpdateReplicationController(controller *api.ReplicationController) (result *api.ReplicationController, err error) { + result = &api.ReplicationController{} if controller.ResourceVersion == 0 { err = fmt.Errorf("invalid update object, missing resource version: %v", controller) return } - err = c.Put().Path("replicationControllers").Path(controller.ID).Body(controller).Do().Into(&result) + err = c.Put().Path("replicationControllers").Path(controller.ID).Body(controller).Do().Into(result) return } @@ -324,30 +332,34 @@ func (c *Client) WatchReplicationControllers(label, field labels.Selector, resou } // ListServices takes a selector, and returns the list of services that match that selector -func (c *Client) ListServices(selector labels.Selector) (result api.ServiceList, err error) { - err = c.Get().Path("services").SelectorParam("labels", selector).Do().Into(&result) +func (c *Client) ListServices(selector labels.Selector) (result *api.ServiceList, err error) { + result = &api.ServiceList{} + err = c.Get().Path("services").SelectorParam("labels", selector).Do().Into(result) return } // GetService returns information about a particular service. -func (c *Client) GetService(id string) (result api.Service, err error) { - err = c.Get().Path("services").Path(id).Do().Into(&result) +func (c *Client) GetService(id string) (result *api.Service, err error) { + result = &api.Service{} + err = c.Get().Path("services").Path(id).Do().Into(result) return } // CreateService creates a new service. -func (c *Client) CreateService(svc api.Service) (result api.Service, err error) { - err = c.Post().Path("services").Body(svc).Do().Into(&result) +func (c *Client) CreateService(svc *api.Service) (result *api.Service, err error) { + result = &api.Service{} + err = c.Post().Path("services").Body(svc).Do().Into(result) return } // UpdateService updates an existing service. -func (c *Client) UpdateService(svc api.Service) (result api.Service, err error) { +func (c *Client) UpdateService(svc *api.Service) (result *api.Service, err error) { + result = &api.Service{} if svc.ResourceVersion == 0 { err = fmt.Errorf("invalid update object, missing resource version: %v", svc) return } - err = c.Put().Path("services").Path(svc.ID).Body(svc).Do().Into(&result) + err = c.Put().Path("services").Path(svc.ID).Body(svc).Do().Into(result) return } @@ -368,8 +380,9 @@ func (c *Client) WatchServices(label, field labels.Selector, resourceVersion uin } // ListEndpoints takes a selector, and returns the list of endpoints that match that selector -func (c *Client) ListEndpoints(selector labels.Selector) (result api.EndpointsList, err error) { - err = c.Get().Path("endpoints").SelectorParam("labels", selector).Do().Into(&result) +func (c *Client) ListEndpoints(selector labels.Selector) (result *api.EndpointsList, err error) { + result = &api.EndpointsList{} + err = c.Get().Path("endpoints").SelectorParam("labels", selector).Do().Into(result) return } @@ -399,7 +412,8 @@ func (c *Client) ServerVersion() (*version.Info, error) { } // ListMinions lists all the minions in the cluster. -func (c *Client) ListMinions() (minionList api.MinionList, err error) { - err = c.Get().Path("minions").Do().Into(&minionList) +func (c *Client) ListMinions() (result *api.MinionList, err error) { + result = &api.MinionList{} + err = c.Get().Path("minions").Do().Into(result) return } diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index d0700c4b113..7cdeebe6fad 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -73,7 +73,7 @@ func TestValidatesHostParameter(t *testing.T) { func TestListEmptyPods(t *testing.T) { c := &testClient{ Request: testRequest{Method: "GET", Path: "/pods"}, - Response: Response{StatusCode: 200, Body: api.PodList{}}, + Response: Response{StatusCode: 200, Body: &api.PodList{}}, } podList, err := c.Setup().ListPods(labels.Everything()) c.Validate(t, podList, err) @@ -83,7 +83,7 @@ func TestListPods(t *testing.T) { c := &testClient{ Request: testRequest{Method: "GET", Path: "/pods"}, Response: Response{StatusCode: 200, - Body: api.PodList{ + Body: &api.PodList{ Items: []api.Pod{ { CurrentState: api.PodState{ @@ -113,7 +113,7 @@ func TestListPodsLabels(t *testing.T) { Request: testRequest{Method: "GET", Path: "/pods", Query: url.Values{"labels": []string{"foo=bar,name=baz"}}}, Response: Response{ StatusCode: 200, - Body: api.PodList{ + Body: &api.PodList{ Items: []api.Pod{ { CurrentState: api.PodState{ @@ -140,7 +140,7 @@ func TestGetPod(t *testing.T) { Request: testRequest{Method: "GET", Path: "/pods/foo"}, Response: Response{ StatusCode: 200, - Body: api.Pod{ + Body: &api.Pod{ CurrentState: api.PodState{ Status: "Foobar", }, @@ -165,7 +165,7 @@ func TestDeletePod(t *testing.T) { } func TestCreatePod(t *testing.T) { - requestPod := api.Pod{ + requestPod := &api.Pod{ CurrentState: api.PodState{ Status: "Foobar", }, @@ -186,7 +186,7 @@ func TestCreatePod(t *testing.T) { } func TestUpdatePod(t *testing.T) { - requestPod := api.Pod{ + requestPod := &api.Pod{ JSONBase: api.JSONBase{ID: "foo", ResourceVersion: 1}, CurrentState: api.PodState{ Status: "Foobar", @@ -208,7 +208,7 @@ func TestListControllers(t *testing.T) { c := &testClient{ Request: testRequest{Method: "GET", Path: "/replicationControllers"}, Response: Response{StatusCode: 200, - Body: api.ReplicationControllerList{ + Body: &api.ReplicationControllerList{ Items: []api.ReplicationController{ { JSONBase: api.JSONBase{ID: "foo"}, @@ -234,7 +234,7 @@ func TestGetController(t *testing.T) { Request: testRequest{Method: "GET", Path: "/replicationControllers/foo"}, Response: Response{ StatusCode: 200, - Body: api.ReplicationController{ + Body: &api.ReplicationController{ JSONBase: api.JSONBase{ID: "foo"}, DesiredState: api.ReplicationControllerState{ Replicas: 2, @@ -251,14 +251,14 @@ func TestGetController(t *testing.T) { } func TestUpdateController(t *testing.T) { - requestController := api.ReplicationController{ + requestController := &api.ReplicationController{ JSONBase: api.JSONBase{ID: "foo", ResourceVersion: 1}, } c := &testClient{ Request: testRequest{Method: "PUT", Path: "/replicationControllers/foo"}, Response: Response{ StatusCode: 200, - Body: api.ReplicationController{ + Body: &api.ReplicationController{ JSONBase: api.JSONBase{ID: "foo"}, DesiredState: api.ReplicationControllerState{ Replicas: 2, @@ -284,14 +284,14 @@ func TestDeleteController(t *testing.T) { } func TestCreateController(t *testing.T) { - requestController := api.ReplicationController{ + requestController := &api.ReplicationController{ JSONBase: api.JSONBase{ID: "foo"}, } c := &testClient{ Request: testRequest{Method: "POST", Path: "/replicationControllers", Body: requestController}, Response: Response{ StatusCode: 200, - Body: api.ReplicationController{ + Body: &api.ReplicationController{ JSONBase: api.JSONBase{ID: "foo"}, DesiredState: api.ReplicationControllerState{ Replicas: 2, @@ -307,9 +307,9 @@ func TestCreateController(t *testing.T) { c.Validate(t, receivedController, err) } -func body(obj interface{}, raw *string) *string { +func body(obj runtime.Object, raw *string) *string { if obj != nil { - bs, _ := runtime.Encode(obj) + bs, _ := runtime.DefaultCodec.Encode(obj) body := string(bs) return &body } @@ -321,13 +321,13 @@ type testRequest struct { Path string Header string Query url.Values - Body interface{} + Body runtime.Object RawBody *string } type Response struct { StatusCode int - Body interface{} + Body runtime.Object RawBody *string } @@ -338,7 +338,6 @@ type testClient struct { Error bool server *httptest.Server handler *util.FakeHandler - Target interface{} // For query args, an optional function to validate the contents // useful when the contents can change but still be correct. // Maps from query arg key to validator. @@ -363,7 +362,23 @@ func (c *testClient) Setup() *testClient { return c } -func (c *testClient) Validate(t *testing.T, received interface{}, err error) { +func (c *testClient) Validate(t *testing.T, received runtime.Object, err error) { + c.ValidateCommon(t, err) + + if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) { + t.Errorf("bad response for request %#v: expected %s, got %s", c.Request, c.Response.Body, received) + } +} + +func (c *testClient) ValidateRaw(t *testing.T, received []byte, err error) { + c.ValidateCommon(t, err) + + if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) { + t.Errorf("bad response for request %#v: expected %s, got %s", c.Request, c.Response.Body, received) + } +} + +func (c *testClient) ValidateCommon(t *testing.T, err error) { defer c.server.Close() if c.Error { @@ -401,17 +416,13 @@ func (c *testClient) Validate(t *testing.T, received interface{}, err error) { if expected, received := requestBody, c.handler.RequestBody; expected != nil && *expected != received { t.Errorf("bad body for request %#v: expected %s, got %s", c.Request, *expected, received) } - - if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) { - t.Errorf("bad response for request %#v: expected %s, got %s", c.Request, c.Response.Body, received) - } } func TestListServices(t *testing.T) { c := &testClient{ Request: testRequest{Method: "GET", Path: "/services"}, Response: Response{StatusCode: 200, - Body: api.ServiceList{ + Body: &api.ServiceList{ Items: []api.Service{ { JSONBase: api.JSONBase{ID: "name"}, @@ -435,7 +446,7 @@ func TestListServicesLabels(t *testing.T) { c := &testClient{ Request: testRequest{Method: "GET", Path: "/services", Query: url.Values{"labels": []string{"foo=bar,name=baz"}}}, Response: Response{StatusCode: 200, - Body: api.ServiceList{ + Body: &api.ServiceList{ Items: []api.Service{ { JSONBase: api.JSONBase{ID: "name"}, @@ -464,7 +475,7 @@ func TestGetService(t *testing.T) { Response: Response{StatusCode: 200, Body: &api.Service{JSONBase: api.JSONBase{ID: "service-1"}}}, } response, err := c.Setup().GetService("1") - c.Validate(t, &response, err) + c.Validate(t, response, err) } func TestCreateService(t *testing.T) { @@ -472,18 +483,18 @@ func TestCreateService(t *testing.T) { Request: testRequest{Method: "POST", Path: "/services", Body: &api.Service{JSONBase: api.JSONBase{ID: "service-1"}}}, Response: Response{StatusCode: 200, Body: &api.Service{JSONBase: api.JSONBase{ID: "service-1"}}}, }).Setup() - response, err := c.Setup().CreateService(api.Service{JSONBase: api.JSONBase{ID: "service-1"}}) - c.Validate(t, &response, err) + response, err := c.Setup().CreateService(&api.Service{JSONBase: api.JSONBase{ID: "service-1"}}) + c.Validate(t, response, err) } func TestUpdateService(t *testing.T) { - svc := api.Service{JSONBase: api.JSONBase{ID: "service-1", ResourceVersion: 1}} + svc := &api.Service{JSONBase: api.JSONBase{ID: "service-1", ResourceVersion: 1}} c := &testClient{ - Request: testRequest{Method: "PUT", Path: "/services/service-1", Body: &svc}, - Response: Response{StatusCode: 200, Body: &svc}, + Request: testRequest{Method: "PUT", Path: "/services/service-1", Body: svc}, + Response: Response{StatusCode: 200, Body: svc}, } response, err := c.Setup().UpdateService(svc) - c.Validate(t, &response, err) + c.Validate(t, response, err) } func TestDeleteService(t *testing.T) { @@ -503,8 +514,8 @@ func TestDoRequest(t *testing.T) { {Client: NewOrDie("localhost", &AuthInfo{"foo", "bar"}), Request: testRequest{Method: "GET", Path: "auth", Header: "Authorization"}, Response: Response{StatusCode: 200}}, {Client: &Client{&RESTClient{httpClient: http.DefaultClient}}, Request: testRequest{Method: "GET", Path: "nocertificate"}, Error: true}, {Request: testRequest{Method: "GET", Path: "error"}, Response: Response{StatusCode: 500}, Error: true}, - {Request: testRequest{Method: "POST", Path: "faildecode"}, Response: Response{StatusCode: 200, RawBody: &invalid}, Target: &struct{}{}}, - {Request: testRequest{Method: "GET", Path: "failread"}, Response: Response{StatusCode: 200, RawBody: &invalid}, Target: &struct{}{}}, + {Request: testRequest{Method: "POST", Path: "faildecode"}, Response: Response{StatusCode: 200, RawBody: &invalid}}, + {Request: testRequest{Method: "GET", Path: "failread"}, Response: Response{StatusCode: 200, RawBody: &invalid}}, } for _, c := range testClients { client := c.Setup() @@ -516,13 +527,13 @@ func TestDoRequest(t *testing.T) { URL: prefix, } response, err := client.doRequest(request) - c.Validate(t, response, err) + c.ValidateRaw(t, response, err) } } func TestDoRequestAccepted(t *testing.T) { - status := api.Status{Status: api.StatusWorking} - expectedBody, _ := runtime.Encode(status) + status := &api.Status{Status: api.StatusWorking} + expectedBody, _ := runtime.DefaultCodec.Encode(status) fakeHandler := util.FakeHandler{ StatusCode: 202, ResponseBody: string(expectedBody), @@ -548,7 +559,7 @@ func TestDoRequestAccepted(t *testing.T) { t.Errorf("Unexpected kind of error: %#v", err) return } - if !reflect.DeepEqual(se.Status, status) { + if !reflect.DeepEqual(&se.Status, status) { t.Errorf("Unexpected status: %#v", se.Status) } if body != nil { @@ -558,8 +569,8 @@ func TestDoRequestAccepted(t *testing.T) { } func TestDoRequestAcceptedSuccess(t *testing.T) { - status := api.Status{Status: api.StatusSuccess} - expectedBody, _ := runtime.Encode(status) + status := &api.Status{Status: api.StatusSuccess} + expectedBody, _ := runtime.DefaultCodec.Encode(status) fakeHandler := util.FakeHandler{ StatusCode: 202, ResponseBody: string(expectedBody), @@ -579,11 +590,11 @@ func TestDoRequestAcceptedSuccess(t *testing.T) { if err != nil { t.Errorf("Unexpected error %#v", err) } - statusOut, err := runtime.Decode(body) + statusOut, err := runtime.DefaultCodec.Decode(body) if err != nil { t.Errorf("Unexpected error %#v", err) } - if !reflect.DeepEqual(&status, statusOut) { + if !reflect.DeepEqual(status, statusOut) { t.Errorf("Unexpected mis-match. Expected %#v. Saw %#v", status, statusOut) } fakeHandler.ValidateRequest(t, "/foo/bar", "GET", nil) @@ -622,5 +633,5 @@ func TestListMinions(t *testing.T) { Response: Response{StatusCode: 200, Body: &api.MinionList{JSONBase: api.JSONBase{ID: "minion-1"}}}, } response, err := c.Setup().ListMinions() - c.Validate(t, &response, err) + c.Validate(t, response, err) } diff --git a/pkg/client/fake.go b/pkg/client/fake.go index 5405255f34c..c89c7aa3bc5 100644 --- a/pkg/client/fake.go +++ b/pkg/client/fake.go @@ -42,14 +42,14 @@ type Fake struct { Watch watch.Interface } -func (c *Fake) ListPods(selector labels.Selector) (api.PodList, error) { +func (c *Fake) ListPods(selector labels.Selector) (*api.PodList, error) { c.Actions = append(c.Actions, FakeAction{Action: "list-pods"}) - return *runtime.CopyOrDie(c.Pods).(*api.PodList), nil + return runtime.DefaultScheme.CopyOrDie(&c.Pods).(*api.PodList), nil } -func (c *Fake) GetPod(name string) (api.Pod, error) { +func (c *Fake) GetPod(name string) (*api.Pod, error) { c.Actions = append(c.Actions, FakeAction{Action: "get-pod", Value: name}) - return api.Pod{}, nil + return &api.Pod{}, nil } func (c *Fake) DeletePod(name string) error { @@ -57,34 +57,34 @@ func (c *Fake) DeletePod(name string) error { return nil } -func (c *Fake) CreatePod(pod api.Pod) (api.Pod, error) { +func (c *Fake) CreatePod(pod *api.Pod) (*api.Pod, error) { c.Actions = append(c.Actions, FakeAction{Action: "create-pod"}) - return api.Pod{}, nil + return &api.Pod{}, nil } -func (c *Fake) UpdatePod(pod api.Pod) (api.Pod, error) { +func (c *Fake) UpdatePod(pod *api.Pod) (*api.Pod, error) { c.Actions = append(c.Actions, FakeAction{Action: "update-pod", Value: pod.ID}) - return api.Pod{}, nil + return &api.Pod{}, nil } -func (c *Fake) ListReplicationControllers(selector labels.Selector) (api.ReplicationControllerList, error) { +func (c *Fake) ListReplicationControllers(selector labels.Selector) (*api.ReplicationControllerList, error) { c.Actions = append(c.Actions, FakeAction{Action: "list-controllers"}) - return api.ReplicationControllerList{}, nil + return &api.ReplicationControllerList{}, nil } -func (c *Fake) GetReplicationController(name string) (api.ReplicationController, error) { +func (c *Fake) GetReplicationController(name string) (*api.ReplicationController, error) { c.Actions = append(c.Actions, FakeAction{Action: "get-controller", Value: name}) - return *runtime.CopyOrDie(c.Ctrl).(*api.ReplicationController), nil + return runtime.DefaultScheme.CopyOrDie(&c.Ctrl).(*api.ReplicationController), nil } -func (c *Fake) CreateReplicationController(controller api.ReplicationController) (api.ReplicationController, error) { +func (c *Fake) CreateReplicationController(controller *api.ReplicationController) (*api.ReplicationController, error) { c.Actions = append(c.Actions, FakeAction{Action: "create-controller", Value: controller}) - return api.ReplicationController{}, nil + return &api.ReplicationController{}, nil } -func (c *Fake) UpdateReplicationController(controller api.ReplicationController) (api.ReplicationController, error) { +func (c *Fake) UpdateReplicationController(controller *api.ReplicationController) (*api.ReplicationController, error) { c.Actions = append(c.Actions, FakeAction{Action: "update-controller", Value: controller}) - return api.ReplicationController{}, nil + return &api.ReplicationController{}, nil } func (c *Fake) DeleteReplicationController(controller string) error { @@ -97,24 +97,24 @@ func (c *Fake) WatchReplicationControllers(label, field labels.Selector, resourc return c.Watch, nil } -func (c *Fake) ListServices(selector labels.Selector) (api.ServiceList, error) { +func (c *Fake) ListServices(selector labels.Selector) (*api.ServiceList, error) { c.Actions = append(c.Actions, FakeAction{Action: "list-services"}) - return c.ServiceList, c.Err + return &c.ServiceList, c.Err } -func (c *Fake) GetService(name string) (api.Service, error) { +func (c *Fake) GetService(name string) (*api.Service, error) { c.Actions = append(c.Actions, FakeAction{Action: "get-service", Value: name}) - return api.Service{}, nil + return &api.Service{}, nil } -func (c *Fake) CreateService(service api.Service) (api.Service, error) { +func (c *Fake) CreateService(service *api.Service) (*api.Service, error) { c.Actions = append(c.Actions, FakeAction{Action: "create-service", Value: service}) - return api.Service{}, nil + return &api.Service{}, nil } -func (c *Fake) UpdateService(service api.Service) (api.Service, error) { +func (c *Fake) UpdateService(service *api.Service) (*api.Service, error) { c.Actions = append(c.Actions, FakeAction{Action: "update-service", Value: service}) - return api.Service{}, nil + return &api.Service{}, nil } func (c *Fake) DeleteService(service string) error { @@ -127,9 +127,9 @@ func (c *Fake) WatchServices(label, field labels.Selector, resourceVersion uint6 return c.Watch, c.Err } -func (c *Fake) ListEndpoints(selector labels.Selector) (api.EndpointsList, error) { +func (c *Fake) ListEndpoints(selector labels.Selector) (*api.EndpointsList, error) { c.Actions = append(c.Actions, FakeAction{Action: "list-endpoints"}) - return c.EndpointsList, c.Err + return runtime.DefaultScheme.CopyOrDie(&c.EndpointsList).(*api.EndpointsList), c.Err } func (c *Fake) WatchEndpoints(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) { @@ -143,7 +143,7 @@ func (c *Fake) ServerVersion() (*version.Info, error) { return &versionInfo, nil } -func (c *Fake) ListMinions() (api.MinionList, error) { +func (c *Fake) ListMinions() (*api.MinionList, error) { c.Actions = append(c.Actions, FakeAction{Action: "list-minions", Value: nil}) - return api.MinionList{}, nil + return &api.MinionList{}, nil } diff --git a/pkg/client/request.go b/pkg/client/request.go index 2aceb7ae479..8e6f41b5a9b 100644 --- a/pkg/client/request.go +++ b/pkg/client/request.go @@ -187,7 +187,8 @@ func (r *Request) Timeout(d time.Duration) *Request { // If obj is a string, try to read a file of that name. // If obj is a []byte, send it directly. // If obj is an io.Reader, use it directly. -// Otherwise, assume obj is an api type and marshall it correctly. +// If obj is a runtime.Object, marshal it correctly. +// Otherwise, set an error. func (r *Request) Body(obj interface{}) *Request { if r.err != nil { return r @@ -204,13 +205,15 @@ func (r *Request) Body(obj interface{}) *Request { r.body = bytes.NewBuffer(t) case io.Reader: r.body = t - default: - data, err := runtime.Encode(obj) + case runtime.Object: + data, err := runtime.DefaultCodec.Encode(t) if err != nil { r.err = err return r } r.body = bytes.NewBuffer(data) + default: + r.err = fmt.Errorf("Unknown type used for body: %#v", obj) } return r } @@ -314,19 +317,19 @@ func (r Result) Raw() ([]byte, error) { } // Get returns the result as an object. -func (r Result) Get() (interface{}, error) { +func (r Result) Get() (runtime.Object, error) { if r.err != nil { return nil, r.err } - return runtime.Decode(r.body) + return runtime.DefaultCodec.Decode(r.body) } // Into stores the result into obj, if possible. -func (r Result) Into(obj interface{}) error { +func (r Result) Into(obj runtime.Object) error { if r.err != nil { return r.err } - return runtime.DecodeInto(r.body, obj) + return runtime.DefaultCodec.DecodeInto(r.body, obj) } // Error returns the error executing the request, nil if no error occurred. diff --git a/pkg/client/request_test.go b/pkg/client/request_test.go index 426f30e0c16..a7dab29af13 100644 --- a/pkg/client/request_test.go +++ b/pkg/client/request_test.go @@ -38,7 +38,7 @@ import ( func TestDoRequestNewWay(t *testing.T) { reqBody := "request body" expectedObj := &api.Service{Port: 12345} - expectedBody, _ := runtime.Encode(expectedObj) + expectedBody, _ := runtime.DefaultCodec.Encode(expectedObj) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -71,9 +71,9 @@ func TestDoRequestNewWay(t *testing.T) { func TestDoRequestNewWayReader(t *testing.T) { reqObj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} - reqBodyExpected, _ := runtime.Encode(reqObj) + reqBodyExpected, _ := runtime.DefaultCodec.Encode(reqObj) expectedObj := &api.Service{Port: 12345} - expectedBody, _ := runtime.Encode(expectedObj) + expectedBody, _ := runtime.DefaultCodec.Encode(expectedObj) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -108,9 +108,9 @@ func TestDoRequestNewWayReader(t *testing.T) { func TestDoRequestNewWayObj(t *testing.T) { reqObj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} - reqBodyExpected, _ := runtime.Encode(reqObj) + reqBodyExpected, _ := runtime.DefaultCodec.Encode(reqObj) expectedObj := &api.Service{Port: 12345} - expectedBody, _ := runtime.Encode(expectedObj) + expectedBody, _ := runtime.DefaultCodec.Encode(expectedObj) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -144,7 +144,7 @@ func TestDoRequestNewWayObj(t *testing.T) { func TestDoRequestNewWayFile(t *testing.T) { reqObj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} - reqBodyExpected, err := runtime.Encode(reqObj) + reqBodyExpected, err := runtime.DefaultCodec.Encode(reqObj) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -160,7 +160,7 @@ func TestDoRequestNewWayFile(t *testing.T) { } expectedObj := &api.Service{Port: 12345} - expectedBody, _ := runtime.Encode(expectedObj) + expectedBody, _ := runtime.DefaultCodec.Encode(expectedObj) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -285,7 +285,7 @@ func TestSetPollPeriod(t *testing.T) { } func TestPolling(t *testing.T) { - objects := []interface{}{ + objects := []runtime.Object{ &api.Status{Status: api.StatusWorking, Details: &api.StatusDetails{ID: "1234"}}, &api.Status{Status: api.StatusWorking, Details: &api.StatusDetails{ID: "1234"}}, &api.Status{Status: api.StatusWorking, Details: &api.StatusDetails{ID: "1234"}}, @@ -295,7 +295,7 @@ func TestPolling(t *testing.T) { callNumber := 0 testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - data, err := runtime.Encode(objects[callNumber]) + data, err := runtime.DefaultCodec.Encode(objects[callNumber]) if err != nil { t.Errorf("Unexpected encode error") } @@ -380,7 +380,7 @@ func checkAuth(t *testing.T, expect AuthInfo, r *http.Request) { func TestWatch(t *testing.T) { var table = []struct { t watch.EventType - obj interface{} + obj runtime.Object }{ {watch.Added, &api.Pod{JSONBase: api.JSONBase{ID: "first"}}}, {watch.Modified, &api.Pod{JSONBase: api.JSONBase{ID: "second"}}}, diff --git a/pkg/controller/replication_controller.go b/pkg/controller/replication_controller.go index 898efa29aab..842610f3174 100644 --- a/pkg/controller/replication_controller.go +++ b/pkg/controller/replication_controller.go @@ -58,7 +58,7 @@ func (r RealPodControl) createReplica(controllerSpec api.ReplicationController) if labels != nil { labels["replicationController"] = controllerSpec.ID } - pod := api.Pod{ + pod := &api.Pod{ DesiredState: controllerSpec.DesiredState.PodTemplate.DesiredState, Labels: controllerSpec.DesiredState.PodTemplate.Labels, } diff --git a/pkg/controller/replication_controller_test.go b/pkg/controller/replication_controller_test.go index 028a0745e1b..fd319b149d0 100644 --- a/pkg/controller/replication_controller_test.go +++ b/pkg/controller/replication_controller_test.go @@ -85,7 +85,7 @@ func newReplicationController(replicas int) api.ReplicationController { } } -func newPodList(count int) api.PodList { +func newPodList(count int) *api.PodList { pods := []api.Pod{} for i := 0; i < count; i++ { pods = append(pods, api.Pod{ @@ -94,7 +94,7 @@ func newPodList(count int) api.PodList { }, }) } - return api.PodList{ + return &api.PodList{ Items: pods, } } @@ -109,7 +109,7 @@ func validateSyncReplication(t *testing.T, fakePodControl *FakePodControl, expec } func TestSyncReplicationControllerDoesNothing(t *testing.T) { - body, _ := runtime.Encode(newPodList(2)) + body, _ := runtime.DefaultCodec.Encode(newPodList(2)) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(body), @@ -129,7 +129,7 @@ func TestSyncReplicationControllerDoesNothing(t *testing.T) { } func TestSyncReplicationControllerDeletes(t *testing.T) { - body, _ := runtime.Encode(newPodList(2)) + body, _ := runtime.DefaultCodec.Encode(newPodList(2)) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(body), @@ -149,7 +149,7 @@ func TestSyncReplicationControllerDeletes(t *testing.T) { } func TestSyncReplicationControllerCreates(t *testing.T) { - body, _ := runtime.Encode(newPodList(0)) + body, _ := runtime.DefaultCodec.Encode(newPodList(0)) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(body), @@ -169,7 +169,7 @@ func TestSyncReplicationControllerCreates(t *testing.T) { } func TestCreateReplica(t *testing.T) { - body, _ := runtime.Encode(api.Pod{}) + body, _ := runtime.DefaultCodec.Encode(&api.Pod{}) fakeHandler := util.FakeHandler{ StatusCode: 200, ResponseBody: string(body), @@ -292,7 +292,7 @@ func TestSyncronize(t *testing.T) { } fakeControllerHandler := util.FakeHandler{ StatusCode: 200, - ResponseBody: runtime.EncodeOrDie(&api.ReplicationControllerList{ + ResponseBody: runtime.DefaultScheme.EncodeOrDie(&api.ReplicationControllerList{ Items: []api.ReplicationController{ controllerSpec1, controllerSpec2, diff --git a/pkg/conversion/converter.go b/pkg/conversion/converter.go index 32cb04da971..b5f7f93c9d5 100644 --- a/pkg/conversion/converter.go +++ b/pkg/conversion/converter.go @@ -37,7 +37,7 @@ type Converter struct { // do the conversion. funcs map[typePair]reflect.Value - // If true, print helpful debugging info. Quite verbose. + // If non-nil, will be called to print helpful debugging info. Quite verbose. Debug DebugLogger } @@ -48,29 +48,43 @@ func NewConverter() *Converter { } } +// Scope is passed to conversion funcs to allow them to continue an ongoing conversion. +// If multiple converters exist in the system, Scope will allow you to use the correct one +// from a conversion function--that is, the one your conversion function was called by. +type Scope interface { + // Call Convert to convert sub-objects. Note that if you call it with your own exact + // parameters, you'll run out of stack space before anything useful happens. + Convert(src, dest interface{}, flags FieldMatchingFlags) error +} + // Register registers a conversion func with the Converter. conversionFunc must take -// two parameters, the input and output type. It must take a pointer to each. It must -// return an error. +// three parameters: a pointer to the input type, a pointer to the output type, and +// a conversion.Scope (which should be used if recursive conversion calls are desired). +// It must return an error. // // Example: -// c.Register(func(in *Pod, out *v1beta1.Pod) error { ... return nil }) +// c.Register(func(in *Pod, out *v1beta1.Pod, s Scope) error { ... return nil }) func (c *Converter) Register(conversionFunc interface{}) error { fv := reflect.ValueOf(conversionFunc) ft := fv.Type() if ft.Kind() != reflect.Func { return fmt.Errorf("expected func, got: %v", ft) } - if ft.NumIn() != 2 { - return fmt.Errorf("expected two in params, got: %v", ft) + if ft.NumIn() != 3 { + return fmt.Errorf("expected three 'in' params, got: %v", ft) } if ft.NumOut() != 1 { - return fmt.Errorf("expected one out param, got: %v", ft) + return fmt.Errorf("expected one 'out' param, got: %v", ft) } if ft.In(0).Kind() != reflect.Ptr { - return fmt.Errorf("expected pointer arg for in param 0, got: %v", ft) + return fmt.Errorf("expected pointer arg for 'in' param 0, got: %v", ft) } if ft.In(1).Kind() != reflect.Ptr { - return fmt.Errorf("expected pointer arg for in param 1, got: %v", ft) + return fmt.Errorf("expected pointer arg for 'in' param 1, got: %v", ft) + } + scopeType := Scope(c) + if e, a := reflect.TypeOf(&scopeType).Elem(), ft.In(2); e != a { + return fmt.Errorf("expected '%v' arg for 'in' param 2, got '%v' (%v)", e, a, ft) } var forErrorType error // This convolution is necessary, otherwise TypeOf picks up on the fact @@ -138,7 +152,8 @@ func (c *Converter) convert(sv, dv reflect.Value, flags FieldMatchingFlags) erro if c.Debug != nil { c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt) } - ret := fv.Call([]reflect.Value{sv.Addr(), dv.Addr()})[0].Interface() + args := []reflect.Value{sv.Addr(), dv.Addr(), reflect.ValueOf(Scope(c))} + ret := fv.Call(args)[0].Interface() // This convolution is necssary because nil interfaces won't convert // to errors. if ret == nil { diff --git a/pkg/conversion/converter_test.go b/pkg/conversion/converter_test.go index 8e33e7e94e9..96c569cd6cd 100644 --- a/pkg/conversion/converter_test.go +++ b/pkg/conversion/converter_test.go @@ -27,28 +27,30 @@ import ( func TestConverter_CallsRegisteredFunctions(t *testing.T) { type A struct { Foo string + Baz int } type B struct { Bar string + Baz int } type C struct{} c := NewConverter() - err := c.Register(func(in *A, out *B) error { + err := c.Register(func(in *A, out *B, s Scope) error { out.Bar = in.Foo - return nil + return s.Convert(&in.Baz, &out.Baz, 0) }) if err != nil { t.Fatalf("unexpected error %v", err) } - err = c.Register(func(in *B, out *A) error { + err = c.Register(func(in *B, out *A, s Scope) error { out.Foo = in.Bar - return nil + return s.Convert(&in.Baz, &out.Baz, 0) }) if err != nil { t.Fatalf("unexpected error %v", err) } - x := A{"hello, intrepid test reader!"} + x := A{"hello, intrepid test reader!", 3} y := B{} err = c.Convert(&x, &y, 0) @@ -58,8 +60,11 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) { if e, a := x.Foo, y.Bar; e != a { t.Errorf("expected %v, got %v", e, a) } + if e, a := x.Baz, y.Baz; e != a { + t.Errorf("expected %v, got %v", e, a) + } - z := B{"all your test are belong to us"} + z := B{"all your test are belong to us", 42} w := A{} err = c.Convert(&z, &w, 0) @@ -69,8 +74,11 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) { if e, a := z.Bar, w.Foo; e != a { t.Errorf("expected %v, got %v", e, a) } + if e, a := z.Baz, w.Baz; e != a { + t.Errorf("expected %v, got %v", e, a) + } - err = c.Register(func(in *A, out *C) error { + err = c.Register(func(in *A, out *C, s Scope) error { return fmt.Errorf("C can't store an A, silly") }) if err != nil { @@ -85,7 +93,7 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) { func TestConverter_fuzz(t *testing.T) { newAnonType := func() interface{} { - return reflect.New(reflect.TypeOf(externalTypeReturn())).Interface() + return reflect.New(reflect.TypeOf(externalTypeReturn()).Elem()).Interface() } // Use the same types from the scheme test. table := []struct { diff --git a/pkg/conversion/scheme.go b/pkg/conversion/scheme.go index 1c0a9836c2d..af88ba4e63e 100644 --- a/pkg/conversion/scheme.go +++ b/pkg/conversion/scheme.go @@ -85,8 +85,8 @@ func NewScheme() *Scheme { // AddKnownTypes registers all types passed in 'types' as being members of version 'version. // Encode() will refuse objects unless their type has been registered with AddKnownTypes. -// All objects passed to types should be structs, not pointers to structs. The name that go -// reports for the struct becomes the "kind" field when encoding. +// All objects passed to types should be pointers to structs. The name that go reports for +// the struct becomes the "kind" field when encoding. func (s *Scheme) AddKnownTypes(version string, types ...interface{}) { knownTypes, found := s.versionMap[version] if !found { @@ -95,8 +95,12 @@ func (s *Scheme) AddKnownTypes(version string, types ...interface{}) { } for _, obj := range types { t := reflect.TypeOf(obj) + if t.Kind() != reflect.Ptr { + panic("All types must be pointers to structs.") + } + t = t.Elem() if t.Kind() != reflect.Struct { - panic("All types must be structs.") + panic("All types must be pointers to structs.") } knownTypes[t.Name()] = t s.typeToVersion[t] = version diff --git a/pkg/conversion/scheme_test.go b/pkg/conversion/scheme_test.go index 0f4d7f2a4e1..c8232ff4ea6 100644 --- a/pkg/conversion/scheme_test.go +++ b/pkg/conversion/scheme_test.go @@ -90,7 +90,7 @@ func externalTypeReturn() interface{} { O *TestType2 `yaml:"O,omitempty" json:"O,omitempty"` P []TestType2 `yaml:"Q,omitempty" json:"Q,omitempty"` } - return TestType1{} + return &TestType1{} } type ExternalInternalSame struct { @@ -124,8 +124,8 @@ var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs( // Returns a new Scheme set up with the test objects. func GetTestScheme() *Scheme { s := NewScheme() - s.AddKnownTypes("", TestType1{}, ExternalInternalSame{}) - s.AddKnownTypes("v1", externalTypeReturn(), ExternalInternalSame{}) + s.AddKnownTypes("", &TestType1{}, &ExternalInternalSame{}) + s.AddKnownTypes("v1", externalTypeReturn(), &ExternalInternalSame{}) s.ExternalVersion = "v1" s.InternalVersion = "" s.MetaInsertionFactory = testMetaInsertionFactory{} diff --git a/pkg/election/etcd_master.go b/pkg/election/etcd_master.go index 2a7020f0572..35775f2a03f 100644 --- a/pkg/election/etcd_master.go +++ b/pkg/election/etcd_master.go @@ -27,6 +27,15 @@ import ( "github.com/golang/glog" ) +// Master is used to announce the current elected master. +type Master string + +// IsAnAPIObject is used solely so we can work with the watch package. +// TODO: Either fix watch so this isn't necessary, or make this a real API Object. +// TODO: when it becomes clear how this package will be used, move these declarations to +// to the proper place. +func (Master) IsAnAPIObject() {} + // NewEtcdMasterElector returns an implementation of election.MasterElector backed by etcd. func NewEtcdMasterElector(h tools.EtcdGetSet) MasterElector { return &etcdMasterElector{etcd: h} @@ -58,7 +67,7 @@ func (e *etcdMasterElector) run(path, id string) { case m := <-masters: e.events <- watch.Event{ Type: watch.Modified, - Object: m, + Object: Master(m), } case e := <-errors: glog.Errorf("error in election: %v", e) diff --git a/pkg/election/etcd_master_test.go b/pkg/election/etcd_master_test.go index ea1a69d4fe1..a34b3eb3c8c 100644 --- a/pkg/election/etcd_master_test.go +++ b/pkg/election/etcd_master_test.go @@ -31,7 +31,7 @@ func TestEtcdMasterOther(t *testing.T) { master := NewEtcdMasterElector(etcd) w := master.Elect(path, "bar") result := <-w.ResultChan() - if result.Type != watch.Modified || result.Object.(string) != "baz" { + if result.Type != watch.Modified || result.Object.(Master) != "baz" { t.Errorf("unexpected event: %#v", result) } w.Stop() @@ -52,7 +52,7 @@ func TestEtcdMasterNoOther(t *testing.T) { master := NewEtcdMasterElector(e) w := master.Elect(path, "bar") result := <-w.ResultChan() - if result.Type != watch.Modified || result.Object.(string) != "bar" { + if result.Type != watch.Modified || result.Object.(Master) != "bar" { t.Errorf("unexpected event: %#v", result) } w.Stop() @@ -91,7 +91,7 @@ func TestEtcdMasterNoOtherThenConflict(t *testing.T) { master := NewEtcdMasterElector(e) w := master.Elect(path, "bar") result := <-w.ResultChan() - if result.Type != watch.Modified || result.Object.(string) != "bar" { + if result.Type != watch.Modified || result.Object.(Master) != "bar" { t.Errorf("unexpected event: %#v", result) } w.Stop() diff --git a/pkg/kubecfg/kubecfg.go b/pkg/kubecfg/kubecfg.go index 35fc4b7bcc5..a64eece05f7 100644 --- a/pkg/kubecfg/kubecfg.go +++ b/pkg/kubecfg/kubecfg.go @@ -174,7 +174,7 @@ func portsFromString(spec string) []api.Port { // RunController creates a new replication controller named 'name' which creates 'replicas' pods running 'image'. func RunController(image, name string, replicas int, client client.Interface, portSpec string, servicePort int) error { - controller := api.ReplicationController{ + controller := &api.ReplicationController{ JSONBase: api.JSONBase{ ID: name, }, @@ -227,8 +227,8 @@ func RunController(image, name string, replicas int, client client.Interface, po return nil } -func createService(name string, port int, client client.Interface) (api.Service, error) { - svc := api.Service{ +func createService(name string, port int, client client.Interface) (*api.Service, error) { + svc := &api.Service{ JSONBase: api.JSONBase{ID: name}, Port: port, Labels: map[string]string{ diff --git a/pkg/kubecfg/kubecfg_test.go b/pkg/kubecfg/kubecfg_test.go index 32cddf70779..092c3978897 100644 --- a/pkg/kubecfg/kubecfg_test.go +++ b/pkg/kubecfg/kubecfg_test.go @@ -46,7 +46,7 @@ func TestUpdateWithPods(t *testing.T) { } Update("foo", &fakeClient, 0, "") if len(fakeClient.Actions) != 5 { - t.Errorf("Unexpected action list %#v", fakeClient.Actions) + t.Fatalf("Unexpected action list %#v", fakeClient.Actions) } validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t) validateAction(client.FakeAction{Action: "list-pods"}, fakeClient.Actions[1], t) @@ -94,7 +94,7 @@ func TestUpdateWithNewImage(t *testing.T) { } validateAction(client.FakeAction{Action: "get-controller", Value: "foo"}, fakeClient.Actions[0], t) - newCtrl := *runtime.CopyOrDie(fakeClient.Ctrl).(*api.ReplicationController) + newCtrl := runtime.DefaultScheme.CopyOrDie(&fakeClient.Ctrl).(*api.ReplicationController) newCtrl.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image = "fooImage:2" validateAction(client.FakeAction{Action: "update-controller", Value: newCtrl}, fakeClient.Actions[1], t) @@ -114,7 +114,7 @@ func TestRunController(t *testing.T) { if len(fakeClient.Actions) != 1 || fakeClient.Actions[0].Action != "create-controller" { t.Errorf("Unexpected actions: %#v", fakeClient.Actions) } - controller := fakeClient.Actions[0].Value.(api.ReplicationController) + controller := fakeClient.Actions[0].Value.(*api.ReplicationController) if controller.ID != name || controller.DesiredState.Replicas != replicas || controller.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image != image { @@ -133,7 +133,7 @@ func TestRunControllerWithService(t *testing.T) { fakeClient.Actions[1].Action != "create-service" { t.Errorf("Unexpected actions: %#v", fakeClient.Actions) } - controller := fakeClient.Actions[0].Value.(api.ReplicationController) + controller := fakeClient.Actions[0].Value.(*api.ReplicationController) if controller.ID != name || controller.DesiredState.Replicas != replicas || controller.DesiredState.PodTemplate.DesiredState.Manifest.Containers[0].Image != image { @@ -152,7 +152,7 @@ func TestStopController(t *testing.T) { fakeClient.Actions[0].Value.(string) != name { t.Errorf("Unexpected Action: %#v", fakeClient.Actions[0]) } - controller := fakeClient.Actions[1].Value.(api.ReplicationController) + controller := fakeClient.Actions[1].Value.(*api.ReplicationController) if fakeClient.Actions[1].Action != "update-controller" || controller.DesiredState.Replicas != 0 { t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1]) @@ -171,7 +171,7 @@ func TestResizeController(t *testing.T) { fakeClient.Actions[0].Value.(string) != name { t.Errorf("Unexpected Action: %#v", fakeClient.Actions[0]) } - controller := fakeClient.Actions[1].Value.(api.ReplicationController) + controller := fakeClient.Actions[1].Value.(*api.ReplicationController) if fakeClient.Actions[1].Action != "update-controller" || controller.DesiredState.Replicas != 17 { t.Errorf("Unexpected Action: %#v", fakeClient.Actions[1]) diff --git a/pkg/kubecfg/parse.go b/pkg/kubecfg/parse.go index 8069d9c1cef..08456e41ef0 100644 --- a/pkg/kubecfg/parse.go +++ b/pkg/kubecfg/parse.go @@ -27,10 +27,11 @@ type Parser struct { storageToType map[string]reflect.Type } -func NewParser(objectMap map[string]interface{}) *Parser { +// NewParser creates a new parser. +func NewParser(objectMap map[string]runtime.Object) *Parser { typeMap := make(map[string]reflect.Type) for name, obj := range objectMap { - typeMap[name] = reflect.TypeOf(obj) + typeMap[name] = reflect.TypeOf(obj).Elem() } return &Parser{typeMap} } @@ -43,12 +44,12 @@ func (p *Parser) ToWireFormat(data []byte, storage string) ([]byte, error) { return nil, fmt.Errorf("unknown storage type: %v", storage) } - obj := reflect.New(prototypeType).Interface() - err := runtime.DecodeInto(data, obj) + obj := reflect.New(prototypeType).Interface().(runtime.Object) + err := runtime.DefaultCodec.DecodeInto(data, obj) if err != nil { return nil, err } - return runtime.Encode(obj) + return runtime.DefaultCodec.Encode(obj) } func (p *Parser) SupportedWireStorage() []string { diff --git a/pkg/kubecfg/parse_test.go b/pkg/kubecfg/parse_test.go index 99e7f907e25..758f9c05c8f 100644 --- a/pkg/kubecfg/parse_test.go +++ b/pkg/kubecfg/parse_test.go @@ -25,15 +25,15 @@ import ( ) func TestParseBadStorage(t *testing.T) { - p := NewParser(map[string]interface{}{}) + p := NewParser(map[string]runtime.Object{}) _, err := p.ToWireFormat([]byte("{}"), "badstorage") if err == nil { t.Errorf("Expected error, received none") } } -func DoParseTest(t *testing.T, storage string, obj interface{}, p *Parser) { - jsonData, _ := runtime.Encode(obj) +func DoParseTest(t *testing.T, storage string, obj runtime.Object, p *Parser) { + jsonData, _ := runtime.DefaultCodec.Encode(obj) yamlData, _ := yaml.Marshal(obj) t.Logf("Intermediate yaml:\n%v\n", string(yamlData)) t.Logf("Intermediate json:\n%v\n", string(jsonData)) @@ -56,14 +56,14 @@ func DoParseTest(t *testing.T, storage string, obj interface{}, p *Parser) { } } -var testParser = NewParser(map[string]interface{}{ - "pods": api.Pod{}, - "services": api.Service{}, - "replicationControllers": api.ReplicationController{}, +var testParser = NewParser(map[string]runtime.Object{ + "pods": &api.Pod{}, + "services": &api.Service{}, + "replicationControllers": &api.ReplicationController{}, }) func TestParsePod(t *testing.T) { - DoParseTest(t, "pods", api.Pod{ + DoParseTest(t, "pods", &api.Pod{ JSONBase: api.JSONBase{APIVersion: "v1beta1", ID: "test pod", Kind: "Pod"}, DesiredState: api.PodState{ Manifest: api.ContainerManifest{ @@ -80,7 +80,7 @@ func TestParsePod(t *testing.T) { } func TestParseService(t *testing.T) { - DoParseTest(t, "services", api.Service{ + DoParseTest(t, "services", &api.Service{ JSONBase: api.JSONBase{APIVersion: "v1beta1", ID: "my service", Kind: "Service"}, Port: 8080, Labels: map[string]string{ @@ -93,7 +93,7 @@ func TestParseService(t *testing.T) { } func TestParseController(t *testing.T) { - DoParseTest(t, "replicationControllers", api.ReplicationController{ + DoParseTest(t, "replicationControllers", &api.ReplicationController{ JSONBase: api.JSONBase{APIVersion: "v1beta1", ID: "my controller", Kind: "ReplicationController"}, DesiredState: api.ReplicationControllerState{ Replicas: 9001, @@ -119,13 +119,15 @@ type TestParseType struct { Data string `json:"data" yaml:"data"` } +func (*TestParseType) IsAnAPIObject() {} + func TestParseCustomType(t *testing.T) { - runtime.AddKnownTypes("", TestParseType{}) - runtime.AddKnownTypes("v1beta1", TestParseType{}) - parser := NewParser(map[string]interface{}{ - "custom": TestParseType{}, + runtime.DefaultScheme.AddKnownTypes("", &TestParseType{}) + runtime.DefaultScheme.AddKnownTypes("v1beta1", &TestParseType{}) + parser := NewParser(map[string]runtime.Object{ + "custom": &TestParseType{}, }) - DoParseTest(t, "custom", TestParseType{ + DoParseTest(t, "custom", &TestParseType{ JSONBase: api.JSONBase{APIVersion: "", ID: "my custom object", Kind: "TestParseType"}, Data: "test data", }, parser) diff --git a/pkg/kubecfg/proxy_server.go b/pkg/kubecfg/proxy_server.go index 03a2dcdcea8..002ba6530ee 100644 --- a/pkg/kubecfg/proxy_server.go +++ b/pkg/kubecfg/proxy_server.go @@ -53,7 +53,7 @@ func (s *ProxyServer) Serve() error { func (s *ProxyServer) doError(w http.ResponseWriter, err error) { w.WriteHeader(http.StatusInternalServerError) w.Header().Add("Content-type", "application/json") - data, _ := runtime.Encode(api.Status{ + data, _ := runtime.DefaultCodec.Encode(&api.Status{ Status: api.StatusFailure, Message: fmt.Sprintf("internal error: %#v", err), }) diff --git a/pkg/kubecfg/resource_printer.go b/pkg/kubecfg/resource_printer.go index 9ba4e6acf86..b3c5a54d348 100644 --- a/pkg/kubecfg/resource_printer.go +++ b/pkg/kubecfg/resource_printer.go @@ -36,7 +36,7 @@ import ( type ResourcePrinter interface { // Print receives an arbitrary JSON body, formats it and prints it to a writer. Print([]byte, io.Writer) error - PrintObj(interface{}, io.Writer) error + PrintObj(runtime.Object, io.Writer) error } // IdentityPrinter is an implementation of ResourcePrinter which simply copies the body out to the output stream. @@ -49,8 +49,8 @@ func (i *IdentityPrinter) Print(data []byte, w io.Writer) error { } // PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer. -func (i *IdentityPrinter) PrintObj(obj interface{}, output io.Writer) error { - data, err := runtime.Encode(obj) +func (i *IdentityPrinter) PrintObj(obj runtime.Object, output io.Writer) error { + data, err := runtime.DefaultCodec.Encode(obj) if err != nil { return err } @@ -75,7 +75,7 @@ func (y *YAMLPrinter) Print(data []byte, w io.Writer) error { } // PrintObj prints the data as YAML. -func (y *YAMLPrinter) PrintObj(obj interface{}, w io.Writer) error { +func (y *YAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error { output, err := yaml.Marshal(obj) if err != nil { return err @@ -251,7 +251,7 @@ func printStatus(status *api.Status, w io.Writer) error { // Print parses the data as JSON, then prints the parsed data in a human-friendly // format according to the type of the data. func (h *HumanReadablePrinter) Print(data []byte, output io.Writer) error { - var mapObj map[string]interface{} + var mapObj map[string]runtime.Object if err := json.Unmarshal([]byte(data), &mapObj); err != nil { return err } @@ -260,7 +260,7 @@ func (h *HumanReadablePrinter) Print(data []byte, output io.Writer) error { return fmt.Errorf("unexpected object with no 'kind' field: %s", data) } - obj, err := runtime.Decode(data) + obj, err := runtime.DefaultCodec.Decode(data) if err != nil { return err } @@ -268,7 +268,7 @@ func (h *HumanReadablePrinter) Print(data []byte, output io.Writer) error { } // PrintObj prints the obj in a human-friendly format according to the type of the obj. -func (h *HumanReadablePrinter) PrintObj(obj interface{}, output io.Writer) error { +func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) error { w := tabwriter.NewWriter(output, 20, 5, 3, ' ', 0) defer w.Flush() if handler := h.handlerMap[reflect.TypeOf(obj)]; handler != nil { @@ -292,7 +292,7 @@ type TemplatePrinter struct { // Print parses the data as JSON, and re-formats it with the Go Template. func (t *TemplatePrinter) Print(data []byte, w io.Writer) error { - obj, err := runtime.Decode(data) + obj, err := runtime.DefaultCodec.Decode(data) if err != nil { return err } @@ -300,6 +300,6 @@ func (t *TemplatePrinter) Print(data []byte, w io.Writer) error { } // PrintObj formats the obj with the Go Template. -func (t *TemplatePrinter) PrintObj(obj interface{}, w io.Writer) error { +func (t *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error { return t.Template.Execute(w, obj) } diff --git a/pkg/kubecfg/resource_printer_test.go b/pkg/kubecfg/resource_printer_test.go index 03b7cc23759..6e72168cc64 100644 --- a/pkg/kubecfg/resource_printer_test.go +++ b/pkg/kubecfg/resource_printer_test.go @@ -67,7 +67,7 @@ func TestYAMLPrinterPrint(t *testing.T) { t.Errorf("Test data and unmarshaled data are not equal: %#v vs %#v", poutput, testData) } - obj := api.Pod{ + obj := &api.Pod{ JSONBase: api.JSONBase{ID: "foo"}, } buf.Reset() @@ -77,8 +77,8 @@ func TestYAMLPrinterPrint(t *testing.T) { if err != nil { t.Errorf("Unexpeted error: %#v", err) } - if !reflect.DeepEqual(obj, objOut) { - t.Errorf("Unexpected inequality: %#v vs %#v", obj, objOut) + if !reflect.DeepEqual(obj, &objOut) { + t.Errorf("Unexpected inequality: %#v vs %#v", obj, &objOut) } } @@ -91,16 +91,16 @@ func TestIdentityPrinter(t *testing.T) { t.Errorf("Bytes are not equal: %s vs %s", str, buff.String()) } - obj := api.Pod{ + obj := &api.Pod{ JSONBase: api.JSONBase{ID: "foo"}, } buff.Reset() printer.PrintObj(obj, buff) - objOut, err := runtime.Decode([]byte(buff.String())) + objOut, err := runtime.DefaultCodec.Decode([]byte(buff.String())) if err != nil { t.Errorf("Unexpeted error: %#v", err) } - if !reflect.DeepEqual(&obj, objOut) { + if !reflect.DeepEqual(obj, objOut) { t.Errorf("Unexpected inequality: %#v vs %#v", obj, objOut) } } @@ -109,8 +109,12 @@ type TestPrintType struct { Data string } +func (*TestPrintType) IsAnAPIObject() {} + type TestUnknownType struct{} +func (*TestUnknownType) IsAnAPIObject() {} + func PrintCustomType(obj *TestPrintType, w io.Writer) error { _, err := fmt.Fprintf(w, "%s", obj.Data) return err diff --git a/pkg/kubelet/config/etcd.go b/pkg/kubelet/config/etcd.go index a48d8413c6b..07cb87b5c8a 100644 --- a/pkg/kubelet/config/etcd.go +++ b/pkg/kubelet/config/etcd.go @@ -47,8 +47,8 @@ type SourceEtcd struct { func NewSourceEtcd(key string, client tools.EtcdClient, updates chan<- interface{}) *SourceEtcd { helper := tools.EtcdHelper{ client, - runtime.Codec, - runtime.ResourceVersioner, + runtime.DefaultCodec, + runtime.DefaultResourceVersioner, } source := &SourceEtcd{ key: key, diff --git a/pkg/master/master.go b/pkg/master/master.go index 8f802108b45..97d1a958a21 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -131,10 +131,10 @@ func (m *Master) init(cloud cloudprovider.Interface, podInfoGetter client.PodInf } // API_v1beta1 returns the resources and codec for API version v1beta1. -func (m *Master) API_v1beta1() (map[string]apiserver.RESTStorage, apiserver.Codec) { +func (m *Master) API_v1beta1() (map[string]apiserver.RESTStorage, runtime.Codec) { storage := make(map[string]apiserver.RESTStorage) for k, v := range m.storage { storage[k] = v } - return storage, runtime.Codec + return storage, runtime.DefaultCodec } diff --git a/pkg/proxy/config/api.go b/pkg/proxy/config/api.go index 7c411ad45d1..1c501d4ce26 100644 --- a/pkg/proxy/config/api.go +++ b/pkg/proxy/config/api.go @@ -29,8 +29,8 @@ import ( // Watcher is the interface needed to receive changes to services and endpoints. type Watcher interface { - ListServices(label labels.Selector) (api.ServiceList, error) - ListEndpoints(label labels.Selector) (api.EndpointsList, error) + ListServices(label labels.Selector) (*api.ServiceList, error) + ListEndpoints(label labels.Selector) (*api.EndpointsList, error) WatchServices(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) WatchEndpoints(label, field labels.Selector, resourceVersion uint64) (watch.Interface, error) } diff --git a/pkg/proxy/config/etcd.go b/pkg/proxy/config/etcd.go index 1e604cbed67..1795b10e1e4 100644 --- a/pkg/proxy/config/etcd.go +++ b/pkg/proxy/config/etcd.go @@ -133,7 +133,7 @@ func (s ConfigSourceEtcd) GetServices() ([]api.Service, []api.Endpoints, error) // and create a Service entry for it. for i, node := range response.Node.Nodes { var svc api.Service - err = runtime.DecodeInto([]byte(node.Value), &svc) + err = runtime.DefaultCodec.DecodeInto([]byte(node.Value), &svc) if err != nil { glog.Errorf("Failed to load Service: %s (%#v)", node.Value, err) continue @@ -166,7 +166,7 @@ func (s ConfigSourceEtcd) GetEndpoints(service string) (api.Endpoints, error) { } // Parse all the endpoint specifications in this value. var e api.Endpoints - err = runtime.DecodeInto([]byte(response.Node.Value), &e) + err = runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &e) return e, err } @@ -176,7 +176,7 @@ func etcdResponseToService(response *etcd.Response) (*api.Service, error) { return nil, fmt.Errorf("invalid response from etcd: %#v", response) } var svc api.Service - err := runtime.DecodeInto([]byte(response.Node.Value), &svc) + err := runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &svc) if err != nil { return nil, err } @@ -230,7 +230,7 @@ func (s ConfigSourceEtcd) ProcessChange(response *etcd.Response) { func (s ConfigSourceEtcd) ProcessEndpointResponse(response *etcd.Response) { glog.Infof("Processing a change in endpoint configuration... %s", *response) var endpoints api.Endpoints - err := runtime.DecodeInto([]byte(response.Node.Value), &endpoints) + err := runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &endpoints) if err != nil { glog.Errorf("Failed to parse service out of etcd key: %v : %+v", response.Node.Value, err) return diff --git a/pkg/registry/binding/storage.go b/pkg/registry/binding/storage.go index c6aeef564c6..85cc2fc16b7 100644 --- a/pkg/registry/binding/storage.go +++ b/pkg/registry/binding/storage.go @@ -23,6 +23,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) // BindingStorage implements the RESTStorage interface. When bindings are written, it @@ -40,32 +41,32 @@ func NewBindingStorage(bindingRegistry Registry) *BindingStorage { } // List returns an error because bindings are write-only objects. -func (*BindingStorage) List(selector labels.Selector) (interface{}, error) { +func (*BindingStorage) List(selector labels.Selector) (runtime.Object, error) { return nil, errors.NewNotFound("binding", "list") } // Get returns an error because bindings are write-only objects. -func (*BindingStorage) Get(id string) (interface{}, error) { +func (*BindingStorage) Get(id string) (runtime.Object, error) { return nil, errors.NewNotFound("binding", id) } // Delete returns an error because bindings are write-only objects. -func (*BindingStorage) Delete(id string) (<-chan interface{}, error) { +func (*BindingStorage) Delete(id string) (<-chan runtime.Object, error) { return nil, errors.NewNotFound("binding", id) } // New returns a new binding object fit for having data unmarshalled into it. -func (*BindingStorage) New() interface{} { +func (*BindingStorage) New() runtime.Object { return &api.Binding{} } // Create attempts to make the assignment indicated by the binding it recieves. -func (b *BindingStorage) Create(obj interface{}) (<-chan interface{}, error) { +func (b *BindingStorage) Create(obj runtime.Object) (<-chan runtime.Object, error) { binding, ok := obj.(*api.Binding) if !ok { return nil, fmt.Errorf("incorrect type: %#v", obj) } - return apiserver.MakeAsync(func() (interface{}, error) { + return apiserver.MakeAsync(func() (runtime.Object, error) { if err := b.registry.ApplyBinding(binding); err != nil { return nil, err } @@ -74,6 +75,6 @@ func (b *BindingStorage) Create(obj interface{}) (<-chan interface{}, error) { } // Update returns an error-- this object may not be updated. -func (b *BindingStorage) Update(obj interface{}) (<-chan interface{}, error) { +func (b *BindingStorage) Update(obj runtime.Object) (<-chan runtime.Object, error) { return nil, fmt.Errorf("Bindings may not be changed.") } diff --git a/pkg/registry/binding/storage_test.go b/pkg/registry/binding/storage_test.go index 6dfe59721d3..d70a6717f82 100644 --- a/pkg/registry/binding/storage_test.go +++ b/pkg/registry/binding/storage_test.go @@ -38,12 +38,12 @@ func TestNewBindingStorage(t *testing.T) { PodID: "foo", Host: "bar", } - body, err := runtime.Encode(binding) + body, err := runtime.DefaultCodec.Encode(binding) if err != nil { t.Fatalf("Unexpected encode error %v", err) } obj := b.New() - err = runtime.DecodeInto(body, obj) + err = runtime.DefaultCodec.DecodeInto(body, obj) if err != nil { t.Fatalf("Unexpected error %v", err) } diff --git a/pkg/registry/controller/registry.go b/pkg/registry/controller/registry.go index ab6d27be14b..621577fd539 100644 --- a/pkg/registry/controller/registry.go +++ b/pkg/registry/controller/registry.go @@ -26,7 +26,7 @@ type Registry interface { ListControllers() (*api.ReplicationControllerList, error) WatchControllers(resourceVersion uint64) (watch.Interface, error) GetController(controllerID string) (*api.ReplicationController, error) - CreateController(controller api.ReplicationController) error - UpdateController(controller api.ReplicationController) error + CreateController(controller *api.ReplicationController) error + UpdateController(controller *api.ReplicationController) error DeleteController(controllerID string) error } diff --git a/pkg/registry/controller/storage.go b/pkg/registry/controller/storage.go index 8ce5e5fe469..9cf5dd51445 100644 --- a/pkg/registry/controller/storage.go +++ b/pkg/registry/controller/storage.go @@ -26,6 +26,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" @@ -51,7 +52,7 @@ func NewRegistryStorage(registry Registry, podRegistry pod.Registry) apiserver.R } // Create registers the given ReplicationController. -func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { +func (rs *RegistryStorage) Create(obj runtime.Object) (<-chan runtime.Object, error) { controller, ok := obj.(*api.ReplicationController) if !ok { return nil, fmt.Errorf("not a replication controller: %#v", obj) @@ -67,8 +68,8 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { controller.CreationTimestamp = util.Now() - return apiserver.MakeAsync(func() (interface{}, error) { - err := rs.registry.CreateController(*controller) + return apiserver.MakeAsync(func() (runtime.Object, error) { + err := rs.registry.CreateController(controller) if err != nil { return nil, err } @@ -77,14 +78,14 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { } // Delete asynchronously deletes the ReplicationController specified by its id. -func (rs *RegistryStorage) Delete(id string) (<-chan interface{}, error) { - return apiserver.MakeAsync(func() (interface{}, error) { +func (rs *RegistryStorage) Delete(id string) (<-chan runtime.Object, error) { + return apiserver.MakeAsync(func() (runtime.Object, error) { return &api.Status{Status: api.StatusSuccess}, rs.registry.DeleteController(id) }), nil } // Get obtains the ReplicationController specified by its id. -func (rs *RegistryStorage) Get(id string) (interface{}, error) { +func (rs *RegistryStorage) Get(id string) (runtime.Object, error) { controller, err := rs.registry.GetController(id) if err != nil { return nil, err @@ -93,7 +94,7 @@ func (rs *RegistryStorage) Get(id string) (interface{}, error) { } // List obtains a list of ReplicationControllers that match selector. -func (rs *RegistryStorage) List(selector labels.Selector) (interface{}, error) { +func (rs *RegistryStorage) List(selector labels.Selector) (runtime.Object, error) { controllers, err := rs.registry.ListControllers() if err != nil { return nil, err @@ -109,13 +110,13 @@ func (rs *RegistryStorage) List(selector labels.Selector) (interface{}, error) { } // New creates a new ReplicationController for use with Create and Update. -func (rs RegistryStorage) New() interface{} { +func (rs RegistryStorage) New() runtime.Object { return &api.ReplicationController{} } // Update replaces a given ReplicationController instance with an existing // instance in storage.registry. -func (rs *RegistryStorage) Update(obj interface{}) (<-chan interface{}, error) { +func (rs *RegistryStorage) Update(obj runtime.Object) (<-chan runtime.Object, error) { controller, ok := obj.(*api.ReplicationController) if !ok { return nil, fmt.Errorf("not a replication controller: %#v", obj) @@ -123,8 +124,8 @@ func (rs *RegistryStorage) Update(obj interface{}) (<-chan interface{}, error) { if errs := validation.ValidateReplicationController(controller); len(errs) > 0 { return nil, errors.NewInvalid("replicationController", controller.ID, errs) } - return apiserver.MakeAsync(func() (interface{}, error) { - err := rs.registry.UpdateController(*controller) + return apiserver.MakeAsync(func() (runtime.Object, error) { + err := rs.registry.UpdateController(controller) if err != nil { return nil, err } @@ -148,7 +149,7 @@ func (rs *RegistryStorage) Watch(label, field labels.Selector, resourceVersion u }), nil } -func (rs *RegistryStorage) waitForController(ctrl api.ReplicationController) (interface{}, error) { +func (rs *RegistryStorage) waitForController(ctrl *api.ReplicationController) (runtime.Object, error) { for { pods, err := rs.podRegistry.ListPods(labels.Set(ctrl.DesiredState.ReplicaSelector).AsSelector()) if err != nil { diff --git a/pkg/registry/controller/storage_test.go b/pkg/registry/controller/storage_test.go index d9514031d3b..a88c3a50ad2 100644 --- a/pkg/registry/controller/storage_test.go +++ b/pkg/registry/controller/storage_test.go @@ -112,13 +112,13 @@ func TestControllerDecode(t *testing.T) { ID: "foo", }, } - body, err := runtime.Encode(controller) + body, err := runtime.DefaultCodec.Encode(controller) if err != nil { t.Errorf("unexpected error: %v", err) } controllerOut := storage.New() - if err := runtime.DecodeInto(body, controllerOut); err != nil { + if err := runtime.DefaultCodec.DecodeInto(body, controllerOut); err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/pkg/registry/endpoint/registry.go b/pkg/registry/endpoint/registry.go index 1cd5f2d5488..ed03f0b1298 100644 --- a/pkg/registry/endpoint/registry.go +++ b/pkg/registry/endpoint/registry.go @@ -27,5 +27,5 @@ type Registry interface { ListEndpoints() (*api.EndpointsList, error) GetEndpoints(name string) (*api.Endpoints, error) WatchEndpoints(labels, fields labels.Selector, resourceVersion uint64) (watch.Interface, error) - UpdateEndpoints(e api.Endpoints) error + UpdateEndpoints(e *api.Endpoints) error } diff --git a/pkg/registry/endpoint/storage.go b/pkg/registry/endpoint/storage.go index b0e4104b9df..3b21e14f779 100644 --- a/pkg/registry/endpoint/storage.go +++ b/pkg/registry/endpoint/storage.go @@ -22,6 +22,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" ) @@ -38,12 +39,12 @@ func NewStorage(registry Registry) apiserver.RESTStorage { } // Get satisfies the RESTStorage interface. -func (rs *Storage) Get(id string) (interface{}, error) { +func (rs *Storage) Get(id string) (runtime.Object, error) { return rs.registry.GetEndpoints(id) } // List satisfies the RESTStorage interface. -func (rs *Storage) List(selector labels.Selector) (interface{}, error) { +func (rs *Storage) List(selector labels.Selector) (runtime.Object, error) { if !selector.Empty() { return nil, errors.New("label selectors are not supported on endpoints") } @@ -57,21 +58,21 @@ func (rs *Storage) Watch(label, field labels.Selector, resourceVersion uint64) ( } // Create satisfies the RESTStorage interface but is unimplemented. -func (rs *Storage) Create(obj interface{}) (<-chan interface{}, error) { +func (rs *Storage) Create(obj runtime.Object) (<-chan runtime.Object, error) { return nil, errors.New("unimplemented") } // Update satisfies the RESTStorage interface but is unimplemented. -func (rs *Storage) Update(obj interface{}) (<-chan interface{}, error) { +func (rs *Storage) Update(obj runtime.Object) (<-chan runtime.Object, error) { return nil, errors.New("unimplemented") } // Delete satisfies the RESTStorage interface but is unimplemented. -func (rs *Storage) Delete(id string) (<-chan interface{}, error) { +func (rs *Storage) Delete(id string) (<-chan runtime.Object, error) { return nil, errors.New("unimplemented") } // New implements the RESTStorage interface. -func (rs Storage) New() interface{} { +func (rs Storage) New() runtime.Object { return &api.Endpoints{} } diff --git a/pkg/registry/etcd/etcd.go b/pkg/registry/etcd/etcd.go index 38dd5f5e48c..0647f382475 100644 --- a/pkg/registry/etcd/etcd.go +++ b/pkg/registry/etcd/etcd.go @@ -45,8 +45,8 @@ func NewRegistry(client tools.EtcdClient) *Registry { registry := &Registry{ EtcdHelper: tools.EtcdHelper{ client, - runtime.Codec, - runtime.ResourceVersioner, + runtime.DefaultCodec, + runtime.DefaultResourceVersioner, }, } registry.manifestFactory = &BasicManifestFactory{ @@ -82,7 +82,7 @@ func (r *Registry) ListPods(selector labels.Selector) (*api.PodList, error) { // WatchPods begins watching for new, changed, or deleted pods. func (r *Registry) WatchPods(resourceVersion uint64, filter func(*api.Pod) bool) (watch.Interface, error) { - return r.WatchList("/registry/pods", resourceVersion, func(obj interface{}) bool { + return r.WatchList("/registry/pods", resourceVersion, func(obj runtime.Object) bool { pod, ok := obj.(*api.Pod) if !ok { glog.Errorf("Unexpected object during pod watch: %#v", obj) @@ -110,14 +110,14 @@ func makeContainerKey(machine string) string { } // CreatePod creates a pod based on a specification. -func (r *Registry) CreatePod(pod api.Pod) error { +func (r *Registry) CreatePod(pod *api.Pod) error { // Set current status to "Waiting". pod.CurrentState.Status = api.PodWaiting pod.CurrentState.Host = "" // DesiredState.Host == "" is a signal to the scheduler that this pod needs scheduling. pod.DesiredState.Status = api.PodRunning pod.DesiredState.Host = "" - return r.CreateObj(makePodKey(pod.ID), &pod) + return r.CreateObj(makePodKey(pod.ID), pod) } // ApplyBinding implements binding's registry @@ -129,7 +129,7 @@ func (r *Registry) ApplyBinding(binding *api.Binding) error { // Returns the current state of the pod, or an error. func (r *Registry) setPodHostTo(podID, oldMachine, machine string) (finalPod *api.Pod, err error) { podKey := makePodKey(podID) - err = r.AtomicUpdate(podKey, &api.Pod{}, func(obj interface{}) (interface{}, error) { + err = r.AtomicUpdate(podKey, &api.Pod{}, func(obj runtime.Object) (runtime.Object, error) { pod, ok := obj.(*api.Pod) if !ok { return nil, fmt.Errorf("unexpected object: %#v", obj) @@ -156,13 +156,13 @@ func (r *Registry) assignPod(podID string, machine string) error { return err } contKey := makeContainerKey(machine) - err = r.AtomicUpdate(contKey, &api.ContainerManifestList{}, func(in interface{}) (interface{}, error) { + err = r.AtomicUpdate(contKey, &api.ContainerManifestList{}, func(in runtime.Object) (runtime.Object, error) { manifests := *in.(*api.ContainerManifestList) manifests.Items = append(manifests.Items, manifest) if !constraint.Allowed(manifests.Items) { return nil, fmt.Errorf("The assignment would cause a constraint violation") } - return manifests, nil + return &manifests, nil }) if err != nil { // Put the pod's host back the way it was. This is a terrible hack that @@ -174,7 +174,7 @@ func (r *Registry) assignPod(podID string, machine string) error { return err } -func (r *Registry) UpdatePod(pod api.Pod) error { +func (r *Registry) UpdatePod(pod *api.Pod) error { return fmt.Errorf("unimplemented!") } @@ -205,7 +205,7 @@ func (r *Registry) DeletePod(podID string) error { } // Next, remove the pod from the machine atomically. contKey := makeContainerKey(machine) - return r.AtomicUpdate(contKey, &api.ContainerManifestList{}, func(in interface{}) (interface{}, error) { + return r.AtomicUpdate(contKey, &api.ContainerManifestList{}, func(in runtime.Object) (runtime.Object, error) { manifests := in.(*api.ContainerManifestList) newManifests := make([]api.ContainerManifest, 0, len(manifests.Items)) found := false @@ -258,7 +258,7 @@ func (r *Registry) GetController(controllerID string) (*api.ReplicationControlle } // CreateController creates a new ReplicationController. -func (r *Registry) CreateController(controller api.ReplicationController) error { +func (r *Registry) CreateController(controller *api.ReplicationController) error { err := r.CreateObj(makeControllerKey(controller.ID), controller) if tools.IsEtcdNodeExist(err) { return errors.NewAlreadyExists("replicationController", controller.ID) @@ -267,8 +267,8 @@ func (r *Registry) CreateController(controller api.ReplicationController) error } // UpdateController replaces an existing ReplicationController. -func (r *Registry) UpdateController(controller api.ReplicationController) error { - return r.SetObj(makeControllerKey(controller.ID), &controller) +func (r *Registry) UpdateController(controller *api.ReplicationController) error { + return r.SetObj(makeControllerKey(controller.ID), controller) } // DeleteController deletes a ReplicationController specified by its ID. @@ -293,7 +293,7 @@ func (r *Registry) ListServices() (*api.ServiceList, error) { } // CreateService creates a new Service. -func (r *Registry) CreateService(svc api.Service) error { +func (r *Registry) CreateService(svc *api.Service) error { err := r.CreateObj(makeServiceKey(svc.ID), svc) if tools.IsEtcdNodeExist(err) { return errors.NewAlreadyExists("service", svc.ID) @@ -352,8 +352,8 @@ func (r *Registry) DeleteService(name string) error { } // UpdateService replaces an existing Service. -func (r *Registry) UpdateService(svc api.Service) error { - return r.SetObj(makeServiceKey(svc.ID), &svc) +func (r *Registry) UpdateService(svc *api.Service) error { + return r.SetObj(makeServiceKey(svc.ID), svc) } // WatchServices begins watching for new, changed, or deleted service configurations. @@ -378,10 +378,10 @@ func (r *Registry) ListEndpoints() (*api.EndpointsList, error) { } // UpdateEndpoints update Endpoints of a Service. -func (r *Registry) UpdateEndpoints(e api.Endpoints) error { +func (r *Registry) UpdateEndpoints(e *api.Endpoints) error { // TODO: this is a really bad misuse of AtomicUpdate, need to compute a diff inside the loop. return r.AtomicUpdate(makeServiceEndpointsKey(e.ID), &api.Endpoints{}, - func(input interface{}) (interface{}, error) { + func(input runtime.Object) (runtime.Object, error) { // TODO: racy - label query is returning different results for two simultaneous updaters return e, nil }) diff --git a/pkg/registry/etcd/etcd_test.go b/pkg/registry/etcd/etcd_test.go index cae9740b6c3..1f61ea085fc 100644 --- a/pkg/registry/etcd/etcd_test.go +++ b/pkg/registry/etcd/etcd_test.go @@ -41,7 +41,7 @@ func NewTestEtcdRegistry(client tools.EtcdClient) *Registry { func TestEtcdGetPod(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Set("/registry/pods/foo", runtime.EncodeOrDie(api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0) + fakeClient.Set("/registry/pods/foo", runtime.DefaultScheme.EncodeOrDie(&api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0) registry := NewTestEtcdRegistry(fakeClient) pod, err := registry.GetPod("foo") if err != nil { @@ -77,9 +77,9 @@ func TestEtcdCreatePod(t *testing.T) { }, E: tools.EtcdErrorNotFound, } - fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(&api.ContainerManifestList{}), 0) + fakeClient.Set("/registry/hosts/machine/kubelet", runtime.DefaultScheme.EncodeOrDie(&api.ContainerManifestList{}), 0) registry := NewTestEtcdRegistry(fakeClient) - err := registry.CreatePod(api.Pod{ + err := registry.CreatePod(&api.Pod{ JSONBase: api.JSONBase{ ID: "foo", }, @@ -108,7 +108,7 @@ func TestEtcdCreatePod(t *testing.T) { t.Fatalf("Unexpected error %v", err) } var pod api.Pod - err = runtime.DecodeInto([]byte(resp.Node.Value), &pod) + err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &pod) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -122,7 +122,7 @@ func TestEtcdCreatePod(t *testing.T) { t.Errorf("unexpected error: %v", err) } - err = runtime.DecodeInto([]byte(resp.Node.Value), &manifests) + err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &manifests) if len(manifests.Items) != 1 || manifests.Items[0].ID != "foo" { t.Errorf("Unexpected manifest list: %#v", manifests) } @@ -133,13 +133,13 @@ func TestEtcdCreatePodAlreadyExisting(t *testing.T) { fakeClient.Data["/registry/pods/foo"] = tools.EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ - Value: runtime.EncodeOrDie(api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), + Value: runtime.DefaultScheme.EncodeOrDie(&api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), }, }, E: nil, } registry := NewTestEtcdRegistry(fakeClient) - err := registry.CreatePod(api.Pod{ + err := registry.CreatePod(&api.Pod{ JSONBase: api.JSONBase{ ID: "foo", }, @@ -165,7 +165,7 @@ func TestEtcdCreatePodWithContainersError(t *testing.T) { E: tools.EtcdErrorValueRequired, } registry := NewTestEtcdRegistry(fakeClient) - err := registry.CreatePod(api.Pod{ + err := registry.CreatePod(&api.Pod{ JSONBase: api.JSONBase{ ID: "foo", }, @@ -205,7 +205,7 @@ func TestEtcdCreatePodWithContainersNotFound(t *testing.T) { E: tools.EtcdErrorNotFound, } registry := NewTestEtcdRegistry(fakeClient) - err := registry.CreatePod(api.Pod{ + err := registry.CreatePod(&api.Pod{ JSONBase: api.JSONBase{ ID: "foo", }, @@ -235,7 +235,7 @@ func TestEtcdCreatePodWithContainersNotFound(t *testing.T) { t.Fatalf("Unexpected error %v", err) } var pod api.Pod - err = runtime.DecodeInto([]byte(resp.Node.Value), &pod) + err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &pod) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -249,7 +249,7 @@ func TestEtcdCreatePodWithContainersNotFound(t *testing.T) { t.Errorf("unexpected error: %v", err) } - err = runtime.DecodeInto([]byte(resp.Node.Value), &manifests) + err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &manifests) if len(manifests.Items) != 1 || manifests.Items[0].ID != "foo" { t.Errorf("Unexpected manifest list: %#v", manifests) } @@ -264,13 +264,13 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) { }, E: tools.EtcdErrorNotFound, } - fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(api.ContainerManifestList{ + fakeClient.Set("/registry/hosts/machine/kubelet", runtime.DefaultScheme.EncodeOrDie(&api.ContainerManifestList{ Items: []api.ContainerManifest{ {ID: "bar"}, }, }), 0) registry := NewTestEtcdRegistry(fakeClient) - err := registry.CreatePod(api.Pod{ + err := registry.CreatePod(&api.Pod{ JSONBase: api.JSONBase{ ID: "foo", }, @@ -300,7 +300,7 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) { t.Fatalf("Unexpected error %v", err) } var pod api.Pod - err = runtime.DecodeInto([]byte(resp.Node.Value), &pod) + err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &pod) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -314,7 +314,7 @@ func TestEtcdCreatePodWithExistingContainers(t *testing.T) { t.Errorf("unexpected error: %v", err) } - err = runtime.DecodeInto([]byte(resp.Node.Value), &manifests) + err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &manifests) if len(manifests.Items) != 2 || manifests.Items[1].ID != "foo" { t.Errorf("Unexpected manifest list: %#v", manifests) } @@ -325,11 +325,11 @@ func TestEtcdDeletePod(t *testing.T) { fakeClient.TestIndex = true key := "/registry/pods/foo" - fakeClient.Set(key, runtime.EncodeOrDie(api.Pod{ + fakeClient.Set(key, runtime.DefaultScheme.EncodeOrDie(&api.Pod{ JSONBase: api.JSONBase{ID: "foo"}, DesiredState: api.PodState{Host: "machine"}, }), 0) - fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(&api.ContainerManifestList{ + fakeClient.Set("/registry/hosts/machine/kubelet", runtime.DefaultScheme.EncodeOrDie(&api.ContainerManifestList{ Items: []api.ContainerManifest{ {ID: "foo"}, }, @@ -350,7 +350,7 @@ func TestEtcdDeletePod(t *testing.T) { t.Fatalf("Unexpected error %v", err) } var manifests api.ContainerManifestList - runtime.DecodeInto([]byte(response.Node.Value), &manifests) + runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &manifests) if len(manifests.Items) != 0 { t.Errorf("Unexpected container set: %s, expected empty", response.Node.Value) } @@ -361,11 +361,11 @@ func TestEtcdDeletePodMultipleContainers(t *testing.T) { fakeClient.TestIndex = true key := "/registry/pods/foo" - fakeClient.Set(key, runtime.EncodeOrDie(api.Pod{ + fakeClient.Set(key, runtime.DefaultScheme.EncodeOrDie(&api.Pod{ JSONBase: api.JSONBase{ID: "foo"}, DesiredState: api.PodState{Host: "machine"}, }), 0) - fakeClient.Set("/registry/hosts/machine/kubelet", runtime.EncodeOrDie(&api.ContainerManifestList{ + fakeClient.Set("/registry/hosts/machine/kubelet", runtime.DefaultScheme.EncodeOrDie(&api.ContainerManifestList{ Items: []api.ContainerManifest{ {ID: "foo"}, {ID: "bar"}, @@ -388,7 +388,7 @@ func TestEtcdDeletePodMultipleContainers(t *testing.T) { t.Fatalf("Unexpected error %v", err) } var manifests api.ContainerManifestList - runtime.DecodeInto([]byte(response.Node.Value), &manifests) + runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &manifests) if len(manifests.Items) != 1 { t.Fatalf("Unexpected manifest set: %#v, expected empty", manifests) } @@ -445,13 +445,13 @@ func TestEtcdListPods(t *testing.T) { Node: &etcd.Node{ Nodes: []*etcd.Node{ { - Value: runtime.EncodeOrDie(api.Pod{ + Value: runtime.DefaultScheme.EncodeOrDie(&api.Pod{ JSONBase: api.JSONBase{ID: "foo"}, DesiredState: api.PodState{Host: "machine"}, }), }, { - Value: runtime.EncodeOrDie(api.Pod{ + Value: runtime.DefaultScheme.EncodeOrDie(&api.Pod{ JSONBase: api.JSONBase{ID: "bar"}, DesiredState: api.PodState{Host: "machine"}, }), @@ -520,10 +520,10 @@ func TestEtcdListControllers(t *testing.T) { Node: &etcd.Node{ Nodes: []*etcd.Node{ { - Value: runtime.EncodeOrDie(api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), + Value: runtime.DefaultScheme.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), }, { - Value: runtime.EncodeOrDie(api.ReplicationController{JSONBase: api.JSONBase{ID: "bar"}}), + Value: runtime.DefaultScheme.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "bar"}}), }, }, }, @@ -543,7 +543,7 @@ func TestEtcdListControllers(t *testing.T) { func TestEtcdGetController(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Set("/registry/controllers/foo", runtime.EncodeOrDie(api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0) + fakeClient.Set("/registry/controllers/foo", runtime.DefaultScheme.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0) registry := NewTestEtcdRegistry(fakeClient) ctrl, err := registry.GetController("foo") if err != nil { @@ -593,7 +593,7 @@ func TestEtcdDeleteController(t *testing.T) { func TestEtcdCreateController(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) registry := NewTestEtcdRegistry(fakeClient) - err := registry.CreateController(api.ReplicationController{ + err := registry.CreateController(&api.ReplicationController{ JSONBase: api.JSONBase{ ID: "foo", }, @@ -607,7 +607,7 @@ func TestEtcdCreateController(t *testing.T) { t.Fatalf("Unexpected error %v", err) } var ctrl api.ReplicationController - err = runtime.DecodeInto([]byte(resp.Node.Value), &ctrl) + err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &ctrl) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -619,10 +619,10 @@ func TestEtcdCreateController(t *testing.T) { func TestEtcdCreateControllerAlreadyExisting(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Set("/registry/controllers/foo", runtime.EncodeOrDie(api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0) + fakeClient.Set("/registry/controllers/foo", runtime.DefaultScheme.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0) registry := NewTestEtcdRegistry(fakeClient) - err := registry.CreateController(api.ReplicationController{ + err := registry.CreateController(&api.ReplicationController{ JSONBase: api.JSONBase{ ID: "foo", }, @@ -636,9 +636,9 @@ func TestEtcdUpdateController(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) fakeClient.TestIndex = true - resp, _ := fakeClient.Set("/registry/controllers/foo", runtime.EncodeOrDie(api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0) + resp, _ := fakeClient.Set("/registry/controllers/foo", runtime.DefaultScheme.EncodeOrDie(&api.ReplicationController{JSONBase: api.JSONBase{ID: "foo"}}), 0) registry := NewTestEtcdRegistry(fakeClient) - err := registry.UpdateController(api.ReplicationController{ + err := registry.UpdateController(&api.ReplicationController{ JSONBase: api.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex}, DesiredState: api.ReplicationControllerState{ Replicas: 2, @@ -662,10 +662,10 @@ func TestEtcdListServices(t *testing.T) { Node: &etcd.Node{ Nodes: []*etcd.Node{ { - Value: runtime.EncodeOrDie(api.Service{JSONBase: api.JSONBase{ID: "foo"}}), + Value: runtime.DefaultScheme.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "foo"}}), }, { - Value: runtime.EncodeOrDie(api.Service{JSONBase: api.JSONBase{ID: "bar"}}), + Value: runtime.DefaultScheme.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "bar"}}), }, }, }, @@ -686,7 +686,7 @@ func TestEtcdListServices(t *testing.T) { func TestEtcdCreateService(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) registry := NewTestEtcdRegistry(fakeClient) - err := registry.CreateService(api.Service{ + err := registry.CreateService(&api.Service{ JSONBase: api.JSONBase{ID: "foo"}, }) if err != nil { @@ -699,7 +699,7 @@ func TestEtcdCreateService(t *testing.T) { } var service api.Service - err = runtime.DecodeInto([]byte(resp.Node.Value), &service) + err = runtime.DefaultCodec.DecodeInto([]byte(resp.Node.Value), &service) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -711,9 +711,9 @@ func TestEtcdCreateService(t *testing.T) { func TestEtcdCreateServiceAlreadyExisting(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Set("/registry/services/specs/foo", runtime.EncodeOrDie(api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) + fakeClient.Set("/registry/services/specs/foo", runtime.DefaultScheme.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) registry := NewTestEtcdRegistry(fakeClient) - err := registry.CreateService(api.Service{ + err := registry.CreateService(&api.Service{ JSONBase: api.JSONBase{ID: "foo"}, }) if !errors.IsAlreadyExists(err) { @@ -723,7 +723,7 @@ func TestEtcdCreateServiceAlreadyExisting(t *testing.T) { func TestEtcdGetService(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) - fakeClient.Set("/registry/services/specs/foo", runtime.EncodeOrDie(api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) + fakeClient.Set("/registry/services/specs/foo", runtime.DefaultScheme.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) registry := NewTestEtcdRegistry(fakeClient) service, err := registry.GetService("foo") if err != nil { @@ -775,7 +775,7 @@ func TestEtcdUpdateService(t *testing.T) { fakeClient := tools.NewFakeEtcdClient(t) fakeClient.TestIndex = true - resp, _ := fakeClient.Set("/registry/services/specs/foo", runtime.EncodeOrDie(api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) + resp, _ := fakeClient.Set("/registry/services/specs/foo", runtime.DefaultScheme.EncodeOrDie(&api.Service{JSONBase: api.JSONBase{ID: "foo"}}), 0) registry := NewTestEtcdRegistry(fakeClient) testService := api.Service{ JSONBase: api.JSONBase{ID: "foo", ResourceVersion: resp.Node.ModifiedIndex}, @@ -786,7 +786,7 @@ func TestEtcdUpdateService(t *testing.T) { "baz": "bar", }, } - err := registry.UpdateService(testService) + err := registry.UpdateService(&testService) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -812,10 +812,10 @@ func TestEtcdListEndpoints(t *testing.T) { Node: &etcd.Node{ Nodes: []*etcd.Node{ { - Value: runtime.EncodeOrDie(api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:8345"}}), + Value: runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:8345"}}), }, { - Value: runtime.EncodeOrDie(api.Endpoints{JSONBase: api.JSONBase{ID: "bar"}}), + Value: runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "bar"}}), }, }, }, @@ -841,7 +841,7 @@ func TestEtcdGetEndpoints(t *testing.T) { Endpoints: []string{"127.0.0.1:34855"}, } - fakeClient.Set("/registry/services/endpoints/foo", runtime.EncodeOrDie(endpoints), 0) + fakeClient.Set("/registry/services/endpoints/foo", runtime.DefaultScheme.EncodeOrDie(endpoints), 0) got, err := registry.GetEndpoints("foo") if err != nil { @@ -862,9 +862,9 @@ func TestEtcdUpdateEndpoints(t *testing.T) { Endpoints: []string{"baz", "bar"}, } - fakeClient.Set("/registry/services/endpoints/foo", runtime.EncodeOrDie(api.Endpoints{}), 0) + fakeClient.Set("/registry/services/endpoints/foo", runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{}), 0) - err := registry.UpdateEndpoints(endpoints) + err := registry.UpdateEndpoints(&endpoints) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -874,7 +874,7 @@ func TestEtcdUpdateEndpoints(t *testing.T) { t.Fatalf("Unexpected error %v", err) } var endpointsOut api.Endpoints - err = runtime.DecodeInto([]byte(response.Node.Value), &endpointsOut) + err = runtime.DefaultCodec.DecodeInto([]byte(response.Node.Value), &endpointsOut) if !reflect.DeepEqual(endpoints, endpointsOut) { t.Errorf("Unexpected endpoints: %#v, expected %#v", endpointsOut, endpoints) } diff --git a/pkg/registry/minion/storage.go b/pkg/registry/minion/storage.go index 27580983aa3..c7da335632f 100644 --- a/pkg/registry/minion/storage.go +++ b/pkg/registry/minion/storage.go @@ -22,6 +22,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -37,7 +38,7 @@ func NewRegistryStorage(m Registry) apiserver.RESTStorage { } } -func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { +func (rs *RegistryStorage) Create(obj runtime.Object) (<-chan runtime.Object, error) { minion, ok := obj.(*api.Minion) if !ok { return nil, fmt.Errorf("not a minion: %#v", obj) @@ -48,7 +49,7 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { minion.CreationTimestamp = util.Now() - return apiserver.MakeAsync(func() (interface{}, error) { + return apiserver.MakeAsync(func() (runtime.Object, error) { err := rs.registry.Insert(minion.ID) if err != nil { return nil, err @@ -64,7 +65,7 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { }), nil } -func (rs *RegistryStorage) Delete(id string) (<-chan interface{}, error) { +func (rs *RegistryStorage) Delete(id string) (<-chan runtime.Object, error) { exists, err := rs.registry.Contains(id) if !exists { return nil, ErrDoesNotExist @@ -72,12 +73,12 @@ func (rs *RegistryStorage) Delete(id string) (<-chan interface{}, error) { if err != nil { return nil, err } - return apiserver.MakeAsync(func() (interface{}, error) { + return apiserver.MakeAsync(func() (runtime.Object, error) { return &api.Status{Status: api.StatusSuccess}, rs.registry.Delete(id) }), nil } -func (rs *RegistryStorage) Get(id string) (interface{}, error) { +func (rs *RegistryStorage) Get(id string) (runtime.Object, error) { exists, err := rs.registry.Contains(id) if !exists { return nil, ErrDoesNotExist @@ -85,26 +86,26 @@ func (rs *RegistryStorage) Get(id string) (interface{}, error) { return rs.toApiMinion(id), err } -func (rs *RegistryStorage) List(selector labels.Selector) (interface{}, error) { +func (rs *RegistryStorage) List(selector labels.Selector) (runtime.Object, error) { nameList, err := rs.registry.List() if err != nil { return nil, err } var list api.MinionList for _, name := range nameList { - list.Items = append(list.Items, rs.toApiMinion(name)) + list.Items = append(list.Items, *rs.toApiMinion(name)) } - return list, nil + return &list, nil } -func (rs RegistryStorage) New() interface{} { +func (rs RegistryStorage) New() runtime.Object { return &api.Minion{} } -func (rs *RegistryStorage) Update(minion interface{}) (<-chan interface{}, error) { +func (rs *RegistryStorage) Update(minion runtime.Object) (<-chan runtime.Object, error) { return nil, fmt.Errorf("Minions can only be created (inserted) and deleted.") } -func (rs *RegistryStorage) toApiMinion(name string) api.Minion { - return api.Minion{JSONBase: api.JSONBase{ID: name}} +func (rs *RegistryStorage) toApiMinion(name string) *api.Minion { + return &api.Minion{JSONBase: api.JSONBase{ID: name}} } diff --git a/pkg/registry/minion/storage_test.go b/pkg/registry/minion/storage_test.go index 96d81595cd5..c6a805c45d6 100644 --- a/pkg/registry/minion/storage_test.go +++ b/pkg/registry/minion/storage_test.go @@ -28,10 +28,10 @@ func TestMinionRegistryStorage(t *testing.T) { m := NewRegistry([]string{"foo", "bar"}) ms := NewRegistryStorage(m) - if obj, err := ms.Get("foo"); err != nil || obj.(api.Minion).ID != "foo" { + if obj, err := ms.Get("foo"); err != nil || obj.(*api.Minion).ID != "foo" { t.Errorf("missing expected object") } - if obj, err := ms.Get("bar"); err != nil || obj.(api.Minion).ID != "bar" { + if obj, err := ms.Get("bar"); err != nil || obj.(*api.Minion).ID != "bar" { t.Errorf("missing expected object") } if _, err := ms.Get("baz"); err != ErrDoesNotExist { @@ -43,10 +43,10 @@ func TestMinionRegistryStorage(t *testing.T) { t.Errorf("insert failed") } obj := <-c - if m, ok := obj.(api.Minion); !ok || m.ID != "baz" { + if m, ok := obj.(*api.Minion); !ok || m.ID != "baz" { t.Errorf("insert return value was weird: %#v", obj) } - if obj, err := ms.Get("baz"); err != nil || obj.(api.Minion).ID != "baz" { + if obj, err := ms.Get("baz"); err != nil || obj.(*api.Minion).ID != "baz" { t.Errorf("insert didn't actually insert") } @@ -78,7 +78,7 @@ func TestMinionRegistryStorage(t *testing.T) { JSONBase: api.JSONBase{ID: "foo"}, }, } - if !reflect.DeepEqual(list.(api.MinionList).Items, expect) { + if !reflect.DeepEqual(list.(*api.MinionList).Items, expect) { t.Errorf("Unexpected list value: %#v", list) } } diff --git a/pkg/registry/pod/registry.go b/pkg/registry/pod/registry.go index af8fd91db47..ef12477e060 100644 --- a/pkg/registry/pod/registry.go +++ b/pkg/registry/pod/registry.go @@ -31,9 +31,9 @@ type Registry interface { // Get a specific pod GetPod(podID string) (*api.Pod, error) // Create a pod based on a specification. - CreatePod(pod api.Pod) error + CreatePod(pod *api.Pod) error // Update an existing pod - UpdatePod(pod api.Pod) error + UpdatePod(pod *api.Pod) error // Delete an existing pod DeletePod(podID string) error } diff --git a/pkg/registry/pod/storage.go b/pkg/registry/pod/storage.go index 730351067f4..3dcd0aebf29 100644 --- a/pkg/registry/pod/storage.go +++ b/pkg/registry/pod/storage.go @@ -29,6 +29,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" @@ -64,7 +65,7 @@ func NewRegistryStorage(config *RegistryStorageConfig) apiserver.RESTStorage { } } -func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { +func (rs *RegistryStorage) Create(obj runtime.Object) (<-chan runtime.Object, error) { pod := obj.(*api.Pod) if len(pod.ID) == 0 { pod.ID = uuid.NewUUID().String() @@ -76,21 +77,21 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { pod.CreationTimestamp = util.Now() - return apiserver.MakeAsync(func() (interface{}, error) { - if err := rs.registry.CreatePod(*pod); err != nil { + return apiserver.MakeAsync(func() (runtime.Object, error) { + if err := rs.registry.CreatePod(pod); err != nil { return nil, err } return rs.registry.GetPod(pod.ID) }), nil } -func (rs *RegistryStorage) Delete(id string) (<-chan interface{}, error) { - return apiserver.MakeAsync(func() (interface{}, error) { +func (rs *RegistryStorage) Delete(id string) (<-chan runtime.Object, error) { + return apiserver.MakeAsync(func() (runtime.Object, error) { return &api.Status{Status: api.StatusSuccess}, rs.registry.DeletePod(id) }), nil } -func (rs *RegistryStorage) Get(id string) (interface{}, error) { +func (rs *RegistryStorage) Get(id string) (runtime.Object, error) { pod, err := rs.registry.GetPod(id) if err != nil { return pod, err @@ -106,7 +107,7 @@ func (rs *RegistryStorage) Get(id string) (interface{}, error) { return pod, err } -func (rs *RegistryStorage) List(selector labels.Selector) (interface{}, error) { +func (rs *RegistryStorage) List(selector labels.Selector) (runtime.Object, error) { pods, err := rs.registry.ListPods(selector) if err == nil { for i := range pods.Items { @@ -131,17 +132,17 @@ func (rs *RegistryStorage) Watch(label, field labels.Selector, resourceVersion u }) } -func (rs RegistryStorage) New() interface{} { +func (rs RegistryStorage) New() runtime.Object { return &api.Pod{} } -func (rs *RegistryStorage) Update(obj interface{}) (<-chan interface{}, error) { +func (rs *RegistryStorage) Update(obj runtime.Object) (<-chan runtime.Object, error) { pod := obj.(*api.Pod) if errs := validation.ValidatePod(pod); len(errs) > 0 { return nil, errors.NewInvalid("pod", pod.ID, errs) } - return apiserver.MakeAsync(func() (interface{}, error) { - if err := rs.registry.UpdatePod(*pod); err != nil { + return apiserver.MakeAsync(func() (runtime.Object, error) { + if err := rs.registry.UpdatePod(pod); err != nil { return nil, err } return rs.registry.GetPod(pod.ID) @@ -235,7 +236,7 @@ func getPodStatus(pod *api.Pod) api.PodStatus { } } -func (rs *RegistryStorage) waitForPodRunning(pod api.Pod) (interface{}, error) { +func (rs *RegistryStorage) waitForPodRunning(pod *api.Pod) (runtime.Object, error) { for { podObj, err := rs.Get(pod.ID) if err != nil || podObj == nil { diff --git a/pkg/registry/pod/storage_test.go b/pkg/registry/pod/storage_test.go index 424333bb0a1..37bb8dab7ce 100644 --- a/pkg/registry/pod/storage_test.go +++ b/pkg/registry/pod/storage_test.go @@ -32,7 +32,7 @@ import ( "github.com/fsouza/go-dockerclient" ) -func expectApiStatusError(t *testing.T, ch <-chan interface{}, msg string) { +func expectApiStatusError(t *testing.T, ch <-chan runtime.Object, msg string) { out := <-ch status, ok := out.(*api.Status) if !ok { @@ -44,7 +44,7 @@ func expectApiStatusError(t *testing.T, ch <-chan interface{}, msg string) { } } -func expectPod(t *testing.T, ch <-chan interface{}) (*api.Pod, bool) { +func expectPod(t *testing.T, ch <-chan runtime.Object) (*api.Pod, bool) { out := <-ch pod, ok := out.(*api.Pod) if !ok || pod == nil { @@ -178,13 +178,13 @@ func TestPodDecode(t *testing.T) { ID: "foo", }, } - body, err := runtime.Encode(expected) + body, err := runtime.DefaultCodec.Encode(expected) if err != nil { t.Errorf("unexpected error: %v", err) } actual := storage.New() - if err := runtime.DecodeInto(body, actual); err != nil { + if err := runtime.DefaultCodec.DecodeInto(body, actual); err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/pkg/registry/registrytest/controller.go b/pkg/registry/registrytest/controller.go index 76c2f26a970..bfec704eb7b 100644 --- a/pkg/registry/registrytest/controller.go +++ b/pkg/registry/registrytest/controller.go @@ -35,11 +35,11 @@ func (r *ControllerRegistry) GetController(ID string) (*api.ReplicationControlle return &api.ReplicationController{}, r.Err } -func (r *ControllerRegistry) CreateController(controller api.ReplicationController) error { +func (r *ControllerRegistry) CreateController(controller *api.ReplicationController) error { return r.Err } -func (r *ControllerRegistry) UpdateController(controller api.ReplicationController) error { +func (r *ControllerRegistry) UpdateController(controller *api.ReplicationController) error { return r.Err } diff --git a/pkg/registry/registrytest/pod.go b/pkg/registry/registrytest/pod.go index 60aa66976e7..27f33ff74c6 100644 --- a/pkg/registry/registrytest/pod.go +++ b/pkg/registry/registrytest/pod.go @@ -68,19 +68,19 @@ func (r *PodRegistry) GetPod(podId string) (*api.Pod, error) { return r.Pod, r.Err } -func (r *PodRegistry) CreatePod(pod api.Pod) error { +func (r *PodRegistry) CreatePod(pod *api.Pod) error { r.Lock() defer r.Unlock() - r.Pod = &pod - r.mux.Action(watch.Added, &pod) + r.Pod = pod + r.mux.Action(watch.Added, pod) return r.Err } -func (r *PodRegistry) UpdatePod(pod api.Pod) error { +func (r *PodRegistry) UpdatePod(pod *api.Pod) error { r.Lock() defer r.Unlock() - r.Pod = &pod - r.mux.Action(watch.Modified, &pod) + r.Pod = pod + r.mux.Action(watch.Modified, pod) return r.Err } diff --git a/pkg/registry/registrytest/service.go b/pkg/registry/registrytest/service.go index d53fc9b62f7..a61ab385f3f 100644 --- a/pkg/registry/registrytest/service.go +++ b/pkg/registry/registrytest/service.go @@ -42,9 +42,9 @@ func (r *ServiceRegistry) ListServices() (*api.ServiceList, error) { return &r.List, r.Err } -func (r *ServiceRegistry) CreateService(svc api.Service) error { - r.Service = &svc - r.List.Items = append(r.List.Items, svc) +func (r *ServiceRegistry) CreateService(svc *api.Service) error { + r.Service = svc + r.List.Items = append(r.List.Items, *svc) return r.Err } @@ -58,7 +58,7 @@ func (r *ServiceRegistry) DeleteService(id string) error { return r.Err } -func (r *ServiceRegistry) UpdateService(svc api.Service) error { +func (r *ServiceRegistry) UpdateService(svc *api.Service) error { r.UpdatedID = svc.ID return r.Err } @@ -76,8 +76,8 @@ func (r *ServiceRegistry) GetEndpoints(id string) (*api.Endpoints, error) { return &r.Endpoints, r.Err } -func (r *ServiceRegistry) UpdateEndpoints(e api.Endpoints) error { - r.Endpoints = e +func (r *ServiceRegistry) UpdateEndpoints(e *api.Endpoints) error { + r.Endpoints = *e return r.Err } diff --git a/pkg/registry/service/registry.go b/pkg/registry/service/registry.go index 26af6077ff5..1176725452b 100644 --- a/pkg/registry/service/registry.go +++ b/pkg/registry/service/registry.go @@ -26,10 +26,10 @@ import ( // Registry is an interface for things that know how to store services. type Registry interface { ListServices() (*api.ServiceList, error) - CreateService(svc api.Service) error + CreateService(svc *api.Service) error GetService(name string) (*api.Service, error) DeleteService(name string) error - UpdateService(svc api.Service) error + UpdateService(svc *api.Service) error WatchServices(labels, fields labels.Selector, resourceVersion uint64) (watch.Interface, error) // TODO: endpoints and their implementation should be separated, setting endpoints should be diff --git a/pkg/registry/service/storage.go b/pkg/registry/service/storage.go index 44da947b49c..579104ff2f3 100644 --- a/pkg/registry/service/storage.go +++ b/pkg/registry/service/storage.go @@ -29,6 +29,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/minion" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" ) @@ -49,7 +50,7 @@ func NewRegistryStorage(registry Registry, cloud cloudprovider.Interface, machin } } -func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { +func (rs *RegistryStorage) Create(obj runtime.Object) (<-chan runtime.Object, error) { srv := obj.(*api.Service) if errs := validation.ValidateService(srv); len(errs) > 0 { return nil, errors.NewInvalid("service", srv.ID, errs) @@ -57,7 +58,7 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { srv.CreationTimestamp = util.Now() - return apiserver.MakeAsync(func() (interface{}, error) { + return apiserver.MakeAsync(func() (runtime.Object, error) { // TODO: Consider moving this to a rectification loop, so that we make/remove external load balancers // correctly no matter what http operations happen. if srv.CreateExternalLoadBalancer { @@ -85,7 +86,7 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { return nil, err } } - err := rs.registry.CreateService(*srv) + err := rs.registry.CreateService(srv) if err != nil { return nil, err } @@ -93,18 +94,18 @@ func (rs *RegistryStorage) Create(obj interface{}) (<-chan interface{}, error) { }), nil } -func (rs *RegistryStorage) Delete(id string) (<-chan interface{}, error) { +func (rs *RegistryStorage) Delete(id string) (<-chan runtime.Object, error) { service, err := rs.registry.GetService(id) if err != nil { return nil, err } - return apiserver.MakeAsync(func() (interface{}, error) { + return apiserver.MakeAsync(func() (runtime.Object, error) { rs.deleteExternalLoadBalancer(service) return &api.Status{Status: api.StatusSuccess}, rs.registry.DeleteService(id) }), nil } -func (rs *RegistryStorage) Get(id string) (interface{}, error) { +func (rs *RegistryStorage) Get(id string) (runtime.Object, error) { s, err := rs.registry.GetService(id) if err != nil { return nil, err @@ -112,7 +113,7 @@ func (rs *RegistryStorage) Get(id string) (interface{}, error) { return s, err } -func (rs *RegistryStorage) List(selector labels.Selector) (interface{}, error) { +func (rs *RegistryStorage) List(selector labels.Selector) (runtime.Object, error) { list, err := rs.registry.ListServices() if err != nil { return nil, err @@ -133,7 +134,7 @@ func (rs *RegistryStorage) Watch(label, field labels.Selector, resourceVersion u return rs.registry.WatchServices(label, field, resourceVersion) } -func (rs RegistryStorage) New() interface{} { +func (rs RegistryStorage) New() runtime.Object { return &api.Service{} } @@ -155,14 +156,14 @@ func GetServiceEnvironmentVariables(registry Registry, machine string) ([]api.En return result, nil } -func (rs *RegistryStorage) Update(obj interface{}) (<-chan interface{}, error) { +func (rs *RegistryStorage) Update(obj runtime.Object) (<-chan runtime.Object, error) { srv := obj.(*api.Service) if errs := validation.ValidateService(srv); len(errs) > 0 { return nil, errors.NewInvalid("service", srv.ID, errs) } - return apiserver.MakeAsync(func() (interface{}, error) { + return apiserver.MakeAsync(func() (runtime.Object, error) { // TODO: check to see if external load balancer status changed - err := rs.registry.UpdateService(*srv) + err := rs.registry.UpdateService(srv) if err != nil { return nil, err } diff --git a/pkg/registry/service/storage_test.go b/pkg/registry/service/storage_test.go index bddfc782cda..4cda3645b27 100644 --- a/pkg/registry/service/storage_test.go +++ b/pkg/registry/service/storage_test.go @@ -89,7 +89,7 @@ func TestServiceStorageValidatesCreate(t *testing.T) { func TestServiceRegistryUpdate(t *testing.T) { registry := registrytest.NewServiceRegistry() - registry.CreateService(api.Service{ + registry.CreateService(&api.Service{ Port: 6502, JSONBase: api.JSONBase{ID: "foo"}, Selector: map[string]string{"bar": "baz1"}, @@ -118,7 +118,7 @@ func TestServiceRegistryUpdate(t *testing.T) { func TestServiceStorageValidatesUpdate(t *testing.T) { registry := registrytest.NewServiceRegistry() - registry.CreateService(api.Service{ + registry.CreateService(&api.Service{ Port: 6502, JSONBase: api.JSONBase{ID: "foo"}, Selector: map[string]string{"bar": "baz"}, @@ -200,7 +200,7 @@ func TestServiceRegistryDelete(t *testing.T) { fakeCloud := &cloud.FakeCloud{} machines := []string{"foo", "bar", "baz"} storage := NewRegistryStorage(registry, fakeCloud, minion.NewRegistry(machines)) - svc := api.Service{ + svc := &api.Service{ JSONBase: api.JSONBase{ID: "foo"}, Selector: map[string]string{"bar": "baz"}, } @@ -220,7 +220,7 @@ func TestServiceRegistryDeleteExternal(t *testing.T) { fakeCloud := &cloud.FakeCloud{} machines := []string{"foo", "bar", "baz"} storage := NewRegistryStorage(registry, fakeCloud, minion.NewRegistry(machines)) - svc := api.Service{ + svc := &api.Service{ JSONBase: api.JSONBase{ID: "foo"}, Selector: map[string]string{"bar": "baz"}, CreateExternalLoadBalancer: true, @@ -263,7 +263,7 @@ func TestServiceRegistryGet(t *testing.T) { fakeCloud := &cloud.FakeCloud{} machines := []string{"foo", "bar", "baz"} storage := NewRegistryStorage(registry, fakeCloud, minion.NewRegistry(machines)) - registry.CreateService(api.Service{ + registry.CreateService(&api.Service{ JSONBase: api.JSONBase{ID: "foo"}, Selector: map[string]string{"bar": "baz"}, }) @@ -282,7 +282,7 @@ func TestServiceRegistryResourceLocation(t *testing.T) { fakeCloud := &cloud.FakeCloud{} machines := []string{"foo", "bar", "baz"} storage := NewRegistryStorage(registry, fakeCloud, minion.NewRegistry(machines)) - registry.CreateService(api.Service{ + registry.CreateService(&api.Service{ JSONBase: api.JSONBase{ID: "foo"}, Selector: map[string]string{"bar": "baz"}, }) @@ -310,11 +310,11 @@ func TestServiceRegistryList(t *testing.T) { fakeCloud := &cloud.FakeCloud{} machines := []string{"foo", "bar", "baz"} storage := NewRegistryStorage(registry, fakeCloud, minion.NewRegistry(machines)) - registry.CreateService(api.Service{ + registry.CreateService(&api.Service{ JSONBase: api.JSONBase{ID: "foo"}, Selector: map[string]string{"bar": "baz"}, }) - registry.CreateService(api.Service{ + registry.CreateService(&api.Service{ JSONBase: api.JSONBase{ID: "foo2"}, Selector: map[string]string{"bar2": "baz2"}, }) diff --git a/pkg/runtime/embedded.go b/pkg/runtime/embedded.go index 8189a120d1a..0d118e977f9 100644 --- a/pkg/runtime/embedded.go +++ b/pkg/runtime/embedded.go @@ -33,7 +33,7 @@ func (a *EmbeddedObject) UnmarshalJSON(b []byte) error { return nil } - obj, err := Decode(b) + obj, err := DefaultCodec.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 DefaultCodec.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 := DefaultCodec.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 := DefaultCodec.Encode(a.Object) if err != nil { panic("impossible to encode API object!") } diff --git a/pkg/runtime/embedded_test.go b/pkg/runtime/embedded_test.go index d9e23780f80..66d4f39c377 100644 --- a/pkg/runtime/embedded_test.go +++ b/pkg/runtime/embedded_test.go @@ -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) } diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go index ac195cd8c2c..b6e944d98c7 100644 --- a/pkg/runtime/helper.go +++ b/pkg/runtime/helper.go @@ -24,41 +24,44 @@ import ( "gopkg.in/v1/yaml" ) -// 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 +var DefaultResourceVersioner ResourceVersioner = NewJSONBaseResourceVersioner() +var DefaultScheme = NewScheme("", "v1beta1") +var DefaultCodec Codec = DefaultScheme + +// 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 } -// 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) -} - -var ResourceVersioner resourceVersioner = NewJSONBaseResourceVersioner() -var conversionScheme = conversion.NewScheme() -var Codec codec = conversionScheme - -func init() { - conversionScheme.InternalVersion = "" - conversionScheme.ExternalVersion = "v1beta1" - conversionScheme.MetaInsertionFactory = metaInsertion{} +// 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 +76,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 +111,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 +149,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 +184,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 +197,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) } diff --git a/pkg/runtime/helper_test.go b/pkg/runtime/helper_test.go index b2bcc9f6bf4..86e368fc58d 100644 --- a/pkg/runtime/helper_test.go +++ b/pkg/runtime/helper_test.go @@ -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"}`) diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go new file mode 100644 index 00000000000..9a49cdade8d --- /dev/null +++ b/pkg/runtime/interfaces.go @@ -0,0 +1,40 @@ +/* +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 + +// Codec defines methods for serializing and deserializing API objects. +type Codec interface { + 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 Object, version uint64) error + ResourceVersion(obj Object) (uint64, error) +} + +// 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() +} diff --git a/pkg/runtime/jsonbase.go b/pkg/runtime/jsonbase.go index bcd6fe0a50d..f6f7c77fe75 100644 --- a/pkg/runtime/jsonbase.go +++ b/pkg/runtime/jsonbase.go @@ -23,13 +23,13 @@ import ( // NewJSONBaseResourceVersioner returns a resourceVersioner that can set or // retrieve ResourceVersion on objects derived from JSONBase. -func NewJSONBaseResourceVersioner() resourceVersioner { +func NewJSONBaseResourceVersioner() ResourceVersioner { return &jsonBaseResourceVersioner{} } 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 diff --git a/pkg/runtime/jsonbase_test.go b/pkg/runtime/jsonbase_test.go index 759cd7611b2..0e0165aacb1 100644 --- a/pkg/runtime/jsonbase_test.go +++ b/pkg/runtime/jsonbase_test.go @@ -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) - } - } } diff --git a/pkg/runtime/types.go b/pkg/runtime/types.go index eb240d1fca5..17148733950 100644 --- a/pkg/runtime/types.go +++ b/pkg/runtime/types.go @@ -29,8 +29,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 +44,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 +61,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() {} diff --git a/pkg/service/endpoints_controller.go b/pkg/service/endpoints_controller.go index d7b08f51260..940c6d42a0e 100644 --- a/pkg/service/endpoints_controller.go +++ b/pkg/service/endpoints_controller.go @@ -73,7 +73,7 @@ func (e *EndpointController) SyncServiceEndpoints() error { endpoints[ix] = net.JoinHostPort(pod.CurrentState.PodIP, strconv.Itoa(port)) } // TODO: this is totally broken, we need to compute this and store inside an AtomicUpdate loop. - err = e.serviceRegistry.UpdateEndpoints(api.Endpoints{ + err = e.serviceRegistry.UpdateEndpoints(&api.Endpoints{ JSONBase: api.JSONBase{ID: service.ID}, Endpoints: endpoints, }) diff --git a/pkg/tools/decoder.go b/pkg/tools/decoder.go index adc1661d702..0c1778a23cf 100644 --- a/pkg/tools/decoder.go +++ b/pkg/tools/decoder.go @@ -22,6 +22,7 @@ import ( "io" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" ) @@ -42,7 +43,7 @@ func NewAPIEventDecoder(stream io.ReadCloser) *APIEventDecoder { // Decode blocks until it can return the next object in the stream. Returns an error // if the stream is closed or an object can't be decoded. -func (d *APIEventDecoder) Decode() (action watch.EventType, object interface{}, err error) { +func (d *APIEventDecoder) Decode() (action watch.EventType, object runtime.Object, err error) { var got api.WatchEvent err = d.decoder.Decode(&got) if err != nil { diff --git a/pkg/tools/etcd_tools.go b/pkg/tools/etcd_tools.go index 0e27458004f..7432a49fad4 100644 --- a/pkg/tools/etcd_tools.go +++ b/pkg/tools/etcd_tools.go @@ -21,6 +21,7 @@ import ( "fmt" "reflect" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/coreos/go-etcd/etcd" ) @@ -38,19 +39,6 @@ var ( EtcdErrorValueRequired = &etcd.EtcdError{ErrorCode: EtcdErrorCodeValueRequired} ) -// Codec provides methods for transforming Etcd values into objects and back. -type Codec interface { - Encode(obj interface{}) (data []byte, err error) - Decode(data []byte) (interface{}, error) - DecodeInto(data []byte, obj interface{}) error -} - -// ResourceVersioner provides methods for managing object modification tracking. -type ResourceVersioner interface { - SetResourceVersion(obj interface{}, version uint64) error - ResourceVersion(obj interface{}) (uint64, error) -} - // EtcdClient is an injectable interface for testing. type EtcdClient interface { AddChild(key, data string, ttl uint64) (*etcd.Response, error) @@ -77,9 +65,9 @@ type EtcdGetSet interface { // EtcdHelper offers common object marshalling/unmarshalling operations on an etcd client. type EtcdHelper struct { Client EtcdGetSet - Codec Codec + Codec runtime.Codec // optional, no atomic operations can be performed without this interface - ResourceVersioner ResourceVersioner + ResourceVersioner runtime.ResourceVersioner } // IsEtcdNotFound returns true iff err is an etcd not found error. @@ -151,9 +139,9 @@ func (h *EtcdHelper) ExtractList(key string, slicePtr interface{}, resourceVersi v := pv.Elem() for _, node := range nodes { obj := reflect.New(v.Type().Elem()) - err = h.Codec.DecodeInto([]byte(node.Value), obj.Interface()) + err = h.Codec.DecodeInto([]byte(node.Value), obj.Interface().(runtime.Object)) if h.ResourceVersioner != nil { - _ = h.ResourceVersioner.SetResourceVersion(obj.Interface(), node.ModifiedIndex) + _ = h.ResourceVersioner.SetResourceVersion(obj.Interface().(runtime.Object), node.ModifiedIndex) // being unable to set the version does not prevent the object from being extracted } if err != nil { @@ -167,12 +155,12 @@ func (h *EtcdHelper) ExtractList(key string, slicePtr interface{}, resourceVersi // ExtractObj unmarshals json found at key into objPtr. On a not found error, will either return // a zero object of the requested type, or an error, depending on ignoreNotFound. Treats // empty responses and nil response nodes exactly like a not found error. -func (h *EtcdHelper) ExtractObj(key string, objPtr interface{}, ignoreNotFound bool) error { +func (h *EtcdHelper) ExtractObj(key string, objPtr runtime.Object, ignoreNotFound bool) error { _, _, err := h.bodyAndExtractObj(key, objPtr, ignoreNotFound) return err } -func (h *EtcdHelper) bodyAndExtractObj(key string, objPtr interface{}, ignoreNotFound bool) (body string, modifiedIndex uint64, err error) { +func (h *EtcdHelper) bodyAndExtractObj(key string, objPtr runtime.Object, ignoreNotFound bool) (body string, modifiedIndex uint64, err error) { response, err := h.Client.Get(key, false, false) if err != nil && !IsEtcdNotFound(err) { @@ -198,7 +186,7 @@ func (h *EtcdHelper) bodyAndExtractObj(key string, objPtr interface{}, ignoreNot } // CreateObj adds a new object at a key unless it already exists. -func (h *EtcdHelper) CreateObj(key string, obj interface{}) error { +func (h *EtcdHelper) CreateObj(key string, obj runtime.Object) error { data, err := h.Codec.Encode(obj) if err != nil { return err @@ -221,7 +209,7 @@ func (h *EtcdHelper) Delete(key string, recursive bool) error { // SetObj marshals obj via json, and stores under key. Will do an // atomic update if obj's ResourceVersion field is set. -func (h *EtcdHelper) SetObj(key string, obj interface{}) error { +func (h *EtcdHelper) SetObj(key string, obj runtime.Object) error { data, err := h.Codec.Encode(obj) if err != nil { return err @@ -240,7 +228,7 @@ func (h *EtcdHelper) SetObj(key string, obj interface{}) error { // Pass an EtcdUpdateFunc to EtcdHelper.AtomicUpdate to make an atomic etcd update. // See the comment for AtomicUpdate for more detail. -type EtcdUpdateFunc func(input interface{}) (output interface{}, err error) +type EtcdUpdateFunc func(input runtime.Object) (output runtime.Object, err error) // AtomicUpdate generalizes the pattern that allows for making atomic updates to etcd objects. // Note, tryUpdate may be called more than once. @@ -248,7 +236,7 @@ type EtcdUpdateFunc func(input interface{}) (output interface{}, err error) // Example: // // h := &util.EtcdHelper{client, encoding, versioning} -// err := h.AtomicUpdate("myKey", &MyType{}, func(input interface{}) (interface{}, error) { +// err := h.AtomicUpdate("myKey", &MyType{}, func(input runtime.Object) (runtime.Object, error) { // // Before this function is called, currentObj has been reset to etcd's current // // contents for "myKey". // @@ -261,14 +249,14 @@ type EtcdUpdateFunc func(input interface{}) (output interface{}, err error) // return cur, nil // }) // -func (h *EtcdHelper) AtomicUpdate(key string, ptrToType interface{}, tryUpdate EtcdUpdateFunc) error { +func (h *EtcdHelper) AtomicUpdate(key string, ptrToType runtime.Object, tryUpdate EtcdUpdateFunc) error { pt := reflect.TypeOf(ptrToType) if pt.Kind() != reflect.Ptr { // Panic is appropriate, because this is a programming error. panic("need ptr to type") } for { - obj := reflect.New(pt.Elem()).Interface() + obj := reflect.New(pt.Elem()).Interface().(runtime.Object) origBody, index, err := h.bodyAndExtractObj(key, obj, true) if err != nil { return err diff --git a/pkg/tools/etcd_tools_test.go b/pkg/tools/etcd_tools_test.go index da9e64c6b99..f7c497f641a 100644 --- a/pkg/tools/etcd_tools_test.go +++ b/pkg/tools/etcd_tools_test.go @@ -24,7 +24,6 @@ import ( "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/coreos/go-etcd/etcd" @@ -40,15 +39,16 @@ type TestResource struct { Value int `json:"value" yaml:"value,omitempty"` } -var scheme *conversion.Scheme -var codec = runtime.Codec -var versioner = runtime.ResourceVersioner +func (*TestResource) IsAnAPIObject() {} + +var scheme *runtime.Scheme +var codec = runtime.DefaultCodec +var versioner = runtime.DefaultResourceVersioner func init() { - scheme = conversion.NewScheme() - scheme.ExternalVersion = "v1beta1" - scheme.AddKnownTypes("", TestResource{}) - scheme.AddKnownTypes("v1beta1", TestResource{}) + scheme = runtime.NewScheme("", "v1beta1") + scheme.AddKnownTypes("", &TestResource{}) + scheme.AddKnownTypes("v1beta1", &TestResource{}) } func TestIsEtcdNotFound(t *testing.T) { @@ -166,7 +166,7 @@ func TestExtractObjNotFoundErr(t *testing.T) { } func TestSetObj(t *testing.T) { - obj := api.Pod{JSONBase: api.JSONBase{ID: "foo"}} + obj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} fakeClient := NewFakeEtcdClient(t) helper := EtcdHelper{fakeClient, codec, versioner} err := helper.SetObj("/some/key", obj) @@ -191,7 +191,7 @@ func TestSetObjWithVersion(t *testing.T) { fakeClient.Data["/some/key"] = EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ - Value: runtime.EncodeOrDie(obj), + Value: runtime.DefaultScheme.EncodeOrDie(obj), ModifiedIndex: 1, }, }, @@ -214,7 +214,7 @@ func TestSetObjWithVersion(t *testing.T) { } func TestSetObjWithoutResourceVersioner(t *testing.T) { - obj := api.Pod{JSONBase: api.JSONBase{ID: "foo"}} + obj := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} fakeClient := NewFakeEtcdClient(t) helper := EtcdHelper{fakeClient, codec, nil} err := helper.SetObj("/some/key", obj) @@ -241,7 +241,7 @@ func TestAtomicUpdate(t *testing.T) { // Create a new node. fakeClient.ExpectNotFoundGet("/some/key") obj := &TestResource{JSONBase: api.JSONBase{ID: "foo"}, Value: 1} - err := helper.AtomicUpdate("/some/key", &TestResource{}, func(in interface{}) (interface{}, error) { + err := helper.AtomicUpdate("/some/key", &TestResource{}, func(in runtime.Object) (runtime.Object, error) { return obj, nil }) if err != nil { @@ -260,7 +260,7 @@ func TestAtomicUpdate(t *testing.T) { // Update an existing node. callbackCalled := false objUpdate := &TestResource{JSONBase: api.JSONBase{ID: "foo"}, Value: 2} - err = helper.AtomicUpdate("/some/key", &TestResource{}, func(in interface{}) (interface{}, error) { + err = helper.AtomicUpdate("/some/key", &TestResource{}, func(in runtime.Object) (runtime.Object, error) { callbackCalled = true if in.(*TestResource).Value != 1 { @@ -295,7 +295,7 @@ func TestAtomicUpdateNoChange(t *testing.T) { // Create a new node. fakeClient.ExpectNotFoundGet("/some/key") obj := &TestResource{JSONBase: api.JSONBase{ID: "foo"}, Value: 1} - err := helper.AtomicUpdate("/some/key", &TestResource{}, func(in interface{}) (interface{}, error) { + err := helper.AtomicUpdate("/some/key", &TestResource{}, func(in runtime.Object) (runtime.Object, error) { return obj, nil }) if err != nil { @@ -306,7 +306,7 @@ func TestAtomicUpdateNoChange(t *testing.T) { callbackCalled := false objUpdate := &TestResource{JSONBase: api.JSONBase{ID: "foo"}, Value: 1} fakeClient.Err = errors.New("should not be called") - err = helper.AtomicUpdate("/some/key", &TestResource{}, func(in interface{}) (interface{}, error) { + err = helper.AtomicUpdate("/some/key", &TestResource{}, func(in runtime.Object) (runtime.Object, error) { callbackCalled = true return objUpdate, nil }) @@ -338,7 +338,7 @@ func TestAtomicUpdate_CreateCollision(t *testing.T) { defer wgDone.Done() firstCall := true - err := helper.AtomicUpdate("/some/key", &TestResource{}, func(in interface{}) (interface{}, error) { + err := helper.AtomicUpdate("/some/key", &TestResource{}, func(in runtime.Object) (runtime.Object, error) { defer func() { firstCall = false }() if firstCall { @@ -348,7 +348,7 @@ func TestAtomicUpdate_CreateCollision(t *testing.T) { } currValue := in.(*TestResource).Value - obj := TestResource{JSONBase: api.JSONBase{ID: "foo"}, Value: currValue + 1} + obj := &TestResource{JSONBase: api.JSONBase{ID: "foo"}, Value: currValue + 1} return obj, nil }) if err != nil { diff --git a/pkg/tools/etcd_tools_watch.go b/pkg/tools/etcd_tools_watch.go index bfb0bafebd1..f8a4d9f845f 100644 --- a/pkg/tools/etcd_tools_watch.go +++ b/pkg/tools/etcd_tools_watch.go @@ -19,6 +19,7 @@ package tools import ( "sync" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/coreos/go-etcd/etcd" @@ -27,10 +28,10 @@ import ( // FilterFunc is a predicate which takes an API object and returns true // iff the object should remain in the set. -type FilterFunc func(obj interface{}) bool +type FilterFunc func(obj runtime.Object) bool // Everything is a FilterFunc which accepts all objects. -func Everything(interface{}) bool { +func Everything(runtime.Object) bool { return true } @@ -59,7 +60,7 @@ func (h *EtcdHelper) Watch(key string, resourceVersion uint64) (watch.Interface, // change or wrap the serialized etcd object. // // startTime := time.Now() -// helper.WatchAndTransform(key, version, func(input interface{}) (interface{}, error) { +// helper.WatchAndTransform(key, version, func(input runtime.Object) (runtime.Object, error) { // value := input.(TimeAwareValue) // value.Since = startTime // return value, nil @@ -72,12 +73,12 @@ func (h *EtcdHelper) WatchAndTransform(key string, resourceVersion uint64, trans } // TransformFunc attempts to convert an object to another object for use with a watcher. -type TransformFunc func(interface{}) (interface{}, error) +type TransformFunc func(runtime.Object) (runtime.Object, error) // etcdWatcher converts a native etcd watch to a watch.Interface. type etcdWatcher struct { - encoding Codec - versioner ResourceVersioner + encoding runtime.Codec + versioner runtime.ResourceVersioner transform TransformFunc list bool // If we're doing a recursive watch, should be true. @@ -98,7 +99,7 @@ type etcdWatcher struct { // newEtcdWatcher returns a new etcdWatcher; if list is true, watch sub-nodes. If you provide a transform // and a versioner, the versioner must be able to handle the objects that transform creates. -func newEtcdWatcher(list bool, filter FilterFunc, encoding Codec, versioner ResourceVersioner, transform TransformFunc) *etcdWatcher { +func newEtcdWatcher(list bool, filter FilterFunc, encoding runtime.Codec, versioner runtime.ResourceVersioner, transform TransformFunc) *etcdWatcher { w := &etcdWatcher{ encoding: encoding, versioner: versioner, @@ -192,7 +193,7 @@ func (w *etcdWatcher) translate() { } } -func (w *etcdWatcher) decodeObject(data []byte, index uint64) (interface{}, error) { +func (w *etcdWatcher) decodeObject(data []byte, index uint64) (runtime.Object, error) { obj, err := w.encoding.Decode(data) if err != nil { return nil, err @@ -260,7 +261,7 @@ func (w *etcdWatcher) sendModify(res *etcd.Response) { } curObjPasses := w.filter(curObj) oldObjPasses := false - var oldObj interface{} + var oldObj runtime.Object if res.PrevNode != nil && res.PrevNode.Value != "" { // Ignore problems reading the old object. if oldObj, err = w.decodeObject([]byte(res.PrevNode.Value), res.PrevNode.ModifiedIndex); err == nil { diff --git a/pkg/tools/etcd_tools_watch_test.go b/pkg/tools/etcd_tools_watch_test.go index 73da21ce234..273ecfe3713 100644 --- a/pkg/tools/etcd_tools_watch_test.go +++ b/pkg/tools/etcd_tools_watch_test.go @@ -33,7 +33,7 @@ func TestWatchInterpretations(t *testing.T) { podFoo := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} podBar := &api.Pod{JSONBase: api.JSONBase{ID: "bar"}} podBaz := &api.Pod{JSONBase: api.JSONBase{ID: "baz"}} - firstLetterIsB := func(obj interface{}) bool { + firstLetterIsB := func(obj runtime.Object) bool { return obj.(*api.Pod).ID[0] == 'b' } @@ -44,66 +44,66 @@ func TestWatchInterpretations(t *testing.T) { nodeValue string expectEmit bool expectType watch.EventType - expectObject interface{} + expectObject runtime.Object }{ "create": { actions: []string{"create", "get"}, - nodeValue: runtime.EncodeOrDie(podBar), + nodeValue: runtime.DefaultScheme.EncodeOrDie(podBar), expectEmit: true, expectType: watch.Added, expectObject: podBar, }, "create but filter blocks": { actions: []string{"create", "get"}, - nodeValue: runtime.EncodeOrDie(podFoo), + nodeValue: runtime.DefaultScheme.EncodeOrDie(podFoo), expectEmit: false, }, "delete": { actions: []string{"delete"}, - prevNodeValue: runtime.EncodeOrDie(podBar), + prevNodeValue: runtime.DefaultScheme.EncodeOrDie(podBar), expectEmit: true, expectType: watch.Deleted, expectObject: podBar, }, "delete but filter blocks": { actions: []string{"delete"}, - nodeValue: runtime.EncodeOrDie(podFoo), + nodeValue: runtime.DefaultScheme.EncodeOrDie(podFoo), expectEmit: false, }, "modify appears to create 1": { actions: []string{"set", "compareAndSwap"}, - nodeValue: runtime.EncodeOrDie(podBar), + nodeValue: runtime.DefaultScheme.EncodeOrDie(podBar), expectEmit: true, expectType: watch.Added, expectObject: podBar, }, "modify appears to create 2": { actions: []string{"set", "compareAndSwap"}, - prevNodeValue: runtime.EncodeOrDie(podFoo), - nodeValue: runtime.EncodeOrDie(podBar), + prevNodeValue: runtime.DefaultScheme.EncodeOrDie(podFoo), + nodeValue: runtime.DefaultScheme.EncodeOrDie(podBar), expectEmit: true, expectType: watch.Added, expectObject: podBar, }, "modify appears to delete": { actions: []string{"set", "compareAndSwap"}, - prevNodeValue: runtime.EncodeOrDie(podBar), - nodeValue: runtime.EncodeOrDie(podFoo), + prevNodeValue: runtime.DefaultScheme.EncodeOrDie(podBar), + nodeValue: runtime.DefaultScheme.EncodeOrDie(podFoo), expectEmit: true, expectType: watch.Deleted, expectObject: podBar, // Should return last state that passed the filter! }, "modify modifies": { actions: []string{"set", "compareAndSwap"}, - prevNodeValue: runtime.EncodeOrDie(podBar), - nodeValue: runtime.EncodeOrDie(podBaz), + prevNodeValue: runtime.DefaultScheme.EncodeOrDie(podBar), + nodeValue: runtime.DefaultScheme.EncodeOrDie(podBaz), expectEmit: true, expectType: watch.Modified, expectObject: podBaz, }, "modify ignores": { actions: []string{"set", "compareAndSwap"}, - nodeValue: runtime.EncodeOrDie(podFoo), + nodeValue: runtime.DefaultScheme.EncodeOrDie(podFoo), expectEmit: false, }, } @@ -259,7 +259,7 @@ func TestWatchEtcdState(t *testing.T) { { Action: "create", Node: &etcd.Node{ - Value: string(runtime.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), + Value: string(runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), }, }, }, @@ -273,12 +273,12 @@ func TestWatchEtcdState(t *testing.T) { { Action: "compareAndSwap", Node: &etcd.Node{ - Value: string(runtime.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:9000"}})), + Value: string(runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:9000"}})), CreatedIndex: 1, ModifiedIndex: 2, }, PrevNode: &etcd.Node{ - Value: string(runtime.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), + Value: string(runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), CreatedIndex: 1, ModifiedIndex: 1, }, @@ -295,7 +295,7 @@ func TestWatchEtcdState(t *testing.T) { R: &etcd.Response{ Action: "get", Node: &etcd.Node{ - Value: string(runtime.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), + Value: string(runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), CreatedIndex: 1, ModifiedIndex: 1, }, @@ -308,12 +308,12 @@ func TestWatchEtcdState(t *testing.T) { { Action: "compareAndSwap", Node: &etcd.Node{ - Value: string(runtime.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:9000"}})), + Value: string(runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{"127.0.0.1:9000"}})), CreatedIndex: 1, ModifiedIndex: 2, }, PrevNode: &etcd.Node{ - Value: string(runtime.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), + Value: string(runtime.DefaultScheme.EncodeOrDie(&api.Endpoints{JSONBase: api.JSONBase{ID: "foo"}, Endpoints: []string{}})), CreatedIndex: 1, ModifiedIndex: 1, }, @@ -370,7 +370,7 @@ func TestWatchFromZeroIndex(t *testing.T) { EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ - Value: runtime.EncodeOrDie(pod), + Value: runtime.DefaultScheme.EncodeOrDie(pod), CreatedIndex: 1, ModifiedIndex: 1, }, @@ -385,7 +385,7 @@ func TestWatchFromZeroIndex(t *testing.T) { EtcdResponseWithError{ R: &etcd.Response{ Node: &etcd.Node{ - Value: runtime.EncodeOrDie(pod), + Value: runtime.DefaultScheme.EncodeOrDie(pod), CreatedIndex: 1, ModifiedIndex: 2, }, @@ -443,13 +443,13 @@ func TestWatchListFromZeroIndex(t *testing.T) { Dir: true, Nodes: etcd.Nodes{ &etcd.Node{ - Value: runtime.EncodeOrDie(pod), + Value: runtime.DefaultScheme.EncodeOrDie(pod), CreatedIndex: 1, ModifiedIndex: 1, Nodes: etcd.Nodes{}, }, &etcd.Node{ - Value: runtime.EncodeOrDie(pod), + Value: runtime.DefaultScheme.EncodeOrDie(pod), CreatedIndex: 2, ModifiedIndex: 2, Nodes: etcd.Nodes{}, diff --git a/pkg/watch/filter.go b/pkg/watch/filter.go index 3471ff84c76..187cc1fbff3 100644 --- a/pkg/watch/filter.go +++ b/pkg/watch/filter.go @@ -24,6 +24,11 @@ type FilterFunc func(in Event) (out Event, keep bool) // Putting a filter on a watch, as an unavoidable side-effect due to the way // go channels work, effectively causes the watch's event channel to have its // queue length increased by one. +// +// WARNING: filter has a fatal flaw, in that it can't properly update the +// Type field (Add/Modified/Deleted) to reflect items beginning to pass the +// filter when they previously didn't. +// func Filter(w Interface, f FilterFunc) Interface { fw := &filteredWatch{ incoming: w, diff --git a/pkg/watch/filter_test.go b/pkg/watch/filter_test.go index a6e59ca3ed9..3fab30ce8a8 100644 --- a/pkg/watch/filter_test.go +++ b/pkg/watch/filter_test.go @@ -23,16 +23,16 @@ import ( func TestFilter(t *testing.T) { table := []Event{ - {Added, "foo"}, - {Added, "bar"}, - {Added, "baz"}, - {Added, "qux"}, - {Added, "zoo"}, + {Added, testType("foo")}, + {Added, testType("bar")}, + {Added, testType("baz")}, + {Added, testType("qux")}, + {Added, testType("zoo")}, } source := NewFake() filtered := Filter(source, func(e Event) (Event, bool) { - return e, e.Object.(string)[0] != 'b' + return e, e.Object.(testType)[0] != 'b' }) go func() { @@ -48,7 +48,7 @@ func TestFilter(t *testing.T) { if !ok { break } - got = append(got, event.Object.(string)) + got = append(got, string(event.Object.(testType))) } if e, a := []string{"foo", "qux", "zoo"}, got; !reflect.DeepEqual(e, a) { @@ -59,11 +59,11 @@ func TestFilter(t *testing.T) { func TestFilterStop(t *testing.T) { source := NewFake() filtered := Filter(source, func(e Event) (Event, bool) { - return e, e.Object.(string)[0] != 'b' + return e, e.Object.(testType)[0] != 'b' }) go func() { - source.Add("foo") + source.Add(testType("foo")) filtered.Stop() }() @@ -73,7 +73,7 @@ func TestFilterStop(t *testing.T) { if !ok { break } - got = append(got, event.Object.(string)) + got = append(got, string(event.Object.(testType))) } if e, a := []string{"foo"}, got; !reflect.DeepEqual(e, a) { diff --git a/pkg/watch/iowatcher.go b/pkg/watch/iowatcher.go index a44eef698da..8e359e63dc2 100644 --- a/pkg/watch/iowatcher.go +++ b/pkg/watch/iowatcher.go @@ -19,6 +19,7 @@ package watch import ( "sync" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) @@ -27,7 +28,7 @@ type Decoder interface { // Decode should return the type of event, the decoded object, or an error. // An error will cause StreamWatcher to call Close(). Decode should block until // it has data or an error occurs. - Decode() (action EventType, object interface{}, err error) + Decode() (action EventType, object runtime.Object, err error) // Close should close the underlying io.Reader, signalling to the source of // the stream that it is no longer being watched. Close() must cause any diff --git a/pkg/watch/iowatcher_test.go b/pkg/watch/iowatcher_test.go index 564b254b11e..511e8de3a5a 100644 --- a/pkg/watch/iowatcher_test.go +++ b/pkg/watch/iowatcher_test.go @@ -20,13 +20,15 @@ import ( "io" "reflect" "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) type fakeDecoder struct { items chan Event } -func (f fakeDecoder) Decode() (action EventType, object interface{}, err error) { +func (f fakeDecoder) Decode() (action EventType, object runtime.Object, err error) { item, open := <-f.items if !open { return action, nil, io.EOF @@ -40,7 +42,7 @@ func (f fakeDecoder) Close() { func TestStreamWatcher(t *testing.T) { table := []Event{ - {Added, "foo"}, + {Added, testType("foo")}, } fd := fakeDecoder{make(chan Event, 5)} diff --git a/pkg/watch/mux.go b/pkg/watch/mux.go index 4935e204ffe..0c87111ee16 100644 --- a/pkg/watch/mux.go +++ b/pkg/watch/mux.go @@ -18,6 +18,8 @@ package watch import ( "sync" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) // Mux distributes event notifications among any number of watchers. Every event @@ -88,7 +90,7 @@ func (m *Mux) closeAll() { } // Action distributes the given event among all watchers. -func (m *Mux) Action(action EventType, obj interface{}) { +func (m *Mux) Action(action EventType, obj runtime.Object) { m.incoming <- Event{action, obj} } diff --git a/pkg/watch/mux_test.go b/pkg/watch/mux_test.go index 80e5c2865c8..a9433a4bea8 100644 --- a/pkg/watch/mux_test.go +++ b/pkg/watch/mux_test.go @@ -22,16 +22,19 @@ import ( "testing" ) +type myType struct { + ID string + Value string +} + +func (*myType) IsAnAPIObject() {} + func TestMux(t *testing.T) { - type myType struct { - ID string - Value string - } table := []Event{ - {Added, myType{"foo", "hello world 1"}}, - {Added, myType{"bar", "hello world 2"}}, - {Modified, myType{"foo", "goodbye world 3"}}, - {Deleted, myType{"bar", "hello world 4"}}, + {Added, &myType{"foo", "hello world 1"}}, + {Added, &myType{"bar", "hello world 2"}}, + {Modified, &myType{"foo", "goodbye world 3"}}, + {Deleted, &myType{"bar", "hello world 4"}}, } // The mux we're testing diff --git a/pkg/watch/watch.go b/pkg/watch/watch.go index 59ddfbc5795..bed7163f144 100644 --- a/pkg/watch/watch.go +++ b/pkg/watch/watch.go @@ -18,6 +18,8 @@ package watch import ( "sync" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) // Interface can be implemented by anything that knows how to watch and report changes. @@ -47,7 +49,7 @@ type Event struct { // If Type == Deleted, then this is the state of the object // immediately before deletion. - Object interface{} + Object runtime.Object } // FakeWatcher lets you test anything that consumes a watch.Interface; threadsafe. @@ -78,21 +80,21 @@ func (f *FakeWatcher) ResultChan() <-chan Event { } // Add sends an add event. -func (f *FakeWatcher) Add(obj interface{}) { +func (f *FakeWatcher) Add(obj runtime.Object) { f.result <- Event{Added, obj} } // Modify sends a modify event. -func (f *FakeWatcher) Modify(obj interface{}) { +func (f *FakeWatcher) Modify(obj runtime.Object) { f.result <- Event{Modified, obj} } // Delete sends a delete event. -func (f *FakeWatcher) Delete(lastValue interface{}) { +func (f *FakeWatcher) Delete(lastValue runtime.Object) { f.result <- Event{Deleted, lastValue} } // Action sends an event of the requested type, for table-based testing. -func (f *FakeWatcher) Action(action EventType, obj interface{}) { +func (f *FakeWatcher) Action(action EventType, obj runtime.Object) { f.result <- Event{action, obj} } diff --git a/pkg/watch/watch_test.go b/pkg/watch/watch_test.go index d54c2e60acb..a44bad88aff 100644 --- a/pkg/watch/watch_test.go +++ b/pkg/watch/watch_test.go @@ -20,17 +20,21 @@ import ( "testing" ) +type testType string + +func (testType) IsAnAPIObject() {} + func TestFake(t *testing.T) { f := NewFake() table := []struct { t EventType - s string + s testType }{ - {Added, "foo"}, - {Modified, "qux"}, - {Modified, "bar"}, - {Deleted, "bar"}, + {Added, testType("foo")}, + {Modified, testType("qux")}, + {Modified, testType("bar")}, + {Deleted, testType("bar")}, } // Prove that f implements Interface by phrasing this as a function. @@ -43,7 +47,7 @@ func TestFake(t *testing.T) { if e, a := expect.t, got.Type; e != a { t.Fatalf("Expected %v, got %v", e, a) } - if a, ok := got.Object.(string); !ok || a != expect.s { + if a, ok := got.Object.(testType); !ok || a != expect.s { t.Fatalf("Expected %v, got %v", expect.s, a) } } @@ -54,10 +58,10 @@ func TestFake(t *testing.T) { } sender := func() { - f.Add("foo") - f.Action(Modified, "qux") - f.Modify("bar") - f.Delete("bar") + f.Add(testType("foo")) + f.Action(Modified, testType("qux")) + f.Modify(testType("bar")) + f.Delete(testType("bar")) f.Stop() } diff --git a/plugin/pkg/scheduler/factory/factory_test.go b/plugin/pkg/scheduler/factory/factory_test.go index 6ff1b50ea94..9f141ddd20e 100644 --- a/plugin/pkg/scheduler/factory/factory_test.go +++ b/plugin/pkg/scheduler/factory/factory_test.go @@ -113,7 +113,7 @@ func TestPollMinions(t *testing.T) { ml := &api.MinionList{Items: item.minions} handler := util.FakeHandler{ StatusCode: 200, - ResponseBody: runtime.EncodeOrDie(ml), + ResponseBody: runtime.DefaultScheme.EncodeOrDie(ml), T: t, } mux := http.NewServeMux() @@ -140,7 +140,7 @@ func TestDefaultErrorFunc(t *testing.T) { testPod := &api.Pod{JSONBase: api.JSONBase{ID: "foo"}} handler := util.FakeHandler{ StatusCode: 200, - ResponseBody: runtime.EncodeOrDie(testPod), + ResponseBody: runtime.DefaultScheme.EncodeOrDie(testPod), T: t, } mux := http.NewServeMux() @@ -259,7 +259,7 @@ func TestBind(t *testing.T) { t.Errorf("Unexpected error: %v", err) continue } - expectedBody := runtime.EncodeOrDie(item.binding) + expectedBody := runtime.DefaultScheme.EncodeOrDie(item.binding) handler.ValidateRequest(t, "/api/v1beta1/bindings", "POST", &expectedBody) } } diff --git a/test/integration/client_test.go b/test/integration/client_test.go index b5ccdc526f3..5d539d11e29 100644 --- a/test/integration/client_test.go +++ b/test/integration/client_test.go @@ -62,7 +62,7 @@ func TestClient(t *testing.T) { } // get a validation error - pod := api.Pod{ + pod := &api.Pod{ DesiredState: api.PodState{ Manifest: api.ContainerManifest{ Version: "v1beta2", diff --git a/test/integration/etcd_tools_test.go b/test/integration/etcd_tools_test.go index b605a0b75e2..c12df1b24ca 100644 --- a/test/integration/etcd_tools_test.go +++ b/test/integration/etcd_tools_test.go @@ -33,17 +33,22 @@ func init() { type stringCodec struct{} -func (c stringCodec) Encode(obj interface{}) ([]byte, error) { - return []byte(obj.(string)), nil +type fakeAPIObject string + +func (*fakeAPIObject) IsAnAPIObject() {} + +func (c stringCodec) Encode(obj runtime.Object) ([]byte, error) { + return []byte(*obj.(*fakeAPIObject)), nil } -func (c stringCodec) Decode(data []byte) (interface{}, error) { - return string(data), nil +func (c stringCodec) Decode(data []byte) (runtime.Object, error) { + o := fakeAPIObject(data) + return &o, nil } -func (c stringCodec) DecodeInto(data []byte, obj interface{}) error { - o := obj.(*string) - *o = string(data) +func (c stringCodec) DecodeInto(data []byte, obj runtime.Object) error { + o := obj.(*fakeAPIObject) + *o = fakeAPIObject(data) return nil } @@ -51,7 +56,8 @@ func TestSetObj(t *testing.T) { client := newEtcdClient() helper := tools.EtcdHelper{Client: client, Codec: stringCodec{}} withEtcdKey(func(key string) { - if err := helper.SetObj(key, "object"); err != nil { + fakeObject := fakeAPIObject("object") + if err := helper.SetObj(key, &fakeObject); err != nil { t.Fatalf("unexpected error: %v", err) } resp, err := client.Get(key, false, false) @@ -72,7 +78,7 @@ func TestExtractObj(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - s := "" + s := fakeAPIObject("") if err := helper.ExtractObj(key, &s, false); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -84,9 +90,9 @@ func TestExtractObj(t *testing.T) { func TestWatch(t *testing.T) { client := newEtcdClient() - helper := tools.EtcdHelper{Client: client, Codec: runtime.Codec, ResourceVersioner: runtime.ResourceVersioner} + helper := tools.EtcdHelper{Client: client, Codec: runtime.DefaultCodec, ResourceVersioner: runtime.DefaultResourceVersioner} withEtcdKey(func(key string) { - resp, err := client.Set(key, runtime.EncodeOrDie(api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0) + resp, err := client.Set(key, runtime.DefaultScheme.EncodeOrDie(&api.Pod{JSONBase: api.JSONBase{ID: "foo"}}), 0) if err != nil { t.Fatalf("unexpected error: %v", err) }