mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 06:27:05 +00:00
Merge pull request #108613 from Huang-Wei/fix-v1beta3-order
Fix a bug that out-of-tree plugin is misplaced when using scheduler v1beta3 config
This commit is contained in:
commit
0b8a665d50
@ -29,7 +29,9 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apiserver/pkg/util/feature"
|
"k8s.io/apiserver/pkg/util/feature"
|
||||||
componentbaseconfig "k8s.io/component-base/config"
|
componentbaseconfig "k8s.io/component-base/config"
|
||||||
"k8s.io/component-base/featuregate"
|
"k8s.io/component-base/featuregate"
|
||||||
@ -39,6 +41,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/apis/config/testing/defaults"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config/testing/defaults"
|
||||||
|
"k8s.io/kubernetes/pkg/scheduler/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSetup(t *testing.T) {
|
func TestSetup(t *testing.T) {
|
||||||
@ -154,6 +157,44 @@ profiles:
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// out-of-tree plugin config v1beta3
|
||||||
|
outOfTreePluginConfigFilev1beta3 := filepath.Join(tmpDir, "outOfTreePluginv1beta3.yaml")
|
||||||
|
if err := os.WriteFile(outOfTreePluginConfigFilev1beta3, []byte(fmt.Sprintf(`
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1beta3
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
clientConnection:
|
||||||
|
kubeconfig: "%s"
|
||||||
|
profiles:
|
||||||
|
- plugins:
|
||||||
|
preFilter:
|
||||||
|
enabled:
|
||||||
|
- name: Foo
|
||||||
|
filter:
|
||||||
|
enabled:
|
||||||
|
- name: Foo
|
||||||
|
`, configKubeconfig)), os.FileMode(0600)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// out-of-tree plugin config v1beta2
|
||||||
|
outOfTreePluginConfigFilev1beta2 := filepath.Join(tmpDir, "outOfTreePluginv1beta2.yaml")
|
||||||
|
if err := os.WriteFile(outOfTreePluginConfigFilev1beta2, []byte(fmt.Sprintf(`
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1beta2
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
clientConnection:
|
||||||
|
kubeconfig: "%s"
|
||||||
|
profiles:
|
||||||
|
- plugins:
|
||||||
|
preFilter:
|
||||||
|
enabled:
|
||||||
|
- name: Foo
|
||||||
|
filter:
|
||||||
|
enabled:
|
||||||
|
- name: Foo
|
||||||
|
`, configKubeconfig)), os.FileMode(0600)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// multiple profiles config
|
// multiple profiles config
|
||||||
multiProfilesConfig := filepath.Join(tmpDir, "multi-profiles.yaml")
|
multiProfilesConfig := filepath.Join(tmpDir, "multi-profiles.yaml")
|
||||||
if err := os.WriteFile(multiProfilesConfig, []byte(fmt.Sprintf(`
|
if err := os.WriteFile(multiProfilesConfig, []byte(fmt.Sprintf(`
|
||||||
@ -211,6 +252,7 @@ leaderElection:
|
|||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
flags []string
|
flags []string
|
||||||
|
registryOptions []Option
|
||||||
restoreFeatures map[featuregate.Feature]bool
|
restoreFeatures map[featuregate.Feature]bool
|
||||||
wantPlugins map[string]*config.Plugins
|
wantPlugins map[string]*config.Plugins
|
||||||
wantLeaderElection *componentbaseconfig.LeaderElectionConfiguration
|
wantLeaderElection *componentbaseconfig.LeaderElectionConfiguration
|
||||||
@ -330,6 +372,56 @@ leaderElection:
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "out-of-tree component configuration v1beta2",
|
||||||
|
flags: []string{
|
||||||
|
"--config", outOfTreePluginConfigFilev1beta2,
|
||||||
|
"--kubeconfig", configKubeconfig,
|
||||||
|
},
|
||||||
|
registryOptions: []Option{WithPlugin("Foo", newFoo)},
|
||||||
|
wantPlugins: map[string]*config.Plugins{
|
||||||
|
"default-scheduler": {
|
||||||
|
Bind: defaults.PluginsV1beta2.Bind,
|
||||||
|
Filter: config.PluginSet{
|
||||||
|
Enabled: append(defaults.PluginsV1beta2.Filter.Enabled, config.Plugin{Name: "Foo"}),
|
||||||
|
},
|
||||||
|
PreFilter: config.PluginSet{
|
||||||
|
Enabled: append(defaults.PluginsV1beta2.PreFilter.Enabled, config.Plugin{Name: "Foo"}),
|
||||||
|
},
|
||||||
|
PostFilter: defaults.PluginsV1beta2.PostFilter,
|
||||||
|
PreScore: defaults.PluginsV1beta2.PreScore,
|
||||||
|
QueueSort: defaults.PluginsV1beta2.QueueSort,
|
||||||
|
Score: defaults.PluginsV1beta2.Score,
|
||||||
|
Reserve: defaults.PluginsV1beta2.Reserve,
|
||||||
|
PreBind: defaults.PluginsV1beta2.PreBind,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "out-of-tree component configuration v1beta3",
|
||||||
|
flags: []string{
|
||||||
|
"--config", outOfTreePluginConfigFilev1beta3,
|
||||||
|
"--kubeconfig", configKubeconfig,
|
||||||
|
},
|
||||||
|
registryOptions: []Option{WithPlugin("Foo", newFoo)},
|
||||||
|
wantPlugins: map[string]*config.Plugins{
|
||||||
|
"default-scheduler": {
|
||||||
|
Bind: defaults.ExpandedPluginsV1beta3.Bind,
|
||||||
|
Filter: config.PluginSet{
|
||||||
|
Enabled: append(defaults.ExpandedPluginsV1beta3.Filter.Enabled, config.Plugin{Name: "Foo"}),
|
||||||
|
},
|
||||||
|
PreFilter: config.PluginSet{
|
||||||
|
Enabled: append(defaults.ExpandedPluginsV1beta3.PreFilter.Enabled, config.Plugin{Name: "Foo"}),
|
||||||
|
},
|
||||||
|
PostFilter: defaults.ExpandedPluginsV1beta3.PostFilter,
|
||||||
|
PreScore: defaults.ExpandedPluginsV1beta3.PreScore,
|
||||||
|
QueueSort: defaults.ExpandedPluginsV1beta3.QueueSort,
|
||||||
|
Score: defaults.ExpandedPluginsV1beta3.Score,
|
||||||
|
Reserve: defaults.ExpandedPluginsV1beta3.Reserve,
|
||||||
|
PreBind: defaults.ExpandedPluginsV1beta3.PreBind,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "leader election CLI args, along with --config arg",
|
name: "leader election CLI args, along with --config arg",
|
||||||
flags: []string{
|
flags: []string{
|
||||||
@ -437,7 +529,7 @@ leaderElection:
|
|||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
_, sched, err := Setup(ctx, opts)
|
_, sched, err := Setup(ctx, opts, tc.registryOptions...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -462,3 +554,29 @@ leaderElection:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simulates an out-of-tree plugin.
|
||||||
|
type foo struct{}
|
||||||
|
|
||||||
|
var _ framework.PreFilterPlugin = &foo{}
|
||||||
|
var _ framework.FilterPlugin = &foo{}
|
||||||
|
|
||||||
|
func (*foo) Name() string {
|
||||||
|
return "Foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFoo(_ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
|
||||||
|
return &foo{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*foo) PreFilter(_ context.Context, _ *framework.CycleState, _ *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*foo) PreFilterExtensions() framework.PreFilterExtensions {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*foo) Filter(_ context.Context, _ *framework.CycleState, _ *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -403,6 +403,37 @@ func getScoreWeights(f *frameworkImpl, pluginsMap map[string]framework.Plugin, p
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type orderedSet struct {
|
||||||
|
set map[string]int
|
||||||
|
list []string
|
||||||
|
deletionCnt int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOrderedSet() *orderedSet {
|
||||||
|
return &orderedSet{set: make(map[string]int)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (os *orderedSet) insert(s string) {
|
||||||
|
if os.has(s) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
os.set[s] = len(os.list)
|
||||||
|
os.list = append(os.list, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (os *orderedSet) has(s string) bool {
|
||||||
|
_, found := os.set[s]
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func (os *orderedSet) delete(s string) {
|
||||||
|
if i, found := os.set[s]; found {
|
||||||
|
delete(os.set, s)
|
||||||
|
os.list = append(os.list[:i-os.deletionCnt], os.list[i+1-os.deletionCnt:]...)
|
||||||
|
os.deletionCnt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (f *frameworkImpl) expandMultiPointPlugins(profile *config.KubeSchedulerProfile, pluginsMap map[string]framework.Plugin) error {
|
func (f *frameworkImpl) expandMultiPointPlugins(profile *config.KubeSchedulerProfile, pluginsMap map[string]framework.Plugin) error {
|
||||||
// initialize MultiPoint plugins
|
// initialize MultiPoint plugins
|
||||||
for _, e := range f.getExtensionPoints(profile.Plugins) {
|
for _, e := range f.getExtensionPoints(profile.Plugins) {
|
||||||
@ -410,9 +441,9 @@ func (f *frameworkImpl) expandMultiPointPlugins(profile *config.KubeSchedulerPro
|
|||||||
pluginType := plugins.Type().Elem()
|
pluginType := plugins.Type().Elem()
|
||||||
// build enabledSet of plugins already registered via normal extension points
|
// build enabledSet of plugins already registered via normal extension points
|
||||||
// to check double registration
|
// to check double registration
|
||||||
enabledSet := sets.NewString()
|
enabledSet := newOrderedSet()
|
||||||
for _, plugin := range e.plugins.Enabled {
|
for _, plugin := range e.plugins.Enabled {
|
||||||
enabledSet.Insert(plugin.Name)
|
enabledSet.insert(plugin.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
disabledSet := sets.NewString()
|
disabledSet := sets.NewString()
|
||||||
@ -426,8 +457,8 @@ func (f *frameworkImpl) expandMultiPointPlugins(profile *config.KubeSchedulerPro
|
|||||||
|
|
||||||
// track plugins enabled via multipoint separately from those enabled by specific extensions,
|
// track plugins enabled via multipoint separately from those enabled by specific extensions,
|
||||||
// so that we can distinguish between double-registration and explicit overrides
|
// so that we can distinguish between double-registration and explicit overrides
|
||||||
multiPointEnabled := sets.NewString()
|
multiPointEnabled := newOrderedSet()
|
||||||
|
overridePlugins := newOrderedSet()
|
||||||
for _, ep := range profile.Plugins.MultiPoint.Enabled {
|
for _, ep := range profile.Plugins.MultiPoint.Enabled {
|
||||||
pg, ok := pluginsMap[ep.Name]
|
pg, ok := pluginsMap[ep.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -449,23 +480,43 @@ func (f *frameworkImpl) expandMultiPointPlugins(profile *config.KubeSchedulerPro
|
|||||||
// the user intent is to override the default plugin or make some other explicit setting.
|
// the user intent is to override the default plugin or make some other explicit setting.
|
||||||
// Either way, discard the MultiPoint value for this plugin.
|
// Either way, discard the MultiPoint value for this plugin.
|
||||||
// This maintains expected behavior for overriding default plugins (see https://github.com/kubernetes/kubernetes/pull/99582)
|
// This maintains expected behavior for overriding default plugins (see https://github.com/kubernetes/kubernetes/pull/99582)
|
||||||
if enabledSet.Has(ep.Name) {
|
if enabledSet.has(ep.Name) {
|
||||||
|
overridePlugins.insert(ep.Name)
|
||||||
klog.InfoS("MultiPoint plugin is explicitly re-configured; overriding", "plugin", ep.Name)
|
klog.InfoS("MultiPoint plugin is explicitly re-configured; overriding", "plugin", ep.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this plugin is already registered via MultiPoint, then this is
|
// if this plugin is already registered via MultiPoint, then this is
|
||||||
// a double registration and an error in the config.
|
// a double registration and an error in the config.
|
||||||
if multiPointEnabled.Has(ep.Name) {
|
if multiPointEnabled.has(ep.Name) {
|
||||||
return fmt.Errorf("plugin %q already registered as %q", ep.Name, pluginType.Name())
|
return fmt.Errorf("plugin %q already registered as %q", ep.Name, pluginType.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
// we only need to update the multipoint set, since we already have the specific extension set from above
|
// we only need to update the multipoint set, since we already have the specific extension set from above
|
||||||
multiPointEnabled.Insert(ep.Name)
|
multiPointEnabled.insert(ep.Name)
|
||||||
|
|
||||||
newPlugins := reflect.Append(plugins, reflect.ValueOf(pg))
|
|
||||||
plugins.Set(newPlugins)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reorder plugins. Here is the expected order:
|
||||||
|
// - part 1: overridePlugins. Their order stay intact as how they're specified in regular extension point.
|
||||||
|
// - part 2: multiPointEnabled - i.e., plugin defined in multipoint but not in regular extension point.
|
||||||
|
// - part 3: other plugins (excluded by part 1 & 2) in regular extension point.
|
||||||
|
newPlugins := reflect.New(reflect.TypeOf(e.slicePtr).Elem()).Elem()
|
||||||
|
// part 1
|
||||||
|
for _, name := range enabledSet.list {
|
||||||
|
if overridePlugins.has(name) {
|
||||||
|
newPlugins = reflect.Append(newPlugins, reflect.ValueOf(pluginsMap[name]))
|
||||||
|
enabledSet.delete(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// part 2
|
||||||
|
for _, name := range multiPointEnabled.list {
|
||||||
|
newPlugins = reflect.Append(newPlugins, reflect.ValueOf(pluginsMap[name]))
|
||||||
|
}
|
||||||
|
// part 3
|
||||||
|
for _, name := range enabledSet.list {
|
||||||
|
newPlugins = reflect.Append(newPlugins, reflect.ValueOf(pluginsMap[name]))
|
||||||
|
}
|
||||||
|
plugins.Set(newPlugins)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -656,6 +656,44 @@ func TestNewFrameworkMultiPointExpansion(t *testing.T) {
|
|||||||
PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Reorder MultiPoint plugins (specified extension only takes precedence when it exists in MultiPoint)",
|
||||||
|
plugins: &config.Plugins{
|
||||||
|
MultiPoint: config.PluginSet{
|
||||||
|
Enabled: []config.Plugin{
|
||||||
|
{Name: testPlugin},
|
||||||
|
{Name: scorePlugin1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Score: config.PluginSet{
|
||||||
|
Enabled: []config.Plugin{
|
||||||
|
{Name: scoreWithNormalizePlugin1},
|
||||||
|
{Name: scorePlugin1},
|
||||||
|
{Name: testPlugin},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantPlugins: &config.Plugins{
|
||||||
|
QueueSort: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
|
PreFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
|
Filter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
|
PostFilter: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
|
PreScore: config.PluginSet{Enabled: []config.Plugin{
|
||||||
|
{Name: testPlugin},
|
||||||
|
{Name: scorePlugin1},
|
||||||
|
}},
|
||||||
|
Score: config.PluginSet{Enabled: []config.Plugin{
|
||||||
|
{Name: scorePlugin1, Weight: 1},
|
||||||
|
{Name: testPlugin, Weight: 1},
|
||||||
|
{Name: scoreWithNormalizePlugin1, Weight: 1},
|
||||||
|
}},
|
||||||
|
Reserve: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
|
Permit: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
|
PreBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
|
Bind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
|
PostBind: config.PluginSet{Enabled: []config.Plugin{{Name: testPlugin}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Override MultiPoint plugins weights",
|
name: "Override MultiPoint plugins weights",
|
||||||
plugins: &config.Plugins{
|
plugins: &config.Plugins{
|
||||||
|
@ -1420,7 +1420,7 @@ func TestUnReserveBindPlugins(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if test.plugin.numBindCalled != 1 {
|
if test.plugin.numBindCalled != 1 {
|
||||||
t.Errorf("Expected the Prebind plugin to be called.")
|
t.Errorf("Expected the Bind plugin to be called.")
|
||||||
}
|
}
|
||||||
|
|
||||||
testutils.CleanupPods(testCtx.ClientSet, t, []*v1.Pod{pod})
|
testutils.CleanupPods(testCtx.ClientSet, t, []*v1.Pod{pod})
|
||||||
@ -2448,6 +2448,9 @@ func initRegistryAndConfig(t *testing.T, plugins ...framework.Plugin) (framework
|
|||||||
pls.PreBind.Enabled = append(pls.PreBind.Enabled, plugin)
|
pls.PreBind.Enabled = append(pls.PreBind.Enabled, plugin)
|
||||||
case *BindPlugin:
|
case *BindPlugin:
|
||||||
pls.Bind.Enabled = append(pls.Bind.Enabled, plugin)
|
pls.Bind.Enabled = append(pls.Bind.Enabled, plugin)
|
||||||
|
// It's intentional to disable the DefaultBind plugin. Otherwise, DefaultBinder's failure would fail
|
||||||
|
// a pod's scheduling, as well as the test BindPlugin's execution.
|
||||||
|
pls.Bind.Disabled = []v1beta3.Plugin{{Name: defaultbinder.Name}}
|
||||||
case *PostBindPlugin:
|
case *PostBindPlugin:
|
||||||
pls.PostBind.Enabled = append(pls.PostBind.Enabled, plugin)
|
pls.PostBind.Enabled = append(pls.PostBind.Enabled, plugin)
|
||||||
case *PermitPlugin:
|
case *PermitPlugin:
|
||||||
|
Loading…
Reference in New Issue
Block a user