helm

package
v0.0.0-...-46bd035 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 30, 2026 License: AGPL-3.0 Imports: 49 Imported by: 0

Documentation

Overview

Package helm wraps helm.sh/helm/v4 to render HelmReleases without shelling out to the `helm` binary.

The exported surface:

  • Client.Template / Client.TemplateDocs render a HelmRelease to YAML documents (the equivalent of `helm template --dry-run --client-only`).
  • Client.SetSourceResolver wires the canonical source-CR lookup surface — production callers pass NewStoreSourceResolver(store) so HelmRepository / OCIRepository / GitRepository / Bucket / ExternalArtifact lookups read straight from the canonical object store. Embedders rendering a single HR without an orchestrator can implement SourceResolver directly.
  • Prepare(hr, charts, provider) performs the pre-render dance (Clone → ResolveChartRef → ExpandValueReferences) — call this before TemplateDocs when rendering a HelmRelease in isolation.
  • Options exposes the helm CLI flags flate understands (--kube-version, --api-versions, --no-hooks, etc.).

The client is safe for concurrent use; chart downloads are cached on disk keyed by chart name + version, and parallel first-loads of the same chart coalesce through a per-path keylock.

Index

Constants

View Source
const DefaultRenderCacheBytes int64 = 1024 << 20

DefaultRenderCacheBytes is the default size of the persistent helm template-output cache: 1 GiB. The CLI surfaces this through the --helm-render-cache-mb flag (1024 by default; 0 disables).

View Source
const DefaultTemplateCacheBytes int64 = 256 << 20

DefaultTemplateCacheBytes is the historical default size of the helm template-output cache: 256 MiB. The CLI surfaces this through the --helm-template-cache-mb flag (256 by default; 0 disables).

Variables

View Source
var BundledKubeVersion = sync.OnceValue(detectBundledKubeVersion)

BundledKubeVersion is the Kubernetes minor version associated with the k8s.io/api module flate was built against. It mirrors what the upstream helm SDK would inject if running inside a cluster of that version, and is flate's default for the `--kube-version` flag.

The mapping is well-defined: k8s.io/api v0.X.Y corresponds to Kubernetes v1.X.Y. We derive X.Y at startup from build-info; if build-info is unavailable, we fall back to a sensible default.

Functions

func Prepare

Prepare runs the standard pre-render dance for a HelmRelease so it is ready to feed into Client.Template / TemplateDocs:

  1. Clone hr so subsequent mutations don't touch the store-canonical copy (the immutability contract every flate controller honors).
  2. Resolve hr.spec.chartRef via the supplied lookup. A chartRef points at a Flux HelmChart CR — typically emitted by a parent Kustomization render — and is rewritten into the concrete hr.Chart projection. Pass SourceResolver.HelmChart when an orchestrator is available, or a custom lookup otherwise.
  3. Expand values / valuesFrom against the supplied provider so hr.Values reflects the merged result a render would consume.

Embedders rendering a single HelmRelease without standing up the orchestrator's HR controller call Prepare then TemplateDocs. The returned *HelmRelease is the cloned, resolved one — pass it to Template / TemplateDocs and discard once rendered.

The Client's per-orchestrator values.Cache is threaded through so platform-wide valuesFrom CMs (a common pattern: one app-config CM referenced by N HRs) parse exactly once across the entire run.

Types

type ChartLoadResult

type ChartLoadResult struct {
	Path        string
	Chart       *chart.Chart
	Fingerprint string
}

ChartLoadResult is the loaded chart plus the on-disk path it came from.

Fingerprint, when non-empty, is a content-addressed sha256 hex of the chart's loader.Load inputs (Metadata + Templates + Files + Schema + chart defaults + subchart contents). Computed lazily by LoadChart so the template-output cache can build a stable key without re-walking the chart on every render. Memoized per (Client, path) keyed by the same (mtime, size) fingerprint chartCacheEntry uses, so a mutable OCI re-push invalidates this digest just as it invalidates the cached *chart.Chart pointer. Empty when the template cache is disabled.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client renders HelmReleases. Construct with NewClient.

func NewClient

func NewClient(layout cacheroot.Layout) (*Client, error)

NewClient constructs a Client backed by the supplied Layout with project-wide defaults (DefaultClientOptions). The helm-tmp/ and helm-cache/ directories are taken from the Layout — helm.Client never composes its own paths. The chart-tarball CAS is rooted at the SHARED <root>/blobs/sha256/ blob store so identical chart bytes deduplicate across HelmRepositories and across orchestrator embedders.

Disk-backed template-output cache defaults to enabled at DefaultRenderCacheBytes, rooted at layout.RenderHelmCache(). When layout.Root is empty (an embedder explicitly opted out of any persistent cache) the disk layer auto-disables so we don't write into the OS tempdir under a synthesised "flate-cache" subdir.

Embedders that need to tune cache sizing (TemplateCacheBytes, …) should call NewClientWithOptions instead.

func NewClientWithOptions

