Skip to content

Commit b15aba5

Browse files
committed
Add lenient decoding path for v1alpha1 kube-scheduler config
This implements a lenient path for decoding a kube-scheduler config file. The config file gets decoded with a strict serializer first, if that fails a lenient CodecFactory that has just v1alpha1 registered into it is used for decoding. The lenient path is to be dropped when support for v1alpha1 is dropped. For more information on the discussion see kubernetes#82924 and the linked PRs.
1 parent 00deec8 commit b15aba5

File tree

7 files changed

+159
-7
lines changed

7 files changed

+159
-7
lines changed

cmd/kube-scheduler/app/options/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ go_library(
3636
"//staging/src/k8s.io/client-go/tools/leaderelection/resourcelock:go_default_library",
3737
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
3838
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
39+
"//staging/src/k8s.io/component-base/codec:go_default_library",
3940
"//staging/src/k8s.io/component-base/config:go_default_library",
4041
"//staging/src/k8s.io/kube-scheduler/config/v1alpha1:go_default_library",
4142
"//vendor/github.com/spf13/pflag:go_default_library",

cmd/kube-scheduler/app/options/configfile.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import (
2121
"io/ioutil"
2222
"os"
2323

24+
"k8s.io/klog"
25+
2426
"k8s.io/apimachinery/pkg/runtime"
27+
"k8s.io/component-base/codec"
2528
kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
2629
kubeschedulerscheme "k8s.io/kubernetes/pkg/scheduler/apis/config/scheme"
2730
kubeschedulerconfigv1alpha1 "k8s.io/kubernetes/pkg/scheduler/apis/config/v1alpha1"
@@ -38,8 +41,28 @@ func loadConfigFromFile(file string) (*kubeschedulerconfig.KubeSchedulerConfigur
3841

3942
func loadConfig(data []byte) (*kubeschedulerconfig.KubeSchedulerConfiguration, error) {
4043
configObj := &kubeschedulerconfig.KubeSchedulerConfiguration{}
41-
if err := runtime.DecodeInto(kubeschedulerscheme.Codecs.UniversalDecoder(), data, configObj); err != nil {
42-
return nil, err
44+
// The UniversalDecoder runs defaulting and returns the internal type by default.
45+
err := runtime.DecodeInto(kubeschedulerscheme.Codecs.UniversalDecoder(), data, configObj)
46+
if err != nil {
47+
// Try strict decoding first. If that fails decode with a lenient
48+
// decoder, which has only v1alpha1 registered, and log a warning.
49+
// The lenient path is to be dropped when support for v1alpha1 is dropped.
50+
if !runtime.IsStrictDecodingError(err) {
51+
return nil, err
52+
}
53+
54+
var lenientErr error
55+
_, lenientCodecs, lenientErr := codec.NewLenientSchemeAndCodecs(
56+
kubeschedulerconfig.AddToScheme,
57+
kubeschedulerconfigv1alpha1.AddToScheme,
58+
)
59+
if lenientErr != nil {
60+
return nil, lenientErr
61+
}
62+
if lenientErr = runtime.DecodeInto(lenientCodecs.UniversalDecoder(), data, configObj); lenientErr != nil {
63+
return nil, fmt.Errorf("failed lenient decoding: %v", err)
64+
}
65+
klog.Warningf("using lenient decoding as strict decoding failed: %v", err)
4366
}
4467

4568
return configObj, nil

cmd/kube-scheduler/app/options/options_test.go

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -472,16 +472,76 @@ pluginConfig:
472472
options: &Options{
473473
ConfigFile: unknownFieldConfig,
474474
},
475-
expectedError: "found unknown field: foo",
476-
checkErrFn: runtime.IsStrictDecodingError,
475+
// TODO (obitech): Remove this comment and add a new test for v1alpha2, when it's available, as this should fail then.
476+
// expectedError: "found unknown field: foo",
477+
// checkErrFn: runtime.IsStrictDecodingError,
478+
expectedUsername: "config",
479+
expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{
480+
SchedulerName: "default-scheduler",
481+
AlgorithmSource: kubeschedulerconfig.SchedulerAlgorithmSource{Provider: &defaultSource},
482+
HardPodAffinitySymmetricWeight: 1,
483+
HealthzBindAddress: "0.0.0.0:10251",
484+
MetricsBindAddress: "0.0.0.0:10251",
485+
LeaderElection: kubeschedulerconfig.KubeSchedulerLeaderElectionConfiguration{
486+
LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
487+
LeaderElect: true,
488+
LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
489+
RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
490+
RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
491+
ResourceLock: "endpointsleases",
492+
ResourceNamespace: "kube-system",
493+
ResourceName: "kube-scheduler",
494+
},
495+
},
496+
ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
497+
Kubeconfig: configKubeconfig,
498+
QPS: 50,
499+
Burst: 100,
500+
ContentType: "application/vnd.kubernetes.protobuf",
501+
},
502+
BindTimeoutSeconds: &defaultBindTimeoutSeconds,
503+
PodInitialBackoffSeconds: &defaultPodInitialBackoffSeconds,
504+
PodMaxBackoffSeconds: &defaultPodMaxBackoffSeconds,
505+
Plugins: nil,
506+
},
477507
},
478508
{
479509
name: "duplicate fields",
480510
options: &Options{
481511
ConfigFile: duplicateFieldConfig,
482512
},
483-
expectedError: `key "leaderElect" already set`,
484-
checkErrFn: runtime.IsStrictDecodingError,
513+
// TODO (obitech): Remove this comment and add a new test for v1alpha2, when it's available, as this should fail then.
514+
// expectedError: `key "leaderElect" already set`,
515+
// checkErrFn: runtime.IsStrictDecodingError,
516+
expectedUsername: "config",
517+
expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{
518+
SchedulerName: "default-scheduler",
519+
AlgorithmSource: kubeschedulerconfig.SchedulerAlgorithmSource{Provider: &defaultSource},
520+
HardPodAffinitySymmetricWeight: 1,
521+
HealthzBindAddress: "0.0.0.0:10251",
522+
MetricsBindAddress: "0.0.0.0:10251",
523+
LeaderElection: kubeschedulerconfig.KubeSchedulerLeaderElectionConfiguration{
524+
LeaderElectionConfiguration: componentbaseconfig.LeaderElectionConfiguration{
525+
LeaderElect: false,
526+
LeaseDuration: metav1.Duration{Duration: 15 * time.Second},
527+
RenewDeadline: metav1.Duration{Duration: 10 * time.Second},
528+
RetryPeriod: metav1.Duration{Duration: 2 * time.Second},
529+
ResourceLock: "endpointsleases",
530+
ResourceNamespace: "kube-system",
531+
ResourceName: "kube-scheduler",
532+
},
533+
},
534+
ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
535+
Kubeconfig: configKubeconfig,
536+
QPS: 50,
537+
Burst: 100,
538+
ContentType: "application/vnd.kubernetes.protobuf",
539+
},
540+
BindTimeoutSeconds: &defaultBindTimeoutSeconds,
541+
PodInitialBackoffSeconds: &defaultPodInitialBackoffSeconds,
542+
PodMaxBackoffSeconds: &defaultPodMaxBackoffSeconds,
543+
Plugins: nil,
544+
},
485545
},
486546
}
487547

