Examples

This document runs though a series of examples of using the Helm SDK. Intended to document various SDK functionalities.

The final example shows main.go driver ( link). That runs the below actions and includes necessary helper functions.

The code for the examples lives in the helm/helm-www/sdkexamples/ directory. And is intended to be fully functional.

Actions

Install Action

This example installs the given chart/release, for the given version and values:

package main

import (
	"context"
	"fmt"
	"log"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/chart/loader"
	"helm.sh/helm/v3/pkg/cli"
	"helm.sh/helm/v3/pkg/downloader"
	"helm.sh/helm/v3/pkg/getter"
)

func runInstall(ctx context.Context, logger *log.Logger, settings *cli.EnvSettings, releaseName string, chartRef string, chartVersion string, releaseValues map[string]interface{}) error {

	actionConfig, err := initActionConfig(settings, logger)
	if err != nil {
		return fmt.Errorf("failed to init action config: %w", err)
	}

	installClient := action.NewInstall(actionConfig)

	installClient.DryRunOption = "none"
	installClient.ReleaseName = releaseName
	installClient.Namespace = settings.Namespace()
	installClient.Version = chartVersion

	registryClient, err := newRegistryClientTLS(
		settings,
		logger,
		installClient.CertFile,
		installClient.KeyFile,
		installClient.CaFile,
		installClient.InsecureSkipTLSverify,
		installClient.PlainHTTP)
	if err != nil {
		return fmt.Errorf("failed to created registry client: %w", err)
	}
	installClient.SetRegistryClient(registryClient)

	chartPath, err := installClient.ChartPathOptions.LocateChart(chartRef, settings)
	if err != nil {
		return err
	}

	providers := getter.All(settings)

	chart, err := loader.Load(chartPath)
	if err != nil {
		return err
	}

	// Check chart dependencies to make sure all are present in /charts
	if chartDependencies := chart.Metadata.Dependencies; chartDependencies != nil {
		if err := action.CheckDependencies(chart, chartDependencies); err != nil {
			err = fmt.Errorf("failed to check chart dependencies: %w", err)
			if !installClient.DependencyUpdate {
				return err
			}

			manager := &downloader.Manager{
				Out:              logger.Writer(),
				ChartPath:        chartPath,
				Keyring:          installClient.ChartPathOptions.Keyring,
				SkipUpdate:       false,
				Getters:          providers,
				RepositoryConfig: settings.RepositoryConfig,
				RepositoryCache:  settings.RepositoryCache,
				Debug:            settings.Debug,
				RegistryClient:   installClient.GetRegistryClient(),
			}
			if err := manager.Update(); err != nil {
				return err
			}
			// Reload the chart with the updated Chart.lock file.
			if chart, err = loader.Load(chartPath); err != nil {
				return fmt.Errorf("failed to reload chart after repo update: %w", err)
			}
		}
	}

	release, err := installClient.RunWithContext(ctx, chart, releaseValues)
	if err != nil {
		return fmt.Errorf("failed to run install: %w", err)
	}

	logger.Printf("release created:\n%+v", *release)

	return nil
}

Upgrade Action

This example upgrades the given release, with the given chart, version and values:

package main

import (
	"context"
	"fmt"
	"log"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/chart/loader"
	"helm.sh/helm/v3/pkg/cli"
	"helm.sh/helm/v3/pkg/downloader"
	"helm.sh/helm/v3/pkg/getter"
)

