diff --git a/pkg/kube/kube.go b/pkg/kube/kube.go index 82c1bba..7a917b9 100644 --- a/pkg/kube/kube.go +++ b/pkg/kube/kube.go @@ -3,7 +3,6 @@ package kube import ( "encoding/base64" "fmt" - "io/ioutil" "os" "path/filepath" "strings" @@ -22,6 +21,7 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/restmapper" "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/clientcmd/api" "github.com/werf/kubedog/pkg/utils" ) @@ -81,6 +81,13 @@ type KubeConfigOptions struct { ConfigPath string ConfigDataBase64 string ConfigPathMergeList []string + + BearerToken string + BearerTokenFile string + + APIServerURL string + Insecure bool + CADataBase64 string } type KubeConfig struct { @@ -91,8 +98,9 @@ type KubeConfig struct { func GetKubeConfig(opts KubeConfigOptions) (*KubeConfig, error) { // Try to load from kubeconfig in flags or from ~/.kube/config - config, outOfClusterErr := getOutOfClusterConfig(opts.Context, opts.ConfigPath, opts.ConfigDataBase64, opts.ConfigPathMergeList) - + config, outOfClusterErr := getOutOfClusterConfig( + opts, + ) if config == nil { if hasInClusterConfig() { // Try to configure as inCluster @@ -124,6 +132,12 @@ type GetAllContextsClientsOptions struct { ConfigPath string ConfigDataBase64 string ConfigPathMergeList []string + BearerToken string + BearerTokenFile string + + APIServerURL string + Insecure bool + CADataBase64 string } type ContextClient struct { @@ -135,8 +149,12 @@ type ContextClient struct { func GetAllContextsClients(opts GetAllContextsClientsOptions) ([]*ContextClient, error) { // Try to load contexts from kubeconfig in flags or from ~/.kube/config var outOfClusterErr error - contexts, outOfClusterErr := getOutOfClusterContextsClients(opts.ConfigPath, opts.ConfigDataBase64, opts.ConfigPathMergeList) - // return if contexts are loaded successfully + + contexts, outOfClusterErr := getOutOfClusterContextsClients(KubeConfigOptions{ + ConfigPath: opts.ConfigPath, + ConfigDataBase64: opts.ConfigDataBase64, + ConfigPathMergeList: opts.ConfigPathMergeList, + }) if len(contexts) > 0 { return contexts, nil } @@ -146,10 +164,26 @@ func GetAllContextsClients(opts GetAllContextsClientsOptions) ([]*ContextClient, if err != nil { return nil, err } - return []*ContextClient{contextClient}, nil } - // if not in cluster return outOfCluster error + + tokenClient, err := getTokenContextClient(KubeConfigOptions{ + ConfigPath: opts.ConfigPath, + ConfigDataBase64: opts.ConfigDataBase64, + ConfigPathMergeList: opts.ConfigPathMergeList, + BearerToken: opts.BearerToken, + BearerTokenFile: opts.BearerTokenFile, + APIServerURL: opts.APIServerURL, + Insecure: opts.Insecure, + CADataBase64: opts.CADataBase64, + }) + if err != nil { + return nil, err + } + if tokenClient != nil { + return []*ContextClient{tokenClient}, nil + } + if outOfClusterErr != nil { return nil, outOfClusterErr } @@ -179,8 +213,7 @@ func setConfigPathMergeListEnvironment(configPathMergeList []string) error { return nil } -func GetClientConfig(context, configPath string, configData []byte, configPathMergeList []string) (clientcmd.ClientConfig, error) { - overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults} +func GetClientConfig(context, configPath string, configData []byte, configPathMergeList []string, overrides *clientcmd.ConfigOverrides) (clientcmd.ClientConfig, error) { if context != "" { overrides.CurrentContext = context } @@ -229,17 +262,35 @@ func parseConfigDataBase64(configDataBase64 string) ([]byte, error) { return configData, nil } -func getOutOfClusterConfig(context, configPath, configDataBase64 string, configPathMergeList []string) (*KubeConfig, error) { +func getOutOfClusterConfig(opts KubeConfigOptions) (*KubeConfig, error) { res := &KubeConfig{} - configData, err := parseConfigDataBase64(configDataBase64) + configData, err := parseConfigDataBase64(opts.ConfigDataBase64) if err != nil { return nil, fmt.Errorf("unable to parse base64 config data: %w", err) } - clientConfig, err := GetClientConfig(context, configPath, configData, configPathMergeList) + overrides := &clientcmd.ConfigOverrides{ + ClusterDefaults: clientcmd.ClusterDefaults, + AuthInfo: api.AuthInfo{ + Token: opts.BearerToken, + TokenFile: opts.BearerTokenFile, + }, + } + + if opts.Context != "" { + overrides.CurrentContext = opts.Context + } + + clientConfig, err := GetClientConfig( + opts.Context, + opts.ConfigPath, + configData, + opts.ConfigPathMergeList, + overrides, + ) if err != nil { - return nil, makeOutOfClusterClientConfigError(configPath, context, err) + return nil, makeOutOfClusterClientConfigError(opts.ConfigDataBase64, opts.Context, err) } if ns, _, err := clientConfig.Namespace(); err != nil { @@ -250,35 +301,50 @@ func getOutOfClusterConfig(context, configPath, configDataBase64 string, configP config, err := clientConfig.ClientConfig() if err != nil { - return nil, makeOutOfClusterClientConfigError(configPath, context, err) + return nil, makeOutOfClusterClientConfigError(opts.ConfigDataBase64, opts.Context, err) } if config == nil { return nil, nil } + res.Config = config - if context == "" { + if opts.Context == "" { if rc, err := clientConfig.RawConfig(); err != nil { return nil, fmt.Errorf("cannot get raw kubernetes config: %w", err) } else { res.Context = rc.CurrentContext } } else { - res.Context = context + res.Context = opts.Context } return res, nil } -func getOutOfClusterContextsClients(configPath, configDataBase64 string, configPathMergeList []string) ([]*ContextClient, error) { +func getOutOfClusterContextsClients(opts KubeConfigOptions) ([]*ContextClient, error) { var res []*ContextClient - configData, err := parseConfigDataBase64(configDataBase64) + configData, err := parseConfigDataBase64(opts.ConfigDataBase64) if err != nil { return nil, fmt.Errorf("unable to parse base64 config data: %w", err) } - clientConfig, err := GetClientConfig("", configPath, configData, configPathMergeList) + overrides := &clientcmd.ConfigOverrides{ + ClusterDefaults: clientcmd.ClusterDefaults, + AuthInfo: api.AuthInfo{ + Token: opts.BearerToken, + TokenFile: opts.BearerTokenFile, + }, + } + + clientConfig, err := GetClientConfig( + opts.Context, + opts.ConfigPath, + configData, + opts.ConfigPathMergeList, + overrides, + ) if err != nil { return nil, err } @@ -289,14 +355,20 @@ func getOutOfClusterContextsClients(configPath, configDataBase64 string, configP } for contextName, context := range rc.Contexts { - clientConfig, err := GetClientConfig(contextName, configPath, configData, configPathMergeList) + clientConfig, err := GetClientConfig( + opts.Context, + opts.ConfigPath, + configData, + opts.ConfigPathMergeList, + overrides, + ) if err != nil { - return nil, makeOutOfClusterClientConfigError(configPath, contextName, err) + return nil, makeOutOfClusterClientConfigError(opts.ConfigPath, contextName, err) } config, err := clientConfig.ClientConfig() if err != nil { - return nil, makeOutOfClusterClientConfigError(configPath, contextName, err) + return nil, makeOutOfClusterClientConfigError(opts.ConfigPath, contextName, err) } clientset, err := kubernetes.NewForConfig(config) @@ -323,7 +395,7 @@ func getInClusterConfig() (*KubeConfig, error) { res.Config = config } - if data, err := ioutil.ReadFile(kubeNamespaceFilePath); err != nil { + if data, err := os.ReadFile(kubeNamespaceFilePath); err != nil { return nil, fmt.Errorf("in-cluster configuration problem: cannot determine default kubernetes namespace: error reading %s: %w", kubeNamespaceFilePath, err) } else { res.DefaultNamespace = string(data) @@ -403,3 +475,29 @@ func restMapper(cachedDiscoveryClient *discovery.CachedDiscoveryInterface) meta. fmt.Printf(s) }) } + +func getTokenContextClient(opts KubeConfigOptions) (*ContextClient, error) { + if opts.BearerToken == "" || opts.APIServerURL == "" { + return nil, fmt.Errorf("cannot create client: missing token or API server URL") + } + + cfg := &rest.Config{ + Host: opts.APIServerURL, + BearerToken: opts.BearerToken, + TLSClientConfig: rest.TLSClientConfig{ + Insecure: opts.Insecure, + CAData: []byte(opts.CADataBase64), + }, + } + + clientset, err := kubernetes.NewForConfig(cfg) + if err != nil { + return nil, fmt.Errorf("cannot create kubernetes client: %w", err) + } + + return &ContextClient{ + ContextName: "token", + ContextNamespace: "", + Client: clientset, + }, nil +} diff --git a/pkg/kube/kube_config_getter.go b/pkg/kube/kube_config_getter.go index c5223a1..3e8f4d0 100644 --- a/pkg/kube/kube_config_getter.go +++ b/pkg/kube/kube_config_getter.go @@ -169,6 +169,6 @@ func (getter *ClientGetterFromConfigData) getRawKubeConfigLoader() (clientcmd.Cl if data, err := base64.StdEncoding.DecodeString(getter.ConfigDataBase64); err != nil { return nil, fmt.Errorf("unable to decode base64 config data: %w", err) } else { - return GetClientConfig(getter.Context, "", data, nil) + return GetClientConfig(getter.Context, "", data, nil, nil) } }