Merge pull request #105741 from neolit123/1.23-kubeadm-kubelet-config-map-change

kubeadm: introduce the UnversionedKubeletConfigMap feature gate
This commit is contained in:
Kubernetes Prow Robot 2021-11-09 11:27:48 -08:00 committed by GitHub
commit 5848dbaa80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 168 additions and 68 deletions

View File

@ -26,24 +26,34 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
) )
func testClusterCfg() *kubeadmapi.ClusterConfiguration { // TODO: cleanup after UnversionedKubeletConfigMap goes GA:
// https://github.com/kubernetes/kubeadm/issues/1582
func testClusterCfg(legacyKubeletConfigMap bool) *kubeadmapi.ClusterConfiguration {
if legacyKubeletConfigMap {
return &kubeadmapi.ClusterConfiguration{
KubernetesVersion: constants.CurrentKubernetesVersion.String(),
}
}
return &kubeadmapi.ClusterConfiguration{ return &kubeadmapi.ClusterConfiguration{
KubernetesVersion: constants.CurrentKubernetesVersion.String(), KubernetesVersion: constants.CurrentKubernetesVersion.String(),
FeatureGates: map[string]bool{features.UnversionedKubeletConfigMap: true},
} }
} }
func TestDefault(t *testing.T) { func TestDefault(t *testing.T) {
clusterCfg := testClusterCfg() legacyKubeletConfigMap := false
clusterCfg := testClusterCfg(legacyKubeletConfigMap)
localAPIEndpoint := &kubeadmapi.APIEndpoint{} localAPIEndpoint := &kubeadmapi.APIEndpoint{}
nodeRegOps := &kubeadmapi.NodeRegistrationOptions{} nodeRegOps := &kubeadmapi.NodeRegistrationOptions{}
Default(clusterCfg, localAPIEndpoint, nodeRegOps) Default(clusterCfg, localAPIEndpoint, nodeRegOps)
if len(clusterCfg.ComponentConfigs) != len(known) { if len(clusterCfg.ComponentConfigs) != len(known) {
t.Errorf("missmatch between supported and defaulted type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(known)) t.Errorf("mismatch between supported and defaulted type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(known))
} }
} }
@ -56,17 +66,42 @@ func TestFromCluster(t *testing.T) {
testKubeletConfigMap(` testKubeletConfigMap(`
apiVersion: kubelet.config.k8s.io/v1beta1 apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration kind: KubeletConfiguration
`), `, false),
} }
client := clientsetfake.NewSimpleClientset(objects...) client := clientsetfake.NewSimpleClientset(objects...)
clusterCfg := testClusterCfg() legacyKubeletConfigMap := false
clusterCfg := testClusterCfg(legacyKubeletConfigMap)
if err := FetchFromCluster(clusterCfg, client); err != nil { if err := FetchFromCluster(clusterCfg, client); err != nil {
t.Fatalf("FetchFromCluster failed: %v", err) t.Fatalf("FetchFromCluster failed: %v", err)
} }
if len(clusterCfg.ComponentConfigs) != len(objects) { if len(clusterCfg.ComponentConfigs) != len(objects) {
t.Fatalf("missmatch between supplied and loaded type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(objects)) t.Fatalf("mismatch between supplied and loaded type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(objects))
}
// TODO: cleanup the legacy case below after UnversionedKubeletConfigMap goes GA:
// https://github.com/kubernetes/kubeadm/issues/1582
objectsLegacyKubelet := []runtime.Object{
testKubeProxyConfigMap(`
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
`),
testKubeletConfigMap(`
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
`, true),
}
clientLegacyKubelet := clientsetfake.NewSimpleClientset(objectsLegacyKubelet...)
legacyKubeletConfigMap = true
clusterCfgLegacyKubelet := testClusterCfg(legacyKubeletConfigMap)
if err := FetchFromCluster(clusterCfgLegacyKubelet, clientLegacyKubelet); err != nil {
t.Fatalf("FetchFromCluster failed: %v", err)
}
if len(clusterCfgLegacyKubelet.ComponentConfigs) != len(objectsLegacyKubelet) {
t.Fatalf("mismatch between supplied and loaded type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(objects))
} }
} }
@ -83,12 +118,13 @@ func TestFetchFromDocumentMap(t *testing.T) {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err) t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
} }
clusterCfg := testClusterCfg() legacyKubeletConfigMap := false
clusterCfg := testClusterCfg(legacyKubeletConfigMap)
if err = FetchFromDocumentMap(clusterCfg, gvkmap); err != nil { if err = FetchFromDocumentMap(clusterCfg, gvkmap); err != nil {
t.Fatalf("FetchFromDocumentMap failed: %v", err) t.Fatalf("FetchFromDocumentMap failed: %v", err)
} }
if len(clusterCfg.ComponentConfigs) != len(gvkmap) { if len(clusterCfg.ComponentConfigs) != len(gvkmap) {
t.Fatalf("missmatch between supplied and loaded type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(gvkmap)) t.Fatalf("mismatch between supplied and loaded type numbers:\n\tgot: %d\n\texpected: %d", len(clusterCfg.ComponentConfigs), len(gvkmap))
} }
} }

