mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-07 03:03:59 +00:00
Do not reload kubeconfig from disk
When `kubeadm init phase bootstrap-token` gets invoked, it reads the kubeconfig from disk repeatedly. This is wasteful, but, more importantly, it blocks the use of `/dev/stdin` and other sources of data that cannot be read repeatedly. This change introduces a new field that caches a parsed kubeconfig and when a new clientset is requested, it is converted from this pre-parsed kubeconfig, the code no longer reaches out to disk.
This commit is contained in:
parent
9d62330bfa
commit
6f06cd6e05
@ -28,6 +28,8 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
@ -87,6 +89,7 @@ type initData struct {
|
||||
cfg *kubeadmapi.InitConfiguration
|
||||
skipTokenPrint bool
|
||||
dryRun bool
|
||||
kubeconfig *clientcmdapi.Config
|
||||
kubeconfigDir string
|
||||
kubeconfigPath string
|
||||
ignorePreflightErrors sets.Set[string]
|
||||
@ -456,6 +459,21 @@ func (d *initData) CertificateDir() string {
|
||||
return d.certificatesDir
|
||||
}
|
||||
|
||||
// KubeConfig returns a kubeconfig after loading it from KubeConfigPath().
|
||||
func (d *initData) KubeConfig() (*clientcmdapi.Config, error) {
|
||||
if d.kubeconfig != nil {
|
||||
return d.kubeconfig, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
d.kubeconfig, err = clientcmd.LoadFromFile(d.KubeConfigPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d.kubeconfig, 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 {
|
||||
@ -536,7 +554,11 @@ func (d *initData) Client() (clientset.Interface, error) {
|
||||
d.adminKubeConfigBootstrapped = true
|
||||
} else {
|
||||
// Alternatively, just load the config pointed at the --kubeconfig path
|
||||
d.client, err = kubeconfigutil.ClientSetFromFile(d.KubeConfigPath())
|
||||
cfg, err := d.KubeConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.client, err = kubeconfigutil.ToClientSet(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -72,6 +72,10 @@ func runBootstrapToken(c workflow.RunData) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kubeconfig, err := data.KubeConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !data.SkipTokenPrint() {
|
||||
tokens := data.Tokens()
|
||||
@ -106,7 +110,7 @@ func runBootstrapToken(c workflow.RunData) error {
|
||||
}
|
||||
|
||||
// Create the cluster-info ConfigMap with the associated RBAC rules
|
||||
if err := clusterinfophase.CreateBootstrapConfigMapIfNotExists(client, data.KubeConfigPath()); err != nil {
|
||||
if err := clusterinfophase.CreateBootstrapConfigMapIfNotExists(client, kubeconfig); err != nil {
|
||||
return errors.Wrap(err, "error creating bootstrap ConfigMap")
|
||||
}
|
||||
if err := clusterinfophase.CreateClusterInfoRBACRules(client); err != nil {
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
)
|
||||
|
||||
@ -38,6 +39,7 @@ type InitData interface {
|
||||
IgnorePreflightErrors() sets.Set[string]
|
||||
CertificateWriteDir() string
|
||||
CertificateDir() string
|
||||
KubeConfig() (*clientcmdapi.Config, error)
|
||||
KubeConfigDir() string
|
||||
KubeConfigPath() string
|
||||
ManifestDir() string
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
)
|
||||
|
||||
@ -41,6 +42,7 @@ func (t *testInitData) SkipTokenPrint() bool { r
|
||||
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 "" }
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
@ -40,19 +40,20 @@ const (
|
||||
)
|
||||
|
||||
// CreateBootstrapConfigMapIfNotExists creates the kube-public ConfigMap if it doesn't exist already
|
||||
func CreateBootstrapConfigMapIfNotExists(client clientset.Interface, file string) error {
|
||||
func CreateBootstrapConfigMapIfNotExists(client clientset.Interface, kubeconfig *clientcmdapi.Config) error {
|
||||
|
||||
fmt.Printf("[bootstrap-token] Creating the %q ConfigMap in the %q namespace\n", bootstrapapi.ConfigMapClusterInfo, metav1.NamespacePublic)
|
||||
|
||||
klog.V(1).Infoln("[bootstrap-token] loading admin kubeconfig")
|
||||
adminConfig, err := clientcmd.LoadFromFile(file)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load admin kubeconfig")
|
||||
}
|
||||
if err = clientcmdapi.FlattenConfig(adminConfig); err != nil {
|
||||
// Clone the kubeconfig so that it's not mutated.
|
||||
adminConfig := kubeconfig.DeepCopy()
|
||||
if err := clientcmdapi.FlattenConfig(adminConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if adminConfig.Contexts[adminConfig.CurrentContext] == nil {
|
||||
return errors.New("invalid kubeconfig")
|
||||
}
|
||||
|
||||
adminCluster := adminConfig.Contexts[adminConfig.CurrentContext].Cluster
|
||||
// Copy the cluster from admin.conf to the bootstrap kubeconfig, contains the CA cert and the server URL
|
||||
klog.V(1).Infoln("[bootstrap-token] copying the cluster from admin.conf to the bootstrap kubeconfig")
|
||||
|
@ -17,8 +17,8 @@ limitations under the License.
|
||||
package clusterinfo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"text/template"
|
||||
"time"
|
||||
@ -31,6 +31,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
clientsetfake "k8s.io/client-go/kubernetes/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
bootstrapapi "k8s.io/cluster-bootstrap/token/api"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
@ -55,34 +56,24 @@ users:
|
||||
func TestCreateBootstrapConfigMapIfNotExists(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fileExist bool
|
||||
createErr error
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
"successful case should have no error",
|
||||
true,
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"if configmap already exists, return error",
|
||||
true,
|
||||
apierrors.NewAlreadyExists(schema.GroupResource{Resource: "configmaps"}, "test"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
"unexpected error should be returned",
|
||||
true,
|
||||
apierrors.NewUnauthorized("go away!"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
"if the file does not exist, return error",
|
||||
false,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
servers := []struct {
|
||||
@ -93,20 +84,12 @@ func TestCreateBootstrapConfigMapIfNotExists(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, server := range servers {
|
||||
file, err := os.CreateTemp("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create tempfile: %v", err)
|
||||
}
|
||||
defer os.Remove(file.Name())
|
||||
var buf bytes.Buffer
|
||||
|
||||
if err := testConfigTempl.Execute(file, server); err != nil {
|
||||
if err := testConfigTempl.Execute(&buf, server); err != nil {
|
||||
t.Fatalf("could not write to tempfile: %v", err)
|
||||
}
|
||||
|
||||
if err := file.Close(); err != nil {
|
||||
t.Fatalf("could not close tempfile: %v", err)
|
||||
}
|
||||
|
||||
// Override the default timeouts to be shorter
|
||||
defaultTimeouts := kubeadmapi.GetActiveTimeouts()
|
||||
defaultAPICallTimeout := defaultTimeouts.KubernetesAPICall
|
||||
@ -124,11 +107,12 @@ func TestCreateBootstrapConfigMapIfNotExists(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
fileName := file.Name()
|
||||
if !tc.fileExist {
|
||||
fileName = "notexistfile"
|
||||
kubeconfig, err := clientcmd.Load(buf.Bytes())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := CreateBootstrapConfigMapIfNotExists(client, fileName)
|
||||
|
||||
err = CreateBootstrapConfigMapIfNotExists(client, kubeconfig)
|
||||
if tc.expectErr && err == nil {
|
||||
t.Errorf("CreateBootstrapConfigMapIfNotExists(%s) wanted error, got nil", tc.name)
|
||||
} else if !tc.expectErr && err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user