diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index d51f0376df6..3813a61b41e 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -101,6 +101,10 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer { j.Spec = api.PodSpec{} c.Fuzz(&j.Spec) }, + func(j *api.Binding, c fuzz.Continue) { + c.Fuzz(&j.ObjectMeta) + j.Target.Name = c.RandString() + }, func(j *api.ReplicationControllerSpec, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again j.TemplateRef = nil // this is required for round trip diff --git a/pkg/api/types.go b/pkg/api/types.go index a6ce21e123f..0619a5f2039 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -918,13 +918,14 @@ type NamespaceList struct { Items []Namespace `json:"items"` } -// Binding is written by a scheduler to cause a pod to be bound to a host. +// Binding ties one object to another - for example, a pod is bound to a node by a scheduler. type Binding struct { - TypeMeta `json:",inline"` + TypeMeta `json:",inline"` + // ObjectMeta describes the object that is being bound. ObjectMeta `json:"metadata,omitempty"` - PodID string `json:"podID"` - Host string `json:"host"` + // Target is the object to bind to. + Target ObjectReference `json:"target"` } // Status is a return value for calls that don't return other objects. diff --git a/pkg/api/v1beta1/conversion.go b/pkg/api/v1beta1/conversion.go index d078df19e63..267389cb8d6 100644 --- a/pkg/api/v1beta1/conversion.go +++ b/pkg/api/v1beta1/conversion.go @@ -1324,6 +1324,24 @@ func init() { return nil }, + func(in *Binding, out *newer.Binding, s conversion.Scope) error { + if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil { + return err + } + out.Target = newer.ObjectReference{ + Name: in.Host, + } + out.Name = in.PodID + return nil + }, + func(in *newer.Binding, out *Binding, s conversion.Scope) error { + if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil { + return err + } + out.Host = in.Target.Name + out.PodID = in.Name + return nil + }, ) if err != nil { // If one of the conversion functions is malformed, detect it immediately. diff --git a/pkg/api/v1beta2/conversion.go b/pkg/api/v1beta2/conversion.go index 83cea0f1e92..923f5468be0 100644 --- a/pkg/api/v1beta2/conversion.go +++ b/pkg/api/v1beta2/conversion.go @@ -1240,6 +1240,24 @@ func init() { return nil }, + func(in *Binding, out *newer.Binding, s conversion.Scope) error { + if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil { + return err + } + out.Target = newer.ObjectReference{ + Name: in.Host, + } + out.Name = in.PodID + return nil + }, + func(in *newer.Binding, out *Binding, s conversion.Scope) error { + if err := s.DefaultConvert(in, out, conversion.IgnoreMissingFields); err != nil { + return err + } + out.Host = in.Target.Name + out.PodID = in.Name + return nil + }, ) if err != nil { // If one of the conversion functions is malformed, detect it immediately. diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 16f01226d2b..4bfe077bea2 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -940,16 +940,14 @@ type NamespaceList struct { Items []Namespace `json:"items" description:"items is the list of Namespace objects in the list"` } -// Binding is written by a scheduler to cause a pod to be bound to a node. Name is not -// required for Bindings. +// Binding ties one object to another - for example, a pod is bound to a node by a scheduler. type Binding struct { - TypeMeta `json:",inline"` + TypeMeta `json:",inline"` + // ObjectMeta describes the object that is being bound. ObjectMeta `json:"metadata,omitempty" description:"standard object metadata; see https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/api-conventions.md#metadata"` - // PodID is a Pod name to be bound to a node. - PodID string `json:"podID" description:"name of the pod to be bound to a node"` - // Host is the name of a node to bind to. - Host string `json:"host" description:"name of the node to bind to"` + // Target is the object to bind to. + Target ObjectReference `json:"target" description:"an object to bind to"` } // Status is a return value for calls that don't return other objects. diff --git a/pkg/client/fake_pods.go b/pkg/client/fake_pods.go index 27e9e7b5551..a5e5e97924d 100644 --- a/pkg/client/fake_pods.go +++ b/pkg/client/fake_pods.go @@ -52,3 +52,8 @@ func (c *FakePods) Update(pod *api.Pod) (*api.Pod, error) { c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "update-pod", Value: pod.Name}) return &api.Pod{}, nil } + +func (c *FakePods) Bind(bind *api.Binding) error { + c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "bind-pod", Value: bind.Name}) + return nil +} diff --git a/pkg/client/pods.go b/pkg/client/pods.go index 7782b948a86..30366081550 100644 --- a/pkg/client/pods.go +++ b/pkg/client/pods.go @@ -36,6 +36,8 @@ type PodInterface interface { Delete(name string) error Create(pod *api.Pod) (*api.Pod, error) Update(pod *api.Pod) (*api.Pod, error) + + Bind(binding *api.Binding) error } // pods implements PodsNamespacer interface @@ -92,3 +94,8 @@ func (c *pods) Update(pod *api.Pod) (result *api.Pod, err error) { err = c.r.Put().Namespace(c.ns).Resource("pods").Name(pod.Name).Body(pod).Do().Into(result) return } + +// Bind applies the provided binding to the named pod in the current namespace (binding.Namespace is ignored). +func (c *pods) Bind(binding *api.Binding) error { + return c.r.Post().Namespace(c.ns).Resource("pods").Name(binding.Name).SubResource("binding").Body(binding).Do().Error() +} diff --git a/pkg/master/master.go b/pkg/master/master.go index 4d35bc34635..f84fd3d4aa0 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -404,9 +404,10 @@ func (m *Master) init(c *Config) { // TODO: Factor out the core API registration m.storage = map[string]apiserver.RESTStorage{ - "pods": podStorage, - "pods/status": podStatusStorage, - "bindings": bindingStorage, + "pods": podStorage, + "pods/status": podStatusStorage, + "pods/binding": bindingStorage, + "bindings": bindingStorage, "replicationControllers": controller.NewREST(registry, podRegistry), "services": service.NewREST(m.serviceRegistry, c.Cloud, m.nodeRegistry, m.portalNet, c.ClusterName), diff --git a/pkg/registry/binding/doc.go b/pkg/registry/binding/doc.go deleted file mode 100644 index 2fdfa915dba..00000000000 --- a/pkg/registry/binding/doc.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package binding contains the middle layer logic for bindings. -// Bindings are objects containing instructions for how a pod ought to -// be bound to a host. This allows a registry object which supports this -// action (ApplyBinding) to be served through an apiserver. -package binding diff --git a/pkg/registry/binding/mock.go b/pkg/registry/binding/mock.go deleted file mode 100644 index f49429cd166..00000000000 --- a/pkg/registry/binding/mock.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package binding - -import ( - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" -) - -// MockRegistry can be used for testing. -type MockRegistry struct { - OnApplyBinding func(binding *api.Binding) error -} - -func (mr MockRegistry) ApplyBinding(ctx api.Context, binding *api.Binding) error { - return mr.OnApplyBinding(binding) -} diff --git a/pkg/registry/binding/registry.go b/pkg/registry/binding/registry.go deleted file mode 100644 index 8a61559bc93..00000000000 --- a/pkg/registry/binding/registry.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package binding - -import ( - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" -) - -// Registry contains the functions needed to support a BindingStorage. -type Registry interface { - // ApplyBinding should apply the binding. That is, it should actually - // assign or place pod binding.PodID on machine binding.Host. - ApplyBinding(ctx api.Context, binding *api.Binding) error -} diff --git a/pkg/registry/binding/rest.go b/pkg/registry/binding/rest.go deleted file mode 100644 index 42d7970870e..00000000000 --- a/pkg/registry/binding/rest.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package binding - -import ( - "fmt" - "net/http" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" -) - -// REST implements the RESTStorage interface for bindings. When bindings are written, it -// changes the location of the affected pods. This information is eventually reflected -// in the pod's CurrentState.Host field. -type REST struct { - registry Registry -} - -// NewREST creates a new REST backed by the given bindingRegistry. -func NewREST(bindingRegistry Registry) *REST { - return &REST{ - registry: bindingRegistry, - } -} - -// New returns a new binding object fit for having data unmarshalled into it. -func (*REST) New() runtime.Object { - return &api.Binding{} -} - -// Create attempts to make the assignment indicated by the binding it recieves. -func (b *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) { - binding, ok := obj.(*api.Binding) - if !ok { - return nil, fmt.Errorf("incorrect type: %#v", obj) - } - if err := b.registry.ApplyBinding(ctx, binding); err != nil { - return nil, err - } - return &api.Status{Status: api.StatusSuccess, Code: http.StatusCreated}, nil -} diff --git a/pkg/registry/binding/rest_test.go b/pkg/registry/binding/rest_test.go deleted file mode 100644 index 5ddac234105..00000000000 --- a/pkg/registry/binding/rest_test.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2014 Google Inc. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package binding - -import ( - "errors" - "net/http" - "reflect" - "testing" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" -) - -func TestNewREST(t *testing.T) { - mockRegistry := MockRegistry{ - OnApplyBinding: func(b *api.Binding) error { return nil }, - } - b := NewREST(mockRegistry) - - binding := &api.Binding{ - PodID: "foo", - Host: "bar", - } - body, err := latest.Codec.Encode(binding) - if err != nil { - t.Fatalf("Unexpected encode error %v", err) - } - obj := b.New() - err = latest.Codec.DecodeInto(body, obj) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if e, a := binding, obj; !reflect.DeepEqual(e, a) { - t.Errorf("Expected %#v, but got %#v", e, a) - } -} - -func TestRESTPost(t *testing.T) { - table := []struct { - b *api.Binding - err error - }{ - {b: &api.Binding{PodID: "foo", Host: "bar"}, err: errors.New("no host bar")}, - {b: &api.Binding{PodID: "baz", Host: "qux"}, err: nil}, - {b: &api.Binding{PodID: "dvorak", Host: "qwerty"}, err: nil}, - } - - for i, item := range table { - mockRegistry := MockRegistry{ - OnApplyBinding: func(b *api.Binding) error { - if !reflect.DeepEqual(item.b, b) { - t.Errorf("%v: expected %#v, but got %#v", i, item, b) - } - return item.err - }, - } - ctx := api.NewContext() - b := NewREST(mockRegistry) - result, err := b.Create(ctx, item.b) - if err != nil && item.err == nil { - t.Errorf("Unexpected error %v", err) - continue - } - if err == nil && item.err != nil { - t.Errorf("Unexpected error %v", err) - continue - } - var expect interface{} - if item.err == nil { - expect = &api.Status{Status: api.StatusSuccess, Code: http.StatusCreated} - } - if e, a := expect, result; !reflect.DeepEqual(e, a) { - t.Errorf("%v: expected %#v, got %#v", i, e, a) - } - } -} diff --git a/pkg/registry/pod/etcd/etcd.go b/pkg/registry/pod/etcd/etcd.go index 2ca4e738446..d0f1c43857e 100644 --- a/pkg/registry/pod/etcd/etcd.go +++ b/pkg/registry/pod/etcd/etcd.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest" "github.com/GoogleCloudPlatform/kubernetes/pkg/constraint" @@ -146,7 +147,14 @@ func (r *BindingREST) New() runtime.Object { // Create ensures a pod is bound to a specific host. func (r *BindingREST) Create(ctx api.Context, obj runtime.Object) (out runtime.Object, err error) { binding := obj.(*api.Binding) - err = r.assignPod(ctx, binding.PodID, binding.Host) + // TODO: move me to a binding strategy + if len(binding.Target.Kind) != 0 && (binding.Target.Kind != "Node" && binding.Target.Kind != "Minion") { + return nil, errors.NewInvalid("binding", binding.Name, errors.ValidationErrorList{errors.NewFieldInvalid("to.kind", binding.Target.Kind, "must be empty, 'Node', or 'Minion'")}) + } + if len(binding.Target.Name) == 0 { + return nil, errors.NewInvalid("binding", binding.Name, errors.ValidationErrorList{errors.NewFieldRequired("to.name", binding.Target.Name)}) + } + err = r.assignPod(ctx, binding.Name, binding.Target.Name) err = etcderr.InterpretCreateError(err, "binding", "") out = &api.Status{Status: api.StatusSuccess} return diff --git a/pkg/registry/pod/etcd/etcd_test.go b/pkg/registry/pod/etcd/etcd_test.go index 3b6781e5c3e..cd7d20c9205 100644 --- a/pkg/registry/pod/etcd/etcd_test.go +++ b/pkg/registry/pod/etcd/etcd_test.go @@ -817,7 +817,10 @@ func TestEtcdCreate(t *testing.T) { } // Suddenly, a wild scheduler appears: - _, err = bindingRegistry.Create(ctx, &api.Binding{PodID: "foo", Host: "machine", ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault}}) + _, err = bindingRegistry.Create(ctx, &api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{Name: "machine"}, + }) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -865,7 +868,10 @@ func TestEtcdCreateBindingNoPod(t *testing.T) { // - Create (apiserver) // - Schedule (scheduler) // - Delete (apiserver) - _, err := bindingRegistry.Create(ctx, &api.Binding{PodID: "foo", Host: "machine", ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault}}) + _, err := bindingRegistry.Create(ctx, &api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{Name: "machine"}, + }) if err == nil { t.Fatalf("Expected not-found-error but got nothing") } @@ -935,7 +941,10 @@ func TestEtcdCreateWithContainersError(t *testing.T) { } // Suddenly, a wild scheduler appears: - _, err = bindingRegistry.Create(ctx, &api.Binding{PodID: "foo", Host: "machine"}) + _, err = bindingRegistry.Create(ctx, &api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{Name: "machine"}, + }) if !errors.IsAlreadyExists(err) { t.Fatalf("Unexpected error returned: %#v", err) } @@ -973,7 +982,10 @@ func TestEtcdCreateWithContainersNotFound(t *testing.T) { } // Suddenly, a wild scheduler appears: - _, err = bindingRegistry.Create(ctx, &api.Binding{PodID: "foo", Host: "machine"}) + _, err = bindingRegistry.Create(ctx, &api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{Name: "machine"}, + }) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -1025,7 +1037,10 @@ func TestEtcdCreateWithExistingContainers(t *testing.T) { } // Suddenly, a wild scheduler appears: - _, err = bindingRegistry.Create(ctx, &api.Binding{PodID: "foo", Host: "machine"}) + _, err = bindingRegistry.Create(ctx, &api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{Name: "machine"}, + }) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -1055,6 +1070,70 @@ func TestEtcdCreateWithExistingContainers(t *testing.T) { } } +func TestEtcdCreateBinding(t *testing.T) { + registry, bindingRegistry, _, fakeClient, _ := newStorage(t) + ctx := api.NewDefaultContext() + fakeClient.TestIndex = true + + testCases := map[string]struct { + binding api.Binding + errOK func(error) bool + }{ + "noName": { + binding: api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{}, + }, + errOK: func(err error) bool { return errors.IsInvalid(err) }, + }, + "badKind": { + binding: api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{Name: "machine", Kind: "unknown"}, + }, + errOK: func(err error) bool { return errors.IsInvalid(err) }, + }, + "emptyKind": { + binding: api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{Name: "machine"}, + }, + errOK: func(err error) bool { return err == nil }, + }, + "kindNode": { + binding: api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{Name: "machine", Kind: "Node"}, + }, + errOK: func(err error) bool { return err == nil }, + }, + "kindMinion": { + binding: api.Binding{ + ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "foo"}, + Target: api.ObjectReference{Name: "machine", Kind: "Minion"}, + }, + errOK: func(err error) bool { return err == nil }, + }, + } + for k, test := range testCases { + key, _ := registry.store.KeyFunc(ctx, "foo") + fakeClient.Data[key] = tools.EtcdResponseWithError{ + R: &etcd.Response{ + Node: nil, + }, + E: tools.EtcdErrorNotFound, + } + fakeClient.Set("/registry/nodes/machine/boundpods", runtime.EncodeOrDie(latest.Codec, &api.BoundPods{}), 0) + if _, err := registry.Create(ctx, validNewPod()); err != nil { + t.Fatalf("%s: unexpected error: %v", k, err) + } + fakeClient.Set("/registry/nodes/machine/boundpods", runtime.EncodeOrDie(latest.Codec, &api.BoundPods{}), 0) + if _, err := bindingRegistry.Create(ctx, &test.binding); !test.errOK(err) { + t.Errorf("%s: unexpected error: %v", k, err) + } + } +} + func TestEtcdUpdateNotFound(t *testing.T) { registry, _, _, fakeClient, _ := newStorage(t) ctx := api.NewDefaultContext() diff --git a/plugin/pkg/scheduler/factory/factory.go b/plugin/pkg/scheduler/factory/factory.go index 2611eaa30db..c201a147972 100644 --- a/plugin/pkg/scheduler/factory/factory.go +++ b/plugin/pkg/scheduler/factory/factory.go @@ -287,9 +287,11 @@ type binder struct { // Bind just does a POST binding RPC. func (b *binder) Bind(binding *api.Binding) error { - glog.V(2).Infof("Attempting to bind %v to %v", binding.PodID, binding.Host) + glog.V(2).Infof("Attempting to bind %v to %v", binding.Name, binding.Target.Name) ctx := api.WithNamespace(api.NewContext(), binding.Namespace) return b.Post().Namespace(api.NamespaceValue(ctx)).Resource("bindings").Body(binding).Do().Error() + // TODO: use Pods interface for binding once clusters are upgraded + // return b.Pods(binding.Namespace).Bind(binding) } type clock interface { diff --git a/plugin/pkg/scheduler/factory/factory_test.go b/plugin/pkg/scheduler/factory/factory_test.go index 9d3eff0a571..70b50b44c56 100644 --- a/plugin/pkg/scheduler/factory/factory_test.go +++ b/plugin/pkg/scheduler/factory/factory_test.go @@ -366,9 +366,11 @@ func TestBind(t *testing.T) { {binding: &api.Binding{ ObjectMeta: api.ObjectMeta{ Namespace: api.NamespaceDefault, + Name: "foo", + }, + Target: api.ObjectReference{ + Name: "foohost.kubernetes.mydomain.com", }, - PodID: "foo", - Host: "foohost.kubernetes.mydomain.com", }}, } diff --git a/plugin/pkg/scheduler/scheduler.go b/plugin/pkg/scheduler/scheduler.go index bf6d3e8b7df..a23205f18a7 100644 --- a/plugin/pkg/scheduler/scheduler.go +++ b/plugin/pkg/scheduler/scheduler.go @@ -80,9 +80,11 @@ func (s *Scheduler) scheduleOne() { return } b := &api.Binding{ - ObjectMeta: api.ObjectMeta{Namespace: pod.Namespace}, - PodID: pod.Name, - Host: dest, + ObjectMeta: api.ObjectMeta{Namespace: pod.Namespace, Name: pod.Name}, + Target: api.ObjectReference{ + Kind: "Node", + Name: dest, + }, } if err := s.config.Binder.Bind(b); err != nil { glog.V(1).Infof("Failed to bind pod: %v", err) diff --git a/plugin/pkg/scheduler/scheduler_test.go b/plugin/pkg/scheduler/scheduler_test.go index d47f3a61a80..5374b929cf9 100644 --- a/plugin/pkg/scheduler/scheduler_test.go +++ b/plugin/pkg/scheduler/scheduler_test.go @@ -25,6 +25,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/record" "github.com/GoogleCloudPlatform/kubernetes/pkg/scheduler" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) type fakeBinder struct { @@ -63,7 +64,7 @@ func TestScheduler(t *testing.T) { { sendPod: podWithID("foo"), algo: mockScheduler{"machine1", nil}, - expectBind: &api.Binding{PodID: "foo", Host: "machine1"}, + expectBind: &api.Binding{ObjectMeta: api.ObjectMeta{Name: "foo"}, Target: api.ObjectReference{Kind: "Node", Name: "machine1"}}, eventReason: "scheduled", }, { sendPod: podWithID("foo"), @@ -74,7 +75,7 @@ func TestScheduler(t *testing.T) { }, { sendPod: podWithID("foo"), algo: mockScheduler{"machine1", nil}, - expectBind: &api.Binding{PodID: "foo", Host: "machine1"}, + expectBind: &api.Binding{ObjectMeta: api.ObjectMeta{Name: "foo"}, Target: api.ObjectReference{Kind: "Node", Name: "machine1"}}, injectBindError: errB, expectError: errB, expectErrorPod: podWithID("foo"), @@ -120,7 +121,7 @@ func TestScheduler(t *testing.T) { t.Errorf("%v: error: wanted %v, got %v", i, e, a) } if e, a := item.expectBind, gotBinding; !reflect.DeepEqual(e, a) { - t.Errorf("%v: error: wanted %v, got %v", i, e, a) + t.Errorf("%v: error: %s", i, util.ObjectDiff(e, a)) } <-called events.Stop()