View File

@ -357,7 +357,8 @@ func TestGeneratedConfigFromCluster(t *testing.T) {
} }
client := clientsetfake.NewSimpleClientset(configMap) client := clientsetfake.NewSimpleClientset(configMap)
cfg, err := clusterConfigHandler.FromCluster(client, testClusterCfg()) legacyKubeletConfigMap := true
cfg, err := clusterConfigHandler.FromCluster(client, testClusterCfg(legacyKubeletConfigMap))
if err != nil { if err != nil {
t.Fatalf("unexpected failure of FromCluster: %v", err) t.Fatalf("unexpected failure of FromCluster: %v", err)
} }
@ -453,7 +454,7 @@ func runClusterConfigFromTest(t *testing.T, perform func(t *testing.T, in string
t.Errorf("unexpected result: %v", got) t.Errorf("unexpected result: %v", got)
} else { } else {
if !reflect.DeepEqual(test.out, got) { if !reflect.DeepEqual(test.out, got) {
t.Errorf("missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.out, got) t.Errorf("mismatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", test.out, got)
} }
} }
} }
@ -482,7 +483,8 @@ func TestLoadingFromCluster(t *testing.T) {
testClusterConfigMap(in, false), testClusterConfigMap(in, false),
) )
return clusterConfigHandler.FromCluster(client, testClusterCfg()) legacyKubeletConfigMap := true
return clusterConfigHandler.FromCluster(client, testClusterCfg(legacyKubeletConfigMap))
}) })
} }
@ -575,7 +577,8 @@ func TestFetchFromClusterWithLocalOverwrites(t *testing.T) {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err) t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
} }
clusterCfg := testClusterCfg() legacyKubeletConfigMap := true
clusterCfg := testClusterCfg(legacyKubeletConfigMap)
err = FetchFromClusterWithLocalOverwrites(clusterCfg, client, docmap) err = FetchFromClusterWithLocalOverwrites(clusterCfg, client, docmap)
if err != nil { if err != nil {
@ -709,7 +712,8 @@ func TestGetVersionStates(t *testing.T) {
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err) t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
} }
clusterCfg := testClusterCfg() legacyKubeletConfigMap := true
clusterCfg := testClusterCfg(legacyKubeletConfigMap)
got, err := GetVersionStates(clusterCfg, client, docmap) got, err := GetVersionStates(clusterCfg, client, docmap)
if err != nil { if err != nil {

View File

@ -19,6 +19,7 @@ package componentconfigs
import ( import (
"path/filepath" "path/filepath"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/version"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2" "k8s.io/klog/v2"
@ -76,8 +77,25 @@ func kubeletConfigFromCluster(h *handler, clientset clientset.Interface, cluster
return nil, err return nil, err
} }
configMapName := constants.GetKubeletConfigMapName(k8sVersion) // TODO: https://github.com/kubernetes/kubeadm/issues/1582
return h.fromConfigMap(clientset, configMapName, constants.KubeletBaseConfigurationConfigMapKey, true) // During the first "kubeadm upgrade apply" when the feature gate goes "true" by default and
// a preferred user value is missing in the ClusterConfiguration, "kubeadm upgrade apply" will try
// to fetch using the new format and that CM will not exist yet.
// Tollerate both the old a new format until UnversionedKubeletConfigMap goes GA and is locked.
// This makes it easier for the users and the code base (avoids changes in /cmd/upgrade/common.go#enforceRequirements).
configMapNameLegacy := constants.GetKubeletConfigMapName(k8sVersion, true)
configMapName := constants.GetKubeletConfigMapName(k8sVersion, false)
klog.V(1).Infof("attempting to download the KubeletConfiguration from the new format location (UnversionedKubeletConfigMap=true)")
cm, err := h.fromConfigMap(clientset, configMapName, constants.KubeletBaseConfigurationConfigMapKey, true)
if err != nil {
klog.V(1).Infof("attempting to download the KubeletConfiguration from the DEPRECATED location (UnversionedKubeletConfigMap=false)")
cm, err = h.fromConfigMap(clientset, configMapNameLegacy, constants.KubeletBaseConfigurationConfigMapKey, true)
if err != nil {
return nil, errors.Wrapf(err, "could not download the kubelet configuration from ConfigMap %q or %q",
configMapName, configMapNameLegacy)
}
}
return cm, nil
} }
// kubeletConfig implements the kubeadmapi.ComponentConfig interface for kubelet // kubeletConfig implements the kubeadmapi.ComponentConfig interface for kubelet

View File

@ -36,10 +36,12 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/constants"
) )
func testKubeletConfigMap(contents string) *v1.ConfigMap { // TODO: cleanup after UnversionedKubeletConfigMap goes GA:
// https://github.com/kubernetes/kubeadm/issues/1582
func testKubeletConfigMap(contents string, legacyKubeletConfigMap bool) *v1.ConfigMap {
return &v1.ConfigMap{ return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion), Name: constants.GetKubeletConfigMapName(constants.CurrentKubernetesVersion, legacyKubeletConfigMap),
Namespace: metav1.NamespaceSystem, Namespace: metav1.NamespaceSystem,
}, },
Data: map[string]string{ Data: map[string]string{
@ -283,8 +285,16 @@ func TestKubeletFromDocumentMap(t *testing.T) {
func TestKubeletFromCluster(t *testing.T) { func TestKubeletFromCluster(t *testing.T) {
runKubeletFromTest(t, func(_ schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error) { runKubeletFromTest(t, func(_ schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error) {
client := clientsetfake.NewSimpleClientset( client := clientsetfake.NewSimpleClientset(
testKubeletConfigMap(yaml), testKubeletConfigMap(yaml, true),
) )
return kubeletHandler.FromCluster(client, testClusterCfg()) legacyKubeletConfigMap := true
return kubeletHandler.FromCluster(client, testClusterCfg(legacyKubeletConfigMap))
})
runKubeletFromTest(t, func(_ schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error) {
client := clientsetfake.NewSimpleClientset(
testKubeletConfigMap(yaml, false),
)
legacyKubeletConfigMap := false
return kubeletHandler.FromCluster(client, testClusterCfg(legacyKubeletConfigMap))
}) })
} }

View File

@ -163,6 +163,7 @@ func TestKubeProxyFromCluster(t *testing.T) {
testKubeProxyConfigMap(yaml), testKubeProxyConfigMap(yaml),
) )
return kubeProxyHandler.FromCluster(client, testClusterCfg()) legacyKubeletConfigMap := true
return kubeProxyHandler.FromCluster(client, testClusterCfg(legacyKubeletConfigMap))
}) })
} }

