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
- Variables
- func ApplyHRCommonMetadata(docs []map[string]any, cm *helmv2.CommonMetadata)
- func ApplyHROriginLabels(docs []map[string]any, hr *manifest.HelmRelease)
- func Prepare(hr *manifest.HelmRelease, lookup manifest.HelmChartLookup, ...) (*manifest.HelmRelease, error)
- type ChartLoadResult
- type Client
- func (c *Client) LoadChart(ctx context.Context, hr *manifest.HelmRelease) (ChartLoadResult, error)
- func (c *Client) LocateChart(hr *manifest.HelmRelease) (string, error)
- func (c *Client) Resolver() SourceResolver
- func (c *Client) SetSourceResolver(r SourceResolver)
- func (c *Client) Template(ctx context.Context, hr *manifest.HelmRelease, hrValues map[string]any, ...) (string, error)
- func (c *Client) TemplateDocs(ctx context.Context, hr *manifest.HelmRelease, values map[string]any, ...) ([]map[string]any, error)
- func (c *Client) ValuesCache() *values.Cache
- type ClientOptions
- type Options
- type SecretGetter
- type SourceResolver
Constants ¶
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).
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 ¶
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 ApplyHRCommonMetadata ¶
func ApplyHRCommonMetadata(docs []map[string]any, cm *helmv2.CommonMetadata)
ApplyHRCommonMetadata merges spec.commonMetadata.labels and .annotations onto every workload rendered doc's metadata, mirroring helm-controller's CommonRenderer pass fed through PostRenderStrategyNoHooks. commonMetadata overwrites chart-template defaults but loses to origin labels on collision — ApplyHROriginLabels runs AFTER this and reasserts the helm.toolkit.fluxcd.io/{name,namespace} keys, matching helm-controller's build.go:46 comment.
Excluded from the stamping pass:
- Hooks (helm.sh/hook annotation): upstream's post-render chain runs after hook separation via PostRenderStrategyNoHooks.
- CRDs (CustomResourceDefinition kind): upstream's separate applyCRDs path attaches only origin labels via setOriginVisitor, never commonMetadata. Stamping CRDs in flate produced labels real Flux never applies in cluster.
func ApplyHROriginLabels ¶
func ApplyHROriginLabels(docs []map[string]any, hr *manifest.HelmRelease)
ApplyHROriginLabels stamps the helm.toolkit.fluxcd.io/{name,namespace} ownership labels onto every non-hook rendered doc, mirroring helm-controller's OriginLabels post-renderer fed through PostRenderStrategyNoHooks. Real Flux uses these to track which HR owns each in-cluster resource for pruning + selection (`kubectl get -l helm.toolkit.fluxcd.io/name=...`), and they take precedence over CommonMetadata when keys collide — so the caller runs this pass AFTER ApplyHRCommonMetadata, matching the upstream order.
Helm hooks (Job / ConfigMap with helm.sh/hook annotation) are intentionally NOT stamped — upstream's BuildPostRenderers chain is fed only the non-hook stream via PostRenderStrategyNoHooks. Stamping hooks in flate would produce labels real Flux never applies in cluster.
func Prepare ¶
func Prepare(hr *manifest.HelmRelease, lookup manifest.HelmChartLookup, provider values.Provider, cache *values.Cache) (*manifest.HelmRelease, error)
Prepare runs the standard pre-render dance for a HelmRelease so it is ready to feed into Client.Template / TemplateDocs:
- Clone hr so subsequent mutations don't touch the store-canonical copy (the immutability contract every flate controller honors).
- 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.
- 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 ¶
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 ¶
NewClient constructs a Client backed by the supplied Layout with project-wide defaults (DefaultClientOptions). helm.Client reads already-fetched charts off disk and composes no cache paths of its own — chart bytes live in the shared source.Cache (the content-addressed <root>/blobs/sha256/ store, so identical chart bytes dedup across HelmRepositories and embedders), populated by the source controller.
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 comes from the source cache (a content-addressed blob for HTTP tarballs, a ref-keyed slot for OCI) and is stable across reconciles; the per-path (mtime,size) re-check below guards a mutable OCI re-push.
func (*Client) LocateChart ¶
func (c *Client) LocateChart(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) 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) 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 a HelmRelease and returns its output split into individual documents (each a generic map).
It deliberately stops at split: List flattening, the post-render passes (ApplyHRCommonMetadata then ApplyHROriginLabels), and DropKinds are orchestrated by the caller — the helmrelease controller — so they run in the right order on the flattened docs. (Flattening must precede the label passes, or List-bundled resources would never receive the helm.toolkit.fluxcd.io ownership labels real Flux applies.)
func (*Client) ValuesCache ¶
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 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 ¶
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.