Helm — Interfaces#
Interface catalog#
kube.Interface#
- Package:
helm.sh/helm/v4/pkg/kube - File:
pkg/kube/interface.go:31 - Methods:
Get(resources ResourceList, related bool) (map[string][]runtime.Object, error) Create(resources ResourceList, options ...ClientCreateOption) (*Result, error) Delete(resources ResourceList, policy metav1.DeletionPropagation) (*Result, []error) Update(original, target ResourceList, options ...ClientUpdateOption) (*Result, error) Build(reader io.Reader, validate bool) (ResourceList, error) IsReachable() error GetWaiter(ws WaitStrategy) (Waiter, error) GetPodList(namespace string, listOptions metav1.ListOptions) (*v1.PodList, error) OutputContainerLogsForPodList(podList *v1.PodList, namespace string, writerFunc func(...) io.Writer) error BuildTable(reader io.Reader, validate bool) (ResourceList, error) - Purpose: Abstracts all Kubernetes cluster operations needed by the action layer. The entire action layer (Install, Upgrade, Rollback, Uninstall) communicates with the cluster exclusively through this interface — no
client-gotypes leak upward. - Implementations:
kube.Client(real cluster, backed byclient-go+fluxcd/cli-utilsfor SSA),kubefake.Clientandkubefake.FailingKubeClient(test doubles inpkg/kube/fake/) - Design quality: Broad by Go interface standards (10 methods), but each method maps 1:1 to a Kubernetes CRUD or readiness concern. The comment explicitly requires concurrency safety. The
BuildTablemethod has a TODO noting it should be folded intoBuildin Helm 5 — the designers are aware of the surface area. Well-segregated for its use case.
kube.Waiter#
- Package:
helm.sh/helm/v4/pkg/kube - File:
pkg/kube/interface.go:81 - Methods:
Wait(resources ResourceList, timeout time.Duration) error WaitWithJobs(resources ResourceList, timeout time.Duration) error WaitForDelete(resources ResourceList, timeout time.Duration) error WatchUntilReady(resources ResourceList, timeout time.Duration) error - Purpose: Separates readiness-waiting logic from CRUD operations. Obtained from
Interface.GetWaiter(WaitStrategy), allowing the wait strategy (regular, with-jobs) to select an appropriate implementation. - Implementations: Returned by
kube.Client.GetWaiter()— the real waiter usesclient-gowatches; the fake implementation is a no-op. - Design quality: Well-segregated from
Interface— waiting is a distinct concern from resource manipulation. TheWatchUntilReadyis specifically for hook lifecycle, with detailed documentation on what “ready” means per Kind.
driver.Driver (composed from role interfaces)#
- Package:
helm.sh/helm/v4/pkg/storage/driver - File:
pkg/storage/driver/driver.go:99 - Methods: Composed from four role interfaces plus
Name() string:// Creator Create(key string, rls release.Releaser) error // Updator Update(key string, rls release.Releaser) error // Deletor Delete(key string) (release.Releaser, error) // Queryor Get(key string) (release.Releaser, error) List(filter func(release.Releaser) bool) ([]release.Releaser, error) Query(labels map[string]string) ([]release.Releaser, error) // Driver itself Name() string - Purpose: Persistence contract for release records. The
Storagewrapper adds MaxHistory enforcement on top of this interface. - Implementations:
driver.Secrets(Kubernetes Secrets, default),driver.ConfigMaps(Kubernetes ConfigMaps),driver.Memory(in-process, for testing),driver.SQL(PostgreSQL viajmoiron/sqlx, new in v4) - Design quality: The decomposition into
Creator,Updator,Deletor,Queryoris a textbook Interface Segregation Principle application. The named role interfaces exist as standalone types, so code that only needs read access can declaredriver.Queryorrather than the fullDriver. The use ofrelease.Releaser(a type alias forany) in method signatures is a v4 versioning strategy to allow the underlying release struct to evolve across major versions.
postrenderer.PostRenderer#
- Package:
helm.sh/helm/v4/pkg/postrenderer - File:
pkg/postrenderer/postrenderer.go:30 - Methods:
Run(renderedManifests *bytes.Buffer) (modifiedManifests *bytes.Buffer, err error) - Purpose: Extension point between template rendering and Kubernetes apply. After
pkg/enginerenders all chart templates to a YAML stream, the action layer optionally passes the entire stream through this interface before splitting it and sending to the kube client. Enables arbitrary YAML transformation (Kustomize overlays, custom scripts, WASM plugins). - Implementations:
postRendererPlugin(backed byplugin.Runtime— exec-based or WASM), plus any user-supplied implementation (the interface is public and exported for embedding in third-party tools). - Design quality: Minimal, single-method interface — maximum implementability. The
*bytes.Bufferchoice (rather thanio.Reader/io.Writer) is pragmatic: callers need to know when the buffer is complete before splitting it back into per-file records. This is the primary extensibility hook for ecosystem tools.
action.RESTClientGetter#
- Package:
helm.sh/helm/v4/pkg/action - File:
pkg/action/action.go:416 - Methods:
ToRESTConfig() (*rest.Config, error) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) ToRESTMapper() (meta.RESTMapper, error) - Purpose: Abstracts how a
*rest.Config(Kubernetes REST client configuration) is obtained. Stored onConfigurationand used lazily to construct thekube.Clientand capability discovery on first use. ThelazyClientpattern inpkg/action/lazyclient.gowraps this getter. - Implementations:
cli.EnvSettings.RESTClientGetter()returns the standardgenericclioptions.ConfigFlagsfromk8s.io/cli-runtime, which reads kubeconfig files. Test code passes custom implementations. - Design quality: Intentionally mirrors the
k8s.io/cli-runtimeRESTClientGetterinterface — Helm does not reinvent this; it provides its own type so it is not forced to take a direct dependency oncli-runtimeinpkg/action. A clean boundary-seam interface.
getter.Getter#
- Package:
helm.sh/helm/v4/pkg/getter - File:
pkg/getter/getter.go:156 - Methods:
Get(url string, options ...Option) (*bytes.Buffer, error) - Purpose: Protocol-agnostic chart fetching. Used by
pkg/downloaderand the action layer to retrieve charts from HTTP/HTTPS endpoints, OCI registries, or local paths. TheProvider/Providersregistry maps URL schemes toConstructorfunctions that produceGetterinstances. - Implementations:
httpGetter(HTTP/HTTPS),OCIGetter(OCI registries), local filesystem path (implicitly, via chart loader) - Design quality: Single-method interface — ideal for the strategy pattern. The
Provider/Providersregistry design allows third parties to register new schemes without modifying core code, though this extension point is not advertised as a plugin API.
release.Accessor and chart.Accessor#
- Package:
helm.sh/helm/v4/pkg/release,helm.sh/helm/v4/pkg/chart - Files:
pkg/release/interfaces.go:29,pkg/chart/interfaces.go:26 - Methods (release.Accessor):
Name() string; Namespace() string; Version() int; Hooks() []Hook Manifest() string; Notes() string; Labels() map[string]string Chart() chart.Charter; Status() string; ApplyMethod() string; DeployedAt() time.Time - Methods (chart.Accessor):
Name() string; IsRoot() bool; MetadataAsMap() map[string]any Files() []*common.File; Templates() []*common.File; ChartFullPath() string IsLibraryChart() bool; Dependencies() []Charter; MetaDependencies() []Dependency Values() map[string]any; Schema() []byte; Deprecated() bool - Purpose: Version-stable access facades for the
ReleaseandChartdomain objects. In Helm v4 the underlying structs are versioned (e.g.,pkg/release/v1); these interfaces allow code that works across versions to depend on the interface rather than a specific struct. The type aliasesReleaser anyandCharter anyin the same packages allow untyped storage at storage-driver boundaries. - Implementations:
release/v1.Release,chart/v2.Chartandchart/v3.Chart - Design quality: Deliberately broad — they expose the full domain object via accessor methods. This is a versioning strategy, not an ISP exercise. The use of
anytype aliases alongside concrete interfaces is an unconventional but intentional Helm v4 design for forward compatibility.
Interface patterns#
- Size distribution: Wide range. Single-method (
PostRenderer,Getter) to 10-method (kube.Interface). The storage driver interfaces explicitly decompose a 7-method set into 4 role interfaces of 1-3 methods each. Overall the project leans toward purposefully-sized interfaces. - Embedding: Heavy use in
driver.Driver, which embedsCreator + Updator + Deletor + Queryor. Alsokube.InterfaceWaitOptionsextendskube.Interfacepattern (though implemented separately). Embedding is used as a composition mechanism for both the driver and for the Accessor interfaces that inheritCharter/Hookmarkers. - Implicit satisfaction: Mixed. Core infrastructure interfaces (
kube.Interface,driver.Driver) are defined by their packages (provider-defined), but the implementations are in the same or adjacent packages.PostRendererandGetterare clearly consumer-defined — they exist so third parties can implement them.RESTClientGetteris defined inpkg/action(the consumer) rather than inpkg/kube(the infrastructure side), which is correct Go style. - stdlib interfaces used:
io.Readerinkube.Interface.Build()andBuildTable();io.Writerindirectly viawriterFunccallbacks;fmt.Stringeris not implemented on major types (they exposeName()rather thanString()). Thebytes.BufferinPostRendererreplacesio.ReadWriterfor practical reasons.
Key abstractions#
kube.Interface— The most architecturally load-bearing interface. All action types talk to Kubernetes only through this abstraction, enabling the entire test suite to run without a real cluster. Its fake implementations are as important as the interface itself.driver.Driverand its role interfaces — The cleanest ISP example in the codebase. The four decomposed role interfaces (Creator,Updator,Deletor,Queryor) are individually usable and make the storage contract explicit and minimal per caller. The SQL backend landing in v4 vindicates this design.postrenderer.PostRenderer— The primary extension point for ecosystem tools. Its single-method design makes it trivially implementable. Kustomize integration, custom webhook transforms, and the upcoming WASM renderer all implement exactly this one method.action.RESTClientGetter— Architectural seam that decouplespkg/action(the embeddable library) fromk8s.io/cli-runtime(a CLI concern). Without this interface, embeddingpkg/actionwould drag in kubeconfig-file-reading logic appropriate only for CLI use.getter.Getter— The protocol-abstraction interface that makes Helm’s chart sources pluggable (HTTP, OCI, local). Combined with theProvider/Providersregistry, it defines a scheme-dispatch pattern that lets new transport protocols be added without touching the downloader or action layer.
Interface-driven extensibility#
Helm uses interfaces at three distinct extensibility tiers:
Tier 1 — Internal swappability (testability): kube.Interface, driver.Driver, action.RESTClientGetter. These exist primarily to enable the fake/stub implementations used in unit tests. The real and fake implementations live in the same package or an adjacent fake/ sub-package. Users of pkg/action as a library can also swap these to connect Helm’s action layer to non-standard Kubernetes environments (e.g., dry-run recorders, auditing proxies).
Tier 2 — User-facing extension points: postrenderer.PostRenderer, getter.Getter. These are explicitly designed for user/ecosystem customization. PostRenderer is part of the public API of pkg/action (the Install.PostRenderer field is exported). Any tool that imports pkg/action can inject a custom post-renderer to transform manifests before apply. Getter allows registering new URL schemes via the Providers registry.
Tier 3 — Version-compatibility facades: release.Accessor, chart.Accessor. These are v4-era interfaces that insulate cross-version code from struct layout changes. They are not extension points in the traditional sense — they are stability contracts as the domain model evolves across major Helm versions.