kubeadm: make super-admin.conf changes to app/cmd

- Update unit tests in certs_test.go related to the "renew" CLI command.
- In /init, (d *initData) Client(), make sure that the new logic
for bootstrapping an "admin.conf" user is performed, by calling
EnsureAdminClusterRoleBinding() from the phases backend. Add a
"adminKubeConfigBootstrapped" flag that helps call this logic only
once per "kubeadm init" binary execution.
- In /phases/init include a new subphase for generating
the "super-admin.conf" file.
- In /phases/reset make sure the file "super-admin.conf" is
cleaned if present. Update unit tests.
This commit is contained in:
Lubomir I. Ivanov 2023-10-19 20:41:23 +03:00
parent 30ed50d32e
commit 837090135f
5 changed files with 46 additions and 19 deletions

View File

@ -134,6 +134,7 @@ func TestRunRenewCommands(t *testing.T) {
// Generate all the kubeconfig files with embedded certs // Generate all the kubeconfig files with embedded certs
for _, kubeConfig := range []string{ for _, kubeConfig := range []string{
kubeadmconstants.AdminKubeConfigFileName, kubeadmconstants.AdminKubeConfigFileName,
kubeadmconstants.SuperAdminKubeConfigFileName,
kubeadmconstants.SchedulerKubeConfigFileName, kubeadmconstants.SchedulerKubeConfigFileName,
kubeadmconstants.ControllerManagerKubeConfigFileName, kubeadmconstants.ControllerManagerKubeConfigFileName,
} { } {
@ -162,6 +163,7 @@ func TestRunRenewCommands(t *testing.T) {
}, },
KubeconfigFiles: []string{ KubeconfigFiles: []string{
kubeadmconstants.AdminKubeConfigFileName, kubeadmconstants.AdminKubeConfigFileName,
kubeadmconstants.SuperAdminKubeConfigFileName,
kubeadmconstants.SchedulerKubeConfigFileName, kubeadmconstants.SchedulerKubeConfigFileName,
kubeadmconstants.ControllerManagerKubeConfigFileName, kubeadmconstants.ControllerManagerKubeConfigFileName,
}, },
@ -214,6 +216,12 @@ func TestRunRenewCommands(t *testing.T) {
kubeadmconstants.AdminKubeConfigFileName, kubeadmconstants.AdminKubeConfigFileName,
}, },
}, },
{
command: "super-admin.conf",
KubeconfigFiles: []string{
kubeadmconstants.SuperAdminKubeConfigFileName,
},
},
{ {
command: "scheduler.conf", command: "scheduler.conf",
KubeconfigFiles: []string{ KubeconfigFiles: []string{

View File

@ -72,20 +72,21 @@ var _ phases.InitData = &initData{}
// initData defines all the runtime information used when running the kubeadm init workflow; // initData defines all the runtime information used when running the kubeadm init workflow;
// this data is shared across all the phases that are included in the workflow. // this data is shared across all the phases that are included in the workflow.
type initData struct { type initData struct {
cfg *kubeadmapi.InitConfiguration cfg *kubeadmapi.InitConfiguration
skipTokenPrint bool skipTokenPrint bool
dryRun bool dryRun bool
kubeconfigDir string kubeconfigDir string
kubeconfigPath string kubeconfigPath string
ignorePreflightErrors sets.Set[string] ignorePreflightErrors sets.Set[string]
certificatesDir string certificatesDir string
dryRunDir string dryRunDir string
externalCA bool externalCA bool
client clientset.Interface client clientset.Interface
outputWriter io.Writer outputWriter io.Writer
uploadCerts bool uploadCerts bool
skipCertificateKeyPrint bool skipCertificateKeyPrint bool
patchesDir string patchesDir string
adminKubeConfigBootstrapped bool
} }
// newCmdInit returns "kubeadm init" command. // newCmdInit returns "kubeadm init" command.
@ -495,12 +496,22 @@ func (d *initData) Client() (clientset.Interface, error) {
// If we're dry-running, we should create a faked client that answers some GETs in order to be able to do the full init flow and just logs the rest of requests // If we're dry-running, we should create a faked client that answers some GETs in order to be able to do the full init flow and just logs the rest of requests
dryRunGetter := apiclient.NewInitDryRunGetter(d.cfg.NodeRegistration.Name, svcSubnetCIDR.String()) dryRunGetter := apiclient.NewInitDryRunGetter(d.cfg.NodeRegistration.Name, svcSubnetCIDR.String())
d.client = apiclient.NewDryRunClient(dryRunGetter, os.Stdout) d.client = apiclient.NewDryRunClient(dryRunGetter, os.Stdout)
} else { } else { // Use a real client
// If we're acting for real, we should create a connection to the API server and wait for it to come up
var err error var err error
d.client, err = kubeconfigutil.ClientSetFromFile(d.KubeConfigPath()) if !d.adminKubeConfigBootstrapped {
if err != nil { // Call EnsureAdminClusterRoleBinding() to obtain a working client from admin.conf.
return nil, err d.client, err = kubeconfigphase.EnsureAdminClusterRoleBinding(kubeadmconstants.KubernetesDir, nil)
if err != nil {
return nil, errors.Wrapf(err, "could not bootstrap the admin user in file %s", kubeadmconstants.AdminKubeConfigFileName)
}
d.adminKubeConfigBootstrapped = true
} else {
// In case adminKubeConfigBootstrapped is already set just return a client from the default
// kubeconfig location.
d.client, err = kubeconfigutil.ClientSetFromFile(d.KubeConfigPath())
if err != nil {
return nil, err
}
} }
} }
} }

View File

@ -41,6 +41,11 @@ var (
short: "Generate a kubeconfig file for the admin to use and for kubeadm itself", short: "Generate a kubeconfig file for the admin to use and for kubeadm itself",
long: "Generate the kubeconfig file for the admin and for kubeadm itself, and save it to %s file.", long: "Generate the kubeconfig file for the admin and for kubeadm itself, and save it to %s file.",
}, },
kubeadmconstants.SuperAdminKubeConfigFileName: {
name: "super-admin",
short: "Generate a kubeconfig file for the super-admin",
long: "Generate a kubeconfig file for the super-admin, and save it to %s file.",
},
kubeadmconstants.KubeletKubeConfigFileName: { kubeadmconstants.KubeletKubeConfigFileName: {
name: "kubelet", name: "kubelet",
short: "Generate a kubeconfig file for the kubelet to use *only* for cluster bootstrapping purposes", short: "Generate a kubeconfig file for the kubelet to use *only* for cluster bootstrapping purposes",
@ -77,6 +82,7 @@ func NewKubeConfigPhase() workflow.Phase {
RunAllSiblings: true, RunAllSiblings: true,
}, },
NewKubeConfigFilePhase(kubeadmconstants.AdminKubeConfigFileName), NewKubeConfigFilePhase(kubeadmconstants.AdminKubeConfigFileName),
NewKubeConfigFilePhase(kubeadmconstants.SuperAdminKubeConfigFileName),
NewKubeConfigFilePhase(kubeadmconstants.KubeletKubeConfigFileName), NewKubeConfigFilePhase(kubeadmconstants.KubeletKubeConfigFileName),
NewKubeConfigFilePhase(kubeadmconstants.ControllerManagerKubeConfigFileName), NewKubeConfigFilePhase(kubeadmconstants.ControllerManagerKubeConfigFileName),
NewKubeConfigFilePhase(kubeadmconstants.SchedulerKubeConfigFileName), NewKubeConfigFilePhase(kubeadmconstants.SchedulerKubeConfigFileName),

View File

@ -169,6 +169,7 @@ func resetConfigDir(configPathDir string, dirsToClean []string, isDryRun bool) {
filesToClean := []string{ filesToClean := []string{
filepath.Join(configPathDir, kubeadmconstants.AdminKubeConfigFileName), filepath.Join(configPathDir, kubeadmconstants.AdminKubeConfigFileName),
filepath.Join(configPathDir, kubeadmconstants.SuperAdminKubeConfigFileName),
filepath.Join(configPathDir, kubeadmconstants.KubeletKubeConfigFileName), filepath.Join(configPathDir, kubeadmconstants.KubeletKubeConfigFileName),
filepath.Join(configPathDir, kubeadmconstants.KubeletBootstrapKubeConfigFileName), filepath.Join(configPathDir, kubeadmconstants.KubeletBootstrapKubeConfigFileName),
filepath.Join(configPathDir, kubeadmconstants.ControllerManagerKubeConfigFileName), filepath.Join(configPathDir, kubeadmconstants.ControllerManagerKubeConfigFileName),

View File

@ -68,6 +68,7 @@ func TestConfigDirCleaner(t *testing.T) {
"manifests/kube-apiserver.yaml", "manifests/kube-apiserver.yaml",
"pki/ca.pem", "pki/ca.pem",
kubeadmconstants.AdminKubeConfigFileName, kubeadmconstants.AdminKubeConfigFileName,
kubeadmconstants.SuperAdminKubeConfigFileName,
kubeadmconstants.KubeletKubeConfigFileName, kubeadmconstants.KubeletKubeConfigFileName,
}, },
verifyExists: []string{ verifyExists: []string{