Skip to content

Commit

Permalink
kubeadm: Fully implement --dry-run
Browse files Browse the repository at this point in the history
  • Loading branch information
luxas committed Aug 22, 2017
1 parent a4b719d commit 29c3b61
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 94 deletions.
65 changes: 56 additions & 9 deletions cmd/kubeadm/app/cmd/init.go
Expand Up @@ -22,6 +22,7 @@ import (
"io/ioutil"
"os"
"text/template"
"path/filepath"
"time"

"github.com/renstrom/dedent"
Expand Down Expand Up @@ -55,6 +56,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/version"
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
)

var (
Expand Down Expand Up @@ -242,28 +244,40 @@ func (i *Init) Run(out io.Writer) error {
return fmt.Errorf("couldn't parse kubernetes version %q: %v", i.cfg.KubernetesVersion, err)
}

// Get directories to write files to; can be faked if we're dry-running
pkiDir, kubeConfigDir, manifestDir, err := getDirectoriesToUse(i.dryRun, i.cfg.CertificatesDir)
if err != nil {
return err
}

adminKubeConfigPath := filepath.Join(kubeConfigDir, kubeadmconstants.AdminKubeConfigFileName)

// PHASE 1: Generate certificates
if err := certsphase.CreatePKIAssets(i.cfg); err != nil {
if err := certsphase.CreatePKIAssets(pkiDir, i.cfg); err != nil {
return err
}

// PHASE 2: Generate kubeconfig files for the admin and the kubelet
if err := kubeconfigphase.CreateInitKubeConfigFiles(kubeadmconstants.KubernetesDir, i.cfg); err != nil {
if err := kubeconfigphase.CreateInitKubeConfigFiles(kubeConfigDir, pkiDir, i.cfg); err != nil {
return err
}

// PHASE 3: Bootstrap the control plane
manifestPath := kubeadmconstants.GetStaticPodDirectory()
if err := controlplanephase.CreateInitStaticPodManifestFiles(manifestPath, i.cfg); err != nil {
if err := controlplanephase.CreateInitStaticPodManifestFiles(manifestDir, i.cfg); err != nil {
return err
}
// Add etcd static pod spec only if external etcd is not configured
if len(i.cfg.Etcd.Endpoints) == 0 {
if err := etcdphase.CreateLocalEtcdStaticPodManifestFile(manifestPath, i.cfg); err != nil {
if err := etcdphase.CreateLocalEtcdStaticPodManifestFile(manifestDir, i.cfg); err != nil {
return err
}
}

// If we're dry-running, print the generated manifests
if err := printFilesIfDryRunning(i.dryRun, manifestDir); err != nil {
return err
}

client, err := createClientsetAndOptionallyWaitForReady(i.cfg, i.dryRun)
if err != nil {
return err
Expand Down Expand Up @@ -294,7 +308,7 @@ func (i *Init) Run(out io.Writer) error {
}

// Create the cluster-info ConfigMap with the associated RBAC rules
if err := clusterinfophase.CreateBootstrapConfigMapIfNotExists(client, kubeadmconstants.GetAdminKubeConfigPath()); err != nil {
if err := clusterinfophase.CreateBootstrapConfigMapIfNotExists(client, adminKubeConfigPath); err != nil {
return err
}
if err := clusterinfophase.CreateClusterInfoRBACRules(client); err != nil {
Expand Down Expand Up @@ -331,7 +345,7 @@ func (i *Init) Run(out io.Writer) error {
}

// Load the CA certificate from so we can pin its public key
caCert, err := pkiutil.TryLoadCertFromDisk(i.cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
caCert, err := pkiutil.TryLoadCertFromDisk(pkiDir, kubeadmconstants.CACertAndKeyBaseName)

// Generate the Master host/port pair used by initDoneTempl
masterHostPort, err := kubeadmutil.GetMasterHostPort(i.cfg)
Expand All @@ -340,8 +354,7 @@ func (i *Init) Run(out io.Writer) error {
}

ctx := map[string]string{
"KubeConfigPath": kubeadmconstants.GetAdminKubeConfigPath(),
"KubeConfigName": kubeadmconstants.AdminKubeConfigFileName,
"KubeConfigPath": adminKubeConfigPath,
"Token": i.cfg.Token,
"CAPubKeyPin": pubkeypin.Hash(caCert),
"MasterHostPort": masterHostPort,
Expand Down Expand Up @@ -374,3 +387,37 @@ func createClientsetAndOptionallyWaitForReady(cfg *kubeadmapi.MasterConfiguratio
}
return client, nil
}

func getDirectoriesToUse(dryRun bool, defaultPkiDir string) (string, string, string, error) {

if dryRun {
dryRunDir, err := ioutil.TempDir("", "kubeadm-init-dryrun")
if err != nil {
return "", "", "", fmt.Errorf("couldn't create a temporary directory for the upgrade: %v")
}
// Use the same temp dir for all
return dryRunDir, dryRunDir, dryRunDir, nil
}

return defaultPkiDir, kubeadmconstants.KubernetesDir, kubeadmconstants.GetStaticPodDirectory(), nil
}

func printFilesIfDryRunning(dryRun bool, manifestDir string) error {
if !dryRun {
return nil
}

fmt.Printf("[dryrun] Wrote certificates, kubeconfig files and control plane manifests to %q\n", manifestDir)
fmt.Println("[dryrun] Won't print certificates or kubeconfig files due to the sensitive nature of them")
fmt.Println("[dryrun] Please go an examine the %q directory for details about what would be written\n", manifestDir)

// Print the contents of the upgraded manifests and pretend like they were in /etc/kubernetes/manifests
files := []dryrunutil.DryRunFile{}
for _, component := range kubeadmconstants.MasterComponents {
realPath := kubeadmconstants.GetStaticPodFilepath(component, manifestDir)
outputPath := kubeadmconstants.GetStaticPodFilepath(component, kubeadmconstants.GetStaticPodDirectory())
files = append(files, dryrunutil.NewDryRunFile(realPath, outputPath))
}

return dryrunutil.DryRunPrintFileContents(files, os.Stdout)
}
8 changes: 4 additions & 4 deletions cmd/kubeadm/app/cmd/phases/certs.go
Expand Up @@ -54,7 +54,7 @@ func getCertsSubCommands() []*cobra.Command {
subCmdProperties := []struct {
use string
short string
cmdFunc func(cfg *kubeadmapi.MasterConfiguration) error
cmdFunc func(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error
}{
{
use: "all",
Expand All @@ -64,7 +64,7 @@ func getCertsSubCommands() []*cobra.Command {
{
use: "ca",
short: "Generate CA certificate and key for a Kubernetes cluster.",
cmdFunc: certsphase.CreateCACertAndKeyfiles,
cmdFunc: certsphase.CreateCACertAndKeyFiles,
},
{
use: "apiserver",
Expand Down Expand Up @@ -118,7 +118,7 @@ func getCertsSubCommands() []*cobra.Command {
}

// runCmdFunc creates a cobra.Command Run function, by composing the call to the given cmdFunc with necessary additional steps (e.g preparation of input parameters)
func runCmdFunc(cmdFunc func(cfg *kubeadmapi.MasterConfiguration) error, cfgPath *string, cfg *kubeadmapiext.MasterConfiguration) func(cmd *cobra.Command, args []string) {
func runCmdFunc(cmdFunc func(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error, cfgPath *string, cfg *kubeadmapiext.MasterConfiguration) func(cmd *cobra.Command, args []string) {

// the following statement build a clousure that wraps a call to a cmdFunc, binding
// the function itself with the specific parameters of each sub command.
Expand All @@ -135,7 +135,7 @@ func runCmdFunc(cmdFunc func(cfg *kubeadmapi.MasterConfiguration) error, cfgPath
kubeadmutil.CheckErr(err)

// Execute the cmdFunc
err = cmdFunc(internalcfg)
err = cmdFunc(internalcfg.CertificatesDir, internalcfg)
kubeadmutil.CheckErr(err)
}
}
12 changes: 7 additions & 5 deletions cmd/kubeadm/app/cmd/phases/kubeconfig.go
Expand Up @@ -54,7 +54,7 @@ func getKubeConfigSubCommands(out io.Writer, outDir string) []*cobra.Command {
subCmdProperties := []struct {
use string
short string
cmdFunc func(outDir string, cfg *kubeadmapi.MasterConfiguration) error
cmdFunc func(outDir, certDir string, cfg *kubeadmapi.MasterConfiguration) error
}{
{
use: "all",
Expand Down Expand Up @@ -84,18 +84,18 @@ func getKubeConfigSubCommands(out io.Writer, outDir string) []*cobra.Command {
{
use: "user",
short: "Outputs a kubeconfig file for an additional user.",
cmdFunc: func(outDir string, cfg *kubeadmapi.MasterConfiguration) error {
cmdFunc: func(outDir, certDir string, cfg *kubeadmapi.MasterConfiguration) error {
if clientName == "" {
return fmt.Errorf("missing required argument client-name")
}

// if the kubeconfig file for an additional user has to use a token, use it
if token != "" {
return kubeconfigphase.WriteKubeConfigWithToken(out, cfg, clientName, token)
return kubeconfigphase.WriteKubeConfigWithToken(out, certDir, cfg, clientName, token)
}

// Otherwise, write a kubeconfig file with a generate client cert
return kubeconfigphase.WriteKubeConfigWithClientCert(out, cfg, clientName)
return kubeconfigphase.WriteKubeConfigWithClientCert(out, certDir, cfg, clientName)
},
},
}
Expand All @@ -105,7 +105,9 @@ func getKubeConfigSubCommands(out io.Writer, outDir string) []*cobra.Command {
cmd := &cobra.Command{
Use: properties.use,
Short: properties.short,
Run: runCmdPhase(properties.cmdFunc, &outDir, &cfgPath, cfg),
Run: runCmdPhase(func(outDir string, cfg *kubeadmapi.MasterConfiguration) error {
return properties.cmdFunc(outDir, cfg.CertificatesDir, cfg)
}, &outDir, &cfgPath, cfg),
}

// Add flags to the command
Expand Down
55 changes: 22 additions & 33 deletions cmd/kubeadm/app/phases/certs/certs.go
Expand Up @@ -32,10 +32,10 @@ import (

// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
// If the PKI assets already exists in the target folder, they are used only if evaluated equal; otherwise an error is returned.
func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration) error {
func CreatePKIAssets(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error {

certActions := []func(cfg *kubeadmapi.MasterConfiguration) error{
CreateCACertAndKeyfiles,
certActions := []func(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error{
CreateCACertAndKeyFiles,
CreateAPIServerCertAndKeyFiles,
CreateAPIServerKubeletClientCertAndKeyFiles,
CreateServiceAccountKeyAndPublicKeyFiles,
Expand All @@ -44,28 +44,28 @@ func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration) error {
}

for _, action := range certActions {
err := action(cfg)
err := action(pkiDir, cfg)
if err != nil {
return err
}
}

fmt.Printf("[certificates] Valid certificates and keys now exist in %q\n", cfg.CertificatesDir)
fmt.Printf("[certificates] Valid certificates and keys now exist in %q\n", pkiDir)

return nil
}

// CreateCACertAndKeyfiles create a new self signed CA certificate and key files.
// CreateCACertAndKeyFiles create a new self signed CA certificate and key files.
// If the CA certificate and key files already exists in the target folder, they are used only if evaluated equal; otherwise an error is returned.
func CreateCACertAndKeyfiles(cfg *kubeadmapi.MasterConfiguration) error {
func CreateCACertAndKeyFiles(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error {

caCert, caKey, err := NewCACertAndKey()
if err != nil {
return err
}

return writeCertificateAuthorithyFilesIfNotExist(
cfg.CertificatesDir,
pkiDir,
kubeadmconstants.CACertAndKeyBaseName,
caCert,
caKey,
Expand All @@ -75,9 +75,9 @@ func CreateCACertAndKeyfiles(cfg *kubeadmapi.MasterConfiguration) error {
// CreateAPIServerCertAndKeyFiles create a new certificate and key files for the apiserver.
// If the apiserver certificate and key files already exists in the target folder, they are used only if evaluated equal; otherwise an error is returned.
// It assumes the cluster CA certificate and key files should exists into the CertificatesDir
func CreateAPIServerCertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration) error {
func CreateAPIServerCertAndKeyFiles(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error {

caCert, caKey, err := loadCertificateAuthorithy(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
caCert, caKey, err := loadCertificateAuthorithy(pkiDir, kubeadmconstants.CACertAndKeyBaseName)
if err != nil {
return err
}
Expand All @@ -88,7 +88,7 @@ func CreateAPIServerCertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration) error {
}

return writeCertificateFilesIfNotExist(
cfg.CertificatesDir,
pkiDir,
kubeadmconstants.APIServerCertAndKeyBaseName,
caCert,
apiCert,
Expand All @@ -99,9 +99,9 @@ func CreateAPIServerCertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration) error {
// CreateAPIServerKubeletClientCertAndKeyFiles create a new CA certificate for kubelets calling apiserver
// If the apiserver-kubelet-client certificate and key files already exists in the target folder, they are used only if evaluated equals; otherwise an error is returned.
// It assumes the cluster CA certificate and key files should exists into the CertificatesDir
func CreateAPIServerKubeletClientCertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration) error {
func CreateAPIServerKubeletClientCertAndKeyFiles(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error {

caCert, caKey, err := loadCertificateAuthorithy(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
caCert, caKey, err := loadCertificateAuthorithy(pkiDir, kubeadmconstants.CACertAndKeyBaseName)
if err != nil {
return err
}
Expand All @@ -112,7 +112,7 @@ func CreateAPIServerKubeletClientCertAndKeyFiles(cfg *kubeadmapi.MasterConfigura
}

return writeCertificateFilesIfNotExist(
cfg.CertificatesDir,
pkiDir,
kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName,
caCert,
apiClientCert,
Expand All @@ -122,15 +122,15 @@ func CreateAPIServerKubeletClientCertAndKeyFiles(cfg *kubeadmapi.MasterConfigura

// CreateServiceAccountKeyAndPublicKeyFiles create a new public/private key files for signing service account users.
// If the sa public/private key files already exists in the target folder, they are used only if evaluated equals; otherwise an error is returned.
func CreateServiceAccountKeyAndPublicKeyFiles(cfg *kubeadmapi.MasterConfiguration) error {
func CreateServiceAccountKeyAndPublicKeyFiles(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error {

saSigningKey, err := NewServiceAccountSigningKey()
if err != nil {
return err
}

return writeKeyFilesIfNotExist(
cfg.CertificatesDir,
pkiDir,
kubeadmconstants.ServiceAccountKeyBaseName,
saSigningKey,
)
Expand All @@ -141,15 +141,15 @@ func CreateServiceAccountKeyAndPublicKeyFiles(cfg *kubeadmapi.MasterConfiguratio
// without the client cert; This is a separte CA, so that front proxy identities cannot hit the API and normal client certs cannot be used
// as front proxies.
// If the front proxy CA certificate and key files already exists in the target folder, they are used only if evaluated equals; otherwise an error is returned.
func CreateFrontProxyCACertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration) error {
func CreateFrontProxyCACertAndKeyFiles(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error {

frontProxyCACert, frontProxyCAKey, err := NewFrontProxyCACertAndKey()
frontProxyCACert, frontProxyCAKey, err := NewCACertAndKey()
if err != nil {
return err
}

return writeCertificateAuthorithyFilesIfNotExist(
cfg.CertificatesDir,
pkiDir,
kubeadmconstants.FrontProxyCACertAndKeyBaseName,
frontProxyCACert,
frontProxyCAKey,
Expand All @@ -159,9 +159,9 @@ func CreateFrontProxyCACertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration) erro
// CreateFrontProxyClientCertAndKeyFiles create a new certificate for proxy server client.
// If the front-proxy-client certificate and key files already exists in the target folder, they are used only if evaluated equals; otherwise an error is returned.
// It assumes the front proxy CAA certificate and key files should exists into the CertificatesDir
func CreateFrontProxyClientCertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration) error {
func CreateFrontProxyClientCertAndKeyFiles(pkiDir string, cfg *kubeadmapi.MasterConfiguration) error {

frontProxyCACert, frontProxyCAKey, err := loadCertificateAuthorithy(cfg.CertificatesDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName)
frontProxyCACert, frontProxyCAKey, err := loadCertificateAuthorithy(pkiDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName)
if err != nil {
return err
}
Expand All @@ -172,7 +172,7 @@ func CreateFrontProxyClientCertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration)
}

return writeCertificateFilesIfNotExist(
cfg.CertificatesDir,
pkiDir,
kubeadmconstants.FrontProxyClientCertAndKeyBaseName,
frontProxyCACert,
frontProxyClientCert,
Expand Down Expand Up @@ -240,17 +240,6 @@ func NewServiceAccountSigningKey() (*rsa.PrivateKey, error) {
return saSigningKey, nil
}

// NewFrontProxyCACertAndKey generate a self signed front proxy CA.
func NewFrontProxyCACertAndKey() (*x509.Certificate, *rsa.PrivateKey, error) {

frontProxyCACert, frontProxyCAKey, err := pkiutil.NewCertificateAuthority()
if err != nil {
return nil, nil, fmt.Errorf("failure while generating front-proxy CA certificate and key: %v", err)
}

return frontProxyCACert, frontProxyCAKey, nil
}

// NewFrontProxyClientCertAndKey generate CA certificate for proxy server client, signed by the given front proxy CA.
func NewFrontProxyClientCertAndKey(frontProxyCACert *x509.Certificate, frontProxyCAKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) {

Expand Down

0 comments on commit 29c3b61

Please sign in to comment.