Armand Grillet

Introduction to the Go Helm client

One nice addon when using Helm is fullstatus. This addon gives you the list of all the API resources started to run a specific release.

Recently, I had to do something similar in Go which first required me to get all the Helm releases on a cluster. The documentation I found for this simple task was not great, I am thus writing it down here.

Initial setup

If your context is set, you do not need to specify a kubeconfig.

Getting releases

package main

import (
	"flag"
	"log"
	"path/filepath"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/kube"
	"k8s.io/client-go/util/homedir"
)

func main() {
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()

	actionConfig := new(action.Configuration)
	err := actionConfig.Init(kube.GetConfig(*kubeconfig, "", ""), "", "", log.Printf)
	if err != nil {
		log.Fatal(err)
	}

	releases, err := action.NewList(actionConfig).Run()
	if err != nil {
		log.Fatal(err)
	}

	for _, release := range releases {
		log.Println(release.Name)
	}
}

The *action.Configuration can be reused to interact with Helm. As you might have noticed, you do not need to register any API group & version to a scheme.

Using releases

The releases include manifests which are extremely useful to understand what is being deployed (this is what helm fullstatus does). For example, you can easily know which releases deploy which images:

var cleanLine string
var imageLine struct {
    Image string `yaml:"image"`
}
images := make(map[string]bool)

for _, release := range releases {
    scanner := bufio.NewScanner(strings.NewReader(release.Manifest))
    for scanner.Scan() {
        cleanLine = strings.Trim(scanner.Text(), " ")
        // Could be '$(params.parser-image)' due to template.
        if (strings.HasPrefix(cleanLine, "image: ") || strings.HasPrefix(cleanLine, "- image: ")) && !strings.Contains(cleanLine, "$") {
            cleanLine = strings.TrimLeft(scanner.Text(), "- ")
            err := yaml.Unmarshal([]byte(cleanLine), &imageLine)
            if err != nil {
                log.Fatal(err)
            }
            // If you don't specify a tag, Kubernetes assumes you mean `latest`.
            if !strings.Contains(imageLine.Image, ":") {
                imageLine.Image += ":latest"
            }
            images[imageLine.Image] = true
        }
    }

    log.Printf("%s : %v", release.Name, images)
}
Ready my other posts | Contact me on Twitter