func runUpgrade(ctx context.Context, logger *log.Logger, settings *cli.EnvSettings, releaseName string, chartRef string, chartVersion string, releaseValues map[string]interface{}) error {
	actionConfig, err := initActionConfig(settings, logger)
	if err != nil {
		return fmt.Errorf("failed to init action config: %w", err)
	}

	upgradeClient := action.NewUpgrade(actionConfig)

	upgradeClient.Namespace = settings.Namespace()
	upgradeClient.DryRunOption = "none"
	upgradeClient.Version = chartVersion

	registryClient, err := newRegistryClientTLS(
		settings,
		logger,
		upgradeClient.CertFile,
		upgradeClient.KeyFile,
		upgradeClient.CaFile,
		upgradeClient.InsecureSkipTLSverify,
		upgradeClient.PlainHTTP)
	if err != nil {
		return fmt.Errorf("missing registry client: %w", err)
	}
	upgradeClient.SetRegistryClient(registryClient)

	chartPath, err := upgradeClient.ChartPathOptions.LocateChart(chartRef, settings)
	if err != nil {
		return err
	}

	providers := getter.All(settings)

	// Check chart dependencies to make sure all are present in /charts
	chart, err := loader.Load(chartPath)
	if err != nil {
		return fmt.Errorf("failed to load chart: %w", err)
	}
	if req := chart.Metadata.Dependencies; req != nil {
		if err := action.CheckDependencies(chart, req); err != nil {
			err = fmt.Errorf("failed to check chart dependencies: %w", err)
			if !upgradeClient.DependencyUpdate {
				return err
			}

			man := &downloader.Manager{
				Out:              logger.Writer(),
				ChartPath:        chartPath,
				Keyring:          upgradeClient.ChartPathOptions.Keyring,
				SkipUpdate:       false,
				Getters:          providers,
				RepositoryConfig: settings.RepositoryConfig,
				RepositoryCache:  settings.RepositoryCache,
				Debug:            settings.Debug,
			}
			if err := man.Update(); err != nil {
				return err
			}
			// Reload the chart with the updated Chart.lock file.
			if chart, err = loader.Load(chartPath); err != nil {
				return fmt.Errorf("failed to reload chart after repo update: %w", err)
			}
		}
	}

	release, err := upgradeClient.RunWithContext(ctx, releaseName, chart, releaseValues)
	if err != nil {
		return fmt.Errorf("failed to run upgrade action: %w", err)
	}

	logger.Printf("release: %+v", release)

	return nil
}

Uninstall Action

This example uninstalls the given release

package main

import (
	"fmt"
	"log"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/cli"
)

func runUninstall(logger *log.Logger, settings *cli.EnvSettings, releaseName string) error {

	actionConfig, err := initActionConfig(settings, logger)
	if err != nil {
		return fmt.Errorf("failed to init action config: %w", err)
	}

	uninstallClient := action.NewUninstall(actionConfig)
	uninstallClient.DeletionPropagation = "foreground" // "background" or "orphan"

	result, err := uninstallClient.Run(releaseName)
	if err != nil {
		return fmt.Errorf("failed to run uninstall action: %w", err)
	}
	if result != nil {
		logger.Printf("result: %+v\n", *result.Release)
	}

	logger.Printf("release \"%s\" uninstalled\n", releaseName)

	return nil
}

List Action

This example lists all released charts (in the currently configured namespace)

package main

import (
	"fmt"
	"log"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/cli"
)

func runList(logger *log.Logger, settings *cli.EnvSettings) error {

	actionConfig, err := initActionConfigList(settings, logger, false)
	if err != nil {
		return fmt.Errorf("failed to init action config: %w", err)
	}

	listClient := action.NewList(actionConfig)
	// Only list deployed
	//listClient.Deployed = true
	listClient.All = true
	listClient.SetStateMask()

	results, err := listClient.Run()
	if err != nil {
		return fmt.Errorf("failed to run list action: %w", err)
	}

	for _, rel := range results {
		logger.Printf("list result: %+v", rel)
	}

	return nil
}

Pull Action

This example pulls a chart from an OCI repository

package main

import (
	"fmt"
	"log"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/cli"
)

func runPull(logger *log.Logger, settings *cli.EnvSettings, chartRef, chartVersion string) error {

	actionConfig, err := initActionConfig(settings, logger)
	if err != nil {
		return fmt.Errorf("failed to init action config: %w", err)
	}

	registryClient, err := newRegistryClient(settings, false)
	if err != nil {
		return fmt.Errorf("failed to created registry client: %w", err)
	}
	actionConfig.RegistryClient = registryClient

	pullClient := action.NewPullWithOpts(
		action.WithConfig(actionConfig))
	// client.RepoURL = ""
	pullClient.DestDir = "./"
	pullClient.Settings = settings
	pullClient.Version = chartVersion

	result, err := pullClient.Run(chartRef)
	if err != nil {
		return fmt.Errorf("failed to pull chart: %w", err)
	}

	logger.Printf("%+v", result)

	return nil
}

Driver

The driver here shows the necessary auxillary functions needed for the Helm SDK actions to function. And shows the above examples in action, to pull, install, update, and uninstall the 'podinfo' chart from an OCI repository.

package main

