diff --git a/cmd/kubeadm/app/componentconfigs/BUILD b/cmd/kubeadm/app/componentconfigs/BUILD index 2a0b648e8aa..2f456d8b9a0 100644 --- a/cmd/kubeadm/app/componentconfigs/BUILD +++ b/cmd/kubeadm/app/componentconfigs/BUILD @@ -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", diff --git a/cmd/kubeadm/app/componentconfigs/configset_test.go b/cmd/kubeadm/app/componentconfigs/configset_test.go index 13652d3ee87..7189e9f7263 100644 --- a/cmd/kubeadm/app/componentconfigs/configset_test.go +++ b/cmd/kubeadm/app/componentconfigs/configset_test.go @@ -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) - } - }) - } -} diff --git a/cmd/kubeadm/app/componentconfigs/fakeconfig_test.go b/cmd/kubeadm/app/componentconfigs/fakeconfig_test.go new file mode 100644 index 00000000000..4db2625f3ec --- /dev/null +++ b/cmd/kubeadm/app/componentconfigs/fakeconfig_test.go @@ -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]) + } + }) + } + }) +} diff --git a/cmd/kubeadm/app/componentconfigs/kubelet_test.go b/cmd/kubeadm/app/componentconfigs/kubelet_test.go index b23106c4021..bf5d7ce8e6c 100644 --- a/cmd/kubeadm/app/componentconfigs/kubelet_test.go +++ b/cmd/kubeadm/app/componentconfigs/kubelet_test.go @@ -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) - } - }) - } -} diff --git a/cmd/kubeadm/app/componentconfigs/kubeproxy_test.go b/cmd/kubeadm/app/componentconfigs/kubeproxy_test.go index 3cd17d9d50c..d68c4c89e16 100644 --- a/cmd/kubeadm/app/componentconfigs/kubeproxy_test.go +++ b/cmd/kubeadm/app/componentconfigs/kubeproxy_test.go @@ -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) - } - }) - } -}