mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 02:41:25 +00:00
Merge pull request #90309 from alculquicondor/plugin-args-decoding
Use internal config types in scheduling plugin args
This commit is contained in:
commit
8dd93ca94c
@ -19,7 +19,6 @@ go_library(
|
|||||||
"//pkg/scheduler/apis/config/scheme:go_default_library",
|
"//pkg/scheduler/apis/config/scheme:go_default_library",
|
||||||
"//pkg/scheduler/apis/config/v1alpha2:go_default_library",
|
"//pkg/scheduler/apis/config/v1alpha2:go_default_library",
|
||||||
"//pkg/scheduler/apis/config/validation:go_default_library",
|
"//pkg/scheduler/apis/config/validation:go_default_library",
|
||||||
"//pkg/scheduler/framework/plugins:go_default_library",
|
|
||||||
"//pkg/scheduler/framework/plugins/interpodaffinity:go_default_library",
|
"//pkg/scheduler/framework/plugins/interpodaffinity:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
@ -21,10 +21,8 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
kubeschedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
|
||||||
"k8s.io/kubernetes/pkg/scheduler/algorithmprovider"
|
"k8s.io/kubernetes/pkg/scheduler/algorithmprovider"
|
||||||
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
|
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins"
|
|
||||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/interpodaffinity"
|
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/interpodaffinity"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -129,10 +127,13 @@ func (o *DeprecatedOptions) ApplyTo(cfg *kubeschedulerconfig.KubeSchedulerConfig
|
|||||||
profile.SchedulerName = o.SchedulerName
|
profile.SchedulerName = o.SchedulerName
|
||||||
}
|
}
|
||||||
if o.HardPodAffinitySymmetricWeight != interpodaffinity.DefaultHardPodAffinityWeight {
|
if o.HardPodAffinitySymmetricWeight != interpodaffinity.DefaultHardPodAffinityWeight {
|
||||||
args := kubeschedulerv1alpha2.InterPodAffinityArgs{
|
plCfg := kubeschedulerconfig.PluginConfig{
|
||||||
HardPodAffinityWeight: &o.HardPodAffinitySymmetricWeight,
|
Name: interpodaffinity.Name,
|
||||||
|
Args: &kubeschedulerconfig.InterPodAffinityArgs{
|
||||||
|
HardPodAffinityWeight: o.HardPodAffinitySymmetricWeight,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
profile.PluginConfig = append(profile.PluginConfig, plugins.NewPluginConfig(interpodaffinity.Name, args))
|
profile.PluginConfig = append(profile.PluginConfig, plCfg)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -203,6 +203,9 @@ profiles:
|
|||||||
disabled:
|
disabled:
|
||||||
- name: baz
|
- name: baz
|
||||||
pluginConfig:
|
pluginConfig:
|
||||||
|
- name: InterPodAffinity
|
||||||
|
args:
|
||||||
|
hardPodAffinityWeight: 2
|
||||||
- name: foo
|
- name: foo
|
||||||
args:
|
args:
|
||||||
bar: baz
|
bar: baz
|
||||||
@ -543,6 +546,12 @@ profiles:
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
PluginConfig: []kubeschedulerconfig.PluginConfig{
|
PluginConfig: []kubeschedulerconfig.PluginConfig{
|
||||||
|
{
|
||||||
|
Name: "InterPodAffinity",
|
||||||
|
Args: &kubeschedulerconfig.InterPodAffinityArgs{
|
||||||
|
HardPodAffinityWeight: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Args: &runtime.Unknown{
|
Args: &runtime.Unknown{
|
||||||
@ -670,9 +679,7 @@ profiles:
|
|||||||
PluginConfig: []kubeschedulerconfig.PluginConfig{
|
PluginConfig: []kubeschedulerconfig.PluginConfig{
|
||||||
{
|
{
|
||||||
Name: "InterPodAffinity",
|
Name: "InterPodAffinity",
|
||||||
Args: &runtime.Unknown{
|
Args: &kubeschedulerconfig.InterPodAffinityArgs{HardPodAffinityWeight: 5},
|
||||||
Raw: []byte(`{"hardPodAffinityWeight":5}`),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -45,7 +45,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/listers/policy/v1beta1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/policy/v1beta1:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
@ -28,3 +28,19 @@ filegroup(
|
|||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["scheme_test.go"],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
||||||
|
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||||
|
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
450
pkg/scheduler/apis/config/scheme/scheme_test.go
Normal file
450
pkg/scheduler/apis/config/scheme/scheme_test.go
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 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 scheme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/kube-scheduler/config/v1alpha2"
|
||||||
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
|
"k8s.io/utils/pointer"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCodecsDecodePluginConfig(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
wantErr string
|
||||||
|
wantProfiles []config.KubeSchedulerProfile
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "v1alpha2 all plugin args in default profile",
|
||||||
|
data: []byte(`
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
profiles:
|
||||||
|
- pluginConfig:
|
||||||
|
- name: InterPodAffinity
|
||||||
|
args:
|
||||||
|
hardPodAffinityWeight: 5
|
||||||
|
- name: NodeLabel
|
||||||
|
args:
|
||||||
|
presentLabels: ["foo"]
|
||||||
|
- name: NodeResourcesFit
|
||||||
|
args:
|
||||||
|
ignoredResources: ["foo"]
|
||||||
|
- name: RequestedToCapacityRatio
|
||||||
|
args:
|
||||||
|
shape:
|
||||||
|
- utilization: 1
|
||||||
|
- name: PodTopologySpread
|
||||||
|
args:
|
||||||
|
defaultConstraints:
|
||||||
|
- maxSkew: 1
|
||||||
|
topologyKey: zone
|
||||||
|
whenUnsatisfiable: ScheduleAnyway
|
||||||
|
- name: ServiceAffinity
|
||||||
|
args:
|
||||||
|
affinityLabels: ["bar"]
|
||||||
|
`),
|
||||||
|
wantProfiles: []config.KubeSchedulerProfile{
|
||||||
|
{
|
||||||
|
SchedulerName: "default-scheduler",
|
||||||
|
PluginConfig: []config.PluginConfig{
|
||||||
|
{
|
||||||
|
Name: "InterPodAffinity",
|
||||||
|
Args: &config.InterPodAffinityArgs{HardPodAffinityWeight: 5},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "NodeLabel",
|
||||||
|
Args: &config.NodeLabelArgs{PresentLabels: []string{"foo"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "NodeResourcesFit",
|
||||||
|
Args: &config.NodeResourcesFitArgs{IgnoredResources: []string{"foo"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "RequestedToCapacityRatio",
|
||||||
|
Args: &config.RequestedToCapacityRatioArgs{
|
||||||
|
Shape: []config.UtilizationShapePoint{{Utilization: 1}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "PodTopologySpread",
|
||||||
|
Args: &config.PodTopologySpreadArgs{
|
||||||
|
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||||
|
{MaxSkew: 1, TopologyKey: "zone", WhenUnsatisfiable: v1.ScheduleAnyway},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "ServiceAffinity",
|
||||||
|
Args: &config.ServiceAffinityArgs{
|
||||||
|
AffinityLabels: []string{"bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1alpha2 plugins can include version and kind",
|
||||||
|
data: []byte(`
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
profiles:
|
||||||
|
- pluginConfig:
|
||||||
|
- name: NodeLabel
|
||||||
|
args:
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
kind: NodeLabelArgs
|
||||||
|
presentLabels: ["bars"]
|
||||||
|
`),
|
||||||
|
wantProfiles: []config.KubeSchedulerProfile{
|
||||||
|
{
|
||||||
|
SchedulerName: "default-scheduler",
|
||||||
|
PluginConfig: []config.PluginConfig{
|
||||||
|
{
|
||||||
|
Name: "NodeLabel",
|
||||||
|
Args: &config.NodeLabelArgs{PresentLabels: []string{"bars"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "plugin group and kind should match the type",
|
||||||
|
data: []byte(`
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
profiles:
|
||||||
|
- pluginConfig:
|
||||||
|
- name: NodeLabel
|
||||||
|
args:
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
kind: InterPodAffinityArgs
|
||||||
|
`),
|
||||||
|
wantErr: "decoding .profiles[0].pluginConfig[0]: args for plugin NodeLabel were not of type NodeLabelArgs.kubescheduler.config.k8s.io, got InterPodAffinityArgs.kubescheduler.config.k8s.io",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// TODO: do not replicate this case for v1beta1.
|
||||||
|
name: "v1alpha2 case insensitive RequestedToCapacityRatioArgs",
|
||||||
|
data: []byte(`
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
profiles:
|
||||||
|
- pluginConfig:
|
||||||
|
- name: RequestedToCapacityRatio
|
||||||
|
args:
|
||||||
|
shape:
|
||||||
|
- utilization: 1
|
||||||
|
score: 2
|
||||||
|
- Utilization: 3
|
||||||
|
Score: 4
|
||||||
|
resources:
|
||||||
|
- name: Upper
|
||||||
|
weight: 1
|
||||||
|
- Name: lower
|
||||||
|
weight: 2
|
||||||
|
`),
|
||||||
|
wantProfiles: []config.KubeSchedulerProfile{
|
||||||
|
{
|
||||||
|
SchedulerName: "default-scheduler",
|
||||||
|
PluginConfig: []config.PluginConfig{
|
||||||
|
{
|
||||||
|
Name: "RequestedToCapacityRatio",
|
||||||
|
Args: &config.RequestedToCapacityRatioArgs{
|
||||||
|
Shape: []config.UtilizationShapePoint{
|
||||||
|
{Utilization: 1, Score: 2},
|
||||||
|
{Utilization: 3, Score: 4},
|
||||||
|
},
|
||||||
|
Resources: []config.ResourceSpec{
|
||||||
|
{Name: "Upper", Weight: 1},
|
||||||
|
{Name: "lower", Weight: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "out-of-tree plugin args",
|
||||||
|
data: []byte(`
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
profiles:
|
||||||
|
- pluginConfig:
|
||||||
|
- name: OutOfTreePlugin
|
||||||
|
args:
|
||||||
|
foo: bar
|
||||||
|
`),
|
||||||
|
wantProfiles: []config.KubeSchedulerProfile{
|
||||||
|
{
|
||||||
|
SchedulerName: "default-scheduler",
|
||||||
|
PluginConfig: []config.PluginConfig{
|
||||||
|
{
|
||||||
|
Name: "OutOfTreePlugin",
|
||||||
|
Args: &runtime.Unknown{
|
||||||
|
ContentType: "application/json",
|
||||||
|
Raw: []byte(`{"foo":"bar"}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty and no plugin args",
|
||||||
|
data: []byte(`
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
profiles:
|
||||||
|
- pluginConfig:
|
||||||
|
- name: InterPodAffinity
|
||||||
|
args:
|
||||||
|
- name: NodeResourcesFit
|
||||||
|
- name: OutOfTreePlugin
|
||||||
|
args:
|
||||||
|
`),
|
||||||
|
wantProfiles: []config.KubeSchedulerProfile{
|
||||||
|
{
|
||||||
|
SchedulerName: "default-scheduler",
|
||||||
|
PluginConfig: []config.PluginConfig{
|
||||||
|
{
|
||||||
|
Name: "InterPodAffinity",
|
||||||
|
// TODO(acondor): Set default values.
|
||||||
|
Args: &config.InterPodAffinityArgs{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "NodeResourcesFit",
|
||||||
|
Args: &config.NodeResourcesFitArgs{},
|
||||||
|
},
|
||||||
|
{Name: "OutOfTreePlugin"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
decoder := Codecs.UniversalDecoder()
|
||||||
|
for _, tt := range testCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
obj, gvk, err := decoder.Decode(tt.data, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
if tt.wantErr != err.Error() {
|
||||||
|
t.Fatalf("got err %w, want %w", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(tt.wantErr) != 0 {
|
||||||
|
t.Fatal("no error produced, wanted %w", tt.wantErr)
|
||||||
|
}
|
||||||
|
got, ok := obj.(*config.KubeSchedulerConfiguration)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("decoded into %s, want %s", gvk, config.SchemeGroupVersion.WithKind("KubeSchedulerConfiguration"))
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tt.wantProfiles, got.Profiles); diff != "" {
|
||||||
|
t.Errorf("unexpected configuration (-want,+got):\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCodecsEncodePluginConfig(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
obj runtime.Object
|
||||||
|
version schema.GroupVersion
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "v1alpha2 in-tree and out-of-tree plugins",
|
||||||
|
version: v1alpha2.SchemeGroupVersion,
|
||||||
|
obj: &v1alpha2.KubeSchedulerConfiguration{
|
||||||
|
Profiles: []v1alpha2.KubeSchedulerProfile{
|
||||||
|
{
|
||||||
|
PluginConfig: []v1alpha2.PluginConfig{
|
||||||
|
{
|
||||||
|
Name: "InterPodAffinity",
|
||||||
|
Args: runtime.RawExtension{
|
||||||
|
Object: &v1alpha2.InterPodAffinityArgs{
|
||||||
|
HardPodAffinityWeight: pointer.Int32Ptr(5),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "RequestedToCapacityRatio",
|
||||||
|
Args: runtime.RawExtension{
|
||||||
|
Object: &v1alpha2.RequestedToCapacityRatioArgs{
|
||||||
|
Shape: []v1alpha2.UtilizationShapePoint{
|
||||||
|
{Utilization: 1, Score: 2},
|
||||||
|
},
|
||||||
|
Resources: []v1alpha2.ResourceSpec{
|
||||||
|
{Name: "lower", Weight: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "OutOfTreePlugin",
|
||||||
|
Args: runtime.RawExtension{
|
||||||
|
Raw: []byte(`{"foo":"bar"}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
clientConnection:
|
||||||
|
acceptContentTypes: ""
|
||||||
|
burst: 0
|
||||||
|
contentType: ""
|
||||||
|
kubeconfig: ""
|
||||||
|
qps: 0
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
leaderElection:
|
||||||
|
leaderElect: null
|
||||||
|
leaseDuration: 0s
|
||||||
|
renewDeadline: 0s
|
||||||
|
resourceLock: ""
|
||||||
|
resourceName: ""
|
||||||
|
resourceNamespace: ""
|
||||||
|
retryPeriod: 0s
|
||||||
|
profiles:
|
||||||
|
- pluginConfig:
|
||||||
|
- args:
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
hardPodAffinityWeight: 5
|
||||||
|
kind: InterPodAffinityArgs
|
||||||
|
name: InterPodAffinity
|
||||||
|
- args:
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
kind: RequestedToCapacityRatioArgs
|
||||||
|
resources:
|
||||||
|
- Name: lower
|
||||||
|
Weight: 2
|
||||||
|
shape:
|
||||||
|
- Score: 2
|
||||||
|
Utilization: 1
|
||||||
|
name: RequestedToCapacityRatio
|
||||||
|
- args:
|
||||||
|
foo: bar
|
||||||
|
name: OutOfTreePlugin
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v1alpha2 in-tree and out-of-tree plugins from internal",
|
||||||
|
version: v1alpha2.SchemeGroupVersion,
|
||||||
|
obj: &config.KubeSchedulerConfiguration{
|
||||||
|
Profiles: []config.KubeSchedulerProfile{
|
||||||
|
{
|
||||||
|
PluginConfig: []config.PluginConfig{
|
||||||
|
{
|
||||||
|
Name: "InterPodAffinity",
|
||||||
|
Args: &config.InterPodAffinityArgs{
|
||||||
|
HardPodAffinityWeight: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "OutOfTreePlugin",
|
||||||
|
Args: &runtime.Unknown{
|
||||||
|
Raw: []byte(`{"foo":"bar"}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
bindTimeoutSeconds: 0
|
||||||
|
clientConnection:
|
||||||
|
acceptContentTypes: ""
|
||||||
|
burst: 0
|
||||||
|
contentType: ""
|
||||||
|
kubeconfig: ""
|
||||||
|
qps: 0
|
||||||
|
disablePreemption: false
|
||||||
|
enableContentionProfiling: false
|
||||||
|
enableProfiling: false
|
||||||
|
healthzBindAddress: ""
|
||||||
|
kind: KubeSchedulerConfiguration
|
||||||
|
leaderElection:
|
||||||
|
leaderElect: false
|
||||||
|
leaseDuration: 0s
|
||||||
|
renewDeadline: 0s
|
||||||
|
resourceLock: ""
|
||||||
|
resourceName: ""
|
||||||
|
resourceNamespace: ""
|
||||||
|
retryPeriod: 0s
|
||||||
|
metricsBindAddress: ""
|
||||||
|
percentageOfNodesToScore: 0
|
||||||
|
podInitialBackoffSeconds: 0
|
||||||
|
podMaxBackoffSeconds: 0
|
||||||
|
profiles:
|
||||||
|
- pluginConfig:
|
||||||
|
- args:
|
||||||
|
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||||
|
hardPodAffinityWeight: 5
|
||||||
|
kind: InterPodAffinityArgs
|
||||||
|
name: InterPodAffinity
|
||||||
|
- args:
|
||||||
|
foo: bar
|
||||||
|
name: OutOfTreePlugin
|
||||||
|
schedulerName: ""
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
yamlInfo, ok := runtime.SerializerInfoForMediaType(Codecs.SupportedMediaTypes(), runtime.ContentTypeYAML)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("unable to locate encoder -- %q is not a supported media type", runtime.ContentTypeYAML)
|
||||||
|
}
|
||||||
|
jsonInfo, ok := runtime.SerializerInfoForMediaType(Codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("unable to locate encoder -- %q is not a supported media type", runtime.ContentTypeJSON)
|
||||||
|
}
|
||||||
|
for _, tt := range testCases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
encoder := Codecs.EncoderForVersion(yamlInfo.Serializer, tt.version)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := encoder.Encode(tt.obj, &buf); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tt.want, buf.String()); diff != "" {
|
||||||
|
t.Errorf("unexpected encoded configuration:\n%s", diff)
|
||||||
|
}
|
||||||
|
encoder = Codecs.EncoderForVersion(jsonInfo.Serializer, tt.version)
|
||||||
|
buf = bytes.Buffer{}
|
||||||
|
if err := encoder.Encode(tt.obj, &buf); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
out, err := yaml.JSONToYAML(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tt.want, string(out)); diff != "" {
|
||||||
|
t.Errorf("unexpected encoded configuration:\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,6 @@ import (
|
|||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
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"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
@ -1598,90 +1597,58 @@ func TestPluginsConfigurationCompatibility(t *testing.T) {
|
|||||||
pluginConfig: []config.PluginConfig{
|
pluginConfig: []config.PluginConfig{
|
||||||
{
|
{
|
||||||
Name: "NodeResourcesFit",
|
Name: "NodeResourcesFit",
|
||||||
Args: &runtime.Unknown{
|
Args: &config.NodeResourcesFitArgs{
|
||||||
Raw: []byte(`{
|
IgnoredResources: []string{"foo", "bar"},
|
||||||
"ignoredResources": [
|
|
||||||
"foo",
|
|
||||||
"bar"
|
|
||||||
]
|
|
||||||
}`),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "PodTopologySpread",
|
Name: "PodTopologySpread",
|
||||||
Args: &runtime.Unknown{
|
Args: &config.PodTopologySpreadArgs{
|
||||||
Raw: []byte(`{
|
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||||
"defaultConstraints": [
|
|
||||||
{
|
{
|
||||||
"maxSkew": 1,
|
MaxSkew: 1,
|
||||||
"topologyKey": "foo",
|
TopologyKey: "foo",
|
||||||
"whenUnsatisfiable": "DoNotSchedule"
|
WhenUnsatisfiable: v1.DoNotSchedule,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"maxSkew": 10,
|
MaxSkew: 10,
|
||||||
"topologyKey": "bar",
|
TopologyKey: "bar",
|
||||||
"whenUnsatisfiable": "ScheduleAnyway"
|
WhenUnsatisfiable: v1.ScheduleAnyway,
|
||||||
}
|
},
|
||||||
]
|
},
|
||||||
}`),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "RequestedToCapacityRatio",
|
Name: "RequestedToCapacityRatio",
|
||||||
Args: &runtime.Unknown{
|
Args: &config.RequestedToCapacityRatioArgs{
|
||||||
Raw: []byte(`{
|
Shape: []config.UtilizationShapePoint{
|
||||||
"shape":[
|
{Utilization: 5, Score: 5},
|
||||||
"Utilization": 5,
|
},
|
||||||
"Score": 5
|
Resources: []config.ResourceSpec{
|
||||||
],
|
{Name: "cpu", Weight: 10},
|
||||||
"resources":[
|
},
|
||||||
"Name": "cpu",
|
|
||||||
"Weight": 10
|
|
||||||
]
|
|
||||||
}`),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "InterPodAffinity",
|
Name: "InterPodAffinity",
|
||||||
Args: &runtime.Unknown{
|
Args: &config.InterPodAffinityArgs{
|
||||||
Raw: []byte(`{
|
HardPodAffinityWeight: 100,
|
||||||
"HardPodAffinityWeight": 100
|
|
||||||
}`),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "NodeLabel",
|
Name: "NodeLabel",
|
||||||
Args: &runtime.Unknown{
|
Args: &config.NodeLabelArgs{
|
||||||
Raw: []byte(`{
|
PresentLabels: []string{"foo", "bar"},
|
||||||
"presentLabels": [
|
AbsentLabels: []string{"apple"},
|
||||||
"foo",
|
PresentLabelsPreference: []string{"dog"},
|
||||||
"bar"
|
AbsentLabelsPreference: []string{"cat"},
|
||||||
],
|
|
||||||
"absentLabels": [
|
|
||||||
"apple"
|
|
||||||
],
|
|
||||||
"presentLabelsPreference": [
|
|
||||||
"dog"
|
|
||||||
],
|
|
||||||
"absentLabelsPreference": [
|
|
||||||
"cat"
|
|
||||||
]
|
|
||||||
}`),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "ServiceAffinity",
|
Name: "ServiceAffinity",
|
||||||
Args: &runtime.Unknown{
|
Args: &config.ServiceAffinityArgs{
|
||||||
Raw: []byte(`{
|
AffinityLabels: []string{"foo", "bar"},
|
||||||
affinityLabels: [
|
AntiAffinityLabelsPreference: []string{"disk", "flash"},
|
||||||
"foo",
|
|
||||||
"bar"
|
|
||||||
],
|
|
||||||
antiAffinityLabelsPreference: [
|
|
||||||
"disk",
|
|
||||||
"flash"
|
|
||||||
]
|
|
||||||
}`),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -20,6 +20,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/config/v1alpha1:go_default_library",
|
"//staging/src/k8s.io/component-base/config/v1alpha1:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1:go_default_library",
|
"//staging/src/k8s.io/kube-scheduler/config/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
||||||
|
@ -17,20 +17,93 @@ limitations under the License.
|
|||||||
package v1alpha2
|
package v1alpha2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
"fmt"
|
||||||
v1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
"sync"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/conversion"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/kube-scheduler/config/v1alpha2"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
"k8s.io/utils/pointer"
|
"k8s.io/utils/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// pluginArgConversionScheme is a scheme with internal and v1alpha2 registered,
|
||||||
|
// used for defaulting/converting typed PluginConfig Args.
|
||||||
|
// Access via getPluginArgConversionScheme()
|
||||||
|
pluginArgConversionScheme *runtime.Scheme
|
||||||
|
initPluginArgConversionScheme sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func getPluginArgConversionScheme() *runtime.Scheme {
|
||||||
|
initPluginArgConversionScheme.Do(func() {
|
||||||
|
// set up the scheme used for plugin arg conversion
|
||||||
|
pluginArgConversionScheme = runtime.NewScheme()
|
||||||
|
utilruntime.Must(AddToScheme(pluginArgConversionScheme))
|
||||||
|
utilruntime.Must(config.AddToScheme(pluginArgConversionScheme))
|
||||||
|
})
|
||||||
|
return pluginArgConversionScheme
|
||||||
|
}
|
||||||
|
|
||||||
func Convert_v1alpha2_KubeSchedulerConfiguration_To_config_KubeSchedulerConfiguration(in *v1alpha2.KubeSchedulerConfiguration, out *config.KubeSchedulerConfiguration, s conversion.Scope) error {
|
func Convert_v1alpha2_KubeSchedulerConfiguration_To_config_KubeSchedulerConfiguration(in *v1alpha2.KubeSchedulerConfiguration, out *config.KubeSchedulerConfiguration, s conversion.Scope) error {
|
||||||
if err := autoConvert_v1alpha2_KubeSchedulerConfiguration_To_config_KubeSchedulerConfiguration(in, out, s); err != nil {
|
if err := autoConvert_v1alpha2_KubeSchedulerConfiguration_To_config_KubeSchedulerConfiguration(in, out, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
out.AlgorithmSource.Provider = pointer.StringPtr(v1alpha2.SchedulerDefaultProviderName)
|
out.AlgorithmSource.Provider = pointer.StringPtr(v1alpha2.SchedulerDefaultProviderName)
|
||||||
|
return convertToInternalPluginConfigArgs(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertToInternalPluginConfigArgs converts PluginConfig#Args into internal
|
||||||
|
// types using a scheme, after applying defaults.
|
||||||
|
func convertToInternalPluginConfigArgs(out *config.KubeSchedulerConfiguration) error {
|
||||||
|
scheme := getPluginArgConversionScheme()
|
||||||
|
for i := range out.Profiles {
|
||||||
|
for j := range out.Profiles[i].PluginConfig {
|
||||||
|
args := out.Profiles[i].PluginConfig[j].Args
|
||||||
|
if args == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, isUnknown := args.(*runtime.Unknown); isUnknown {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
scheme.Default(args)
|
||||||
|
internalArgs, err := scheme.ConvertToVersion(args, config.SchemeGroupVersion)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("converting .Profiles[%d].PluginConfig[%d].Args into internal type: %w", i, j, err)
|
||||||
|
}
|
||||||
|
out.Profiles[i].PluginConfig[j].Args = internalArgs
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Convert_config_KubeSchedulerConfiguration_To_v1alpha2_KubeSchedulerConfiguration(in *config.KubeSchedulerConfiguration, out *v1alpha2.KubeSchedulerConfiguration, s conversion.Scope) error {
|
func Convert_config_KubeSchedulerConfiguration_To_v1alpha2_KubeSchedulerConfiguration(in *config.KubeSchedulerConfiguration, out *v1alpha2.KubeSchedulerConfiguration, s conversion.Scope) error {
|
||||||
return autoConvert_config_KubeSchedulerConfiguration_To_v1alpha2_KubeSchedulerConfiguration(in, out, s)
|
if err := autoConvert_config_KubeSchedulerConfiguration_To_v1alpha2_KubeSchedulerConfiguration(in, out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return convertToExternalPluginConfigArgs(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertToExternalPluginConfigArgs converts PluginConfig#Args into
|
||||||
|
// external (versioned) types using a scheme.
|
||||||
|
func convertToExternalPluginConfigArgs(out *v1alpha2.KubeSchedulerConfiguration) error {
|
||||||
|
scheme := getPluginArgConversionScheme()
|
||||||
|
for i := range out.Profiles {
|
||||||
|
for j := range out.Profiles[i].PluginConfig {
|
||||||
|
args := out.Profiles[i].PluginConfig[j].Args
|
||||||
|
if args.Object == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, isUnknown := args.Object.(*runtime.Unknown); isUnknown {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
externalArgs, err := scheme.ConvertToVersion(args.Object, SchemeGroupVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out.Profiles[i].PluginConfig[j].Args.Object = externalArgs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,6 @@ import (
|
|||||||
policylisters "k8s.io/client-go/listers/policy/v1beta1"
|
policylisters "k8s.io/client-go/listers/policy/v1beta1"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
|
||||||
"k8s.io/kubernetes/pkg/controller/volume/scheduling"
|
"k8s.io/kubernetes/pkg/controller/volume/scheduling"
|
||||||
kubefeatures "k8s.io/kubernetes/pkg/features"
|
kubefeatures "k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/algorithmprovider"
|
"k8s.io/kubernetes/pkg/scheduler/algorithmprovider"
|
||||||
@ -162,12 +161,13 @@ func (c *Configurator) create() (*Scheduler, error) {
|
|||||||
if len(ignoredExtendedResources) > 0 {
|
if len(ignoredExtendedResources) > 0 {
|
||||||
for i := range c.profiles {
|
for i := range c.profiles {
|
||||||
prof := &c.profiles[i]
|
prof := &c.profiles[i]
|
||||||
prof.PluginConfig = append(prof.PluginConfig,
|
pc := schedulerapi.PluginConfig{
|
||||||
frameworkplugins.NewPluginConfig(
|
Name: noderesources.FitName,
|
||||||
noderesources.FitName,
|
Args: &schedulerapi.NodeResourcesFitArgs{
|
||||||
schedulerv1alpha2.NodeResourcesFitArgs{IgnoredResources: ignoredExtendedResources},
|
IgnoredResources: ignoredExtendedResources,
|
||||||
),
|
},
|
||||||
)
|
}
|
||||||
|
prof.PluginConfig = append(prof.PluginConfig, pc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,9 +280,8 @@ func (c *Configurator) createFromConfig(policy schedulerapi.Policy) (*Scheduler,
|
|||||||
// HardPodAffinitySymmetricWeight in the policy config takes precedence over
|
// HardPodAffinitySymmetricWeight in the policy config takes precedence over
|
||||||
// CLI configuration.
|
// CLI configuration.
|
||||||
if policy.HardPodAffinitySymmetricWeight != 0 {
|
if policy.HardPodAffinitySymmetricWeight != 0 {
|
||||||
v := policy.HardPodAffinitySymmetricWeight
|
args.InterPodAffinityArgs = &schedulerapi.InterPodAffinityArgs{
|
||||||
args.InterPodAffinityArgs = &schedulerv1alpha2.InterPodAffinityArgs{
|
HardPodAffinityWeight: policy.HardPodAffinitySymmetricWeight,
|
||||||
HardPodAffinityWeight: &v,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ package scheduler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
@ -161,10 +160,18 @@ func TestCreateFromConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify that node label predicate/priority are converted to framework plugins.
|
// Verify that node label predicate/priority are converted to framework plugins.
|
||||||
wantArgs := `{"Name":"NodeLabel","Args":{"presentLabels":["zone"],"absentLabels":["foo"],"presentLabelsPreference":["l1"],"absentLabelsPreference":["l2"]}}`
|
var wantArgs runtime.Object = &schedulerapi.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"zone"},
|
||||||
|
AbsentLabels: []string{"foo"},
|
||||||
|
PresentLabelsPreference: []string{"l1"},
|
||||||
|
AbsentLabelsPreference: []string{"l2"},
|
||||||
|
}
|
||||||
verifyPluginConvertion(t, nodelabel.Name, []string{"FilterPlugin", "ScorePlugin"}, prof, &factory.profiles[0], 6, wantArgs)
|
verifyPluginConvertion(t, nodelabel.Name, []string{"FilterPlugin", "ScorePlugin"}, prof, &factory.profiles[0], 6, wantArgs)
|
||||||
// Verify that service affinity custom predicate/priority is converted to framework plugin.
|
// Verify that service affinity custom predicate/priority is converted to framework plugin.
|
||||||
wantArgs = `{"Name":"ServiceAffinity","Args":{"affinityLabels":["zone","foo"],"antiAffinityLabelsPreference":["rack","zone"]}}`
|
wantArgs = &schedulerapi.ServiceAffinityArgs{
|
||||||
|
AffinityLabels: []string{"zone", "foo"},
|
||||||
|
AntiAffinityLabelsPreference: []string{"rack", "zone"},
|
||||||
|
}
|
||||||
verifyPluginConvertion(t, serviceaffinity.Name, []string{"FilterPlugin", "ScorePlugin"}, prof, &factory.profiles[0], 6, wantArgs)
|
verifyPluginConvertion(t, serviceaffinity.Name, []string{"FilterPlugin", "ScorePlugin"}, prof, &factory.profiles[0], 6, wantArgs)
|
||||||
// TODO(#87703): Verify all plugin configs.
|
// TODO(#87703): Verify all plugin configs.
|
||||||
})
|
})
|
||||||
@ -172,8 +179,8 @@ func TestCreateFromConfig(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyPluginConvertion(t *testing.T, name string, extentionPoints []string, prof *profile.Profile, cfg *schedulerapi.KubeSchedulerProfile, wantWeight int32, wantArgs string) {
|
func verifyPluginConvertion(t *testing.T, name string, extensionPoints []string, prof *profile.Profile, cfg *schedulerapi.KubeSchedulerProfile, wantWeight int32, wantArgs runtime.Object) {
|
||||||
for _, extensionPoint := range extentionPoints {
|
for _, extensionPoint := range extensionPoints {
|
||||||
plugin, ok := findPlugin(name, extensionPoint, prof)
|
plugin, ok := findPlugin(name, extensionPoint, prof)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("%q plugin does not exist in framework.", name)
|
t.Fatalf("%q plugin does not exist in framework.", name)
|
||||||
@ -185,12 +192,8 @@ func verifyPluginConvertion(t *testing.T, name string, extentionPoints []string,
|
|||||||
}
|
}
|
||||||
// Verify that the policy config is converted to plugin config.
|
// Verify that the policy config is converted to plugin config.
|
||||||
pluginConfig := findPluginConfig(name, cfg)
|
pluginConfig := findPluginConfig(name, cfg)
|
||||||
encoding, err := json.Marshal(pluginConfig)
|
if diff := cmp.Diff(wantArgs, pluginConfig.Args); diff != "" {
|
||||||
if err != nil {
|
t.Errorf("Config for %v plugin mismatch (-want,+got):\n%s", name, diff)
|
||||||
t.Errorf("Failed to marshal %+v: %v", pluginConfig, err)
|
|
||||||
}
|
|
||||||
if string(encoding) != wantArgs {
|
|
||||||
t.Errorf("Config for %v plugin mismatch. got: %v, want: %v", name, string(encoding), wantArgs)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,7 +253,7 @@ func TestCreateFromConfigWithHardPodAffinitySymmetricWeight(t *testing.T) {
|
|||||||
for _, cfg := range factory.profiles[0].PluginConfig {
|
for _, cfg := range factory.profiles[0].PluginConfig {
|
||||||
if cfg.Name == interpodaffinity.Name {
|
if cfg.Name == interpodaffinity.Name {
|
||||||
foundAffinityCfg = true
|
foundAffinityCfg = true
|
||||||
wantArgs := &runtime.Unknown{Raw: []byte(`{"hardPodAffinityWeight":10}`)}
|
wantArgs := &schedulerapi.InterPodAffinityArgs{HardPodAffinityWeight: 10}
|
||||||
|
|
||||||
if diff := cmp.Diff(wantArgs, cfg.Args); diff != "" {
|
if diff := cmp.Diff(wantArgs, cfg.Args); diff != "" {
|
||||||
t.Errorf("wrong InterPodAffinity args (-want, +got): %s", diff)
|
t.Errorf("wrong InterPodAffinity args (-want, +got): %s", diff)
|
||||||
|
@ -31,10 +31,8 @@ go_library(
|
|||||||
"//pkg/scheduler/framework/plugins/volumerestrictions:go_default_library",
|
"//pkg/scheduler/framework/plugins/volumerestrictions:go_default_library",
|
||||||
"//pkg/scheduler/framework/plugins/volumezone:go_default_library",
|
"//pkg/scheduler/framework/plugins/volumezone:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -10,6 +10,7 @@ go_library(
|
|||||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/interpodaffinity",
|
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/interpodaffinity",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//pkg/scheduler/internal/parallelize:go_default_library",
|
"//pkg/scheduler/internal/parallelize:go_default_library",
|
||||||
"//pkg/scheduler/util:go_default_library",
|
"//pkg/scheduler/util:go_default_library",
|
||||||
@ -19,9 +20,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,13 +32,11 @@ go_test(
|
|||||||
],
|
],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//pkg/scheduler/internal/cache:go_default_library",
|
"//pkg/scheduler/internal/cache:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,9 +22,8 @@ import (
|
|||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
"k8s.io/utils/pointer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -46,7 +45,7 @@ var _ framework.ScorePlugin = &InterPodAffinity{}
|
|||||||
|
|
||||||
// InterPodAffinity is a plugin that checks inter pod affinity
|
// InterPodAffinity is a plugin that checks inter pod affinity
|
||||||
type InterPodAffinity struct {
|
type InterPodAffinity struct {
|
||||||
args schedulerv1alpha2.InterPodAffinityArgs
|
args config.InterPodAffinityArgs
|
||||||
sharedLister framework.SharedLister
|
sharedLister framework.SharedLister
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
@ -66,26 +65,30 @@ func New(plArgs runtime.Object, h framework.FrameworkHandle) (framework.Plugin,
|
|||||||
if h.SnapshotSharedLister() == nil {
|
if h.SnapshotSharedLister() == nil {
|
||||||
return nil, fmt.Errorf("SnapshotSharedlister is nil")
|
return nil, fmt.Errorf("SnapshotSharedlister is nil")
|
||||||
}
|
}
|
||||||
pl := &InterPodAffinity{
|
args, err := getArgs(plArgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := ValidateHardPodAffinityWeight(field.NewPath("hardPodAffinityWeight"), args.HardPodAffinityWeight); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &InterPodAffinity{
|
||||||
|
args: args,
|
||||||
sharedLister: h.SnapshotSharedLister(),
|
sharedLister: h.SnapshotSharedLister(),
|
||||||
}
|
}, nil
|
||||||
if err := framework.DecodeInto(plArgs, &pl.args); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := validateArgs(&pl.args); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if pl.args.HardPodAffinityWeight == nil {
|
|
||||||
pl.args.HardPodAffinityWeight = pointer.Int32Ptr(DefaultHardPodAffinityWeight)
|
|
||||||
}
|
|
||||||
return pl, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateArgs(args *schedulerv1alpha2.InterPodAffinityArgs) error {
|
func getArgs(obj runtime.Object) (config.InterPodAffinityArgs, error) {
|
||||||
if args.HardPodAffinityWeight == nil {
|
if obj == nil {
|
||||||
return nil
|
return config.InterPodAffinityArgs{
|
||||||
|
HardPodAffinityWeight: DefaultHardPodAffinityWeight,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
return ValidateHardPodAffinityWeight(field.NewPath("hardPodAffinityWeight"), *args.HardPodAffinityWeight)
|
ptr, ok := obj.(*config.InterPodAffinityArgs)
|
||||||
|
if !ok {
|
||||||
|
return config.InterPodAffinityArgs{}, fmt.Errorf("want args to be of type InterPodAffinityArgs, got %T", obj)
|
||||||
|
}
|
||||||
|
return *ptr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateHardPodAffinityWeight validates that weight is within allowed range.
|
// ValidateHardPodAffinityWeight validates that weight is within allowed range.
|
||||||
|
@ -137,7 +137,7 @@ func (pl *InterPodAffinity) processExistingPod(state *preScoreState, existingPod
|
|||||||
// For every hard pod affinity term of <existingPod>, if <pod> matches the term,
|
// For every hard pod affinity term of <existingPod>, if <pod> matches the term,
|
||||||
// increment <p.counts> for every node in the cluster with the same <term.TopologyKey>
|
// increment <p.counts> for every node in the cluster with the same <term.TopologyKey>
|
||||||
// value as that of <existingPod>'s node by the constant <ipa.hardPodAffinityWeight>
|
// value as that of <existingPod>'s node by the constant <ipa.hardPodAffinityWeight>
|
||||||
if *pl.args.HardPodAffinityWeight > 0 {
|
if pl.args.HardPodAffinityWeight > 0 {
|
||||||
terms := existingPodAffinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution
|
terms := existingPodAffinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution
|
||||||
// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
|
// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
|
||||||
//if len(existingPodAffinity.PodAffinity.RequiredDuringSchedulingRequiredDuringExecution) != 0 {
|
//if len(existingPodAffinity.PodAffinity.RequiredDuringSchedulingRequiredDuringExecution) != 0 {
|
||||||
@ -145,7 +145,7 @@ func (pl *InterPodAffinity) processExistingPod(state *preScoreState, existingPod
|
|||||||
//}
|
//}
|
||||||
for i := range terms {
|
for i := range terms {
|
||||||
term := &terms[i]
|
term := &terms[i]
|
||||||
processedTerm, err := newWeightedAffinityTerm(existingPod, term, *pl.args.HardPodAffinityWeight)
|
processedTerm, err := newWeightedAffinityTerm(existingPod, term, pl.args.HardPodAffinityWeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -18,17 +18,14 @@ package interpodaffinity
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
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/kubernetes/pkg/scheduler/apis/config"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
||||||
"k8s.io/utils/pointer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPreferredAffinity(t *testing.T) {
|
func TestPreferredAffinity(t *testing.T) {
|
||||||
@ -521,8 +518,8 @@ func TestPreferredAffinity(t *testing.T) {
|
|||||||
state := framework.NewCycleState()
|
state := framework.NewCycleState()
|
||||||
snapshot := cache.NewSnapshot(test.pods, test.nodes)
|
snapshot := cache.NewSnapshot(test.pods, test.nodes)
|
||||||
p := &InterPodAffinity{
|
p := &InterPodAffinity{
|
||||||
args: schedulerv1alpha2.InterPodAffinityArgs{
|
args: config.InterPodAffinityArgs{
|
||||||
HardPodAffinityWeight: pointer.Int32Ptr(DefaultHardPodAffinityWeight),
|
HardPodAffinityWeight: DefaultHardPodAffinityWeight,
|
||||||
},
|
},
|
||||||
sharedLister: snapshot,
|
sharedLister: snapshot,
|
||||||
}
|
}
|
||||||
@ -630,7 +627,7 @@ func TestPreferredAffinityWithHardPodAffinitySymmetricWeight(t *testing.T) {
|
|||||||
snapshot := cache.NewSnapshot(test.pods, test.nodes)
|
snapshot := cache.NewSnapshot(test.pods, test.nodes)
|
||||||
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot))
|
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot))
|
||||||
|
|
||||||
args := &runtime.Unknown{Raw: []byte(fmt.Sprintf(`{"hardPodAffinityWeight":%d}`, test.hardPodAffinityWeight))}
|
args := &config.InterPodAffinityArgs{HardPodAffinityWeight: test.hardPodAffinityWeight}
|
||||||
p, err := New(args, fh)
|
p, err := New(args, fh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -17,13 +17,9 @@ limitations under the License.
|
|||||||
package plugins
|
package plugins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
|
||||||
"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/framework/plugins/defaultpodtopologyspread"
|
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/defaultpodtopologyspread"
|
||||||
@ -165,15 +161,15 @@ type ConfigProducerArgs struct {
|
|||||||
// Weight used for priority functions.
|
// Weight used for priority functions.
|
||||||
Weight int32
|
Weight int32
|
||||||
// NodeLabelArgs is the args for the NodeLabel plugin.
|
// NodeLabelArgs is the args for the NodeLabel plugin.
|
||||||
NodeLabelArgs *schedulerv1alpha2.NodeLabelArgs
|
NodeLabelArgs *config.NodeLabelArgs
|
||||||
// RequestedToCapacityRatioArgs is the args for the RequestedToCapacityRatio plugin.
|
// RequestedToCapacityRatioArgs is the args for the RequestedToCapacityRatio plugin.
|
||||||
RequestedToCapacityRatioArgs *schedulerv1alpha2.RequestedToCapacityRatioArgs
|
RequestedToCapacityRatioArgs *config.RequestedToCapacityRatioArgs
|
||||||
// ServiceAffinityArgs is the args for the ServiceAffinity plugin.
|
// ServiceAffinityArgs is the args for the ServiceAffinity plugin.
|
||||||
ServiceAffinityArgs *schedulerv1alpha2.ServiceAffinityArgs
|
ServiceAffinityArgs *config.ServiceAffinityArgs
|
||||||
// NodeResourcesFitArgs is the args for the NodeResources fit filter.
|
// NodeResourcesFitArgs is the args for the NodeResources fit filter.
|
||||||
NodeResourcesFitArgs *schedulerv1alpha2.NodeResourcesFitArgs
|
NodeResourcesFitArgs *config.NodeResourcesFitArgs
|
||||||
// InterPodAffinityArgs is the args for InterPodAffinity plugin
|
// InterPodAffinityArgs is the args for InterPodAffinity plugin
|
||||||
InterPodAffinityArgs *schedulerv1alpha2.InterPodAffinityArgs
|
InterPodAffinityArgs *config.InterPodAffinityArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigProducer returns the set of plugins and their configuration for a
|
// ConfigProducer returns the set of plugins and their configuration for a
|
||||||
@ -227,7 +223,8 @@ func NewLegacyRegistry() *LegacyRegistry {
|
|||||||
plugins.Filter = appendToPluginSet(plugins.Filter, noderesources.FitName, nil)
|
plugins.Filter = appendToPluginSet(plugins.Filter, noderesources.FitName, nil)
|
||||||
plugins.PreFilter = appendToPluginSet(plugins.PreFilter, noderesources.FitName, nil)
|
plugins.PreFilter = appendToPluginSet(plugins.PreFilter, noderesources.FitName, nil)
|
||||||
if args.NodeResourcesFitArgs != nil {
|
if args.NodeResourcesFitArgs != nil {
|
||||||
pluginConfig = append(pluginConfig, NewPluginConfig(noderesources.FitName, args.NodeResourcesFitArgs))
|
pluginConfig = append(pluginConfig,
|
||||||
|
config.PluginConfig{Name: noderesources.FitName, Args: args.NodeResourcesFitArgs})
|
||||||
}
|
}
|
||||||
plugins.Filter = appendToPluginSet(plugins.Filter, nodename.Name, nil)
|
plugins.Filter = appendToPluginSet(plugins.Filter, nodename.Name, nil)
|
||||||
plugins.Filter = appendToPluginSet(plugins.Filter, nodeports.Name, nil)
|
plugins.Filter = appendToPluginSet(plugins.Filter, nodeports.Name, nil)
|
||||||
@ -245,7 +242,8 @@ func NewLegacyRegistry() *LegacyRegistry {
|
|||||||
plugins.Filter = appendToPluginSet(plugins.Filter, noderesources.FitName, nil)
|
plugins.Filter = appendToPluginSet(plugins.Filter, noderesources.FitName, nil)
|
||||||
plugins.PreFilter = appendToPluginSet(plugins.PreFilter, noderesources.FitName, nil)
|
plugins.PreFilter = appendToPluginSet(plugins.PreFilter, noderesources.FitName, nil)
|
||||||
if args.NodeResourcesFitArgs != nil {
|
if args.NodeResourcesFitArgs != nil {
|
||||||
pluginConfig = append(pluginConfig, NewPluginConfig(noderesources.FitName, args.NodeResourcesFitArgs))
|
pluginConfig = append(pluginConfig,
|
||||||
|
config.PluginConfig{Name: noderesources.FitName, Args: args.NodeResourcesFitArgs})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
@ -320,7 +318,8 @@ func NewLegacyRegistry() *LegacyRegistry {
|
|||||||
func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
|
func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
|
||||||
plugins.Filter = appendToPluginSet(plugins.Filter, nodelabel.Name, nil)
|
plugins.Filter = appendToPluginSet(plugins.Filter, nodelabel.Name, nil)
|
||||||
if args.NodeLabelArgs != nil {
|
if args.NodeLabelArgs != nil {
|
||||||
pluginConfig = append(pluginConfig, NewPluginConfig(nodelabel.Name, args.NodeLabelArgs))
|
pluginConfig = append(pluginConfig,
|
||||||
|
config.PluginConfig{Name: nodelabel.Name, Args: args.NodeLabelArgs})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
@ -328,7 +327,8 @@ func NewLegacyRegistry() *LegacyRegistry {
|
|||||||
func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
|
func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
|
||||||
plugins.Filter = appendToPluginSet(plugins.Filter, serviceaffinity.Name, nil)
|
plugins.Filter = appendToPluginSet(plugins.Filter, serviceaffinity.Name, nil)
|
||||||
if args.ServiceAffinityArgs != nil {
|
if args.ServiceAffinityArgs != nil {
|
||||||
pluginConfig = append(pluginConfig, NewPluginConfig(serviceaffinity.Name, args.ServiceAffinityArgs))
|
pluginConfig = append(pluginConfig,
|
||||||
|
config.PluginConfig{Name: serviceaffinity.Name, Args: args.ServiceAffinityArgs})
|
||||||
}
|
}
|
||||||
plugins.PreFilter = appendToPluginSet(plugins.PreFilter, serviceaffinity.Name, nil)
|
plugins.PreFilter = appendToPluginSet(plugins.PreFilter, serviceaffinity.Name, nil)
|
||||||
return
|
return
|
||||||
@ -362,7 +362,8 @@ func NewLegacyRegistry() *LegacyRegistry {
|
|||||||
plugins.PreScore = appendToPluginSet(plugins.PreScore, interpodaffinity.Name, nil)
|
plugins.PreScore = appendToPluginSet(plugins.PreScore, interpodaffinity.Name, nil)
|
||||||
plugins.Score = appendToPluginSet(plugins.Score, interpodaffinity.Name, &args.Weight)
|
plugins.Score = appendToPluginSet(plugins.Score, interpodaffinity.Name, &args.Weight)
|
||||||
if args.InterPodAffinityArgs != nil {
|
if args.InterPodAffinityArgs != nil {
|
||||||
pluginConfig = append(pluginConfig, NewPluginConfig(interpodaffinity.Name, args.InterPodAffinityArgs))
|
pluginConfig = append(pluginConfig,
|
||||||
|
config.PluginConfig{Name: interpodaffinity.Name, Args: args.InterPodAffinityArgs})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
@ -390,7 +391,8 @@ func NewLegacyRegistry() *LegacyRegistry {
|
|||||||
func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
|
func(args ConfigProducerArgs) (plugins config.Plugins, pluginConfig []config.PluginConfig) {
|
||||||
plugins.Score = appendToPluginSet(plugins.Score, noderesources.RequestedToCapacityRatioName, &args.Weight)
|
plugins.Score = appendToPluginSet(plugins.Score, noderesources.RequestedToCapacityRatioName, &args.Weight)
|
||||||
if args.RequestedToCapacityRatioArgs != nil {
|
if args.RequestedToCapacityRatioArgs != nil {
|
||||||
pluginConfig = append(pluginConfig, NewPluginConfig(noderesources.RequestedToCapacityRatioName, args.RequestedToCapacityRatioArgs))
|
pluginConfig = append(pluginConfig,
|
||||||
|
config.PluginConfig{Name: noderesources.RequestedToCapacityRatioName, Args: args.RequestedToCapacityRatioArgs})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
@ -403,7 +405,8 @@ func NewLegacyRegistry() *LegacyRegistry {
|
|||||||
weight := args.Weight * int32(len(args.NodeLabelArgs.PresentLabelsPreference)+len(args.NodeLabelArgs.AbsentLabelsPreference))
|
weight := args.Weight * int32(len(args.NodeLabelArgs.PresentLabelsPreference)+len(args.NodeLabelArgs.AbsentLabelsPreference))
|
||||||
plugins.Score = appendToPluginSet(plugins.Score, nodelabel.Name, &weight)
|
plugins.Score = appendToPluginSet(plugins.Score, nodelabel.Name, &weight)
|
||||||
if args.NodeLabelArgs != nil {
|
if args.NodeLabelArgs != nil {
|
||||||
pluginConfig = append(pluginConfig, NewPluginConfig(nodelabel.Name, args.NodeLabelArgs))
|
pluginConfig = append(pluginConfig,
|
||||||
|
config.PluginConfig{Name: nodelabel.Name, Args: args.NodeLabelArgs})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
@ -415,7 +418,8 @@ func NewLegacyRegistry() *LegacyRegistry {
|
|||||||
weight := args.Weight * int32(len(args.ServiceAffinityArgs.AntiAffinityLabelsPreference))
|
weight := args.Weight * int32(len(args.ServiceAffinityArgs.AntiAffinityLabelsPreference))
|
||||||
plugins.Score = appendToPluginSet(plugins.Score, serviceaffinity.Name, &weight)
|
plugins.Score = appendToPluginSet(plugins.Score, serviceaffinity.Name, &weight)
|
||||||
if args.ServiceAffinityArgs != nil {
|
if args.ServiceAffinityArgs != nil {
|
||||||
pluginConfig = append(pluginConfig, NewPluginConfig(serviceaffinity.Name, args.ServiceAffinityArgs))
|
pluginConfig = append(pluginConfig,
|
||||||
|
config.PluginConfig{Name: serviceaffinity.Name, Args: args.ServiceAffinityArgs})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
@ -487,19 +491,6 @@ func appendToPluginSet(set *config.PluginSet, name string, weight *int32) *confi
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPluginConfig builds a PluginConfig with the struct of args marshaled.
|
|
||||||
// It panics if it fails to marshal.
|
|
||||||
func NewPluginConfig(pluginName string, args interface{}) config.PluginConfig {
|
|
||||||
encoding, err := json.Marshal(args)
|
|
||||||
if err != nil {
|
|
||||||
klog.Fatalf("failed to marshal %+v: %v", args, err)
|
|
||||||
}
|
|
||||||
return config.PluginConfig{
|
|
||||||
Name: pluginName,
|
|
||||||
Args: &runtime.Unknown{Raw: encoding},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProcessPredicatePolicy given a PredicatePolicy, return the plugin name implementing the predicate and update
|
// ProcessPredicatePolicy given a PredicatePolicy, return the plugin name implementing the predicate and update
|
||||||
// the ConfigProducerArgs if necessary.
|
// the ConfigProducerArgs if necessary.
|
||||||
func (lr *LegacyRegistry) ProcessPredicatePolicy(policy config.PredicatePolicy, pluginArgs *ConfigProducerArgs) string {
|
func (lr *LegacyRegistry) ProcessPredicatePolicy(policy config.PredicatePolicy, pluginArgs *ConfigProducerArgs) string {
|
||||||
@ -526,7 +517,7 @@ func (lr *LegacyRegistry) ProcessPredicatePolicy(policy config.PredicatePolicy,
|
|||||||
if policy.Argument.ServiceAffinity != nil {
|
if policy.Argument.ServiceAffinity != nil {
|
||||||
// map LabelsPresence policy to ConfigProducerArgs that's used to configure the ServiceAffinity plugin.
|
// map LabelsPresence policy to ConfigProducerArgs that's used to configure the ServiceAffinity plugin.
|
||||||
if pluginArgs.ServiceAffinityArgs == nil {
|
if pluginArgs.ServiceAffinityArgs == nil {
|
||||||
pluginArgs.ServiceAffinityArgs = &schedulerv1alpha2.ServiceAffinityArgs{}
|
pluginArgs.ServiceAffinityArgs = &config.ServiceAffinityArgs{}
|
||||||
}
|
}
|
||||||
pluginArgs.ServiceAffinityArgs.AffinityLabels = append(pluginArgs.ServiceAffinityArgs.AffinityLabels, policy.Argument.ServiceAffinity.Labels...)
|
pluginArgs.ServiceAffinityArgs.AffinityLabels = append(pluginArgs.ServiceAffinityArgs.AffinityLabels, policy.Argument.ServiceAffinity.Labels...)
|
||||||
|
|
||||||
@ -539,7 +530,7 @@ func (lr *LegacyRegistry) ProcessPredicatePolicy(policy config.PredicatePolicy,
|
|||||||
if policy.Argument.LabelsPresence != nil {
|
if policy.Argument.LabelsPresence != nil {
|
||||||
// Map LabelPresence policy to ConfigProducerArgs that's used to configure the NodeLabel plugin.
|
// Map LabelPresence policy to ConfigProducerArgs that's used to configure the NodeLabel plugin.
|
||||||
if pluginArgs.NodeLabelArgs == nil {
|
if pluginArgs.NodeLabelArgs == nil {
|
||||||
pluginArgs.NodeLabelArgs = &schedulerv1alpha2.NodeLabelArgs{}
|
pluginArgs.NodeLabelArgs = &config.NodeLabelArgs{}
|
||||||
}
|
}
|
||||||
if policy.Argument.LabelsPresence.Presence {
|
if policy.Argument.LabelsPresence.Presence {
|
||||||
pluginArgs.NodeLabelArgs.PresentLabels = append(pluginArgs.NodeLabelArgs.PresentLabels, policy.Argument.LabelsPresence.Labels...)
|
pluginArgs.NodeLabelArgs.PresentLabels = append(pluginArgs.NodeLabelArgs.PresentLabels, policy.Argument.LabelsPresence.Labels...)
|
||||||
@ -587,7 +578,7 @@ func (lr *LegacyRegistry) ProcessPriorityPolicy(policy config.PriorityPolicy, co
|
|||||||
// This name is then used to find the registered plugin and run the plugin instead of the priority.
|
// This name is then used to find the registered plugin and run the plugin instead of the priority.
|
||||||
priorityName = serviceaffinity.Name
|
priorityName = serviceaffinity.Name
|
||||||
if configProducerArgs.ServiceAffinityArgs == nil {
|
if configProducerArgs.ServiceAffinityArgs == nil {
|
||||||
configProducerArgs.ServiceAffinityArgs = &schedulerv1alpha2.ServiceAffinityArgs{}
|
configProducerArgs.ServiceAffinityArgs = &config.ServiceAffinityArgs{}
|
||||||
}
|
}
|
||||||
configProducerArgs.ServiceAffinityArgs.AntiAffinityLabelsPreference = append(
|
configProducerArgs.ServiceAffinityArgs.AntiAffinityLabelsPreference = append(
|
||||||
configProducerArgs.ServiceAffinityArgs.AntiAffinityLabelsPreference,
|
configProducerArgs.ServiceAffinityArgs.AntiAffinityLabelsPreference,
|
||||||
@ -601,7 +592,7 @@ func (lr *LegacyRegistry) ProcessPriorityPolicy(policy config.PriorityPolicy, co
|
|||||||
// This name is then used to find the registered plugin and run the plugin instead of the priority.
|
// This name is then used to find the registered plugin and run the plugin instead of the priority.
|
||||||
priorityName = nodelabel.Name
|
priorityName = nodelabel.Name
|
||||||
if configProducerArgs.NodeLabelArgs == nil {
|
if configProducerArgs.NodeLabelArgs == nil {
|
||||||
configProducerArgs.NodeLabelArgs = &schedulerv1alpha2.NodeLabelArgs{}
|
configProducerArgs.NodeLabelArgs = &config.NodeLabelArgs{}
|
||||||
}
|
}
|
||||||
if policy.Argument.LabelPreference.Presence {
|
if policy.Argument.LabelPreference.Presence {
|
||||||
configProducerArgs.NodeLabelArgs.PresentLabelsPreference = append(
|
configProducerArgs.NodeLabelArgs.PresentLabelsPreference = append(
|
||||||
@ -618,19 +609,19 @@ func (lr *LegacyRegistry) ProcessPriorityPolicy(policy config.PriorityPolicy, co
|
|||||||
|
|
||||||
if policy.Argument.RequestedToCapacityRatioArguments != nil {
|
if policy.Argument.RequestedToCapacityRatioArguments != nil {
|
||||||
policyArgs := policy.Argument.RequestedToCapacityRatioArguments
|
policyArgs := policy.Argument.RequestedToCapacityRatioArguments
|
||||||
args := &schedulerv1alpha2.RequestedToCapacityRatioArgs{}
|
args := &config.RequestedToCapacityRatioArgs{}
|
||||||
|
|
||||||
args.Shape = make([]schedulerv1alpha2.UtilizationShapePoint, len(policyArgs.Shape))
|
args.Shape = make([]config.UtilizationShapePoint, len(policyArgs.Shape))
|
||||||
for i, s := range policyArgs.Shape {
|
for i, s := range policyArgs.Shape {
|
||||||
args.Shape[i] = schedulerv1alpha2.UtilizationShapePoint{
|
args.Shape[i] = config.UtilizationShapePoint{
|
||||||
Utilization: s.Utilization,
|
Utilization: s.Utilization,
|
||||||
Score: s.Score,
|
Score: s.Score,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Resources = make([]schedulerv1alpha2.ResourceSpec, len(policyArgs.Resources))
|
args.Resources = make([]config.ResourceSpec, len(policyArgs.Resources))
|
||||||
for i, r := range policyArgs.Resources {
|
for i, r := range policyArgs.Resources {
|
||||||
args.Resources[i] = schedulerv1alpha2.ResourceSpec{
|
args.Resources[i] = config.ResourceSpec{
|
||||||
Name: r.Name,
|
Name: r.Name,
|
||||||
Weight: r.Weight,
|
Weight: r.Weight,
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@ go_library(
|
|||||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodelabel",
|
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodelabel",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,11 +19,11 @@ go_test(
|
|||||||
srcs = ["node_label_test.go"],
|
srcs = ["node_label_test.go"],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//pkg/scheduler/internal/cache:go_default_library",
|
"//pkg/scheduler/internal/cache:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -51,8 +51,8 @@ func validateNoConflict(presentLabels []string, absentLabels []string) error {
|
|||||||
|
|
||||||
// New initializes a new plugin and returns it.
|
// New initializes a new plugin and returns it.
|
||||||
func New(plArgs runtime.Object, handle framework.FrameworkHandle) (framework.Plugin, error) {
|
func New(plArgs runtime.Object, handle framework.FrameworkHandle) (framework.Plugin, error) {
|
||||||
args := schedulerv1alpha2.NodeLabelArgs{}
|
args, err := getArgs(plArgs)
|
||||||
if err := framework.DecodeInto(plArgs, &args); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateNoConflict(args.PresentLabels, args.AbsentLabels); err != nil {
|
if err := validateNoConflict(args.PresentLabels, args.AbsentLabels); err != nil {
|
||||||
@ -67,10 +67,21 @@ func New(plArgs runtime.Object, handle framework.FrameworkHandle) (framework.Plu
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getArgs(obj runtime.Object) (config.NodeLabelArgs, error) {
|
||||||
|
if obj == nil {
|
||||||
|
return config.NodeLabelArgs{}, nil
|
||||||
|
}
|
||||||
|
ptr, ok := obj.(*config.NodeLabelArgs)
|
||||||
|
if !ok {
|
||||||
|
return config.NodeLabelArgs{}, fmt.Errorf("want args to be of type NodeLabelArgs, got %T", obj)
|
||||||
|
}
|
||||||
|
return *ptr, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NodeLabel checks whether a pod can fit based on the node labels which match a filter that it requests.
|
// NodeLabel checks whether a pod can fit based on the node labels which match a filter that it requests.
|
||||||
type NodeLabel struct {
|
type NodeLabel struct {
|
||||||
handle framework.FrameworkHandle
|
handle framework.FrameworkHandle
|
||||||
args schedulerv1alpha2.NodeLabelArgs
|
args config.NodeLabelArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ framework.FilterPlugin = &NodeLabel{}
|
var _ framework.FilterPlugin = &NodeLabel{}
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
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/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
||||||
)
|
)
|
||||||
@ -30,35 +30,54 @@ import (
|
|||||||
func TestValidateNodeLabelArgs(t *testing.T) {
|
func TestValidateNodeLabelArgs(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args string
|
args config.NodeLabelArgs
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "happy case",
|
name: "happy case",
|
||||||
args: `{"presentLabels" : ["foo", "bar"], "absentLabels" : ["baz"], "presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foo", "bar"},
|
||||||
|
AbsentLabels: []string{"baz"},
|
||||||
|
PresentLabelsPreference: []string{"foo", "bar"},
|
||||||
|
AbsentLabelsPreference: []string{"baz"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "label presence conflict",
|
name: "label presence conflict",
|
||||||
// "bar" exists in both present and absent labels therefore validation should fail.
|
// "bar" exists in both present and absent labels therefore validation should fail.
|
||||||
args: `{"presentLabels" : ["foo", "bar"], "absentLabels" : ["bar", "baz"], "presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foo", "bar"},
|
||||||
|
AbsentLabels: []string{"bar", "baz"},
|
||||||
|
PresentLabelsPreference: []string{"foo", "bar"},
|
||||||
|
AbsentLabelsPreference: []string{"baz"},
|
||||||
|
},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "label preference conflict",
|
name: "label preference conflict",
|
||||||
// "bar" exists in both present and absent labels preferences therefore validation should fail.
|
// "bar" exists in both present and absent labels preferences therefore validation should fail.
|
||||||
args: `{"presentLabels" : ["foo", "bar"], "absentLabels" : ["baz"], "presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["bar", "baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foo", "bar"},
|
||||||
|
AbsentLabels: []string{"baz"},
|
||||||
|
PresentLabelsPreference: []string{"foo", "bar"},
|
||||||
|
AbsentLabelsPreference: []string{"bar", "baz"},
|
||||||
|
},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "both label presence and preference conflict",
|
name: "both label presence and preference conflict",
|
||||||
args: `{"presentLabels" : ["foo", "bar"], "absentLabels" : ["bar", "baz"], "presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["bar", "baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foo", "bar"},
|
||||||
|
AbsentLabels: []string{"bar", "baz"},
|
||||||
|
PresentLabelsPreference: []string{"foo", "bar"},
|
||||||
|
AbsentLabelsPreference: []string{"bar", "baz"},
|
||||||
|
},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
args := &runtime.Unknown{Raw: []byte(test.args)}
|
_, err := New(&test.args, nil)
|
||||||
_, err := New(args, nil)
|
|
||||||
if test.err && err == nil {
|
if test.err && err == nil {
|
||||||
t.Fatal("Plugin initialization should fail.")
|
t.Fatal("Plugin initialization should fail.")
|
||||||
}
|
}
|
||||||
@ -74,57 +93,81 @@ func TestNodeLabelFilter(t *testing.T) {
|
|||||||
var pod *v1.Pod
|
var pod *v1.Pod
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
rawArgs string
|
args config.NodeLabelArgs
|
||||||
res framework.Code
|
res framework.Code
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "present label does not match",
|
name: "present label does not match",
|
||||||
rawArgs: `{"presentLabels" : ["baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"baz"},
|
||||||
|
},
|
||||||
res: framework.UnschedulableAndUnresolvable,
|
res: framework.UnschedulableAndUnresolvable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "absent label does not match",
|
name: "absent label does not match",
|
||||||
rawArgs: `{"absentLabels" : ["baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
AbsentLabels: []string{"baz"},
|
||||||
|
},
|
||||||
res: framework.Success,
|
res: framework.Success,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "one of two present labels matches",
|
name: "one of two present labels matches",
|
||||||
rawArgs: `{"presentLabels" : ["foo", "baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foo", "baz"},
|
||||||
|
},
|
||||||
res: framework.UnschedulableAndUnresolvable,
|
res: framework.UnschedulableAndUnresolvable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "one of two absent labels matches",
|
name: "one of two absent labels matches",
|
||||||
rawArgs: `{"absentLabels" : ["foo", "baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
AbsentLabels: []string{"foo", "baz"},
|
||||||
|
},
|
||||||
res: framework.UnschedulableAndUnresolvable,
|
res: framework.UnschedulableAndUnresolvable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all present abels match",
|
name: "all present labels match",
|
||||||
rawArgs: `{"presentLabels" : ["foo", "bar"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foo", "bar"},
|
||||||
|
},
|
||||||
res: framework.Success,
|
res: framework.Success,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all absent labels match",
|
name: "all absent labels match",
|
||||||
rawArgs: `{"absentLabels" : ["foo", "bar"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
AbsentLabels: []string{"foo", "bar"},
|
||||||
|
},
|
||||||
res: framework.UnschedulableAndUnresolvable,
|
res: framework.UnschedulableAndUnresolvable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "both present and absent label matches",
|
name: "both present and absent label matches",
|
||||||
rawArgs: `{"presentLabels" : ["foo"], "absentLabels" : ["bar"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foo"},
|
||||||
|
AbsentLabels: []string{"bar"},
|
||||||
|
},
|
||||||
res: framework.UnschedulableAndUnresolvable,
|
res: framework.UnschedulableAndUnresolvable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "neither present nor absent label matches",
|
name: "neither present nor absent label matches",
|
||||||
rawArgs: `{"presentLabels" : ["foz"], "absentLabels" : ["baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foz"},
|
||||||
|
AbsentLabels: []string{"baz"},
|
||||||
|
},
|
||||||
res: framework.UnschedulableAndUnresolvable,
|
res: framework.UnschedulableAndUnresolvable,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "present label matches and absent label doesn't match",
|
name: "present label matches and absent label doesn't match",
|
||||||
rawArgs: `{"presentLabels" : ["foo"], "absentLabels" : ["baz"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foo"},
|
||||||
|
AbsentLabels: []string{"baz"},
|
||||||
|
},
|
||||||
res: framework.Success,
|
res: framework.Success,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "present label doesn't match and absent label matches",
|
name: "present label doesn't match and absent label matches",
|
||||||
rawArgs: `{"presentLabels" : ["foz"], "absentLabels" : ["bar"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabels: []string{"foz"},
|
||||||
|
AbsentLabels: []string{"bar"},
|
||||||
|
},
|
||||||
res: framework.UnschedulableAndUnresolvable,
|
res: framework.UnschedulableAndUnresolvable,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -135,8 +178,7 @@ func TestNodeLabelFilter(t *testing.T) {
|
|||||||
nodeInfo := framework.NewNodeInfo()
|
nodeInfo := framework.NewNodeInfo()
|
||||||
nodeInfo.SetNode(&node)
|
nodeInfo.SetNode(&node)
|
||||||
|
|
||||||
args := &runtime.Unknown{Raw: []byte(test.rawArgs)}
|
p, err := New(&test.args, nil)
|
||||||
p, err := New(args, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create plugin: %v", err)
|
t.Fatalf("Failed to create plugin: %v", err)
|
||||||
}
|
}
|
||||||
@ -151,73 +193,102 @@ func TestNodeLabelFilter(t *testing.T) {
|
|||||||
|
|
||||||
func TestNodeLabelScore(t *testing.T) {
|
func TestNodeLabelScore(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
rawArgs string
|
args config.NodeLabelArgs
|
||||||
want int64
|
want int64
|
||||||
name string
|
name string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
want: framework.MaxNodeScore,
|
want: framework.MaxNodeScore,
|
||||||
rawArgs: `{"presentLabelsPreference" : ["foo"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabelsPreference: []string{"foo"},
|
||||||
|
},
|
||||||
name: "one present label match",
|
name: "one present label match",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: 0,
|
want: 0,
|
||||||
rawArgs: `{"presentLabelsPreference" : ["somelabel"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabelsPreference: []string{"somelabel"},
|
||||||
|
},
|
||||||
name: "one present label mismatch",
|
name: "one present label mismatch",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: framework.MaxNodeScore,
|
want: framework.MaxNodeScore,
|
||||||
rawArgs: `{"presentLabelsPreference" : ["foo", "bar"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabelsPreference: []string{"foo", "bar"},
|
||||||
|
},
|
||||||
name: "two present labels match",
|
name: "two present labels match",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: 0,
|
want: 0,
|
||||||
rawArgs: `{"presentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabelsPreference: []string{"somelabel1", "somelabel2"},
|
||||||
|
},
|
||||||
name: "two present labels mismatch",
|
name: "two present labels mismatch",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: framework.MaxNodeScore / 2,
|
want: framework.MaxNodeScore / 2,
|
||||||
rawArgs: `{"presentLabelsPreference" : ["foo", "somelabel"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabelsPreference: []string{"foo", "somelabel"},
|
||||||
|
},
|
||||||
name: "two present labels only one matches",
|
name: "two present labels only one matches",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: 0,
|
want: 0,
|
||||||
rawArgs: `{"absentLabelsPreference" : ["foo"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
AbsentLabelsPreference: []string{"foo"},
|
||||||
|
},
|
||||||
name: "one absent label match",
|
name: "one absent label match",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: framework.MaxNodeScore,
|
want: framework.MaxNodeScore,
|
||||||
rawArgs: `{"absentLabelsPreference" : ["somelabel"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
AbsentLabelsPreference: []string{"somelabel"},
|
||||||
|
},
|
||||||
name: "one absent label mismatch",
|
name: "one absent label mismatch",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: 0,
|
want: 0,
|
||||||
rawArgs: `{"absentLabelsPreference" : ["foo", "bar"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
AbsentLabelsPreference: []string{"foo", "bar"},
|
||||||
|
},
|
||||||
name: "two absent labels match",
|
name: "two absent labels match",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: framework.MaxNodeScore,
|
want: framework.MaxNodeScore,
|
||||||
rawArgs: `{"absentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
AbsentLabelsPreference: []string{"somelabel1", "somelabel2"},
|
||||||
|
},
|
||||||
name: "two absent labels mismatch",
|
name: "two absent labels mismatch",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: framework.MaxNodeScore / 2,
|
want: framework.MaxNodeScore / 2,
|
||||||
rawArgs: `{"absentLabelsPreference" : ["foo", "somelabel"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
AbsentLabelsPreference: []string{"foo", "somelabel"},
|
||||||
|
},
|
||||||
name: "two absent labels only one matches",
|
name: "two absent labels only one matches",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: framework.MaxNodeScore,
|
want: framework.MaxNodeScore,
|
||||||
rawArgs: `{"presentLabelsPreference" : ["foo", "bar"], "absentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabelsPreference: []string{"foo", "bar"},
|
||||||
|
AbsentLabelsPreference: []string{"somelabel1", "somelabel2"},
|
||||||
|
},
|
||||||
name: "two present labels match, two absent labels mismatch",
|
name: "two present labels match, two absent labels mismatch",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: 0,
|
want: 0,
|
||||||
rawArgs: `{"absentLabelsPreference" : ["foo", "bar"], "presentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabelsPreference: []string{"somelabel1", "somelabel2"},
|
||||||
|
AbsentLabelsPreference: []string{"foo", "bar"},
|
||||||
|
},
|
||||||
name: "two present labels both mismatch, two absent labels both match",
|
name: "two present labels both mismatch, two absent labels both match",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
want: 3 * framework.MaxNodeScore / 4,
|
want: 3 * framework.MaxNodeScore / 4,
|
||||||
rawArgs: `{"presentLabelsPreference" : ["foo", "somelabel"], "absentLabelsPreference" : ["somelabel1", "somelabel2"]}`,
|
args: config.NodeLabelArgs{
|
||||||
|
PresentLabelsPreference: []string{"foo", "somelabel"},
|
||||||
|
AbsentLabelsPreference: []string{"somelabel1", "somelabel2"},
|
||||||
|
},
|
||||||
name: "two present labels one matches, two absent labels mismatch",
|
name: "two present labels one matches, two absent labels mismatch",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -227,8 +298,7 @@ func TestNodeLabelScore(t *testing.T) {
|
|||||||
state := framework.NewCycleState()
|
state := framework.NewCycleState()
|
||||||
node := &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: map[string]string{"foo": "", "bar": ""}}}
|
node := &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: map[string]string{"foo": "", "bar": ""}}}
|
||||||
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(cache.NewSnapshot(nil, []*v1.Node{node})))
|
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(cache.NewSnapshot(nil, []*v1.Node{node})))
|
||||||
args := &runtime.Unknown{Raw: []byte(test.rawArgs)}
|
p, err := New(&test.args, fh)
|
||||||
p, err := New(args, fh)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create plugin: %+v", err)
|
t.Fatalf("Failed to create plugin: %+v", err)
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -59,15 +58,14 @@ go_test(
|
|||||||
deps = [
|
deps = [
|
||||||
"//pkg/apis/core/v1/helper:go_default_library",
|
"//pkg/apis/core/v1/helper:go_default_library",
|
||||||
"//pkg/features:go_default_library",
|
"//pkg/features:go_default_library",
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//pkg/scheduler/internal/cache:go_default_library",
|
"//pkg/scheduler/internal/cache:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
|
||||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -24,9 +24,9 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
|
||||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -64,16 +64,27 @@ func (f *Fit) Name() string {
|
|||||||
|
|
||||||
// NewFit initializes a new plugin and returns it.
|
// NewFit initializes a new plugin and returns it.
|
||||||
func NewFit(plArgs runtime.Object, _ framework.FrameworkHandle) (framework.Plugin, error) {
|
func NewFit(plArgs runtime.Object, _ framework.FrameworkHandle) (framework.Plugin, error) {
|
||||||
args := &schedulerv1alpha2.NodeResourcesFitArgs{}
|
args, err := getFitArgs(plArgs)
|
||||||
if err := framework.DecodeInto(plArgs, args); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
fit := &Fit{
|
||||||
fit := &Fit{}
|
ignoredResources: sets.NewString(args.IgnoredResources...),
|
||||||
fit.ignoredResources = sets.NewString(args.IgnoredResources...)
|
}
|
||||||
return fit, nil
|
return fit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getFitArgs(obj runtime.Object) (config.NodeResourcesFitArgs, error) {
|
||||||
|
if obj == nil {
|
||||||
|
return config.NodeResourcesFitArgs{}, nil
|
||||||
|
}
|
||||||
|
ptr, ok := obj.(*config.NodeResourcesFitArgs)
|
||||||
|
if !ok {
|
||||||
|
return config.NodeResourcesFitArgs{}, fmt.Errorf("want args to be of type NodeResourcesFitArgs, got %T", obj)
|
||||||
|
}
|
||||||
|
return *ptr, nil
|
||||||
|
}
|
||||||
|
|
||||||
// computePodResourceRequest returns a framework.Resource that covers the largest
|
// computePodResourceRequest returns a framework.Resource that covers the largest
|
||||||
// width in each resource dimension. Because init-containers run sequentially, we collect
|
// width in each resource dimension. Because init-containers run sequentially, we collect
|
||||||
// the max in each dimension iteratively. In contrast, we sum the resource vectors for
|
// the max in each dimension iteratively. In contrast, we sum the resource vectors for
|
||||||
|
@ -19,10 +19,11 @@ package noderesources
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||||
@ -94,7 +95,7 @@ func TestEnoughRequests(t *testing.T) {
|
|||||||
pod *v1.Pod
|
pod *v1.Pod
|
||||||
nodeInfo *framework.NodeInfo
|
nodeInfo *framework.NodeInfo
|
||||||
name string
|
name string
|
||||||
ignoredResources []byte
|
args config.NodeResourcesFitArgs
|
||||||
wantInsufficientResources []InsufficientResource
|
wantInsufficientResources []InsufficientResource
|
||||||
wantStatus *framework.Status
|
wantStatus *framework.Status
|
||||||
}{
|
}{
|
||||||
@ -341,7 +342,9 @@ func TestEnoughRequests(t *testing.T) {
|
|||||||
pod: newResourcePod(
|
pod: newResourcePod(
|
||||||
framework.Resource{MilliCPU: 1, Memory: 1, ScalarResources: map[v1.ResourceName]int64{extendedResourceB: 1}}),
|
framework.Resource{MilliCPU: 1, Memory: 1, ScalarResources: map[v1.ResourceName]int64{extendedResourceB: 1}}),
|
||||||
nodeInfo: framework.NewNodeInfo(newResourcePod(framework.Resource{MilliCPU: 0, Memory: 0})),
|
nodeInfo: framework.NewNodeInfo(newResourcePod(framework.Resource{MilliCPU: 0, Memory: 0})),
|
||||||
ignoredResources: []byte(`{"IgnoredResources" : ["example.com/bbb"]}`),
|
args: config.NodeResourcesFitArgs{
|
||||||
|
IgnoredResources: []string{"example.com/bbb"},
|
||||||
|
},
|
||||||
name: "skip checking ignored extended resource",
|
name: "skip checking ignored extended resource",
|
||||||
wantInsufficientResources: []InsufficientResource{},
|
wantInsufficientResources: []InsufficientResource{},
|
||||||
},
|
},
|
||||||
@ -371,8 +374,10 @@ func TestEnoughRequests(t *testing.T) {
|
|||||||
node := v1.Node{Status: v1.NodeStatus{Capacity: makeResources(10, 20, 32, 5, 20, 5).Capacity, Allocatable: makeAllocatableResources(10, 20, 32, 5, 20, 5)}}
|
node := v1.Node{Status: v1.NodeStatus{Capacity: makeResources(10, 20, 32, 5, 20, 5).Capacity, Allocatable: makeAllocatableResources(10, 20, 32, 5, 20, 5)}}
|
||||||
test.nodeInfo.SetNode(&node)
|
test.nodeInfo.SetNode(&node)
|
||||||
|
|
||||||
args := &runtime.Unknown{Raw: test.ignoredResources}
|
p, err := NewFit(&test.args, nil)
|
||||||
p, _ := NewFit(args, nil)
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
cycleState := framework.NewCycleState()
|
cycleState := framework.NewCycleState()
|
||||||
preFilterStatus := p.(framework.PreFilterPlugin).PreFilter(context.Background(), cycleState, test.pod)
|
preFilterStatus := p.(framework.PreFilterPlugin).PreFilter(context.Background(), cycleState, test.pod)
|
||||||
if !preFilterStatus.IsSuccess() {
|
if !preFilterStatus.IsSuccess() {
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
|
||||||
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
)
|
)
|
||||||
@ -49,8 +48,8 @@ type functionShapePoint struct {
|
|||||||
|
|
||||||
// NewRequestedToCapacityRatio initializes a new plugin and returns it.
|
// NewRequestedToCapacityRatio initializes a new plugin and returns it.
|
||||||
func NewRequestedToCapacityRatio(plArgs runtime.Object, handle framework.FrameworkHandle) (framework.Plugin, error) {
|
func NewRequestedToCapacityRatio(plArgs runtime.Object, handle framework.FrameworkHandle) (framework.Plugin, error) {
|
||||||
args := &schedulerv1alpha2.RequestedToCapacityRatioArgs{}
|
args, err := getRequestedToCapacityRatioArgs(plArgs)
|
||||||
if err := framework.DecodeInto(plArgs, args); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +91,17 @@ func NewRequestedToCapacityRatio(plArgs runtime.Object, handle framework.Framewo
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRequestedToCapacityRatioArgs(obj runtime.Object) (config.RequestedToCapacityRatioArgs, error) {
|
||||||
|
if obj == nil {
|
||||||
|
return config.RequestedToCapacityRatioArgs{}, nil
|
||||||
|
}
|
||||||
|
ptr, ok := obj.(*config.RequestedToCapacityRatioArgs)
|
||||||
|
if !ok {
|
||||||
|
return config.RequestedToCapacityRatioArgs{}, fmt.Errorf("want args to be of type RequestedToCapacityRatioArgs, got %T", obj)
|
||||||
|
}
|
||||||
|
return *ptr, nil
|
||||||
|
}
|
||||||
|
|
||||||
// RequestedToCapacityRatio is a score plugin that allow users to apply bin packing
|
// RequestedToCapacityRatio is a score plugin that allow users to apply bin packing
|
||||||
// on core resources like CPU, Memory as well as extended resources like accelerators.
|
// on core resources like CPU, Memory as well as extended resources like accelerators.
|
||||||
type RequestedToCapacityRatio struct {
|
type RequestedToCapacityRatio struct {
|
||||||
|
@ -24,8 +24,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
||||||
)
|
)
|
||||||
@ -68,8 +67,17 @@ func TestRequestedToCapacityRatio(t *testing.T) {
|
|||||||
state := framework.NewCycleState()
|
state := framework.NewCycleState()
|
||||||
snapshot := cache.NewSnapshot(test.scheduledPods, test.nodes)
|
snapshot := cache.NewSnapshot(test.scheduledPods, test.nodes)
|
||||||
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot))
|
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot))
|
||||||
args := &runtime.Unknown{Raw: []byte(`{"shape" : [{"utilization" : 0, "score" : 10}, {"utilization" : 100, "score" : 0}], "resources" : [{"name" : "memory", "weight" : 1}, {"name" : "cpu", "weight" : 1}]}`)}
|
args := config.RequestedToCapacityRatioArgs{
|
||||||
p, err := NewRequestedToCapacityRatio(args, fh)
|
Shape: []config.UtilizationShapePoint{
|
||||||
|
{Utilization: 0, Score: 10},
|
||||||
|
{Utilization: 100, Score: 0},
|
||||||
|
},
|
||||||
|
Resources: []config.ResourceSpec{
|
||||||
|
{Name: "memory", Weight: 1},
|
||||||
|
{Name: "cpu", Weight: 1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
p, err := NewRequestedToCapacityRatio(&args, fh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -352,8 +360,16 @@ func TestResourceBinPackingSingleExtended(t *testing.T) {
|
|||||||
state := framework.NewCycleState()
|
state := framework.NewCycleState()
|
||||||
snapshot := cache.NewSnapshot(test.pods, test.nodes)
|
snapshot := cache.NewSnapshot(test.pods, test.nodes)
|
||||||
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot))
|
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot))
|
||||||
args := &runtime.Unknown{Raw: []byte(`{"shape" : [{"utilization" : 0, "score" : 0}, {"utilization" : 100, "score" : 1}], "resources" : [{"name" : "intel.com/foo", "weight" : 1}]}`)}
|
args := config.RequestedToCapacityRatioArgs{
|
||||||
p, err := NewRequestedToCapacityRatio(args, fh)
|
Shape: []config.UtilizationShapePoint{
|
||||||
|
{Utilization: 0, Score: 0},
|
||||||
|
{Utilization: 100, Score: 1},
|
||||||
|
},
|
||||||
|
Resources: []config.ResourceSpec{
|
||||||
|
{Name: "intel.com/foo", Weight: 1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
p, err := NewRequestedToCapacityRatio(&args, fh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -587,8 +603,17 @@ func TestResourceBinPackingMultipleExtended(t *testing.T) {
|
|||||||
state := framework.NewCycleState()
|
state := framework.NewCycleState()
|
||||||
snapshot := cache.NewSnapshot(test.pods, test.nodes)
|
snapshot := cache.NewSnapshot(test.pods, test.nodes)
|
||||||
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot))
|
fh, _ := framework.NewFramework(nil, nil, nil, framework.WithSnapshotSharedLister(snapshot))
|
||||||
args := &runtime.Unknown{Raw: []byte(`{"shape" : [{"utilization" : 0, "score" : 0}, {"utilization" : 100, "score" : 1}], "resources" : [{"name" : "intel.com/foo", "weight" : 3}, {"name" : "intel.com/bar", "weight": 5}]}`)}
|
args := config.RequestedToCapacityRatioArgs{
|
||||||
p, err := NewRequestedToCapacityRatio(args, fh)
|
Shape: []config.UtilizationShapePoint{
|
||||||
|
{Utilization: 0, Score: 0},
|
||||||
|
{Utilization: 100, Score: 1},
|
||||||
|
},
|
||||||
|
Resources: []config.ResourceSpec{
|
||||||
|
{Name: "intel.com/foo", Weight: 3},
|
||||||
|
{Name: "intel.com/bar", Weight: 5},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
p, err := NewRequestedToCapacityRatio(&args, fh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -608,49 +633,3 @@ func TestResourceBinPackingMultipleExtended(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO compatibility test once the plugin args move to v1beta1.
|
|
||||||
// UtilizationShapePoint and ResourceSpec fields of the plugin args struct are not annotated
|
|
||||||
// with JSON tags in v1alpha2 to maintain backward compatibility with the args shipped with v1.18.
|
|
||||||
// See https://github.com/kubernetes/kubernetes/pull/88585#discussion_r405021905
|
|
||||||
|
|
||||||
func TestPluginArgsJSONEncodingIsCaseInsensitive(t *testing.T) {
|
|
||||||
rawArgs := &runtime.Unknown{Raw: []byte(`
|
|
||||||
{
|
|
||||||
"shape": [{"Utilization": 1, "Score": 1}, {"utilization": 2, "score": 2}],
|
|
||||||
"resources": [{"Name":"a","Weight":1},{"name":"b","weight":2}]
|
|
||||||
}
|
|
||||||
`)}
|
|
||||||
|
|
||||||
args := &schedulerv1alpha2.RequestedToCapacityRatioArgs{}
|
|
||||||
if err := framework.DecodeInto(rawArgs, args); err != nil {
|
|
||||||
t.Fatalf("expected no error, got: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedArgs := &schedulerv1alpha2.RequestedToCapacityRatioArgs{
|
|
||||||
Shape: []schedulerv1alpha2.UtilizationShapePoint{
|
|
||||||
{
|
|
||||||
Utilization: 1,
|
|
||||||
Score: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Utilization: 2,
|
|
||||||
Score: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Resources: []schedulerv1alpha2.ResourceSpec{
|
|
||||||
{
|
|
||||||
Name: "a",
|
|
||||||
Weight: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "b",
|
|
||||||
Weight: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(expectedArgs, args) {
|
|
||||||
t.Errorf("expected: \n\t%#v,\ngot: \n\t%#v", expectedArgs, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -11,6 +11,7 @@ go_library(
|
|||||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/podtopologyspread",
|
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/podtopologyspread",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
"//pkg/scheduler/framework/plugins/helper:go_default_library",
|
"//pkg/scheduler/framework/plugins/helper:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//pkg/scheduler/internal/parallelize:go_default_library",
|
"//pkg/scheduler/internal/parallelize:go_default_library",
|
||||||
@ -24,7 +25,6 @@ go_library(
|
|||||||
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/listers/apps/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/apps/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
"//vendor/k8s.io/klog:go_default_library",
|
"//vendor/k8s.io/klog:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -38,6 +38,7 @@ go_test(
|
|||||||
],
|
],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//pkg/scheduler/internal/cache:go_default_library",
|
"//pkg/scheduler/internal/cache:go_default_library",
|
||||||
"//pkg/scheduler/internal/parallelize:go_default_library",
|
"//pkg/scheduler/internal/parallelize:go_default_library",
|
||||||
@ -50,7 +51,6 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
||||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||||
],
|
],
|
||||||
|
@ -28,7 +28,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/internal/parallelize"
|
"k8s.io/kubernetes/pkg/scheduler/internal/parallelize"
|
||||||
@ -518,7 +518,7 @@ func TestPreFilterState(t *testing.T) {
|
|||||||
informerFactory := informers.NewSharedInformerFactory(fake.NewSimpleClientset(tt.objs...), 0)
|
informerFactory := informers.NewSharedInformerFactory(fake.NewSimpleClientset(tt.objs...), 0)
|
||||||
pl := PodTopologySpread{
|
pl := PodTopologySpread{
|
||||||
sharedLister: cache.NewSnapshot(tt.existingPods, tt.nodes),
|
sharedLister: cache.NewSnapshot(tt.existingPods, tt.nodes),
|
||||||
args: schedulerv1alpha2.PodTopologySpreadArgs{
|
args: config.PodTopologySpreadArgs{
|
||||||
DefaultConstraints: tt.defaultConstraints,
|
DefaultConstraints: tt.defaultConstraints,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
appslisters "k8s.io/client-go/listers/apps/v1"
|
appslisters "k8s.io/client-go/listers/apps/v1"
|
||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
corelisters "k8s.io/client-go/listers/core/v1"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ var (
|
|||||||
|
|
||||||
// PodTopologySpread is a plugin that ensures pod's topologySpreadConstraints is satisfied.
|
// PodTopologySpread is a plugin that ensures pod's topologySpreadConstraints is satisfied.
|
||||||
type PodTopologySpread struct {
|
type PodTopologySpread struct {
|
||||||
args schedulerv1alpha2.PodTopologySpreadArgs
|
args config.PodTopologySpreadArgs
|
||||||
sharedLister framework.SharedLister
|
sharedLister framework.SharedLister
|
||||||
services corelisters.ServiceLister
|
services corelisters.ServiceLister
|
||||||
replicationCtrls corelisters.ReplicationControllerLister
|
replicationCtrls corelisters.ReplicationControllerLister
|
||||||
@ -71,17 +71,21 @@ func (pl *PodTopologySpread) BuildArgs() interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New initializes a new plugin and returns it.
|
// New initializes a new plugin and returns it.
|
||||||
func New(args runtime.Object, h framework.FrameworkHandle) (framework.Plugin, error) {
|
func New(plArgs runtime.Object, h framework.FrameworkHandle) (framework.Plugin, error) {
|
||||||
if h.SnapshotSharedLister() == nil {
|
if h.SnapshotSharedLister() == nil {
|
||||||
return nil, fmt.Errorf("SnapshotSharedlister is nil")
|
return nil, fmt.Errorf("SnapshotSharedlister is nil")
|
||||||
}
|
}
|
||||||
pl := &PodTopologySpread{sharedLister: h.SnapshotSharedLister()}
|
args, err := getArgs(plArgs)
|
||||||
if err := framework.DecodeInto(args, &pl.args); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := validateArgs(&pl.args); err != nil {
|
if err := validateArgs(&args); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
pl := &PodTopologySpread{
|
||||||
|
sharedLister: h.SnapshotSharedLister(),
|
||||||
|
args: args,
|
||||||
|
}
|
||||||
if len(pl.args.DefaultConstraints) != 0 {
|
if len(pl.args.DefaultConstraints) != 0 {
|
||||||
if h.SharedInformerFactory() == nil {
|
if h.SharedInformerFactory() == nil {
|
||||||
return nil, fmt.Errorf("SharedInformerFactory is nil")
|
return nil, fmt.Errorf("SharedInformerFactory is nil")
|
||||||
@ -91,6 +95,17 @@ func New(args runtime.Object, h framework.FrameworkHandle) (framework.Plugin, er
|
|||||||
return pl, nil
|
return pl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getArgs(obj runtime.Object) (config.PodTopologySpreadArgs, error) {
|
||||||
|
if obj == nil {
|
||||||
|
return config.PodTopologySpreadArgs{}, nil
|
||||||
|
}
|
||||||
|
ptr, ok := obj.(*config.PodTopologySpreadArgs)
|
||||||
|
if !ok {
|
||||||
|
return config.PodTopologySpreadArgs{}, fmt.Errorf("want args to be of type PodTopologySpreadArgs, got %T", obj)
|
||||||
|
}
|
||||||
|
return *ptr, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (pl *PodTopologySpread) setListers(factory informers.SharedInformerFactory) {
|
func (pl *PodTopologySpread) setListers(factory informers.SharedInformerFactory) {
|
||||||
pl.services = factory.Core().V1().Services().Lister()
|
pl.services = factory.Core().V1().Services().Lister()
|
||||||
pl.replicationCtrls = factory.Core().V1().ReplicationControllers().Lister()
|
pl.replicationCtrls = factory.Core().V1().ReplicationControllers().Lister()
|
||||||
@ -101,7 +116,7 @@ func (pl *PodTopologySpread) setListers(factory informers.SharedInformerFactory)
|
|||||||
// validateArgs replicates the validation from
|
// validateArgs replicates the validation from
|
||||||
// pkg/apis/core/validation.validateTopologySpreadConstraints.
|
// pkg/apis/core/validation.validateTopologySpreadConstraints.
|
||||||
// This has the additional check for .labelSelector to be nil.
|
// This has the additional check for .labelSelector to be nil.
|
||||||
func validateArgs(args *schedulerv1alpha2.PodTopologySpreadArgs) error {
|
func validateArgs(args *config.PodTopologySpreadArgs) error {
|
||||||
var allErrs field.ErrorList
|
var allErrs field.ErrorList
|
||||||
path := field.NewPath("defaultConstraints")
|
path := field.NewPath("defaultConstraints")
|
||||||
for i, c := range args.DefaultConstraints {
|
for i, c := range args.DefaultConstraints {
|
||||||
|
@ -20,12 +20,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
||||||
)
|
)
|
||||||
@ -33,25 +32,13 @@ import (
|
|||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
args runtime.Unknown
|
args config.PodTopologySpreadArgs
|
||||||
wantErr string
|
wantErr string
|
||||||
wantArgs schedulerv1alpha2.PodTopologySpreadArgs
|
|
||||||
}{
|
}{
|
||||||
{name: "empty args"},
|
{name: "empty args"},
|
||||||
{
|
{
|
||||||
name: "valid constraints",
|
name: "valid constraints",
|
||||||
args: runtime.Unknown{
|
args: config.PodTopologySpreadArgs{
|
||||||
ContentType: runtime.ContentTypeYAML,
|
|
||||||
Raw: []byte(`defaultConstraints:
|
|
||||||
- maxSkew: 1
|
|
||||||
topologyKey: "node"
|
|
||||||
whenUnsatisfiable: "ScheduleAnyway"
|
|
||||||
- maxSkew: 5
|
|
||||||
topologyKey: "zone"
|
|
||||||
whenUnsatisfiable: "DoNotSchedule"
|
|
||||||
`),
|
|
||||||
},
|
|
||||||
wantArgs: schedulerv1alpha2.PodTopologySpreadArgs{
|
|
||||||
DefaultConstraints: []v1.TopologySpreadConstraint{
|
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||||
{
|
{
|
||||||
MaxSkew: 1,
|
MaxSkew: 1,
|
||||||
@ -68,66 +55,75 @@ func TestNew(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "repeated constraints",
|
name: "repeated constraints",
|
||||||
args: runtime.Unknown{
|
args: config.PodTopologySpreadArgs{
|
||||||
ContentType: runtime.ContentTypeYAML,
|
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||||
Raw: []byte(`defaultConstraints:
|
{
|
||||||
- maxSkew: 1
|
MaxSkew: 1,
|
||||||
topologyKey: "node"
|
TopologyKey: "node",
|
||||||
whenUnsatisfiable: "ScheduleAnyway"
|
WhenUnsatisfiable: v1.ScheduleAnyway,
|
||||||
- maxSkew: 5
|
},
|
||||||
topologyKey: "node"
|
{
|
||||||
whenUnsatisfiable: "ScheduleAnyway"
|
MaxSkew: 5,
|
||||||
`),
|
TopologyKey: "node",
|
||||||
|
WhenUnsatisfiable: v1.ScheduleAnyway,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
wantErr: "Duplicate value",
|
wantErr: "Duplicate value",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unknown whenUnsatisfiable",
|
name: "unknown whenUnsatisfiable",
|
||||||
args: runtime.Unknown{
|
args: config.PodTopologySpreadArgs{
|
||||||
ContentType: runtime.ContentTypeYAML,
|
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||||
Raw: []byte(`defaultConstraints:
|
{
|
||||||
- maxSkew: 1
|
MaxSkew: 1,
|
||||||
topologyKey: "node"
|
TopologyKey: "node",
|
||||||
whenUnsatisfiable: "Unknown"
|
WhenUnsatisfiable: "Unknown",
|
||||||
`),
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
wantErr: "Unsupported value",
|
wantErr: "Unsupported value",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "negative maxSkew",
|
name: "negative maxSkew",
|
||||||
args: runtime.Unknown{
|
args: config.PodTopologySpreadArgs{
|
||||||
ContentType: runtime.ContentTypeYAML,
|
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||||
Raw: []byte(`defaultConstraints:
|
{
|
||||||
- maxSkew: -1
|
MaxSkew: -1,
|
||||||
topologyKey: "node"
|
TopologyKey: "node",
|
||||||
whenUnsatisfiable: "ScheduleAnyway"
|
WhenUnsatisfiable: v1.ScheduleAnyway,
|
||||||
`),
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
wantErr: "must be greater than zero",
|
wantErr: "must be greater than zero",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty topologyKey",
|
name: "empty topologyKey",
|
||||||
args: runtime.Unknown{
|
args: config.PodTopologySpreadArgs{
|
||||||
ContentType: runtime.ContentTypeYAML,
|
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||||
Raw: []byte(`defaultConstraints:
|
{
|
||||||
- maxSkew: 1
|
MaxSkew: 1,
|
||||||
whenUnsatisfiable: "ScheduleAnyway"
|
WhenUnsatisfiable: v1.ScheduleAnyway,
|
||||||
`),
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
wantErr: "can not be empty",
|
wantErr: "can not be empty",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with label selector",
|
name: "with label selector",
|
||||||
args: runtime.Unknown{
|
args: config.PodTopologySpreadArgs{
|
||||||
ContentType: runtime.ContentTypeYAML,
|
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||||
Raw: []byte(`defaultConstraints:
|
{
|
||||||
- maxSkew: 1
|
MaxSkew: 1,
|
||||||
topologyKey: "rack"
|
TopologyKey: "rack",
|
||||||
whenUnsatisfiable: "ScheduleAnyway"
|
WhenUnsatisfiable: v1.ScheduleAnyway,
|
||||||
labelSelector:
|
LabelSelector: &metav1.LabelSelector{
|
||||||
matchLabels:
|
MatchLabels: map[string]string{
|
||||||
foo: "bar"
|
"foo": "bar",
|
||||||
`),
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
wantErr: "constraint must not define a selector",
|
wantErr: "constraint must not define a selector",
|
||||||
},
|
},
|
||||||
@ -142,7 +138,7 @@ func TestNew(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
pl, err := New(&tc.args, f)
|
_, err = New(&tc.args, f)
|
||||||
if len(tc.wantErr) != 0 {
|
if len(tc.wantErr) != 0 {
|
||||||
if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
|
if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
|
||||||
t.Errorf("must fail, got error %q, want %q", err, tc.wantErr)
|
t.Errorf("must fail, got error %q, want %q", err, tc.wantErr)
|
||||||
@ -152,10 +148,6 @@ func TestNew(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
plObj := pl.(*PodTopologySpread)
|
|
||||||
if diff := cmp.Diff(tc.wantArgs, plObj.BuildArgs()); diff != "" {
|
|
||||||
t.Errorf("wrong plugin build args (-want,+got):\n%s", diff)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/internal/parallelize"
|
"k8s.io/kubernetes/pkg/scheduler/internal/parallelize"
|
||||||
@ -195,7 +195,7 @@ func TestPreScoreStateEmptyNodes(t *testing.T) {
|
|||||||
informerFactory := informers.NewSharedInformerFactory(fake.NewSimpleClientset(tt.objs...), 0)
|
informerFactory := informers.NewSharedInformerFactory(fake.NewSimpleClientset(tt.objs...), 0)
|
||||||
pl := PodTopologySpread{
|
pl := PodTopologySpread{
|
||||||
sharedLister: cache.NewSnapshot(nil, tt.nodes),
|
sharedLister: cache.NewSnapshot(nil, tt.nodes),
|
||||||
args: schedulerv1alpha2.PodTopologySpreadArgs{
|
args: config.PodTopologySpreadArgs{
|
||||||
DefaultConstraints: tt.defaultConstraints,
|
DefaultConstraints: tt.defaultConstraints,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -729,7 +729,7 @@ func BenchmarkTestDefaultEvenPodsSpreadPriority(b *testing.B) {
|
|||||||
snapshot := cache.NewSnapshot(existingPods, allNodes)
|
snapshot := cache.NewSnapshot(existingPods, allNodes)
|
||||||
p := &PodTopologySpread{
|
p := &PodTopologySpread{
|
||||||
sharedLister: snapshot,
|
sharedLister: snapshot,
|
||||||
args: schedulerv1alpha2.PodTopologySpreadArgs{
|
args: config.PodTopologySpreadArgs{
|
||||||
DefaultConstraints: []v1.TopologySpreadConstraint{
|
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||||
{MaxSkew: 1, TopologyKey: v1.LabelHostname, WhenUnsatisfiable: v1.ScheduleAnyway},
|
{MaxSkew: 1, TopologyKey: v1.LabelHostname, WhenUnsatisfiable: v1.ScheduleAnyway},
|
||||||
{MaxSkew: 1, TopologyKey: v1.LabelZoneFailureDomain, WhenUnsatisfiable: v1.ScheduleAnyway},
|
{MaxSkew: 1, TopologyKey: v1.LabelZoneFailureDomain, WhenUnsatisfiable: v1.ScheduleAnyway},
|
||||||
|
@ -6,13 +6,13 @@ go_library(
|
|||||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/serviceaffinity",
|
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/serviceaffinity",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
"//pkg/scheduler/framework/plugins/helper:go_default_library",
|
"//pkg/scheduler/framework/plugins/helper:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,12 +21,12 @@ go_test(
|
|||||||
srcs = ["service_affinity_test.go"],
|
srcs = ["service_affinity_test.go"],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/scheduler/apis/config:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||||
"//pkg/scheduler/framework/v1alpha1/fake:go_default_library",
|
"//pkg/scheduler/framework/v1alpha1/fake:go_default_library",
|
||||||
"//pkg/scheduler/internal/cache:go_default_library",
|
"//pkg/scheduler/internal/cache:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
corelisters "k8s.io/client-go/listers/core/v1"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper"
|
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
)
|
)
|
||||||
@ -64,12 +64,11 @@ func (s *preFilterState) Clone() framework.StateData {
|
|||||||
|
|
||||||
// New initializes a new plugin and returns it.
|
// New initializes a new plugin and returns it.
|
||||||
func New(plArgs runtime.Object, handle framework.FrameworkHandle) (framework.Plugin, error) {
|
func New(plArgs runtime.Object, handle framework.FrameworkHandle) (framework.Plugin, error) {
|
||||||
args := schedulerv1alpha2.ServiceAffinityArgs{}
|
args, err := getArgs(plArgs)
|
||||||
if err := framework.DecodeInto(plArgs, &args); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
informerFactory := handle.SharedInformerFactory()
|
serviceLister := handle.SharedInformerFactory().Core().V1().Services().Lister()
|
||||||
serviceLister := informerFactory.Core().V1().Services().Lister()
|
|
||||||
|
|
||||||
return &ServiceAffinity{
|
return &ServiceAffinity{
|
||||||
sharedLister: handle.SnapshotSharedLister(),
|
sharedLister: handle.SnapshotSharedLister(),
|
||||||
@ -78,9 +77,20 @@ func New(plArgs runtime.Object, handle framework.FrameworkHandle) (framework.Plu
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getArgs(obj runtime.Object) (config.ServiceAffinityArgs, error) {
|
||||||
|
if obj == nil {
|
||||||
|
return config.ServiceAffinityArgs{}, nil
|
||||||
|
}
|
||||||
|
ptr, ok := obj.(*config.ServiceAffinityArgs)
|
||||||
|
if !ok {
|
||||||
|
return config.ServiceAffinityArgs{}, fmt.Errorf("want args to be of type ServiceAffinityArgs, got %T", obj)
|
||||||
|
}
|
||||||
|
return *ptr, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceAffinity is a plugin that checks service affinity.
|
// ServiceAffinity is a plugin that checks service affinity.
|
||||||
type ServiceAffinity struct {
|
type ServiceAffinity struct {
|
||||||
args schedulerv1alpha2.ServiceAffinityArgs
|
args config.ServiceAffinityArgs
|
||||||
sharedLister framework.SharedLister
|
sharedLister framework.SharedLister
|
||||||
serviceLister corelisters.ServiceLister
|
serviceLister corelisters.ServiceLister
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
schedulerv1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||||
fakeframework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/fake"
|
fakeframework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/fake"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
|
||||||
@ -165,7 +165,7 @@ func TestServiceAffinity(t *testing.T) {
|
|||||||
p := &ServiceAffinity{
|
p := &ServiceAffinity{
|
||||||
sharedLister: snapshot,
|
sharedLister: snapshot,
|
||||||
serviceLister: fakeframework.ServiceLister(test.services),
|
serviceLister: fakeframework.ServiceLister(test.services),
|
||||||
args: schedulerv1alpha2.ServiceAffinityArgs{
|
args: config.ServiceAffinityArgs{
|
||||||
AffinityLabels: test.labels,
|
AffinityLabels: test.labels,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -389,7 +389,7 @@ func TestServiceAffinityScore(t *testing.T) {
|
|||||||
p := &ServiceAffinity{
|
p := &ServiceAffinity{
|
||||||
sharedLister: snapshot,
|
sharedLister: snapshot,
|
||||||
serviceLister: serviceLister,
|
serviceLister: serviceLister,
|
||||||
args: schedulerv1alpha2.ServiceAffinityArgs{
|
args: config.ServiceAffinityArgs{
|
||||||
AntiAffinityLabelsPreference: test.labels,
|
AntiAffinityLabelsPreference: test.labels,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -606,7 +606,7 @@ func TestPreFilterDisabled(t *testing.T) {
|
|||||||
node := v1.Node{}
|
node := v1.Node{}
|
||||||
nodeInfo.SetNode(&node)
|
nodeInfo.SetNode(&node)
|
||||||
p := &ServiceAffinity{
|
p := &ServiceAffinity{
|
||||||
args: schedulerv1alpha2.ServiceAffinityArgs{
|
args: config.ServiceAffinityArgs{
|
||||||
AffinityLabels: []string{"region"},
|
AffinityLabels: []string{"region"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//staging/src/k8s.io/component-base/config/v1alpha1:go_default_library",
|
"//staging/src/k8s.io/component-base/config/v1alpha1:go_default_library",
|
||||||
"//staging/src/k8s.io/kube-scheduler/config/v1:go_default_library",
|
"//staging/src/k8s.io/kube-scheduler/config/v1:go_default_library",
|
||||||
|
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,10 +17,14 @@ limitations under the License.
|
|||||||
package v1alpha2
|
package v1alpha2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
||||||
v1 "k8s.io/kube-scheduler/config/v1"
|
v1 "k8s.io/kube-scheduler/config/v1"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -73,17 +77,17 @@ type KubeSchedulerConfiguration struct {
|
|||||||
// Duration to wait for a binding operation to complete before timing out
|
// Duration to wait for a binding operation to complete before timing out
|
||||||
// Value must be non-negative integer. The value zero indicates no waiting.
|
// Value must be non-negative integer. The value zero indicates no waiting.
|
||||||
// If this value is nil, the default value will be used.
|
// If this value is nil, the default value will be used.
|
||||||
BindTimeoutSeconds *int64 `json:"bindTimeoutSeconds"`
|
BindTimeoutSeconds *int64 `json:"bindTimeoutSeconds,omitempty"`
|
||||||
|
|
||||||
// PodInitialBackoffSeconds is the initial backoff for unschedulable pods.
|
// PodInitialBackoffSeconds is the initial backoff for unschedulable pods.
|
||||||
// If specified, it must be greater than 0. If this value is null, the default value (1s)
|
// If specified, it must be greater than 0. If this value is null, the default value (1s)
|
||||||
// will be used.
|
// will be used.
|
||||||
PodInitialBackoffSeconds *int64 `json:"podInitialBackoffSeconds"`
|
PodInitialBackoffSeconds *int64 `json:"podInitialBackoffSeconds,omitempty"`
|
||||||
|
|
||||||
// PodMaxBackoffSeconds is the max backoff for unschedulable pods.
|
// PodMaxBackoffSeconds is the max backoff for unschedulable pods.
|
||||||
// If specified, it must be greater than podInitialBackoffSeconds. If this value is null,
|
// If specified, it must be greater than podInitialBackoffSeconds. If this value is null,
|
||||||
// the default value (10s) will be used.
|
// the default value (10s) will be used.
|
||||||
PodMaxBackoffSeconds *int64 `json:"podMaxBackoffSeconds"`
|
PodMaxBackoffSeconds *int64 `json:"podMaxBackoffSeconds,omitempty"`
|
||||||
|
|
||||||
// Profiles are scheduling profiles that kube-scheduler supports. Pods can
|
// Profiles are scheduling profiles that kube-scheduler supports. Pods can
|
||||||
// choose to be scheduled under a particular profile by setting its associated
|
// choose to be scheduled under a particular profile by setting its associated
|
||||||
@ -91,12 +95,40 @@ type KubeSchedulerConfiguration struct {
|
|||||||
// with the "default-scheduler" profile, if present here.
|
// with the "default-scheduler" profile, if present here.
|
||||||
// +listType=map
|
// +listType=map
|
||||||
// +listMapKey=schedulerName
|
// +listMapKey=schedulerName
|
||||||
Profiles []KubeSchedulerProfile `json:"profiles"`
|
Profiles []KubeSchedulerProfile `json:"profiles,omitempty"`
|
||||||
|
|
||||||
// Extenders are the list of scheduler extenders, each holding the values of how to communicate
|
// Extenders are the list of scheduler extenders, each holding the values of how to communicate
|
||||||
// with the extender. These extenders are shared by all scheduler profiles.
|
// with the extender. These extenders are shared by all scheduler profiles.
|
||||||
// +listType=set
|
// +listType=set
|
||||||
Extenders []v1.Extender `json:"extenders"`
|
Extenders []v1.Extender `json:"extenders,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeNestedObjects decodes plugin args for known types.
|
||||||
|
func (c *KubeSchedulerConfiguration) DecodeNestedObjects(d runtime.Decoder) error {
|
||||||
|
for i := range c.Profiles {
|
||||||
|
prof := &c.Profiles[i]
|
||||||
|
for j := range prof.PluginConfig {
|
||||||
|
err := prof.PluginConfig[j].decodeNestedObjects(d)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("decoding .profiles[%d].pluginConfig[%d]: %w", i, j, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeNestedObjects encodes plugin args.
|
||||||
|
func (c *KubeSchedulerConfiguration) EncodeNestedObjects(e runtime.Encoder) error {
|
||||||
|
for i := range c.Profiles {
|
||||||
|
prof := &c.Profiles[i]
|
||||||
|
for j := range prof.PluginConfig {
|
||||||
|
err := prof.PluginConfig[j].encodeNestedObjects(e)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("encoding .profiles[%d].pluginConfig[%d]: %w", i, j, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// KubeSchedulerProfile is a scheduling profile.
|
// KubeSchedulerProfile is a scheduling profile.
|
||||||
@ -202,3 +234,41 @@ type PluginConfig struct {
|
|||||||
// Args defines the arguments passed to the plugins at the time of initialization. Args can have arbitrary structure.
|
// Args defines the arguments passed to the plugins at the time of initialization. Args can have arbitrary structure.
|
||||||
Args runtime.RawExtension `json:"args,omitempty"`
|
Args runtime.RawExtension `json:"args,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *PluginConfig) decodeNestedObjects(d runtime.Decoder) error {
|
||||||
|
gvk := SchemeGroupVersion.WithKind(c.Name + "Args")
|
||||||
|
// dry-run to detect and skip out-of-tree plugin args.
|
||||||
|
if _, _, err := d.Decode(nil, &gvk, nil); runtime.IsNotRegisteredError(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, parsedGvk, err := d.Decode(c.Args.Raw, &gvk, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("decoding args for plugin %s: %w", c.Name, err)
|
||||||
|
}
|
||||||
|
if parsedGvk.GroupKind() != gvk.GroupKind() {
|
||||||
|
return fmt.Errorf("args for plugin %s were not of type %s, got %s", c.Name, gvk.GroupKind(), parsedGvk.GroupKind())
|
||||||
|
}
|
||||||
|
c.Args.Object = obj
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PluginConfig) encodeNestedObjects(e runtime.Encoder) error {
|
||||||
|
if c.Args.Object == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := e.Encode(c.Args.Object, &buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// The <e> encoder might be a YAML encoder, but the parent encoder expects
|
||||||
|
// JSON output, so we convert YAML back to JSON.
|
||||||
|
// This is a no-op if <e> produces JSON.
|
||||||
|
json, err := yaml.YAMLToJSON(buf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Args.Raw = json
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
package v1alpha2
|
package v1alpha2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
gojson "encoding/json"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
@ -95,7 +97,7 @@ type RequestedToCapacityRatioArgs struct {
|
|||||||
Resources []ResourceSpec `json:"resources,omitempty"`
|
Resources []ResourceSpec `json:"resources,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add JSON tags and backward compatible conversion in v1beta1.
|
// TODO add JSON tags and remove custom unmarshalling in v1beta1.
|
||||||
// UtilizationShapePoint and ResourceSpec fields are not annotated with JSON tags in v1alpha2
|
// UtilizationShapePoint and ResourceSpec fields are not annotated with JSON tags in v1alpha2
|
||||||
// to maintain backward compatibility with the args shipped with v1.18.
|
// to maintain backward compatibility with the args shipped with v1.18.
|
||||||
// See https://github.com/kubernetes/kubernetes/pull/88585#discussion_r405021905
|
// See https://github.com/kubernetes/kubernetes/pull/88585#discussion_r405021905
|
||||||
@ -108,6 +110,13 @@ type UtilizationShapePoint struct {
|
|||||||
Score int32
|
Score int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON provides case insensitive unmarshalling for the type.
|
||||||
|
// TODO remove when copying to v1beta1.
|
||||||
|
func (t *UtilizationShapePoint) UnmarshalJSON(data []byte) error {
|
||||||
|
type internal *UtilizationShapePoint
|
||||||
|
return gojson.Unmarshal(data, internal(t))
|
||||||
|
}
|
||||||
|
|
||||||
// ResourceSpec represents single resource and weight for bin packing of priority RequestedToCapacityRatioArguments.
|
// ResourceSpec represents single resource and weight for bin packing of priority RequestedToCapacityRatioArguments.
|
||||||
type ResourceSpec struct {
|
type ResourceSpec struct {
|
||||||
// Name of the resource to be managed by RequestedToCapacityRatio function.
|
// Name of the resource to be managed by RequestedToCapacityRatio function.
|
||||||
@ -116,6 +125,13 @@ type ResourceSpec struct {
|
|||||||
Weight int64
|
Weight int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON provides case insensitive unmarshalling for the type.
|
||||||
|
// TODO remove when copying to v1beta1.
|
||||||
|
func (t *ResourceSpec) UnmarshalJSON(data []byte) error {
|
||||||
|
type internal *ResourceSpec
|
||||||
|
return gojson.Unmarshal(data, internal(t))
|
||||||
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
// ServiceAffinityArgs holds arguments used to configure the ServiceAffinity plugin.
|
// ServiceAffinityArgs holds arguments used to configure the ServiceAffinity plugin.
|
||||||
|
@ -9,6 +9,7 @@ require (
|
|||||||
k8s.io/api v0.0.0
|
k8s.io/api v0.0.0
|
||||||
k8s.io/apimachinery v0.0.0
|
k8s.io/apimachinery v0.0.0
|
||||||
k8s.io/component-base v0.0.0
|
k8s.io/component-base v0.0.0
|
||||||
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
|
Loading…
Reference in New Issue
Block a user