From f2c270734ee227b03d35819e5007f73b3263b5eb Mon Sep 17 00:00:00 2001 From: Evgeniy Frolov Date: Wed, 21 Jan 2026 12:29:18 +0300 Subject: [PATCH 1/5] feat(kube): add support for kube token Signed-off-by: Evgeniy Frolov --- pkg/kube/kube.go | 64 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/pkg/kube/kube.go b/pkg/kube/kube.go index 82c1bba..4d3ca6c 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" @@ -81,6 +80,9 @@ type KubeConfigOptions struct { ConfigPath string ConfigDataBase64 string ConfigPathMergeList []string + + BearerToken string + BearerTokenFile string } type KubeConfig struct { @@ -91,8 +93,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 @@ -135,8 +138,11 @@ 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 } @@ -229,17 +235,22 @@ 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) + clientConfig, err := GetClientConfig( + opts.Context, + opts.ConfigPath, + configData, + opts.ConfigPathMergeList, + ) 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 +261,38 @@ 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 } + + applyBearerToken(config, opts) + 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) + clientConfig, err := GetClientConfig("", opts.ConfigPath, configData, opts.ConfigPathMergeList) if err != nil { return nil, err } @@ -289,16 +303,18 @@ func getOutOfClusterContextsClients(configPath, configDataBase64 string, configP } for contextName, context := range rc.Contexts { - clientConfig, err := GetClientConfig(contextName, configPath, configData, configPathMergeList) + clientConfig, err := GetClientConfig(contextName, opts.ConfigPath, configData, opts.ConfigPathMergeList) 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) } + applyBearerToken(config, opts) + clientset, err := kubernetes.NewForConfig(config) if err != nil { return nil, err @@ -323,7 +339,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 +419,13 @@ func restMapper(cachedDiscoveryClient *discovery.CachedDiscoveryInterface) meta. fmt.Printf(s) }) } + +func applyBearerToken(config *rest.Config, opts KubeConfigOptions) { + if opts.BearerToken != "" { + config.BearerToken = opts.BearerToken + config.BearerTokenFile = "" + } else if opts.BearerTokenFile != "" { + config.BearerTokenFile = opts.BearerTokenFile + config.BearerToken = "" + } +} From f5aa9539173e4c5babab8eda09f8c20c2e47b338 Mon Sep 17 00:00:00 2001 From: Evgeniy Frolov Date: Mon, 26 Jan 2026 12:42:59 +0300 Subject: [PATCH 2/5] chore: configure bearer token via clientcmd overrides Signed-off-by: Evgeniy Frolov --- pkg/kube/kube.go | 56 +++++++++++++++++++++++----------- pkg/kube/kube_config_getter.go | 2 +- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/pkg/kube/kube.go b/pkg/kube/kube.go index 4d3ca6c..feeda8b 100644 --- a/pkg/kube/kube.go +++ b/pkg/kube/kube.go @@ -21,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" ) @@ -185,8 +186,8 @@ 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 } @@ -243,11 +244,24 @@ func getOutOfClusterConfig(opts KubeConfigOptions) (*KubeConfig, error) { return nil, fmt.Errorf("unable to parse base64 config data: %w", err) } + 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(opts.ConfigDataBase64, opts.Context, err) @@ -267,8 +281,6 @@ func getOutOfClusterConfig(opts KubeConfigOptions) (*KubeConfig, error) { return nil, nil } - applyBearerToken(config, opts) - res.Config = config if opts.Context == "" { @@ -292,7 +304,21 @@ func getOutOfClusterContextsClients(opts KubeConfigOptions) ([]*ContextClient, e return nil, fmt.Errorf("unable to parse base64 config data: %w", err) } - clientConfig, err := GetClientConfig("", opts.ConfigPath, configData, opts.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 } @@ -303,7 +329,13 @@ func getOutOfClusterContextsClients(opts KubeConfigOptions) ([]*ContextClient, e } for contextName, context := range rc.Contexts { - clientConfig, err := GetClientConfig(contextName, opts.ConfigPath, configData, opts.ConfigPathMergeList) + clientConfig, err := GetClientConfig( + opts.Context, + opts.ConfigPath, + configData, + opts.ConfigPathMergeList, + overrides, + ) if err != nil { return nil, makeOutOfClusterClientConfigError(opts.ConfigPath, contextName, err) } @@ -313,8 +345,6 @@ func getOutOfClusterContextsClients(opts KubeConfigOptions) ([]*ContextClient, e return nil, makeOutOfClusterClientConfigError(opts.ConfigPath, contextName, err) } - applyBearerToken(config, opts) - clientset, err := kubernetes.NewForConfig(config) if err != nil { return nil, err @@ -419,13 +449,3 @@ func restMapper(cachedDiscoveryClient *discovery.CachedDiscoveryInterface) meta. fmt.Printf(s) }) } - -func applyBearerToken(config *rest.Config, opts KubeConfigOptions) { - if opts.BearerToken != "" { - config.BearerToken = opts.BearerToken - config.BearerTokenFile = "" - } else if opts.BearerTokenFile != "" { - config.BearerTokenFile = opts.BearerTokenFile - config.BearerToken = "" - } -} 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) } } From 19e5a0db5bc87c66a3ac5ae89d3add7130ecd9b4 Mon Sep 17 00:00:00 2001 From: Evgeniy Frolov Date: Wed, 28 Jan 2026 12:42:26 +0300 Subject: [PATCH 3/5] chore: lint Signed-off-by: Evgeniy Frolov --- pkg/kube/kube.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/kube/kube.go b/pkg/kube/kube.go index feeda8b..fd6ee58 100644 --- a/pkg/kube/kube.go +++ b/pkg/kube/kube.go @@ -187,7 +187,6 @@ func setConfigPathMergeListEnvironment(configPathMergeList []string) error { } func GetClientConfig(context, configPath string, configData []byte, configPathMergeList []string, overrides *clientcmd.ConfigOverrides) (clientcmd.ClientConfig, error) { - if context != "" { overrides.CurrentContext = context } From 99b263deddf7371e60ae668fd43627daa5e2391e Mon Sep 17 00:00:00 2001 From: Evgeniy Frolov Date: Wed, 28 Jan 2026 13:57:33 +0300 Subject: [PATCH 4/5] add context client with token Signed-off-by: Evgeniy Frolov --- pkg/kube/kube.go | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/pkg/kube/kube.go b/pkg/kube/kube.go index fd6ee58..1b02141 100644 --- a/pkg/kube/kube.go +++ b/pkg/kube/kube.go @@ -128,6 +128,8 @@ type GetAllContextsClientsOptions struct { ConfigPath string ConfigDataBase64 string ConfigPathMergeList []string + BearerToken string + BearerTokenFile string } type ContextClient struct { @@ -139,6 +141,7 @@ 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(KubeConfigOptions{ ConfigPath: opts.ConfigPath, ConfigDataBase64: opts.ConfigDataBase64, @@ -153,10 +156,23 @@ 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, + }) + if err != nil { + return nil, err + } + if tokenClient != nil { + return []*ContextClient{tokenClient}, nil + } + if outOfClusterErr != nil { return nil, outOfClusterErr } @@ -448,3 +464,25 @@ func restMapper(cachedDiscoveryClient *discovery.CachedDiscoveryInterface) meta. fmt.Printf(s) }) } + +func getTokenContextClient(opts KubeConfigOptions) (*ContextClient, error) { + kubeConfig, err := getOutOfClusterConfig(opts) + if err != nil { + return nil, err + } + + if kubeConfig == nil || kubeConfig.Config == nil { + return nil, nil + } + + clientset, err := kubernetes.NewForConfig(kubeConfig.Config) + if err != nil { + return nil, err + } + + return &ContextClient{ + ContextName: "token", + ContextNamespace: kubeConfig.DefaultNamespace, + Client: clientset, + }, nil +} From d35355f580f8760e8beb7a7e37bf66543fea1f43 Mon Sep 17 00:00:00 2001 From: Evgeniy Frolov Date: Wed, 28 Jan 2026 17:35:12 +0300 Subject: [PATCH 5/5] test Signed-off-by: Evgeniy Frolov --- pkg/kube/kube.go | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/pkg/kube/kube.go b/pkg/kube/kube.go index 1b02141..7a917b9 100644 --- a/pkg/kube/kube.go +++ b/pkg/kube/kube.go @@ -84,6 +84,10 @@ type KubeConfigOptions struct { BearerToken string BearerTokenFile string + + APIServerURL string + Insecure bool + CADataBase64 string } type KubeConfig struct { @@ -130,6 +134,10 @@ type GetAllContextsClientsOptions struct { ConfigPathMergeList []string BearerToken string BearerTokenFile string + + APIServerURL string + Insecure bool + CADataBase64 string } type ContextClient struct { @@ -165,6 +173,9 @@ func GetAllContextsClients(opts GetAllContextsClientsOptions) ([]*ContextClient, ConfigPathMergeList: opts.ConfigPathMergeList, BearerToken: opts.BearerToken, BearerTokenFile: opts.BearerTokenFile, + APIServerURL: opts.APIServerURL, + Insecure: opts.Insecure, + CADataBase64: opts.CADataBase64, }) if err != nil { return nil, err @@ -466,23 +477,27 @@ func restMapper(cachedDiscoveryClient *discovery.CachedDiscoveryInterface) meta. } func getTokenContextClient(opts KubeConfigOptions) (*ContextClient, error) { - kubeConfig, err := getOutOfClusterConfig(opts) - if err != nil { - return nil, err + if opts.BearerToken == "" || opts.APIServerURL == "" { + return nil, fmt.Errorf("cannot create client: missing token or API server URL") } - if kubeConfig == nil || kubeConfig.Config == nil { - return nil, nil + cfg := &rest.Config{ + Host: opts.APIServerURL, + BearerToken: opts.BearerToken, + TLSClientConfig: rest.TLSClientConfig{ + Insecure: opts.Insecure, + CAData: []byte(opts.CADataBase64), + }, } - clientset, err := kubernetes.NewForConfig(kubeConfig.Config) + clientset, err := kubernetes.NewForConfig(cfg) if err != nil { - return nil, err + return nil, fmt.Errorf("cannot create kubernetes client: %w", err) } return &ContextClient{ ContextName: "token", - ContextNamespace: kubeConfig.DefaultNamespace, + ContextNamespace: "", Client: clientset, }, nil }