kubeadm: update the rest of the code base for structured ExtraArgs

In all places map[string]string compounds were used directly
for extra args. Modify said locations to use []Arg
and the new utilities Get/SetArgValue(), ArgumentsTo/FromCommand().
This commit is contained in:
Lubomir I. Ivanov 2023-07-05 18:33:05 +03:00
parent a505c7160e
commit f3da0d4ed3
10 changed files with 202 additions and 179 deletions

View File

@ -26,6 +26,7 @@ import (
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
@ -78,8 +79,8 @@ func runKubeletFinalizeCertRotation(c workflow.RunData) error {
// If yes, use that path, else use the kubeadm provided value.
cfg := data.Cfg()
pkiPath := filepath.Join(data.KubeletDir(), "pki")
val, ok := cfg.NodeRegistration.KubeletExtraArgs["cert-dir"]
if ok {
val, idx := kubeadmapi.GetArgValue(cfg.NodeRegistration.KubeletExtraArgs, "cert-dir", -1)
if idx > -1 {
pkiPath = val
}

View File

@ -162,46 +162,47 @@ func CreateStaticPodFiles(manifestDir, patchesDir string, cfg *kubeadmapi.Cluste
// getAPIServerCommand builds the right API server command from the given config object and version
func getAPIServerCommand(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint *kubeadmapi.APIEndpoint) []string {
defaultArguments := map[string]string{
"advertise-address": localAPIEndpoint.AdvertiseAddress,
"enable-admission-plugins": "NodeRestriction",
"service-cluster-ip-range": cfg.Networking.ServiceSubnet,
"service-account-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName),
"service-account-signing-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName),
"service-account-issuer": fmt.Sprintf("https://kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
"client-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.CACertName),
"tls-cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerCertName),
"tls-private-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKeyName),
"kubelet-client-certificate": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKubeletClientCertName),
"kubelet-client-key": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKubeletClientKeyName),
"enable-bootstrap-token-auth": "true",
"secure-port": fmt.Sprintf("%d", localAPIEndpoint.BindPort),
"allow-privileged": "true",
"kubelet-preferred-address-types": "InternalIP,ExternalIP,Hostname",
defaultArguments := []kubeadmapi.Arg{
{Name: "advertise-address", Value: localAPIEndpoint.AdvertiseAddress},
{Name: "enable-admission-plugins", Value: "NodeRestriction"},
{Name: "service-cluster-ip-range", Value: cfg.Networking.ServiceSubnet},
{Name: "service-account-key-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName)},
{Name: "service-account-signing-key-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName)},
{Name: "service-account-issuer", Value: fmt.Sprintf("https://kubernetes.default.svc.%s", cfg.Networking.DNSDomain)},
{Name: "client-ca-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.CACertName)},
{Name: "tls-cert-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerCertName)},
{Name: "tls-private-key-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKeyName)},
{Name: "kubelet-client-certificate", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKubeletClientCertName)},
{Name: "kubelet-client-key", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKubeletClientKeyName)},
{Name: "enable-bootstrap-token-auth", Value: "true"},
{Name: "secure-port", Value: fmt.Sprintf("%d", localAPIEndpoint.BindPort)},
{Name: "allow-privileged", Value: "true"},
{Name: "kubelet-preferred-address-types", Value: "InternalIP,ExternalIP,Hostname"},
// add options to configure the front proxy. Without the generated client cert, this will never be useable
// so add it unconditionally with recommended values
"requestheader-username-headers": "X-Remote-User",
"requestheader-group-headers": "X-Remote-Group",
"requestheader-extra-headers-prefix": "X-Remote-Extra-",
"requestheader-client-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertName),
"requestheader-allowed-names": "front-proxy-client",
"proxy-client-cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyClientCertName),
"proxy-client-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyClientKeyName),
{Name: "requestheader-username-headers", Value: "X-Remote-User"},
{Name: "requestheader-group-headers", Value: "X-Remote-Group"},
{Name: "requestheader-extra-headers-prefix", Value: "X-Remote-Extra-"},
{Name: "requestheader-client-ca-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertName)},
{Name: "requestheader-allowed-names", Value: "front-proxy-client"},
{Name: "proxy-client-cert-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyClientCertName)},
{Name: "proxy-client-key-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyClientKeyName)},
}
command := []string{"kube-apiserver"}
// If the user set endpoints for an external etcd cluster
if cfg.Etcd.External != nil {
defaultArguments["etcd-servers"] = strings.Join(cfg.Etcd.External.Endpoints, ",")
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "etcd-servers", strings.Join(cfg.Etcd.External.Endpoints, ","), 1)
// Use any user supplied etcd certificates
if cfg.Etcd.External.CAFile != "" {
defaultArguments["etcd-cafile"] = cfg.Etcd.External.CAFile
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "etcd-cafile", cfg.Etcd.External.CAFile, 1)
}
if cfg.Etcd.External.CertFile != "" && cfg.Etcd.External.KeyFile != "" {
defaultArguments["etcd-certfile"] = cfg.Etcd.External.CertFile
defaultArguments["etcd-keyfile"] = cfg.Etcd.External.KeyFile
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "etcd-certfile", cfg.Etcd.External.CertFile, 1)
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "etcd-keyfile", cfg.Etcd.External.KeyFile, 1)
}
} else {
// Default to etcd static pod on localhost
@ -210,24 +211,25 @@ func getAPIServerCommand(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint
if utilsnet.IsIPv6String(localAPIEndpoint.AdvertiseAddress) {
etcdLocalhostAddress = "::1"
}
defaultArguments["etcd-servers"] = fmt.Sprintf("https://%s", net.JoinHostPort(etcdLocalhostAddress, strconv.Itoa(kubeadmconstants.EtcdListenClientPort)))
defaultArguments["etcd-cafile"] = filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCACertName)
defaultArguments["etcd-certfile"] = filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientCertName)
defaultArguments["etcd-keyfile"] = filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientKeyName)
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "etcd-servers", fmt.Sprintf("https://%s", net.JoinHostPort(etcdLocalhostAddress, strconv.Itoa(kubeadmconstants.EtcdListenClientPort))), 1)
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "etcd-cafile", filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCACertName), 1)
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "etcd-certfile", filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientCertName), 1)
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "etcd-keyfile", filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientKeyName), 1)
// Apply user configurations for local etcd
if cfg.Etcd.Local != nil {
if value, ok := cfg.Etcd.Local.ExtraArgs["advertise-client-urls"]; ok {
defaultArguments["etcd-servers"] = value
if value, idx := kubeadmapi.GetArgValue(cfg.Etcd.Local.ExtraArgs, "advertise-client-urls", -1); idx > -1 {
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "etcd-servers", value, 1)
}
}
}
if cfg.APIServer.ExtraArgs == nil {
cfg.APIServer.ExtraArgs = map[string]string{}
cfg.APIServer.ExtraArgs = []kubeadmapi.Arg{}
}
cfg.APIServer.ExtraArgs["authorization-mode"] = getAuthzModes(cfg.APIServer.ExtraArgs["authorization-mode"])
command = append(command, kubeadmutil.BuildArgumentListFromMap(defaultArguments, cfg.APIServer.ExtraArgs)...)
authzVal, _ := kubeadmapi.GetArgValue(cfg.APIServer.ExtraArgs, "authorization-mode", -1)
cfg.APIServer.ExtraArgs = kubeadmapi.SetArgValues(cfg.APIServer.ExtraArgs, "authorization-mode", getAuthzModes(authzVal), 1)
command = append(command, kubeadmutil.ArgumentsToCommand(defaultArguments, cfg.APIServer.ExtraArgs)...)
return command
}
@ -302,46 +304,46 @@ func getControllerManagerCommand(cfg *kubeadmapi.ClusterConfiguration) []string
kubeconfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ControllerManagerKubeConfigFileName)
caFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.CACertName)
defaultArguments := map[string]string{
"bind-address": "127.0.0.1",
"leader-elect": "true",
"kubeconfig": kubeconfigFile,
"authentication-kubeconfig": kubeconfigFile,
"authorization-kubeconfig": kubeconfigFile,
"client-ca-file": caFile,
"requestheader-client-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertName),
"root-ca-file": caFile,
"service-account-private-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName),
"cluster-signing-cert-file": caFile,
"cluster-signing-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName),
"use-service-account-credentials": "true",
"controllers": "*,bootstrapsigner,tokencleaner",
defaultArguments := []kubeadmapi.Arg{
{Name: "bind-address", Value: "127.0.0.1"},
{Name: "leader-elect", Value: "true"},
{Name: "kubeconfig", Value: kubeconfigFile},
{Name: "authentication-kubeconfig", Value: kubeconfigFile},
{Name: "authorization-kubeconfig", Value: kubeconfigFile},
{Name: "client-ca-file", Value: caFile},
{Name: "requestheader-client-ca-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertName)},
{Name: "root-ca-file", Value: caFile},
{Name: "service-account-private-key-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName)},
{Name: "cluster-signing-cert-file", Value: caFile},
{Name: "cluster-signing-key-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName)},
{Name: "use-service-account-credentials", Value: "true"},
{Name: "controllers", Value: "*,bootstrapsigner,tokencleaner"},
}
// If using external CA, pass empty string to controller manager instead of ca.key/ca.crt path,
// so that the csrsigning controller fails to start
if res, _ := certphase.UsingExternalCA(cfg); res {
defaultArguments["cluster-signing-key-file"] = ""
defaultArguments["cluster-signing-cert-file"] = ""
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "cluster-signing-key-file", "", 1)
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "cluster-signing-cert-file", "", 1)
}
// Let the controller-manager allocate Node CIDRs for the Pod network.
// Each node will get a subspace of the address CIDR provided with --pod-network-cidr.
if cfg.Networking.PodSubnet != "" {
defaultArguments["allocate-node-cidrs"] = "true"
defaultArguments["cluster-cidr"] = cfg.Networking.PodSubnet
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "allocate-node-cidrs", "true", 1)
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "cluster-cidr", cfg.Networking.PodSubnet, 1)
if cfg.Networking.ServiceSubnet != "" {
defaultArguments["service-cluster-ip-range"] = cfg.Networking.ServiceSubnet
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "service-cluster-ip-range", cfg.Networking.ServiceSubnet, 1)
}
}
// Set cluster name
if cfg.ClusterName != "" {
defaultArguments["cluster-name"] = cfg.ClusterName
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "cluster-name", cfg.ClusterName, 1)
}
command := []string{"kube-controller-manager"}
command = append(command, kubeadmutil.BuildArgumentListFromMap(defaultArguments, cfg.ControllerManager.ExtraArgs)...)
command = append(command, kubeadmutil.ArgumentsToCommand(defaultArguments, cfg.ControllerManager.ExtraArgs)...)
return command
}
@ -349,15 +351,15 @@ func getControllerManagerCommand(cfg *kubeadmapi.ClusterConfiguration) []string
// getSchedulerCommand builds the right scheduler command from the given config object and version
func getSchedulerCommand(cfg *kubeadmapi.ClusterConfiguration) []string {
kubeconfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.SchedulerKubeConfigFileName)
defaultArguments := map[string]string{
"bind-address": "127.0.0.1",
"leader-elect": "true",
"kubeconfig": kubeconfigFile,
"authentication-kubeconfig": kubeconfigFile,
"authorization-kubeconfig": kubeconfigFile,
defaultArguments := []kubeadmapi.Arg{
{Name: "bind-address", Value: "127.0.0.1"},
{Name: "leader-elect", Value: "true"},
{Name: "kubeconfig", Value: kubeconfigFile},
{Name: "authentication-kubeconfig", Value: kubeconfigFile},
{Name: "authorization-kubeconfig", Value: kubeconfigFile},
}
command := []string{"kube-scheduler"}
command = append(command, kubeadmutil.BuildArgumentListFromMap(defaultArguments, cfg.Scheduler.ExtraArgs)...)
command = append(command, kubeadmutil.ArgumentsToCommand(defaultArguments, cfg.Scheduler.ExtraArgs)...)
return command
}

