fix: template helmDefaults.postRendererArgs with release data (#2583)

PR #1839 introduced template rendering for postRendererArgs, but PR #2510
reverted it while fixing a separate regression. This left helmDefaults-level
postRendererArgs containing template expressions (e.g. {{ .Release.Name }})
passed to helm as literal strings instead of being resolved per-release.

Add renderPostRendererArgs() that templates helmDefaults.postRendererArgs
at flag-generation time using the release's template data, reusing the
existing createReleaseTemplateData() helper. Release-level args are already
templated by ExecuteTemplateExpressions and CLI args are static, so only
the helmDefaults path needs rendering.

Fixes #2580

Signed-off-by: opencode <opencode@users.noreply.github.com>
Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
yxxhero 2026-05-11 09:05:06 +08:00 committed by GitHub
parent 9e6ed57015
commit c82c61e061
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 190 additions and 7 deletions

View File

@ -13,10 +13,12 @@ import (
"github.com/helmfile/chartify"
"helm.sh/helm/v4/pkg/storage/driver"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/kubedog"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/resource"
"github.com/helmfile/helmfile/pkg/tmpl"
)
type Dependency struct {
@ -115,7 +117,7 @@ func (st *HelmState) appendPostRenderFlags(flags []string, release *ReleaseSpec,
}
// append post-renderer-args flags to helm flags
func (st *HelmState) appendPostRenderArgsFlags(flags []string, release *ReleaseSpec, postRendererArgs []string) []string {
func (st *HelmState) appendPostRenderArgsFlags(flags []string, release *ReleaseSpec, postRendererArgs []string) ([]string, error) {
postRendererArgsFlags := []string{}
switch {
case len(release.PostRendererArgs) != 0:
@ -123,14 +125,44 @@ func (st *HelmState) appendPostRenderArgsFlags(flags []string, release *ReleaseS
case len(postRendererArgs) != 0:
postRendererArgsFlags = postRendererArgs
case len(st.HelmDefaults.PostRendererArgs) != 0:
postRendererArgsFlags = st.HelmDefaults.PostRendererArgs
rendered, err := st.renderPostRendererArgs(release, st.HelmDefaults.PostRendererArgs)
if err != nil {
return nil, err
}
postRendererArgsFlags = rendered
}
for _, arg := range postRendererArgsFlags {
if arg != "" {
flags = append(flags, "--post-renderer-args="+arg)
}
}
return flags
return flags, nil
}
func (st *HelmState) renderPostRendererArgs(release *ReleaseSpec, args []string) ([]string, error) {
vals := st.RenderedValues
if vals == nil {
vals = make(map[string]any)
}
fs := st.fs
if fs == nil {
fs = filesystem.DefaultFileSystem()
}
tmplData := st.createReleaseTemplateData(release, vals)
renderer := tmpl.NewFileRenderer(fs, st.basePath, tmplData)
result := make([]string, 0, len(args))
for _, arg := range args {
rendered, err := renderer.RenderTemplateContentToString([]byte(arg))
if err != nil {
return nil, fmt.Errorf("failed rendering postRendererArg %q for release %q: %w", arg, release.Name, err)
}
result = append(result, rendered)
}
return result, nil
}
// append skip-schema-validation flags to helm flags

View File

@ -3627,7 +3627,10 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp
if opt != nil {
postRendererArgs = opt.PostRendererArgs
}
flags = st.appendPostRenderArgsFlags(flags, release, postRendererArgs)
flags, err = st.appendPostRenderArgsFlags(flags, release, postRendererArgs)
if err != nil {
return nil, nil, err
}
skipSchemaValidation := false
if opt != nil {
@ -3670,7 +3673,10 @@ func (st *HelmState) flagsForTemplate(helm helmexec.Interface, release *ReleaseS
skipSchemaValidation = opt.SkipSchemaValidation
}
flags = st.appendPostRenderFlags(flags, release, postRenderer, helm)
flags = st.appendPostRenderArgsFlags(flags, release, postRendererArgs)
flags, err := st.appendPostRenderArgsFlags(flags, release, postRendererArgs)
if err != nil {
return nil, nil, err
}
flags = st.appendApiVersionsFlags(flags, release, kubeVersion)
flags = st.appendChartDownloadFlags(flags, release)
flags = st.appendShowOnlyFlags(flags, showOnly)
@ -3793,7 +3799,11 @@ func (st *HelmState) flagsForDiff(helm helmexec.Interface, release *ReleaseSpec,
if opt != nil {
postRendererArgs = opt.PostRendererArgs
}
flags = st.appendPostRenderArgsFlags(flags, release, postRendererArgs)
var err error
flags, err = st.appendPostRenderArgsFlags(flags, release, postRendererArgs)
if err != nil {
return nil, nil, err
}
skipSchemaValidation := false
if opt != nil {
@ -3823,7 +3833,6 @@ func (st *HelmState) flagsForDiff(helm helmexec.Interface, release *ReleaseSpec,
takeOwnership = opt.TakeOwnership
}
var err error
flags, err = st.appendTakeOwnershipFlagsForDiff(flags, release, takeOwnership, pluginsDir)
if err != nil {
return nil, nil, err

View File

@ -1048,6 +1048,51 @@ func TestHelmState_flagsForUpgrade(t *testing.T) {
"--namespace", "test-namespace",
},
},
{
name: "post-renderer-args-helmdefault-templated-with-release-name",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &enable,
PostRendererArgs: []string{"{{ .Release.Name }}", "--chart={{ .Release.Chart }}"},
},
version: semver.MustParse("3.10.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Verify: &disable,
Name: "my-release",
Namespace: "test-namespace",
CreateNamespace: &disable,
},
want: []string{
"--version", "0.1",
"--post-renderer-args=my-release",
"--post-renderer-args=--chart=test/chart",
"--namespace", "test-namespace",
},
},
{
name: "post-renderer-args-helmdefault-templated-with-namespace",
defaults: HelmSpec{
Verify: false,
CreateNamespace: &enable,
PostRendererArgs: []string{"{{ .Release.Namespace }}/{{ .Release.Name }}"},
},
version: semver.MustParse("3.10.0"),
release: &ReleaseSpec{
Chart: "test/chart",
Version: "0.1",
Verify: &disable,
Name: "my-release",
Namespace: "test-namespace",
CreateNamespace: &disable,
},
want: []string{
"--version", "0.1",
"--post-renderer-args=test-namespace/my-release",
"--namespace", "test-namespace",
},
},
{
name: "description-from-release",
defaults: HelmSpec{

View File

@ -116,6 +116,7 @@ ${kubectl} create namespace ${test_ns} || fail "Could not create namespace ${tes
. ${dir}/test-cases/yaml-overwrite.sh
. ${dir}/test-cases/chart-needs.sh
. ${dir}/test-cases/postrender.sh
. ${dir}/test-cases/postrender-defaults-args.sh
. ${dir}/test-cases/issue-2515.sh
. ${dir}/test-cases/chartify.sh
. ${dir}/test-cases/deps-mr-1011.sh

View File

@ -0,0 +1,25 @@
postrender_defaults_args_case_input_dir="${cases_dir}/postrender-defaults-args/input"
postrender_defaults_args_case_output_dir="${cases_dir}/postrender-defaults-args/output"
# Helm 4 requires post-renderers to be plugins
if [ "${HELMFILE_HELM4}" = "1" ]; then
info "Installing echo-args post-renderer plugin for Helm 4"
${helm} plugin uninstall echo-args &>/dev/null || true
${helm} plugin install ${postrender_defaults_args_case_input_dir}/helm-plugin-echo-args ${PLUGIN_INSTALL_FLAGS} || fail "Failed to install echo-args plugin"
fi
config_file="helmfile.yaml.gotmpl"
postrender_defaults_args_tmp=$(mktemp -d)
test_start "postrender-defaults-args template"
info "Running helmfile template with helmDefaults.postRendererArgs containing {{ .Release.Name }}"
${helmfile} -f ${postrender_defaults_args_case_input_dir}/${config_file} template --concurrency 1 &> ${postrender_defaults_args_tmp}/template.out || fail "\"helmfile template\" shouldn't fail"
info "Verifying that helmDefaults.postRendererArgs were templated with release names"
grep -q "name: rendered-arg-foo" ${postrender_defaults_args_tmp}/template.out || fail "Expected postRendererArg 'foo' for release foo, but not found in output"
grep -q "name: rendered-arg-bar" ${postrender_defaults_args_tmp}/template.out || fail "Expected postRendererArg 'bar' for release bar, but not found in output"
info "Verifying that literal template expression was NOT passed"
grep -q "rendered-arg-{{ .Release.Name }}" ${postrender_defaults_args_tmp}/template.out && fail "Template expression was NOT rendered (found literal {{ .Release.Name }})"
test_pass "postrender-defaults-args template"

View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
arg=$1
cat
echo "---"
cat <<EOS
apiVersion: v1
kind: ConfigMap
metadata:
name: rendered-arg-${arg}
data:
arg: ${arg}
EOS

View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -e
arg=$2
input=$(cat)
echo "$input"
echo "---"
cat <<EOS
apiVersion: v1
kind: ConfigMap
metadata:
name: rendered-arg-${arg}
data:
arg: ${arg}
EOS

View File

@ -0,0 +1,10 @@
apiVersion: v1
type: postrenderer/v1
name: echo-args
version: 0.1.0
runtime: subprocess
runtimeConfig:
platformCommand:
- command: ${HELM_PLUGIN_DIR}/echo-args.sh
args:
- ${HELM_POST_RENDERER_ARGS}

View File

@ -0,0 +1,30 @@
helmDefaults:
postRenderer: {{ if eq (env "HELMFILE_HELM4") "1" }}echo-args{{ else }}./echo-args.bash{{ end }}
postRendererArgs:
- "{{ `{{ .Release.Name }}` }}"
releases:
- name: foo
chart: ../../../charts/raw
values:
- templates:
- |
apiVersion: v1
kind: ConfigMap
metadata:
name: {{`{{ .Release.Name }}`}}-1
namespace: {{`{{ .Release.Namespace }}`}}
data:
foo: FOO
- name: bar
chart: ../../../charts/raw
values:
- templates:
- |
apiVersion: v1
kind: ConfigMap
metadata:
name: {{`{{ .Release.Name }}`}}-2
namespace: {{`{{ .Release.Namespace }}`}}
data:
bar: BAR