View File

@ -260,14 +260,24 @@ const (
KubeProxyConfigMapKey = "config.conf" KubeProxyConfigMapKey = "config.conf"
// KubeletBaseConfigurationConfigMapPrefix specifies in what ConfigMap in the kube-system namespace the initial remote configuration of kubelet should be stored // KubeletBaseConfigurationConfigMapPrefix specifies in what ConfigMap in the kube-system namespace the initial remote configuration of kubelet should be stored
// TODO: Remove once UnversionedKubeletConfigMap graduates to GA:
// https://github.com/kubernetes/kubeadm/issues/1582
KubeletBaseConfigurationConfigMapPrefix = "kubelet-config-" KubeletBaseConfigurationConfigMapPrefix = "kubelet-config-"
// KubeletBaseConfigurationConfigMap specifies in what ConfigMap in the kube-system namespace the initial remote configuration of kubelet should be stored
KubeletBaseConfigurationConfigMap = "kubelet-config"
// KubeletBaseConfigurationConfigMapKey specifies in what ConfigMap key the initial remote configuration of kubelet should be stored // KubeletBaseConfigurationConfigMapKey specifies in what ConfigMap key the initial remote configuration of kubelet should be stored
KubeletBaseConfigurationConfigMapKey = "kubelet" KubeletBaseConfigurationConfigMapKey = "kubelet"
// KubeletBaseConfigMapRolePrefix defines the base kubelet configuration ConfigMap. // KubeletBaseConfigMapRolePrefix defines the base kubelet configuration ConfigMap.
// TODO: Remove once UnversionedKubeletConfigMap graduates to GA:
// https://github.com/kubernetes/kubeadm/issues/1582
KubeletBaseConfigMapRolePrefix = "kubeadm:kubelet-config-" KubeletBaseConfigMapRolePrefix = "kubeadm:kubelet-config-"
// KubeletBaseConfigMapRolePrefix defines the base kubelet configuration ConfigMap.
KubeletBaseConfigMapRole = "kubeadm:kubelet-config"
// KubeletRunDirectory specifies the directory where the kubelet runtime information is stored. // KubeletRunDirectory specifies the directory where the kubelet runtime information is stored.
KubeletRunDirectory = "/var/lib/kubelet" KubeletRunDirectory = "/var/lib/kubelet"
@ -672,6 +682,11 @@ func GetAPIServerVirtualIP(svcSubnetList string) (net.IP, error) {
} }
// GetKubeletConfigMapName returns the right ConfigMap name for the right branch of k8s // GetKubeletConfigMapName returns the right ConfigMap name for the right branch of k8s
func GetKubeletConfigMapName(k8sVersion *version.Version) string { // TODO: Remove the legacy arg once UnversionedKubeletConfigMap graduates to GA:
// https://github.com/kubernetes/kubeadm/issues/1582
func GetKubeletConfigMapName(k8sVersion *version.Version, legacy bool) string {
if !legacy {
return KubeletBaseConfigurationConfigMap
}
return fmt.Sprintf("%s%d.%d", KubeletBaseConfigurationConfigMapPrefix, k8sVersion.Major(), k8sVersion.Minor()) return fmt.Sprintf("%s%d.%d", KubeletBaseConfigurationConfigMapPrefix, k8sVersion.Major(), k8sVersion.Minor())
} }