func NewClientWithOptions(layout cacheroot.Layout, opts ClientOptions) (*Client, error)

NewClientWithOptions is NewClient with explicit ClientOptions. Pass DefaultClientOptions() to get historical defaults; build the struct manually to disable individual caches (e.g. TemplateCacheBytes=0 for the template-output cache).

func (*Client) LoadChart

func (c *Client) LoadChart(ctx context.Context, hr *manifest.HelmRelease) (ChartLoadResult, error)

LoadChart resolves and loads the chart into helm's in-memory model. Parsed *chart.Chart values are cached by path — Helm's loader.Load reparses the tgz (and recompiles values.schema.json) on every call, which is a significant render-time hot spot when many HelmReleases share a base chart (bjw-s app-template, podinfo, common-library, …). Path is content-addressed by Helm's own cacher (name-version-digest), so this is safe across reconciles.

func (*Client) LocateChart

func (c *Client) LocateChart(ctx context.Context, hr *manifest.HelmRelease) (string, error)

LocateChart returns a filesystem path to the chart referenced by hr. The caller is responsible for cleanup (chart paths inside the cache are reused across calls; paths inside the tmp dir are not).

func (*Client) Resolver

func (c *Client) Resolver() SourceResolver

Resolver returns the configured SourceResolver, or nil when none has been wired. Exposed so the HelmRelease controller (and embedders calling Prepare) can pass resolver.HelmChart into ResolveChartRef without holding a separate reference to the resolver.

func (*Client) SetOCIPuller

func (c *Client) SetOCIPuller(p OCIPuller)

SetOCIPuller installs the OCI fetcher helm.Client routes HelmRepository(type=oci) and OCIRepository chart resolution through. The orchestrator wires source/oci.Fetcher when EnableOCI=true; embedders without one (or EnableOCI=false runs) leave it nil and helm.Client falls back to its built-in registry-client pull. Safe to call before any Template call.

func (*Client) SetSecretGetter

func (c *Client) SetSecretGetter(g SecretGetter)

SetSecretGetter installs a Secret lookup function so HelmRepository SecretRef credentials can be resolved at pull time. Safe to call before any Add* — typically once at orchestrator construction.

func (*Client) SetSourceResolver

func (c *Client) SetSourceResolver(r SourceResolver)

SetSourceResolver installs the canonical lookup surface for HelmRepository / OCIRepository / local-artifact sources. helm.Client reads through the resolver on every Template call — there's no alternate path. Safe to call before any template call; typically once at orchestrator construction.

func (*Client) SetTaskYield

func (c *Client) SetTaskYield(yield func(fn func()))

SetTaskYield installs a callback that the helm client uses to release its worker-pool slot during long-running network I/O (OCI pulls). The orchestrator wires this to task.Service.YieldSlot so concurrent helm renders don't block the pool while one of them is mid-pull. nil disables the yield (the I/O runs while still holding the slot — the legacy behavior for embedders without a task pool).

Kept as a callback rather than a direct task.Service dependency to avoid importing pkg/task into pkg/helm — the orchestrator wires the function reference and helm stays dependency-free.

func (*Client) Template

func (c *Client) Template(ctx context.Context, hr *manifest.HelmRelease, hrValues map[string]any, opts Options) (string, error)

Template renders a HelmRelease and returns the rendered manifest as a single YAML string (multiple documents separated by "---" lines).

