Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I pass custom kube config to helm3 go client? #6910

Closed
vadasambar opened this issue Nov 8, 2019 · 18 comments
Closed

How do I pass custom kube config to helm3 go client? #6910

vadasambar opened this issue Nov 8, 2019 · 18 comments

Comments

@vadasambar
Copy link

vadasambar commented Nov 8, 2019

Hi, is there a way to pass custom kube config to helm go client? I want to use helm 3 client to programmatically install a helm chart in a remote cluster. I want to create a kube config in my code and pass it to the client so that I can install the chart in my remote Kubernetes cluster.

I went through the code and seems like I can pass genericclioptions.RESTClientGetter to client.New

func New(getter genericclioptions.RESTClientGetter) *Client {

But I am not sure about how I would go about creating my own genericclioptions.RESTClientGetter so that it also contains the custom kube config that I want to pass.

Thank you :D

@vadasambar
Copy link
Author

Related: #6024

@bacongobbler
Copy link
Member

bacongobbler commented Nov 8, 2019

@bacongobbler
Copy link
Member

clientcmd also has a canonical example for loading your own ClientConfig which implements the RESTClientGetter interface. https://godoc.org/k8s.io/client-go/tools/clientcmd

@vadasambar
Copy link
Author

Thank you for the links :D

I went through the resource links. This might be a silly question but I could not find anything in the second link which implemented RESTClientGetter interface (I searched for ToRESTConfig, one of the functions of RESTClientGetter interface but could not find anything implementing it).

Also, I am building rest.Config in my code by reading the the data from a Kubernetes Secret resource.

@turkenh
Copy link

turkenh commented Nov 21, 2019

@vadasambar the following worked for me to install a helm chart programatically with helm3.

package main

import (
	"fmt"
	"os"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/chart/loader"
	"helm.sh/helm/v3/pkg/kube"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
)

func main() {
	chartPath := "/tmp/my-chart-0.1.0.tgz"
	chart, err := loader.Load(chartPath)
	if err != nil {
		panic(err)
	}

	kubeconfigPath := "/tmp/my-kubeconfig"
	releaseName := "my-release"
	releaseNamespace := "default"
	actionConfig := new(action.Configuration)
	if err := actionConfig.Init(kube.GetConfig(kubeconfigPath, "", releaseNamespace), releaseNamespace, os.Getenv("HELM_DRIVER"), func(format string, v ...interface{}) {
		fmt.Sprintf(format, v)
	}); err != nil {
		panic(err)
	}

	iCli := action.NewInstall(actionConfig)
	iCli.Namespace = releaseNamespace
	iCli.ReleaseName = releaseName
	rel, err := iCli.Run(chart, nil)
	if err != nil {
		panic(err)
	}
	fmt.Println("Successfully installed release: ", rel.Name)
}

@vadasambar
Copy link
Author

Hey @turkenh ! Sorry for the late reply. Thank you for the example :D

@hickeyma
Copy link
Contributor

@vadasambar Closing as the issue is answered and example given. Thanks for the example @turkenh

@glianyi
Copy link

glianyi commented Dec 11, 2019

mark

@triThirty
Copy link

@vadasambar hi, I am searching for the RESTClientGetter object. Could you please teel me how to get an object which implements RESTClientGetter interface? Thank you so much.

@bahlo
Copy link

bahlo commented Mar 19, 2020

How would I initialize an action.Configuration with a kubconfig in memory as []byte? Do I really have to write it to disk, just to read it again?

@liuming-dev
Copy link
Contributor

liuming-dev commented Mar 19, 2020

How would I initialize an action.Configuration with a kubconfig in memory as []byte? Do I really have to write it to disk, just to read it again?

e.g.

	restClientGetter := helmapi.NewRESTClientGetter(namespace, kubeConfig)

	configuration := new(action.Configuration)
	if err := configuration.Init(restClientGetter, namespace, os.Getenv("HELM_DRIVER"), util.FakeDebugLog); err != nil {
		return nil, err
	}
func NewRESTClientGetter(namespace, kubeConfig string) *SimpleRESTClientGetter {
	return &SimpleRESTClientGetter{
		Namespace:  namespace,
		KubeConfig: kubeConfig,
	}
}

SimpleRESTClientGetter is an implementation of RESTClientGetter
KubeConfig is the string content of a KubeConfig

@bahlo
Copy link

bahlo commented Mar 19, 2020

@liuming-tech I'm sorry, but what pkg is helmapi? I can't find a SimpleRESTClientGetter anywhere.

@liuming-dev
Copy link
Contributor

@liuming-tech I'm sorry, but what pkg is helmapi? I can't find a SimpleRESTClientGetter anywhere.

Oh sorry, helmapi is pkg of private module, SimpleRESTClientGetter implements RESTClientGetter

func (c *SimpleRESTClientGetter) ToRESTConfig() (*rest.Config, error) {
	config, err := clientcmd.RESTConfigFromKubeConfig([]byte(c.KubeConfig))
	if err != nil {
		return nil, err
	}
	return config, nil
}

@hickeyma
Copy link
Contributor

hickeyma commented Mar 19, 2020

@bahlo Have a look at: https://helm.sh/docs/topics/advanced/#simple-example

@bahlo
Copy link

bahlo commented Mar 19, 2020

@hickeyma That's getting the kubeconfig path via env var, I want to pass in the kubeconfig itself.

@bahlo
Copy link

bahlo commented Mar 19, 2020

@liuming-tech Thanks, do you have the other three methods available as well? Otherwise I'll try to implement them myself.

@liuming-dev
Copy link
Contributor

@liuming-tech Thanks, do you have the other three methods available as well? Otherwise I'll try to implement them myself.

import (
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/discovery/cached/memory"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/restmapper"
	"k8s.io/client-go/tools/clientcmd"
)

type SimpleRESTClientGetter struct {
	Namespace  string
	KubeConfig string
}

func NewRESTClientGetter(namespace, kubeConfig string) *SimpleRESTClientGetter {
	return &SimpleRESTClientGetter{
		Namespace:  namespace,
		KubeConfig: kubeConfig,
	}
}

func (c *SimpleRESTClientGetter) ToRESTConfig() (*rest.Config, error) {
	config, err := clientcmd.RESTConfigFromKubeConfig([]byte(c.KubeConfig))
	if err != nil {
		return nil, err
	}
	return config, nil
}

func (c *SimpleRESTClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
	config, err := c.ToRESTConfig()
	if err != nil {
		return nil, err
	}

	// The more groups you have, the more discovery requests you need to make.
	// given 25 groups (our groups + a few custom conf) with one-ish version each, discovery needs to make 50 requests
	// double it just so we don't end up here again for a while.  This config is only used for discovery.
	config.Burst = 100

	discoveryClient, _ := discovery.NewDiscoveryClientForConfig(config)
	return memory.NewMemCacheClient(discoveryClient), nil
}

func (c *SimpleRESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) {
	discoveryClient, err := c.ToDiscoveryClient()
	if err != nil {
		return nil, err
	}

	mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient)
	expander := restmapper.NewShortcutExpander(mapper, discoveryClient)
	return expander, nil
}

func (c *SimpleRESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
	// use the standard defaults for this client command
	// DEPRECATED: remove and replace with something more accurate
	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig

	overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults}
	overrides.Context.Namespace = c.Namespace

	return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
}

If there are any improvement, tell me.

@bahlo
Copy link

bahlo commented Mar 20, 2020

@liuming-tech Thanks a lot, that helps!

I think in general a kube.Load(kubeconfig []byte) method would be useful in this pkg.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants