From 679f0519804afeaa5ce8b930a30976ade2860fe0 Mon Sep 17 00:00:00 2001 From: Evans Mungai Date: Fri, 12 Dec 2025 17:00:18 +0000 Subject: [PATCH] Preserve nil values in chart already Signed-off-by: Evans Mungai --- pkg/chart/common/util/coalesce.go | 27 +++++++++++++++++++------- pkg/chart/common/util/coalesce_test.go | 13 +++++++++---- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/pkg/chart/common/util/coalesce.go b/pkg/chart/common/util/coalesce.go index 3220a275c..6c72b3d56 100644 --- a/pkg/chart/common/util/coalesce.go +++ b/pkg/chart/common/util/coalesce.go @@ -302,23 +302,36 @@ func coalesceTablesFullKey(printf printFn, dst, src map[string]interface{}, pref if dst == nil { return src } + // Track original non-nil src keys before modifying src + // This lets us distinguish between user nullifying a chart default vs + // user setting nil for a key not in chart defaults. + srcOriginalNonNil := make(map[string]bool) + for key, val := range src { + if val != nil { + srcOriginalNonNil[key] = true + } + } + for key, val := range dst { + if val == nil { + src[key] = nil + } + } // Because dest has higher precedence than src, dest values override src // values. for key, val := range src { fullkey := concatPrefix(prefix, key) - dv, ok := dst[key] - if !ok { - dst[key] = val - } else if dv == nil && !merge && val != nil { + if dv, ok := dst[key]; ok && !merge && dv == nil && srcOriginalNonNil[key] { // When coalescing (not merging), if dst has nil and src has a non-nil // value, the user is nullifying a chart default - remove the key. - // Per Helm docs: setting a key to null deletes it. - // But if src also has nil (or key not in src), preserve the nil (issue #31643). + // But if src also has nil (or key not in src), preserve the nil delete(dst, key) + } else if !ok { + // key not in user values, preserve src value (including nil) + dst[key] = val } else if istable(val) { if istable(dv) { coalesceTablesFullKey(printf, dv.(map[string]interface{}), val.(map[string]interface{}), fullkey, merge) - } else if dv != nil { + } else { printf("warning: cannot overwrite table with non table for %s (%v)", fullkey, val) } } else if istable(dv) && val != nil { diff --git a/pkg/chart/common/util/coalesce_test.go b/pkg/chart/common/util/coalesce_test.go index 598f1f9ef..84442e6cd 100644 --- a/pkg/chart/common/util/coalesce_test.go +++ b/pkg/chart/common/util/coalesce_test.go @@ -491,23 +491,28 @@ func TestCoalesceTables(t *testing.T) { is.Equal("pequod", dst2["boat"], "Expected boat string, got %v", dst2["boat"]) is.Equal("black", dst2["hole"], "Expected hole string, got %v", dst2["hole"]) }) - t.Run("empty chart map with nil user value", func(t *testing.T) { + t.Run("chart values with nil user value", func(t *testing.T) { dst := map[string]any{ "foo": "bar", "baz": nil, // explicit nil from user } // Chart's default values (src - lower priority) - empty map - src := map[string]any{} + src := map[string]any{ + "ben": nil, + } CoalesceTables(dst, src) // "foo" should be preserved is.Equal("bar", dst["foo"]) + _, ok := dst["ben"] + is.True(ok, "Expected ben key to be present") + is.Nil(dst["ben"], "Expected ben key to be nil but it is not") - _, ok := dst["baz"] + _, ok = dst["baz"] is.True(ok, "Expected baz key to be present but it was removed") - is.True(dst["baz"] == nil, "Expected baz key to be nil but it is not") + is.Nil(dst["baz"], "Expected baz key to be nil but it is not") }) }