Output is cached by computeTemplateKey when c.templateCache is wired (NewClient's default, sized by ClientOptions.TemplateCacheBytes). Cache hits skip action.Install.RunWithContext — the single largest CPU + allocation consumer in the codebase (cited at ~300 MB on a 200-HR run). Each key folds in every input that affects the rendered bytes (chart content fingerprint, fully-resolved values, render Options, HR-level action.Install fields like ReleaseName, CRDs policy, hooks, post-renderers) so a stale entry never serves a different render.

func (*Client) TemplateDocs

func (c *Client) TemplateDocs(ctx context.Context, hr *manifest.HelmRelease, values map[string]any, opts Options) ([]map[string]any, error)

TemplateDocs renders and returns each document parsed as a generic map.

func (*Client) ValuesCache

func (c *Client) ValuesCache() *values.Cache

ValuesCache returns the Client's per-orchestrator valuesFrom parse cache so callers (helm.Prepare, the HR controller) can pass it through to values.ExpandValueReferences. Always non-nil for Clients constructed via NewClient.

type ClientOptions

type ClientOptions struct {
	// TemplateCacheBytes caps the in-memory helm-template-output
	// cache. <= 0 disables the cache. Use DefaultTemplateCacheBytes
	// for the project-wide default.
	TemplateCacheBytes int64

	// RenderCacheBytes caps the persistent on-disk helm template-
	// output cache. <= 0 disables disk caching. Disk
	// caching is also disabled when RenderCacheRoot is empty even
	// if RenderCacheBytes > 0 — the wiring requires both.
	RenderCacheBytes int64

	// RenderCacheRoot is the on-disk directory the persistent
	// template-output cache writes into (typically
	// layout.RenderHelmCache()). Empty disables disk caching even
	// when RenderCacheBytes > 0.
	RenderCacheRoot string
}

ClientOptions tunes NewClientWithOptions construction. Callers that want historical defaults pass DefaultClientOptions(); embedders that want to disable specific caches (memory-constrained CI, embedders that prefer their own caching layer) build the struct field-by-field.

func DefaultClientOptions

func DefaultClientOptions() ClientOptions

DefaultClientOptions returns the ClientOptions a vanilla NewClient call uses — historical project defaults baked in. Disk caching is off by default at the constructor level: a vanilla NewClient caller hasn't provided a cache root, so we can't safely choose one for them. Callers wanting disk caching populate RenderCacheRoot explicitly (typically from layout.RenderHelmCache()).

type OCIPuller

OCIPuller fetches an OCI artifact into a content-addressed slot directory. Helm.Client uses it to route HelmRepository(type=oci) and OCIRepository chart resolution through the same machinery as source/oci.Fetcher — applying spec.verify / certSecretRef / proxySecretRef / insecure / layerSelector / ignore uniformly regardless of whether the chart was referenced via OCIRepository or HelmRepository(type=oci). When nil, helm.Client falls back to its built-in registry-client pull (no TLS/auth/verify surface) — matches the pre-unification behavior for EnableOCI=false runs.

source/oci.Fetcher satisfies this interface verbatim (type alias).

type Options

type Options struct {
	// SkipCRDs excludes CRDs from the rendered output.
	SkipCRDs bool
	// SkipTests excludes templates that are helm test hooks.
	SkipTests bool
	// SkipSecrets excludes Secret resources from the output. flate
	// uses placeholder values anyway but stripping makes diffs tidier.
	SkipSecrets bool
	// SkipKinds is an arbitrary list of kinds to drop.
	SkipKinds []string

	// KubeVersion overrides Capabilities.KubeVersion (.Capabilities.KubeVersion).
	KubeVersion string
	// APIVersions overrides Capabilities.APIVersions
	// (.Capabilities.APIVersions). Comma-separated.
	APIVersions string

	// IsUpgrade sets .Release.IsUpgrade instead of .Release.IsInstall.
	IsUpgrade bool
	// NoHooks excludes hook-annotated templates.
	NoHooks bool
	// ShowOnly limits output to specific template paths.
	ShowOnly []string
	// EnableDNS enables DNS lookups during templating.
	EnableDNS bool
	// SkipSchemaValidation opts out of `values.schema.json` validation
	// for every HR — helm's jsonschema validator recompiles the schema
	// per render and dominates allocation churn on big repos. The
	// per-HR placeholder-detection path still kicks in on top of this:
	// even with this flag false, schema validation is skipped when the
	// HR values contain a wipe placeholder.
	SkipSchemaValidation bool
}

Options collects the helm template flags flate supports.

func (Options) SkipResourceKinds

func (o Options) SkipResourceKinds() []string

SkipResourceKinds returns the union of canonical and user-specified kinds to drop from rendered output.

type SecretGetter

type SecretGetter = source.SecretGetter

SecretGetter is the same shape as source.SecretGetter; aliased so the helm Client and the source Fetchers consume one canonical type. The orchestrator wires the same closure into both.

type SourceResolver

type SourceResolver interface {
	// HelmRepository returns the HelmRepository at the given (ns, name)
	// or nil. Used for index.yaml fetch + SecretRef/CertSecretRef.
	HelmRepository(namespace, name string) *manifest.HelmRepository
	// OCIRepository returns the OCIRepository at the given (ns, name)
	// or nil. The .url field is the chart-artifact root.
	OCIRepository(namespace, name string) *manifest.OCIRepository
	// LocalSourceArtifact returns the fetched on-disk artifact for a
	// GitRepository / Bucket / ExternalArtifact source, or nil. Charts
	// live at <artifact.LocalPath>/<hr.Chart.Name> for any of those
	// three sourceRef.kind values.
	LocalSourceArtifact(kind, namespace, name string) *store.SourceArtifact
	// HelmChart returns the HelmChartSource at the given (ns, name) or
	// nil. Used by HelmRelease.ResolveChartRef to materialize a
	// spec.chartRef into a concrete chart reference.
	HelmChart(namespace, name string) *manifest.HelmChartSource
}

SourceResolver is the lookup surface helm.Client needs to resolve a HelmRelease's sourceRef against the canonical object store.

The interface exists so helm.Client reads source CRs from the canonical store on every lookup rather than maintaining a parallel registry — see NewStoreSourceResolver for the production-side adapter. Embedders rendering a single HelmRelease without an Orchestrator can implement this directly.

A nil return from any method means "not found" — callers translate that into a typed manifest.ErrObjectNotFound error.

func NewStoreSourceResolver

func NewStoreSourceResolver(s *store.Store) SourceResolver

NewStoreSourceResolver returns a SourceResolver backed by the canonical object store. The orchestrator wires this into helm.Client so chart-source lookups read straight from the Store.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL