From 7221589dde6c77a7f79a86f2e32756fc4a177cc8 Mon Sep 17 00:00:00 2001 From: "Bobby (Babak) Salamat" Date: Thu, 8 Nov 2018 18:06:15 -0800 Subject: [PATCH] Add "reserve" and "prebind" plugin interfaces for the scheduling framework. --- pkg/scheduler/plugins/examples/multipoint.go | 62 ++++++++++++++++ pkg/scheduler/plugins/examples/prebind.go | 48 ++++++++++++ pkg/scheduler/plugins/examples/stateful.go | 68 +++++++++++++++++ pkg/scheduler/plugins/registrar.go | 77 ++++++++++++++++++++ pkg/scheduler/plugins/v1alpha1/interface.go | 63 ++++++++++++++++ 5 files changed, 318 insertions(+) create mode 100644 pkg/scheduler/plugins/examples/multipoint.go create mode 100644 pkg/scheduler/plugins/examples/prebind.go create mode 100644 pkg/scheduler/plugins/examples/stateful.go create mode 100644 pkg/scheduler/plugins/registrar.go create mode 100644 pkg/scheduler/plugins/v1alpha1/interface.go diff --git a/pkg/scheduler/plugins/examples/multipoint.go b/pkg/scheduler/plugins/examples/multipoint.go new file mode 100644 index 00000000000..3b82f219802 --- /dev/null +++ b/pkg/scheduler/plugins/examples/multipoint.go @@ -0,0 +1,62 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 examples + +import ( + "fmt" + + "k8s.io/api/core/v1" + plugins "k8s.io/kubernetes/pkg/scheduler/plugins/v1alpha1" +) + +// MultipointCommunicatingPlugin is an example of a plugin that implements two +// extension points. It communicates through pluginContext with another function. +type MultipointCommunicatingPlugin struct{} + +var _ = plugins.ReservePlugin(MultipointCommunicatingPlugin{}) + +// Name returns name of the plugin. It is used in logs, etc. +func (mc MultipointCommunicatingPlugin) Name() string { + return "multipoint-communicating-plugin" +} + +// Reserve is the functions invoked by the framework at "reserve" extension point. +func (mc MultipointCommunicatingPlugin) Reserve(ps plugins.PluginSet, pod *v1.Pod, nodeName string) error { + if pod == nil { + return fmt.Errorf("pod cannot be nil") + } + if pod.Name == "my-test-pod" { + ps.Data().Ctx.SyncWrite(plugins.ContextKey(pod.Name), "never bind") + } + return nil +} + +// Prebind is the functions invoked by the framework at "prebind" extension point. +func (mc MultipointCommunicatingPlugin) Prebind(ps plugins.PluginSet, pod *v1.Pod, nodeName string) (bool, error) { + if pod == nil { + return false, fmt.Errorf("pod cannot be nil") + } + if v, e := ps.Data().Ctx.SyncRead(plugins.ContextKey(pod.Name)); e == nil && v == "never bind" { + return false, nil + } + return true, nil +} + +// NewMultipointCommunicatingPlugin initializes a new plugin and returns it. +func NewMultipointCommunicatingPlugin() *MultipointCommunicatingPlugin { + return &MultipointCommunicatingPlugin{} +} diff --git a/pkg/scheduler/plugins/examples/prebind.go b/pkg/scheduler/plugins/examples/prebind.go new file mode 100644 index 00000000000..13e71eb0bd3 --- /dev/null +++ b/pkg/scheduler/plugins/examples/prebind.go @@ -0,0 +1,48 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 examples + +import ( + "fmt" + + "k8s.io/api/core/v1" + plugins "k8s.io/kubernetes/pkg/scheduler/plugins/v1alpha1" +) + +// StatelessPrebindExample is an example of a simple plugin that has no state +// and implements only one hook for prebind. +type StatelessPrebindExample struct{} + +var _ = plugins.PrebindPlugin(StatelessPrebindExample{}) + +// Name returns name of the plugin. It is used in logs, etc. +func (sr StatelessPrebindExample) Name() string { + return "stateless-prebind-plugin-example" +} + +// Prebind is the functions invoked by the framework at "prebind" extension point. +func (sr StatelessPrebindExample) Prebind(ps plugins.PluginSet, pod *v1.Pod, nodeName string) (bool, error) { + if pod == nil { + return false, fmt.Errorf("pod cannot be nil") + } + return true, nil +} + +// NewStatelessPrebindExample initializes a new plugin and returns it. +func NewStatelessPrebindExample() *StatelessPrebindExample { + return &StatelessPrebindExample{} +} diff --git a/pkg/scheduler/plugins/examples/stateful.go b/pkg/scheduler/plugins/examples/stateful.go new file mode 100644 index 00000000000..2b8b210305e --- /dev/null +++ b/pkg/scheduler/plugins/examples/stateful.go @@ -0,0 +1,68 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 examples + +import ( + "fmt" + "k8s.io/klog" + + "k8s.io/api/core/v1" + plugins "k8s.io/kubernetes/pkg/scheduler/plugins/v1alpha1" +) + +// StatefulMultipointExample is an example plugin that is executed at multiple extension points. +// This plugin is stateful. It receives arguments at initialization (NewMultipointPlugin) +// and changes its state when it is executed. +type StatefulMultipointExample struct { + mpState map[int]string + numRuns int +} + +var _ = plugins.ReservePlugin(&StatefulMultipointExample{}) +var _ = plugins.PrebindPlugin(&StatefulMultipointExample{}) + +// Name returns name of the plugin. It is used in logs, etc. +func (mp *StatefulMultipointExample) Name() string { + return "multipoint-plugin-example" +} + +// Reserve is the functions invoked by the framework at "reserve" extension point. +func (mp *StatefulMultipointExample) Reserve(ps plugins.PluginSet, pod *v1.Pod, nodeName string) error { + mp.numRuns++ + return nil +} + +// Prebind is the functions invoked by the framework at "prebind" extension point. +func (mp *StatefulMultipointExample) Prebind(ps plugins.PluginSet, pod *v1.Pod, nodeName string) (bool, error) { + mp.numRuns++ + if pod == nil { + return false, fmt.Errorf("pod must not be nil") + } + return true, nil +} + +// NewStatefulMultipointExample initializes a new plugin and returns it. +func NewStatefulMultipointExample(initState ...interface{}) *StatefulMultipointExample { + if len(initState) == 0 { + klog.Error("StatefulMultipointExample needs exactly one argument for initialization") + return nil + } + mp := StatefulMultipointExample{ + mpState: initState[0].(map[int]string), + } + return &mp +} diff --git a/pkg/scheduler/plugins/registrar.go b/pkg/scheduler/plugins/registrar.go new file mode 100644 index 00000000000..4eab86ecc15 --- /dev/null +++ b/pkg/scheduler/plugins/registrar.go @@ -0,0 +1,77 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 plugins + +import ( + "k8s.io/kubernetes/pkg/scheduler/internal/cache" + plugins "k8s.io/kubernetes/pkg/scheduler/plugins/v1alpha1" +) + +// DefaultPluginSet is the default plugin registrar used by the default scheduler. +type DefaultPluginSet struct { + data *plugins.PluginData + reservePlugins []plugins.ReservePlugin + prebindPlugins []plugins.PrebindPlugin +} + +var _ = plugins.PluginSet(&DefaultPluginSet{}) + +// ReservePlugins returns a slice of default reserve plugins. +func (r *DefaultPluginSet) ReservePlugins() []plugins.ReservePlugin { + return r.reservePlugins +} + +// PrebindPlugins returns a slice of default prebind plugins. +func (r *DefaultPluginSet) PrebindPlugins() []plugins.PrebindPlugin { + return r.prebindPlugins +} + +// Data returns a pointer to PluginData. +func (r *DefaultPluginSet) Data() *plugins.PluginData { + return r.data +} + +// NewDefaultPluginSet initializes default plugin set and returns its pointer. +func NewDefaultPluginSet(ctx *plugins.PluginContext, schedulerCache *cache.Cache) *DefaultPluginSet { + defaultRegistrar := DefaultPluginSet{ + data: &plugins.PluginData{ + Ctx: ctx, + SchedulerCache: schedulerCache, + }, + } + defaultRegistrar.registerReservePlugins() + defaultRegistrar.registerPrebindPlugins() + return &defaultRegistrar +} + +func (r DefaultPluginSet) registerReservePlugins() { + r.reservePlugins = []plugins.ReservePlugin{ + // Init functions of all reserve plugins go here. They are called in the + // same order that they are registered. + // Example: + // examples.NewStatefulMultipointExample(map[int]string{1: "test1", 2: "test2"}), + } +} + +func (r DefaultPluginSet) registerPrebindPlugins() { + r.prebindPlugins = []plugins.PrebindPlugin{ + // Init functions of all prebind plugins go here. They are called in the + // same order that they are registered. + // Example: + // examples.NewStatelessPrebindExample(), + } +} diff --git a/pkg/scheduler/plugins/v1alpha1/interface.go b/pkg/scheduler/plugins/v1alpha1/interface.go new file mode 100644 index 00000000000..0d0c90b43e6 --- /dev/null +++ b/pkg/scheduler/plugins/v1alpha1/interface.go @@ -0,0 +1,63 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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. +*/ + +// This file defines the scheduling framework plugin interfaces. + +package v1alpha1 + +import ( + "k8s.io/api/core/v1" + "k8s.io/kubernetes/pkg/scheduler/internal/cache" +) + +// PluginData carries information that plugins may need. +type PluginData struct { + Ctx *PluginContext + SchedulerCache *cache.Cache + // We may want to add the scheduling queue here too. +} + +// Plugin is the parent type for all the scheduling framework plugins. +type Plugin interface { + Name() string +} + +// ReservePlugin is an interface for Reserve plugins. These plugins are called +// at the reservation point, AKA "assume". These are meant to updated the state +// of the plugin. They do not return any value (other than error). +type ReservePlugin interface { + Plugin + // Reserve is called by the scheduling framework when the scheduler cache is + // updated. + Reserve(ps PluginSet, p *v1.Pod, nodeName string) error +} + +// PrebindPlugin is an interface that must be implemented by "prebind" plugins. +// These plugins are called before a pod being scheduled +type PrebindPlugin interface { + Plugin + // Prebind is called before binding a pod. All prebind plugins must return + // or the pod will not be sent for binding. + Prebind(ps PluginSet, p *v1.Pod, nodeName string) (bool, error) +} + +// PluginSet registers plugins used by the scheduling framework. +// The plugins registered are called at specified points in an scheduling cycle. +type PluginSet interface { + Data() *PluginData + ReservePlugins() []ReservePlugin + PrebindPlugins() []PrebindPlugin +}