1
0
mirror of https://github.com/kubernetes-sigs/descheduler.git synced 2026-01-26 13:29:11 +01:00

Detect individual extension points from plugin types

- Populate extension points automatically from plugin types
- Make a list of enabled extension points based on a profile
  configuration
- Populate filter and pre-eviction filter handles from their
  corresponding extension points
This commit is contained in:
Jan Chaloupka
2023-03-30 10:56:19 +02:00
parent 3897ff069f
commit 3510aba01d
6 changed files with 901 additions and 72 deletions

View File

@@ -42,6 +42,9 @@ var (
_ frameworktypes.EvictorPlugin = &FakePlugin{}
_ frameworktypes.DeschedulePlugin = &FakePlugin{}
_ frameworktypes.BalancePlugin = &FakePlugin{}
_ frameworktypes.EvictorPlugin = &FakeFilterPlugin{}
_ frameworktypes.DeschedulePlugin = &FakeDeschedulePlugin{}
_ frameworktypes.BalancePlugin = &FakeBalancePlugin{}
)
// FakePlugin is a configurable plugin used for testing
@@ -84,6 +87,10 @@ func New(args runtime.Object, handle frameworktypes.Handle) (frameworktypes.Plug
return ev, nil
}
func (c *FakePlugin) AddReactor(extensionPoint string, reaction ReactionFunc) {
c.ReactionChain = append(c.ReactionChain, &SimpleReactor{ExtensionPoint: extensionPoint, Reaction: reaction})
}
// Name retrieves the plugin name
func (d *FakePlugin) Name() string {
return d.PluginName
@@ -103,7 +110,7 @@ func (d *FakePlugin) handleAction(action Action) *frameworktypes.Status {
if !reactor.Handles(actionCopy) {
continue
}
handled, err := reactor.React(actionCopy)
handled, _, err := reactor.React(actionCopy)
if !handled {
continue
}
@@ -136,3 +143,268 @@ func (d *FakePlugin) Balance(ctx context.Context, nodes []*v1.Node) *frameworkty
nodes: nodes,
})
}
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// FakeDeschedulePluginArgs holds arguments used to configure FakeDeschedulePlugin plugin.
type FakeDeschedulePluginArgs struct {
metav1.TypeMeta `json:",inline"`
}
// FakeDeschedulePlugin is a configurable plugin used for testing
type FakeDeschedulePlugin struct {
PluginName string
// ReactionChain is the list of reactors that will be attempted for every
// request in the order they are tried.
ReactionChain []Reactor
args runtime.Object
handle frameworktypes.Handle
}
func NewFakeDeschedulePluginFncFromFake(fp *FakeDeschedulePlugin) pluginregistry.PluginBuilder {
return func(args runtime.Object, handle frameworktypes.Handle) (frameworktypes.Plugin, error) {
fakePluginArgs, ok := args.(*FakeDeschedulePluginArgs)
if !ok {
return nil, fmt.Errorf("want args to be of type FakeDeschedulePluginArgs, got %T", args)
}
fp.handle = handle
fp.args = fakePluginArgs
return fp, nil
}
}
// New builds plugin from its arguments while passing a handle
func NewFakeDeschedule(args runtime.Object, handle frameworktypes.Handle) (frameworktypes.Plugin, error) {
fakePluginArgs, ok := args.(*FakeDeschedulePluginArgs)
if !ok {
return nil, fmt.Errorf("want args to be of type FakePluginArgs, got %T", args)
}
ev := &FakeDeschedulePlugin{}
ev.handle = handle
ev.args = fakePluginArgs
return ev, nil
}
func (c *FakeDeschedulePlugin) AddReactor(extensionPoint string, reaction ReactionFunc) {
c.ReactionChain = append(c.ReactionChain, &SimpleReactor{ExtensionPoint: extensionPoint, Reaction: reaction})
}
// Name retrieves the plugin name
func (d *FakeDeschedulePlugin) Name() string {
return d.PluginName
}
func (d *FakeDeschedulePlugin) Deschedule(ctx context.Context, nodes []*v1.Node) *frameworktypes.Status {
return d.handleAction(&DescheduleActionImpl{
ActionImpl: ActionImpl{
handle: d.handle,
extensionPoint: string(frameworktypes.DescheduleExtensionPoint),
},
nodes: nodes,
})
}
func (d *FakeDeschedulePlugin) handleAction(action Action) *frameworktypes.Status {
actionCopy := action.DeepCopy()
for _, reactor := range d.ReactionChain {
if !reactor.Handles(actionCopy) {
continue
}
handled, _, err := reactor.React(actionCopy)
if !handled {
continue
}
return &frameworktypes.Status{
Err: err,
}
}
return &frameworktypes.Status{
Err: fmt.Errorf("unhandled %q action", action.GetExtensionPoint()),
}
}
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// FakeBalancePluginArgs holds arguments used to configure FakeBalancePlugin plugin.
type FakeBalancePluginArgs struct {
metav1.TypeMeta `json:",inline"`
}
// FakeBalancePlugin is a configurable plugin used for testing
type FakeBalancePlugin struct {
PluginName string
// ReactionChain is the list of reactors that will be attempted for every
// request in the order they are tried.
ReactionChain []Reactor
args runtime.Object
handle frameworktypes.Handle
}
func NewFakeBalancePluginFncFromFake(fp *FakeBalancePlugin) pluginregistry.PluginBuilder {
return func(args runtime.Object, handle frameworktypes.Handle) (frameworktypes.Plugin, error) {
fakePluginArgs, ok := args.(*FakeBalancePluginArgs)
if !ok {
return nil, fmt.Errorf("want args to be of type FakeBalancePluginArgs, got %T", args)
}
fp.handle = handle
fp.args = fakePluginArgs
return fp, nil
}
}
// New builds plugin from its arguments while passing a handle
func NewFakeBalance(args runtime.Object, handle frameworktypes.Handle) (frameworktypes.Plugin, error) {
fakePluginArgs, ok := args.(*FakeBalancePluginArgs)
if !ok {
return nil, fmt.Errorf("want args to be of type FakePluginArgs, got %T", args)
}
ev := &FakeBalancePlugin{}
ev.handle = handle
ev.args = fakePluginArgs
return ev, nil
}
func (c *FakeBalancePlugin) AddReactor(extensionPoint string, reaction ReactionFunc) {
c.ReactionChain = append(c.ReactionChain, &SimpleReactor{ExtensionPoint: extensionPoint, Reaction: reaction})
}
// Name retrieves the plugin name
func (d *FakeBalancePlugin) Name() string {
return d.PluginName
}
func (d *FakeBalancePlugin) Balance(ctx context.Context, nodes []*v1.Node) *frameworktypes.Status {
return d.handleAction(&BalanceActionImpl{
ActionImpl: ActionImpl{
handle: d.handle,
extensionPoint: string(frameworktypes.BalanceExtensionPoint),
},
nodes: nodes,
})
}
func (d *FakeBalancePlugin) handleAction(action Action) *frameworktypes.Status {
actionCopy := action.DeepCopy()
for _, reactor := range d.ReactionChain {
if !reactor.Handles(actionCopy) {
continue
}
handled, _, err := reactor.React(actionCopy)
if !handled {
continue
}
return &frameworktypes.Status{
Err: err,
}
}
return &frameworktypes.Status{
Err: fmt.Errorf("unhandled %q action", action.GetExtensionPoint()),
}
}
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// FakeFilterPluginArgs holds arguments used to configure FakeFilterPlugin plugin.
type FakeFilterPluginArgs struct {
metav1.TypeMeta `json:",inline"`
}
// FakeFilterPlugin is a configurable plugin used for testing
type FakeFilterPlugin struct {
PluginName string
// ReactionChain is the list of reactors that will be attempted for every
// request in the order they are tried.
ReactionChain []Reactor
args runtime.Object
handle frameworktypes.Handle
}
func NewFakeFilterPluginFncFromFake(fp *FakeFilterPlugin) pluginregistry.PluginBuilder {
return func(args runtime.Object, handle frameworktypes.Handle) (frameworktypes.Plugin, error) {
fakePluginArgs, ok := args.(*FakeFilterPluginArgs)
if !ok {
return nil, fmt.Errorf("want args to be of type FakeFilterPluginArgs, got %T", args)
}
fp.handle = handle
fp.args = fakePluginArgs
return fp, nil
}
}
// New builds plugin from its arguments while passing a handle
func NewFakeFilter(args runtime.Object, handle frameworktypes.Handle) (frameworktypes.Plugin, error) {
fakePluginArgs, ok := args.(*FakeFilterPluginArgs)
if !ok {
return nil, fmt.Errorf("want args to be of type FakePluginArgs, got %T", args)
}
ev := &FakeFilterPlugin{}
ev.handle = handle
ev.args = fakePluginArgs
return ev, nil
}
func (c *FakeFilterPlugin) AddReactor(extensionPoint string, reaction ReactionFunc) {
c.ReactionChain = append(c.ReactionChain, &SimpleReactor{ExtensionPoint: extensionPoint, Reaction: reaction})
}
// Name retrieves the plugin name
func (d *FakeFilterPlugin) Name() string {
return d.PluginName
}
func (d *FakeFilterPlugin) Filter(pod *v1.Pod) bool {
return d.handleBoolAction(&FilterActionImpl{
ActionImpl: ActionImpl{
handle: d.handle,
extensionPoint: string(frameworktypes.FilterExtensionPoint),
},
})
}
func (d *FakeFilterPlugin) PreEvictionFilter(pod *v1.Pod) bool {
return d.handleBoolAction(&PreEvictionFilterActionImpl{
ActionImpl: ActionImpl{
handle: d.handle,
extensionPoint: string(frameworktypes.PreEvictionFilterExtensionPoint),
},
})
}
func (d *FakeFilterPlugin) handleBoolAction(action Action) bool {
actionCopy := action.DeepCopy()
for _, reactor := range d.ReactionChain {
if !reactor.Handles(actionCopy) {
continue
}
handled, filter, _ := reactor.React(actionCopy)
if !handled {
continue
}
return filter
}
panic(fmt.Errorf("unhandled %q action", action.GetExtensionPoint()))
}

