161 lines
5.2 KiB
Go
161 lines
5.2 KiB
Go
package framework
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
|
|
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
|
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
|
)
|
|
|
|
const (
|
|
// MinClusterScore is the minimum score a Score plugin is expected to return.
|
|
MinClusterScore int64 = 0
|
|
|
|
// MaxClusterScore is the maximum score a Score plugin is expected to return.
|
|
MaxClusterScore int64 = 100
|
|
)
|
|
|
|
// Framework manages the set of plugins in use by the scheduling framework.
|
|
// Configured plugins are called at specified points in a scheduling context.
|
|
type Framework interface {
|
|
|
|
// RunFilterPlugins runs the set of configured Filter plugins for resources on
|
|
// the given cluster.
|
|
RunFilterPlugins(ctx context.Context, placement *policyv1alpha1.Placement, bindingSpec *workv1alpha2.ResourceBindingSpec, clusterv1alpha1 *clusterv1alpha1.Cluster) *Result
|
|
|
|
// RunScorePlugins runs the set of configured Score plugins, it returns a map of plugin name to cores
|
|
RunScorePlugins(ctx context.Context, placement *policyv1alpha1.Placement, spec *workv1alpha2.ResourceBindingSpec, clusters []*clusterv1alpha1.Cluster) (PluginToClusterScores, error)
|
|
}
|
|
|
|
// Plugin is the parent type for all the scheduling framework plugins.
|
|
type Plugin interface {
|
|
Name() string
|
|
}
|
|
|
|
// FilterPlugin is an interface for filter plugins. These filters are used to filter out clusters
|
|
// that are not fit for the resource.
|
|
type FilterPlugin interface {
|
|
Plugin
|
|
// Filter is called by the scheduling framework.
|
|
Filter(ctx context.Context, placement *policyv1alpha1.Placement, bindingSpec *workv1alpha2.ResourceBindingSpec, clusterv1alpha1 *clusterv1alpha1.Cluster) *Result
|
|
}
|
|
|
|
// Result indicates the result of running a plugin. It consists of a code, a
|
|
// message and (optionally) an error. When the status code is not `Success`,
|
|
// the reasons should explain why.
|
|
type Result struct {
|
|
code Code
|
|
reasons []string
|
|
err error
|
|
}
|
|
|
|
// Code is the Status code/type which is returned from plugins.
|
|
type Code int
|
|
|
|
// These are predefined codes used in a Status.
|
|
const (
|
|
// Success means that plugin ran correctly and found resource schedulable.
|
|
// NOTE: A nil status is also considered as "Success".
|
|
Success Code = iota
|
|
// Unschedulable is used when a plugin finds the resource unschedulable.
|
|
// The accompanying status message should explain why the it is unschedulable.
|
|
Unschedulable
|
|
// Error is used for internal plugin errors, unexpected input, etc.
|
|
Error
|
|
)
|
|
|
|
// NewResult makes a result out of the given arguments and returns its pointer.
|
|
func NewResult(code Code, reasons ...string) *Result {
|
|
s := &Result{
|
|
code: code,
|
|
reasons: reasons,
|
|
}
|
|
if code == Error {
|
|
s.err = errors.New(strings.Join(reasons, ","))
|
|
}
|
|
return s
|
|
}
|
|
|
|
// PluginToResult maps plugin name to Result.
|
|
type PluginToResult map[string]*Result
|
|
|
|
// Merge merges the statuses in the map into one. The resulting status code have the following
|
|
// precedence: Error, Unschedulable.
|
|
func (p PluginToResult) Merge() *Result {
|
|
if len(p) == 0 {
|
|
return nil
|
|
}
|
|
|
|
finalStatus := NewResult(Success)
|
|
var hasUnschedulable bool
|
|
for _, s := range p {
|
|
if s.code == Error {
|
|
finalStatus.err = s.err
|
|
} else if s.code == Unschedulable {
|
|
hasUnschedulable = true
|
|
}
|
|
finalStatus.code = s.code
|
|
finalStatus.reasons = append(finalStatus.reasons, s.reasons...)
|
|
}
|
|
|
|
if finalStatus.err != nil {
|
|
finalStatus.code = Error
|
|
} else if hasUnschedulable {
|
|
finalStatus.code = Unschedulable
|
|
}
|
|
return finalStatus
|
|
}
|
|
|
|
// IsSuccess returns true if and only if "Result" is nil or Code is "Success".
|
|
func (s *Result) IsSuccess() bool {
|
|
return s == nil || s.code == Success
|
|
}
|
|
|
|
// AsError returns nil if the Result is a success; otherwise returns an "error" object
|
|
// with a concatenated message on reasons of the Result.
|
|
func (s *Result) AsError() error {
|
|
if s.IsSuccess() {
|
|
return nil
|
|
}
|
|
if s.err != nil {
|
|
return s.err
|
|
}
|
|
return errors.New(strings.Join(s.reasons, ", "))
|
|
}
|
|
|
|
// ScorePlugin is an interface that must be implemented by "Score" plugins to rank
|
|
// clusters that passed the filtering phase.
|
|
type ScorePlugin interface {
|
|
Plugin
|
|
// Score is called on each filtered cluster. It must return success and an integer
|
|
// indicating the rank of the cluster. All scoring plugins must return success or
|
|
// the resource will be rejected.
|
|
Score(ctx context.Context, placement *policyv1alpha1.Placement, spec *workv1alpha2.ResourceBindingSpec, cluster *clusterv1alpha1.Cluster) (int64, *Result)
|
|
|
|
// ScoreExtensions returns a ScoreExtensions interface
|
|
// if it implements one, or nil if does not.
|
|
ScoreExtensions() ScoreExtensions
|
|
}
|
|
|
|
// ScoreExtensions is an interface for Score extended functionality.
|
|
type ScoreExtensions interface {
|
|
// NormalizeScore is called for all cluster scores produced
|
|
// by the same plugin's "Score"
|
|
NormalizeScore(ctx context.Context, scores ClusterScoreList) *Result
|
|
}
|
|
|
|
// ClusterScore represent the cluster score.
|
|
type ClusterScore struct {
|
|
Cluster *clusterv1alpha1.Cluster
|
|
Score int64
|
|
}
|
|
|
|
// ClusterScoreList declares a list of clusters and their scores.
|
|
type ClusterScoreList []ClusterScore
|
|
|
|
// PluginToClusterScores declares a map from plugin name to its ClusterScoreList.
|
|
type PluginToClusterScores map[string]ClusterScoreList
|