From bec9ddce16e50ea16598d2b04a583fc944a40ee0 Mon Sep 17 00:00:00 2001 From: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Date: Wed, 19 Aug 2026 07:51:21 -0700 Subject: [PATCH] Default HookOutputFunc to io.Discard to fix panic on hook-output-log-policy Motivation: helm-controller crashes with a nil pointer dereference whenever a chart hook Pod/Job carries the `helm.sh/hook-output-log-policy` annotation, because Helm dereferences `cfg.HookOutputFunc` without a nil check, and `ConfigFactory.Build()` never set that field. Approach: Set `HookOutputFunc` on the configuration returned by `ConfigFactory.Build()` in internal/action/config.go to a function returning `io.Discard`, matching the default Helm's own CLI initialization uses, so hook container logs are discarded instead of causing a nil dereference. Validation: Added a unit test in internal/action/config_test.go asserting `ConfigFactory.Build().HookOutputFunc` is non-nil and returns `io.Discard`. Report: https://github.com/fluxcd/helm-controller/issues/1562 Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Assisted-by: claude-sonnet-5 (via Claude Code) --- internal/action/config.go | 5 +++++ internal/action/config_test.go | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/internal/action/config.go b/internal/action/config.go index 61a42e201..f19a3972d 100644 --- a/internal/action/config.go +++ b/internal/action/config.go @@ -19,6 +19,7 @@ package action import ( "context" "fmt" + "io" "log/slog" helmaction "helm.sh/helm/v4/pkg/action" @@ -183,6 +184,10 @@ func (c *ConfigFactory) Build(log slog.Handler, observers ...storage.ObserveFunc conf.RESTClientGetter = c.Getter conf.Releases = c.NewStorage(observers...) conf.KubeClient = client + // Helm's own CLI initialization defaults HookOutputFunc to io.Discard, but + // helmaction.NewConfiguration does not. Without it, hooks annotated with + // helm.sh/hook-output-log-policy make Helm panic on a nil HookOutputFunc. + conf.HookOutputFunc = func(_, _, _ string) io.Writer { return io.Discard } return conf } diff --git a/internal/action/config_test.go b/internal/action/config_test.go index 1c82b6cbf..cbb322997 100644 --- a/internal/action/config_test.go +++ b/internal/action/config_test.go @@ -18,6 +18,7 @@ package action import ( "errors" + "io" "log/slog" "testing" @@ -257,6 +258,15 @@ func TestConfigFactory_Build(t *testing.T) { g.Expect(cfg.Releases).ToNot(BeNil()) g.Expect(cfg.Releases.Driver).To(BeAssignableToTypeOf(&storage.Observer{})) }) + + t.Run("sets a non-nil HookOutputFunc", func(t *testing.T) { + g := NewWithT(t) + + cfg := (&ConfigFactory{}).Build(nil) + + g.Expect(cfg.HookOutputFunc).ToNot(BeNil()) + g.Expect(cfg.HookOutputFunc("ns", "pod", "container")).To(Equal(io.Discard)) + }) } func TestConfigFactory_Valid(t *testing.T) {