Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kubeadm: Graduate certs phase #70331

Merged
merged 2 commits into from
Oct 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 1 addition & 6 deletions cmd/kubeadm/app/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ func NewCmdInit(out io.Writer) *cobra.Command {

// initialize the workflow runner with the list of phases
initRunner.AppendPhase(phases.NewPreflightMasterPhase())
initRunner.AppendPhase(phases.NewCertsPhase())
initRunner.AppendPhase(phases.NewKubeletStartPhase())
// TODO: add other phases to the runner.

Expand Down Expand Up @@ -427,12 +428,6 @@ func runInit(i *initData, out io.Writer) error {

if res, _ := certsphase.UsingExternalCA(i.cfg); !res {

// PHASE 1: Generate certificates
glog.V(1).Infof("[init] creating PKI Assets")
if err := certsphase.CreatePKIAssets(i.cfg); err != nil {
return err
}

// PHASE 2: Generate kubeconfig files for the admin and the kubelet
glog.V(2).Infof("[init] generating kubeconfig files")
if err := kubeconfigphase.CreateInitKubeConfigFiles(kubeConfigDir, i.cfg); err != nil {
Expand Down
2 changes: 0 additions & 2 deletions cmd/kubeadm/app/cmd/phases/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ go_test(
name = "go_default_test",
srcs = [
"addons_test.go",
"certs_test.go",
"controlplane_test.go",
"etcd_test.go",
"kubeconfig_test.go",
Expand All @@ -84,7 +83,6 @@ go_test(
"//cmd/kubeadm/test:go_default_library",
"//cmd/kubeadm/test/cmd:go_default_library",
"//cmd/kubeadm/test/kubeconfig:go_default_library",
"//pkg/util/node:go_default_library",
"//pkg/version:go_default_library",
"//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
Expand Down
164 changes: 76 additions & 88 deletions cmd/kubeadm/app/cmd/phases/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ import (
"fmt"
"strings"

"github.com/pkg/errors"
"github.com/spf13/cobra"

kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
certscmdphase "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/certs"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
"k8s.io/kubernetes/pkg/util/normalizer"
)

Expand Down Expand Up @@ -66,6 +66,24 @@ var (
` + cmdutil.AlphaDisclaimer)
)

// certsData defines the behavior that a runtime data struct passed to the certs 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 certsData interface {
Cfg() *kubeadmapi.InitConfiguration
CertificateDir() string
CertificateWriteDir() string
}

// NewCertsPhase returns the phase for the certs
func NewCertsPhase() workflow.Phase {
return workflow.Phase{
Name: "certs",
Short: "Certificate generation",
Phases: getCertsSubPhases(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UX generation for subcommands is kind of fragmented: getCertsSubPhases, makePhase, makePhaseForCert, makePhasesForCA
IMO, as soon as run functions will be moved into functions as suggested below, the above set of functions can be simplified...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

}
}

// NewCmdCerts returns main command for certs phase
func NewCmdCerts() *cobra.Command {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need both a "cert" phase and a command. the phase will create/handle the command creation.
[2]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept this for the renewal, it's the only thing it contains, and it shouldn't go in the init workflow.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think the renew part needs to be synced with @fabriziopandini 's PR here:
#70333

maybe this PR should not touch it, but rather the other PR should perform the move.

Copy link
Contributor Author

@ereslibre ereslibre Oct 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will have a look on how to avoid this change at all so I don't introduce conflicts in #70333. @fabriziopandini how do you feel about this change? Should I avoid it completely?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ereslibre I'm fine with the current proposed implementation.
I will rebase #70333 after this one merge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed all changes related to this on this PR

cmd := &cobra.Command{
Expand All @@ -75,83 +93,72 @@ func NewCmdCerts() *cobra.Command {
Long: cmdutil.MacroCommandLongDescription,
}

cmd.AddCommand(getCertsSubCommands("")...)
cmd.AddCommand(getCertsSubCommands()...)
return cmd
}

// getCertsSubCommands returns sub commands for certs phase
func getCertsSubCommands(defaultKubernetesVersion string) []*cobra.Command {
func getCertsSubCommands() []*cobra.Command {
return []*cobra.Command{certscmdphase.NewCmdCertsRenewal()}
}

cfg := &kubeadmapiv1beta1.InitConfiguration{}
// getCertsSubPhases returns sub phases for certs phase
func getCertsSubPhases() []workflow.Phase {
subPhases := []workflow.Phase{}

// Default values for the cobra help text
kubeadmscheme.Scheme.Default(cfg)
certTree, _ := certsphase.GetDefaultCertList().AsMap().CertTree()

var cfgPath string
for ca, certList := range certTree {
caPhase := newCertSubPhase(ca, runCAPhase(ca))
subPhases = append(subPhases, caPhase)

// Special case commands
// All runs CreatePKIAssets, which isn't a particular certificate
allCmd := &cobra.Command{
Use: "all",
Short: "Generates all PKI assets necessary to establish the control plane",
Long: allCertsLongDesc,
Example: allCertsExample,
Run: func(cmd *cobra.Command, args []string) {
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
kubeadmutil.CheckErr(err)
for _, cert := range certList {
certPhase := newCertSubPhase(cert, runCertPhase(cert, ca))
subPhases = append(subPhases, certPhase)
}

err = certsphase.CreatePKIAssets(internalcfg)
kubeadmutil.CheckErr(err)
},
subPhases = append(subPhases, caPhase)
}
addFlags(allCmd, &cfgPath, cfg, true)

// SA creates the private/public key pair, which doesn't use x509 at all
saCmd := &cobra.Command{
Use: "sa",
saPhase := workflow.Phase{
Name: "sa",
Short: "Generates a private key for signing service account tokens along with its public key",
Long: saKeyLongDesc,
Run: func(cmd *cobra.Command, args []string) {
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
kubeadmutil.CheckErr(err)

err = certsphase.CreateServiceAccountKeyAndPublicKeyFiles(internalcfg)
kubeadmutil.CheckErr(err)
},
Run: runCertsSa,
}
addFlags(saCmd, &cfgPath, cfg, false)

// "renew" command
renewCmd := certscmdphase.NewCmdCertsRenewal()
subPhases = append(subPhases, saPhase)

subCmds := []*cobra.Command{allCmd, saCmd, renewCmd}

certTree, err := certsphase.GetDefaultCertList().AsMap().CertTree()
kubeadmutil.CheckErr(err)
return subPhases
}

for ca, certList := range certTree {
// Don't use pointers from for loops, they will be rewrittenb
caCmds := makeCommandsForCA(ca, certList, &cfgPath, cfg)
subCmds = append(subCmds, caCmds...)
func runCertsSa(c workflow.RunData) error {
data, ok := c.(certsData)
if !ok {
return errors.New("certs phase invoked with an invalid data struct")
}

return subCmds
cfg := data.Cfg()
cfg.CertificatesDir = data.CertificateWriteDir()
defer func() { cfg.CertificatesDir = data.CertificateDir() }()

return certsphase.CreateServiceAccountKeyAndPublicKeyFiles(cfg)
}

func makeCmd(certSpec *certsphase.KubeadmCert, cfgPath *string, cfg *kubeadmapiv1beta1.InitConfiguration) *cobra.Command {
cmd := &cobra.Command{
Use: certSpec.Name,
func newCertSubPhase(certSpec *certsphase.KubeadmCert, run func(c workflow.RunData) error) workflow.Phase {
phase := workflow.Phase{
Name: certSpec.Name,
Short: fmt.Sprintf("Generates the %s", certSpec.LongName),
Long: fmt.Sprintf(
genericLongDesc,
certSpec.LongName,
certSpec.BaseName,
getSANDescription(certSpec),
),
Run: run,
}
addFlags(cmd, cfgPath, cfg, certSpec.Name == "apiserver")
// Add flags to the command
return cmd
return phase
}

func getSANDescription(certSpec *certsphase.KubeadmCert) string {
Expand Down Expand Up @@ -188,51 +195,32 @@ func getSANDescription(certSpec *certsphase.KubeadmCert) string {
return fmt.Sprintf("\n\nDefault SANs are %s", strings.Join(sans, ", "))
}

func addFlags(cmd *cobra.Command, cfgPath *string, cfg *kubeadmapiv1beta1.InitConfiguration, addAPIFlags bool) {
options.AddCertificateDirFlag(cmd.Flags(), &cfg.CertificatesDir)
options.AddConfigFlag(cmd.Flags(), cfgPath)
if addAPIFlags {
cmd.Flags().StringVar(&cfg.Networking.DNSDomain, "service-dns-domain", cfg.Networking.DNSDomain, "Alternative domain for services, to use for the API server serving cert")
cmd.Flags().StringVar(&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet, "Alternative range of IP address for service VIPs, from which derives the internal API server VIP that will be added to the API Server serving cert")
cmd.Flags().StringSliceVar(&cfg.APIServerCertSANs, "apiserver-cert-extra-sans", []string{}, "Optional extra altnames to use for the API server serving cert. Can be both IP addresses and DNS names")
cmd.Flags().StringVar(&cfg.APIEndpoint.AdvertiseAddress, "apiserver-advertise-address", cfg.APIEndpoint.AdvertiseAddress, "The IP address the API server is accessible on, to use for the API server serving cert")
}
}

func makeCommandsForCA(ca *certsphase.KubeadmCert, certList []*certsphase.KubeadmCert, cfgPath *string, cfg *kubeadmapiv1beta1.InitConfiguration) []*cobra.Command {
subCmds := []*cobra.Command{}

caCmd := makeCmd(ca, cfgPath, cfg)
caCmd.Run = func(cmd *cobra.Command, args []string) {
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(*cfgPath, cfg)
kubeadmutil.CheckErr(err)

err = certsphase.CreateCACertAndKeyFiles(ca, internalcfg)
kubeadmutil.CheckErr(err)
}
func runCAPhase(ca *certsphase.KubeadmCert) func(c workflow.RunData) error {
return func(c workflow.RunData) error {
data, ok := c.(certsData)
if !ok {
return errors.New("certs phase invoked with an invalid data struct")
}

subCmds = append(subCmds, caCmd)
cfg := data.Cfg()
cfg.CertificatesDir = data.CertificateWriteDir()
defer func() { cfg.CertificatesDir = data.CertificateDir() }()

for _, cert := range certList {
certCmd := makeCommandForCert(cert, ca, cfgPath, cfg)
subCmds = append(subCmds, certCmd)
return certsphase.CreateCACertAndKeyFiles(ca, cfg)
}

return subCmds
}

func makeCommandForCert(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert, cfgPath *string, cfg *kubeadmapiv1beta1.InitConfiguration) *cobra.Command {
certCmd := makeCmd(cert, cfgPath, cfg)
func runCertPhase(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert) func(c workflow.RunData) error {
return func(c workflow.RunData) error {
data, ok := c.(certsData)
if !ok {
return errors.New("certs phase invoked with an invalid data struct")
}

certCmd.Run = func(cmd *cobra.Command, args []string) {
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(*cfgPath, cfg)
kubeadmutil.CheckErr(err)
err = configutil.VerifyAPIServerBindAddress(internalcfg.APIEndpoint.AdvertiseAddress)
kubeadmutil.CheckErr(err)
cfg := data.Cfg()
cfg.CertificatesDir = data.CertificateWriteDir()
defer func() { cfg.CertificatesDir = data.CertificateDir() }()

err = certsphase.CreateCertAndKeyFilesWithCA(cert, caCert, internalcfg)
kubeadmutil.CheckErr(err)
return certsphase.CreateCertAndKeyFilesWithCA(cert, caCert, cfg)
}

return certCmd
}