View File

@ -375,11 +375,11 @@ func TestGetAPIServerCommand(t *testing.T) {
CertificatesDir: testCertsDir,
APIServer: kubeadmapi.APIServer{
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
"service-cluster-ip-range": "baz",
"advertise-address": "9.9.9.9",
"audit-policy-file": "/etc/config/audit.yaml",
"audit-log-path": "/var/log/kubernetes",
ExtraArgs: []kubeadmapi.Arg{
{Name: "service-cluster-ip-range", Value: "baz"},
{Name: "advertise-address", Value: "9.9.9.9"},
{Name: "audit-policy-file", Value: "/etc/config/audit.yaml"},
{Name: "audit-log-path", Value: "/var/log/kubernetes"},
},
},
},
@ -425,8 +425,8 @@ func TestGetAPIServerCommand(t *testing.T) {
CertificatesDir: testCertsDir,
APIServer: kubeadmapi.APIServer{
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
"authorization-mode": kubeadmconstants.ModeABAC,
ExtraArgs: []kubeadmapi.Arg{
{Name: "authorization-mode", Value: kubeadmconstants.ModeABAC},
},
},
},
@ -470,12 +470,12 @@ func TestGetAPIServerCommand(t *testing.T) {
CertificatesDir: testCertsDir,
APIServer: kubeadmapi.APIServer{
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
"authorization-mode": strings.Join([]string{
ExtraArgs: []kubeadmapi.Arg{
{Name: "authorization-mode", Value: strings.Join([]string{
kubeadmconstants.ModeNode,
kubeadmconstants.ModeRBAC,
kubeadmconstants.ModeWebhook,
}, ","),
}, ",")},
},
},
},
@ -660,7 +660,7 @@ func TestGetControllerManagerCommand(t *testing.T) {
cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{PodSubnet: "10.0.1.15/16", DNSDomain: "cluster.local"},
ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{"node-cidr-mask-size": "20"},
ExtraArgs: []kubeadmapi.Arg{{Name: "node-cidr-mask-size", Value: "20"}},
},
CertificatesDir: testCertsDir,
KubernetesVersion: cpVersion,
@ -726,7 +726,7 @@ func TestGetControllerManagerCommand(t *testing.T) {
DNSDomain: "cluster.local",
},
ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{"allocate-node-cidrs": "false"},
ExtraArgs: []kubeadmapi.Arg{{Name: "allocate-node-cidrs", Value: "false"}},
},
CertificatesDir: testCertsDir,
KubernetesVersion: cpVersion,
@ -790,7 +790,10 @@ func TestGetControllerManagerCommand(t *testing.T) {
DNSDomain: "cluster.local",
},
ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{"node-cidr-mask-size-ipv4": "20", "node-cidr-mask-size-ipv6": "80"},
ExtraArgs: []kubeadmapi.Arg{
{Name: "node-cidr-mask-size-ipv4", Value: "20"},
{Name: "node-cidr-mask-size-ipv6", Value: "80"},
},
},
CertificatesDir: testCertsDir,
KubernetesVersion: cpVersion,