@@ -523,7 +583,7 @@ pluginConfig:
523583
return
524584
}
525585
if username != tc.expectedUsername {
526-
t.Errorf("expected server call with user %s, got %s", tc.expectedUsername, username)
586+
t.Errorf("expected server call with user %q, got %q", tc.expectedUsername, username)
527587
}
528588
})
529589
}

staging/src/k8s.io/component-base/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ filegroup(
1111
":package-srcs",
1212
"//staging/src/k8s.io/component-base/cli/flag:all-srcs",
1313
"//staging/src/k8s.io/component-base/cli/globalflag:all-srcs",
14+
"//staging/src/k8s.io/component-base/codec:all-srcs",
1415
"//staging/src/k8s.io/component-base/config:all-srcs",
1516
"//staging/src/k8s.io/component-base/featuregate:all-srcs",
1617
"//staging/src/k8s.io/component-base/logs:all-srcs",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = ["codec.go"],
6+
importmap = "k8s.io/kubernetes/vendor/k8s.io/component-base/codec",
7+
importpath = "k8s.io/component-base/codec",
8+
visibility = ["//visibility:public"],
9+
deps = [
10+
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
11+
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
12+
],
13+
)
14+
15+
filegroup(
16+
name = "package-srcs",
17+
srcs = glob(["**"]),
18+
tags = ["automanaged"],
19+
visibility = ["//visibility:private"],
20+
)
21+
22+
filegroup(
23+
name = "all-srcs",
24+
srcs = [":package-srcs"],
25+
tags = ["automanaged"],
26+
visibility = ["//visibility:public"],
27+
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package codec
18+
19+
import (
20+
"fmt"
21+
22+
"k8s.io/apimachinery/pkg/runtime"
23+
"k8s.io/apimachinery/pkg/runtime/serializer"
24+
)
25+
26+
// NewLenientSchemeAndCodecs constructs a CodecFactory with strict decoding
27+
// disabled, that has only the Schemes registered into it which are passed
28+
// and added via AddToScheme functions. This can be used to skip strict decoding
29+
// a specific version only.
30+
func NewLenientSchemeAndCodecs(addToSchemeFns ...func(s *runtime.Scheme) error) (*runtime.Scheme, *serializer.CodecFactory, error) {
31+
lenientScheme := runtime.NewScheme()
32+
for _, s := range addToSchemeFns {
33+
if err := s(lenientScheme); err != nil {
34+
return nil, nil, fmt.Errorf("unable to add API to lenient scheme: %v", err)
35+
}
36+
}
37+
lenientCodecs := serializer.NewCodecFactory(lenientScheme, serializer.DisableStrict)
38+
return lenientScheme, &lenientCodecs, nil
39+
}

vendor/modules.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,7 @@ k8s.io/code-generator/third_party/forked/golang/reflect
16671667
# k8s.io/component-base v0.0.0 => ./staging/src/k8s.io/component-base
16681668
k8s.io/component-base/cli/flag
16691669
k8s.io/component-base/cli/globalflag
1670+
k8s.io/component-base/codec
16701671
k8s.io/component-base/config
16711672
k8s.io/component-base/config/v1alpha1
16721673
k8s.io/component-base/config/validation

0 commit comments

Comments
 (0)