diff --git a/internal/chart/v3/util/dependencies.go b/internal/chart/v3/util/dependencies.go index 91427091a..063e4fab1 100644 --- a/internal/chart/v3/util/dependencies.go +++ b/internal/chart/v3/util/dependencies.go @@ -214,9 +214,12 @@ Loop: // recursively call self to process sub dependencies for _, t := range cd { - // cvals[path][alias] may be stale when an alias is used, because ancestor - // CoalesceValues calls used the original chart name. Fill in missing keys from the - // correctly-coalesced top-level entry, keeping the nested entry authoritative. + // When a dependency uses an alias, cvals[path][alias] may be missing keys + // that were coalesced into cvals[originalName] by ancestor CoalesceValues + // calls (which used the original chart name, not the alias). To correct + // this, we backfill any missing keys from the top-level entry into the + // nested path entry. The nested entry is authoritative: if a key exists + // in both, the nested (alias-keyed) value wins. if path != "" { if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { diff --git a/internal/chart/v3/util/dependencies_test.go b/internal/chart/v3/util/dependencies_test.go index 8b3d458e8..6ca87cf4d 100644 --- a/internal/chart/v3/util/dependencies_test.go +++ b/internal/chart/v3/util/dependencies_test.go @@ -578,6 +578,47 @@ func TestDependencyEnabledAliasNestedCondition(t *testing.T) { } } +// TestDependencyEnabledAliasNestedConditionEnabled tests that overriding util.enabled=true +// through the parent chart's values correctly includes the dependency even when the leaf +// chart's default disables it. This is the complementary positive case to +// TestDependencyEnabledAliasNestedCondition. +func TestDependencyEnabledAliasNestedConditionEnabled(t *testing.T) { + // Chart structure: + // parentchart -> mid (alias: midchart) -> leaf (alias: leafchart) -> util (condition: util.enabled) + // + // leaf/values.yaml sets util.enabled: false by default. + // User-provided values override util.enabled=true via the full alias path. + // Expected: util IS included in the output. + c := loadChart(t, "testdata/alias-condition-nested") + vals := map[string]interface{}{ + "midchart": map[string]interface{}{ + "enabled": true, + "leafchart": map[string]interface{}{ + "enabled": true, + "util": map[string]interface{}{ + "enabled": true, + }, + }, + }, + } + if err := processDependencyEnabled(c, vals, ""); err != nil { + t.Fatalf("error processing enabled dependencies: %v", err) + } + + names := extractChartNames(c) + expected := []string{"parentchart", "parentchart.midchart", "parentchart.midchart.leafchart", "parentchart.midchart.leafchart.util"} + sort.Strings(expected) + + if len(names) != len(expected) { + t.Fatalf("slice lengths do not match: got %v, expected %v", names, expected) + } + for i := range names { + if names[i] != expected[i] { + t.Fatalf("slice values do not match: got %v, expected %v", names, expected) + } + } +} + func TestChartWithDependencyAliasedTwiceAndDoublyReferencedSubDependency(t *testing.T) { c := loadChart(t, "testdata/chart-with-dependency-aliased-twice") diff --git a/pkg/chart/v2/util/dependencies.go b/pkg/chart/v2/util/dependencies.go index 727d0dcf0..38ca554b3 100644 --- a/pkg/chart/v2/util/dependencies.go +++ b/pkg/chart/v2/util/dependencies.go @@ -214,9 +214,12 @@ Loop: // recursively call self to process sub dependencies for _, t := range cd { - // cvals[path][alias] may be stale when an alias is used, because ancestor - // CoalesceValues calls used the original chart name. Fill in missing keys from the - // correctly-coalesced top-level entry, keeping the nested entry authoritative. + // When a dependency uses an alias, cvals[path][alias] may be missing keys + // that were coalesced into cvals[originalName] by ancestor CoalesceValues + // calls (which used the original chart name, not the alias). To correct + // this, we backfill any missing keys from the top-level entry into the + // nested path entry. The nested entry is authoritative: if a key exists + // in both, the nested (alias-keyed) value wins. if path != "" { if pt, err := cvals.Table(strings.TrimSuffix(path, ".")); err == nil { if top, ok := cvals[t.Metadata.Name].(map[string]interface{}); ok { diff --git a/pkg/chart/v2/util/dependencies_test.go b/pkg/chart/v2/util/dependencies_test.go index 0cc1c8967..23d4b8c32 100644 --- a/pkg/chart/v2/util/dependencies_test.go +++ b/pkg/chart/v2/util/dependencies_test.go @@ -163,6 +163,47 @@ func TestDependencyEnabledAliasNestedCondition(t *testing.T) { } } +// TestDependencyEnabledAliasNestedConditionEnabled tests that overriding util.enabled=true +// through the parent chart's values correctly includes the dependency even when the leaf +// chart's default disables it. This is the complementary positive case to +// TestDependencyEnabledAliasNestedCondition. +func TestDependencyEnabledAliasNestedConditionEnabled(t *testing.T) { + // Chart structure: + // parentchart -> mid (alias: midchart) -> leaf (alias: leafchart) -> util (condition: util.enabled) + // + // leaf/values.yaml sets util.enabled: false by default. + // User-provided values override util.enabled=true via the full alias path. + // Expected: util IS included in the output. + c := loadChart(t, "testdata/alias-condition-nested") + vals := map[string]interface{}{ + "midchart": map[string]interface{}{ + "enabled": true, + "leafchart": map[string]interface{}{ + "enabled": true, + "util": map[string]interface{}{ + "enabled": true, + }, + }, + }, + } + if err := processDependencyEnabled(c, vals, ""); err != nil { + t.Fatalf("error processing enabled dependencies: %v", err) + } + + names := extractChartNames(c) + expected := []string{"parentchart", "parentchart.midchart", "parentchart.midchart.leafchart", "parentchart.midchart.leafchart.util"} + sort.Strings(expected) + + if len(names) != len(expected) { + t.Fatalf("slice lengths do not match: got %v, expected %v", names, expected) + } + for i := range names { + if names[i] != expected[i] { + t.Fatalf("slice values do not match: got %v, expected %v", names, expected) + } + } +} + // extractChartNames recursively searches chart dependencies returning all charts found func extractChartNames(c *chart.Chart) []string { var out []string