This commit is contained in:
yxxhero 2026-04-29 07:54:20 +05:30 committed by GitHub
commit bbe81400db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 650 additions and 49 deletions

View File

@ -7,12 +7,14 @@ import (
"testing"
"dario.cat/mergo"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/envvar"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/testhelper"
@ -932,3 +934,450 @@ releases:
})
}
}
func TestMergedReleaseTemplateData_IncludesReleaseValues(t *testing.T) {
logger := zaptest.NewLogger(t).Sugar()
testValsRuntime, err := vals.New(vals.Options{CacheSize: 32})
require.NoError(t, err)
yamlFile := "/example/path/to/helmfile.yaml"
yamlContent := []byte(`environments:
default:
values:
- env.yaml
releases:
- name: myrelease
chart: mychart
values:
- values.yaml
`)
envYamlFile := "/example/path/to/env.yaml"
envYamlContent := []byte(`envKey: envValue`)
valuesFile := "/example/path/to/values.yaml"
valuesContent := []byte(`ingress:
enabled: true
host: example.com`)
testFs := testhelper.NewTestFs(map[string]string{
envYamlFile: string(envYamlContent),
valuesFile: string(valuesContent),
})
testFs.Cwd = "/example/path/to"
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
env := environment.Environment{
Name: "default",
}
state, err := NewCreator(logger, testFs.ToFileSystem(), testValsRuntime, nil, "", "", r, false, "").
ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "default", true, true, true, &env, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
release := state.Releases[0]
templateData, err := state.mergedReleaseTemplateData(&release)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
ingress, ok := templateData.Values["ingress"]
if !ok {
t.Fatalf("expected .Values to contain 'ingress' key from release values")
}
ingressMap, ok := ingress.(map[string]any)
if !ok {
t.Fatalf("expected ingress to be a map, got %T", ingress)
}
if ingressMap["enabled"] != true {
t.Errorf("expected ingress.enabled to be true, got %v", ingressMap["enabled"])
}
if ingressMap["host"] != "example.com" {
t.Errorf("expected ingress.host to be 'example.com', got %v", ingressMap["host"])
}
if templateData.Values["envKey"] != "envValue" {
t.Errorf("expected envKey to be 'envValue', got %v", templateData.Values["envKey"])
}
}
func TestMergedReleaseTemplateData_ReleaseValuesOverrideEnvValues(t *testing.T) {
logger := zaptest.NewLogger(t).Sugar()
testValsRuntime, err := vals.New(vals.Options{CacheSize: 32})
require.NoError(t, err)
yamlFile := "/example/path/to/helmfile.yaml"
yamlContent := []byte(`environments:
default:
values:
- env.yaml
releases:
- name: myrelease
chart: mychart
values:
- values.yaml
`)
envYamlFile := "/example/path/to/env.yaml"
envYamlContent := []byte(`replicaCount: 1`)
valuesFile := "/example/path/to/values.yaml"
valuesContent := []byte(`replicaCount: 3`)
testFs := testhelper.NewTestFs(map[string]string{
envYamlFile: string(envYamlContent),
valuesFile: string(valuesContent),
})
testFs.Cwd = "/example/path/to"
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
env := environment.Environment{
Name: "default",
}
state, err := NewCreator(logger, testFs.ToFileSystem(), testValsRuntime, nil, "", "", r, false, "").
ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "default", true, true, true, &env, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
release := state.Releases[0]
templateData, err := state.mergedReleaseTemplateData(&release)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if templateData.Values["replicaCount"] != 3 {
t.Errorf("expected replicaCount to be 3 (release value overriding env), got %v", templateData.Values["replicaCount"])
}
}
func TestMergedReleaseTemplateData_InlineValues(t *testing.T) {
logger := zaptest.NewLogger(t).Sugar()
testValsRuntime, err := vals.New(vals.Options{CacheSize: 32})
require.NoError(t, err)
yamlFile := "/example/path/to/helmfile.yaml"
yamlContent := []byte(`releases:
- name: myrelease
chart: mychart
values:
- ingress:
enabled: true
host: example.com
`)
testFs := testhelper.NewTestFs(map[string]string{})
testFs.Cwd = "/example/path/to"
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
state, err := NewCreator(logger, testFs.ToFileSystem(), testValsRuntime, nil, "", "", r, false, "").
ParseAndLoad(yamlContent, filepath.Dir(yamlFile), yamlFile, "default", true, true, true, nil, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
release := state.Releases[0]
templateData, err := state.mergedReleaseTemplateData(&release)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
ingress, ok := templateData.Values["ingress"]
if !ok {
t.Fatalf("expected .Values to contain 'ingress' key from inline release values")
}
ingressMap, ok := ingress.(map[string]any)
if !ok {
t.Fatalf("expected ingress to be a map, got %T", ingress)
}
if ingressMap["enabled"] != true {
t.Errorf("expected ingress.enabled to be true, got %v", ingressMap["enabled"])
}
}
func newTestHelmStateWithFiles(t *testing.T, files map[string]string) *HelmState {
t.Helper()
const basePath = "/project"
logger := zaptest.NewLogger(t).Sugar()
valsRuntime, err := vals.New(vals.Options{CacheSize: 32})
require.NoError(t, err)
testFs := testhelper.NewTestFs(files)
testFs.Cwd = basePath
return &HelmState{
logger: logger,
fs: testFs.ToFileSystem(),
valsRuntime: valsRuntime,
basePath: basePath,
FilePath: basePath + "/helmfile.yaml",
RenderedValues: map[string]any{},
}
}
func TestResolveReleaseValues_Empty(t *testing.T) {
st := newTestHelmStateWithFiles(t, map[string]string{})
release := &ReleaseSpec{Name: "myrelease", Chart: "mychart"}
result, err := st.resolveReleaseValues(release)
require.NoError(t, err)
assert.Empty(t, result)
}
func TestResolveReleaseValues_FromFile(t *testing.T) {
valuesFile := "/project/values.yaml"
valuesContent := `replicaCount: 2
image:
repository: nginx
tag: "1.21"
`
st := newTestHelmStateWithFiles(t, map[string]string{
valuesFile: valuesContent,
})
release := &ReleaseSpec{
Name: "myrelease",
Chart: "mychart",
Values: []any{
"values.yaml",
},
}
result, err := st.resolveReleaseValues(release)
require.NoError(t, err)
assert.Equal(t, 2, result["replicaCount"])
imageMap, ok := result["image"].(map[string]any)
require.True(t, ok, "expected image to be a map")
assert.Equal(t, "nginx", imageMap["repository"])
}
func TestResolveReleaseValues_InlineMap(t *testing.T) {
st := newTestHelmStateWithFiles(t, map[string]string{})
release := &ReleaseSpec{
Name: "myrelease",
Chart: "mychart",
Values: []any{
map[string]any{
"replicaCount": 5,
"service": map[string]any{
"type": "ClusterIP",
"port": 80,
},
},
},
}
result, err := st.resolveReleaseValues(release)
require.NoError(t, err)
assert.Equal(t, 5, result["replicaCount"])
serviceMap, ok := result["service"].(map[string]any)
require.True(t, ok, "expected service to be a map")
assert.Equal(t, "ClusterIP", serviceMap["type"])
}
func TestResolveReleaseValues_MultipleSourcesMerged(t *testing.T) {
baseValuesFile := "/project/base.yaml"
baseValuesContent := `replicaCount: 1
service:
type: ClusterIP
`
overrideValuesFile := "/project/override.yaml"
overrideValuesContent := `replicaCount: 3
ingress:
enabled: true
`
st := newTestHelmStateWithFiles(t, map[string]string{
baseValuesFile: baseValuesContent,
overrideValuesFile: overrideValuesContent,
})
release := &ReleaseSpec{
Name: "myrelease",
Chart: "mychart",
Values: []any{
"base.yaml",
"override.yaml",
},
}
result, err := st.resolveReleaseValues(release)
require.NoError(t, err)
// override.yaml value wins
assert.Equal(t, 3, result["replicaCount"])
// from base.yaml
serviceMap, ok := result["service"].(map[string]any)
require.True(t, ok, "expected service to be a map")
assert.Equal(t, "ClusterIP", serviceMap["type"])
// from override.yaml
ingressMap, ok := result["ingress"].(map[string]any)
require.True(t, ok, "expected ingress to be a map")
assert.Equal(t, true, ingressMap["enabled"])
}
func TestResolveReleaseValues_FileAndInlineMerged(t *testing.T) {
valuesFile := "/project/values.yaml"
valuesContent := `replicaCount: 1
`
st := newTestHelmStateWithFiles(t, map[string]string{
valuesFile: valuesContent,
})
release := &ReleaseSpec{
Name: "myrelease",
Chart: "mychart",
Values: []any{
"values.yaml",
map[string]any{
"extraEnv": "production",
},
},
}
result, err := st.resolveReleaseValues(release)
require.NoError(t, err)
assert.Equal(t, 1, result["replicaCount"])
assert.Equal(t, "production", result["extraEnv"])
}
func TestRenderValuesFileToBytesWithData_PlainYAML(t *testing.T) {
valuesFile := "/project/values.yaml"
valuesContent := `replicaCount: 2
image:
repository: nginx
`
st := newTestHelmStateWithFiles(t, map[string]string{
valuesFile: valuesContent,
})
release := &ReleaseSpec{Name: "myrelease", Chart: "mychart"}
tmplData := st.createReleaseTemplateData(release, map[string]any{})
result, err := st.renderValuesFileToBytesWithData(valuesFile, tmplData)
require.NoError(t, err)
assert.Contains(t, string(result), "replicaCount: 2")
assert.Contains(t, string(result), "repository: nginx")
}
func TestRenderValuesFileToBytesWithData_WithValuesTemplate(t *testing.T) {
valuesFile := "/project/values.yaml.gotmpl"
valuesContent := `replicaCount: {{ .Values.replicaCount }}
enabled: {{ .Values.ingress.enabled }}
`
st := newTestHelmStateWithFiles(t, map[string]string{
valuesFile: valuesContent,
})
release := &ReleaseSpec{Name: "myrelease", Chart: "mychart"}
tmplData := st.createReleaseTemplateData(release, map[string]any{
"replicaCount": 3,
"ingress": map[string]any{
"enabled": true,
},
})
result, err := st.renderValuesFileToBytesWithData(valuesFile, tmplData)
require.NoError(t, err)
assert.Contains(t, string(result), "replicaCount: 3")
assert.Contains(t, string(result), "enabled: true")
}
func TestRenderValuesFileToBytesWithData_WithReleaseTemplate(t *testing.T) {
valuesFile := "/project/values.yaml.gotmpl"
valuesContent := `releaseName: {{ .Release.Name }}
releaseNamespace: {{ .Release.Namespace }}
`
st := newTestHelmStateWithFiles(t, map[string]string{
valuesFile: valuesContent,
})
release := &ReleaseSpec{Name: "myapp", Chart: "mychart", Namespace: "production"}
tmplData := st.createReleaseTemplateData(release, map[string]any{})
result, err := st.renderValuesFileToBytesWithData(valuesFile, tmplData)
require.NoError(t, err)
assert.Contains(t, string(result), "releaseName: myapp")
assert.Contains(t, string(result), "releaseNamespace: production")
}
func TestGenerateTemporaryReleaseValuesFilesWithData_StringPath(t *testing.T) {
t.Setenv(envvar.TempDir, t.TempDir())
patchFile := "/project/patch.yaml.gotmpl"
patchContent := `enabled: {{ .Values.ingress.enabled }}
host: {{ .Values.ingress.host }}
`
st := newTestHelmStateWithFiles(t, map[string]string{
patchFile: patchContent,
})
release := &ReleaseSpec{Name: "myrelease", Chart: "mychart"}
tmplData := st.createReleaseTemplateData(release, map[string]any{
"ingress": map[string]any{
"enabled": true,
"host": "example.com",
},
})
generatedFiles, err := st.generateTemporaryReleaseValuesFilesWithData(
release,
[]any{"patch.yaml.gotmpl"},
func() (releaseTemplateData, error) { return tmplData, nil },
)
require.NoError(t, err)
require.Len(t, generatedFiles, 1)
// The temp files are created on the real OS filesystem via os.Create, so we read them with os.ReadFile
content, err := filesystem.DefaultFileSystem().ReadFile(generatedFiles[0])
require.NoError(t, err)
assert.Contains(t, string(content), "enabled: true")
assert.Contains(t, string(content), "host: example.com")
}
func TestGenerateTemporaryReleaseValuesFilesWithData_InlineMap(t *testing.T) {
t.Setenv(envvar.TempDir, t.TempDir())
st := newTestHelmStateWithFiles(t, map[string]string{})
release := &ReleaseSpec{Name: "myrelease", Chart: "mychart"}
tmplData := st.createReleaseTemplateData(release, map[string]any{})
inlineValues := map[string]any{
"replicaCount": 5,
"service": map[string]any{
"type": "NodePort",
},
}
generatedFiles, err := st.generateTemporaryReleaseValuesFilesWithData(
release,
[]any{inlineValues},
func() (releaseTemplateData, error) { return tmplData, nil },
)
require.NoError(t, err)
require.Len(t, generatedFiles, 1)
// The temp files are created on the real OS filesystem via os.Create, so we read them with os.ReadFile
content, err := filesystem.DefaultFileSystem().ReadFile(generatedFiles[0])
require.NoError(t, err)
assert.Contains(t, string(content), "replicaCount: 5")
assert.Contains(t, string(content), "NodePort")
}
func TestGenerateTemporaryReleaseValuesFilesWithData_UnknownTypeError(t *testing.T) {
st := newTestHelmStateWithFiles(t, map[string]string{})
release := &ReleaseSpec{Name: "myrelease", Chart: "mychart"}
tmplData := st.createReleaseTemplateData(release, map[string]any{})
// Passing an unsupported type (int) should return an error
_, err := st.generateTemporaryReleaseValuesFilesWithData(
release,
[]any{42},
func() (releaseTemplateData, error) { return tmplData, nil },
)
require.Error(t, err)
assert.Contains(t, err.Error(), "unexpected type of value")
}

View File

@ -404,9 +404,28 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp
shouldRun = true
}
// patchTemplateData is computed lazily on first use: only when an actual string patch/transformer
// file path is rendered. Inline-map entries don't require template data at all, so we avoid
// unnecessary I/O for releases whose patches are all inline maps or that have no string entries.
var (
cachedPatchTemplateData releaseTemplateData
cachedPatchTemplateDataErr error
cachedPatchTemplateDataSet bool
)
getPatchTemplateData := func() (releaseTemplateData, error) {
if !cachedPatchTemplateDataSet {
cachedPatchTemplateDataSet = true
cachedPatchTemplateData, cachedPatchTemplateDataErr = st.mergedReleaseTemplateData(release)
if cachedPatchTemplateDataErr != nil {
cachedPatchTemplateDataErr = fmt.Errorf("failed to compute merged release values for patch rendering: %v", cachedPatchTemplateDataErr)
}
}
return cachedPatchTemplateData, cachedPatchTemplateDataErr
}
jsonPatches := release.JSONPatches
if len(jsonPatches) > 0 {
generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, jsonPatches)
generatedFiles, err := st.generateTemporaryReleaseValuesFilesWithData(release, jsonPatches, getPatchTemplateData)
if err != nil {
return nil, clean, err
}
@ -420,7 +439,7 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp
strategicMergePatches := release.StrategicMergePatches
if len(strategicMergePatches) > 0 {
generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, strategicMergePatches)
generatedFiles, err := st.generateTemporaryReleaseValuesFilesWithData(release, strategicMergePatches, getPatchTemplateData)
if err != nil {
return nil, clean, err
}
@ -434,7 +453,7 @@ func (st *HelmState) PrepareChartify(helm helmexec.Interface, release *ReleaseSp
transformers := release.Transformers
if len(transformers) > 0 {
generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, transformers)
generatedFiles, err := st.generateTemporaryReleaseValuesFilesWithData(release, transformers, getPatchTemplateData)
if err != nil {
return nil, clean, err
}

View File

@ -36,6 +36,7 @@ import (
"github.com/helmfile/helmfile/pkg/event"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/maputil"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/tmpl"
"github.com/helmfile/helmfile/pkg/yaml"
@ -3916,15 +3917,93 @@ func (st *HelmState) newReleaseTemplateData(release *ReleaseSpec) releaseTemplat
return templateData
}
func (st *HelmState) newReleaseTemplateFuncMap(dir string) template.FuncMap {
r := tmpl.NewFileRenderer(st.fs, dir, nil)
return r.Context.CreateFuncMap()
func (st *HelmState) mergedReleaseTemplateData(release *ReleaseSpec) (releaseTemplateData, error) {
releaseValues, err := st.resolveReleaseValues(release)
if err != nil {
return releaseTemplateData{}, err
}
mergedVals := maputil.MergeMaps(st.Values(), releaseValues)
return st.createReleaseTemplateData(release, mergedVals), nil
}
func (st *HelmState) RenderReleaseValuesFileToBytes(release *ReleaseSpec, path string) ([]byte, error) {
templateData := st.newReleaseTemplateData(release)
// prepareReleaseValuesEntries normalizes release.Values path entries (applying ValuesPathPrefix)
// and evaluates any vals ref+ secrets, returning the fully-rendered values slice ready for processing.
func (st *HelmState) prepareReleaseValuesEntries(release *ReleaseSpec) ([]any, error) {
values := []any{}
for _, v := range release.Values {
switch typedValue := v.(type) {
case string:
path := st.storage().normalizePath(release.ValuesPathPrefix + typedValue)
values = append(values, path)
default:
values = append(values, typedValue)
}
}
valuesMapSecretsRendered, err := st.valsRuntime.Eval(map[string]any{"values": values})
if err != nil {
return nil, err
}
valuesSecretsRendered, ok := valuesMapSecretsRendered["values"].([]any)
if !ok {
return nil, fmt.Errorf("failed to render values in %s for release %s: type %T isn't supported", st.FilePath, release.Name, valuesMapSecretsRendered["values"])
}
return valuesSecretsRendered, nil
}
func (st *HelmState) resolveReleaseValues(release *ReleaseSpec) (map[string]any, error) {
merged := map[string]any{}
valuesSecretsRendered, err := st.prepareReleaseValuesEntries(release)
if err != nil {
return nil, err
}
for _, v := range valuesSecretsRendered {
switch typedValue := v.(type) {
case string:
paths, skip, err := st.storage().resolveFile(st.getReleaseMissingFileHandler(release), "values", typedValue, st.getReleaseMissingFileHandlerConfig(release).resolveFileOptions()...)
if err != nil {
return nil, err
}
if skip {
continue
}
if len(paths) > 1 {
return nil, fmt.Errorf("glob patterns in release values are not supported for template data resolution: value=%q, resolvedPaths=%v", typedValue, paths)
}
path := paths[0]
yamlBytes, err := st.RenderReleaseValuesFileToBytes(release, path)
if err != nil {
return nil, fmt.Errorf("failed to render values file \"%s\": %v", typedValue, err)
}
var vals map[string]any
if err := yaml.Unmarshal(yamlBytes, &vals); err != nil {
return nil, fmt.Errorf("failed to parse values file \"%s\": %v", typedValue, err)
}
merged = maputil.MergeMaps(merged, vals)
case map[any]any:
strMap, err := maputil.CastKeysToStrings(typedValue)
if err != nil {
return nil, err
}
merged = maputil.MergeMaps(merged, strMap)
case map[string]any:
merged = maputil.MergeMaps(merged, typedValue)
default:
return nil, fmt.Errorf("unexpected type of value in release values: value=%v, type=%T", typedValue, typedValue)
}
}
return merged, nil
}
func (st *HelmState) renderValuesFileToBytesWithData(path string, templateData releaseTemplateData) ([]byte, error) {
r := tmpl.NewFileRenderer(st.fs, filepath.Dir(path), templateData)
rawBytes, err := r.RenderToBytes(path)
if err != nil {
@ -3955,6 +4034,27 @@ func (st *HelmState) RenderReleaseValuesFileToBytes(release *ReleaseSpec, path s
return rawBytes, nil
}
func (st *HelmState) generateTemporaryReleaseValuesFilesWithData(release *ReleaseSpec, values []any, getTemplateData func() (releaseTemplateData, error)) ([]string, error) {
return st.generateTemporaryReleaseValuesFilesCore(release, values, func(path string) ([]byte, error) {
templateData, err := getTemplateData()
if err != nil {
return nil, err
}
return st.renderValuesFileToBytesWithData(path, templateData)
})
}
func (st *HelmState) newReleaseTemplateFuncMap(dir string) template.FuncMap {
r := tmpl.NewFileRenderer(st.fs, dir, nil)
return r.Context.CreateFuncMap()
}
func (st *HelmState) RenderReleaseValuesFileToBytes(release *ReleaseSpec, path string) ([]byte, error) {
templateData := st.newReleaseTemplateData(release)
return st.renderValuesFileToBytesWithData(path, templateData)
}
func (st *HelmState) storage() *Storage {
return &Storage{
FilePath: st.FilePath,
@ -4080,6 +4180,14 @@ func (st *HelmState) getMissingFileHandler() *string {
}
func (st *HelmState) generateTemporaryReleaseValuesFiles(release *ReleaseSpec, values []any) ([]string, error) {
return st.generateTemporaryReleaseValuesFilesCore(release, values, func(path string) ([]byte, error) {
return st.RenderReleaseValuesFileToBytes(release, path)
})
}
// generateTemporaryReleaseValuesFilesCore is the shared implementation for generating temporary values files.
// renderStringValue is called for each string value entry after the file path has been resolved.
func (st *HelmState) generateTemporaryReleaseValuesFilesCore(release *ReleaseSpec, values []any, renderStringValue func(path string) ([]byte, error)) ([]string, error) {
generatedFiles := []string{}
for _, value := range values {
@ -4098,45 +4206,86 @@ func (st *HelmState) generateTemporaryReleaseValuesFiles(release *ReleaseSpec, v
}
path := paths[0]
yamlBytes, err := st.RenderReleaseValuesFileToBytes(release, path)
yamlBytes, err := renderStringValue(path)
if err != nil {
return generatedFiles, fmt.Errorf("failed to render values files \"%s\": %v", typedValue, err)
}
valfile, err := createTempValuesFile(release, yamlBytes)
if err := func() error {
valfile, err := createTempValuesFile(release, yamlBytes)
if err != nil {
return err
}
defer func() {
_ = valfile.Close()
}()
if _, err := valfile.Write(yamlBytes); err != nil {
return fmt.Errorf("failed to write %s: %v", valfile.Name(), err)
}
st.logger.Debugf("Successfully generated the value file from %s to %s", path, valfile.Name())
generatedFiles = append(generatedFiles, valfile.Name())
return nil
}(); err != nil {
return generatedFiles, err
}
case map[any]any:
strMap, err := maputil.CastKeysToStrings(typedValue)
if err != nil {
return generatedFiles, err
}
defer func() {
_ = valfile.Close()
}()
if err := func() error {
valfile, err := createTempValuesFile(release, strMap)
if err != nil {
return err
}
defer func() {
_ = valfile.Close()
}()
if _, err := valfile.Write(yamlBytes); err != nil {
return generatedFiles, fmt.Errorf("failed to write %s: %v", valfile.Name(), err)
}
encoder := yaml.NewEncoder(valfile)
defer func() {
_ = encoder.Close()
}()
st.logger.Debugf("Successfully generated the value file at %s. produced:\n%s", path, string(yamlBytes))
if err := encoder.Encode(strMap); err != nil {
return err
}
generatedFiles = append(generatedFiles, valfile.Name())
case map[any]any, map[string]any:
valfile, err := createTempValuesFile(release, typedValue)
if err != nil {
generatedFiles = append(generatedFiles, valfile.Name())
return nil
}(); err != nil {
return generatedFiles, err
}
defer func() {
_ = valfile.Close()
}()
case map[string]any:
if err := func() error {
valfile, err := createTempValuesFile(release, typedValue)
if err != nil {
return err
}
defer func() {
_ = valfile.Close()
}()
encoder := yaml.NewEncoder(valfile)
defer func() {
_ = encoder.Close()
}()
encoder := yaml.NewEncoder(valfile)
defer func() {
_ = encoder.Close()
}()
if err := encoder.Encode(typedValue); err != nil {
if err := encoder.Encode(typedValue); err != nil {
return err
}
generatedFiles = append(generatedFiles, valfile.Name())
return nil
}(); err != nil {
return generatedFiles, err
}
generatedFiles = append(generatedFiles, valfile.Name())
default:
return generatedFiles, fmt.Errorf("unexpected type of value: value=%v, type=%T", typedValue, typedValue)
}
@ -4145,27 +4294,11 @@ func (st *HelmState) generateTemporaryReleaseValuesFiles(release *ReleaseSpec, v
}
func (st *HelmState) generateVanillaValuesFiles(release *ReleaseSpec) ([]string, error) {
values := []any{}
for _, v := range release.Values {
switch typedValue := v.(type) {
case string:
path := st.storage().normalizePath(release.ValuesPathPrefix + typedValue)
values = append(values, path)
default:
values = append(values, v)
}
}
valuesMapSecretsRendered, err := st.valsRuntime.Eval(map[string]any{"values": values})
valuesSecretsRendered, err := st.prepareReleaseValuesEntries(release)
if err != nil {
return nil, err
}
valuesSecretsRendered, ok := valuesMapSecretsRendered["values"].([]any)
if !ok {
return nil, fmt.Errorf("Failed to render values in %s for release %s: type %T isn't supported", st.FilePath, release.Name, valuesMapSecretsRendered["values"])
}
generatedFiles, err := st.generateTemporaryReleaseValuesFiles(release, valuesSecretsRendered)
if err != nil {
return nil, err