View File

@ -72,8 +72,8 @@ func getHostPathVolumesForTheControlPlane(cfg *kubeadmapi.ClusterConfiguration)
mounts.NewHostPathMount(kubeadmconstants.KubeControllerManager, kubeadmconstants.KubeConfigVolumeName, controllerManagerKubeConfigFile, controllerManagerKubeConfigFile, true, &hostPathFileOrCreate)
// Mount for the flexvolume directory (/usr/libexec/kubernetes/kubelet-plugins/volume/exec by default)
// Flexvolume dir must NOT be readonly as it is used for third-party plugins to integrate with their storage backends via unix domain socket.
flexvolumeDirVolumePath, ok := cfg.ControllerManager.ExtraArgs["flex-volume-plugin-dir"]
if !ok {
flexvolumeDirVolumePath, idx := kubeadmapi.GetArgValue(cfg.ControllerManager.ExtraArgs, "flex-volume-plugin-dir", -1)
if idx == -1 {
flexvolumeDirVolumePath = defaultFlexvolumeDirVolumePath
}
mounts.NewHostPathMount(kubeadmconstants.KubeControllerManager, flexvolumeDirVolumeName, flexvolumeDirVolumePath, flexvolumeDirVolumePath, false, &hostPathDirectoryOrCreate)

View File

@ -239,31 +239,31 @@ func getEtcdCommand(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.A
if utilsnet.IsIPv6String(endpoint.AdvertiseAddress) {
etcdLocalhostAddress = "::1"
}
defaultArguments := map[string]string{
"name": nodeName,
// TODO: start using --initial-corrupt-check once the graduated flag is available:
defaultArguments := []kubeadmapi.Arg{
{Name: "name", Value: nodeName},
// TODO: start using --initial-corrupt-check once the graduated flag is available,
// https://github.com/kubernetes/kubeadm/issues/2676
"experimental-initial-corrupt-check": "true",
"listen-client-urls": fmt.Sprintf("%s,%s", etcdutil.GetClientURLByIP(etcdLocalhostAddress), etcdutil.GetClientURL(endpoint)),
"advertise-client-urls": etcdutil.GetClientURL(endpoint),
"listen-peer-urls": etcdutil.GetPeerURL(endpoint),
"initial-advertise-peer-urls": etcdutil.GetPeerURL(endpoint),
"data-dir": cfg.Etcd.Local.DataDir,
"cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdServerCertName),
"key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdServerKeyName),
"trusted-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCACertName),
"client-cert-auth": "true",
"peer-cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdPeerCertName),
"peer-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdPeerKeyName),
"peer-trusted-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCACertName),
"peer-client-cert-auth": "true",
"snapshot-count": "10000",
"listen-metrics-urls": fmt.Sprintf("http://%s", net.JoinHostPort(etcdLocalhostAddress, strconv.Itoa(kubeadmconstants.EtcdMetricsPort))),
"experimental-watch-progress-notify-interval": "5s",
{Name: "experimental-initial-corrupt-check", Value: "true"},
{Name: "listen-client-urls", Value: fmt.Sprintf("%s,%s", etcdutil.GetClientURLByIP(etcdLocalhostAddress), etcdutil.GetClientURL(endpoint))},
{Name: "advertise-client-urls", Value: etcdutil.GetClientURL(endpoint)},
{Name: "listen-peer-urls", Value: etcdutil.GetPeerURL(endpoint)},
{Name: "initial-advertise-peer-urls", Value: etcdutil.GetPeerURL(endpoint)},
{Name: "data-dir", Value: cfg.Etcd.Local.DataDir},
{Name: "cert-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdServerCertName)},
{Name: "key-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdServerKeyName)},
{Name: "trusted-ca-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCACertName)},
{Name: "client-cert-auth", Value: "true"},
{Name: "peer-cert-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdPeerCertName)},
{Name: "peer-key-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdPeerKeyName)},
{Name: "peer-trusted-ca-file", Value: filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCACertName)},
{Name: "peer-client-cert-auth", Value: "true"},
{Name: "snapshot-count", Value: "10000"},
{Name: "listen-metrics-urls", Value: fmt.Sprintf("http://%s", net.JoinHostPort(etcdLocalhostAddress, strconv.Itoa(kubeadmconstants.EtcdMetricsPort)))},
{Name: "experimental-watch-progress-notify-interval", Value: "5s"},
}
if len(initialCluster) == 0 {
defaultArguments["initial-cluster"] = fmt.Sprintf("%s=%s", nodeName, etcdutil.GetPeerURL(endpoint))
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "initial-cluster", fmt.Sprintf("%s=%s", nodeName, etcdutil.GetPeerURL(endpoint)), 1)
} else {
// NB. the joining etcd member should be part of the initialCluster list
endpoints := []string{}
@ -271,12 +271,12 @@ func getEtcdCommand(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.A
endpoints = append(endpoints, fmt.Sprintf("%s=%s", member.Name, member.PeerURL))
}
defaultArguments["initial-cluster"] = strings.Join(endpoints, ",")
defaultArguments["initial-cluster-state"] = "existing"
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "initial-cluster", strings.Join(endpoints, ","), 1)
defaultArguments = kubeadmapi.SetArgValues(defaultArguments, "initial-cluster-state", "existing", 1)
}
command := []string{"etcd"}
command = append(command, kubeadmutil.BuildArgumentListFromMap(defaultArguments, cfg.Etcd.Local.ExtraArgs)...)
command = append(command, kubeadmutil.ArgumentsToCommand(defaultArguments, cfg.Etcd.Local.ExtraArgs)...)
return command
}

View File

@ -176,7 +176,7 @@ func TestGetEtcdCommand(t *testing.T) {
name string
advertiseAddress string
nodeName string
extraArgs map[string]string
extraArgs []kubeadmapi.Arg
initialCluster []etcdutil.Member
expected []string
}{
@ -243,9 +243,9 @@ func TestGetEtcdCommand(t *testing.T) {
name: "Extra args",
advertiseAddress: "1.2.3.4",
nodeName: "bar",
extraArgs: map[string]string{
"listen-client-urls": "https://10.0.1.10:2379",
"advertise-client-urls": "https://10.0.1.10:2379",
extraArgs: []kubeadmapi.Arg{
{Name: "listen-client-urls", Value: "https://10.0.1.10:2379"},
{Name: "advertise-client-urls", Value: "https://10.0.1.10:2379"},
},
expected: []string{
"etcd",

View File

@ -51,7 +51,7 @@ func GetNodeNameAndHostname(cfg *kubeadmapi.NodeRegistrationOptions) (string, st
if cfg.Name != "" {
nodeName = cfg.Name
}
if name, ok := cfg.KubeletExtraArgs["hostname-override"]; ok {
if name, idx := kubeadmapi.GetArgValue(cfg.KubeletExtraArgs, "hostname-override", -1); idx > -1 {
nodeName = name
}
return nodeName, hostname, err
@ -65,23 +65,23 @@ func WriteKubeletDynamicEnvFile(cfg *kubeadmapi.ClusterConfiguration, nodeReg *k
pauseImage: images.GetPauseImage(cfg),
registerTaintsUsingFlags: registerTaintsUsingFlags,
}
stringMap := buildKubeletArgMap(flagOpts)
argList := kubeadmutil.BuildArgumentListFromMap(stringMap, nodeReg.KubeletExtraArgs)
stringMap := buildKubeletArgs(flagOpts)
argList := kubeadmutil.ArgumentsToCommand(stringMap, nodeReg.KubeletExtraArgs)
envFileContent := fmt.Sprintf("%s=%q\n", constants.KubeletEnvFileVariableName, strings.Join(argList, " "))
return writeKubeletFlagBytesToDisk([]byte(envFileContent), kubeletDir)
}
// buildKubeletArgMapCommon takes a kubeletFlagsOpts object and builds based on that a string-string map with flags
// buildKubeletArgsCommon takes a kubeletFlagsOpts object and builds based on that a slice of arguments
// that are common to both Linux and Windows
func buildKubeletArgMapCommon(opts kubeletFlagsOpts) map[string]string {
kubeletFlags := map[string]string{}
kubeletFlags["container-runtime-endpoint"] = opts.nodeRegOpts.CRISocket
func buildKubeletArgsCommon(opts kubeletFlagsOpts) []kubeadmapi.Arg {
kubeletFlags := []kubeadmapi.Arg{}
kubeletFlags = append(kubeletFlags, kubeadmapi.Arg{Name: "container-runtime-endpoint", Value: opts.nodeRegOpts.CRISocket})
// This flag passes the pod infra container image (e.g. "pause" image) to the kubelet
// and prevents its garbage collection
if opts.pauseImage != "" {
kubeletFlags["pod-infra-container-image"] = opts.pauseImage
kubeletFlags = append(kubeletFlags, kubeadmapi.Arg{Name: "pod-infra-container-image", Value: opts.pauseImage})
}
if opts.registerTaintsUsingFlags && opts.nodeRegOpts.Taints != nil && len(opts.nodeRegOpts.Taints) > 0 {
@ -89,8 +89,7 @@ func buildKubeletArgMapCommon(opts kubeletFlagsOpts) map[string]string {
for _, taint := range opts.nodeRegOpts.Taints {
taintStrs = append(taintStrs, taint.ToString())
}
kubeletFlags["register-with-taints"] = strings.Join(taintStrs, ",")
kubeletFlags = append(kubeletFlags, kubeadmapi.Arg{Name: "register-with-taints", Value: strings.Join(taintStrs, ",")})
}
// Pass the "--hostname-override" flag to the kubelet only if it's different from the hostname
@ -100,7 +99,7 @@ func buildKubeletArgMapCommon(opts kubeletFlagsOpts) map[string]string {
}
if nodeName != hostname {
klog.V(1).Infof("setting kubelet hostname-override to %q", nodeName)
kubeletFlags["hostname-override"] = nodeName
kubeletFlags = append(kubeletFlags, kubeadmapi.Arg{Name: "hostname-override", Value: nodeName})
}
return kubeletFlags
@ -121,8 +120,8 @@ func writeKubeletFlagBytesToDisk(b []byte, kubeletDir string) error {
return nil
}
// buildKubeletArgMap takes a kubeletFlagsOpts object and builds based on that a string-string map with flags
// buildKubeletArgs takes a kubeletFlagsOpts object and builds based on that a slice of arguments
// that should be given to the local kubelet daemon.
func buildKubeletArgMap(opts kubeletFlagsOpts) map[string]string {
return buildKubeletArgMapCommon(opts)
func buildKubeletArgs(opts kubeletFlagsOpts) []kubeadmapi.Arg {
return buildKubeletArgsCommon(opts)
}

View File

@ -27,23 +27,25 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func TestBuildKubeletArgMap(t *testing.T) {
func TestBuildKubeletArgs(t *testing.T) {
tests := []struct {
name string
opts kubeletFlagsOpts
expected map[string]string
expected []kubeadmapi.Arg
}{
{
name: "hostname override",
opts: kubeletFlagsOpts{
nodeRegOpts: &kubeadmapi.NodeRegistrationOptions{
CRISocket: "unix:///var/run/containerd/containerd.sock",
KubeletExtraArgs: map[string]string{"hostname-override": "override-name"},
KubeletExtraArgs: []kubeadmapi.Arg{
{Name: "hostname-override", Value: "override-name"},
},
},
expected: map[string]string{
"container-runtime-endpoint": "unix:///var/run/containerd/containerd.sock",
"hostname-override": "override-name",
},
expected: []kubeadmapi.Arg{
{Name: "container-runtime-endpoint", Value: "unix:///var/run/containerd/containerd.sock"},
{Name: "hostname-override", Value: "override-name"},
},
},
{
@ -66,9 +68,9 @@ func TestBuildKubeletArgMap(t *testing.T) {
},
registerTaintsUsingFlags: true,
},
expected: map[string]string{
"container-runtime-endpoint": "unix:///var/run/containerd/containerd.sock",
"register-with-taints": "foo=bar:baz,key=val:eff",
expected: []kubeadmapi.Arg{
{Name: "container-runtime-endpoint", Value: "unix:///var/run/containerd/containerd.sock"},
{Name: "register-with-taints", Value: "foo=bar:baz,key=val:eff"},
},
},
{
@ -79,19 +81,19 @@ func TestBuildKubeletArgMap(t *testing.T) {
},
pauseImage: "registry.k8s.io/pause:3.9",
},
expected: map[string]string{
"container-runtime-endpoint": "unix:///var/run/containerd/containerd.sock",
"pod-infra-container-image": "registry.k8s.io/pause:3.9",
expected: []kubeadmapi.Arg{
{Name: "container-runtime-endpoint", Value: "unix:///var/run/containerd/containerd.sock"},
{Name: "pod-infra-container-image", Value: "registry.k8s.io/pause:3.9"},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := buildKubeletArgMap(test.opts)
actual := buildKubeletArgs(test.opts)
if !reflect.DeepEqual(actual, test.expected) {
t.Errorf(
"failed buildKubeletArgMap:\n\texpected: %v\n\t actual: %v",
"failed buildKubeletArgs:\n\texpected: %v\n\t actual: %v",
test.expected,
actual,
)
@ -116,7 +118,9 @@ func TestGetNodeNameAndHostname(t *testing.T) {
name: "overridden hostname",
opts: kubeletFlagsOpts{
nodeRegOpts: &kubeadmapi.NodeRegistrationOptions{
KubeletExtraArgs: map[string]string{"hostname-override": "override-name"},
KubeletExtraArgs: []kubeadmapi.Arg{
{Name: "hostname-override", Value: "override-name"},
},
},
},
expectedNodeName: "override-name",
@ -126,7 +130,9 @@ func TestGetNodeNameAndHostname(t *testing.T) {
name: "overridden hostname uppercase",
opts: kubeletFlagsOpts{
nodeRegOpts: &kubeadmapi.NodeRegistrationOptions{
KubeletExtraArgs: map[string]string{"hostname-override": "OVERRIDE-NAME"},
KubeletExtraArgs: []kubeadmapi.Arg{
{Name: "hostname-override", Value: "OVERRIDE-NAME"},
},
},
},
expectedNodeName: "OVERRIDE-NAME",
@ -136,7 +142,9 @@ func TestGetNodeNameAndHostname(t *testing.T) {
name: "hostname contains only spaces",
opts: kubeletFlagsOpts{
nodeRegOpts: &kubeadmapi.NodeRegistrationOptions{
KubeletExtraArgs: map[string]string{"hostname-override": " "},
KubeletExtraArgs: []kubeadmapi.Arg{
{Name: "hostname-override", Value: " "},
},
},
},
expectedNodeName: " ",
@ -146,7 +154,9 @@ func TestGetNodeNameAndHostname(t *testing.T) {
name: "empty parameter",
opts: kubeletFlagsOpts{
nodeRegOpts: &kubeadmapi.NodeRegistrationOptions{
KubeletExtraArgs: map[string]string{"hostname-override": ""},
KubeletExtraArgs: []kubeadmapi.Arg{
{Name: "hostname-override", Value: ""},
},
},
},
expectedNodeName: "",

View File

@ -295,7 +295,7 @@ func GetAPIServerProbeAddress(endpoint *kubeadmapi.APIEndpoint) string {
// GetControllerManagerProbeAddress returns the kubernetes controller manager probe address
func GetControllerManagerProbeAddress(cfg *kubeadmapi.ClusterConfiguration) string {
if addr, exists := cfg.ControllerManager.ExtraArgs[kubeControllerManagerBindAddressArg]; exists {
if addr, idx := kubeadmapi.GetArgValue(cfg.ControllerManager.ExtraArgs, kubeControllerManagerBindAddressArg, -1); idx > -1 {
return getProbeAddress(addr)
}
return "127.0.0.1"
@ -303,7 +303,7 @@ func GetControllerManagerProbeAddress(cfg *kubeadmapi.ClusterConfiguration) stri
// GetSchedulerProbeAddress returns the kubernetes scheduler probe address
func GetSchedulerProbeAddress(cfg *kubeadmapi.ClusterConfiguration) string {
if addr, exists := cfg.Scheduler.ExtraArgs[kubeSchedulerBindAddressArg]; exists {
if addr, idx := kubeadmapi.GetArgValue(cfg.Scheduler.ExtraArgs, kubeSchedulerBindAddressArg, -1); idx > -1 {
return getProbeAddress(addr)
}
return "127.0.0.1"
@ -320,7 +320,7 @@ func GetEtcdProbeEndpoint(cfg *kubeadmapi.Etcd, isIPv6 bool) (string, int, v1.UR
if cfg.Local == nil || cfg.Local.ExtraArgs == nil {
return localhost, kubeadmconstants.EtcdMetricsPort, v1.URISchemeHTTP
}
if arg, exists := cfg.Local.ExtraArgs["listen-metrics-urls"]; exists {
if arg, idx := kubeadmapi.GetArgValue(cfg.Local.ExtraArgs, "listen-metrics-urls", -1); idx > -1 {
// Use the first url in the listen-metrics-urls if multiple URL's are specified.
arg = strings.Split(arg, ",")[0]
parsedURL, err := url.Parse(arg)

View File

@ -107,7 +107,7 @@ func TestGetControllerManagerProbeAddress(t *testing.T) {
desc: "no controller manager extra args leads to 127.0.0.1 being used",
cfg: &kubeadmapi.ClusterConfiguration{
ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{},
ExtraArgs: []kubeadmapi.Arg{},
},
},
expected: "127.0.0.1",
@ -116,8 +116,8 @@ func TestGetControllerManagerProbeAddress(t *testing.T) {
desc: "setting controller manager extra address arg to something acknowledges it",
cfg: &kubeadmapi.ClusterConfiguration{
ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
kubeControllerManagerBindAddressArg: "10.10.10.10",
ExtraArgs: []kubeadmapi.Arg{
{Name: kubeControllerManagerBindAddressArg, Value: "10.10.10.10"},
},
},
},
@ -127,8 +127,8 @@ func TestGetControllerManagerProbeAddress(t *testing.T) {
desc: "setting controller manager extra ipv6 address arg to something acknowledges it",
cfg: &kubeadmapi.ClusterConfiguration{
ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
kubeControllerManagerBindAddressArg: "2001:abcd:bcda::1",
ExtraArgs: []kubeadmapi.Arg{
{Name: kubeControllerManagerBindAddressArg, Value: "2001:abcd:bcda::1"},
},
},
},
@ -138,8 +138,8 @@ func TestGetControllerManagerProbeAddress(t *testing.T) {
desc: "setting controller manager extra address arg to 0.0.0.0 returns empty",
cfg: &kubeadmapi.ClusterConfiguration{
ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
kubeControllerManagerBindAddressArg: "0.0.0.0",
ExtraArgs: []kubeadmapi.Arg{
{Name: kubeControllerManagerBindAddressArg, Value: "0.0.0.0"},
},
},
},
@ -149,8 +149,8 @@ func TestGetControllerManagerProbeAddress(t *testing.T) {
desc: "setting controller manager extra ipv6 address arg to :: returns empty",
cfg: &kubeadmapi.ClusterConfiguration{
ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
kubeControllerManagerBindAddressArg: "::",
ExtraArgs: []kubeadmapi.Arg{
{Name: kubeControllerManagerBindAddressArg, Value: "::"},
},
},
},
@ -178,7 +178,7 @@ func TestGetSchedulerProbeAddress(t *testing.T) {
desc: "no scheduler extra args leads to 127.0.0.1 being used",
cfg: &kubeadmapi.ClusterConfiguration{
Scheduler: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{},
ExtraArgs: []kubeadmapi.Arg{},
},
},
expected: "127.0.0.1",
@ -187,8 +187,8 @@ func TestGetSchedulerProbeAddress(t *testing.T) {
desc: "setting scheduler extra address arg to something acknowledges it",
cfg: &kubeadmapi.ClusterConfiguration{
Scheduler: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
kubeSchedulerBindAddressArg: "10.10.10.10",
ExtraArgs: []kubeadmapi.Arg{
{Name: kubeSchedulerBindAddressArg, Value: "10.10.10.10"},
},
},
},
@ -198,8 +198,8 @@ func TestGetSchedulerProbeAddress(t *testing.T) {
desc: "setting scheduler extra ipv6 address arg to something acknowledges it",
cfg: &kubeadmapi.ClusterConfiguration{
Scheduler: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
kubeSchedulerBindAddressArg: "2001:abcd:bcda::1",
ExtraArgs: []kubeadmapi.Arg{
{Name: kubeSchedulerBindAddressArg, Value: "2001:abcd:bcda::1"},
},
},
},
@ -209,8 +209,8 @@ func TestGetSchedulerProbeAddress(t *testing.T) {
desc: "setting scheduler extra ipv6 address arg to 0.0.0.0 returns empty",
cfg: &kubeadmapi.ClusterConfiguration{
Scheduler: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
kubeSchedulerBindAddressArg: "0.0.0.0",
ExtraArgs: []kubeadmapi.Arg{
{Name: kubeSchedulerBindAddressArg, Value: "0.0.0.0"},
},
},
},
@ -220,8 +220,8 @@ func TestGetSchedulerProbeAddress(t *testing.T) {
desc: "setting scheduler extra ipv6 address arg to :: returns empty",
cfg: &kubeadmapi.ClusterConfiguration{
Scheduler: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{
kubeSchedulerBindAddressArg: "::",
ExtraArgs: []kubeadmapi.Arg{
{Name: kubeSchedulerBindAddressArg, Value: "::"},
},
},
},
@ -251,8 +251,9 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
name: "etcd probe URL from two URLs",
cfg: &kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
ExtraArgs: map[string]string{
"listen-metrics-urls": "https://1.2.3.4:1234,https://4.3.2.1:2381"},
ExtraArgs: []kubeadmapi.Arg{
{Name: "listen-metrics-urls", Value: "https://1.2.3.4:1234,https://4.3.2.1:2381"},
},
},
},
isIPv6: false,
@ -264,8 +265,9 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
name: "etcd probe URL with HTTP scheme",
cfg: &kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
ExtraArgs: map[string]string{
"listen-metrics-urls": "http://1.2.3.4:1234"},
ExtraArgs: []kubeadmapi.Arg{
{Name: "listen-metrics-urls", Value: "http://1.2.3.4:1234"},
},
},
},
isIPv6: false,
@ -277,8 +279,9 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
name: "etcd probe URL without scheme should result in defaults",
cfg: &kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
ExtraArgs: map[string]string{
"listen-metrics-urls": "1.2.3.4"},
ExtraArgs: []kubeadmapi.Arg{
{Name: "listen-metrics-urls", Value: "1.2.3.4"},
},
},
},
isIPv6: false,
@ -290,8 +293,9 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
name: "etcd probe URL without port",
cfg: &kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
ExtraArgs: map[string]string{
"listen-metrics-urls": "https://1.2.3.4"},
ExtraArgs: []kubeadmapi.Arg{
{Name: "listen-metrics-urls", Value: "https://1.2.3.4"},
},
},
},
isIPv6: false,
@ -303,8 +307,9 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
name: "etcd probe URL from two IPv6 URLs",
cfg: &kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
ExtraArgs: map[string]string{
"listen-metrics-urls": "https://[2001:abcd:bcda::1]:1234,https://[2001:abcd:bcda::2]:2381"},
ExtraArgs: []kubeadmapi.Arg{
{Name: "listen-metrics-urls", Value: "https://[2001:abcd:bcda::1]:1234,https://[2001:abcd:bcda::2]:2381"},
},
},
},
isIPv6: true,
@ -316,8 +321,9 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
name: "etcd probe localhost IPv6 URL with HTTP scheme",
cfg: &kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
ExtraArgs: map[string]string{
"listen-metrics-urls": "http://[::1]:1234"},
ExtraArgs: []kubeadmapi.Arg{
{Name: "listen-metrics-urls", Value: "http://[::1]:1234"},
},
},
},
isIPv6: true,
@ -329,8 +335,9 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
name: "etcd probe IPv6 URL with HTTP scheme",
cfg: &kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
ExtraArgs: map[string]string{
"listen-metrics-urls": "http://[2001:abcd:bcda::1]:1234"},
ExtraArgs: []kubeadmapi.Arg{
{Name: "listen-metrics-urls", Value: "http://[2001:abcd:bcda::1]:1234"},
},
},
},
isIPv6: true,
@ -342,8 +349,9 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
name: "etcd probe IPv6 URL without port",
cfg: &kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
ExtraArgs: map[string]string{
"listen-metrics-urls": "https://[2001:abcd:bcda::1]"},
ExtraArgs: []kubeadmapi.Arg{
{Name: "listen-metrics-urls", Value: "https://[2001:abcd:bcda::1]"},
},
},
},
isIPv6: true,