Merge pull request #92435 from rosti/kubeadm-cc-test-refactor

kubeadm: refactor and cleanup component config tests
This commit is contained in:
Kubernetes Prow Robot 2020-07-03 20:30:48 -07:00 committed by GitHub
commit 6eca9f653b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 892 additions and 2570 deletions

View File

@ -45,12 +45,14 @@ go_test(
srcs = [
"checksums_test.go",
"configset_test.go",
"fakeconfig_test.go",
"kubelet_test.go",
"kubeproxy_test.go",
],
embed = [":go_default_library"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
"//cmd/kubeadm/app/apis/output:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
@ -59,7 +61,8 @@ go_test(
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/component-base/config/v1alpha1:go_default_library",
"//staging/src/k8s.io/kube-proxy/config/v1alpha1:go_default_library",

View File

@ -17,25 +17,26 @@ limitations under the License.
package componentconfigs
import (
"reflect"
"testing"
"github.com/lithammer/dedent"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/version"
clientsetfake "k8s.io/client-go/kubernetes/fake"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
outputapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/output"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
func testClusterCfg() *kubeadmapi.ClusterConfiguration {
return &kubeadmapi.ClusterConfiguration{
KubernetesVersion: constants.CurrentKubernetesVersion.String(),
}
}
func TestDefault(t *testing.T) {
clusterCfg := &kubeadmapi.ClusterConfiguration{}
clusterCfg := testClusterCfg()
localAPIEndpoint := &kubeadmapi.APIEndpoint{}
nodeRegOps := &kubeadmapi.NodeRegistrationOptions{}
@ -47,39 +48,18 @@ func TestDefault(t *testing.T) {
}
func TestFromCluster(t *testing.T) {
clusterCfg := &kubeadmapi.ClusterConfiguration{
KubernetesVersion: constants.CurrentKubernetesVersion.String(),
}
k8sVersion := version.MustParseGeneric(clusterCfg.KubernetesVersion)
objects := []runtime.Object{
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeProxyConfigMap,
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeProxyConfigMapKey: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
`),
},
},
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(k8sVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`),
},
},
testKubeProxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
`),
testKubeletConfigMap(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`),
}
client := clientsetfake.NewSimpleClientset(objects...)
clusterCfg := testClusterCfg()
if err := FetchFromCluster(clusterCfg, client); err != nil {
t.Fatalf("FetchFromCluster failed: %v", err)
@ -103,7 +83,7 @@ func TestFetchFromDocumentMap(t *testing.T) {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
clusterCfg := &kubeadmapi.ClusterConfiguration{}
clusterCfg := testClusterCfg()
if err = FetchFromDocumentMap(clusterCfg, gvkmap); err != nil {
t.Fatalf("FetchFromDocumentMap failed: %v", err)
}
@ -112,542 +92,3 @@ func TestFetchFromDocumentMap(t *testing.T) {
t.Fatalf("missmatch between supplied and loaded type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(gvkmap))
}
}
func kubeproxyConfigMap(contents string) *v1.ConfigMap {
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeProxyConfigMap,
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeProxyConfigMapKey: dedent.Dedent(contents),
},
}
}
func TestFetchFromClusterWithLocalUpgrades(t *testing.T) {
cases := []struct {
desc string
obj runtime.Object
config string
expectedValue string
expectedErr bool
}{
{
desc: "reconginzed cluster object without overwrite is used",
obj: kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
hostnameOverride: foo
`),
expectedValue: "foo",
},
{
desc: "reconginzed cluster object with overwrite is not used",
obj: kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
hostnameOverride: foo
`),
config: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
hostnameOverride: bar
`),
expectedValue: "bar",
},
{
desc: "old config without overwrite returns an error",
obj: kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
hostnameOverride: foo
`),
expectedErr: true,
},
{
desc: "old config with recognized overwrite returns success",
obj: kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
hostnameOverride: foo
`),
config: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
hostnameOverride: bar
`),
expectedValue: "bar",
},
{
desc: "old config with old overwrite returns an error",
obj: kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
hostnameOverride: foo
`),
config: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
hostnameOverride: bar
`),
expectedErr: true,
},
}
for _, test := range cases {
t.Run(test.desc, func(t *testing.T) {
clusterCfg := &kubeadmapi.ClusterConfiguration{
KubernetesVersion: constants.CurrentKubernetesVersion.String(),
}
k8sVersion := version.MustParseGeneric(clusterCfg.KubernetesVersion)
client := clientsetfake.NewSimpleClientset(
test.obj,
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(k8sVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`),
},
},
)
docmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test.config))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
err = FetchFromClusterWithLocalOverwrites(clusterCfg, client, docmap)
if err != nil {
if !test.expectedErr {
t.Errorf("unexpected failure: %v", err)
}
} else {
if test.expectedErr {
t.Error("unexpected success")
} else {
kubeproxyCfg, ok := clusterCfg.ComponentConfigs[KubeProxyGroup]
if !ok {
t.Error("the config was reported as loaded, but was not in reality")
} else {
actualConfig, ok := kubeproxyCfg.(*kubeProxyConfig)
if !ok {
t.Error("the config is not of the expected type")
} else if actualConfig.config.HostnameOverride != test.expectedValue {
t.Errorf("unexpected value:\n\tgot: %q\n\texpected: %q", actualConfig.config.HostnameOverride, test.expectedValue)
}
}
}
}
})
}
}
func TestGetVersionStates(t *testing.T) {
tests := []struct {
desc string
objects []runtime.Object
substitutes string
expected []outputapi.ComponentConfigVersionState
}{
{
desc: "Normal config",
objects: []runtime.Object{
kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
`),
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`),
},
},
},
expected: []outputapi.ComponentConfigVersionState{
{
Group: "kubeproxy.config.k8s.io",
CurrentVersion: "v1alpha1",
PreferredVersion: "v1alpha1",
ManualUpgradeRequired: false,
},
{
Group: "kubelet.config.k8s.io",
CurrentVersion: "v1beta1",
PreferredVersion: "v1beta1",
ManualUpgradeRequired: false,
},
},
},
{
desc: "Normal config ignoring a current substitute",
objects: []runtime.Object{
kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
`),
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`),
},
},
},
substitutes: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
`),
expected: []outputapi.ComponentConfigVersionState{
{
Group: "kubeproxy.config.k8s.io",
CurrentVersion: "v1alpha1",
PreferredVersion: "v1alpha1",
ManualUpgradeRequired: false,
},
{
Group: "kubelet.config.k8s.io",
CurrentVersion: "v1beta1",
PreferredVersion: "v1beta1",
ManualUpgradeRequired: false,
},
},
},
{
desc: "Normal config with an old substitute",
objects: []runtime.Object{
kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
`),
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`),
},
},
},
substitutes: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
expected: []outputapi.ComponentConfigVersionState{
{
Group: "kubeproxy.config.k8s.io",
CurrentVersion: "v1alpha0",
PreferredVersion: "v1alpha1",
ManualUpgradeRequired: true,
},
{
Group: "kubelet.config.k8s.io",
CurrentVersion: "v1beta1",
PreferredVersion: "v1beta1",
ManualUpgradeRequired: false,
},
},
},
{
desc: "Old user supplied config",
objects: []runtime.Object{
kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1alpha1
kind: KubeletConfiguration
`),
},
},
},
expected: []outputapi.ComponentConfigVersionState{
{
Group: "kubeproxy.config.k8s.io",
CurrentVersion: "v1alpha0",
PreferredVersion: "v1alpha1",
ManualUpgradeRequired: true,
},
{
Group: "kubelet.config.k8s.io",
CurrentVersion: "v1alpha1",
PreferredVersion: "v1beta1",
ManualUpgradeRequired: true,
},
},
},
{
desc: "Old user supplied config with a proper substitute",
objects: []runtime.Object{
kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1alpha1
kind: KubeletConfiguration
`),
},
},
},
substitutes: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
`),
expected: []outputapi.ComponentConfigVersionState{
{
Group: "kubeproxy.config.k8s.io",
CurrentVersion: "v1alpha1",
PreferredVersion: "v1alpha1",
ManualUpgradeRequired: false,
},
{
Group: "kubelet.config.k8s.io",
CurrentVersion: "v1alpha1",
PreferredVersion: "v1beta1",
ManualUpgradeRequired: true,
},
},
},
{
desc: "Old user supplied config with an old substitute",
objects: []runtime.Object{
kubeproxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1alpha1
kind: KubeletConfiguration
`),
},
},
},
substitutes: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
expected: []outputapi.ComponentConfigVersionState{
{
Group: "kubeproxy.config.k8s.io",
CurrentVersion: "v1alpha0",
PreferredVersion: "v1alpha1",
ManualUpgradeRequired: true,
},
{
Group: "kubelet.config.k8s.io",
CurrentVersion: "v1alpha1",
PreferredVersion: "v1beta1",
ManualUpgradeRequired: true,
},
},
},
{
desc: "Old kubeadm generated config",
objects: []runtime.Object{
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeProxyConfigMap,
Namespace: metav1.NamespaceSystem,
Annotations: map[string]string{
constants.ComponentConfigHashAnnotationKey: "sha256:8d3dfd7abcac205f6744d8e9db44505cce0c15b0a5395501e272fc18bd54c13c",
},
},
Data: map[string]string{
constants.KubeProxyConfigMapKey: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
},
},
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`),
},
},
},
expected: []outputapi.ComponentConfigVersionState{
{
Group: "kubeproxy.config.k8s.io",
PreferredVersion: "v1alpha1",
ManualUpgradeRequired: false,
},
{
Group: "kubelet.config.k8s.io",
CurrentVersion: "v1beta1",
PreferredVersion: "v1beta1",
ManualUpgradeRequired: false,
},
},
},
{
desc: "Old kubeadm generated config with a proper substitute",
objects: []runtime.Object{
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeProxyConfigMap,
Namespace: metav1.NamespaceSystem,
Annotations: map[string]string{
constants.ComponentConfigHashAnnotationKey: "sha256:8d3dfd7abcac205f6744d8e9db44505cce0c15b0a5395501e272fc18bd54c13c",
},
},
Data: map[string]string{
constants.KubeProxyConfigMapKey: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
},
},
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`),
},
},
},
substitutes: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
`),
expected: []outputapi.ComponentConfigVersionState{
{
Group: "kubeproxy.config.k8s.io",
CurrentVersion: "v1alpha1",
PreferredVersion: "v1alpha1",
ManualUpgradeRequired: false,
},
{
Group: "kubelet.config.k8s.io",
CurrentVersion: "v1beta1",
PreferredVersion: "v1beta1",
ManualUpgradeRequired: false,
},
},
},
{
desc: "Old kubeadm generated config with an old substitute",
objects: []runtime.Object{
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeProxyConfigMap,
Namespace: metav1.NamespaceSystem,
Annotations: map[string]string{
constants.ComponentConfigHashAnnotationKey: "sha256:8d3dfd7abcac205f6744d8e9db44505cce0c15b0a5395501e272fc18bd54c13c",
},
},
Data: map[string]string{
constants.KubeProxyConfigMapKey: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
},
},
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`),
},
},
},
substitutes: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
expected: []outputapi.ComponentConfigVersionState{
{
Group: "kubeproxy.config.k8s.io",
CurrentVersion: "v1alpha0",
PreferredVersion: "v1alpha1",
ManualUpgradeRequired: true,
},
{
Group: "kubelet.config.k8s.io",
CurrentVersion: "v1beta1",
PreferredVersion: "v1beta1",
ManualUpgradeRequired: false,
},
},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
docmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test.substitutes))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
clusterCfg := &kubeadmapi.ClusterConfiguration{
KubernetesVersion: constants.CurrentKubernetesVersion.String(),
}
client := clientsetfake.NewSimpleClientset(test.objects...)
got, err := GetVersionStates(clusterCfg, client, docmap)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(got, test.expected) {
t.Fatalf("unexpected result:\n\texpected: %v\n\tgot: %v", test.expected, got)
}
})
}
}