View File

@ -35,13 +35,16 @@ const (
PublicKeysECDSA = "PublicKeysECDSA" PublicKeysECDSA = "PublicKeysECDSA"
// RootlessControlPlane is expected to be in alpha in v1.22 // RootlessControlPlane is expected to be in alpha in v1.22
RootlessControlPlane = "RootlessControlPlane" RootlessControlPlane = "RootlessControlPlane"
// UnversionedKubeletConfigMap is expected to be alpha in 1.23
UnversionedKubeletConfigMap = "UnversionedKubeletConfigMap"
) )
// InitFeatureGates are the default feature gates for the init command // InitFeatureGates are the default feature gates for the init command
var InitFeatureGates = FeatureList{ var InitFeatureGates = FeatureList{
IPv6DualStack: {FeatureSpec: featuregate.FeatureSpec{Default: true, LockToDefault: true, PreRelease: featuregate.GA}, HiddenInHelpText: true}, IPv6DualStack: {FeatureSpec: featuregate.FeatureSpec{Default: true, LockToDefault: true, PreRelease: featuregate.GA}, HiddenInHelpText: true},
PublicKeysECDSA: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}}, PublicKeysECDSA: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
RootlessControlPlane: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}}, RootlessControlPlane: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
UnversionedKubeletConfigMap: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
} }
// Feature represents a feature being gated // Feature represents a feature being gated