View File

@@ -24,6 +24,8 @@ type Action interface {
DeepCopy() Action
}
type ReactionFunc func(action Action) (handled, filter bool, err error)
// Reactor is an interface to allow the composition of reaction functions.
type Reactor interface {
// Handles indicates whether or not this Reactor deals with a given
@@ -31,7 +33,8 @@ type Reactor interface {
Handles(action Action) bool
// React handles the action. It may choose to
// delegate by indicated handled=false.
React(action Action) (handled bool, err error)
// filter is used to store results of filter based actions
React(action Action) (handled, filter bool, err error)
}
// SimpleReactor is a Reactor. Each reaction function is attached to a given extensionPoint. "*" in either field matches everything for that value.
@@ -44,12 +47,10 @@ func (r *SimpleReactor) Handles(action Action) bool {
return r.ExtensionPoint == "*" || r.ExtensionPoint == action.GetExtensionPoint()
}
func (r *SimpleReactor) React(action Action) (bool, error) {
func (r *SimpleReactor) React(action Action) (bool, bool, error) {
return r.Reaction(action)
}
type ReactionFunc func(action Action) (handled bool, err error)
type DescheduleAction interface {
Action
CanDeschedule() bool
@@ -62,6 +63,16 @@ type BalanceAction interface {
Nodes() []*v1.Node
}
type FilterAction interface {
Action
CanFilter() bool
}
type PreEvictionFilterAction interface {
Action
CanPreEvictionFilter() bool
}
type ActionImpl struct {
handle frameworktypes.Handle
extensionPoint string
@@ -130,6 +141,30 @@ func (a BalanceActionImpl) DeepCopy() Action {
}
}
func (c *FakePlugin) AddReactor(extensionPoint string, reaction ReactionFunc) {
c.ReactionChain = append(c.ReactionChain, &SimpleReactor{ExtensionPoint: extensionPoint, Reaction: reaction})
type FilterActionImpl struct {
ActionImpl
}
func (d FilterActionImpl) CanFilter() bool {
return true
}
func (a FilterActionImpl) DeepCopy() Action {
return FilterActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
}
}
type PreEvictionFilterActionImpl struct {
ActionImpl
}
func (d PreEvictionFilterActionImpl) CanPreEvictionFilter() bool {
return true
}
func (a PreEvictionFilterActionImpl) DeepCopy() Action {
return PreEvictionFilterActionImpl{
ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl),
}
}

View File

@@ -25,6 +25,81 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FakeBalancePluginArgs) DeepCopyInto(out *FakeBalancePluginArgs) {
*out = *in
out.TypeMeta = in.TypeMeta
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FakeBalancePluginArgs.
func (in *FakeBalancePluginArgs) DeepCopy() *FakeBalancePluginArgs {
if in == nil {
return nil
}
out := new(FakeBalancePluginArgs)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FakeBalancePluginArgs) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FakeDeschedulePluginArgs) DeepCopyInto(out *FakeDeschedulePluginArgs) {
*out = *in
out.TypeMeta = in.TypeMeta
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FakeDeschedulePluginArgs.
func (in *FakeDeschedulePluginArgs) DeepCopy() *FakeDeschedulePluginArgs {
if in == nil {
return nil
}
out := new(FakeDeschedulePluginArgs)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FakeDeschedulePluginArgs) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FakeFilterPluginArgs) DeepCopyInto(out *FakeFilterPluginArgs) {
*out = *in
out.TypeMeta = in.TypeMeta
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FakeFilterPluginArgs.
func (in *FakeFilterPluginArgs) DeepCopy() *FakeFilterPluginArgs {
if in == nil {
return nil
}
out := new(FakeFilterPluginArgs)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FakeFilterPluginArgs) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FakePluginArgs) DeepCopyInto(out *FakePluginArgs) {
*out = *in