mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-08 11:38:15 +00:00
Add a subbindings resource as /pods/{name}/binding
Allows POST to create a binding as a child. Also refactors internal and v1beta3 Binding to be more generic (so that other resources can support Bindings).
This commit is contained in:
parent
227a1d306d
commit
dfc19185f5
@ -101,6 +101,10 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
|
|||||||
j.Spec = api.PodSpec{}
|
j.Spec = api.PodSpec{}
|
||||||
c.Fuzz(&j.Spec)
|
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) {
|
func(j *api.ReplicationControllerSpec, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(j) // fuzz self without calling this function again
|
c.FuzzNoCustom(j) // fuzz self without calling this function again
|
||||||
j.TemplateRef = nil // this is required for round trip
|
j.TemplateRef = nil // this is required for round trip
|
||||||
|
@ -918,13 +918,14 @@ type NamespaceList struct {
|
|||||||
Items []Namespace `json:"items"`
|
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 {
|
type Binding struct {
|
||||||
TypeMeta `json:",inline"`
|
TypeMeta `json:",inline"`
|
||||||
|
// ObjectMeta describes the object that is being bound.
|
||||||
ObjectMeta `json:"metadata,omitempty"`
|
ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
PodID string `json:"podID"`
|
// Target is the object to bind to.
|
||||||
Host string `json:"host"`
|
Target ObjectReference `json:"target"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status is a return value for calls that don't return other objects.
|
// Status is a return value for calls that don't return other objects.
|
||||||
|
@ -1324,6 +1324,24 @@ func init() {
|
|||||||
|
|
||||||
return nil
|
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 err != nil {
|
||||||
// If one of the conversion functions is malformed, detect it immediately.
|
// If one of the conversion functions is malformed, detect it immediately.
|
||||||
|
@ -1240,6 +1240,24 @@ func init() {
|
|||||||
|
|
||||||
return nil
|
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 err != nil {
|
||||||
// If one of the conversion functions is malformed, detect it immediately.
|
// If one of the conversion functions is malformed, detect it immediately.
|
||||||
|
@ -940,16 +940,14 @@ type NamespaceList struct {
|
|||||||
Items []Namespace `json:"items" description:"items is the list of Namespace objects in the list"`
|
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
|
// Binding ties one object to another - for example, a pod is bound to a node by a scheduler.
|
||||||
// required for Bindings.
|
|
||||||
type Binding struct {
|
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"`
|
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.
|
// Target is the object to bind to.
|
||||||
PodID string `json:"podID" description:"name of the pod to be bound to a node"`
|
Target ObjectReference `json:"target" description:"an object to bind to"`
|
||||||
// Host is the name of a node to bind to.
|
|
||||||
Host string `json:"host" description:"name of the node to bind to"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status is a return value for calls that don't return other objects.
|
// Status is a return value for calls that don't return other objects.
|
||||||
|
@ -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})
|
c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "update-pod", Value: pod.Name})
|
||||||
return &api.Pod{}, nil
|
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
|
||||||
|
}
|
||||||
|
@ -36,6 +36,8 @@ type PodInterface interface {
|
|||||||
Delete(name string) error
|
Delete(name string) error
|
||||||
Create(pod *api.Pod) (*api.Pod, error)
|
Create(pod *api.Pod) (*api.Pod, error)
|
||||||
Update(pod *api.Pod) (*api.Pod, error)
|
Update(pod *api.Pod) (*api.Pod, error)
|
||||||
|
|
||||||
|
Bind(binding *api.Binding) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// pods implements PodsNamespacer interface
|
// 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)
|
err = c.r.Put().Namespace(c.ns).Resource("pods").Name(pod.Name).Body(pod).Do().Into(result)
|
||||||
return
|
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()
|
||||||
|
}
|
||||||
|
@ -406,6 +406,7 @@ func (m *Master) init(c *Config) {
|
|||||||
m.storage = map[string]apiserver.RESTStorage{
|
m.storage = map[string]apiserver.RESTStorage{
|
||||||
"pods": podStorage,
|
"pods": podStorage,
|
||||||
"pods/status": podStatusStorage,
|
"pods/status": podStatusStorage,
|
||||||
|
"pods/binding": bindingStorage,
|
||||||
"bindings": bindingStorage,
|
"bindings": bindingStorage,
|
||||||
|
|
||||||
"replicationControllers": controller.NewREST(registry, podRegistry),
|
"replicationControllers": controller.NewREST(registry, podRegistry),
|
||||||
|
@ -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
|
|
@ -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)
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,6 +20,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd"
|
etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/constraint"
|
"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.
|
// 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) {
|
func (r *BindingREST) Create(ctx api.Context, obj runtime.Object) (out runtime.Object, err error) {
|
||||||
binding := obj.(*api.Binding)
|
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", "")
|
err = etcderr.InterpretCreateError(err, "binding", "")
|
||||||
out = &api.Status{Status: api.StatusSuccess}
|
out = &api.Status{Status: api.StatusSuccess}
|
||||||
return
|
return
|
||||||
|
@ -817,7 +817,10 @@ func TestEtcdCreate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Suddenly, a wild scheduler appears:
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -865,7 +868,10 @@ func TestEtcdCreateBindingNoPod(t *testing.T) {
|
|||||||
// - Create (apiserver)
|
// - Create (apiserver)
|
||||||
// - Schedule (scheduler)
|
// - Schedule (scheduler)
|
||||||
// - Delete (apiserver)
|
// - 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 {
|
if err == nil {
|
||||||
t.Fatalf("Expected not-found-error but got nothing")
|
t.Fatalf("Expected not-found-error but got nothing")
|
||||||
}
|
}
|
||||||
@ -935,7 +941,10 @@ func TestEtcdCreateWithContainersError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Suddenly, a wild scheduler appears:
|
// 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) {
|
if !errors.IsAlreadyExists(err) {
|
||||||
t.Fatalf("Unexpected error returned: %#v", err)
|
t.Fatalf("Unexpected error returned: %#v", err)
|
||||||
}
|
}
|
||||||
@ -973,7 +982,10 @@ func TestEtcdCreateWithContainersNotFound(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Suddenly, a wild scheduler appears:
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -1025,7 +1037,10 @@ func TestEtcdCreateWithExistingContainers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Suddenly, a wild scheduler appears:
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
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) {
|
func TestEtcdUpdateNotFound(t *testing.T) {
|
||||||
registry, _, _, fakeClient, _ := newStorage(t)
|
registry, _, _, fakeClient, _ := newStorage(t)
|
||||||
ctx := api.NewDefaultContext()
|
ctx := api.NewDefaultContext()
|
||||||
|
@ -287,9 +287,11 @@ type binder struct {
|
|||||||
|
|
||||||
// Bind just does a POST binding RPC.
|
// Bind just does a POST binding RPC.
|
||||||
func (b *binder) Bind(binding *api.Binding) error {
|
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)
|
ctx := api.WithNamespace(api.NewContext(), binding.Namespace)
|
||||||
return b.Post().Namespace(api.NamespaceValue(ctx)).Resource("bindings").Body(binding).Do().Error()
|
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 {
|
type clock interface {
|
||||||
|
@ -366,9 +366,11 @@ func TestBind(t *testing.T) {
|
|||||||
{binding: &api.Binding{
|
{binding: &api.Binding{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
Namespace: api.NamespaceDefault,
|
Namespace: api.NamespaceDefault,
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
Target: api.ObjectReference{
|
||||||
|
Name: "foohost.kubernetes.mydomain.com",
|
||||||
},
|
},
|
||||||
PodID: "foo",
|
|
||||||
Host: "foohost.kubernetes.mydomain.com",
|
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,9 +80,11 @@ func (s *Scheduler) scheduleOne() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
b := &api.Binding{
|
b := &api.Binding{
|
||||||
ObjectMeta: api.ObjectMeta{Namespace: pod.Namespace},
|
ObjectMeta: api.ObjectMeta{Namespace: pod.Namespace, Name: pod.Name},
|
||||||
PodID: pod.Name,
|
Target: api.ObjectReference{
|
||||||
Host: dest,
|
Kind: "Node",
|
||||||
|
Name: dest,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if err := s.config.Binder.Bind(b); err != nil {
|
if err := s.config.Binder.Bind(b); err != nil {
|
||||||
glog.V(1).Infof("Failed to bind pod: %v", err)
|
glog.V(1).Infof("Failed to bind pod: %v", err)
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/scheduler"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/scheduler"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeBinder struct {
|
type fakeBinder struct {
|
||||||
@ -63,7 +64,7 @@ func TestScheduler(t *testing.T) {
|
|||||||
{
|
{
|
||||||
sendPod: podWithID("foo"),
|
sendPod: podWithID("foo"),
|
||||||
algo: mockScheduler{"machine1", nil},
|
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",
|
eventReason: "scheduled",
|
||||||
}, {
|
}, {
|
||||||
sendPod: podWithID("foo"),
|
sendPod: podWithID("foo"),
|
||||||
@ -74,7 +75,7 @@ func TestScheduler(t *testing.T) {
|
|||||||
}, {
|
}, {
|
||||||
sendPod: podWithID("foo"),
|
sendPod: podWithID("foo"),
|
||||||
algo: mockScheduler{"machine1", nil},
|
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,
|
injectBindError: errB,
|
||||||
expectError: errB,
|
expectError: errB,
|
||||||
expectErrorPod: podWithID("foo"),
|
expectErrorPod: podWithID("foo"),
|
||||||
@ -120,7 +121,7 @@ func TestScheduler(t *testing.T) {
|
|||||||
t.Errorf("%v: error: wanted %v, got %v", i, e, a)
|
t.Errorf("%v: error: wanted %v, got %v", i, e, a)
|
||||||
}
|
}
|
||||||
if e, a := item.expectBind, gotBinding; !reflect.DeepEqual(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
|
<-called
|
||||||
events.Stop()
|
events.Stop()
|
||||||
|
Loading…
Reference in New Issue
Block a user