Skip to content

Instantly share code, notes, and snippets.

@gmorse81
Last active December 16, 2020 06:23
Show Gist options
  • Save gmorse81/67340239fa2ce01416766826724a74c9 to your computer and use it in GitHub Desktop.
Save gmorse81/67340239fa2ce01416766826724a74c9 to your computer and use it in GitHub Desktop.
This is an example of how to use the helm v3 go client with a GCP GKE cluster without calling the gcloud CLI or providing a kube config file. This has been cobbled together from many other examples found around the interwebs
package main
import (
"context"
"fmt"
"log"
"net/http"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"k8s.io/client-go/rest"
)
var (
googleScopes = []string{
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/userinfo.email",
}
_ rest.AuthProvider = &googleAuthProvider{}
)
const (
googleAuthPlugin = "google"
)
func init() {
if err := rest.RegisterAuthProviderPlugin(googleAuthPlugin, newGoogleAuthProvider); err != nil {
log.Fatalf("Failed to register %s auth plugin: %v", googleAuthPlugin, err)
}
}
type googleAuthProvider struct {
tokenSource oauth2.TokenSource
}
func (g *googleAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper {
return &oauth2.Transport{
Base: rt,
Source: g.tokenSource,
}
}
func (g *googleAuthProvider) Login() error { return nil }
func getTokenSource() (oauth2.TokenSource, error) {
// here i am using google.DefaultTokenSource, but other token sources could be used.
return google.DefaultTokenSource(context.TODO(), googleScopes...)
}
func newGoogleAuthProvider(addr string, config map[string]string, persister rest.AuthProviderConfigPersister) (rest.AuthProvider, error) {
ts, err := getTokenSource()
if err != nil {
return nil, fmt.Errorf("failed to create google token source: %+v", err)
}
return &googleAuthProvider{tokenSource: ts}, nil
}
package main
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"
)
//SimpleRESTClientGetter see RESTClientGetter
type SimpleRESTClientGetter struct {
Restconf *rest.Config
Namespace string
}
//NewRESTClientGetter see RESTClientGetter
func NewRESTClientGetter(r *rest.Config, n string) *SimpleRESTClientGetter {
return &SimpleRESTClientGetter{
Restconf: r,
Namespace: n,
}
}
//ToRESTConfig see RESTClientGetter
func (c *SimpleRESTClientGetter) ToRESTConfig() (*rest.Config, error) {
return c.Restconf, nil
}
//ToDiscoveryClient see RESTClientGetter
func (c *SimpleRESTClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
config, err := c.ToRESTConfig()
if err != nil {
return nil, err
}
config.Burst = 100
discoveryClient, _ := discovery.NewDiscoveryClientForConfig(config)
return memory.NewMemCacheClient(discoveryClient), nil
}
//ToRESTMapper see RESTClientGetter
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
}
//ToRawKubeConfigLoader see RESTClientGetter
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)
}
package main
import (
"context"
"encoding/base64"
"fmt"
"log"
"os"
container "cloud.google.com/go/container/apiv1"
"google.golang.org/api/option"
containerpb "google.golang.org/genproto/googleapis/container/v1"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"helm.sh/helm/v3/pkg/action"
"k8s.io/client-go/rest"
)
const clusterIdentifierString = "projects/YOURPROJECT/location/YOURREGION/cluster/YOURCLUSTERNAME"
func main() {
ctx := context.Background()
ts, err := getTokenSource()
if err != nil {
log.Fatalf("failed to create google token source: %+v", err)
}
c, err := container.NewClusterManagerClient(ctx, option.WithTokenSource(ts))
if err != nil {
log.Fatal(err)
}
req := &containerpb.GetClusterRequest{
Name: clusterIdentifierString,
}
resp, err := c.GetCluster(ctx, req)
if err != nil {
log.Fatal(err)
}
ca, err := base64.StdEncoding.DecodeString(resp.MasterAuth.GetClusterCaCertificate())
config := &rest.Config{
TLSClientConfig: rest.TLSClientConfig{
CAData: ca,
},
Host: "https://"+resp.Endpoint,
AuthProvider: &clientcmdapi.AuthProviderConfig{
Name: googleAuthPlugin,
},
}
// this commented code shows that at this point you can use the kube client-go.
// clientset, err := kubernetes.NewForConfig(config)
// if err != nil {
// panic(err.Error())
// }
// svcs, err := clientset.CoreV1().Services("").List(ctx, metav1.ListOptions{})
// if err != nil {
// panic(err)
// }
// log.Printf("There are %d servicesin the cluster", len(svcs.Items))
actionConfig := new(action.Configuration)
if err := actionConfig.Init(NewRESTClientGetter(config, ""), "", os.Getenv("HELM_DRIVER"), log.Printf); err != nil {
log.Printf("%+v", err)
os.Exit(1)
}
client := action.NewList(actionConfig)
// Only list deployed
client.Deployed = true
results, err := client.Run()
if err != nil {
log.Printf("%+v", err)
os.Exit(1)
}
for _, rel := range results {
fmt.Println(rel.Name+" "+rel.Namespace+" "+rel.Info.Status.String())
}
}
@gmorse81
Copy link
Author

gmorse81 commented Dec 16, 2020

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