Merge pull request #121743 from neolit123/1.29-super-admin-conf

kubeadm: ensure the kubelet and kube-apiserver wait checks go first
This commit is contained in:
Kubernetes Prow Robot 2023-11-06 14:24:01 +01:00 committed by GitHub
commit cf4d031dbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 27 deletions

View File

@ -496,21 +496,28 @@ func (d *initData) OutputWriter() io.Writer {
return d.outputWriter
}
// getDryRunClient creates a fake client that answers some GET calls in order to be able to do the full init flow in dry-run mode.
func getDryRunClient(d *initData) (clientset.Interface, error) {
svcSubnetCIDR, err := kubeadmconstants.GetKubernetesServiceCIDR(d.cfg.Networking.ServiceSubnet)
if err != nil {
return nil, errors.Wrapf(err, "unable to get internal Kubernetes Service IP from the given service CIDR (%s)", d.cfg.Networking.ServiceSubnet)
}
dryRunGetter := apiclient.NewInitDryRunGetter(d.cfg.NodeRegistration.Name, svcSubnetCIDR.String())
return apiclient.NewDryRunClient(dryRunGetter, os.Stdout), nil
}
// 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.
// Important. This function must be called after the admin.conf kubeconfig file is created.
func (d *initData) Client() (clientset.Interface, error) {
var err error
if d.client == nil {
if d.dryRun {
svcSubnetCIDR, err := kubeadmconstants.GetKubernetesServiceCIDR(d.cfg.Networking.ServiceSubnet)
d.client, err = getDryRunClient(d)
if err != nil {
return nil, errors.Wrapf(err, "unable to get internal Kubernetes Service IP from the given service CIDR (%s)", d.cfg.Networking.ServiceSubnet)
return nil, err
}
// 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())
d.client = apiclient.NewDryRunClient(dryRunGetter, os.Stdout)
} else { // Use a real client
var err error
if !d.adminKubeConfigBootstrapped {
// Call EnsureAdminClusterRoleBinding() to obtain a working client from admin.conf.
d.client, err = kubeconfigphase.EnsureAdminClusterRoleBinding(kubeadmconstants.KubernetesDir, nil)
@ -531,6 +538,28 @@ func (d *initData) Client() (clientset.Interface, error) {
return d.client, nil
}
// ClientWithoutBootstrap returns a dry-run client or a regular client from admin.conf.
// Unlike Client(), it does not call EnsureAdminClusterRoleBinding() or sets d.client.
// This means the client only has anonymous permissions and does not persist in initData.
func (d *initData) ClientWithoutBootstrap() (clientset.Interface, error) {
var (
client clientset.Interface
err error
)
if d.dryRun {
client, err = getDryRunClient(d)
if err != nil {
return nil, err
}
} else { // Use a real client
client, err = kubeconfigutil.ClientSetFromFile(d.KubeConfigPath())
if err != nil {
return nil, err
}
}
return client, nil
}
// Tokens returns an array of token strings.
func (d *initData) Tokens() []string {
tokens := []string{}

View File

@ -45,6 +45,7 @@ type InitData interface {
ExternalCA() bool
OutputWriter() io.Writer
Client() (clientset.Interface, error)
ClientWithoutBootstrap() (clientset.Interface, error)
Tokens() []string
PatchesDir() string
}

View File

@ -31,22 +31,23 @@ type testInitData struct{}
// testInitData must satisfy InitData.
var _ InitData = &testInitData{}
func (t *testInitData) UploadCerts() bool { return false }
func (t *testInitData) CertificateKey() string { return "" }
func (t *testInitData) SetCertificateKey(key string) {}
func (t *testInitData) SkipCertificateKeyPrint() bool { return false }
func (t *testInitData) Cfg() *kubeadmapi.InitConfiguration { return nil }
func (t *testInitData) DryRun() bool { return false }
func (t *testInitData) SkipTokenPrint() bool { return false }
func (t *testInitData) IgnorePreflightErrors() sets.Set[string] { return nil }
func (t *testInitData) CertificateWriteDir() string { return "" }
func (t *testInitData) CertificateDir() string { return "" }
func (t *testInitData) KubeConfigDir() string { return "" }
func (t *testInitData) KubeConfigPath() string { return "" }
func (t *testInitData) ManifestDir() string { return "" }
func (t *testInitData) KubeletDir() string { return "" }
func (t *testInitData) ExternalCA() bool { return false }
func (t *testInitData) OutputWriter() io.Writer { return nil }
func (t *testInitData) Client() (clientset.Interface, error) { return nil, nil }
func (t *testInitData) Tokens() []string { return nil }
func (t *testInitData) PatchesDir() string { return "" }
func (t *testInitData) UploadCerts() bool { return false }
func (t *testInitData) CertificateKey() string { return "" }
func (t *testInitData) SetCertificateKey(key string) {}
func (t *testInitData) SkipCertificateKeyPrint() bool { return false }
func (t *testInitData) Cfg() *kubeadmapi.InitConfiguration { return nil }
func (t *testInitData) DryRun() bool { return false }
func (t *testInitData) SkipTokenPrint() bool { return false }
func (t *testInitData) IgnorePreflightErrors() sets.Set[string] { return nil }
func (t *testInitData) CertificateWriteDir() string { return "" }
func (t *testInitData) CertificateDir() string { return "" }
func (t *testInitData) KubeConfigDir() string { return "" }
func (t *testInitData) KubeConfigPath() string { return "" }
func (t *testInitData) ManifestDir() string { return "" }
func (t *testInitData) KubeletDir() string { return "" }
func (t *testInitData) ExternalCA() bool { return false }
func (t *testInitData) OutputWriter() io.Writer { return nil }
func (t *testInitData) Client() (clientset.Interface, error) { return nil, nil }
func (t *testInitData) ClientWithoutBootstrap() (clientset.Interface, error) { return nil, nil }
func (t *testInitData) Tokens() []string { return nil }
func (t *testInitData) PatchesDir() string { return "" }

View File

@ -82,9 +82,10 @@ func runWaitControlPlanePhase(c workflow.RunData) error {
// waiter holds the apiclient.Waiter implementation of choice, responsible for querying the API server in various ways and waiting for conditions to be fulfilled
klog.V(1).Infoln("[wait-control-plane] Waiting for the API server to be healthy")
client, err := data.Client()
// WaitForAPI uses the /healthz endpoint, thus a client without permissions works fine
client, err := data.ClientWithoutBootstrap()
if err != nil {
return errors.Wrap(err, "cannot obtain client")
return errors.Wrap(err, "cannot obtain client without bootstrap")
}
timeout := data.Cfg().ClusterConfiguration.APIServer.TimeoutForControlPlane.Duration