1
0
mirror of https://github.com/rancher/cli.git synced 2026-02-05 09:48:36 +01:00

Cache kubeconfig used for kubectl commands

This commit is contained in:
rmweir
2020-11-13 19:42:47 -07:00
committed by Ricardo Weir
parent 8de9f8e29a
commit be4db7f360
4 changed files with 113 additions and 9 deletions

View File

@@ -34,11 +34,13 @@ import (
managementClient "github.com/rancher/types/client/management/v3" managementClient "github.com/rancher/types/client/management/v3"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
"k8s.io/client-go/tools/clientcmd/api"
) )
const ( const (
letters = "abcdefghijklmnopqrstuvwxyz0123456789" letters = "abcdefghijklmnopqrstuvwxyz0123456789"
cfgFile = "cli2.json" cfgFile = "cli2.json"
kubeConfigKeyFormat = "%s-%s"
) )
var ( var (
@@ -143,6 +145,31 @@ func listRoleTemplateBindings(ctx *cli.Context, b []RoleTemplateBinding) error {
return writer.Err() return writer.Err()
} }
func getKubeConfigForUser(ctx *cli.Context, user string) (*api.Config, error) {
cf, err := loadConfig(ctx)
if err != nil {
return nil, err
}
focusedServer := cf.FocusedServer()
kubeConfig, _ := focusedServer.KubeConfigs[fmt.Sprintf(kubeConfigKeyFormat, user, focusedServer.FocusedCluster())]
return kubeConfig, nil
}
func setKubeConfigForUser(ctx *cli.Context, user string, kubeConfig *api.Config) error {
cf, err := loadConfig(ctx)
if err != nil {
return err
}
if cf.FocusedServer().KubeConfigs == nil {
cf.FocusedServer().KubeConfigs = make(map[string]*api.Config)
}
focusedServer := cf.FocusedServer()
focusedServer.KubeConfigs[fmt.Sprintf(kubeConfigKeyFormat, user, focusedServer.FocusedCluster())] = kubeConfig
return cf.Write()
}
func usersToNameMapping(u []managementClient.User) map[string]string { func usersToNameMapping(u []managementClient.User) map[string]string {
userMapping := make(map[string]string) userMapping := make(map[string]string)
for _, user := range u { for _, user := range u {

View File

@@ -5,8 +5,13 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"strings"
"github.com/rancher/norman/clientbase"
client "github.com/rancher/types/client/management/v3"
"github.com/urfave/cli" "github.com/urfave/cli"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/clientcmd/api"
) )
func KubectlCommand() cli.Command { func KubectlCommand() cli.Command {
@@ -37,29 +42,72 @@ func runKubectl(ctx *cli.Context) error {
return err return err
} }
cluster, err := getClusterByID(c, c.UserConfig.FocusedCluster()) config, err := loadConfig(ctx)
if err != nil { if err != nil {
return err return err
} }
config, err := c.ManagementClient.Cluster.ActionGenerateKubeconfig(cluster) currentRancherServer := config.FocusedServer()
if currentRancherServer == nil {
return fmt.Errorf("no focused server")
}
currentToken := currentRancherServer.AccessKey
t, err := c.ManagementClient.Token.ByID(currentToken)
if err != nil { if err != nil {
return err return err
} }
currentUser := t.UserID
kubeConfig, err := getKubeConfigForUser(ctx, currentUser)
if err != nil {
return err
}
var isTokenValid bool
if kubeConfig != nil {
tokenID, err := extractKubeconfigTokenID(*kubeConfig)
if err != nil {
return err
}
isTokenValid, err = validateToken(tokenID, c.ManagementClient.Token)
if err != nil {
return err
}
}
if kubeConfig == nil || !isTokenValid {
cluster, err := getClusterByID(c, c.UserConfig.FocusedCluster())
if err != nil {
return err
}
config, err := c.ManagementClient.Cluster.ActionGenerateKubeconfig(cluster)
if err != nil {
return err
}
kubeConfigBytes := []byte(config.Config)
kubeConfig, err = clientcmd.Load(kubeConfigBytes)
if err != nil {
return err
}
if err := setKubeConfigForUser(ctx, currentUser, kubeConfig); err != nil {
return err
}
}
tmpfile, err := ioutil.TempFile("", "rancher-") tmpfile, err := ioutil.TempFile("", "rancher-")
if err != nil { if err != nil {
return err return err
} }
defer os.Remove(tmpfile.Name()) defer os.Remove(tmpfile.Name())
_, err = tmpfile.Write([]byte(config.Config)) if err := clientcmd.WriteToFile(*kubeConfig, tmpfile.Name()); err != nil {
if err != nil {
return err return err
} }
if err := tmpfile.Close(); err != nil {
err = tmpfile.Close()
if err != nil {
return err return err
} }
@@ -74,3 +122,29 @@ func runKubectl(ctx *cli.Context) error {
} }
return nil return nil
} }
func extractKubeconfigTokenID(kubeconfig api.Config) (string, error) {
if len(kubeconfig.AuthInfos) != 1 {
return "", fmt.Errorf("invalid kubeconfig, expected to contain exactly 1 user")
}
var parts []string
for _, val := range kubeconfig.AuthInfos {
parts = strings.Split(val.Token, ":")
if len(parts) != 2 {
return "", fmt.Errorf("failed to parse kubeconfig token")
}
}
return parts[0], nil
}
func validateToken(tokenID string, tokenClient client.TokenOperations) (bool, error) {
token, err := tokenClient.ByID(tokenID)
if err != nil {
if !clientbase.IsNotFound(err) {
return false, err
}
return false, nil
}
return !token.Expired, nil
}

View File

@@ -8,6 +8,7 @@ import (
"strings" "strings"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"k8s.io/client-go/tools/clientcmd/api"
) )
// Config holds the main config for the user // Config holds the main config for the user
@@ -28,6 +29,7 @@ type ServerConfig struct {
Project string `json:"project"` Project string `json:"project"`
CACerts string `json:"cacert"` CACerts string `json:"cacert"`
KubeCredentials map[string]*ExecCredential `json:"kubeCredentials"` KubeCredentials map[string]*ExecCredential `json:"kubeCredentials"`
KubeConfigs map[string]*api.Config `json:"kubeConfigs"`
} }
func (c Config) Write() error { func (c Config) Write() error {

1
go.mod
View File

@@ -22,4 +22,5 @@ require (
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
k8s.io/client-go v12.0.0+incompatible
) )