View File

@ -0,0 +1,722 @@
/*
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 componentconfigs
import (
"crypto/sha256"
"fmt"
"reflect"
"strings"
"testing"
"time"
"github.com/lithammer/dedent"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
clientset "k8s.io/client-go/kubernetes"
clientsetfake "k8s.io/client-go/kubernetes/fake"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
outputapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/output"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
// All tests in this file use an alternative set of `known` component configs.
// In this case it's just one known config and it's kubeadm's very own ClusterConfiguration.
// ClusterConfiguration is normally not managed by this package. It's only used, because of the following:
// - It's a versioned API that is under the control of kubeadm maintainers. This enables us to test
// the componentconfigs package more thoroughly without having to have full and always up to date
// knowledge about the config of another component.
// - Other components often introduce new fields in their configs without bumping up the config version.
// This, often times, requires that the PR that introduces such new fields to touch kubeadm test code.
// Doing so, requires more work on the part of developers and reviewers. When kubeadm moves out of k/k
// this would allow for more sporadic breaks in kubeadm tests as PRs that merge in k/k and introduce
// new fields won't be able to fix the tests in kubeadm.
// - If we implement tests for all common functionality using the config of another component and it gets
// deprecated and/or we stop supporting it in production, we'll have to focus on a massive test refactoring
// or just continue importing this config just for test use.
//
// Thus, to reduce maintenance costs without sacrificing test coverage, we introduce this mini-framework
// and set of tests here which replace the normal component configs with a single one (ClusterConfiguration)
// and test the component config independent logic of this package.
// clusterConfigHandler is the handler instance for the latest supported ClusterConfiguration to be used in tests
var clusterConfigHandler = handler{
GroupVersion: kubeadmapiv1.SchemeGroupVersion,
AddToScheme: kubeadmapiv1.AddToScheme,
CreateEmpty: func() kubeadmapi.ComponentConfig {
return &clusterConfig{
configBase: configBase{
GroupVersion: kubeadmapiv1.SchemeGroupVersion,
},
}
},
fromCluster: clusterConfigFromCluster,
}
func clusterConfigFromCluster(h *handler, clientset clientset.Interface, _ *kubeadmapi.ClusterConfiguration) (kubeadmapi.ComponentConfig, error) {
return h.fromConfigMap(clientset, constants.KubeadmConfigConfigMap, constants.ClusterConfigurationConfigMapKey, true)
}
type clusterConfig struct {
configBase
config kubeadmapiv1.ClusterConfiguration
}
func (cc *clusterConfig) DeepCopy() kubeadmapi.ComponentConfig {
result := &clusterConfig{}
cc.configBase.DeepCopyInto(&result.configBase)
cc.config.DeepCopyInto(&result.config)
return result
}
func (cc *clusterConfig) Marshal() ([]byte, error) {
return cc.configBase.Marshal(&cc.config)
}
func (cc *clusterConfig) Unmarshal(docmap kubeadmapi.DocumentMap) error {
return cc.configBase.Unmarshal(docmap, &cc.config)
}
func (cc *clusterConfig) Default(_ *kubeadmapi.ClusterConfiguration, _ *kubeadmapi.APIEndpoint, _ *kubeadmapi.NodeRegistrationOptions) {
cc.config.ClusterName = "foo"
cc.config.KubernetesVersion = "bar"
}
// fakeKnown replaces temporarily during the execution of each test here known (in configset.go)
var fakeKnown = []*handler{
&clusterConfigHandler,
}
// fakeKnownContext is the func that houses the fake component config context.
// NOTE: It does not support concurrent test execution!
func fakeKnownContext(f func()) {
// Save the real values
realKnown := known
realScheme := Scheme
realCodecs := Codecs
// Replace the context with the fake context
known = fakeKnown
Scheme = kubeadmscheme.Scheme
Codecs = kubeadmscheme.Codecs
// Upon function exit, restore the real values
defer func() {
known = realKnown
Scheme = realScheme
Codecs = realCodecs
}()
// Call f in the fake context
f()
}
// testClusterConfigMap is a short hand for creating and possibly signing a test config map.
// This produces config maps that can be loaded by clusterConfigFromCluster
func testClusterConfigMap(yaml string, signIt bool) *v1.ConfigMap {
cm := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeadmConfigConfigMap,
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.ClusterConfigurationConfigMapKey: dedent.Dedent(yaml),
},
}
if signIt {
SignConfigMap(cm)
}
return cm
}
// oldClusterConfigVersion is used as an old unsupported version in tests throughout this file
const oldClusterConfigVersion = "v1alpha1"
var (
// currentClusterConfigVersion represents the current actively supported version of ClusterConfiguration
currentClusterConfigVersion = kubeadmapiv1.SchemeGroupVersion.Version
// currentFooClusterConfig is a minimal currently supported ClusterConfiguration
// with a well known value of clusterName (in this case `foo`)
currentFooClusterConfig = fmt.Sprintf(`
apiVersion: %s
kind: ClusterConfiguration
clusterName: foo
`, kubeadmapiv1.SchemeGroupVersion)
// oldFooClusterConfig is a minimal unsupported ClusterConfiguration
// with a well known value of clusterName (in this case `foo`)
oldFooClusterConfig = fmt.Sprintf(`
apiVersion: %s/%s
kind: ClusterConfiguration
clusterName: foo
`, kubeadmapiv1.GroupName, oldClusterConfigVersion)
// currentBarClusterConfig is a minimal currently supported ClusterConfiguration
// with a well known value of clusterName (in this case `bar`)
currentBarClusterConfig = fmt.Sprintf(`
apiVersion: %s
kind: ClusterConfiguration
clusterName: bar
`, kubeadmapiv1.SchemeGroupVersion)
// oldBarClusterConfig is a minimal unsupported ClusterConfiguration
// with a well known value of clusterName (in this case `bar`)
oldBarClusterConfig = fmt.Sprintf(`
apiVersion: %s/%s
kind: ClusterConfiguration
clusterName: bar
`, kubeadmapiv1.GroupName, oldClusterConfigVersion)
// This is the "minimal" valid config that can be unmarshalled to and from YAML.
// Due to same static defaulting it's not exactly small in size.
validUnmarshallableClusterConfig = struct {
yaml string
obj kubeadmapiv1.ClusterConfiguration
}{
yaml: dedent.Dedent(`
apiServer:
timeoutForControlPlane: 4m
apiVersion: kubeadm.k8s.io/v1beta2
certificatesDir: /etc/kubernetes/pki
clusterName: LeCluster
controllerManager: {}
dns:
type: CoreDNS
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: k8s.gcr.io
kind: ClusterConfiguration
kubernetesVersion: 1.2.3
networking:
dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12
scheduler: {}
`),
obj: kubeadmapiv1.ClusterConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
Kind: "ClusterConfiguration",
},
ClusterName: "LeCluster",
KubernetesVersion: "1.2.3",
CertificatesDir: "/etc/kubernetes/pki",
ImageRepository: "k8s.gcr.io",
Networking: kubeadmapiv1.Networking{
DNSDomain: "cluster.local",
ServiceSubnet: "10.96.0.0/12",
},
DNS: kubeadmapiv1.DNS{
Type: kubeadmapiv1.CoreDNS,
},
Etcd: kubeadmapiv1.Etcd{
Local: &kubeadmapiv1.LocalEtcd{
DataDir: "/var/lib/etcd",
},
},
APIServer: kubeadmapiv1.APIServer{
TimeoutForControlPlane: &metav1.Duration{
Duration: 4 * time.Minute,
},
},
},
}
)
func TestConfigBaseMarshal(t *testing.T) {
fakeKnownContext(func() {
cfg := &clusterConfig{
configBase: configBase{
GroupVersion: kubeadmapiv1.SchemeGroupVersion,
},
config: kubeadmapiv1.ClusterConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
Kind: "ClusterConfiguration",
},
ClusterName: "LeCluster",
KubernetesVersion: "1.2.3",
},
}
b, err := cfg.Marshal()
if err != nil {
t.Fatalf("Marshal failed: %v", err)
}
got := strings.TrimSpace(string(b))
expected := strings.TrimSpace(dedent.Dedent(`
apiServer: {}
apiVersion: kubeadm.k8s.io/v1beta2
clusterName: LeCluster
controllerManager: {}
dns:
type: ""
etcd: {}
kind: ClusterConfiguration
kubernetesVersion: 1.2.3
networking: {}
scheduler: {}
`))
if expected != got {
t.Fatalf("Missmatch between expected and got:\nExpected:\n%s\n---\nGot:\n%s", expected, got)
}
})
}
func TestConfigBaseUnmarshal(t *testing.T) {
fakeKnownContext(func() {
expected := &clusterConfig{
configBase: configBase{
GroupVersion: kubeadmapiv1.SchemeGroupVersion,
},
config: validUnmarshallableClusterConfig.obj,
}
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(validUnmarshallableClusterConfig.yaml))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
got := &clusterConfig{
configBase: configBase{
GroupVersion: kubeadmapiv1.SchemeGroupVersion,
},
}
if err = got.Unmarshal(gvkmap); err != nil {
t.Fatalf("unexpected failure of Unmarshal: %v", err)
}
if !reflect.DeepEqual(got, expected) {
t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", expected, got)
}
})
}
func TestGeneratedConfigFromCluster(t *testing.T) {
fakeKnownContext(func() {
testYAML := dedent.Dedent(`
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
`)
testYAMLHash := fmt.Sprintf("sha256:%x", sha256.Sum256([]byte(testYAML)))
// The SHA256 sum of "The quick brown fox jumps over the lazy dog"
const mismatchHash = "sha256:d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
tests := []struct {
name string
hash string
userSupplied bool
}{
{
name: "Matching hash means generated config",
hash: testYAMLHash,
},
{
name: "Missmatching hash means user supplied config",
hash: mismatchHash,
userSupplied: true,
},
{
name: "No hash means user supplied config",
userSupplied: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
configMap := testClusterConfigMap(testYAML, false)
if test.hash != "" {
configMap.Annotations = map[string]string{
constants.ComponentConfigHashAnnotationKey: test.hash,
}
}
client := clientsetfake.NewSimpleClientset(configMap)
cfg, err := clusterConfigHandler.FromCluster(client, testClusterCfg())
if err != nil {
t.Fatalf("unexpected failure of FromCluster: %v", err)
}
got := cfg.IsUserSupplied()
if got != test.userSupplied {
t.Fatalf("mismatch between expected and got:\n\tExpected: %t\n\tGot: %t", test.userSupplied, got)
}
})
}
})
}
// runClusterConfigFromTest holds common test case data and evaluation code for handler.From* functions
func runClusterConfigFromTest(t *testing.T, perform func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error)) {
fakeKnownContext(func() {
tests := []struct {
name string
in string
out *clusterConfig
expectErr bool
}{
{
name: "Empty document map should return nothing successfully",
},
{
name: "Non-empty document map without the proper API group returns nothing successfully",
in: dedent.Dedent(`
apiVersion: api.example.com/v1
kind: Configuration
`),
},
{
name: "Old config version returns an error",
in: dedent.Dedent(`
apiVersion: kubeadm.k8s.io/v1alpha1
kind: ClusterConfiguration
`),
expectErr: true,
},
{
name: "Unknown kind returns an error",
in: dedent.Dedent(`
apiVersion: kubeadm.k8s.io/v1beta2
kind: Configuration
`),
expectErr: true,
},
{
name: "Valid config gets loaded",
in: validUnmarshallableClusterConfig.yaml,
out: &clusterConfig{
configBase: configBase{
GroupVersion: clusterConfigHandler.GroupVersion,
userSupplied: true,
},
config: validUnmarshallableClusterConfig.obj,
},
},
{
name: "Valid config gets loaded even if coupled with an extra document",
in: "apiVersion: api.example.com/v1\nkind: Configuration\n---\n" + validUnmarshallableClusterConfig.yaml,
out: &clusterConfig{
configBase: configBase{
GroupVersion: clusterConfigHandler.GroupVersion,
userSupplied: true,
},
config: validUnmarshallableClusterConfig.obj,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
componentCfg, err := perform(t, test.in)
if err != nil {
if !test.expectErr {
t.Errorf("unexpected failure: %v", err)
}
} else {
if test.expectErr {
t.Error("unexpected success")
} else {
if componentCfg == nil {
if test.out != nil {
t.Error("unexpected nil result")
}
} else {
if got, ok := componentCfg.(*clusterConfig); !ok {
t.Error("different result type")
} else {
if test.out == nil {
t.Errorf("unexpected result: %v", got)
} else {
if !reflect.DeepEqual(test.out, got) {
t.Errorf("missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.out, got)
}
}
}
}
}
}
})
}
})
}
func TestLoadingFromDocumentMap(t *testing.T) {
runClusterConfigFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(in))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
return clusterConfigHandler.FromDocumentMap(gvkmap)
})
}
func TestLoadingFromCluster(t *testing.T) {
runClusterConfigFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
client := clientsetfake.NewSimpleClientset(
testClusterConfigMap(in, false),
)
return clusterConfigHandler.FromCluster(client, testClusterCfg())
})
}
func TestFetchFromClusterWithLocalOverwrites(t *testing.T) {
fakeKnownContext(func() {
cases := []struct {
desc string
obj runtime.Object
config string
expectedValue string
isNotLoaded bool
expectedErr bool
}{
{
desc: "appropriate cluster object without overwrite is used",
obj: testClusterConfigMap(currentFooClusterConfig, false),
expectedValue: "foo",
},
{
desc: "appropriate cluster object with appropriate overwrite is overwritten",
obj: testClusterConfigMap(currentFooClusterConfig, false),
config: dedent.Dedent(currentBarClusterConfig),
expectedValue: "bar",
},
{
desc: "appropriate cluster object with old overwrite returns an error",
obj: testClusterConfigMap(currentFooClusterConfig, false),
config: dedent.Dedent(oldBarClusterConfig),
expectedErr: true,
},
{
desc: "old config without overwrite returns an error",
obj: testClusterConfigMap(oldFooClusterConfig, false),
expectedErr: true,
},
{
desc: "old config with appropriate overwrite returns the substitute",
obj: testClusterConfigMap(oldFooClusterConfig, false),
config: dedent.Dedent(currentBarClusterConfig),
expectedValue: "bar",
},
{
desc: "old config with old overwrite returns an error",
obj: testClusterConfigMap(oldFooClusterConfig, false),
config: dedent.Dedent(oldBarClusterConfig),
expectedErr: true,
},
{
desc: "appropriate signed cluster object without overwrite is used",
obj: testClusterConfigMap(currentFooClusterConfig, true),
expectedValue: "foo",
},
{
desc: "appropriate signed cluster object with appropriate overwrite is overwritten",
obj: testClusterConfigMap(currentFooClusterConfig, true),
config: dedent.Dedent(currentBarClusterConfig),
expectedValue: "bar",
},
{
desc: "appropriate signed cluster object with old overwrite returns an error",
obj: testClusterConfigMap(currentFooClusterConfig, true),
config: dedent.Dedent(oldBarClusterConfig),
expectedErr: true,
},
{
desc: "old signed config without an overwrite is not loaded",
obj: testClusterConfigMap(oldFooClusterConfig, true),
isNotLoaded: true,
},
{
desc: "old signed config with appropriate overwrite returns the substitute",
obj: testClusterConfigMap(oldFooClusterConfig, true),
config: dedent.Dedent(currentBarClusterConfig),
expectedValue: "bar",
},
{
desc: "old signed config with old overwrite returns an error",
obj: testClusterConfigMap(oldFooClusterConfig, true),
config: dedent.Dedent(oldBarClusterConfig),
expectedErr: true,
},
}
for _, test := range cases {
t.Run(test.desc, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset(test.obj)
docmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test.config))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
clusterCfg := testClusterCfg()
err = FetchFromClusterWithLocalOverwrites(clusterCfg, client, docmap)
if err != nil {
if !test.expectedErr {
t.Errorf("unexpected failure: %v", err)
}
} else {
if test.expectedErr {
t.Error("unexpected success")
} else {
clusterCfg, ok := clusterCfg.ComponentConfigs[kubeadmapiv1.GroupName]
if !ok {
if !test.isNotLoaded {
t.Error("no config was loaded when it should have been")
}
} else {
actualConfig, ok := clusterCfg.(*clusterConfig)
if !ok {
t.Error("the config is not of the expected type")
} else if actualConfig.config.ClusterName != test.expectedValue {
t.Errorf("unexpected value:\n\tgot: %q\n\texpected: %q", actualConfig.config.ClusterName, test.expectedValue)
}
}
}
}
})
}
})
}
func TestGetVersionStates(t *testing.T) {
fakeKnownContext(func() {
versionStateCurrent := outputapi.ComponentConfigVersionState{
Group: kubeadmapiv1.GroupName,
CurrentVersion: currentClusterConfigVersion,
PreferredVersion: currentClusterConfigVersion,
}
versionStateOld := outputapi.ComponentConfigVersionState{
Group: kubeadmapiv1.GroupName,
CurrentVersion: oldClusterConfigVersion,
PreferredVersion: currentClusterConfigVersion,
ManualUpgradeRequired: true,
}
cases := []struct {
desc string
obj runtime.Object
config string
expected outputapi.ComponentConfigVersionState
}{
{
desc: "appropriate cluster object without overwrite",
obj: testClusterConfigMap(currentFooClusterConfig, false),
expected: versionStateCurrent,
},
{
desc: "appropriate cluster object with appropriate overwrite",
obj: testClusterConfigMap(currentFooClusterConfig, false),
config: dedent.Dedent(currentBarClusterConfig),
expected: versionStateCurrent,
},
{
desc: "appropriate cluster object with old overwrite",
obj: testClusterConfigMap(currentFooClusterConfig, false),
config: dedent.Dedent(oldBarClusterConfig),
expected: versionStateOld,
},
{
desc: "old config without overwrite returns an error",
obj: testClusterConfigMap(oldFooClusterConfig, false),
expected: versionStateOld,
},
{
desc: "old config with appropriate overwrite",
obj: testClusterConfigMap(oldFooClusterConfig, false),
config: dedent.Dedent(currentBarClusterConfig),
expected: versionStateCurrent,
},
{
desc: "old config with old overwrite",
obj: testClusterConfigMap(oldFooClusterConfig, false),
config: dedent.Dedent(oldBarClusterConfig),
expected: versionStateOld,
},
{
desc: "appropriate signed cluster object without overwrite",
obj: testClusterConfigMap(currentFooClusterConfig, true),
expected: versionStateCurrent,
},
{
desc: "appropriate signed cluster object with appropriate overwrite",
obj: testClusterConfigMap(currentFooClusterConfig, true),
config: dedent.Dedent(currentBarClusterConfig),
expected: versionStateCurrent,
},
{
desc: "appropriate signed cluster object with old overwrit",
obj: testClusterConfigMap(currentFooClusterConfig, true),
config: dedent.Dedent(oldBarClusterConfig),
expected: versionStateOld,
},
{
desc: "old signed config without an overwrite",
obj: testClusterConfigMap(oldFooClusterConfig, true),
expected: outputapi.ComponentConfigVersionState{
Group: kubeadmapiv1.GroupName,
CurrentVersion: "", // The config is treated as if it's missing
PreferredVersion: currentClusterConfigVersion,
},
},
{
desc: "old signed config with appropriate overwrite",
obj: testClusterConfigMap(oldFooClusterConfig, true),
config: dedent.Dedent(currentBarClusterConfig),
expected: versionStateCurrent,
},
{
desc: "old signed config with old overwrite",
obj: testClusterConfigMap(oldFooClusterConfig, true),
config: dedent.Dedent(oldBarClusterConfig),
expected: versionStateOld,
},
}
for _, test := range cases {
t.Run(test.desc, func(t *testing.T) {
client := clientsetfake.NewSimpleClientset(test.obj)
docmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test.config))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
clusterCfg := testClusterCfg()
got, err := GetVersionStates(clusterCfg, client, docmap)
if err != nil {
t.Errorf("unexpected error: %v", err)
} else if len(got) != 1 {
t.Errorf("got %d, but expected only a single result: %v", len(got), got)
} else if got[0] != test.expected {
t.Errorf("unexpected result:\n\texpected: %v\n\tgot: %v", test.expected, got[0])
}
})
}
})
}

View File

@ -17,18 +17,16 @@ limitations under the License.
package componentconfigs
import (
"crypto/sha256"
"fmt"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/lithammer/dedent"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/apimachinery/pkg/runtime/schema"
clientsetfake "k8s.io/client-go/kubernetes/fake"
kubeletconfig "k8s.io/kubelet/config/v1beta1"
utilpointer "k8s.io/utils/pointer"
@ -37,139 +35,17 @@ import (
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
// kubeletMarshalCases holds common marshal test cases for both the marshal and unmarshal tests
var kubeletMarshalCases = []struct {
name string
obj *kubeletConfig
yaml string
}{
{
name: "Empty config",
obj: &kubeletConfig{
configBase: configBase{
GroupVersion: kubeletconfig.SchemeGroupVersion,
},
config: kubeletconfig.KubeletConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeletconfig.SchemeGroupVersion.String(),
Kind: "KubeletConfiguration",
},
},
func testKubeletConfigMap(contents string) *v1.ConfigMap {
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion),
Namespace: metav1.NamespaceSystem,
},
yaml: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous: {}
webhook:
cacheTTL: 0s
x509: {}
authorization:
webhook:
cacheAuthorizedTTL: 0s
cacheUnauthorizedTTL: 0s
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
logging: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
runtimeRequestTimeout: 0s
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s
`),
},
{
name: "Non empty config",
obj: &kubeletConfig{
configBase: configBase{
GroupVersion: kubeletconfig.SchemeGroupVersion,
},
config: kubeletconfig.KubeletConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeletconfig.SchemeGroupVersion.String(),
Kind: "KubeletConfiguration",
},
Address: "1.2.3.4",
Port: 12345,
RotateCertificates: true,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(contents),
},
yaml: dedent.Dedent(`
address: 1.2.3.4
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous: {}
webhook:
cacheTTL: 0s
x509: {}
authorization:
webhook:
cacheAuthorizedTTL: 0s
cacheUnauthorizedTTL: 0s
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
logging: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
port: 12345
rotateCertificates: true
runtimeRequestTimeout: 0s
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s
`),
},
}
func TestKubeletMarshal(t *testing.T) {
for _, test := range kubeletMarshalCases {
t.Run(test.name, func(t *testing.T) {
b, err := test.obj.Marshal()
if err != nil {
t.Fatalf("Marshal failed: %v", err)
}
got := strings.TrimSpace(string(b))
expected := strings.TrimSpace(test.yaml)
if expected != string(got) {
t.Fatalf("Missmatch between expected and got:\nExpected:\n%s\n---\nGot:\n%s", expected, string(got))
}
})
}
}
func TestKubeletUnmarshal(t *testing.T) {
for _, test := range kubeletMarshalCases {
t.Run(test.name, func(t *testing.T) {
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test.yaml))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
got := &kubeletConfig{
configBase: configBase{
GroupVersion: kubeletconfig.SchemeGroupVersion,
},
}
if err = got.Unmarshal(gvkmap); err != nil {
t.Fatalf("unexpected failure of Unmarshal: %v", err)
}
if !reflect.DeepEqual(got, test.obj) {
t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.obj, got)
}
})
}
}
@ -411,227 +287,43 @@ func TestKubeletDefault(t *testing.T) {
}
// runKubeletFromTest holds common test case data and evaluation code for kubeletHandler.From* functions
func runKubeletFromTest(t *testing.T, perform func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error)) {
tests := []struct {
name string
in string
out *kubeletConfig
expectErr bool
}{
{
name: "Empty document map should return nothing successfully",
},
{
name: "Non-empty non-kubelet document map returns nothing successfully",
in: dedent.Dedent(`
apiVersion: api.example.com/v1
kind: Configuration
`),
},
{
name: "Old kubelet version returns an error",
in: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1alpha1
kind: KubeletConfiguration
`),
expectErr: true,
},
{
name: "Wrong kubelet kind returns an error",
in: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: Configuration
`),
expectErr: true,
},
{
name: "Valid kubelet only config gets loaded",
in: dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 1.2.3.4
port: 12345
rotateCertificates: true
`),
out: &kubeletConfig{
configBase: configBase{
GroupVersion: kubeletHandler.GroupVersion,
userSupplied: true,
},
config: kubeletconfig.KubeletConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeletHandler.GroupVersion.String(),
Kind: "KubeletConfiguration",
},
Address: "1.2.3.4",
Port: 12345,
RotateCertificates: true,
},
},
},
{
name: "Valid kubelet config gets loaded when coupled with an extra document",
in: dedent.Dedent(`
apiVersion: api.example.com/v1
kind: Configuration
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 1.2.3.4
port: 12345
rotateCertificates: true
`),
out: &kubeletConfig{
configBase: configBase{
GroupVersion: kubeletHandler.GroupVersion,
userSupplied: true,
},
config: kubeletconfig.KubeletConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeletHandler.GroupVersion.String(),
Kind: "KubeletConfiguration",
},
Address: "1.2.3.4",
Port: 12345,
RotateCertificates: true,
},
},
},
}
func runKubeletFromTest(t *testing.T, perform func(gvk schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error)) {
const (
kind = "KubeletConfiguration"
clusterDomain = "foo.bar"
)
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
componentCfg, err := perform(t, test.in)
if err != nil {
if !test.expectErr {
t.Errorf("unexpected failure: %v", err)
}
} else {
if test.expectErr {
t.Error("unexpected success")
} else {
if componentCfg == nil {
if test.out != nil {
t.Error("unexpected nil result")
}
} else {
if got, ok := componentCfg.(*kubeletConfig); !ok {
t.Error("different result type")
} else {
if test.out == nil {
t.Errorf("unexpected result: %v", got)
} else {
if !reflect.DeepEqual(test.out, got) {
t.Errorf("missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.out, got)
}
}
}
}
}
}
})
gvk := kubeletHandler.GroupVersion.WithKind(kind)
yaml := fmt.Sprintf("apiVersion: %s\nkind: %s\nclusterDomain: %s", kubeletHandler.GroupVersion, kind, clusterDomain)
cfg, err := perform(gvk, yaml)
if err != nil {
t.Fatalf("unexpected failure: %v", err)
}
if cfg == nil {
t.Fatal("no config loaded where it should have been")
}
if kubeletCfg, ok := cfg.(*kubeletConfig); !ok {
t.Fatalf("found different object type than expected: %s", reflect.TypeOf(cfg))
} else if kubeletCfg.config.ClusterDomain != clusterDomain {
t.Fatalf("unexpected control value (clusterDomain):\n\tgot: %q\n\texpected: %q", kubeletCfg.config.ClusterDomain, clusterDomain)
}
}
func TestKubeletFromDocumentMap(t *testing.T) {
runKubeletFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(in))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
return kubeletHandler.FromDocumentMap(gvkmap)
runKubeletFromTest(t, func(gvk schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error) {
return kubeletHandler.FromDocumentMap(kubeadmapi.DocumentMap{
gvk: []byte(yaml),
})
})
}
func TestKubeletFromCluster(t *testing.T) {
runKubeletFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
clusterCfg := &kubeadmapi.ClusterConfiguration{
KubernetesVersion: constants.CurrentKubernetesVersion.String(),
}
k8sVersion := version.MustParseGeneric(clusterCfg.KubernetesVersion)
runKubeletFromTest(t, func(_ schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error) {
client := clientsetfake.NewSimpleClientset(
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(k8sVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: in,
},
},
testKubeletConfigMap(yaml),
)
return kubeletHandler.FromCluster(client, clusterCfg)
return kubeletHandler.FromCluster(client, testClusterCfg())
})
}
func TestGeneratedKubeletFromCluster(t *testing.T) {
testYAML := dedent.Dedent(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: 1.2.3.4
port: 12345
rotateCertificates: true
`)
testYAMLHash := fmt.Sprintf("sha256:%x", sha256.Sum256([]byte(testYAML)))
// The SHA256 sum of "The quick brown fox jumps over the lazy dog"
const mismatchHash = "sha256:d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
tests := []struct {
name string
hash string
userSupplied bool
}{
{
name: "Matching hash means generated config",
hash: testYAMLHash,
},
{
name: "Missmatching hash means user supplied config",
hash: mismatchHash,
userSupplied: true,
},
{
name: "No hash means user supplied config",
userSupplied: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
clusterCfg := &kubeadmapi.ClusterConfiguration{
KubernetesVersion: constants.CurrentKubernetesVersion.String(),
}
k8sVersion := version.MustParseGeneric(clusterCfg.KubernetesVersion)
configMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(k8sVersion),
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeletBaseConfigurationConfigMapKey: testYAML,
},
}
if test.hash != "" {
configMap.Annotations = map[string]string{
constants.ComponentConfigHashAnnotationKey: test.hash,
}
}
client := clientsetfake.NewSimpleClientset(configMap)
cfg, err := kubeletHandler.FromCluster(client, clusterCfg)
if err != nil {
t.Fatalf("unexpected failure of FromCluster: %v", err)
}
got := cfg.IsUserSupplied()
if got != test.userSupplied {
t.Fatalf("mismatch between expected and got:\n\tExpected: %t\n\tGot: %t", test.userSupplied, got)
}
})
}
}

View File

@ -17,10 +17,8 @@ limitations under the License.
package componentconfigs
import (
"crypto/sha256"
"fmt"
"reflect"
"strings"
"testing"
"github.com/lithammer/dedent"
@ -31,185 +29,22 @@ import (
componentbaseconfig "k8s.io/component-base/config/v1alpha1"
kubeproxyconfig "k8s.io/kube-proxy/config/v1alpha1"
"k8s.io/apimachinery/pkg/runtime/schema"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
// kubeProxyMarshalCases holds common marshal test cases for both the marshal and unmarshal tests
var kubeProxyMarshalCases = []struct {
name string
obj *kubeProxyConfig
yaml string
}{
{
name: "Empty config",
obj: &kubeProxyConfig{
configBase: configBase{
GroupVersion: kubeproxyconfig.SchemeGroupVersion,
},
config: kubeproxyconfig.KubeProxyConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeproxyconfig.SchemeGroupVersion.String(),
Kind: "KubeProxyConfiguration",
},
},
func testKubeProxyConfigMap(contents string) *v1.ConfigMap {
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeProxyConfigMap,
Namespace: metav1.NamespaceSystem,
},
yaml: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: ""
bindAddressHardFail: false
clientConnection:
acceptContentTypes: ""
burst: 0
contentType: ""
kubeconfig: ""
qps: 0
clusterCIDR: ""
configSyncPeriod: 0s
conntrack:
maxPerCore: null
min: null
tcpCloseWaitTimeout: null
tcpEstablishedTimeout: null
detectLocalMode: ""
enableProfiling: false
healthzBindAddress: ""
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: null
minSyncPeriod: 0s
syncPeriod: 0s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
strictARP: false
syncPeriod: 0s
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
kind: KubeProxyConfiguration
metricsBindAddress: ""
mode: ""
nodePortAddresses: null
oomScoreAdj: null
portRange: ""
showHiddenMetricsForVersion: ""
udpIdleTimeout: 0s
winkernel:
enableDSR: false
networkName: ""
sourceVip: ""
`),
},
{
name: "Non empty config",
obj: &kubeProxyConfig{
configBase: configBase{
GroupVersion: kubeproxyconfig.SchemeGroupVersion,
},
config: kubeproxyconfig.KubeProxyConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeproxyconfig.SchemeGroupVersion.String(),
Kind: "KubeProxyConfiguration",
},
BindAddress: "1.2.3.4",
EnableProfiling: true,
},
Data: map[string]string{
constants.KubeProxyConfigMapKey: dedent.Dedent(contents),
},
yaml: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 1.2.3.4
bindAddressHardFail: false
clientConnection:
acceptContentTypes: ""
burst: 0
contentType: ""
kubeconfig: ""
qps: 0
clusterCIDR: ""
configSyncPeriod: 0s
conntrack:
maxPerCore: null
min: null
tcpCloseWaitTimeout: null
tcpEstablishedTimeout: null
detectLocalMode: ""
enableProfiling: true
healthzBindAddress: ""
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: null
minSyncPeriod: 0s
syncPeriod: 0s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
strictARP: false
syncPeriod: 0s
tcpFinTimeout: 0s
tcpTimeout: 0s
udpTimeout: 0s
kind: KubeProxyConfiguration
metricsBindAddress: ""
mode: ""
nodePortAddresses: null
oomScoreAdj: null
portRange: ""
showHiddenMetricsForVersion: ""
udpIdleTimeout: 0s
winkernel:
enableDSR: false
networkName: ""
sourceVip: ""
`),
},
}
func TestKubeProxyMarshal(t *testing.T) {
for _, test := range kubeProxyMarshalCases {
t.Run(test.name, func(t *testing.T) {
b, err := test.obj.Marshal()
if err != nil {
t.Fatalf("Marshal failed: %v", err)
}
got := strings.TrimSpace(string(b))
expected := strings.TrimSpace(test.yaml)
if expected != string(got) {
t.Fatalf("Missmatch between expected and got:\nExpected:\n%s\n---\nGot:\n%s", expected, string(got))
}
})
}
}
func TestKubeProxyUnmarshal(t *testing.T) {
for _, test := range kubeProxyMarshalCases {
t.Run(test.name, func(t *testing.T) {
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test.yaml))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
got := &kubeProxyConfig{
configBase: configBase{
GroupVersion: kubeproxyconfig.SchemeGroupVersion,
},
}
if err = got.Unmarshal(gvkmap); err != nil {
t.Fatalf("unexpected failure of Unmarshal: %v", err)
}
if !reflect.DeepEqual(got, test.obj) {
t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.obj, got)
}
})
}
}
@ -331,210 +166,44 @@ func TestKubeProxyDefault(t *testing.T) {
}
// runKubeProxyFromTest holds common test case data and evaluation code for kubeProxyHandler.From* functions
func runKubeProxyFromTest(t *testing.T, perform func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error)) {
tests := []struct {
name string
in string
out *kubeProxyConfig
expectErr bool
}{
{
name: "Empty document map should return nothing successfully",
},
{
name: "Non-empty non-kube-proxy document map returns nothing successfully",
in: dedent.Dedent(`
apiVersion: api.example.com/v1
kind: Configuration
`),
},
{
name: "Old kube-proxy version returns an error",
in: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha0
kind: KubeProxyConfiguration
`),
expectErr: true,
},
{
name: "Wrong kube-proxy kind returns an error",
in: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: Configuration
`),
expectErr: true,
},
{
name: "Valid kube-proxy only config gets loaded",
in: dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
bindAddress: 1.2.3.4
enableProfiling: true
`),
out: &kubeProxyConfig{
configBase: configBase{
GroupVersion: kubeProxyHandler.GroupVersion,
userSupplied: true,
},
config: kubeproxyconfig.KubeProxyConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeProxyHandler.GroupVersion.String(),
Kind: "KubeProxyConfiguration",
},
BindAddress: "1.2.3.4",
EnableProfiling: true,
},
},
},
{
name: "Valid kube-proxy config gets loaded when coupled with an extra document",
in: dedent.Dedent(`
apiVersion: api.example.com/v1
kind: Configuration
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
bindAddress: 1.2.3.4
enableProfiling: true
`),
out: &kubeProxyConfig{
configBase: configBase{
GroupVersion: kubeProxyHandler.GroupVersion,
userSupplied: true,
},
config: kubeproxyconfig.KubeProxyConfiguration{
TypeMeta: metav1.TypeMeta{
APIVersion: kubeProxyHandler.GroupVersion.String(),
Kind: "KubeProxyConfiguration",
},
BindAddress: "1.2.3.4",
EnableProfiling: true,
},
},
},
}
func runKubeProxyFromTest(t *testing.T, perform func(gvk schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error)) {
const (
kind = "KubeProxyConfiguration"
clusterCIDR = "1.2.3.4/16"
)
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
componentCfg, err := perform(t, test.in)
if err != nil {
if !test.expectErr {
t.Errorf("unexpected failure: %v", err)
}
} else {
if test.expectErr {
t.Error("unexpected success")
} else {
if componentCfg == nil {
if test.out != nil {
t.Error("unexpected nil result")
}
} else {
if got, ok := componentCfg.(*kubeProxyConfig); !ok {
t.Error("different result type")
} else {
if test.out == nil {
t.Errorf("unexpected result: %v", got)
} else {
if !reflect.DeepEqual(test.out, got) {
t.Errorf("missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.out, got)
}
}
}
}
}
}
})
gvk := kubeProxyHandler.GroupVersion.WithKind(kind)
yaml := fmt.Sprintf("apiVersion: %s\nkind: %s\nclusterCIDR: %s", kubeProxyHandler.GroupVersion, kind, clusterCIDR)
cfg, err := perform(gvk, yaml)
if err != nil {
t.Fatalf("unexpected failure: %v", err)
}
if cfg == nil {
t.Fatal("no config loaded where it should have been")
}
if kubeproxyCfg, ok := cfg.(*kubeProxyConfig); !ok {
t.Fatalf("found different object type than expected: %s", reflect.TypeOf(cfg))
} else if kubeproxyCfg.config.ClusterCIDR != clusterCIDR {
t.Fatalf("unexpected control value (clusterDomain):\n\tgot: %q\n\texpected: %q", kubeproxyCfg.config.ClusterCIDR, clusterCIDR)
}
}
func TestKubeProxyFromDocumentMap(t *testing.T) {
runKubeProxyFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(in))
if err != nil {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
}
return kubeProxyHandler.FromDocumentMap(gvkmap)
runKubeProxyFromTest(t, func(gvk schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error) {
return kubeProxyHandler.FromDocumentMap(kubeadmapi.DocumentMap{
gvk: []byte(yaml),
})
})
}
func TestKubeProxyFromCluster(t *testing.T) {
runKubeProxyFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
runKubeProxyFromTest(t, func(_ schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error) {
client := clientsetfake.NewSimpleClientset(
&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeProxyConfigMap,
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeProxyConfigMapKey: in,
},
},
testKubeProxyConfigMap(yaml),
)
return kubeProxyHandler.FromCluster(client, &kubeadmapi.ClusterConfiguration{})
return kubeProxyHandler.FromCluster(client, testClusterCfg())
})
}
func TestGeneratedKubeProxyFromCluster(t *testing.T) {
testYAML := dedent.Dedent(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
bindAddress: 1.2.3.4
enableProfiling: true
`)
testYAMLHash := fmt.Sprintf("sha256:%x", sha256.Sum256([]byte(testYAML)))
// The SHA256 sum of "The quick brown fox jumps over the lazy dog"
const mismatchHash = "sha256:d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"
tests := []struct {
name string
hash string
userSupplied bool
}{
{
name: "Matching hash means generated config",
hash: testYAMLHash,
},
{
name: "Missmatching hash means user supplied config",
hash: mismatchHash,
userSupplied: true,
},
{
name: "No hash means user supplied config",
userSupplied: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
configMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubeProxyConfigMap,
Namespace: metav1.NamespaceSystem,
},
Data: map[string]string{
constants.KubeProxyConfigMapKey: testYAML,
},
}
if test.hash != "" {
configMap.Annotations = map[string]string{
constants.ComponentConfigHashAnnotationKey: test.hash,
}
}
client := clientsetfake.NewSimpleClientset(configMap)
cfg, err := kubeProxyHandler.FromCluster(client, &kubeadmapi.ClusterConfiguration{})
if err != nil {
t.Fatalf("unexpected failure of FromCluster: %v", err)
}
got := cfg.IsUserSupplied()
if got != test.userSupplied {
t.Fatalf("mismatch between expected and got:\n\tExpected: %t\n\tGot: %t", test.userSupplied, got)
}
})
}
}

View File

@ -56,8 +56,6 @@ go_test(
embed = [":go_default_library"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
"//cmd/kubeadm/app/componentconfigs:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
@ -74,7 +72,6 @@ go_test(
"//staging/src/k8s.io/client-go/testing:go_default_library",
"//vendor/github.com/lithammer/dedent:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/github.com/pmezard/go-difflib/difflib:go_default_library",
"//vendor/sigs.k8s.io/yaml:go_default_library",
],
)

View File

@ -23,8 +23,6 @@ import (
"path/filepath"
"testing"
"github.com/pmezard/go-difflib/difflib"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
@ -32,24 +30,11 @@ import (
"sigs.k8s.io/yaml"
)
func diff(expected, actual []byte) string {
// Write out the diff
var diffBytes bytes.Buffer
difflib.WriteUnifiedDiff(&diffBytes, difflib.UnifiedDiff{
A: difflib.SplitLines(string(expected)),
B: difflib.SplitLines(string(actual)),
FromFile: "expected",
ToFile: "actual",
Context: 3,
})
return diffBytes.String()
}
func TestLoadInitConfigurationFromFile(t *testing.T) {
// Create temp folder for the test case
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
t.Fatalf("Couldn't create tmpdir: %v", err)
}
defer os.RemoveAll(tmpdir)
@ -100,7 +85,7 @@ func TestLoadInitConfigurationFromFile(t *testing.T) {
cfgPath := filepath.Join(tmpdir, rt.name)
err := ioutil.WriteFile(cfgPath, rt.fileContents, 0644)
if err != nil {
t.Errorf("Couldn't create file")
t.Errorf("Couldn't create file: %v", err)
return
}
@ -116,7 +101,7 @@ func TestLoadInitConfigurationFromFile(t *testing.T) {
}
if obj == nil {
t.Errorf("Unexpected nil return value")
t.Error("Unexpected nil return value")
}
}
})

View File

@ -17,100 +17,103 @@ limitations under the License.
package config
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)
const (
nodeV1beta1YAML = "testdata/conversion/node/v1beta1.yaml"
nodeV1beta2YAML = "testdata/conversion/node/v1beta2.yaml"
nodeInternalYAML = "testdata/conversion/node/internal.yaml"
nodeIncompleteYAML = "testdata/defaulting/node/incomplete.yaml"
nodeDefaultedYAML = "testdata/defaulting/node/defaulted.yaml"
nodeInvalidYAML = "testdata/validation/invalid_nodecfg.yaml"
"github.com/lithammer/dedent"
)
func TestLoadJoinConfigurationFromFile(t *testing.T) {
// Create temp folder for the test case
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir: %v", err)
}
defer os.RemoveAll(tmpdir)
// cfgFiles is in cluster_test.go
var tests = []struct {
name, in, out string
groupVersion schema.GroupVersion
expectedErr bool
name string
fileContents string
expectErr bool
}{
// These tests are reading one file, loading it using LoadJoinConfigurationFromFile that all of kubeadm is using for unmarshal of our API types,
// and then marshals the internal object to the expected groupVersion
{ // v1beta1 -> internal
name: "v1beta1ToInternal",
in: nodeV1beta1YAML,
out: nodeInternalYAML,
groupVersion: kubeadm.SchemeGroupVersion,
{
name: "empty file causes error",
expectErr: true,
},
{ // v1beta1 -> internal -> v1beta1
name: "v1beta1Tov1beta1",
in: nodeV1beta1YAML,
out: nodeV1beta1YAML,
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
{
name: "Invalid v1beta1 causes error",
fileContents: dedent.Dedent(`
apiVersion: kubeadm.k8s.io/v1beta1
kind: JoinConfiguration
`),
expectErr: true,
},
{ // v1beta2 -> internal
name: "v1beta2ToInternal",
in: nodeV1beta2YAML,
out: nodeInternalYAML,
groupVersion: kubeadm.SchemeGroupVersion,
{
name: "valid v1beta1 is loaded",
fileContents: dedent.Dedent(`
apiVersion: kubeadm.k8s.io/v1beta1
kind: JoinConfiguration
caCertPath: /etc/kubernetes/pki/ca.crt
discovery:
bootstrapToken:
apiServerEndpoint: kube-apiserver:6443
token: abcdef.0123456789abcdef
unsafeSkipCAVerification: true
timeout: 5m0s
tlsBootstrapToken: abcdef.0123456789abcdef
`),
},
{ // v1beta2 -> internal -> v1beta2
name: "v1beta2Tov1beta2",
in: nodeV1beta2YAML,
out: nodeV1beta2YAML,
groupVersion: kubeadmapiv1beta2.SchemeGroupVersion,
{
name: "Invalid v1beta2 causes error",
fileContents: dedent.Dedent(`
apiVersion: kubeadm.k8s.io/v1beta2
kind: JoinConfiguration
`),
expectErr: true,
},
// These tests are reading one file that has only a subset of the fields populated, loading it using LoadJoinConfigurationFromFile,
// and then marshals the internal object to the expected groupVersion
{ // v1beta2 -> default -> validate -> internal -> v1beta2
name: "incompleteYAMLToDefaultedv1beta2",
in: nodeIncompleteYAML,
out: nodeDefaultedYAML,
groupVersion: kubeadmapiv1beta2.SchemeGroupVersion,
},
{ // v1beta2 -> validation should fail
name: "invalidYAMLShouldFail",
in: nodeInvalidYAML,
expectedErr: true,
{
name: "valid v1beta2 is loaded",
fileContents: dedent.Dedent(`
apiVersion: kubeadm.k8s.io/v1beta2
kind: JoinConfiguration
caCertPath: /etc/kubernetes/pki/ca.crt
discovery:
bootstrapToken:
apiServerEndpoint: kube-apiserver:6443
token: abcdef.0123456789abcdef
unsafeSkipCAVerification: true
timeout: 5m0s
tlsBootstrapToken: abcdef.0123456789abcdef
`),
},
}
for _, rt := range tests {
t.Run(rt.name, func(t2 *testing.T) {
internalcfg, err := LoadJoinConfigurationFromFile(rt.in)
cfgPath := filepath.Join(tmpdir, rt.name)
err := ioutil.WriteFile(cfgPath, []byte(rt.fileContents), 0644)
if err != nil {
if rt.expectedErr {
t.Errorf("Couldn't create file: %v", err)
return
}
obj, err := LoadJoinConfigurationFromFile(cfgPath)
if rt.expectErr {
if err == nil {
t.Error("Unexpected success")
}
} else {
if err != nil {
t.Errorf("Error reading file: %v", err)
return
}
t2.Fatalf("couldn't unmarshal test data: %v", err)
} else if rt.expectedErr {
t2.Fatalf("expected error, but no error returned")
}
actual, err := kubeadmutil.MarshalToYamlForCodecs(internalcfg, rt.groupVersion, scheme.Codecs)
if err != nil {
t2.Fatalf("couldn't marshal internal object: %v", err)
}
expected, err := ioutil.ReadFile(rt.out)
if err != nil {
t2.Fatalf("couldn't read test data: %v", err)
}
if !bytes.Equal(expected, actual) {
t2.Errorf("the expected and actual output differs.\n\tin: %s\n\tout: %s\n\tgroupversion: %s\n\tdiff: \n%s\n",
rt.in, rt.out, rt.groupVersion.String(), diff(expected, actual))
if obj == nil {
t.Error("Unexpected nil return value")
}
}
})
}

View File

@ -1,208 +0,0 @@
APIServer:
CertSANs: null
ExtraArgs:
authorization-mode: Node,RBAC,Webhook
ExtraVolumes:
- HostPath: /host/read-only
MountPath: /mount/read-only
Name: ReadOnlyVolume
PathType: ""
ReadOnly: true
- HostPath: /host/writable
MountPath: /mount/writable
Name: WritableVolume
PathType: ""
ReadOnly: false
TimeoutForControlPlane: 4m0s
BootstrapTokens:
- Description: ""
Expires: null
Groups:
- system:bootstrappers:kubeadm:default-node-token
TTL: 24h0m0s
Token: s73ybu.6tw6wnqgp5z0wb77
Usages:
- signing
- authentication
CIImageRepository: ""
CertificatesDir: /etc/kubernetes/pki
ClusterName: kubernetes
ComponentConfigs:
KubeProxy:
BindAddress: 0.0.0.0
BindAddressHardFail: false
ClientConnection:
AcceptContentTypes: ""
Burst: 10
ContentType: application/vnd.kubernetes.protobuf
Kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
QPS: 5
ClusterCIDR: ""
ConfigSyncPeriod: 15m0s
Conntrack:
MaxPerCore: 32768
Min: 131072
TCPCloseWaitTimeout: 1h0m0s
TCPEstablishedTimeout: 24h0m0s
EnableProfiling: false
FeatureGates:
ServiceNodeExclusion: true
SupportIPVSProxyMode: true
HealthzBindAddress: 0.0.0.0:10256
HostnameOverride: ""
IPTables:
MasqueradeAll: false
MasqueradeBit: 14
MinSyncPeriod: 0s
SyncPeriod: 30s
IPVS:
ExcludeCIDRs: null
MinSyncPeriod: 0s
Scheduler: ""
SyncPeriod: 30s
MetricsBindAddress: 127.0.0.1:10249
Mode: iptables
NodePortAddresses: null
OOMScoreAdj: -999
PortRange: ""
UDPIdleTimeout: 250ms
Winkernel:
EnableDSR: false
NetworkName: ""
SourceVip: ""
Kubelet:
Address: 1.2.3.4
AllowedUnsafeSysctls: null
Authentication:
Anonymous:
Enabled: false
Webhook:
CacheTTL: 2m0s
Enabled: true
X509:
ClientCAFile: /etc/kubernetes/pki/ca.crt
Authorization:
Mode: Webhook
Webhook:
CacheAuthorizedTTL: 5m0s
CacheUnauthorizedTTL: 30s
CPUCFSQuota: true
CPUCFSQuotaPeriod: 0s
CPUManagerPolicy: none
CPUManagerReconcilePeriod: 10s
CgroupDriver: cgroupfs
CgroupRoot: ""
CgroupsPerQOS: true
ClusterDNS:
- 10.96.0.10
ClusterDomain: cluster.local
ConfigMapAndSecretChangeDetectionStrategy: Watch
ContainerLogMaxFiles: 5
ContainerLogMaxSize: 10Mi
ContentType: application/vnd.kubernetes.protobuf
EnableContentionProfiling: false
EnableControllerAttachDetach: true
EnableDebuggingHandlers: true
EnforceNodeAllocatable:
- pods
EventBurst: 10
EventRecordQPS: 5
EvictionHard:
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
EvictionMaxPodGracePeriod: 0
EvictionMinimumReclaim: null
EvictionPressureTransitionPeriod: 5m0s
EvictionSoft: null
EvictionSoftGracePeriod: null
FailSwapOn: true
FeatureGates: null
FileCheckFrequency: 20s
HTTPCheckFrequency: 20s
HairpinMode: promiscuous-bridge
HealthzBindAddress: 127.0.0.1
HealthzPort: 10248
IPTablesDropBit: 15
IPTablesMasqueradeBit: 14
ImageGCHighThresholdPercent: 85
ImageGCLowThresholdPercent: 80
ImageMinimumGCAge: 2m0s
KubeAPIBurst: 10
KubeAPIQPS: 5
KubeReserved: null
KubeReservedCgroup: ""
KubeletCgroups: ""
MakeIPTablesUtilChains: true
MaxOpenFiles: 1000000
MaxPods: 110
NodeLeaseDurationSeconds: 40
NodeStatusReportFrequency: 1m0s
NodeStatusUpdateFrequency: 10s
OOMScoreAdj: -999
PodCIDR: ""
PodPidsLimit: -1
PodsPerCore: 0
Port: 10250
ProtectKernelDefaults: false
QOSReserved: null
ReadOnlyPort: 0
RegistryBurst: 10
RegistryPullQPS: 5
ResolverConfig: /etc/resolv.conf
RotateCertificates: true
RuntimeRequestTimeout: 2m0s
SerializeImagePulls: true
ServerTLSBootstrap: false
StaticPodPath: /etc/kubernetes/manifests
StaticPodURL: ""
StaticPodURLHeader: null
StreamingConnectionIdleTimeout: 4h0m0s
SyncFrequency: 1m0s
SystemCgroups: ""
SystemReserved: null
SystemReservedCgroup: ""
TLSCertFile: ""
TLSCipherSuites: null
TLSMinVersion: ""
TLSPrivateKeyFile: ""
VolumeStatsAggPeriod: 1m0s
ControlPlaneEndpoint: ""
ControllerManager:
ExtraArgs: null
ExtraVolumes: null
DNS:
ImageRepository: ""
ImageTag: ""
Type: CoreDNS
Etcd:
External: null
Local:
DataDir: /var/lib/etcd
ExtraArgs: null
ImageRepository: ""
ImageTag: ""
PeerCertSANs: null
ServerCertSANs: null
FeatureGates: null
ImageRepository: k8s.gcr.io
KubernetesVersion: v1.12.2
LocalAPIEndpoint:
AdvertiseAddress: 192.168.2.2
BindPort: 6443
Networking:
DNSDomain: cluster.local
PodSubnet: ""
ServiceSubnet: 10.96.0.0/12
NodeRegistration:
CRISocket: /var/run/dockershim.sock
KubeletExtraArgs: null
Name: control-plane-1
Taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
Scheduler:
ExtraArgs: null
ExtraVolumes: null
UseHyperKubeImage: true

View File

@ -1,206 +0,0 @@
APIServer:
CertSANs: null
ExtraArgs:
authorization-mode: Node,RBAC,Webhook
ExtraVolumes:
- HostPath: /host/read-only
MountPath: /mount/read-only
Name: ReadOnlyVolume
PathType: ""
ReadOnly: true
- HostPath: /host/writable
MountPath: /mount/writable
Name: WritableVolume
PathType: ""
ReadOnly: false
TimeoutForControlPlane: 4m0s
BootstrapTokens:
- Description: ""
Expires: null
Groups:
- system:bootstrappers:kubeadm:default-node-token
TTL: 24h0m0s
Token: s73ybu.6tw6wnqgp5z0wb77
Usages:
- signing
- authentication
CIImageRepository: ""
CertificatesDir: /etc/kubernetes/pki
ClusterName: kubernetes
ComponentConfigs:
KubeProxy:
BindAddress: 0.0.0.0
BindAddressHardFail: false
ClientConnection:
AcceptContentTypes: ""
Burst: 10
ContentType: application/vnd.kubernetes.protobuf
Kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
QPS: 5
ClusterCIDR: ""
ConfigSyncPeriod: 15m0s
Conntrack:
MaxPerCore: 32768
Min: 131072
TCPCloseWaitTimeout: 1h0m0s
TCPEstablishedTimeout: 24h0m0s
EnableProfiling: false
FeatureGates:
ServiceNodeExclusion: true
SupportIPVSProxyMode: true
HealthzBindAddress: 0.0.0.0:10256
HostnameOverride: ""
IPTables:
MasqueradeAll: false
MasqueradeBit: 14
MinSyncPeriod: 0s
SyncPeriod: 30s
IPVS:
ExcludeCIDRs: null
MinSyncPeriod: 0s
Scheduler: ""
SyncPeriod: 30s
MetricsBindAddress: 127.0.0.1:10249
Mode: iptables
NodePortAddresses: null
OOMScoreAdj: -999
PortRange: ""
UDPIdleTimeout: 250ms
Winkernel:
EnableDSR: false
NetworkName: ""
SourceVip: ""
Kubelet:
Address: 1.2.3.4
Authentication:
Anonymous:
Enabled: false
Webhook:
CacheTTL: 2m0s
Enabled: true
X509:
ClientCAFile: /etc/kubernetes/pki/ca.crt
Authorization:
Mode: Webhook
Webhook:
CacheAuthorizedTTL: 5m0s
CacheUnauthorizedTTL: 30s
CPUCFSQuota: true
CPUCFSQuotaPeriod: 0s
CPUManagerPolicy: none
CPUManagerReconcilePeriod: 10s
CgroupDriver: cgroupfs
CgroupRoot: ""
CgroupsPerQOS: true
ClusterDNS:
- 10.96.0.10
ClusterDomain: cluster.local
ConfigMapAndSecretChangeDetectionStrategy: Watch
ContainerLogMaxFiles: 5
ContainerLogMaxSize: 10Mi
ContentType: application/vnd.kubernetes.protobuf
EnableContentionProfiling: false
EnableControllerAttachDetach: true
EnableDebuggingHandlers: true
EnforceNodeAllocatable:
- pods
EventBurst: 10
EventRecordQPS: 5
EvictionHard:
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
EvictionMaxPodGracePeriod: 0
EvictionMinimumReclaim: null
EvictionPressureTransitionPeriod: 5m0s
EvictionSoft: null
EvictionSoftGracePeriod: null
FailSwapOn: true
FeatureGates: null
FileCheckFrequency: 20s
HTTPCheckFrequency: 20s
HairpinMode: promiscuous-bridge
HealthzBindAddress: 127.0.0.1
HealthzPort: 10248
IPTablesDropBit: 15
IPTablesMasqueradeBit: 14
ImageGCHighThresholdPercent: 85
ImageGCLowThresholdPercent: 80
ImageMinimumGCAge: 2m0s
KubeAPIBurst: 10
KubeAPIQPS: 5
KubeReserved: null
KubeReservedCgroup: ""
KubeletCgroups: ""
MakeIPTablesUtilChains: true
MaxOpenFiles: 1000000
MaxPods: 110
NodeLeaseDurationSeconds: 40
NodeStatusReportFrequency: 1m0s
NodeStatusUpdateFrequency: 10s
OOMScoreAdj: -999
PodCIDR: ""
PodPidsLimit: -1
PodsPerCore: 0
Port: 10250
ProtectKernelDefaults: false
QOSReserved: null
ReadOnlyPort: 0
RegistryBurst: 10
RegistryPullQPS: 5
ResolverConfig: /etc/resolv.conf
RotateCertificates: true
RuntimeRequestTimeout: 2m0s
SerializeImagePulls: true
ServerTLSBootstrap: false
StaticPodPath: /etc/kubernetes/manifests
StaticPodURL: ""
StaticPodURLHeader: null
StreamingConnectionIdleTimeout: 4h0m0s
SyncFrequency: 1m0s
SystemCgroups: ""
SystemReserved: null
SystemReservedCgroup: ""
TLSCertFile: ""
TLSCipherSuites: null
TLSMinVersion: ""
TLSPrivateKeyFile: ""
VolumeStatsAggPeriod: 1m0s
ControlPlaneEndpoint: ""
ControllerManager:
ExtraArgs: null
ExtraVolumes: null
DNS:
ImageRepository: ""
ImageTag: ""
Type: CoreDNS
Etcd:
External: null
Local:
DataDir: /var/lib/etcd
ExtraArgs: null
ImageRepository: ""
ImageTag: ""
PeerCertSANs: null
ServerCertSANs: null
FeatureGates: null
ImageRepository: k8s.gcr.io
KubernetesVersion: v1.12.2
LocalAPIEndpoint:
AdvertiseAddress: 192.168.2.2
BindPort: 6443
Networking:
DNSDomain: cluster.local
PodSubnet: ""
ServiceSubnet: 10.96.0.0/12
NodeRegistration:
CRISocket: /var/run/dockershim.sock
KubeletExtraArgs: null
Name: control-plane-1
Taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
Scheduler:
ExtraArgs: null
ExtraVolumes: null
UseHyperKubeImage: true

View File

@ -1,170 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta1
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: s73ybu.6tw6wnqgp5z0wb77
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.2.2
bindPort: 6443
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
---
apiServer:
extraArgs:
authorization-mode: Node,RBAC,Webhook
extraVolumes:
- hostPath: /host/read-only
mountPath: /mount/read-only
name: ReadOnlyVolume
readOnly: true
- hostPath: /host/writable
mountPath: /mount/writable
name: WritableVolume
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta1
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: ""
controllerManager: {}
dns:
type: CoreDNS
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: k8s.gcr.io
kind: ClusterConfiguration
kubernetesVersion: v1.12.2
networking:
dnsDomain: cluster.local
podSubnet: ""
serviceSubnet: 10.96.0.0/12
scheduler: {}
useHyperKubeImage: true
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
bindAddressHardFail: false
clientConnection:
acceptContentTypes: ""
burst: 10
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
qps: 5
clusterCIDR: ""
configSyncPeriod: 15m0s
conntrack:
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
featureGates:
ServiceNodeExclusion: true
SupportIPVSProxyMode: true
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: iptables
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
udpIdleTimeout: 250ms
winkernel:
enableDSR: false
networkName: ""
sourceVip: ""
---
address: 1.2.3.4
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 2m0s
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
cgroupDriver: cgroupfs
cgroupsPerQOS: true
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
configMapAndSecretChangeDetectionStrategy: Watch
containerLogMaxFiles: 5
containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf
cpuCFSQuota: true
cpuCFSQuotaPeriod: 0s
cpuManagerPolicy: none
cpuManagerReconcilePeriod: 10s
enableControllerAttachDetach: true
enableDebuggingHandlers: true
enforceNodeAllocatable:
- pods
eventBurst: 10
eventRecordQPS: 5
evictionHard:
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
evictionPressureTransitionPeriod: 5m0s
failSwapOn: true
fileCheckFrequency: 20s
hairpinMode: promiscuous-bridge
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 20s
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
imageMinimumGCAge: 2m0s
iptablesDropBit: 15
iptablesMasqueradeBit: 14
kind: KubeletConfiguration
kubeAPIBurst: 10
kubeAPIQPS: 5
makeIPTablesUtilChains: true
maxOpenFiles: 1000000
maxPods: 110
nodeLeaseDurationSeconds: 40
nodeStatusReportFrequency: 1m0s
nodeStatusUpdateFrequency: 10s
oomScoreAdj: -999
podPidsLimit: -1
port: 10250
registryBurst: 10
registryPullQPS: 5
resolvConf: /etc/resolv.conf
rotateCertificates: true
runtimeRequestTimeout: 2m0s
serializeImagePulls: true
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
volumeStatsAggPeriod: 1m0s
allowedUnsafeSysctls: []

View File

@ -1,168 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta1
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: s73ybu.6tw6wnqgp5z0wb77
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.2.2
bindPort: 6443
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
---
apiServer:
extraArgs:
authorization-mode: Node,RBAC,Webhook
extraVolumes:
- hostPath: /host/read-only
mountPath: /mount/read-only
name: ReadOnlyVolume
readOnly: true
- hostPath: /host/writable
mountPath: /mount/writable
name: WritableVolume
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta1
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: ""
controllerManager: {}
dns:
type: CoreDNS
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: k8s.gcr.io
kind: ClusterConfiguration
kubernetesVersion: v1.12.2
networking:
dnsDomain: cluster.local
podSubnet: ""
serviceSubnet: 10.96.0.0/12
scheduler: {}
useHyperKubeImage: true
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
bindAddressHardFail: false
clientConnection:
acceptContentTypes: ""
burst: 10
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
qps: 5
clusterCIDR: ""
configSyncPeriod: 15m0s
conntrack:
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
featureGates:
ServiceNodeExclusion: true
SupportIPVSProxyMode: true
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: iptables
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
udpIdleTimeout: 250ms
winkernel:
enableDSR: false
networkName: ""
sourceVip: ""
---
address: 1.2.3.4
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 2m0s
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
cgroupDriver: cgroupfs
cgroupsPerQOS: true
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
configMapAndSecretChangeDetectionStrategy: Watch
containerLogMaxFiles: 5
containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf
cpuCFSQuota: true
cpuCFSQuotaPeriod: 0s
cpuManagerPolicy: none
cpuManagerReconcilePeriod: 10s
enableControllerAttachDetach: true
enableDebuggingHandlers: true
enforceNodeAllocatable:
- pods
eventBurst: 10
eventRecordQPS: 5
evictionHard:
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
evictionPressureTransitionPeriod: 5m0s
failSwapOn: true
fileCheckFrequency: 20s
hairpinMode: promiscuous-bridge
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 20s
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
imageMinimumGCAge: 2m0s
iptablesDropBit: 15
iptablesMasqueradeBit: 14
kind: KubeletConfiguration
kubeAPIBurst: 10
kubeAPIQPS: 5
makeIPTablesUtilChains: true
maxOpenFiles: 1000000
maxPods: 110
nodeLeaseDurationSeconds: 40
nodeStatusReportFrequency: 1m0s
nodeStatusUpdateFrequency: 10s
oomScoreAdj: -999
podPidsLimit: -1
port: 10250
registryBurst: 10
registryPullQPS: 5
resolvConf: /etc/resolv.conf
rotateCertificates: true
runtimeRequestTimeout: 2m0s
serializeImagePulls: true
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
volumeStatsAggPeriod: 1m0s

View File

@ -1,23 +0,0 @@
CACertPath: /etc/kubernetes/pki/ca.crt
ControlPlane:
CertificateKey: ""
LocalAPIEndpoint:
AdvertiseAddress: 192.168.2.2
BindPort: 6443
Discovery:
BootstrapToken:
APIServerEndpoint: kube-apiserver:6443
CACertHashes: null
Token: abcdef.0123456789abcdef
UnsafeSkipCAVerification: true
File: null
TLSBootstrapToken: abcdef.0123456789abcdef
Timeout: 5m0s
NodeRegistration:
CRISocket: /var/run/dockershim.sock
IgnorePreflightErrors: null
KubeletExtraArgs: null
Name: control-plane-1
Taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -1,20 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta1
caCertPath: /etc/kubernetes/pki/ca.crt
controlPlane:
localAPIEndpoint:
advertiseAddress: 192.168.2.2
bindPort: 6443
discovery:
bootstrapToken:
apiServerEndpoint: kube-apiserver:6443
token: abcdef.0123456789abcdef
unsafeSkipCAVerification: true
timeout: 5m0s
tlsBootstrapToken: abcdef.0123456789abcdef
kind: JoinConfiguration
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -1,20 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta2
caCertPath: /etc/kubernetes/pki/ca.crt
controlPlane:
localAPIEndpoint:
advertiseAddress: 192.168.2.2
bindPort: 6443
discovery:
bootstrapToken:
apiServerEndpoint: kube-apiserver:6443
token: abcdef.0123456789abcdef
unsafeSkipCAVerification: true
timeout: 5m0s
tlsBootstrapToken: abcdef.0123456789abcdef
kind: JoinConfiguration
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master

View File

@ -1,155 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta1
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: s73ybu.6tw6wnqgp5z0wb77
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.2.2
bindPort: 6443
nodeRegistration:
criSocket: /var/run/criruntime.sock
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
---
apiServer:
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta1
certificatesDir: /var/lib/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: ""
controllerManager: {}
dns:
type: CoreDNS
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: my-company.com
kind: ClusterConfiguration
kubernetesVersion: v1.13.0
networking:
dnsDomain: cluster.global
podSubnet: 10.148.0.0/16
serviceSubnet: 10.196.0.0/12
scheduler: {}
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
bindAddressHardFail: false
clientConnection:
acceptContentTypes: ""
burst: 10
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
qps: 5
clusterCIDR: 10.148.0.0/16
configSyncPeriod: 15m0s
conntrack:
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: ""
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
udpIdleTimeout: 250ms
winkernel:
enableDSR: false
networkName: ""
sourceVip: ""
---
address: 0.0.0.0
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 2m0s
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
cgroupDriver: cgroupfs
cgroupsPerQOS: true
clusterDNS:
- 10.192.0.10
clusterDomain: cluster.global
configMapAndSecretChangeDetectionStrategy: Watch
containerLogMaxFiles: 5
containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf
cpuCFSQuota: true
cpuCFSQuotaPeriod: 100ms
cpuManagerPolicy: none
cpuManagerReconcilePeriod: 10s
enableControllerAttachDetach: true
enableDebuggingHandlers: true
enforceNodeAllocatable:
- pods
eventBurst: 10
eventRecordQPS: 5
evictionHard:
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
nodefs.inodesFree: 5%
evictionPressureTransitionPeriod: 5m0s
failSwapOn: true
fileCheckFrequency: 20s
hairpinMode: promiscuous-bridge
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 20s
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
imageMinimumGCAge: 2m0s
iptablesDropBit: 15
iptablesMasqueradeBit: 14
kind: KubeletConfiguration
kubeAPIBurst: 10
kubeAPIQPS: 5
makeIPTablesUtilChains: true
maxOpenFiles: 1000000
maxPods: 110
nodeLeaseDurationSeconds: 40
nodeStatusReportFrequency: 1m0s
nodeStatusUpdateFrequency: 10s
oomScoreAdj: -999
podPidsLimit: -1
port: 10250
registryBurst: 10
registryPullQPS: 5
resolvConf: /etc/resolv.conf
rotateCertificates: true
runtimeRequestTimeout: 2m0s
serializeImagePulls: true
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
volumeStatsAggPeriod: 1m0s

View File

@ -1,154 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta1
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: s73ybu.6tw6wnqgp5z0wb77
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.168.2.2
bindPort: 6443
nodeRegistration:
criSocket: /var/run/criruntime.sock
name: control-plane-1
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
---
apiServer:
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta1
certificatesDir: /var/lib/kubernetes/pki
clusterName: kubernetes
controlPlaneEndpoint: ""
controllerManager: {}
dns:
type: CoreDNS
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: my-company.com
kind: ClusterConfiguration
kubernetesVersion: v1.13.0
networking:
dnsDomain: cluster.global
podSubnet: 10.148.0.0/16
serviceSubnet: 10.196.0.0/12
scheduler: {}
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
bindAddressHardFail: false
clientConnection:
acceptContentTypes: ""
burst: 10
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
qps: 5
clusterCIDR: 10.148.0.0/16
configSyncPeriod: 15m0s
conntrack:
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: ""
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
udpIdleTimeout: 250ms
winkernel:
enableDSR: false
networkName: ""
sourceVip: ""
---
address: 0.0.0.0
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
anonymous:
enabled: false
webhook:
cacheTTL: 2m0s
enabled: true
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
mode: Webhook
webhook:
cacheAuthorizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
cgroupDriver: cgroupfs
cgroupsPerQOS: true
clusterDNS:
- 10.192.0.10
clusterDomain: cluster.global
configMapAndSecretChangeDetectionStrategy: Watch
containerLogMaxFiles: 5
containerLogMaxSize: 10Mi
contentType: application/vnd.kubernetes.protobuf
cpuCFSQuota: true
cpuCFSQuotaPeriod: 100ms
cpuManagerPolicy: none
cpuManagerReconcilePeriod: 10s
enableControllerAttachDetach: true
enableDebuggingHandlers: true
enforceNodeAllocatable:
- pods
eventBurst: 10
eventRecordQPS: 5
evictionHard:
imagefs.available: 15%
memory.available: 100Mi
nodefs.available: 10%
evictionPressureTransitionPeriod: 5m0s
failSwapOn: true
fileCheckFrequency: 20s
hairpinMode: promiscuous-bridge
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 20s
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
imageMinimumGCAge: 2m0s
iptablesDropBit: 15
iptablesMasqueradeBit: 14
kind: KubeletConfiguration
kubeAPIBurst: 10
kubeAPIQPS: 5
makeIPTablesUtilChains: true
maxOpenFiles: 1000000
maxPods: 110
nodeLeaseDurationSeconds: 40
nodeStatusReportFrequency: 1m0s
nodeStatusUpdateFrequency: 10s
oomScoreAdj: -999
podPidsLimit: -1
port: 10250
registryBurst: 10
registryPullQPS: 5
resolvConf: /etc/resolv.conf
rotateCertificates: true
runtimeRequestTimeout: 2m0s
serializeImagePulls: true
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 4h0m0s
syncFrequency: 1m0s
volumeStatsAggPeriod: 1m0s

View File

@ -1,19 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta1
kind: InitConfiguration
bootstrapTokens:
- token: s73ybu.6tw6wnqgp5z0wb77
localAPIEndpoint:
advertiseAddress: 192.168.2.2
nodeRegistration:
criSocket: /var/run/criruntime.sock
name: control-plane-1
---
apiVersion: kubeadm.k8s.io/v1beta1
certificatesDir: /var/lib/kubernetes/pki
imageRepository: my-company.com
kind: ClusterConfiguration
kubernetesVersion: v1.13.0
networking:
dnsDomain: cluster.global
podSubnet: 10.148.0.0/16
serviceSubnet: 10.196.0.0/12

View File

@ -1,14 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta2
caCertPath: /etc/kubernetes/pki/ca.crt
discovery:
bootstrapToken:
apiServerEndpoint: kube-apiserver:6443
token: abcdef.0123456789abcdef
unsafeSkipCAVerification: true
timeout: 5m0s
tlsBootstrapToken: abcdef.0123456789abcdef
kind: JoinConfiguration
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: thegopher
taints: null

View File

@ -1,11 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta2
discovery:
bootstrapToken:
apiServerEndpoint: kube-apiserver:6443
token: abcdef.0123456789abcdef
unsafeSkipCAVerification: true
tlsBootstrapToken: abcdef.0123456789abcdef
kind: JoinConfiguration
nodeRegistration:
criSocket: /var/run/dockershim.sock
name: thegopher

View File

@ -1,7 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
networking:
dnsDomain: INVALID-DOMAIN-!!!!
podSubnet: ""
serviceSubnet: 10.96.0.0/12
useHyperKubeImage: false

View File

@ -1,15 +0,0 @@
apiVersion: kubeadm.k8s.io/v1beta2
kind: NodeConfiguration
caCertPath: relativepath
discovery:
timeout: not-a-time
bootstrapToken:
token: invalidtoken
apiServerEndpoints:
- INVALID_URL
unsafeSkipCAVerification: false
file:
kubeConfigPath: relativepath
nodeRegistration:
criSocket: relativepath
name: NODE-1