View File

@ -33,6 +33,7 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs" "k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
) )
@ -61,8 +62,17 @@ func CreateConfigMap(cfg *kubeadmapi.ClusterConfiguration, client clientset.Inte
return err return err
} }
configMapName := kubeadmconstants.GetKubeletConfigMapName(k8sVersion) // TODO: cleanup after UnversionedKubeletConfigMap goes GA:
// https://github.com/kubernetes/kubeadm/issues/1582
legacyKubeletCM := !features.Enabled(cfg.FeatureGates, features.UnversionedKubeletConfigMap)
configMapName := kubeadmconstants.GetKubeletConfigMapName(k8sVersion, legacyKubeletCM)
fmt.Printf("[kubelet] Creating a ConfigMap %q in namespace %s with the configuration for the kubelets in the cluster\n", configMapName, metav1.NamespaceSystem) fmt.Printf("[kubelet] Creating a ConfigMap %q in namespace %s with the configuration for the kubelets in the cluster\n", configMapName, metav1.NamespaceSystem)
if legacyKubeletCM {
fmt.Printf("NOTE: The %q naming of the kubelet ConfigMap is deprecated. "+
"Once the UnversionedKubeletConfigMap feature gate graduates to Beta the default name will become just %q. "+
"Kubeadm upgrade will handle this transition transparently.\n",
configMapName, kubeadmconstants.KubeletBaseConfigurationConfigMap)
}
kubeletCfg, ok := cfg.ComponentConfigs[componentconfigs.KubeletGroup] kubeletCfg, ok := cfg.ComponentConfigs[componentconfigs.KubeletGroup]
if !ok { if !ok {
@ -92,17 +102,19 @@ func CreateConfigMap(cfg *kubeadmapi.ClusterConfiguration, client clientset.Inte
return err return err
} }
if err := createConfigMapRBACRules(client, k8sVersion); err != nil { if err := createConfigMapRBACRules(client, k8sVersion, legacyKubeletCM); err != nil {
return errors.Wrap(err, "error creating kubelet configuration configmap RBAC rules") return errors.Wrap(err, "error creating kubelet configuration configmap RBAC rules")
} }
return nil return nil
} }
// createConfigMapRBACRules creates the RBAC rules for exposing the base kubelet ConfigMap in the kube-system namespace to unauthenticated users // createConfigMapRBACRules creates the RBAC rules for exposing the base kubelet ConfigMap in the kube-system namespace to unauthenticated users
func createConfigMapRBACRules(client clientset.Interface, k8sVersion *version.Version) error { // TODO: Remove the legacy arg once UnversionedKubeletConfigMap graduates to GA:
// https://github.com/kubernetes/kubeadm/issues/1582
func createConfigMapRBACRules(client clientset.Interface, k8sVersion *version.Version, legacy bool) error {
if err := apiclient.CreateOrUpdateRole(client, &rbac.Role{ if err := apiclient.CreateOrUpdateRole(client, &rbac.Role{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configMapRBACName(k8sVersion), Name: configMapRBACName(k8sVersion, legacy),
Namespace: metav1.NamespaceSystem, Namespace: metav1.NamespaceSystem,
}, },
Rules: []rbac.PolicyRule{ Rules: []rbac.PolicyRule{
@ -110,7 +122,7 @@ func createConfigMapRBACRules(client clientset.Interface, k8sVersion *version.Ve
Verbs: []string{"get"}, Verbs: []string{"get"},
APIGroups: []string{""}, APIGroups: []string{""},
Resources: []string{"configmaps"}, Resources: []string{"configmaps"},
ResourceNames: []string{kubeadmconstants.GetKubeletConfigMapName(k8sVersion)}, ResourceNames: []string{kubeadmconstants.GetKubeletConfigMapName(k8sVersion, legacy)},
}, },
}, },
}); err != nil { }); err != nil {
@ -119,13 +131,13 @@ func createConfigMapRBACRules(client clientset.Interface, k8sVersion *version.Ve
return apiclient.CreateOrUpdateRoleBinding(client, &rbac.RoleBinding{ return apiclient.CreateOrUpdateRoleBinding(client, &rbac.RoleBinding{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: configMapRBACName(k8sVersion), Name: configMapRBACName(k8sVersion, legacy),
Namespace: metav1.NamespaceSystem, Namespace: metav1.NamespaceSystem,
}, },
RoleRef: rbac.RoleRef{ RoleRef: rbac.RoleRef{
APIGroup: rbac.GroupName, APIGroup: rbac.GroupName,
Kind: "Role", Kind: "Role",
Name: configMapRBACName(k8sVersion), Name: configMapRBACName(k8sVersion, legacy),
}, },
Subjects: []rbac.Subject{ Subjects: []rbac.Subject{
{ {
@ -140,37 +152,13 @@ func createConfigMapRBACRules(client clientset.Interface, k8sVersion *version.Ve
}) })
} }
// DownloadConfig downloads the kubelet configuration from a ConfigMap and writes it to disk.
// DEPRECATED: Do not use in new code!
func DownloadConfig(client clientset.Interface, kubeletVersionStr string, kubeletDir string) error {
// Parse the desired kubelet version
kubeletVersion, err := version.ParseSemantic(kubeletVersionStr)
if err != nil {
return err
}
// Download the ConfigMap from the cluster based on what version the kubelet is
configMapName := kubeadmconstants.GetKubeletConfigMapName(kubeletVersion)
fmt.Printf("[kubelet-start] Downloading configuration for the kubelet from the %q ConfigMap in the %s namespace\n",
configMapName, metav1.NamespaceSystem)
kubeletCfgMap, err := apiclient.GetConfigMapWithRetry(client, metav1.NamespaceSystem, configMapName)
if err != nil {
return err
}
// Check for the key existence, otherwise we'll panic here
kubeletCfg, ok := kubeletCfgMap.Data[kubeadmconstants.KubeletBaseConfigurationConfigMapKey]
if !ok {
return errors.Errorf("no key %q found in config map %s", kubeadmconstants.KubeletBaseConfigurationConfigMapKey, configMapName)
}
return writeConfigBytesToDisk([]byte(kubeletCfg), kubeletDir)
}
// configMapRBACName returns the name for the Role/RoleBinding for the kubelet config configmap for the right branch of k8s // configMapRBACName returns the name for the Role/RoleBinding for the kubelet config configmap for the right branch of k8s
func configMapRBACName(k8sVersion *version.Version) string { // TODO: Remove the legacy arg once UnversionedKubeletConfigMap graduates to GA:
// https://github.com/kubernetes/kubeadm/issues/1582
func configMapRBACName(k8sVersion *version.Version, legacy bool) string {
if !legacy {
return kubeadmconstants.KubeletBaseConfigMapRole
}
return fmt.Sprintf("%s%d.%d", kubeadmconstants.KubeletBaseConfigMapRolePrefix, k8sVersion.Major(), k8sVersion.Minor()) return fmt.Sprintf("%s%d.%d", kubeadmconstants.KubeletBaseConfigMapRolePrefix, k8sVersion.Major(), k8sVersion.Minor())
} }

View File

@ -69,7 +69,7 @@ func TestCreateConfigMapRBACRules(t *testing.T) {
return true, nil, nil return true, nil, nil
}) })
if err := createConfigMapRBACRules(client, version.MustParseSemantic("v1.11.0")); err != nil { if err := createConfigMapRBACRules(client, version.MustParseSemantic("v1.11.0"), false); err != nil {
t.Errorf("createConfigMapRBACRules: unexpected error %v", err) t.Errorf("createConfigMapRBACRules: unexpected error %v", err)
} }
} }

