diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index eae504c15c40d..63bc061757cc3 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -518,7 +518,10 @@ func (d *initData) Client() (clientset.Interface, error) { return nil, err } } else { // Use a real client - if !d.adminKubeConfigBootstrapped { + isDefaultKubeConfigPath := d.KubeConfigPath() == kubeadmconstants.GetAdminKubeConfigPath() + // Only bootstrap the admin.conf if it's used by the user (i.e. --kubeconfig has its default value) + // 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) if err != nil { @@ -526,8 +529,7 @@ func (d *initData) Client() (clientset.Interface, error) { } d.adminKubeConfigBootstrapped = true } else { - // In case adminKubeConfigBootstrapped is already set just return a client from the default - // kubeconfig location. + // Alternatively, just load the config pointed at the --kubeconfig path d.client, err = kubeconfigutil.ClientSetFromFile(d.KubeConfigPath()) if err != nil { return nil, err diff --git a/cmd/kubeadm/app/cmd/init_test.go b/cmd/kubeadm/app/cmd/init_test.go index 13bfb7deb2ccf..5b00de2ff344d 100644 --- a/cmd/kubeadm/app/cmd/init_test.go +++ b/cmd/kubeadm/app/cmd/init_test.go @@ -17,7 +17,10 @@ limitations under the License. package cmd import ( + "context" "fmt" + "net/http" + "net/http/httptest" "os" "path/filepath" "testing" @@ -54,6 +57,20 @@ kind: ClusterConfiguration controlPlaneEndpoint: "3.4.5.6" `, kubeadmapiv1.SchemeGroupVersion.String(), expectedCRISocket) +const testKubeconfigDataFormat = `--- +apiVersion: v1 +clusters: +- name: foo-cluster + cluster: + server: %s +contexts: +- name: foo-context + context: + cluster: foo-cluster +current-context: foo-context +kind: Config +` + func TestNewInitData(t *testing.T) { // create temp directory tmpDir, err := os.MkdirTemp("", "kubeadm-init-test") @@ -349,3 +366,38 @@ func expectedInitIgnorePreflightErrors(expectedItems ...string) func(t *testing. } } } + +func TestInitDataClientWithNonDefaultKubeconfig(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodHead { + w.WriteHeader(http.StatusMethodNotAllowed) + } + })) + defer ts.Close() + + kubeconfigPath := filepath.Join(t.TempDir(), "custom.conf") + if err := os.WriteFile(kubeconfigPath, []byte(fmt.Sprintf(testKubeconfigDataFormat, ts.URL)), 0o600); err != nil { + t.Fatalf("os.WriteFile returned unexpected error: %v", err) + } + + // initialize an external init option and inject it to the init cmd + initOptions := newInitOptions() + initOptions.skipCRIDetect = true // avoid CRI detection in unit tests + initOptions.kubeconfigPath = kubeconfigPath + cmd := newCmdInit(nil, initOptions) + + data, err := newInitData(cmd, nil, initOptions, nil) + if err != nil { + t.Fatalf("newInitData returned unexpected error: %v", err) + } + + client, err := data.Client() + if err != nil { + t.Fatalf("data.Client returned unexpected error: %v", err) + } + + result := client.Discovery().RESTClient().Verb("HEAD").Do(context.Background()) + if err := result.Error(); err != nil { + t.Fatalf("REST client request returned unexpected error: %v", err) + } +}