-
Notifications
You must be signed in to change notification settings - Fork 38.7k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
) | ||
|
||
|
@@ -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(), | ||
} | ||
} | ||
|
||
// NewCmdCerts returns main command for certs phase | ||
func NewCmdCerts() *cobra.Command { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: maybe this PR should not touch it, but rather the other PR should perform the move. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ereslibre I'm fine with the current proposed implementation. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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{ | ||
|
@@ -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 { | ||
|
@@ -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 | ||
} |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated