mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 03:11:40 +00:00
migration configuration: parsing & validation.
This commit is contained in:
parent
7da7d2084c
commit
de5a30a786
@ -29,6 +29,7 @@ filegroup(
|
||||
"//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/informerfactory:all-srcs",
|
||||
"//staging/src/k8s.io/controller-manager/pkg/leadermigration/config:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
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