diff --git a/cmd/kube-scheduler/app/options/BUILD b/cmd/kube-scheduler/app/options/BUILD index ebe68f08340..3531b68930c 100644 --- a/cmd/kube-scheduler/app/options/BUILD +++ b/cmd/kube-scheduler/app/options/BUILD @@ -20,9 +20,12 @@ go_library( "//pkg/scheduler/apis/config/validation:go_default_library", "//pkg/scheduler/factory:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/config:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured: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/apimachinery/pkg/runtime/serializer/json:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", diff --git a/cmd/kube-scheduler/app/options/configfile.go b/cmd/kube-scheduler/app/options/configfile.go index 7648f76fd49..a564c71409e 100644 --- a/cmd/kube-scheduler/app/options/configfile.go +++ b/cmd/kube-scheduler/app/options/configfile.go @@ -17,11 +17,16 @@ limitations under the License. package options import ( + "bytes" "errors" + "fmt" "io/ioutil" "os" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer/json" kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" kubeschedulerscheme "k8s.io/kubernetes/pkg/scheduler/apis/config/scheme" @@ -40,8 +45,48 @@ func loadConfigFromFile(file string) (*kubeschedulerconfig.KubeSchedulerConfigur func loadConfig(data []byte) (*kubeschedulerconfig.KubeSchedulerConfiguration, error) { configObj := &kubeschedulerconfig.KubeSchedulerConfiguration{} if err := runtime.DecodeInto(kubeschedulerscheme.Codecs.UniversalDecoder(), data, configObj); err != nil { + + // if this is a componentconfig/v1alpha1 KubeSchedulerConfiguration object, coerce it to kubescheduler.config.k8s.io/v1alpha1 with a warning + // TODO: drop this block in 1.13 + if runtime.IsNotRegisteredError(err) { + originalErr := err + var ( + u = &unstructured.Unstructured{} + codec = json.NewYAMLSerializer(json.DefaultMetaFactory, kubeschedulerscheme.Scheme, kubeschedulerscheme.Scheme) + legacyConfigGVK = schema.GroupVersionKind{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeSchedulerConfiguration"} + ) + // attempt to decode to an unstructured object + obj, gvk, err := codec.Decode(data, nil, u) + + // if this errored, or the object we read was not the legacy alpha gvk, return the original error + if err != nil || gvk == nil || *gvk != legacyConfigGVK { + return nil, originalErr + } + + fmt.Printf("WARNING: the provided config file is an unsupported apiVersion (%q), which will be removed in future releases\n\n", legacyConfigGVK.GroupVersion().String()) + fmt.Printf("WARNING: switch to command-line flags or update your config file apiVersion to %q\n\n", kubeschedulerconfigv1alpha1.SchemeGroupVersion.String()) + fmt.Printf("WARNING: apiVersions at alpha-level are not guaranteed to be supported in future releases\n\n") + + // attempt to coerce to the new alpha gvk + if err := meta.NewAccessor().SetAPIVersion(obj, kubeschedulerconfigv1alpha1.SchemeGroupVersion.String()); err != nil { + // return the original error on failure + return nil, originalErr + } + + // attempt to encode the coerced apiVersion back to bytes + buffer := bytes.NewBuffer([]byte{}) + if err := codec.Encode(obj, buffer); err != nil { + // return the original error on failure + return nil, originalErr + } + + // re-attempt to load the coerced apiVersion + return loadConfig(buffer.Bytes()) + } + return nil, err } + return configObj, nil } diff --git a/cmd/kube-scheduler/app/options/options_test.go b/cmd/kube-scheduler/app/options/options_test.go index bacab57fc58..e8e683c82ea 100644 --- a/cmd/kube-scheduler/app/options/options_test.go +++ b/cmd/kube-scheduler/app/options/options_test.go @@ -111,6 +111,17 @@ leaderElection: t.Fatal(err) } + invalidconfigFile := filepath.Join(tmpDir, "scheduler_invalid.yaml") + if err := ioutil.WriteFile(invalidconfigFile, []byte(fmt.Sprintf(` +apiVersion: componentconfig/v1alpha2 +kind: KubeSchedulerConfiguration +clientConnection: + kubeconfig: "%s" +leaderElection: + leaderElect: true`, configKubeconfig)), os.FileMode(0600)); err != nil { + t.Fatal(err) + } + // flag-specified kubeconfig flagKubeconfig := filepath.Join(tmpDir, "flag.kubeconfig") if err := ioutil.WriteFile(flagKubeconfig, []byte(fmt.Sprintf(` @@ -195,9 +206,53 @@ users: }, }, { - name: "config file in componentconfig/v1alpha1", - options: &Options{ConfigFile: oldconfigFile}, - expectedError: "no kind \"KubeSchedulerConfiguration\" is registered for version \"componentconfig/v1alpha1\" in scheme", + name: "config file in componentconfig/v1alpha1", + options: &Options{ + ConfigFile: oldconfigFile, + ComponentConfig: func() kubeschedulerconfig.KubeSchedulerConfiguration { + cfg, err := newDefaultComponentConfig() + if err != nil { + t.Fatal(err) + } + return *cfg + }(), + }, + // TODO: switch this to expect an error in 1.13 when the special-case coercion is removed from loadConfig + // expectedError: "no kind \"KubeSchedulerConfiguration\" is registered for version \"componentconfig/v1alpha1\"", + expectedUsername: "config", + expectedConfig: kubeschedulerconfig.KubeSchedulerConfiguration{ + SchedulerName: "default-scheduler", + AlgorithmSource: kubeschedulerconfig.SchedulerAlgorithmSource{Provider: &defaultSource}, + HardPodAffinitySymmetricWeight: 1, + HealthzBindAddress: "0.0.0.0:10251", + MetricsBindAddress: "0.0.0.0:10251", + FailureDomains: "kubernetes.io/hostname,failure-domain.beta.kubernetes.io/zone,failure-domain.beta.kubernetes.io/region", + LeaderElection: kubeschedulerconfig.KubeSchedulerLeaderElectionConfiguration{ + LeaderElectionConfiguration: apiserverconfig.LeaderElectionConfiguration{ + LeaderElect: true, + LeaseDuration: metav1.Duration{Duration: 15 * time.Second}, + RenewDeadline: metav1.Duration{Duration: 10 * time.Second}, + RetryPeriod: metav1.Duration{Duration: 2 * time.Second}, + ResourceLock: "endpoints", + }, + LockObjectNamespace: "kube-system", + LockObjectName: "kube-scheduler", + }, + ClientConnection: apimachineryconfig.ClientConnectionConfiguration{ + Kubeconfig: configKubeconfig, + QPS: 50, + Burst: 100, + ContentType: "application/vnd.kubernetes.protobuf", + }, + PercentageOfNodesToScore: 50, + BindTimeoutSeconds: &defaultBindTimeoutSeconds, + }, + }, + + { + name: "invalid config file in componentconfig/v1alpha2", + options: &Options{ConfigFile: invalidconfigFile}, + expectedError: "no kind \"KubeSchedulerConfiguration\" is registered for version \"componentconfig/v1alpha2\"", }, { name: "kubeconfig flag",