View File

@ -545,7 +545,7 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
}, },
}, },
{ {
Name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap. Name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion, false), // Kubelet component config from corresponding ConfigMap.
Data: map[string]string{ Data: map[string]string{
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]), kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
}, },
@ -589,7 +589,7 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
}, },
}, },
{ {
Name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap. Name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion, false), // Kubelet component config from corresponding ConfigMap.
Data: map[string]string{ Data: map[string]string{
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]), kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
}, },
@ -622,7 +622,7 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
}, },
}, },
{ {
Name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap. Name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion, false), // Kubelet component config from corresponding ConfigMap.
Data: map[string]string{ Data: map[string]string{
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]), kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
}, },
@ -666,7 +666,7 @@ func TestGetInitConfigurationFromCluster(t *testing.T) {
}, },
}, },
{ {
Name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap. Name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion, false), // Kubelet component config from corresponding ConfigMap.
Data: map[string]string{ Data: map[string]string{
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]), kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
}, },

View File

@ -69,6 +69,9 @@ var _ = Describe("kubelet-config ConfigMap", func() {
m := getClusterConfiguration(f.ClientSet) m := getClusterConfiguration(f.ClientSet)
// Extract the kubernetesVersion // Extract the kubernetesVersion
// TODO: remove this after the UnversionedKubeletConfigMap feature gate goes GA:
// https://github.com/kubernetes/kubeadm/issues/1582
// At that point parsing the k8s version will no longer be needed in this test.
gomega.Expect(m).To(gomega.HaveKey("kubernetesVersion")) gomega.Expect(m).To(gomega.HaveKey("kubernetesVersion"))
k8sVersionString := m["kubernetesVersion"].(string) k8sVersionString := m["kubernetesVersion"].(string)
k8sVersion, err := version.ParseSemantic(k8sVersionString) k8sVersion, err := version.ParseSemantic(k8sVersionString)
@ -76,9 +79,31 @@ var _ = Describe("kubelet-config ConfigMap", func() {
framework.Failf("error reading kubernetesVersion from %s ConfigMap: %v", kubeadmConfigName, err) framework.Failf("error reading kubernetesVersion from %s ConfigMap: %v", kubeadmConfigName, err)
} }
// Extract the value of the UnversionedKubeletConfigMap feature gate if its present.
// TODO: remove this after the UnversionedKubeletConfigMap feature gate goes GA:
// https://github.com/kubernetes/kubeadm/issues/1582
var UnversionedKubeletConfigMap bool
if _, ok := m["featureGates"]; ok {
if featureGates, ok := m["featureGates"].(map[string]bool); ok {
// TODO: update the default to true once this graduates to Beta.
UnversionedKubeletConfigMap = false
if val, ok := featureGates["UnversionedKubeletConfigMap"]; ok {
UnversionedKubeletConfigMap = val
}
} else {
framework.Failf("unable to cast the featureGates field in the %s ConfigMap", kubeadmConfigName)
}
}
// Computes all the names derived from the kubernetesVersion // Computes all the names derived from the kubernetesVersion
kubeletConfigConfigMapName = fmt.Sprintf("kubelet-config-%d.%d", k8sVersion.Major(), k8sVersion.Minor()) kubeletConfigConfigMapName = "kubelet-config"
kubeletConfigRoleName = fmt.Sprintf("kubeadm:kubelet-config-%d.%d", k8sVersion.Major(), k8sVersion.Minor()) kubeletConfigRoleName = "kubeadm:kubelet-config"
// TODO: remove this after the UnversionedKubeletConfigMap feature gate goes GA:
// https://github.com/kubernetes/kubeadm/issues/1582
if !UnversionedKubeletConfigMap {
kubeletConfigConfigMapName = fmt.Sprintf("kubelet-config-%d.%d", k8sVersion.Major(), k8sVersion.Minor())
kubeletConfigRoleName = fmt.Sprintf("kubeadm:kubelet-config-%d.%d", k8sVersion.Major(), k8sVersion.Minor())
}
kubeletConfigRoleBindingName = kubeletConfigRoleName kubeletConfigRoleBindingName = kubeletConfigRoleName
kubeletConfigConfigMapResource.Name = kubeletConfigConfigMapName kubeletConfigConfigMapResource.Name = kubeletConfigConfigMapName
}) })