Merge pull request #90309 from alculquicondor/plugin-args-decoding

Use internal config types in scheduling plugin args
This commit is contained in:
Kubernetes Prow Robot 2020-04-30 05:32:18 -07:00 committed by GitHub
commit 8dd93ca94c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1129 additions and 439 deletions

View File

@ -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",

View File

@ -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
} }

View File

@ -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}`),
},
}, },
}, },
}, },

View File

@ -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",
], ],

View File

@ -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",
],
)

View 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)
}
})
}
}

View File

@ -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"
]
}`),
}, },
}, },
}, },

View File

@ -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",

View File

@ -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
} }

View File

@ -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,
} }
} }

View File

@ -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)

View File

@ -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",
], ],
) )

View File

@ -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",
], ],
) )

View File

@ -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.

View File

@ -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
} }

View File

@ -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)

View File

@ -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,
} }

View File

@ -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",
], ],
) )

View File

@ -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{}

View File

@ -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)
} }

View File

@ -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",
], ],
) )

View File

@ -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

View File

@ -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() {

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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",
], ],

View File

@ -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,
}, },
} }

View File

@ -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 {

View File

@ -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)
}
}) })
} }
} }

View File

@ -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},

View File

@ -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",
], ],
) )

View File

@ -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
} }

View File

@ -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"},
}, },
} }

View File

@ -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",
], ],
) )

View File

@ -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
}

View File

@ -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.

View File

@ -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 (