From 7d605467dc0926f07fd9825e754589d4c026b3ce Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Wed, 23 Jul 2014 16:09:37 -0700 Subject: [PATCH] New scheduler API This commit adds a Binding object. The idea is that schedulers can write these to cause pods to be asssigned to hosts. I'll provide an implementation along with a rudimentary scheduler plugin. This continues k8s' tradition of phrasing all APIs as RESTful handlers. --- pkg/api/helper.go | 2 + pkg/api/helper_test.go | 1 + pkg/api/types.go | 7 +++ pkg/api/v1beta1/types.go | 7 +++ pkg/master/master.go | 1 + pkg/registry/bindingstorage.go | 74 +++++++++++++++++++++++++++++ pkg/registry/bindingstorage_test.go | 45 ++++++++++++++++++ 7 files changed, 137 insertions(+) create mode 100644 pkg/registry/bindingstorage.go create mode 100644 pkg/registry/bindingstorage_test.go diff --git a/pkg/api/helper.go b/pkg/api/helper.go index 7b7174b9616..a25cb087ae2 100644 --- a/pkg/api/helper.go +++ b/pkg/api/helper.go @@ -64,6 +64,7 @@ func init() { ServerOp{}, ContainerManifestList{}, Endpoints{}, + Binding{}, ) AddKnownTypes("v1beta1", v1beta1.PodList{}, @@ -79,6 +80,7 @@ func init() { v1beta1.ServerOp{}, v1beta1.ContainerManifestList{}, v1beta1.Endpoints{}, + v1beta1.Binding{}, ) // TODO: when we get more of this stuff, move to its own file. This is not a diff --git a/pkg/api/helper_test.go b/pkg/api/helper_test.go index 37eab722af3..798092e80e4 100644 --- a/pkg/api/helper_test.go +++ b/pkg/api/helper_test.go @@ -152,6 +152,7 @@ func TestTypes(t *testing.T) { &ServerOp{}, &ContainerManifestList{}, &Endpoints{}, + &Binding{}, } for _, item := range table { // Try a few times, since runTest uses random values. diff --git a/pkg/api/types.go b/pkg/api/types.go index 3c7b2df2c86..0ce643d52be 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -335,6 +335,13 @@ type MinionList struct { Items []Minion `json:"minions,omitempty" yaml:"minions,omitempty"` } +// Binding is written by a scheduler to cause a pod to be bound to a host. +type Binding struct { + JSONBase `json:",inline" yaml:",inline"` + PodID string `json:"podID" yaml:"podID"` + Host string `json:"host" yaml:"host"` +} + // 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. diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 16346bccad2..94a8c17e929 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -338,6 +338,13 @@ type MinionList struct { Items []Minion `json:"minions,omitempty" yaml:"minions,omitempty"` } +// Binding is written by a scheduler to cause a pod to be bound to a host. +type Binding struct { + JSONBase `json:",inline" yaml:",inline"` + PodID string `json:"podID" yaml:"podID"` + Host string `json:"host" yaml:"host"` +} + // 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. diff --git a/pkg/master/master.go b/pkg/master/master.go index 24e2df6a5a6..a01070ad418 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -122,6 +122,7 @@ func (m *Master) init(cloud cloudprovider.Interface, podInfoGetter client.PodInf "replicationControllers": registry.NewControllerRegistryStorage(m.controllerRegistry, m.podRegistry), "services": registry.MakeServiceRegistryStorage(m.serviceRegistry, cloud, m.minionRegistry), "minions": registry.MakeMinionRegistryStorage(m.minionRegistry), + "bindings": registry.MakeBindingStorage(m.podRegistry), } } diff --git a/pkg/registry/bindingstorage.go b/pkg/registry/bindingstorage.go new file mode 100644 index 00000000000..74900fdfbca --- /dev/null +++ b/pkg/registry/bindingstorage.go @@ -0,0 +1,74 @@ +/* +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 registry + +import ( + "fmt" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" + "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" +) + +// BindingStorage implements the RESTStorage interface. 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 BindingStorage struct { + podRegistry PodRegistry +} + +// MakeBindingStorage makes a new BindingStorage backed by the given PodRegistry. +func MakeBindingStorage(podRegistry PodRegistry) *BindingStorage { + return &BindingStorage{ + podRegistry: podRegistry, + } +} + +// List returns an error because bindings are write-only objects. +func (*BindingStorage) List(selector labels.Selector) (interface{}, error) { + return nil, apiserver.NewNotFoundErr("binding", "list") +} + +// Get returns an error because bindings are write-only objects. +func (*BindingStorage) Get(id string) (interface{}, error) { + return nil, apiserver.NewNotFoundErr("binding", id) +} + +// Delete returns an error because bindings are write-only objects. +func (*BindingStorage) Delete(id string) (<-chan interface{}, error) { + return nil, apiserver.NewNotFoundErr("binding", id) +} + +// New returns a new binding object fit for having data unmarshalled into it. +func (*BindingStorage) New() interface{} { + return &api.Binding{} +} + +// Create attempts to make the assignment indicated by the binding it recieves. +func (b *BindingStorage) Create(obj interface{}) (<-chan interface{}, error) { + binding, ok := obj.(*api.Binding) + if !ok { + return nil, fmt.Errorf("incorrect type: %#v", obj) + } + _ = binding + return nil, fmt.Errorf("Implementation coming in the future. Storage layer can't easily support this yet.") +} + +// Update returns an error-- this object may not be updated. +func (b *BindingStorage) Update(obj interface{}) (<-chan interface{}, error) { + return nil, fmt.Errorf("Bindings may not be changed.") +} diff --git a/pkg/registry/bindingstorage_test.go b/pkg/registry/bindingstorage_test.go new file mode 100644 index 00000000000..a4371e8bfed --- /dev/null +++ b/pkg/registry/bindingstorage_test.go @@ -0,0 +1,45 @@ +/* +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 registry + +import ( + "reflect" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +func TestBindingStorage_Extract(t *testing.T) { + b := &BindingStorage{} + + binding := &api.Binding{ + PodID: "foo", + Host: "bar", + } + body, err := api.Encode(binding) + if err != nil { + t.Fatalf("Unexpected encode error %v", err) + } + obj := b.New() + err = api.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) + } +}