mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 03:11:40 +00:00
Merge pull request #96226 from jiahuif/feature/migration-configuration/parsing
leader migration configuration: parsing & validation.
This commit is contained in:
commit
b860d08e4b
@ -29,6 +29,7 @@ filegroup(
|
|||||||
"//staging/src/k8s.io/controller-manager/pkg/clientbuilder:all-srcs",
|
"//staging/src/k8s.io/controller-manager/pkg/clientbuilder:all-srcs",
|
||||||
"//staging/src/k8s.io/controller-manager/pkg/features:all-srcs",
|
"//staging/src/k8s.io/controller-manager/pkg/features:all-srcs",
|
||||||
"//staging/src/k8s.io/controller-manager/pkg/informerfactory:all-srcs",
|
"//staging/src/k8s.io/controller-manager/pkg/informerfactory:all-srcs",
|
||||||
|
"//staging/src/k8s.io/controller-manager/pkg/leadermigration/config:all-srcs",
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["config.go"],
|
||||||
|
importmap = "k8s.io/kubernetes/vendor/k8s.io/controller-manager/pkg/leadermigration/config",
|
||||||
|
importpath = "k8s.io/controller-manager/pkg/leadermigration/config",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||||
|
"//staging/src/k8s.io/controller-manager/config:go_default_library",
|
||||||
|
"//staging/src/k8s.io/controller-manager/config/v1alpha1:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["config_test.go"],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/controller-manager/config:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
util "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
internal "k8s.io/controller-manager/config"
|
||||||
|
"k8s.io/controller-manager/config/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceLockLeases is the resourceLock value for 'leases' API
|
||||||
|
const ResourceLockLeases = "leases"
|
||||||
|
|
||||||
|
// ResourceLockEndpoints is the resourceLock value for 'endpoints' API
|
||||||
|
const ResourceLockEndpoints = "endpoints"
|
||||||
|
|
||||||
|
var cfgScheme = runtime.NewScheme()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// internal
|
||||||
|
util.Must(internal.AddToScheme(cfgScheme))
|
||||||
|
|
||||||
|
// v1alpha1
|
||||||
|
util.Must(v1alpha1.AddToScheme(cfgScheme))
|
||||||
|
util.Must(cfgScheme.SetVersionPriority(v1alpha1.SchemeGroupVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadLeaderMigrationConfiguration reads LeaderMigrationConfiguration from a YAML file at the given path.
|
||||||
|
// The parsed LeaderMigrationConfiguration may be invalid.
|
||||||
|
// It returns an error if the file did not exist.
|
||||||
|
func ReadLeaderMigrationConfiguration(configFilePath string) (*internal.LeaderMigrationConfiguration, error) {
|
||||||
|
data, err := ioutil.ReadFile(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to read leader migration configuration from %q: %v", configFilePath, err)
|
||||||
|
}
|
||||||
|
config, gvk, err := serializer.NewCodecFactory(cfgScheme).UniversalDecoder().Decode(data, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
internalConfig, ok := config.(*internal.LeaderMigrationConfiguration)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected config type: %v", gvk)
|
||||||
|
}
|
||||||
|
return internalConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateLeaderMigrationConfiguration validates the LeaderMigrationConfiguration against common errors.
|
||||||
|
// It checks required names and whether resourceLock is either 'leases' or 'endpoints'.
|
||||||
|
// It will return nil if it does not find anything wrong.
|
||||||
|
func ValidateLeaderMigrationConfiguration(config *internal.LeaderMigrationConfiguration) (allErrs field.ErrorList) {
|
||||||
|
if config.LeaderName == "" {
|
||||||
|
allErrs = append(allErrs, field.Required(field.NewPath("leaderName"),
|
||||||
|
"leaderName must be set for LeaderMigrationConfiguration"))
|
||||||
|
}
|
||||||
|
if config.ResourceLock != ResourceLockLeases && config.ResourceLock != ResourceLockEndpoints {
|
||||||
|
allErrs = append(allErrs, field.Invalid(field.NewPath("resourceLock"), config.ResourceLock,
|
||||||
|
"resource Lock must be one of 'leases' or 'endpoints'"))
|
||||||
|
}
|
||||||
|
// validate controllerLeaders
|
||||||
|
fldPath := field.NewPath("controllerLeaders")
|
||||||
|
for i, controllerLeader := range config.ControllerLeaders {
|
||||||
|
path := fldPath.Index(i)
|
||||||
|
allErrs = append(allErrs, validateControllerLeaderConfiguration(path, &controllerLeader)...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateControllerLeaderConfiguration(path *field.Path, config *internal.ControllerLeaderConfiguration) (allErrs field.ErrorList) {
|
||||||
|
if config == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if config.Component == "" {
|
||||||
|
allErrs = append(allErrs, field.Required(path.Child("component"), "component must be set"))
|
||||||
|
}
|
||||||
|
if config.Name == "" {
|
||||||
|
allErrs = append(allErrs, field.Required(path.Child("name"), "name must be set"))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
internal "k8s.io/controller-manager/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadLeaderMigrationConfiguration(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
content string
|
||||||
|
expected *internal.LeaderMigrationConfiguration
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
content: "",
|
||||||
|
expected: nil,
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong type",
|
||||||
|
content: `
|
||||||
|
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||||
|
kind: KubeletConfiguration
|
||||||
|
`,
|
||||||
|
expected: nil,
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "basic",
|
||||||
|
content: `
|
||||||
|
apiVersion: controllermanager.config.k8s.io/v1alpha1
|
||||||
|
kind: LeaderMigrationConfiguration
|
||||||
|
leaderName: migration-120-to-121
|
||||||
|
resourceLock: leases
|
||||||
|
controllerLeaders: []
|
||||||
|
`,
|
||||||
|
expected: &internal.LeaderMigrationConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{},
|
||||||
|
LeaderName: "migration-120-to-121",
|
||||||
|
ResourceLock: "leases",
|
||||||
|
ControllerLeaders: []internal.ControllerLeaderConfiguration{},
|
||||||
|
},
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "endpoints",
|
||||||
|
content: `
|
||||||
|
apiVersion: controllermanager.config.k8s.io/v1alpha1
|
||||||
|
kind: LeaderMigrationConfiguration
|
||||||
|
leaderName: migration-120-to-121
|
||||||
|
resourceLock: endpoints
|
||||||
|
controllerLeaders: []
|
||||||
|
`,
|
||||||
|
expected: &internal.LeaderMigrationConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{},
|
||||||
|
LeaderName: "migration-120-to-121",
|
||||||
|
ResourceLock: "endpoints",
|
||||||
|
ControllerLeaders: []internal.ControllerLeaderConfiguration{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "withLeaders",
|
||||||
|
content: `
|
||||||
|
apiVersion: controllermanager.config.k8s.io/v1alpha1
|
||||||
|
kind: LeaderMigrationConfiguration
|
||||||
|
leaderName: migration-120-to-121
|
||||||
|
resourceLock: endpoints
|
||||||
|
controllerLeaders:
|
||||||
|
- name: route-controller
|
||||||
|
component: kube-controller-manager
|
||||||
|
- name: service-controller
|
||||||
|
component: kube-controller-manager
|
||||||
|
`,
|
||||||
|
expected: &internal.LeaderMigrationConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{},
|
||||||
|
LeaderName: "migration-120-to-121",
|
||||||
|
ResourceLock: "endpoints",
|
||||||
|
ControllerLeaders: []internal.ControllerLeaderConfiguration{
|
||||||
|
{
|
||||||
|
Name: "route-controller",
|
||||||
|
Component: "kube-controller-manager",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "service-controller",
|
||||||
|
Component: "kube-controller-manager",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
configFile, err := ioutil.TempFile("", tc.name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(configFile.Name())
|
||||||
|
err = ioutil.WriteFile(configFile.Name(), []byte(tc.content), os.FileMode(0755))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
result, err := ReadLeaderMigrationConfiguration(configFile.Name())
|
||||||
|
if tc.expectErr && err == nil {
|
||||||
|
t.Errorf("unexpected no error for %s", tc.name)
|
||||||
|
} else if !tc.expectErr && err != nil {
|
||||||
|
t.Errorf("get error from ReadLeaderElectionConfiguration: %#v", err)
|
||||||
|
} else if !reflect.DeepEqual(result, tc.expected) {
|
||||||
|
t.Errorf("result not matching expected, got %#v, expected %#v", result, tc.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateLeaderMigrationConfiguration(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
config *internal.LeaderMigrationConfiguration
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty name",
|
||||||
|
config: &internal.LeaderMigrationConfiguration{
|
||||||
|
LeaderName: "",
|
||||||
|
ResourceLock: ResourceLockLeases,
|
||||||
|
ControllerLeaders: []internal.ControllerLeaderConfiguration{},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid resourceLock",
|
||||||
|
config: &internal.LeaderMigrationConfiguration{
|
||||||
|
LeaderName: "test",
|
||||||
|
ResourceLock: "invalid",
|
||||||
|
ControllerLeaders: []internal.ControllerLeaderConfiguration{},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty controllerLeaders (valid)",
|
||||||
|
config: &internal.LeaderMigrationConfiguration{
|
||||||
|
LeaderName: "test",
|
||||||
|
ResourceLock: ResourceLockLeases,
|
||||||
|
ControllerLeaders: []internal.ControllerLeaderConfiguration{},
|
||||||
|
},
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "endpoints",
|
||||||
|
config: &internal.LeaderMigrationConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{},
|
||||||
|
LeaderName: "migration-120-to-121",
|
||||||
|
ResourceLock: ResourceLockEndpoints,
|
||||||
|
ControllerLeaders: []internal.ControllerLeaderConfiguration{
|
||||||
|
{
|
||||||
|
Name: "route-controller",
|
||||||
|
Component: "kube-controller-manager",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
errs := ValidateLeaderMigrationConfiguration(tc.config)
|
||||||
|
if tc.expectErr && len(errs) == 0 {
|
||||||
|
t.Errorf("calling ValidateLeaderMigrationConfiguration expected errors but got no error")
|
||||||
|
}
|
||||||
|
if !tc.expectErr && len(errs) != 0 {
|
||||||
|
t.Errorf("calling ValidateLeaderMigrationConfiguration expected no error but got %v", errs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user