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:
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
1
go.mod
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user