mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
kubeadm graduate kubeconfig phase
This commit is contained in:
parent
03a145de8a
commit
3cadb3ca1d
@ -50,7 +50,6 @@ import (
|
|||||||
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||||
controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
|
controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
|
||||||
etcdphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/etcd"
|
etcdphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/etcd"
|
||||||
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
|
||||||
kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
|
kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
|
||||||
markmasterphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/markmaster"
|
markmasterphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/markmaster"
|
||||||
patchnodephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/patchnode"
|
patchnodephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/patchnode"
|
||||||
@ -130,6 +129,7 @@ type initData struct {
|
|||||||
ignorePreflightErrors sets.String
|
ignorePreflightErrors sets.String
|
||||||
certificatesDir string
|
certificatesDir string
|
||||||
dryRunDir string
|
dryRunDir string
|
||||||
|
externalCA bool
|
||||||
client clientset.Interface
|
client clientset.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ func NewCmdInit(out io.Writer) *cobra.Command {
|
|||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
data := c.(initData)
|
data := c.(initData)
|
||||||
fmt.Printf("[init] using Kubernetes version: %s\n", data.cfg.KubernetesVersion)
|
fmt.Printf("[init] Using Kubernetes version: %s\n", data.cfg.KubernetesVersion)
|
||||||
|
|
||||||
err = initRunner.Run()
|
err = initRunner.Run()
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
@ -166,8 +166,9 @@ 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.NewCertsPhase())
|
|
||||||
initRunner.AppendPhase(phases.NewKubeletStartPhase())
|
initRunner.AppendPhase(phases.NewKubeletStartPhase())
|
||||||
|
initRunner.AppendPhase(phases.NewCertsPhase())
|
||||||
|
initRunner.AppendPhase(phases.NewKubeConfigPhase())
|
||||||
// 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
|
||||||
@ -313,6 +314,9 @@ func newInitData(cmd *cobra.Command, options *initOptions) (initData, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if an external CA is provided by the user.
|
||||||
|
externalCA, _ := certsphase.UsingExternalCA(cfg)
|
||||||
|
|
||||||
return initData{
|
return initData{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
certificatesDir: cfg.CertificatesDir,
|
certificatesDir: cfg.CertificatesDir,
|
||||||
@ -320,6 +324,7 @@ func newInitData(cmd *cobra.Command, options *initOptions) (initData, error) {
|
|||||||
dryRun: options.dryRun,
|
dryRun: options.dryRun,
|
||||||
dryRunDir: dryRunDir,
|
dryRunDir: dryRunDir,
|
||||||
ignorePreflightErrors: ignorePreflightErrorsSet,
|
ignorePreflightErrors: ignorePreflightErrorsSet,
|
||||||
|
externalCA: externalCA,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,6 +385,11 @@ func (d initData) KubeletDir() string {
|
|||||||
return kubeadmconstants.KubeletRunDirectory
|
return kubeadmconstants.KubeletRunDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExternalCA returns true if an external CA is provided by the user.
|
||||||
|
func (d initData) ExternalCA() bool {
|
||||||
|
return d.externalCA
|
||||||
|
}
|
||||||
|
|
||||||
// Client returns a Kubernetes client to be used by kubeadm.
|
// Client returns a Kubernetes client to be used by kubeadm.
|
||||||
// This function is implemented as a singleton, thus avoiding to recreate the client when it is used by different phases.
|
// This function is implemented as a singleton, thus avoiding to recreate the client when it is used by different phases.
|
||||||
// Important. This function must be called after the admin.conf kubeconfig file is created.
|
// Important. This function must be called after the admin.conf kubeconfig file is created.
|
||||||
@ -426,18 +436,6 @@ func runInit(i *initData, out io.Writer) error {
|
|||||||
|
|
||||||
adminKubeConfigPath := filepath.Join(kubeConfigDir, kubeadmconstants.AdminKubeConfigFileName)
|
adminKubeConfigPath := filepath.Join(kubeConfigDir, kubeadmconstants.AdminKubeConfigFileName)
|
||||||
|
|
||||||
if res, _ := certsphase.UsingExternalCA(i.cfg); !res {
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
fmt.Println("[externalca] the file 'ca.key' was not found, yet all other certificates are present. Using external CA mode - certificates or kubeconfig will not be generated")
|
|
||||||
}
|
|
||||||
|
|
||||||
if features.Enabled(i.cfg.FeatureGates, features.Auditing) {
|
if features.Enabled(i.cfg.FeatureGates, features.Auditing) {
|
||||||
// Setup the AuditPolicy (either it was passed in and exists or it wasn't passed in and generate a default policy)
|
// Setup the AuditPolicy (either it was passed in and exists or it wasn't passed in and generate a default policy)
|
||||||
if i.cfg.AuditPolicyConfiguration.Path != "" {
|
if i.cfg.AuditPolicyConfiguration.Path != "" {
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||||
@ -36,23 +35,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
allCertsLongDesc = normalizer.LongDesc(`
|
|
||||||
Generates a self-signed CA to provision identities for each component in the cluster (including nodes)
|
|
||||||
and client certificates to be used by various components.
|
|
||||||
|
|
||||||
If a given certificate and private key pair both exist, kubeadm skips the generation step and
|
|
||||||
existing files will be used.
|
|
||||||
` + cmdutil.AlphaDisclaimer)
|
|
||||||
|
|
||||||
allCertsExample = normalizer.Examples(`
|
|
||||||
# Creates all PKI assets necessary to establish the control plane,
|
|
||||||
# functionally equivalent to what generated by kubeadm init.
|
|
||||||
kubeadm alpha phase certs all
|
|
||||||
|
|
||||||
# Creates all PKI assets using options read from a configuration file.
|
|
||||||
kubeadm alpha phase certs all --config masterconfiguration.yaml
|
|
||||||
`)
|
|
||||||
|
|
||||||
saKeyLongDesc = fmt.Sprintf(normalizer.LongDesc(`
|
saKeyLongDesc = fmt.Sprintf(normalizer.LongDesc(`
|
||||||
Generates the private key for signing service account tokens along with its public key, and saves them into
|
Generates the private key for signing service account tokens along with its public key, and saves them into
|
||||||
%s and %s files.
|
%s and %s files.
|
||||||
@ -71,6 +53,7 @@ var (
|
|||||||
// (and thus with different runtime data struct, all of them requested to be compliant to this interface)
|
// (and thus with different runtime data struct, all of them requested to be compliant to this interface)
|
||||||
type certsData interface {
|
type certsData interface {
|
||||||
Cfg() *kubeadmapi.InitConfiguration
|
Cfg() *kubeadmapi.InitConfiguration
|
||||||
|
ExternalCA() bool
|
||||||
CertificateDir() string
|
CertificateDir() string
|
||||||
CertificateWriteDir() string
|
CertificateWriteDir() string
|
||||||
}
|
}
|
||||||
@ -80,7 +63,8 @@ func NewCertsPhase() workflow.Phase {
|
|||||||
return workflow.Phase{
|
return workflow.Phase{
|
||||||
Name: "certs",
|
Name: "certs",
|
||||||
Short: "Certificate generation",
|
Short: "Certificate generation",
|
||||||
Phases: getCertsSubPhases(),
|
Phases: newCertSubPhases(),
|
||||||
|
Run: runCerts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,8 +86,8 @@ func getCertsSubCommands() []*cobra.Command {
|
|||||||
return []*cobra.Command{certscmdphase.NewCmdCertsRenewal()}
|
return []*cobra.Command{certscmdphase.NewCmdCertsRenewal()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCertsSubPhases returns sub phases for certs phase
|
// newCertSubPhases returns sub phases for certs phase
|
||||||
func getCertsSubPhases() []workflow.Phase {
|
func newCertSubPhases() []workflow.Phase {
|
||||||
subPhases := []workflow.Phase{}
|
subPhases := []workflow.Phase{}
|
||||||
|
|
||||||
certTree, _ := certsphase.GetDefaultCertList().AsMap().CertTree()
|
certTree, _ := certsphase.GetDefaultCertList().AsMap().CertTree()
|
||||||
@ -116,8 +100,6 @@ func getCertsSubPhases() []workflow.Phase {
|
|||||||
certPhase := newCertSubPhase(cert, runCertPhase(cert, ca))
|
certPhase := newCertSubPhase(cert, runCertPhase(cert, ca))
|
||||||
subPhases = append(subPhases, certPhase)
|
subPhases = append(subPhases, certPhase)
|
||||||
}
|
}
|
||||||
|
|
||||||
subPhases = append(subPhases, caPhase)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SA creates the private/public key pair, which doesn't use x509 at all
|
// SA creates the private/public key pair, which doesn't use x509 at all
|
||||||
@ -133,19 +115,6 @@ func getCertsSubPhases() []workflow.Phase {
|
|||||||
return subPhases
|
return subPhases
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCertsSa(c workflow.RunData) error {
|
|
||||||
data, ok := c.(certsData)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("certs phase invoked with an invalid data struct")
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := data.Cfg()
|
|
||||||
cfg.CertificatesDir = data.CertificateWriteDir()
|
|
||||||
defer func() { cfg.CertificatesDir = data.CertificateDir() }()
|
|
||||||
|
|
||||||
return certsphase.CreateServiceAccountKeyAndPublicKeyFiles(cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCertSubPhase(certSpec *certsphase.KubeadmCert, run func(c workflow.RunData) error) workflow.Phase {
|
func newCertSubPhase(certSpec *certsphase.KubeadmCert, run func(c workflow.RunData) error) workflow.Phase {
|
||||||
phase := workflow.Phase{
|
phase := workflow.Phase{
|
||||||
Name: certSpec.Name,
|
Name: certSpec.Name,
|
||||||
@ -195,6 +164,37 @@ func getSANDescription(certSpec *certsphase.KubeadmCert) string {
|
|||||||
return fmt.Sprintf("\n\nDefault SANs are %s", strings.Join(sans, ", "))
|
return fmt.Sprintf("\n\nDefault SANs are %s", strings.Join(sans, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runCertsSa(c workflow.RunData) error {
|
||||||
|
data, ok := c.(certsData)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("certs phase invoked with an invalid data struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if external CA mode, skip service account key generation
|
||||||
|
if data.ExternalCA() {
|
||||||
|
fmt.Printf("[certs] External CA mode: Using existing sa keys\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if dryrunning, write certificates to a temporary folder (and defer restore to the path originally specified by the user)
|
||||||
|
cfg := data.Cfg()
|
||||||
|
cfg.CertificatesDir = data.CertificateWriteDir()
|
||||||
|
defer func() { cfg.CertificatesDir = data.CertificateDir() }()
|
||||||
|
|
||||||
|
// create the new service account key (or use existing)
|
||||||
|
return certsphase.CreateServiceAccountKeyAndPublicKeyFiles(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCerts(c workflow.RunData) error {
|
||||||
|
data, ok := c.(certsData)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("certs phase invoked with an invalid data struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[certs] Using certificateDir folder %q\n", data.CertificateWriteDir())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func runCAPhase(ca *certsphase.KubeadmCert) func(c workflow.RunData) error {
|
func runCAPhase(ca *certsphase.KubeadmCert) func(c workflow.RunData) error {
|
||||||
return func(c workflow.RunData) error {
|
return func(c workflow.RunData) error {
|
||||||
data, ok := c.(certsData)
|
data, ok := c.(certsData)
|
||||||
@ -202,10 +202,24 @@ func runCAPhase(ca *certsphase.KubeadmCert) func(c workflow.RunData) error {
|
|||||||
return errors.New("certs phase invoked with an invalid data struct")
|
return errors.New("certs phase invoked with an invalid data struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if external CA mode, skips certificate authority generation
|
||||||
|
if data.ExternalCA() {
|
||||||
|
fmt.Printf("[certs] External CA mode: Using existing %s certificate authority\n", ca.BaseName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if using external etcd, skips etcd certificate authority generation
|
||||||
|
if data.Cfg().Etcd.External != nil && ca.Name == "etcd-ca" {
|
||||||
|
fmt.Printf("[certs] External etcd mode: Skipping %s certificate authority generation\n", ca.BaseName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if dryrunning, write certificates authority to a temporary folder (and defer restore to the path originally specified by the user)
|
||||||
cfg := data.Cfg()
|
cfg := data.Cfg()
|
||||||
cfg.CertificatesDir = data.CertificateWriteDir()
|
cfg.CertificatesDir = data.CertificateWriteDir()
|
||||||
defer func() { cfg.CertificatesDir = data.CertificateDir() }()
|
defer func() { cfg.CertificatesDir = data.CertificateDir() }()
|
||||||
|
|
||||||
|
// create the new certificate authority (or use existing)
|
||||||
return certsphase.CreateCACertAndKeyFiles(ca, cfg)
|
return certsphase.CreateCACertAndKeyFiles(ca, cfg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,10 +231,24 @@ func runCertPhase(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert)
|
|||||||
return errors.New("certs phase invoked with an invalid data struct")
|
return errors.New("certs phase invoked with an invalid data struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if external CA mode, skip certificate generation
|
||||||
|
if data.ExternalCA() {
|
||||||
|
fmt.Printf("[certs] External CA mode: Using existing %s certificate\n", cert.BaseName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if using external etcd, skips etcd certificates generation
|
||||||
|
if data.Cfg().Etcd.External != nil && cert.CAName == "etcd-ca" {
|
||||||
|
fmt.Printf("[certs] External etcd mode: Skipping %s certificate authority generation\n", cert.BaseName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if dryrunning, write certificates to a temporary folder (and defer restore to the path originally specified by the user)
|
||||||
cfg := data.Cfg()
|
cfg := data.Cfg()
|
||||||
cfg.CertificatesDir = data.CertificateWriteDir()
|
cfg.CertificatesDir = data.CertificateWriteDir()
|
||||||
defer func() { cfg.CertificatesDir = data.CertificateDir() }()
|
defer func() { cfg.CertificatesDir = data.CertificateDir() }()
|
||||||
|
|
||||||
|
// create the new certificate (or use existing)
|
||||||
return certsphase.CreateCertAndKeyFilesWithCA(cert, caCert, cfg)
|
return certsphase.CreateCertAndKeyFilesWithCA(cert, caCert, cfg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,14 +19,13 @@ package phases
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||||
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/phases/workflow"
|
||||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||||
@ -34,62 +33,136 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
allKubeconfigLongDesc = normalizer.LongDesc(`
|
kubeconfigLongDesc = normalizer.LongDesc(`
|
||||||
Generates all kubeconfig files necessary to establish the control plane and the admin kubeconfig file.
|
kubeconfig file utilities.
|
||||||
` + cmdutil.AlphaDisclaimer)
|
` + cmdutil.AlphaDisclaimer)
|
||||||
|
|
||||||
allKubeconfigExample = normalizer.Examples(`
|
|
||||||
# Generates all kubeconfig files, functionally equivalent to what generated
|
|
||||||
# by kubeadm init.
|
|
||||||
kubeadm alpha phase kubeconfig all
|
|
||||||
|
|
||||||
# Generates all kubeconfig files using options read from a configuration file.
|
|
||||||
kubeadm alpha phase kubeconfig all --config masterconfiguration.yaml
|
|
||||||
`)
|
|
||||||
|
|
||||||
adminKubeconfigLongDesc = fmt.Sprintf(normalizer.LongDesc(`
|
|
||||||
Generates the kubeconfig file for the admin and for kubeadm itself, and saves it to %s file.
|
|
||||||
`+cmdutil.AlphaDisclaimer), kubeadmconstants.AdminKubeConfigFileName)
|
|
||||||
|
|
||||||
kubeletKubeconfigLongDesc = fmt.Sprintf(normalizer.LongDesc(`
|
|
||||||
Generates the kubeconfig file for the kubelet to use and saves it to %s file.
|
|
||||||
|
|
||||||
Please note that this should *only* be used for bootstrapping purposes. After your control plane is up,
|
|
||||||
you should request all kubelet credentials from the CSR API.
|
|
||||||
`+cmdutil.AlphaDisclaimer), filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletKubeConfigFileName))
|
|
||||||
|
|
||||||
controllerManagerKubeconfigLongDesc = fmt.Sprintf(normalizer.LongDesc(`
|
|
||||||
Generates the kubeconfig file for the controller manager to use and saves it to %s file.
|
|
||||||
`+cmdutil.AlphaDisclaimer), filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ControllerManagerKubeConfigFileName))
|
|
||||||
|
|
||||||
schedulerKubeconfigLongDesc = fmt.Sprintf(normalizer.LongDesc(`
|
|
||||||
Generates the kubeconfig file for the scheduler to use and saves it to %s file.
|
|
||||||
`+cmdutil.AlphaDisclaimer), filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.SchedulerKubeConfigFileName))
|
|
||||||
|
|
||||||
userKubeconfigLongDesc = normalizer.LongDesc(`
|
userKubeconfigLongDesc = normalizer.LongDesc(`
|
||||||
Outputs a kubeconfig file for an additional user.
|
Outputs a kubeconfig file for an additional user.
|
||||||
` + cmdutil.AlphaDisclaimer)
|
` + cmdutil.AlphaDisclaimer)
|
||||||
|
|
||||||
userKubeconfigExample = normalizer.Examples(`
|
userKubeconfigExample = normalizer.Examples(`
|
||||||
# Outputs a kubeconfig file for an additional user named foo
|
# Outputs a kubeconfig file for an additional user named foo
|
||||||
kubeadm alpha phase kubeconfig user --client-name=foo
|
kubeadm alpha kubeconfig user --client-name=foo
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
kubeconfigFilePhaseProperties = map[string]struct {
|
||||||
|
name string
|
||||||
|
short string
|
||||||
|
long string
|
||||||
|
}{
|
||||||
|
kubeadmconstants.AdminKubeConfigFileName: {
|
||||||
|
name: "admin",
|
||||||
|
short: "Generates a kubeconfig file for the admin to use and for kubeadm itself",
|
||||||
|
long: "Generates the kubeconfig file for the admin and for kubeadm itself, and saves it to %s file.",
|
||||||
|
},
|
||||||
|
kubeadmconstants.KubeletKubeConfigFileName: {
|
||||||
|
name: "kubelet",
|
||||||
|
short: "Generates a kubeconfig file for the kubelet to use *only* for cluster bootstrapping purposes",
|
||||||
|
long: normalizer.LongDesc(`
|
||||||
|
Generates the kubeconfig file for the kubelet to use and saves it to %s file.
|
||||||
|
|
||||||
|
Please note that this should *only* be used for cluster bootstrapping purposes. After your control plane is up,
|
||||||
|
you should request all kubelet credentials from the CSR API.`),
|
||||||
|
},
|
||||||
|
kubeadmconstants.ControllerManagerKubeConfigFileName: {
|
||||||
|
name: "controller-manager",
|
||||||
|
short: "Generates a kubeconfig file for the controller manager to use",
|
||||||
|
long: "Generates the kubeconfig file for the controller manager to use and saves it to %s file",
|
||||||
|
},
|
||||||
|
kubeadmconstants.SchedulerKubeConfigFileName: {
|
||||||
|
name: "scheduler",
|
||||||
|
short: "Generates a kubeconfig file for the scheduler to use",
|
||||||
|
long: "Generates the kubeconfig file for the scheduler to use and saves it to %s file.",
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// kubeConfigData defines the behavior that a runtime data struct passed to the kubeconfig 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 kubeConfigData interface {
|
||||||
|
Cfg() *kubeadmapi.InitConfiguration
|
||||||
|
ExternalCA() bool
|
||||||
|
CertificateDir() string
|
||||||
|
CertificateWriteDir() string
|
||||||
|
KubeConfigDir() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKubeConfigPhase creates a kubeadm workflow phase that creates all kubeconfig files necessary to establish the control plane and the admin kubeconfig file.
|
||||||
|
func NewKubeConfigPhase() workflow.Phase {
|
||||||
|
return workflow.Phase{
|
||||||
|
Name: "kubeconfig",
|
||||||
|
Short: "Generates all kubeconfig files necessary to establish the control plane and the admin kubeconfig file",
|
||||||
|
Phases: []workflow.Phase{
|
||||||
|
NewKubeConfigFilePhase(kubeadmconstants.AdminKubeConfigFileName),
|
||||||
|
NewKubeConfigFilePhase(kubeadmconstants.KubeletKubeConfigFileName),
|
||||||
|
NewKubeConfigFilePhase(kubeadmconstants.ControllerManagerKubeConfigFileName),
|
||||||
|
NewKubeConfigFilePhase(kubeadmconstants.SchedulerKubeConfigFileName),
|
||||||
|
},
|
||||||
|
Run: runKubeConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKubeConfigFilePhase creates a kubeadm workflow phase that creates a kubeconfig file.
|
||||||
|
func NewKubeConfigFilePhase(kubeConfigFileName string) workflow.Phase {
|
||||||
|
return workflow.Phase{
|
||||||
|
Name: kubeconfigFilePhaseProperties[kubeConfigFileName].name,
|
||||||
|
Short: kubeconfigFilePhaseProperties[kubeConfigFileName].short,
|
||||||
|
Long: fmt.Sprintf(kubeconfigFilePhaseProperties[kubeConfigFileName].long, kubeConfigFileName),
|
||||||
|
Run: runKubeConfigFile(kubeConfigFileName),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runKubeConfig(c workflow.RunData) error {
|
||||||
|
data, ok := c.(kubeConfigData)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("kubeconfig phase invoked with an invalid data struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[kubeconfig] Using kubeconfig folder %q\n", data.KubeConfigDir())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// runKubeConfigFile executes kubeconfig creation logic.
|
||||||
|
func runKubeConfigFile(kubeConfigFileName string) func(workflow.RunData) error {
|
||||||
|
return func(c workflow.RunData) error {
|
||||||
|
data, ok := c.(kubeConfigData)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("kubeconfig phase invoked with an invalid data struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
// if external CA mode, skip certificate authority generation
|
||||||
|
if data.ExternalCA() {
|
||||||
|
//TODO: implement validation of existing kubeconfig files
|
||||||
|
fmt.Printf("[kubeconfig] External CA mode: Using user provided %s\n", kubeConfigFileName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if dryrunning, reads certificates from a temporary folder (and defer restore to the path originally specified by the user)
|
||||||
|
cfg := data.Cfg()
|
||||||
|
cfg.CertificatesDir = data.CertificateWriteDir()
|
||||||
|
defer func() { cfg.CertificatesDir = data.CertificateDir() }()
|
||||||
|
|
||||||
|
// creates the KubeConfig file (or use existing)
|
||||||
|
return kubeconfigphase.CreateKubeConfigFile(kubeConfigFileName, data.KubeConfigDir(), data.Cfg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewCmdKubeConfig returns main command for kubeconfig phase
|
// NewCmdKubeConfig returns main command for kubeconfig phase
|
||||||
func NewCmdKubeConfig(out io.Writer) *cobra.Command {
|
func NewCmdKubeConfig(out io.Writer) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "kubeconfig",
|
Use: "kubeconfig",
|
||||||
Short: "Generates all kubeconfig files necessary to establish the control plane and the admin kubeconfig file",
|
Short: "kubeconfig file utilities",
|
||||||
Long: cmdutil.MacroCommandLongDescription,
|
Long: kubeconfigLongDesc,
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(getKubeConfigSubCommands(out, kubeadmconstants.KubernetesDir, "")...)
|
cmd.AddCommand(NewCmdUserKubeConfig(out, kubeadmconstants.KubernetesDir, ""))
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// getKubeConfigSubCommands returns sub commands for kubeconfig phase
|
// NewCmdUserKubeConfig returns sub commands for kubeconfig phase
|
||||||
func getKubeConfigSubCommands(out io.Writer, outDir, defaultKubernetesVersion string) []*cobra.Command {
|
func NewCmdUserKubeConfig(out io.Writer, outDir, defaultKubernetesVersion string) *cobra.Command {
|
||||||
|
|
||||||
cfg := &kubeadmapiv1beta1.InitConfiguration{}
|
cfg := &kubeadmapiv1beta1.InitConfiguration{}
|
||||||
|
|
||||||
@ -98,52 +171,14 @@ func getKubeConfigSubCommands(out io.Writer, outDir, defaultKubernetesVersion st
|
|||||||
|
|
||||||
var cfgPath, token, clientName string
|
var cfgPath, token, clientName string
|
||||||
var organizations []string
|
var organizations []string
|
||||||
var subCmds []*cobra.Command
|
|
||||||
|
|
||||||
subCmdProperties := []struct {
|
// Creates the UX Command
|
||||||
use string
|
cmd := &cobra.Command{
|
||||||
short string
|
Use: "user",
|
||||||
long string
|
Short: "Outputs a kubeconfig file for an additional user",
|
||||||
examples string
|
Long: userKubeconfigLongDesc,
|
||||||
cmdFunc func(outDir string, cfg *kubeadmapi.InitConfiguration) error
|
Example: userKubeconfigExample,
|
||||||
}{
|
Run: runCmdPhase(func(outDir string, cfg *kubeadmapi.InitConfiguration) error {
|
||||||
{
|
|
||||||
use: "all",
|
|
||||||
short: "Generates all kubeconfig files necessary to establish the control plane and the admin kubeconfig file",
|
|
||||||
long: allKubeconfigLongDesc,
|
|
||||||
examples: allKubeconfigExample,
|
|
||||||
cmdFunc: kubeconfigphase.CreateInitKubeConfigFiles,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
use: "admin",
|
|
||||||
short: "Generates a kubeconfig file for the admin to use and for kubeadm itself",
|
|
||||||
long: adminKubeconfigLongDesc,
|
|
||||||
cmdFunc: kubeconfigphase.CreateAdminKubeConfigFile,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
use: "kubelet",
|
|
||||||
short: "Generates a kubeconfig file for the kubelet to use. Please note that this should be used *only* for bootstrapping purposes",
|
|
||||||
long: kubeletKubeconfigLongDesc,
|
|
||||||
cmdFunc: kubeconfigphase.CreateKubeletKubeConfigFile,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
use: "controller-manager",
|
|
||||||
short: "Generates a kubeconfig file for the controller manager to use",
|
|
||||||
long: controllerManagerKubeconfigLongDesc,
|
|
||||||
cmdFunc: kubeconfigphase.CreateControllerManagerKubeConfigFile,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
use: "scheduler",
|
|
||||||
short: "Generates a kubeconfig file for the scheduler to use",
|
|
||||||
long: schedulerKubeconfigLongDesc,
|
|
||||||
cmdFunc: kubeconfigphase.CreateSchedulerKubeConfigFile,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
use: "user",
|
|
||||||
short: "Outputs a kubeconfig file for an additional user",
|
|
||||||
long: userKubeconfigLongDesc,
|
|
||||||
examples: userKubeconfigExample,
|
|
||||||
cmdFunc: func(outDir string, cfg *kubeadmapi.InitConfiguration) error {
|
|
||||||
if clientName == "" {
|
if clientName == "" {
|
||||||
return errors.New("missing required argument --client-name")
|
return errors.New("missing required argument --client-name")
|
||||||
}
|
}
|
||||||
@ -155,39 +190,17 @@ func getKubeConfigSubCommands(out io.Writer, outDir, defaultKubernetesVersion st
|
|||||||
|
|
||||||
// Otherwise, write a kubeconfig file with a generate client cert
|
// Otherwise, write a kubeconfig file with a generate client cert
|
||||||
return kubeconfigphase.WriteKubeConfigWithClientCert(out, cfg, clientName, organizations)
|
return kubeconfigphase.WriteKubeConfigWithClientCert(out, cfg, clientName, organizations)
|
||||||
},
|
}, &outDir, &cfgPath, cfg, defaultKubernetesVersion),
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, properties := range subCmdProperties {
|
|
||||||
// Creates the UX Command
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: properties.use,
|
|
||||||
Short: properties.short,
|
|
||||||
Long: properties.long,
|
|
||||||
Example: properties.examples,
|
|
||||||
Run: runCmdPhase(properties.cmdFunc, &outDir, &cfgPath, cfg, defaultKubernetesVersion),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add flags to the command
|
// Add flags to the command
|
||||||
if properties.use != "user" {
|
|
||||||
cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file. WARNING: Usage of a configuration file is experimental")
|
|
||||||
}
|
|
||||||
cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, "The path where certificates are stored")
|
cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, "The path where certificates are stored")
|
||||||
cmd.Flags().StringVar(&cfg.APIEndpoint.AdvertiseAddress, "apiserver-advertise-address", cfg.APIEndpoint.AdvertiseAddress, "The IP address the API server is accessible on")
|
cmd.Flags().StringVar(&cfg.APIEndpoint.AdvertiseAddress, "apiserver-advertise-address", cfg.APIEndpoint.AdvertiseAddress, "The IP address the API server is accessible on")
|
||||||
cmd.Flags().Int32Var(&cfg.APIEndpoint.BindPort, "apiserver-bind-port", cfg.APIEndpoint.BindPort, "The port the API server is accessible on")
|
cmd.Flags().Int32Var(&cfg.APIEndpoint.BindPort, "apiserver-bind-port", cfg.APIEndpoint.BindPort, "The port the API server is accessible on")
|
||||||
cmd.Flags().StringVar(&outDir, "kubeconfig-dir", outDir, "The path where to save the kubeconfig file")
|
cmd.Flags().StringVar(&outDir, "kubeconfig-dir", outDir, "The path where to save the kubeconfig file")
|
||||||
if properties.use == "all" || properties.use == "kubelet" {
|
|
||||||
cmd.Flags().StringVar(&cfg.NodeRegistration.Name, "node-name", cfg.NodeRegistration.Name, `The node name that should be used for the kubelet client certificate`)
|
|
||||||
}
|
|
||||||
if properties.use == "user" {
|
|
||||||
cmd.Flags().StringVar(&token, "token", token, "The token that should be used as the authentication mechanism for this kubeconfig, instead of client certificates")
|
cmd.Flags().StringVar(&token, "token", token, "The token that should be used as the authentication mechanism for this kubeconfig, instead of client certificates")
|
||||||
cmd.Flags().StringVar(&clientName, "client-name", clientName, "The name of user. It will be used as the CN if client certificates are created")
|
cmd.Flags().StringVar(&clientName, "client-name", clientName, "The name of user. It will be used as the CN if client certificates are created")
|
||||||
cmd.Flags().StringSliceVar(&organizations, "org", organizations, "The orgnizations of the client certificate. It will be used as the O if client certificates are created")
|
cmd.Flags().StringSliceVar(&organizations, "org", organizations, "The orgnizations of the client certificate. It will be used as the O if client certificates are created")
|
||||||
}
|
|
||||||
|
|
||||||
subCmds = append(subCmds, cmd)
|
return cmd
|
||||||
}
|
|
||||||
|
|
||||||
return subCmds
|
|
||||||
}
|
}
|
||||||
|
@ -20,299 +20,30 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
|
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
|
||||||
|
|
||||||
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
||||||
cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd"
|
cmdtestutil "k8s.io/kubernetes/cmd/kubeadm/test/cmd"
|
||||||
kubeconfigtestutil "k8s.io/kubernetes/cmd/kubeadm/test/kubeconfig"
|
kubeconfigtestutil "k8s.io/kubernetes/cmd/kubeadm/test/kubeconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKubeConfigCSubCommandsHasFlags(t *testing.T) {
|
func TestKubeConfigCSubCommandsHasFlags(t *testing.T) {
|
||||||
|
cmd := NewCmdUserKubeConfig(nil, "", phaseTestK8sVersion)
|
||||||
|
|
||||||
subCmds := getKubeConfigSubCommands(nil, "", phaseTestK8sVersion)
|
flags := []string{
|
||||||
|
|
||||||
commonFlags := []string{
|
|
||||||
"cert-dir",
|
"cert-dir",
|
||||||
"apiserver-advertise-address",
|
"apiserver-advertise-address",
|
||||||
"apiserver-bind-port",
|
"apiserver-bind-port",
|
||||||
"kubeconfig-dir",
|
"kubeconfig-dir",
|
||||||
}
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
command string
|
|
||||||
additionalFlags []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
command: "all",
|
|
||||||
additionalFlags: []string{
|
|
||||||
"config",
|
|
||||||
"node-name",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "admin",
|
|
||||||
additionalFlags: []string{
|
|
||||||
"config",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "kubelet",
|
|
||||||
additionalFlags: []string{
|
|
||||||
"config",
|
|
||||||
"node-name",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "controller-manager",
|
|
||||||
additionalFlags: []string{
|
|
||||||
"config",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "scheduler",
|
|
||||||
additionalFlags: []string{
|
|
||||||
"config",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "user",
|
|
||||||
additionalFlags: []string{
|
|
||||||
"token",
|
"token",
|
||||||
"client-name",
|
"client-name",
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
cmdtestutil.AssertSubCommandHasFlags(t, []*cobra.Command{cmd}, "user", flags...)
|
||||||
expectedFlags := append(commonFlags, test.additionalFlags...)
|
|
||||||
cmdtestutil.AssertSubCommandHasFlags(t, subCmds, test.command, expectedFlags...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKubeConfigSubCommandsThatCreateFilesWithFlags(t *testing.T) {
|
|
||||||
|
|
||||||
commonFlags := []string{
|
|
||||||
"--apiserver-advertise-address=1.2.3.4",
|
|
||||||
"--apiserver-bind-port=1234",
|
|
||||||
}
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
command string
|
|
||||||
additionalFlags []string
|
|
||||||
expectedFiles []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
command: "all",
|
|
||||||
additionalFlags: []string{"--node-name=valid-nome-name"},
|
|
||||||
expectedFiles: []string{
|
|
||||||
kubeadmconstants.AdminKubeConfigFileName,
|
|
||||||
kubeadmconstants.KubeletKubeConfigFileName,
|
|
||||||
kubeadmconstants.ControllerManagerKubeConfigFileName,
|
|
||||||
kubeadmconstants.SchedulerKubeConfigFileName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "admin",
|
|
||||||
expectedFiles: []string{kubeadmconstants.AdminKubeConfigFileName},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "kubelet",
|
|
||||||
additionalFlags: []string{"--node-name=valid-nome-name"},
|
|
||||||
expectedFiles: []string{kubeadmconstants.KubeletKubeConfigFileName},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "controller-manager",
|
|
||||||
expectedFiles: []string{kubeadmconstants.ControllerManagerKubeConfigFileName},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "scheduler",
|
|
||||||
expectedFiles: []string{kubeadmconstants.SchedulerKubeConfigFileName},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var kubeConfigAssertions = map[string]struct {
|
|
||||||
clientName string
|
|
||||||
organizations []string
|
|
||||||
}{
|
|
||||||
kubeadmconstants.AdminKubeConfigFileName: {
|
|
||||||
clientName: "kubernetes-admin",
|
|
||||||
organizations: []string{kubeadmconstants.MastersGroup},
|
|
||||||
},
|
|
||||||
kubeadmconstants.KubeletKubeConfigFileName: {
|
|
||||||
clientName: "system:node:valid-nome-name",
|
|
||||||
organizations: []string{kubeadmconstants.NodesGroup},
|
|
||||||
},
|
|
||||||
kubeadmconstants.ControllerManagerKubeConfigFileName: {
|
|
||||||
clientName: kubeadmconstants.ControllerManagerUser,
|
|
||||||
},
|
|
||||||
kubeadmconstants.SchedulerKubeConfigFileName: {
|
|
||||||
clientName: kubeadmconstants.SchedulerUser,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
|
|
||||||
// Create temp folder for the test case
|
|
||||||
tmpdir := testutil.SetupTempDir(t)
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
|
|
||||||
// Adds a pki folder with a ca certs to the temp folder
|
|
||||||
pkidir := testutil.SetupPkiDirWithCertificateAuthorithy(t, tmpdir)
|
|
||||||
|
|
||||||
outputdir := tmpdir
|
|
||||||
|
|
||||||
// Retrieves ca cert for assertions
|
|
||||||
caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkidir, kubeadmconstants.CACertAndKeyBaseName)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("couldn't retrieve ca cert: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get subcommands working in the temporary directory
|
|
||||||
subCmds := getKubeConfigSubCommands(nil, tmpdir, phaseTestK8sVersion)
|
|
||||||
|
|
||||||
// Execute the subcommand
|
|
||||||
certDirFlag := fmt.Sprintf("--cert-dir=%s", pkidir)
|
|
||||||
outputDirFlag := fmt.Sprintf("--kubeconfig-dir=%s", outputdir)
|
|
||||||
allFlags := append(commonFlags, certDirFlag)
|
|
||||||
allFlags = append(allFlags, outputDirFlag)
|
|
||||||
allFlags = append(allFlags, test.additionalFlags...)
|
|
||||||
cmdtestutil.RunSubCommand(t, subCmds, test.command, allFlags...)
|
|
||||||
|
|
||||||
// Checks that requested files are there
|
|
||||||
testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
|
|
||||||
|
|
||||||
// Checks contents of generated files
|
|
||||||
for _, file := range test.expectedFiles {
|
|
||||||
|
|
||||||
// reads generated files
|
|
||||||
config, err := clientcmd.LoadFromFile(filepath.Join(tmpdir, file))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("couldn't load generated kubeconfig file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checks that CLI flags are properly propagated and kubeconfig properties are correct
|
|
||||||
kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
|
|
||||||
|
|
||||||
expectedClientName := kubeConfigAssertions[file].clientName
|
|
||||||
expectedOrganizations := kubeConfigAssertions[file].organizations
|
|
||||||
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, expectedClientName, expectedOrganizations...)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKubeConfigSubCommandsThatCreateFilesWithConfigFile(t *testing.T) {
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
command string
|
|
||||||
expectedFiles []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
command: "all",
|
|
||||||
expectedFiles: []string{
|
|
||||||
kubeadmconstants.AdminKubeConfigFileName,
|
|
||||||
kubeadmconstants.KubeletKubeConfigFileName,
|
|
||||||
kubeadmconstants.ControllerManagerKubeConfigFileName,
|
|
||||||
kubeadmconstants.SchedulerKubeConfigFileName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "admin",
|
|
||||||
expectedFiles: []string{kubeadmconstants.AdminKubeConfigFileName},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "kubelet",
|
|
||||||
expectedFiles: []string{kubeadmconstants.KubeletKubeConfigFileName},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "controller-manager",
|
|
||||||
expectedFiles: []string{kubeadmconstants.ControllerManagerKubeConfigFileName},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: "scheduler",
|
|
||||||
expectedFiles: []string{kubeadmconstants.SchedulerKubeConfigFileName},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var kubeConfigAssertions = map[string]struct {
|
|
||||||
clientName string
|
|
||||||
organizations []string
|
|
||||||
}{
|
|
||||||
kubeadmconstants.AdminKubeConfigFileName: {
|
|
||||||
clientName: "kubernetes-admin",
|
|
||||||
organizations: []string{kubeadmconstants.MastersGroup},
|
|
||||||
},
|
|
||||||
kubeadmconstants.KubeletKubeConfigFileName: {
|
|
||||||
clientName: "system:node:valid-node-name",
|
|
||||||
organizations: []string{kubeadmconstants.NodesGroup},
|
|
||||||
},
|
|
||||||
kubeadmconstants.ControllerManagerKubeConfigFileName: {
|
|
||||||
clientName: kubeadmconstants.ControllerManagerUser,
|
|
||||||
},
|
|
||||||
kubeadmconstants.SchedulerKubeConfigFileName: {
|
|
||||||
clientName: kubeadmconstants.SchedulerUser,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
|
|
||||||
// Create temp folder for the test case
|
|
||||||
tmpdir := testutil.SetupTempDir(t)
|
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
|
|
||||||
// Adds a pki folder with a ca certs to the temp folder
|
|
||||||
pkidir := testutil.SetupPkiDirWithCertificateAuthorithy(t, tmpdir)
|
|
||||||
|
|
||||||
// Retrieves ca cert for assertions
|
|
||||||
caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkidir, kubeadmconstants.CACertAndKeyBaseName)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("couldn't retrieve ca cert: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a master configuration file
|
|
||||||
cfg := &kubeadmapi.InitConfiguration{
|
|
||||||
APIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
|
|
||||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
|
||||||
CertificatesDir: pkidir,
|
|
||||||
},
|
|
||||||
NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-node-name"},
|
|
||||||
}
|
|
||||||
cfgPath := testutil.SetupInitConfigurationFile(t, tmpdir, cfg)
|
|
||||||
|
|
||||||
// Get subcommands working in the temporary directory
|
|
||||||
subCmds := getKubeConfigSubCommands(nil, tmpdir, phaseTestK8sVersion)
|
|
||||||
|
|
||||||
// Execute the subcommand
|
|
||||||
configFlag := fmt.Sprintf("--config=%s", cfgPath)
|
|
||||||
cmdtestutil.RunSubCommand(t, subCmds, test.command, configFlag)
|
|
||||||
|
|
||||||
// Checks that requested files are there
|
|
||||||
testutil.AssertFileExists(t, tmpdir, test.expectedFiles...)
|
|
||||||
|
|
||||||
// Checks contents of generated files
|
|
||||||
for _, file := range test.expectedFiles {
|
|
||||||
|
|
||||||
// reads generated files
|
|
||||||
config, err := clientcmd.LoadFromFile(filepath.Join(tmpdir, file))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("couldn't load generated kubeconfig file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checks that config file properties are properly propagated and kubeconfig properties are correct
|
|
||||||
kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
|
|
||||||
|
|
||||||
expectedClientName := kubeConfigAssertions[file].clientName
|
|
||||||
expectedOrganizations := kubeConfigAssertions[file].organizations
|
|
||||||
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, expectedClientName, expectedOrganizations...)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
|
func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
|
||||||
@ -361,11 +92,11 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
|
|||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
// Get subcommands working in the temporary directory
|
// Get subcommands working in the temporary directory
|
||||||
subCmds := getKubeConfigSubCommands(buf, tmpdir, phaseTestK8sVersion)
|
cmd := NewCmdUserKubeConfig(buf, tmpdir, phaseTestK8sVersion)
|
||||||
|
|
||||||
// Execute the subcommand
|
// Execute the subcommand
|
||||||
allFlags := append(commonFlags, test.additionalFlags...)
|
allFlags := append(commonFlags, test.additionalFlags...)
|
||||||
cmdtestutil.RunSubCommand(t, subCmds, test.command, allFlags...)
|
cmdtestutil.RunSubCommand(t, []*cobra.Command{cmd}, test.command, allFlags...)
|
||||||
|
|
||||||
// reads kubeconfig written to stdout
|
// reads kubeconfig written to stdout
|
||||||
config, err := clientcmd.Load(buf.Bytes())
|
config, err := clientcmd.Load(buf.Bytes())
|
||||||
|
@ -81,7 +81,7 @@ func runPreflightMaster(c workflow.RunData) error {
|
|||||||
return errors.New("preflight phase invoked with an invalid data struct")
|
return errors.New("preflight phase invoked with an invalid data struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("[preflight] running pre-flight checks")
|
fmt.Println("[preflight] Running pre-flight checks")
|
||||||
if err := preflight.RunInitMasterChecks(utilsexec.New(), data.Cfg(), data.IgnorePreflightErrors()); err != nil {
|
if err := preflight.RunInitMasterChecks(utilsexec.New(), data.Cfg(), data.IgnorePreflightErrors()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
@ -57,7 +56,7 @@ func CreatePKIAssets(cfg *kubeadmapi.InitConfiguration) error {
|
|||||||
return errors.Wrap(err, "error creating PKI assets")
|
return errors.Wrap(err, "error creating PKI assets")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[certificates] valid certificates and keys now exist in %q\n", cfg.CertificatesDir)
|
fmt.Printf("[certs] valid certificates and keys now exist in %q\n", cfg.CertificatesDir)
|
||||||
|
|
||||||
// Service accounts are not x509 certs, so handled separately
|
// Service accounts are not x509 certs, so handled separately
|
||||||
if err := CreateServiceAccountKeyAndPublicKeyFiles(cfg); err != nil {
|
if err := CreateServiceAccountKeyAndPublicKeyFiles(cfg); err != nil {
|
||||||
@ -190,15 +189,14 @@ func writeCertificateAuthorithyFilesIfNotExist(pkiDir string, baseName string, c
|
|||||||
// kubeadm doesn't validate the existing certificate Authority more than this;
|
// kubeadm doesn't validate the existing certificate Authority more than this;
|
||||||
// Basically, if we find a certificate file with the same path; and it is a CA
|
// Basically, if we find a certificate file with the same path; and it is a CA
|
||||||
// kubeadm thinks those files are equal and doesn't bother writing a new file
|
// kubeadm thinks those files are equal and doesn't bother writing a new file
|
||||||
fmt.Printf("[certificates] Using the existing %s certificate and key.\n", baseName)
|
fmt.Printf("[certs] Using the existing %q certificate and key\n", baseName)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Write .crt and .key files to disk
|
// Write .crt and .key files to disk
|
||||||
|
fmt.Printf("[certs] Generating %q certificate and key\n", baseName)
|
||||||
|
|
||||||
if err := pkiutil.WriteCertAndKey(pkiDir, baseName, caCert, caKey); err != nil {
|
if err := pkiutil.WriteCertAndKey(pkiDir, baseName, caCert, caKey); err != nil {
|
||||||
return errors.Wrapf(err, "failure while saving %s certificate and key", baseName)
|
return errors.Wrapf(err, "failure while saving %s certificate and key", baseName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[certificates] Generated %s certificate and key.\n", baseName)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -226,17 +224,16 @@ func writeCertificateFilesIfNotExist(pkiDir string, baseName string, signingCert
|
|||||||
// Basically, if we find a certificate file with the same path; and it is signed by
|
// Basically, if we find a certificate file with the same path; and it is signed by
|
||||||
// the expected certificate authority, kubeadm thinks those files are equal and
|
// the expected certificate authority, kubeadm thinks those files are equal and
|
||||||
// doesn't bother writing a new file
|
// doesn't bother writing a new file
|
||||||
fmt.Printf("[certificates] Using the existing %s certificate and key.\n", baseName)
|
fmt.Printf("[certs] Using the existing %q certificate and key\n", baseName)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Write .crt and .key files to disk
|
// Write .crt and .key files to disk
|
||||||
|
fmt.Printf("[certs] Generating %q certificate and key\n", baseName)
|
||||||
|
|
||||||
if err := pkiutil.WriteCertAndKey(pkiDir, baseName, cert, key); err != nil {
|
if err := pkiutil.WriteCertAndKey(pkiDir, baseName, cert, key); err != nil {
|
||||||
return errors.Wrapf(err, "failure while saving %s certificate and key", baseName)
|
return errors.Wrapf(err, "failure while saving %s certificate and key", baseName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[certificates] Generated %s certificate and key.\n", baseName)
|
|
||||||
if pkiutil.HasServerAuth(cert) {
|
if pkiutil.HasServerAuth(cert) {
|
||||||
fmt.Printf("[certificates] %s serving cert is signed for DNS names %v and IPs %v\n", baseName, cert.DNSNames, cert.IPAddresses)
|
fmt.Printf("[certs] %s serving cert is signed for DNS names %v and IPs %v\n", baseName, cert.DNSNames, cert.IPAddresses)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,10 +258,12 @@ func writeKeyFilesIfNotExist(pkiDir string, baseName string, key *rsa.PrivateKey
|
|||||||
// kubeadm doesn't validate the existing certificate key more than this;
|
// kubeadm doesn't validate the existing certificate key more than this;
|
||||||
// Basically, if we find a key file with the same path kubeadm thinks those files
|
// Basically, if we find a key file with the same path kubeadm thinks those files
|
||||||
// are equal and doesn't bother writing a new file
|
// are equal and doesn't bother writing a new file
|
||||||
fmt.Printf("[certificates] Using the existing %s key.\n", baseName)
|
fmt.Printf("[certs] Using the existing %q key\n", baseName)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Write .key and .pub files to disk
|
// Write .key and .pub files to disk
|
||||||
|
fmt.Printf("[certs] Generating %q key and public key\n", baseName)
|
||||||
|
|
||||||
if err := pkiutil.WriteKey(pkiDir, baseName, key); err != nil {
|
if err := pkiutil.WriteKey(pkiDir, baseName, key); err != nil {
|
||||||
return errors.Wrapf(err, "failure while saving %s key", baseName)
|
return errors.Wrapf(err, "failure while saving %s key", baseName)
|
||||||
}
|
}
|
||||||
@ -272,7 +271,6 @@ func writeKeyFilesIfNotExist(pkiDir string, baseName string, key *rsa.PrivateKey
|
|||||||
if err := pkiutil.WritePublicKey(pkiDir, baseName, &key.PublicKey); err != nil {
|
if err := pkiutil.WritePublicKey(pkiDir, baseName, &key.PublicKey); err != nil {
|
||||||
return errors.Wrapf(err, "failure while saving %s public key", baseName)
|
return errors.Wrapf(err, "failure while saving %s public key", baseName)
|
||||||
}
|
}
|
||||||
fmt.Printf("[certificates] Generated %s key and public key.\n", baseName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -18,17 +18,15 @@ package kubeconfig
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"crypto/rsa"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
@ -88,32 +86,11 @@ func CreateJoinControlPlaneKubeConfigFiles(outDir string, cfg *kubeadmapi.InitCo
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAdminKubeConfigFile create a kubeconfig file for the admin to use and for kubeadm itself.
|
// CreateKubeConfigFile creates a kubeconfig file.
|
||||||
// If the kubeconfig file already exists, it is used only if evaluated equal; otherwise an error is returned.
|
// If the kubeconfig file already exists, it is used only if evaluated equal; otherwise an error is returned.
|
||||||
func CreateAdminKubeConfigFile(outDir string, cfg *kubeadmapi.InitConfiguration) error {
|
func CreateKubeConfigFile(kubeConfigFileName string, outDir string, cfg *kubeadmapi.InitConfiguration) error {
|
||||||
glog.V(1).Infoln("create a kubeconfig file for the admin and for kubeadm itself")
|
glog.V(1).Infof("creating kubeconfig file for %s", kubeConfigFileName)
|
||||||
return createKubeConfigFiles(outDir, cfg, kubeadmconstants.AdminKubeConfigFileName)
|
return createKubeConfigFiles(outDir, cfg, kubeConfigFileName)
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKubeletKubeConfigFile create a kubeconfig file for the Kubelet to use.
|
|
||||||
// If the kubeconfig file already exists, it is used only if evaluated equal; otherwise an error is returned.
|
|
||||||
func CreateKubeletKubeConfigFile(outDir string, cfg *kubeadmapi.InitConfiguration) error {
|
|
||||||
glog.V(1).Infoln("creating a kubeconfig file for the Kubelet")
|
|
||||||
return createKubeConfigFiles(outDir, cfg, kubeadmconstants.KubeletKubeConfigFileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateControllerManagerKubeConfigFile create a kubeconfig file for the ControllerManager to use.
|
|
||||||
// If the kubeconfig file already exists, it is used only if evaluated equal; otherwise an error is returned.
|
|
||||||
func CreateControllerManagerKubeConfigFile(outDir string, cfg *kubeadmapi.InitConfiguration) error {
|
|
||||||
glog.V(1).Infoln("creating kubeconfig file for the ControllerManager")
|
|
||||||
return createKubeConfigFiles(outDir, cfg, kubeadmconstants.ControllerManagerKubeConfigFileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateSchedulerKubeConfigFile create a create a kubeconfig file for the Scheduler to use.
|
|
||||||
// If the kubeconfig file already exists, it is used only if evaluated equal; otherwise an error is returned.
|
|
||||||
func CreateSchedulerKubeConfigFile(outDir string, cfg *kubeadmapi.InitConfiguration) error {
|
|
||||||
glog.V(1).Infoln("creating kubeconfig file for Scheduler")
|
|
||||||
return createKubeConfigFiles(outDir, cfg, kubeadmconstants.SchedulerKubeConfigFileName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createKubeConfigFiles creates all the requested kubeconfig files.
|
// createKubeConfigFiles creates all the requested kubeconfig files.
|
||||||
@ -248,12 +225,12 @@ func createKubeConfigFileIfNotExists(outDir, filename string, config *clientcmda
|
|||||||
|
|
||||||
// Check if the file exist, and if it doesn't, just write it to disk
|
// Check if the file exist, and if it doesn't, just write it to disk
|
||||||
if _, err := os.Stat(kubeConfigFilePath); os.IsNotExist(err) {
|
if _, err := os.Stat(kubeConfigFilePath); os.IsNotExist(err) {
|
||||||
|
fmt.Printf("[kubeconfig] Writing %q kubeconfig file\n", filename)
|
||||||
|
|
||||||
err = kubeconfigutil.WriteToDisk(kubeConfigFilePath, config)
|
err = kubeconfigutil.WriteToDisk(kubeConfigFilePath, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to save kubeconfig file %s on disk", kubeConfigFilePath)
|
return errors.Wrapf(err, "failed to save kubeconfig file %s on disk", kubeConfigFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[kubeconfig] Wrote KubeConfig file to disk: %q\n", kubeConfigFilePath)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,22 +292,6 @@ func TestCreateKubeconfigFilesAndWrappers(t *testing.T) {
|
|||||||
kubeadmconstants.SchedulerKubeConfigFileName,
|
kubeadmconstants.SchedulerKubeConfigFileName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ // Test CreateAdminKubeConfigFile (wrapper to createKubeConfigFile)
|
|
||||||
createKubeConfigFunction: CreateAdminKubeConfigFile,
|
|
||||||
expectedFiles: []string{kubeadmconstants.AdminKubeConfigFileName},
|
|
||||||
},
|
|
||||||
{ // Test CreateKubeletKubeConfigFile (wrapper to createKubeConfigFile)
|
|
||||||
createKubeConfigFunction: CreateKubeletKubeConfigFile,
|
|
||||||
expectedFiles: []string{kubeadmconstants.KubeletKubeConfigFileName},
|
|
||||||
},
|
|
||||||
{ // Test CreateControllerManagerKubeConfigFile (wrapper to createKubeConfigFile)
|
|
||||||
createKubeConfigFunction: CreateControllerManagerKubeConfigFile,
|
|
||||||
expectedFiles: []string{kubeadmconstants.ControllerManagerKubeConfigFileName},
|
|
||||||
},
|
|
||||||
{ // Test createKubeConfigFile (wrapper to createKubeConfigFile)
|
|
||||||
createKubeConfigFunction: CreateSchedulerKubeConfigFile,
|
|
||||||
expectedFiles: []string{kubeadmconstants.SchedulerKubeConfigFileName},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -40,7 +40,6 @@ func TestCmdInitToken(t *testing.T) {
|
|||||||
args string
|
args string
|
||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
/*
|
|
||||||
{
|
{
|
||||||
name: "invalid token size",
|
name: "invalid token size",
|
||||||
args: "--token=abcd:1234567890abcd",
|
args: "--token=abcd:1234567890abcd",
|
||||||
@ -51,7 +50,6 @@ func TestCmdInitToken(t *testing.T) {
|
|||||||
args: "--token=Abcdef:1234567890abcdef",
|
args: "--token=Abcdef:1234567890abcdef",
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
*/
|
|
||||||
{
|
{
|
||||||
name: "valid token is accepted",
|
name: "valid token is accepted",
|
||||||
args: "--token=abcdef.0123456789abcdef",
|
args: "--token=abcdef.0123456789abcdef",
|
||||||
|
Loading…
Reference in New Issue
Block a user