import (
	"bufio"
	"context"
	"fmt"
	"log"
	"os"

	"helm.sh/helm/v3/pkg/action"
	"helm.sh/helm/v3/pkg/cli"
	"helm.sh/helm/v3/pkg/registry"
)

var helmDriver string = os.Getenv("HELM_DRIVER")

func initActionConfig(settings *cli.EnvSettings, logger *log.Logger) (*action.Configuration, error) {
	return initActionConfigList(settings, logger, false)
}

func initActionConfigList(settings *cli.EnvSettings, logger *log.Logger, allNamespaces bool) (*action.Configuration, error) {

	actionConfig := new(action.Configuration)

	namespace := func() string {
		// For list action, you can pass an empty string instead of settings.Namespace() to list
		// all namespaces
		if allNamespaces {
			return ""
		}
		return settings.Namespace()
	}()

	if err := actionConfig.Init(
		settings.RESTClientGetter(),
		namespace,
		helmDriver,
		logger.Printf); err != nil {
		return nil, err
	}

	return actionConfig, nil
}

func newRegistryClient(settings *cli.EnvSettings, plainHTTP bool) (*registry.Client, error) {
	opts := []registry.ClientOption{
		registry.ClientOptDebug(settings.Debug),
		registry.ClientOptEnableCache(true),
		registry.ClientOptWriter(os.Stderr),
		registry.ClientOptCredentialsFile(settings.RegistryConfig),
	}
	if plainHTTP {
		opts = append(opts, registry.ClientOptPlainHTTP())
	}

	// Create a new registry client
	registryClient, err := registry.NewClient(opts...)
	if err != nil {
		return nil, err
	}
	return registryClient, nil
}

func newRegistryClientTLS(settings *cli.EnvSettings, logger *log.Logger, certFile, keyFile, caFile string, insecureSkipTLSverify, plainHTTP bool) (*registry.Client, error) {
	if certFile != "" && keyFile != "" || caFile != "" || insecureSkipTLSverify {
		registryClient, err := registry.NewRegistryClientWithTLS(
			logger.Writer(),
			certFile,
			keyFile,
			caFile,
			insecureSkipTLSverify,
			settings.RegistryConfig,
			settings.Debug)

		if err != nil {
			return nil, err
		}
		return registryClient, nil
	}
	registryClient, err := newRegistryClient(settings, plainHTTP)
	if err != nil {
		return nil, err
	}
	return registryClient, nil
}

func main() {

	logger := log.Default()

	// For convenience, initialize SDK setting via CLI mechanism
	settings := cli.New()

	// Release name, chart and values
	releaseName := "helm-sdk-example"
	chartRef := "oci://ghcr.io/stefanprodan/charts/podinfo"
	releaseValues := map[string]interface{}{
		"replicaCount": "2",
	}

	// Pull the chart to the local filesystem
	if err := runPull(logger, settings, chartRef, "6.4.1"); err != nil {
		fmt.Printf("failed to run pull: %+v", err)
		os.Exit(1)
	}

	// Install the chart (from the pulled chart local archive)
	if err := runInstall(context.TODO(), logger, settings, releaseName, "./podinfo-6.4.1.tgz", "", releaseValues); err != nil {
		fmt.Printf("failed to run install: %+v", err)
		os.Exit(1)
	}

	// List installed charts
	if err := runList(logger, settings); err != nil {
		fmt.Printf("failed to run list: %+v", err)
		os.Exit(1)
	}

	//
	fmt.Print("Chart installed. Press 'Return' to continue...")
	bufio.NewReader(os.Stdin).ReadBytes('\n')

	// Upgrade to version 6.5.4, updating the replicaCount to three
	releaseValues["replicaCount"] = "3"
	if err := runUpgrade(context.TODO(), logger, settings, releaseName, chartRef, "6.5.4", releaseValues); err != nil {
		fmt.Printf("failed to run upgrade: %+v", err)
		os.Exit(1)
	}

	// List installed charts
	if err := runList(logger, settings); err != nil {
		fmt.Printf("failed to run list: %+v", err)
		os.Exit(1)
	}

	//
	fmt.Print("Chart upgraded. Press 'Return' to continue...")
	bufio.NewReader(os.Stdin).ReadBytes('\n')

	// Uninstall the chart
	if err := runUninstall(logger, settings, releaseName); err != nil {
		fmt.Printf("failed to run uninstall: %+v", err)
		os.Exit(1)
	}
}