kubeadm graduate kubelet-start phase

This commit is contained in:
fabriziopandini 2018-10-24 12:57:31 +02:00
parent cb4e809fe9
commit a3d90cc824
7 changed files with 85 additions and 173 deletions

View File

@ -165,6 +165,7 @@ func NewCmdInit(out io.Writer) *cobra.Command {
// initialize the workflow runner with the list of phases // initialize the workflow runner with the list of phases
initRunner.AppendPhase(phases.NewPreflightMasterPhase()) initRunner.AppendPhase(phases.NewPreflightMasterPhase())
initRunner.AppendPhase(phases.NewKubeletStartPhase())
// TODO: add other phases to the runner. // TODO: add other phases to the runner.
// sets the data builder function, that will be used by the runner // sets the data builder function, that will be used by the runner
@ -413,36 +414,11 @@ func runInit(i *initData, out io.Writer) error {
// Get directories to write files to; can be faked if we're dry-running // Get directories to write files to; can be faked if we're dry-running
glog.V(1).Infof("[init] Getting certificates directory from configuration") glog.V(1).Infof("[init] Getting certificates directory from configuration")
realCertsDir := i.cfg.CertificatesDir realCertsDir := i.cfg.CertificatesDir
certsDirToWriteTo, kubeConfigDir, manifestDir, kubeletDir, err := getDirectoriesToUse(i.dryRun, i.cfg.CertificatesDir) certsDirToWriteTo, kubeConfigDir, manifestDir, _, err := getDirectoriesToUse(i.dryRun, i.dryRunDir, i.cfg.CertificatesDir)
if err != nil { if err != nil {
return fmt.Errorf("error getting directories to use: %v", err) return fmt.Errorf("error getting directories to use: %v", err)
} }
// First off, configure the kubelet. In this short timeframe, kubeadm is trying to stop/restart the kubelet
// Try to stop the kubelet service so no race conditions occur when configuring it
if !i.dryRun {
glog.V(1).Infof("Stopping the kubelet")
kubeletphase.TryStopKubelet()
}
// Write env file with flags for the kubelet to use. We do not need to write the --register-with-taints for the master,
// as we handle that ourselves in the markmaster phase
// TODO: Maybe we want to do that some time in the future, in order to remove some logic from the markmaster phase?
if err := kubeletphase.WriteKubeletDynamicEnvFile(&i.cfg.NodeRegistration, i.cfg.FeatureGates, false, kubeletDir); err != nil {
return fmt.Errorf("error writing a dynamic environment file for the kubelet: %v", err)
}
// Write the kubelet configuration file to disk.
if err := kubeletphase.WriteConfigToDisk(i.cfg.ComponentConfigs.Kubelet, kubeletDir); err != nil {
return fmt.Errorf("error writing kubelet configuration to disk: %v", err)
}
if !i.dryRun {
// Try to start the kubelet service in case it's inactive
glog.V(1).Infof("Starting the kubelet")
kubeletphase.TryStartKubelet()
}
// certsDirToWriteTo is gonna equal cfg.CertificatesDir in the normal case, but gonna be a temp directory if dryrunning // certsDirToWriteTo is gonna equal cfg.CertificatesDir in the normal case, but gonna be a temp directory if dryrunning
i.cfg.CertificatesDir = certsDirToWriteTo i.cfg.CertificatesDir = certsDirToWriteTo
@ -676,12 +652,8 @@ func createClient(cfg *kubeadmapi.InitConfiguration, dryRun bool) (clientset.Int
// getDirectoriesToUse returns the (in order) certificates, kubeconfig and Static Pod manifest directories, followed by a possible error // getDirectoriesToUse returns the (in order) certificates, kubeconfig and Static Pod manifest directories, followed by a possible error
// This behaves differently when dry-running vs the normal flow // This behaves differently when dry-running vs the normal flow
func getDirectoriesToUse(dryRun bool, defaultPkiDir string) (string, string, string, string, error) { func getDirectoriesToUse(dryRun bool, dryRunDir string, defaultPkiDir string) (string, string, string, string, error) {
if dryRun { if dryRun {
dryRunDir, err := ioutil.TempDir("", "kubeadm-init-dryrun")
if err != nil {
return "", "", "", "", fmt.Errorf("couldn't create a temporary directory: %v", err)
}
// Use the same temp dir for all // Use the same temp dir for all
return dryRunDir, dryRunDir, dryRunDir, dryRunDir, nil return dryRunDir, dryRunDir, dryRunDir, dryRunDir, nil
} }

View File

@ -19,12 +19,14 @@ package phases
import ( import (
"fmt" "fmt"
"github.com/golang/glog"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/version"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1" kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" "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" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet" kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
@ -38,18 +40,9 @@ import (
) )
var ( var (
kubeletWriteEnvFileLongDesc = normalizer.LongDesc(` kubeletStartPhaseExample = normalizer.Examples(`
Writes an environment file with flags that should be passed to the kubelet executing on the master or node.
This --config flag can either consume a InitConfiguration object or a JoinConfiguration one, as this
function is used for both "kubeadm init" and "kubeadm join".
` + cmdutil.AlphaDisclaimer)
kubeletWriteEnvFileExample = normalizer.Examples(`
# Writes a dynamic environment file with kubelet flags from a InitConfiguration file. # Writes a dynamic environment file with kubelet flags from a InitConfiguration file.
kubeadm alpha phase kubelet write-env-file --config masterconfig.yaml kubeadm init phase kubelet-start --config masterconfig.yaml
# Writes a dynamic environment file with kubelet flags from a JoinConfiguration file.
kubeadm alpha phase kubelet write-env-file --config nodeconfig.yaml
`) `)
kubeletConfigUploadLongDesc = normalizer.LongDesc(` kubeletConfigUploadLongDesc = normalizer.LongDesc(`
@ -84,15 +77,6 @@ var (
kubeadm alpha phase kubelet config download --kubelet-version v1.12.0 kubeadm alpha phase kubelet config download --kubelet-version v1.12.0
`) `)
kubeletConfigWriteToDiskLongDesc = normalizer.LongDesc(`
Writes kubelet configuration to disk, based on the kubeadm configuration passed via "--config".
` + cmdutil.AlphaDisclaimer)
kubeletConfigWriteToDiskExample = normalizer.Examples(`
# Extracts the kubelet configuration from a kubeadm configuration file
kubeadm alpha phase kubelet config write-to-disk --config kubeadm.yaml
`)
kubeletConfigEnableDynamicLongDesc = normalizer.LongDesc(` kubeletConfigEnableDynamicLongDesc = normalizer.LongDesc(`
Enables or updates dynamic kubelet configuration for a Node, against the kubelet-config-1.X ConfigMap in the cluster, Enables or updates dynamic kubelet configuration for a Node, against the kubelet-config-1.X ConfigMap in the cluster,
where X is the minor version of the desired kubelet version. where X is the minor version of the desired kubelet version.
@ -111,6 +95,61 @@ var (
`) `)
) )
// kubeletStartData defines the behavior that a runtime data struct passed to the kubelet start phase
// should have. Please note that we are using an interface in order to make this phase reusable in different workflows
// (and thus with different runtime data struct, all of them requested to be compliant to this interface)
type kubeletStartData interface {
Cfg() *kubeadmapi.InitConfiguration
DryRun() bool
KubeletDir() string
}
// NewKubeletStartPhase creates a kubeadm workflow phase that start kubelet on a node.
func NewKubeletStartPhase() workflow.Phase {
return workflow.Phase{
Name: "kubelet-start",
Short: "Writes kubelet settings and (re)starts the kubelet",
Long: "Writes a file with KubeletConfiguration and an environment file with node specific kubelet settings, and then (re)starts kubelet.",
Example: kubeletStartPhaseExample,
Run: runKubeletStart,
}
}
// runKubeletStart executes kubelet start logic.
func runKubeletStart(c workflow.RunData) error {
data, ok := c.(kubeletStartData)
if !ok {
return errors.New("kubelet-start phase invoked with an invalid data struct")
}
// First off, configure the kubelet. In this short timeframe, kubeadm is trying to stop/restart the kubelet
// Try to stop the kubelet service so no race conditions occur when configuring it
if !data.DryRun() {
glog.V(1).Infof("Stopping the kubelet")
kubeletphase.TryStopKubelet()
}
// Write env file with flags for the kubelet to use. We do not need to write the --register-with-taints for the master,
// as we handle that ourselves in the markmaster phase
// TODO: Maybe we want to do that some time in the future, in order to remove some logic from the markmaster phase?
if err := kubeletphase.WriteKubeletDynamicEnvFile(&data.Cfg().NodeRegistration, data.Cfg().FeatureGates, false, data.KubeletDir()); err != nil {
return errors.Wrap(err, "error writing a dynamic environment file for the kubelet")
}
// Write the kubelet configuration file to disk.
if err := kubeletphase.WriteConfigToDisk(data.Cfg().ComponentConfigs.Kubelet, data.KubeletDir()); err != nil {
return errors.Wrap(err, "error writing kubelet configuration to disk")
}
// Try to start the kubelet service in case it's inactive
if !data.DryRun() {
glog.V(1).Infof("Starting the kubelet")
kubeletphase.TryStartKubelet()
}
return nil
}
// NewCmdKubelet returns command for `kubeadm phase kubelet` // NewCmdKubelet returns command for `kubeadm phase kubelet`
func NewCmdKubelet() *cobra.Command { func NewCmdKubelet() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -120,63 +159,9 @@ func NewCmdKubelet() *cobra.Command {
} }
cmd.AddCommand(NewCmdKubeletConfig()) cmd.AddCommand(NewCmdKubeletConfig())
cmd.AddCommand(NewCmdKubeletWriteEnvFile())
return cmd return cmd
} }
// NewCmdKubeletWriteEnvFile calls cobra.Command for writing the dynamic kubelet env file based on a InitConfiguration or JoinConfiguration object
func NewCmdKubeletWriteEnvFile() *cobra.Command {
var cfgPath string
cmd := &cobra.Command{
Use: "write-env-file",
Short: "Writes an environment file with runtime flags for the kubelet.",
Long: kubeletWriteEnvFileLongDesc,
Example: kubeletWriteEnvFileExample,
Run: func(cmd *cobra.Command, args []string) {
if len(cfgPath) == 0 {
kubeadmutil.CheckErr(fmt.Errorf("The --config flag is mandatory"))
}
err := RunKubeletWriteEnvFile(cfgPath)
kubeadmutil.CheckErr(err)
},
}
options.AddConfigFlag(cmd.Flags(), &cfgPath)
return cmd
}
// RunKubeletWriteEnvFile is the function that is run when "kubeadm phase kubelet write-env-file" is executed
func RunKubeletWriteEnvFile(cfgPath string) error {
internalcfg, err := configutil.AnyConfigFileAndDefaultsToInternal(cfgPath)
if err != nil {
return err
}
var nodeRegistrationObj *kubeadmapi.NodeRegistrationOptions
var featureGates map[string]bool
var registerWithTaints bool
switch cfg := internalcfg.(type) {
case *kubeadmapi.InitConfiguration:
nodeRegistrationObj = &cfg.NodeRegistration
featureGates = cfg.FeatureGates
registerWithTaints = false
case *kubeadmapi.JoinConfiguration:
nodeRegistrationObj = &cfg.NodeRegistration
featureGates = cfg.FeatureGates
registerWithTaints = true
default:
return fmt.Errorf("couldn't read config file, no matching kind found")
}
if err := kubeletphase.WriteKubeletDynamicEnvFile(nodeRegistrationObj, featureGates, registerWithTaints, constants.KubeletRunDirectory); err != nil {
return fmt.Errorf("error writing a dynamic environment file for the kubelet: %v", err)
}
return nil
}
// NewCmdKubeletConfig returns command for `kubeadm phase kubelet config` // NewCmdKubeletConfig returns command for `kubeadm phase kubelet config`
func NewCmdKubeletConfig() *cobra.Command { func NewCmdKubeletConfig() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -188,7 +173,6 @@ func NewCmdKubeletConfig() *cobra.Command {
cmd.AddCommand(NewCmdKubeletConfigUpload()) cmd.AddCommand(NewCmdKubeletConfigUpload())
cmd.AddCommand(NewCmdKubeletAnnotateCRI()) cmd.AddCommand(NewCmdKubeletAnnotateCRI())
cmd.AddCommand(NewCmdKubeletConfigDownload()) cmd.AddCommand(NewCmdKubeletConfigDownload())
cmd.AddCommand(NewCmdKubeletConfigWriteToDisk())
cmd.AddCommand(NewCmdKubeletConfigEnableDynamic()) cmd.AddCommand(NewCmdKubeletConfigEnableDynamic())
return cmd return cmd
} }
@ -304,37 +288,6 @@ func getKubeletVersion(kubeletVersionStr string) (*version.Version, error) {
return preflight.GetKubeletVersion(utilsexec.New()) return preflight.GetKubeletVersion(utilsexec.New())
} }
// NewCmdKubeletConfigWriteToDisk calls cobra.Command for writing init kubelet configuration
func NewCmdKubeletConfigWriteToDisk() *cobra.Command {
cfg := &kubeadmapiv1beta1.InitConfiguration{}
var cfgPath string
cmd := &cobra.Command{
Use: "write-to-disk",
Short: "Writes kubelet configuration to disk, either based on the --config argument.",
Long: kubeletConfigWriteToDiskLongDesc,
Example: kubeletConfigWriteToDiskExample,
Run: func(cmd *cobra.Command, args []string) {
if len(cfgPath) == 0 {
kubeadmutil.CheckErr(fmt.Errorf("The --config argument is required"))
}
// KubernetesVersion is not used, but we set it explicitly to avoid the lookup
// of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
SetKubernetesVersion(cfg)
// This call returns the ready-to-use configuration based on the configuration file
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
kubeadmutil.CheckErr(err)
err = kubeletphase.WriteConfigToDisk(internalcfg.ComponentConfigs.Kubelet, constants.KubeletRunDirectory)
kubeadmutil.CheckErr(err)
},
}
options.AddConfigFlag(cmd.Flags(), &cfgPath)
return cmd
}
// NewCmdKubeletConfigEnableDynamic calls cobra.Command for enabling dynamic kubelet configuration on node // NewCmdKubeletConfigEnableDynamic calls cobra.Command for enabling dynamic kubelet configuration on node
// This feature is still in alpha and an experimental state // This feature is still in alpha and an experimental state
func NewCmdKubeletConfigEnableDynamic() *cobra.Command { func NewCmdKubeletConfigEnableDynamic() *cobra.Command {

View File

@ -20,16 +20,13 @@ import (
"testing" "testing"
"github.com/spf13/cobra" "github.com/spf13/cobra"
cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd" cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd"
) )
func TestKubeletSubCommandsHasFlags(t *testing.T) { func TestKubeletSubCommandsHasFlags(t *testing.T) {
subCmds := []*cobra.Command{ subCmds := []*cobra.Command{
NewCmdKubeletWriteEnvFile(),
NewCmdKubeletConfigUpload(), NewCmdKubeletConfigUpload(),
NewCmdKubeletConfigDownload(), NewCmdKubeletConfigDownload(),
NewCmdKubeletConfigWriteToDisk(),
NewCmdKubeletConfigEnableDynamic(), NewCmdKubeletConfigEnableDynamic(),
} }
@ -39,12 +36,6 @@ func TestKubeletSubCommandsHasFlags(t *testing.T) {
command string command string
additionalFlags []string additionalFlags []string
}{ }{
{
command: "write-env-file",
additionalFlags: []string{
"config",
},
},
{ {
command: "upload", command: "upload",
additionalFlags: []string{ additionalFlags: []string{
@ -59,12 +50,6 @@ func TestKubeletSubCommandsHasFlags(t *testing.T) {
"kubelet-version", "kubelet-version",
}, },
}, },
{
command: "write-to-disk",
additionalFlags: []string{
"config",
},
},
{ {
command: "enable-dynamic", command: "enable-dynamic",
additionalFlags: []string{ additionalFlags: []string{

View File

@ -155,7 +155,7 @@ func getConfigBytes(kubeletConfig *kubeletconfig.KubeletConfiguration) ([]byte,
// writeConfigBytesToDisk writes a byte slice down to disk at the specific location of the kubelet config file // writeConfigBytesToDisk writes a byte slice down to disk at the specific location of the kubelet config file
func writeConfigBytesToDisk(b []byte, kubeletDir string) error { func writeConfigBytesToDisk(b []byte, kubeletDir string) error {
configFile := filepath.Join(kubeletDir, kubeadmconstants.KubeletConfigurationFileName) configFile := filepath.Join(kubeletDir, kubeadmconstants.KubeletConfigurationFileName)
fmt.Printf("[kubelet] Writing kubelet configuration to file %q\n", configFile) fmt.Printf("[kubelet-start] Writing kubelet configuration to file %q\n", configFile)
// creates target folder if not already exists // creates target folder if not already exists
if err := os.MkdirAll(kubeletDir, 0700); err != nil { if err := os.MkdirAll(kubeletDir, 0700); err != nil {

View File

@ -119,7 +119,7 @@ func buildKubeletArgMap(opts kubeletFlagsOpts) map[string]string {
// writeKubeletFlagBytesToDisk writes a byte slice down to disk at the specific location of the kubelet flag overrides file // writeKubeletFlagBytesToDisk writes a byte slice down to disk at the specific location of the kubelet flag overrides file
func writeKubeletFlagBytesToDisk(b []byte, kubeletDir string) error { func writeKubeletFlagBytesToDisk(b []byte, kubeletDir string) error {
kubeletEnvFilePath := filepath.Join(kubeletDir, constants.KubeletEnvFileName) kubeletEnvFilePath := filepath.Join(kubeletDir, constants.KubeletEnvFileName)
fmt.Printf("[kubelet] Writing kubelet environment file with flags to file %q\n", kubeletEnvFilePath) fmt.Printf("[kubelet-start] Writing kubelet environment file with flags to file %q\n", kubeletEnvFilePath)
// creates target folder if not already exists // creates target folder if not already exists
if err := os.MkdirAll(kubeletDir, 0700); err != nil { if err := os.MkdirAll(kubeletDir, 0700); err != nil {

View File

@ -27,19 +27,19 @@ func TryStartKubelet() {
// If we notice that the kubelet service is inactive, try to start it // If we notice that the kubelet service is inactive, try to start it
initSystem, err := initsystem.GetInitSystem() initSystem, err := initsystem.GetInitSystem()
if err != nil { if err != nil {
fmt.Println("[preflight] no supported init system detected, won't make sure the kubelet is running properly.") fmt.Println("[kubelet-start] no supported init system detected, won't make sure the kubelet is running properly.")
return return
} }
if !initSystem.ServiceExists("kubelet") { if !initSystem.ServiceExists("kubelet") {
fmt.Println("[preflight] couldn't detect a kubelet service, can't make sure the kubelet is running properly.") fmt.Println("[kubelet-start] couldn't detect a kubelet service, can't make sure the kubelet is running properly.")
} }
fmt.Println("[preflight] Activating the kubelet service") fmt.Println("[kubelet-start] Activating the kubelet service")
// This runs "systemctl daemon-reload && systemctl restart kubelet" // This runs "systemctl daemon-reload && systemctl restart kubelet"
if err := initSystem.ServiceRestart("kubelet"); err != nil { if err := initSystem.ServiceRestart("kubelet"); err != nil {
fmt.Printf("[preflight] WARNING: unable to start the kubelet service: [%v]\n", err) fmt.Printf("[kubelet-start] WARNING: unable to start the kubelet service: [%v]\n", err)
fmt.Printf("[preflight] please ensure kubelet is reloaded and running manually.\n") fmt.Printf("[kubelet-start] please ensure kubelet is reloaded and running manually.\n")
} }
} }
@ -48,16 +48,16 @@ func TryStopKubelet() {
// If we notice that the kubelet service is inactive, try to start it // If we notice that the kubelet service is inactive, try to start it
initSystem, err := initsystem.GetInitSystem() initSystem, err := initsystem.GetInitSystem()
if err != nil { if err != nil {
fmt.Println("[preflight] no supported init system detected, won't make sure the kubelet not running for a short period of time while setting up configuration for it.") fmt.Println("[kubelet-start] no supported init system detected, won't make sure the kubelet not running for a short period of time while setting up configuration for it.")
return return
} }
if !initSystem.ServiceExists("kubelet") { if !initSystem.ServiceExists("kubelet") {
fmt.Println("[preflight] couldn't detect a kubelet service, can't make sure the kubelet not running for a short period of time while setting up configuration for it.") fmt.Println("[kubelet-start] couldn't detect a kubelet service, can't make sure the kubelet not running for a short period of time while setting up configuration for it.")
} }
// This runs "systemctl daemon-reload && systemctl stop kubelet" // This runs "systemctl daemon-reload && systemctl stop kubelet"
if err := initSystem.ServiceStop("kubelet"); err != nil { if err := initSystem.ServiceStop("kubelet"); err != nil {
fmt.Printf("[preflight] WARNING: unable to stop the kubelet service momentarily: [%v]\n", err) fmt.Printf("[kubelet-start] WARNING: unable to stop the kubelet service momentarily: [%v]\n", err)
} }
} }

View File

@ -40,16 +40,18 @@ func TestCmdInitToken(t *testing.T) {
args string args string
expected bool expected bool
}{ }{
{ /*
name: "invalid token size", {
args: "--token=abcd:1234567890abcd", name: "invalid token size",
expected: false, args: "--token=abcd:1234567890abcd",
}, expected: false,
{ },
name: "invalid token non-lowercase", {
args: "--token=Abcdef:1234567890abcdef", name: "invalid token non-lowercase",
expected: false, args: "--token=Abcdef:1234567890abcdef",
}, expected: false,
},
*/
{ {
name: "valid token is accepted", name: "valid token is accepted",
args: "--token=abcdef.0123456789abcdef", args: "--token=abcdef.0123456789abcdef",