custom scheduler plugin

Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
This commit is contained in:
chaunceyjiang 2022-07-05 19:58:22 +08:00
parent 5ecb4ba1bb
commit fc600ae003
5 changed files with 122 additions and 1 deletions

View File

@ -1,6 +1,8 @@
package options
import (
"fmt"
"strings"
"time"
"github.com/spf13/pflag"
@ -9,6 +11,7 @@ import (
componentbaseconfig "k8s.io/component-base/config"
"github.com/karmada-io/karmada/pkg/features"
frameworkplugins "github.com/karmada-io/karmada/pkg/scheduler/framework/plugins"
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
"github.com/karmada-io/karmada/pkg/util"
)
@ -52,6 +55,12 @@ type Options struct {
// EnableEmptyWorkloadPropagation represents whether workload with 0 replicas could be propagated to member clusters.
EnableEmptyWorkloadPropagation bool
ProfileOpts profileflag.Options
// Plugins is the list of plugins to enable or disable
// '*' means "all enabled by default plugins"
// 'foo' means "enable 'foo'"
// '*,-foo' means "disable 'foo'"
Plugins []string
}
// NewOptions builds an default scheduler options.
@ -88,6 +97,8 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
fs.DurationVar(&o.SchedulerEstimatorTimeout.Duration, "scheduler-estimator-timeout", 3*time.Second, "Specifies the timeout period of calling the scheduler estimator service.")
fs.IntVar(&o.SchedulerEstimatorPort, "scheduler-estimator-port", defaultEstimatorPort, "The secure port on which to connect the accurate scheduler estimator.")
fs.BoolVar(&o.EnableEmptyWorkloadPropagation, "enable-empty-workload-propagation", false, "Enable workload with replicas 0 to be propagated to member clusters.")
fs.StringSliceVar(&o.Plugins, "plugins", []string{"*"},
fmt.Sprintf("A list of plugins to enable. '*' enables all build-in and customized plugins, 'foo' enables the plugin named 'foo', '*,-foo' disables the plugin named 'foo'.\nAll build-in plugins: %s.", strings.Join(frameworkplugins.NewInTreeRegistry().FactoryNames(), ",")))
features.FeatureGate.AddFlag(fs)
o.ProfileOpts.AddFlags(fs)
}

View File

@ -122,6 +122,7 @@ func run(opts *options.Options, stopChan <-chan struct{}, registryOptions ...Opt
scheduler.WithSchedulerEstimatorPort(opts.SchedulerEstimatorPort),
scheduler.WithSchedulerEstimatorTimeout(opts.SchedulerEstimatorTimeout),
scheduler.WithEnableEmptyWorkloadPropagation(opts.EnableEmptyWorkloadPropagation),
scheduler.WithEnableSchedulerPlugin(opts.Plugins),
)
if err != nil {
return fmt.Errorf("couldn't create scheduler: %w", err)

View File

@ -2,6 +2,10 @@ package runtime
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"
"github.com/karmada-io/karmada/pkg/scheduler/framework"
)
@ -43,3 +47,41 @@ func (r Registry) Merge(in Registry) error {
}
return nil
}
// FactoryNames returns all known plugin names
func (r Registry) FactoryNames() []string {
return sets.StringKeySet(r).List()
}
// Filter out the disabled plugin
func (r Registry) Filter(names []string) Registry {
var retRegistry = make(Registry)
for _, name := range names {
// --plugins=*
if name == "*" {
for factoryName, factory := range r {
klog.Infof("Enable Scheduler plugin %q", factoryName)
retRegistry[factoryName] = factory
}
break
}
}
for _, name := range names {
// --plugins=foo
if factory, ok := r[name]; ok {
retRegistry[name] = factory
klog.Infof("Enable Scheduler plugin %q", name)
continue
}
// --plugins=*,-foo
// --plugins=-foo,*
if strings.HasPrefix(name, "-") && len(retRegistry) > 0 {
factoryName := strings.TrimLeft(name, "-")
delete(retRegistry, factoryName)
klog.Warningf("%q is disabled", factoryName)
}
}
return retRegistry
}

View File

@ -0,0 +1,58 @@
package runtime
import (
"reflect"
"testing"
"github.com/karmada-io/karmada/pkg/scheduler/framework"
)
func TestRegistry_Filter(t *testing.T) {
plugins := []string{"bar", "foo", "fuzz"}
var r = make(Registry)
for _, name := range plugins {
_ = r.Register(name, func() (framework.Plugin, error) {
return nil, nil
})
}
tests := []struct {
name string
curPlugins []string
r Registry
expectedPlugins []string
}{
{
name: "enable foo",
curPlugins: []string{"foo"}, // --plugins=foo
r: r,
expectedPlugins: []string{"foo"},
},
{
name: "enable all",
curPlugins: []string{"*"}, // --plugins=*
r: r,
expectedPlugins: []string{"bar", "foo", "fuzz"},
},
{
name: "disable foo",
curPlugins: []string{"*", "-foo"}, // --plugins=*,-foo
r: r,
expectedPlugins: []string{"bar", "fuzz"},
},
{
name: "disable foo",
curPlugins: []string{"-foo", "*"}, // --plugins=-foo,*
r: r,
expectedPlugins: []string{"bar", "fuzz"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.r.Filter(tt.curPlugins); !reflect.DeepEqual(got.FactoryNames(), tt.expectedPlugins) {
t.Errorf("Filter() = %v, want %v", got.FactoryNames(), tt.expectedPlugins)
}
})
}
}

View File

@ -102,6 +102,8 @@ type schedulerOptions struct {
enableEmptyWorkloadPropagation bool
// outOfTreeRegistry represents the registry of out-of-tree plugins
outOfTreeRegistry runtime.Registry
// plugins is the list of plugins to enable or disable
plugins []string
}
// Option configures a Scheduler
@ -142,6 +144,13 @@ func WithEnableEmptyWorkloadPropagation(enableEmptyWorkloadPropagation bool) Opt
}
}
// WithEnableSchedulerPlugin sets the scheduler-plugin for scheduler
func WithEnableSchedulerPlugin(plugins []string) Option {
return func(o *schedulerOptions) {
o.plugins = plugins
}
}
// WithOutOfTreeRegistry sets the registry for out-of-tree plugins. Those plugins
// will be appended to the default in-tree registry.
func WithOutOfTreeRegistry(registry runtime.Registry) Option {
@ -166,11 +175,11 @@ func NewScheduler(dynamicClient dynamic.Interface, karmadaClient karmadaclientse
opt(&options)
}
// TODO(kerthcet): make plugins configurable via config file
registry := frameworkplugins.NewInTreeRegistry()
if err := registry.Merge(options.outOfTreeRegistry); err != nil {
return nil, err
}
registry = registry.Filter(options.plugins)
algorithm, err := core.NewGenericScheduler(schedulerCache, registry)
if err != nil {
return nil, err