Merge pull request #138686 from neolit123/automated-cherry-pick-of-#138449-origin-release-1.33

Automated cherry pick of #138449: kubeadm: use the localAPIEndpoint for all API calls in 'init'
This commit is contained in:
Kubernetes Prow Robot
2026-05-11 21:51:49 +05:30
committed by GitHub
8 changed files with 84 additions and 56 deletions

View File

@@ -19,10 +19,8 @@ package cmd
import (
"fmt"
"io"
"net"
"os"
"path/filepath"
"strconv"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -92,6 +90,7 @@ type initData struct {
skipTokenPrint bool
dryRun bool
kubeconfig *clientcmdapi.Config
kubeconfigOriginal *clientcmdapi.Config
kubeconfigDir string
kubeconfigPath string
ignorePreflightErrors sets.Set[string]
@@ -462,6 +461,9 @@ func (d *initData) CertificateDir() string {
}
// KubeConfig returns a kubeconfig after loading it from KubeConfigPath().
// If the default kubeconfig path is used (admin.conf), instead of constructing
// a kubeconfig that points to the control plane endpoint, make it point to the localAPIEndpoint.
// This would allow 'kubeadm init' to only talk to the local kube-apiserver instance.
func (d *initData) KubeConfig() (*clientcmdapi.Config, error) {
if d.kubeconfig != nil {
return d.kubeconfig, nil
@@ -472,10 +474,26 @@ func (d *initData) KubeConfig() (*clientcmdapi.Config, error) {
if err != nil {
return nil, err
}
d.kubeconfigOriginal = d.kubeconfig.DeepCopy()
if d.kubeconfigPath == kubeadmconstants.GetAdminKubeConfigPath() {
kubeconfigutil.PointKubeConfigToLocalAPIEndpoint(d.kubeconfig, &d.Cfg().LocalAPIEndpoint)
}
return d.kubeconfig, nil
}
// KubeConfigOriginal returns the original kubeconfig loaded from file, without any modifications.
func (d *initData) KubeConfigOriginal() (*clientcmdapi.Config, error) {
if d.kubeconfigOriginal == nil {
if _, err := d.KubeConfig(); err != nil {
return nil, err
}
}
return d.kubeconfigOriginal, nil
}
// KubeConfigDir returns the path of the Kubernetes configuration folder or the temporary folder path in case of DryRun.
func (d *initData) KubeConfigDir() string {
if d.dryRun {
@@ -520,8 +538,12 @@ func (d *initData) OutputWriter() io.Writer {
// 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) {
kubeconfig, err := d.KubeConfig()
if err != nil {
return nil, err
}
dryRun := apiclient.NewDryRun()
if err := dryRun.WithKubeConfigFile(d.KubeConfigPath()); err != nil {
if err := dryRun.WithKubeConfig(kubeconfig); err != nil {
return nil, err
}
dryRun.WithDefaultMarshalFunction().
@@ -549,7 +571,11 @@ func (d *initData) Client() (clientset.Interface, error) {
// and if the bootstrapping was not already done
if !d.adminKubeConfigBootstrapped && isDefaultKubeConfigPath {
// Call EnsureAdminClusterRoleBinding() to obtain a working client from admin.conf.
d.client, err = kubeconfigphase.EnsureAdminClusterRoleBinding(kubeadmconstants.KubernetesDir, nil)
d.client, err = kubeconfigphase.EnsureAdminClusterRoleBinding(
kubeadmconstants.KubernetesDir,
&d.Cfg().LocalAPIEndpoint,
nil,
)
if err != nil {
return nil, errors.Wrapf(err, "could not bootstrap the admin user in file %s", kubeadmconstants.AdminKubeConfigFileName)
}
@@ -570,30 +596,6 @@ func (d *initData) Client() (clientset.Interface, error) {
return d.client, nil
}
// WaitControlPlaneClient returns a basic client used for the purpose of waiting
// for control plane components to report 'ok' on their respective health check endpoints.
// It uses the admin.conf as the base, but modifies it to point at the local API server instead
// of the control plane endpoint.
func (d *initData) WaitControlPlaneClient() (clientset.Interface, error) {
config, err := clientcmd.LoadFromFile(d.KubeConfigPath())
if err != nil {
return nil, err
}
for _, v := range config.Clusters {
v.Server = fmt.Sprintf("https://%s",
net.JoinHostPort(
d.Cfg().LocalAPIEndpoint.AdvertiseAddress,
strconv.Itoa(int(d.Cfg().LocalAPIEndpoint.BindPort)),
),
)
}
client, err := kubeconfigutil.ToClientSet(config)
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

@@ -72,7 +72,7 @@ func runBootstrapToken(c workflow.RunData) error {
if err != nil {
return err
}
kubeconfig, err := data.KubeConfig()
kubeconfig, err := data.KubeConfigOriginal()
if err != nil {
return err
}

View File

@@ -40,6 +40,7 @@ type InitData interface {
CertificateWriteDir() string
CertificateDir() string
KubeConfig() (*clientcmdapi.Config, error)
KubeConfigOriginal() (*clientcmdapi.Config, error)
KubeConfigDir() string
KubeConfigPath() string
ManifestDir() string
@@ -47,7 +48,6 @@ type InitData interface {
ExternalCA() bool
OutputWriter() io.Writer
Client() (clientset.Interface, error)
WaitControlPlaneClient() (clientset.Interface, error)
Tokens() []string
PatchesDir() string
}

View File

@@ -32,24 +32,24 @@ 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) KubeConfig() (*clientcmdapi.Config, error) { return nil, nil }
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) WaitControlPlaneClient() (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) KubeConfig() (*clientcmdapi.Config, error) { return nil, nil }
func (t *testInitData) KubeConfigOriginal() (*clientcmdapi.Config, error) { return nil, nil }
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 "" }

View File

@@ -63,7 +63,7 @@ func runWaitControlPlanePhase(c workflow.RunData) error {
}
}
client, err := data.WaitControlPlaneClient()
client, err := data.Client()
if err != nil {
return errors.Wrap(err, "cannot obtain client without bootstrap")
}

View File

@@ -623,14 +623,19 @@ type EnsureRBACFunc func(context.Context, clientset.Interface, clientset.Interfa
// constructs a client from super-admin.conf if the file exists. It then proceeds
// to pass the clients to EnsureAdminClusterRoleBindingImpl. The function returns a
// usable client from admin.conf with RBAC properly constructed or an error.
func EnsureAdminClusterRoleBinding(outDir string, ensureRBACFunc EnsureRBACFunc) (clientset.Interface, error) {
func EnsureAdminClusterRoleBinding(outDir string, lae *kubeadmapi.APIEndpoint, ensureRBACFunc EnsureRBACFunc) (clientset.Interface, error) {
var (
err error
adminClient, superAdminClient clientset.Interface
)
// Create a client from admin.conf.
adminClient, err = kubeconfigutil.ClientSetFromFile(filepath.Join(outDir, kubeadmconstants.AdminKubeConfigFileName))
kubeconfig, err := clientcmd.LoadFromFile(filepath.Join(outDir, kubeadmconstants.AdminKubeConfigFileName))
if err != nil {
return nil, err
}
kubeconfigutil.PointKubeConfigToLocalAPIEndpoint(kubeconfig, lae)
adminClient, err = kubeconfigutil.ToClientSet(kubeconfig)
if err != nil {
return nil, err
}
@@ -638,7 +643,12 @@ func EnsureAdminClusterRoleBinding(outDir string, ensureRBACFunc EnsureRBACFunc)
// Create a client from super-admin.conf.
superAdminPath := filepath.Join(outDir, kubeadmconstants.SuperAdminKubeConfigFileName)
if _, err := os.Stat(superAdminPath); err == nil {
superAdminClient, err = kubeconfigutil.ClientSetFromFile(superAdminPath)
kubeconfig, err := clientcmd.LoadFromFile(superAdminPath)
if err != nil {
return nil, err
}
kubeconfigutil.PointKubeConfigToLocalAPIEndpoint(kubeconfig, lae)
superAdminClient, err = kubeconfigutil.ToClientSet(kubeconfig)
if err != nil {
return nil, err
}

View File

@@ -991,7 +991,7 @@ func TestEnsureAdminClusterRoleBinding(t *testing.T) {
}
}
client, err := EnsureAdminClusterRoleBinding(dir, ensureRBACFunc)
client, err := EnsureAdminClusterRoleBinding(dir, &kubeadmapi.APIEndpoint{}, ensureRBACFunc)
if (err != nil) != tc.expectedError {
t.Fatalf("expected error: %v, got: %v, error: %v", err != nil, tc.expectedError, err)
}

View File

@@ -18,13 +18,17 @@ package kubeconfig
import (
"fmt"
"net"
"os"
"strconv"
"github.com/pkg/errors"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
// CreateBasic creates a basic, general KubeConfig object that then can be extended
@@ -121,6 +125,18 @@ func GetClusterFromKubeConfig(config *clientcmdapi.Config) (string, *clientcmdap
return "", nil, errors.Errorf("the current context is invalid: %s", config.CurrentContext)
}
// PointKubeConfigToLocalAPIEndpoint modifies the provided kubeconfig to point to the given APIEndpoint.
func PointKubeConfigToLocalAPIEndpoint(config *clientcmdapi.Config, lae *kubeadmapi.APIEndpoint) {
for _, v := range config.Clusters {
v.Server = fmt.Sprintf("https://%s",
net.JoinHostPort(
lae.AdvertiseAddress,
strconv.Itoa(int(lae.BindPort)),
),
)
}
}
// HasAuthenticationCredentials returns true if the current user has valid authentication credentials for
// token authentication, basic authentication or X509 authentication
func HasAuthenticationCredentials(config *clientcmdapi.Config) bool {