mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Add decoding of nested scheduling plugin configs
Signed-off-by: Aldo Culquicondor <acondor@google.com>
This commit is contained in:
parent
7814f3aaf7
commit
6b153dc920
@ -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(
|
||||
name = "go_default_library",
|
||||
@ -28,3 +28,19 @@ filegroup(
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["scheme_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/scheduler/apis/config:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
||||
"//vendor/github.com/google/go-cmp/cmp:go_default_library",
|
||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
450
pkg/scheduler/apis/config/scheme/scheme_test.go
Normal file
450
pkg/scheduler/apis/config/scheme/scheme_test.go
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package scheme
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kube-scheduler/config/v1alpha2"
|
||||
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func TestCodecsDecodePluginConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantErr string
|
||||
wantProfiles []config.KubeSchedulerProfile
|
||||
}{
|
||||
{
|
||||
name: "v1alpha2 all plugin args in default profile",
|
||||
data: []byte(`
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
kind: KubeSchedulerConfiguration
|
||||
profiles:
|
||||
- pluginConfig:
|
||||
- name: InterPodAffinity
|
||||
args:
|
||||
hardPodAffinityWeight: 5
|
||||
- name: NodeLabel
|
||||
args:
|
||||
presentLabels: ["foo"]
|
||||
- name: NodeResourcesFit
|
||||
args:
|
||||
ignoredResources: ["foo"]
|
||||
- name: RequestedToCapacityRatio
|
||||
args:
|
||||
shape:
|
||||
- utilization: 1
|
||||
- name: PodTopologySpread
|
||||
args:
|
||||
defaultConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: zone
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
- name: ServiceAffinity
|
||||
args:
|
||||
affinityLabels: ["bar"]
|
||||
`),
|
||||
wantProfiles: []config.KubeSchedulerProfile{
|
||||
{
|
||||
SchedulerName: "default-scheduler",
|
||||
PluginConfig: []config.PluginConfig{
|
||||
{
|
||||
Name: "InterPodAffinity",
|
||||
Args: &config.InterPodAffinityArgs{HardPodAffinityWeight: 5},
|
||||
},
|
||||
{
|
||||
Name: "NodeLabel",
|
||||
Args: &config.NodeLabelArgs{PresentLabels: []string{"foo"}},
|
||||
},
|
||||
{
|
||||
Name: "NodeResourcesFit",
|
||||
Args: &config.NodeResourcesFitArgs{IgnoredResources: []string{"foo"}},
|
||||
},
|
||||
{
|
||||
Name: "RequestedToCapacityRatio",
|
||||
Args: &config.RequestedToCapacityRatioArgs{
|
||||
Shape: []config.UtilizationShapePoint{{Utilization: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "PodTopologySpread",
|
||||
Args: &config.PodTopologySpreadArgs{
|
||||
DefaultConstraints: []v1.TopologySpreadConstraint{
|
||||
{MaxSkew: 1, TopologyKey: "zone", WhenUnsatisfiable: v1.ScheduleAnyway},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "ServiceAffinity",
|
||||
Args: &config.ServiceAffinityArgs{
|
||||
AffinityLabels: []string{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v1alpha2 plugins can include version and kind",
|
||||
data: []byte(`
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
kind: KubeSchedulerConfiguration
|
||||
profiles:
|
||||
- pluginConfig:
|
||||
- name: NodeLabel
|
||||
args:
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
kind: NodeLabelArgs
|
||||
presentLabels: ["bars"]
|
||||
`),
|
||||
wantProfiles: []config.KubeSchedulerProfile{
|
||||
{
|
||||
SchedulerName: "default-scheduler",
|
||||
PluginConfig: []config.PluginConfig{
|
||||
{
|
||||
Name: "NodeLabel",
|
||||
Args: &config.NodeLabelArgs{PresentLabels: []string{"bars"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "plugin group and kind should match the type",
|
||||
data: []byte(`
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
kind: KubeSchedulerConfiguration
|
||||
profiles:
|
||||
- pluginConfig:
|
||||
- name: NodeLabel
|
||||
args:
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
kind: InterPodAffinityArgs
|
||||
`),
|
||||
wantErr: "decoding .profiles[0].pluginConfig[0]: args for plugin NodeLabel were not of type NodeLabelArgs.kubescheduler.config.k8s.io, got InterPodAffinityArgs.kubescheduler.config.k8s.io",
|
||||
},
|
||||
{
|
||||
// TODO: do not replicate this case for v1beta1.
|
||||
name: "v1alpha2 case insensitive RequestedToCapacityRatioArgs",
|
||||
data: []byte(`
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
kind: KubeSchedulerConfiguration
|
||||
profiles:
|
||||
- pluginConfig:
|
||||
- name: RequestedToCapacityRatio
|
||||
args:
|
||||
shape:
|
||||
- utilization: 1
|
||||
score: 2
|
||||
- Utilization: 3
|
||||
Score: 4
|
||||
resources:
|
||||
- name: Upper
|
||||
weight: 1
|
||||
- Name: lower
|
||||
weight: 2
|
||||
`),
|
||||
wantProfiles: []config.KubeSchedulerProfile{
|
||||
{
|
||||
SchedulerName: "default-scheduler",
|
||||
PluginConfig: []config.PluginConfig{
|
||||
{
|
||||
Name: "RequestedToCapacityRatio",
|
||||
Args: &config.RequestedToCapacityRatioArgs{
|
||||
Shape: []config.UtilizationShapePoint{
|
||||
{Utilization: 1, Score: 2},
|
||||
{Utilization: 3, Score: 4},
|
||||
},
|
||||
Resources: []config.ResourceSpec{
|
||||
{Name: "Upper", Weight: 1},
|
||||
{Name: "lower", Weight: 2},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "out-of-tree plugin args",
|
||||
data: []byte(`
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
kind: KubeSchedulerConfiguration
|
||||
profiles:
|
||||
- pluginConfig:
|
||||
- name: OutOfTreePlugin
|
||||
args:
|
||||
foo: bar
|
||||
`),
|
||||
wantProfiles: []config.KubeSchedulerProfile{
|
||||
{
|
||||
SchedulerName: "default-scheduler",
|
||||
PluginConfig: []config.PluginConfig{
|
||||
{
|
||||
Name: "OutOfTreePlugin",
|
||||
Args: &runtime.Unknown{
|
||||
ContentType: "application/json",
|
||||
Raw: []byte(`{"foo":"bar"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty and no plugin args",
|
||||
data: []byte(`
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
kind: KubeSchedulerConfiguration
|
||||
profiles:
|
||||
- pluginConfig:
|
||||
- name: InterPodAffinity
|
||||
args:
|
||||
- name: NodeResourcesFit
|
||||
- name: OutOfTreePlugin
|
||||
args:
|
||||
`),
|
||||
wantProfiles: []config.KubeSchedulerProfile{
|
||||
{
|
||||
SchedulerName: "default-scheduler",
|
||||
PluginConfig: []config.PluginConfig{
|
||||
{
|
||||
Name: "InterPodAffinity",
|
||||
// TODO(acondor): Set default values.
|
||||
Args: &config.InterPodAffinityArgs{},
|
||||
},
|
||||
{
|
||||
Name: "NodeResourcesFit",
|
||||
Args: &config.NodeResourcesFitArgs{},
|
||||
},
|
||||
{Name: "OutOfTreePlugin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
decoder := Codecs.UniversalDecoder()
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obj, gvk, err := decoder.Decode(tt.data, nil, nil)
|
||||
if err != nil {
|
||||
if tt.wantErr != err.Error() {
|
||||
t.Fatalf("got err %w, want %w", err, tt.wantErr)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(tt.wantErr) != 0 {
|
||||
t.Fatal("no error produced, wanted %w", tt.wantErr)
|
||||
}
|
||||
got, ok := obj.(*config.KubeSchedulerConfiguration)
|
||||
if !ok {
|
||||
t.Fatalf("decoded into %s, want %s", gvk, config.SchemeGroupVersion.WithKind("KubeSchedulerConfiguration"))
|
||||
}
|
||||
if diff := cmp.Diff(tt.wantProfiles, got.Profiles); diff != "" {
|
||||
t.Errorf("unexpected configuration (-want,+got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodecsEncodePluginConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
obj runtime.Object
|
||||
version schema.GroupVersion
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "v1alpha2 in-tree and out-of-tree plugins",
|
||||
version: v1alpha2.SchemeGroupVersion,
|
||||
obj: &v1alpha2.KubeSchedulerConfiguration{
|
||||
Profiles: []v1alpha2.KubeSchedulerProfile{
|
||||
{
|
||||
PluginConfig: []v1alpha2.PluginConfig{
|
||||
{
|
||||
Name: "InterPodAffinity",
|
||||
Args: runtime.RawExtension{
|
||||
Object: &v1alpha2.InterPodAffinityArgs{
|
||||
HardPodAffinityWeight: pointer.Int32Ptr(5),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "RequestedToCapacityRatio",
|
||||
Args: runtime.RawExtension{
|
||||
Object: &v1alpha2.RequestedToCapacityRatioArgs{
|
||||
Shape: []v1alpha2.UtilizationShapePoint{
|
||||
{Utilization: 1, Score: 2},
|
||||
},
|
||||
Resources: []v1alpha2.ResourceSpec{
|
||||
{Name: "lower", Weight: 2},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "OutOfTreePlugin",
|
||||
Args: runtime.RawExtension{
|
||||
Raw: []byte(`{"foo":"bar"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: `apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 0
|
||||
contentType: ""
|
||||
kubeconfig: ""
|
||||
qps: 0
|
||||
kind: KubeSchedulerConfiguration
|
||||
leaderElection:
|
||||
leaderElect: null
|
||||
leaseDuration: 0s
|
||||
renewDeadline: 0s
|
||||
resourceLock: ""
|
||||
resourceName: ""
|
||||
resourceNamespace: ""
|
||||
retryPeriod: 0s
|
||||
profiles:
|
||||
- pluginConfig:
|
||||
- args:
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
hardPodAffinityWeight: 5
|
||||
kind: InterPodAffinityArgs
|
||||
name: InterPodAffinity
|
||||
- args:
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
kind: RequestedToCapacityRatioArgs
|
||||
resources:
|
||||
- Name: lower
|
||||
Weight: 2
|
||||
shape:
|
||||
- Score: 2
|
||||
Utilization: 1
|
||||
name: RequestedToCapacityRatio
|
||||
- args:
|
||||
foo: bar
|
||||
name: OutOfTreePlugin
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "v1alpha2 in-tree and out-of-tree plugins from internal",
|
||||
version: v1alpha2.SchemeGroupVersion,
|
||||
obj: &config.KubeSchedulerConfiguration{
|
||||
Profiles: []config.KubeSchedulerProfile{
|
||||
{
|
||||
PluginConfig: []config.PluginConfig{
|
||||
{
|
||||
Name: "InterPodAffinity",
|
||||
Args: &config.InterPodAffinityArgs{
|
||||
HardPodAffinityWeight: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "OutOfTreePlugin",
|
||||
Args: &runtime.Unknown{
|
||||
Raw: []byte(`{"foo":"bar"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: `apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
bindTimeoutSeconds: 0
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 0
|
||||
contentType: ""
|
||||
kubeconfig: ""
|
||||
qps: 0
|
||||
disablePreemption: false
|
||||
enableContentionProfiling: false
|
||||
enableProfiling: false
|
||||
healthzBindAddress: ""
|
||||
kind: KubeSchedulerConfiguration
|
||||
leaderElection:
|
||||
leaderElect: false
|
||||
leaseDuration: 0s
|
||||
renewDeadline: 0s
|
||||
resourceLock: ""
|
||||
resourceName: ""
|
||||
resourceNamespace: ""
|
||||
retryPeriod: 0s
|
||||
metricsBindAddress: ""
|
||||
percentageOfNodesToScore: 0
|
||||
podInitialBackoffSeconds: 0
|
||||
podMaxBackoffSeconds: 0
|
||||
profiles:
|
||||
- pluginConfig:
|
||||
- args:
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha2
|
||||
hardPodAffinityWeight: 5
|
||||
kind: InterPodAffinityArgs
|
||||
name: InterPodAffinity
|
||||
- args:
|
||||
foo: bar
|
||||
name: OutOfTreePlugin
|
||||
schedulerName: ""
|
||||
`,
|
||||
},
|
||||
}
|
||||
yamlInfo, ok := runtime.SerializerInfoForMediaType(Codecs.SupportedMediaTypes(), runtime.ContentTypeYAML)
|
||||
if !ok {
|
||||
t.Fatalf("unable to locate encoder -- %q is not a supported media type", runtime.ContentTypeYAML)
|
||||
}
|
||||
jsonInfo, ok := runtime.SerializerInfoForMediaType(Codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||
if !ok {
|
||||
t.Fatalf("unable to locate encoder -- %q is not a supported media type", runtime.ContentTypeJSON)
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
encoder := Codecs.EncoderForVersion(yamlInfo.Serializer, tt.version)
|
||||
var buf bytes.Buffer
|
||||
if err := encoder.Encode(tt.obj, &buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if diff := cmp.Diff(tt.want, buf.String()); diff != "" {
|
||||
t.Errorf("unexpected encoded configuration:\n%s", diff)
|
||||
}
|
||||
encoder = Codecs.EncoderForVersion(jsonInfo.Serializer, tt.version)
|
||||
buf = bytes.Buffer{}
|
||||
if err := encoder.Encode(tt.obj, &buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := yaml.JSONToYAML(buf.Bytes())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if diff := cmp.Diff(tt.want, string(out)); diff != "" {
|
||||
t.Errorf("unexpected encoded configuration:\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ go_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/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/kube-scheduler/config/v1:go_default_library",
|
||||
"//staging/src/k8s.io/kube-scheduler/config/v1alpha2:go_default_library",
|
||||
|
@ -17,20 +17,93 @@ limitations under the License.
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
v1alpha2 "k8s.io/kube-scheduler/config/v1alpha2"
|
||||
"fmt"
|
||||
"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/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 {
|
||||
if err := autoConvert_v1alpha2_KubeSchedulerConfiguration_To_config_KubeSchedulerConfiguration(in, out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ go_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/kube-scheduler/config/v1:go_default_library",
|
||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -17,10 +17,14 @@ limitations under the License.
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
||||
v1 "k8s.io/kube-scheduler/config/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -73,17 +77,17 @@ type KubeSchedulerConfiguration struct {
|
||||
// Duration to wait for a binding operation to complete before timing out
|
||||
// Value must be non-negative integer. The value zero indicates no waiting.
|
||||
// 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.
|
||||
// If specified, it must be greater than 0. If this value is null, the default value (1s)
|
||||
// will be used.
|
||||
PodInitialBackoffSeconds *int64 `json:"podInitialBackoffSeconds"`
|
||||
PodInitialBackoffSeconds *int64 `json:"podInitialBackoffSeconds,omitempty"`
|
||||
|
||||
// PodMaxBackoffSeconds is the max backoff for unschedulable pods.
|
||||
// If specified, it must be greater than podInitialBackoffSeconds. If this value is null,
|
||||
// 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
|
||||
// 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.
|
||||
// +listType=map
|
||||
// +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
|
||||
// with the extender. These extenders are shared by all scheduler profiles.
|
||||
// +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.
|
||||
@ -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 runtime.RawExtension `json:"args,omitempty"`
|
||||
}
|
||||
|
||||
func (c *PluginConfig) decodeNestedObjects(d runtime.Decoder) error {
|
||||
gvk := SchemeGroupVersion.WithKind(c.Name + "Args")
|
||||
// dry-run to detect and skip out-of-tree plugin args.
|
||||
if _, _, err := d.Decode(nil, &gvk, nil); runtime.IsNotRegisteredError(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
obj, parsedGvk, err := d.Decode(c.Args.Raw, &gvk, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decoding args for plugin %s: %w", c.Name, err)
|
||||
}
|
||||
if parsedGvk.GroupKind() != gvk.GroupKind() {
|
||||
return fmt.Errorf("args for plugin %s were not of type %s, got %s", c.Name, gvk.GroupKind(), parsedGvk.GroupKind())
|
||||
}
|
||||
c.Args.Object = obj
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *PluginConfig) encodeNestedObjects(e runtime.Encoder) error {
|
||||
if c.Args.Object == nil {
|
||||
return nil
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err := e.Encode(c.Args.Object, &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The <e> encoder might be a YAML encoder, but the parent encoder expects
|
||||
// JSON output, so we convert YAML back to JSON.
|
||||
// This is a no-op if <e> produces JSON.
|
||||
json, err := yaml.YAMLToJSON(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Args.Raw = json
|
||||
return nil
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
gojson "encoding/json"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
@ -95,7 +97,7 @@ type RequestedToCapacityRatioArgs struct {
|
||||
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
|
||||
// to maintain backward compatibility with the args shipped with v1.18.
|
||||
// See https://github.com/kubernetes/kubernetes/pull/88585#discussion_r405021905
|
||||
@ -108,6 +110,13 @@ type UtilizationShapePoint struct {
|
||||
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.
|
||||
type ResourceSpec struct {
|
||||
// Name of the resource to be managed by RequestedToCapacityRatio function.
|
||||
@ -116,6 +125,13 @@ type ResourceSpec struct {
|
||||
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
|
||||
|
||||
// ServiceAffinityArgs holds arguments used to configure the ServiceAffinity plugin.
|
||||
|
@ -9,6 +9,7 @@ require (
|
||||
k8s.io/api v0.0.0
|
||||
k8s.io/apimachinery v0.0.0
|
||||
k8s.io/component-base v0.0.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
replace (
|
||||
|
Loading…
Reference in New Issue
Block a user