diff --git a/pkg/helm/fsloader.go b/pkg/helm/fsloader.go index af95c94bb..b382e2cef 100644 --- a/pkg/helm/fsloader.go +++ b/pkg/helm/fsloader.go @@ -21,6 +21,8 @@ import ( "helm.sh/helm/v3/pkg/chart" chartLoader "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/engine" ) // LoadChart loads a Helm chart from an fs.FS at the specified path. @@ -72,3 +74,40 @@ func LoadChart(resourceFS fs.FS, chartPath string) (*chart.Chart, error) { return loadedChart, nil } + +// RenderChart renders a Helm chart's templates with the provided values. +// This does not require cluster access - it's a pure template rendering operation. +// Returns a map of template name to rendered content. +func RenderChart(resourceFS fs.FS, chartPath string, values Values, namespace, releaseName string) (map[string]string, error) { + loadedChart, err := LoadChart(resourceFS, chartPath) + if err != nil { + return nil, fmt.Errorf("failed to load chart: %w", err) + } + + return RenderLoadedChart(loadedChart, values, namespace, releaseName) +} + +// RenderLoadedChart renders an already-loaded chart's templates with the provided values. +// Returns a map of template name to rendered content. +func RenderLoadedChart(loadedChart *chart.Chart, values Values, namespace, releaseName string) (map[string]string, error) { + // Create release options for rendering + options := chartutil.ReleaseOptions{ + Name: releaseName, + Namespace: namespace, + IsInstall: true, + } + + // Merge values with chart defaults + chartValues, err := chartutil.ToRenderValues(loadedChart, values, options, nil) + if err != nil { + return nil, fmt.Errorf("failed to create render values: %w", err) + } + + // Render templates + rendered, err := engine.Render(loadedChart, chartValues) + if err != nil { + return nil, fmt.Errorf("failed to render chart templates: %w", err) + } + + return rendered, nil +} diff --git a/pkg/helm/fsloader_test.go b/pkg/helm/fsloader_test.go index 35d55785e..04947ab85 100644 --- a/pkg/helm/fsloader_test.go +++ b/pkg/helm/fsloader_test.go @@ -15,11 +15,43 @@ package helm import ( + "maps" "os" + "strings" "testing" "testing/fstest" ) +func TestRenderChart(t *testing.T) { + testFS := os.DirFS("testdata") + + t.Run("renders template with values", func(t *testing.T) { + rendered, err := RenderChart(testFS, "chart", Values{"value": "hello"}, "test-ns", "my-release") + if err != nil { + t.Fatalf("expected no error, got: %v", err) + } + + cm, ok := rendered["test-chart/templates/configmap.yaml"] + if !ok { + t.Fatalf("expected configmap template in output, got keys: %v", maps.Keys(rendered)) + } + + if !strings.Contains(cm, "namespace: test-ns") { + t.Errorf("expected namespace 'test-ns' in rendered output, got:\n%s", cm) + } + if !strings.Contains(cm, `value: "hello"`) { + t.Errorf("expected value 'hello' in rendered output, got:\n%s", cm) + } + }) + + t.Run("returns error for missing chart", func(t *testing.T) { + _, err := RenderChart(testFS, "nonexistent", nil, "ns", "rel") + if err == nil { + t.Fatal("expected error for non-existent chart path") + } + }) +} + func TestLoadChart(t *testing.T) { testFS := os.DirFS("testdata")