Add parameter to render helmfile as go template without .gotmpl extension (#2312)
* Add parameter to render helmfile as go template without gotmpl extension Signed-off-by: Ronaldo <ronaldo.ur@gmail.com> * Update pkg/envvar/const.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Signed-off-by: Ronaldo <ronaldo.ur@gmail.com> Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
7a9175b7c4
commit
f3b19fd81e
|
|
@ -586,6 +586,7 @@ Helmfile uses some OS environment variables to override default behaviour:
|
|||
* `HELMFILE_CACHE_HOME` - specify directory to store cached files for remote operations
|
||||
* `HELMFILE_FILE_PATH` - specify the path to the helmfile.yaml file
|
||||
* `HELMFILE_INTERACTIVE` - enable interactive mode, expecting `true` lower case. The same as `--interactive` CLI flag
|
||||
* `HELMFILE_RENDER_YAML` - force helmfile.yaml to be rendered as a Go template regardless of file extension, expecting `true` lower case. Useful for migrating from v0 to v1 without renaming files to `.gotmpl`
|
||||
|
||||
## CLI Reference
|
||||
|
||||
|
|
|
|||
|
|
@ -4455,3 +4455,149 @@ func TestGetArgs(t *testing.T) {
|
|||
require.Equalf(t, test.expected, strings.Join(receivedArgs, " "), "expected args %s, received args %s", test.expected, strings.Join(receivedArgs, " "))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderYamlEnvVar(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
envValue string
|
||||
filename string
|
||||
content string
|
||||
shouldRender bool
|
||||
expectErr bool
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "default behavior - helmfile.yaml without .gotmpl is NOT rendered",
|
||||
envValue: "",
|
||||
filename: "helmfile.yaml",
|
||||
content: "releases:\n- name: {{ .Environment.Name }}-app\n chart: test/chart\n",
|
||||
shouldRender: false,
|
||||
expectErr: false,
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "default behavior - helmfile.yaml.gotmpl IS rendered",
|
||||
envValue: "",
|
||||
filename: "helmfile.yaml.gotmpl",
|
||||
content: "releases:\n- name: {{ .Environment.Name }}-app\n chart: test/chart\n",
|
||||
shouldRender: true,
|
||||
expectErr: false,
|
||||
expectedOutput: "default-app",
|
||||
},
|
||||
{
|
||||
name: "HELMFILE_RENDER_YAML=true - helmfile.yaml IS rendered",
|
||||
envValue: "true",
|
||||
filename: "helmfile.yaml",
|
||||
content: "releases:\n- name: {{ .Environment.Name }}-app\n chart: test/chart\n",
|
||||
shouldRender: true,
|
||||
expectErr: false,
|
||||
expectedOutput: "default-app",
|
||||
},
|
||||
{
|
||||
name: "HELMFILE_RENDER_YAML=true - helmfile.yaml.gotmpl IS rendered",
|
||||
envValue: "true",
|
||||
filename: "helmfile.yaml.gotmpl",
|
||||
content: "releases:\n- name: {{ .Environment.Name }}-app\n chart: test/chart\n",
|
||||
shouldRender: true,
|
||||
expectErr: false,
|
||||
expectedOutput: "default-app",
|
||||
},
|
||||
{
|
||||
name: "HELMFILE_RENDER_YAML=false - helmfile.yaml is NOT rendered",
|
||||
envValue: "false",
|
||||
filename: "helmfile.yaml",
|
||||
content: "releases:\n- name: {{ .Environment.Name }}-app\n chart: test/chart\n",
|
||||
shouldRender: false,
|
||||
expectErr: false,
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "HELMFILE_RENDER_YAML=false - helmfile.yaml.gotmpl IS rendered (extension takes precedence)",
|
||||
envValue: "false",
|
||||
filename: "helmfile.yaml.gotmpl",
|
||||
content: "releases:\n- name: {{ .Environment.Name }}-app\n chart: test/chart\n",
|
||||
shouldRender: true,
|
||||
expectErr: false,
|
||||
expectedOutput: "default-app",
|
||||
},
|
||||
{
|
||||
name: "HELMFILE_RENDER_YAML=TRUE (uppercase) - should NOT render (strict comparison)",
|
||||
envValue: "TRUE",
|
||||
filename: "helmfile.yaml",
|
||||
content: "releases:\n- name: {{ .Environment.Name }}-app\n chart: test/chart\n",
|
||||
shouldRender: false,
|
||||
expectErr: false,
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "HELMFILE_RENDER_YAML=1 - should NOT render (strict comparison)",
|
||||
envValue: "1",
|
||||
filename: "helmfile.yaml",
|
||||
content: "releases:\n- name: {{ .Environment.Name }}-app\n chart: test/chart\n",
|
||||
shouldRender: false,
|
||||
expectErr: false,
|
||||
expectedOutput: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.envValue != "" {
|
||||
t.Setenv(envvar.RenderYaml, tc.envValue)
|
||||
}
|
||||
|
||||
files := map[string]string{
|
||||
fmt.Sprintf("/path/to/%s", tc.filename): tc.content,
|
||||
}
|
||||
|
||||
app := &App{
|
||||
OverrideHelmBinary: DefaultHelmBinary,
|
||||
OverrideKubeContext: "default",
|
||||
DisableKubeVersionAutoDetection: true,
|
||||
Logger: newAppTestLogger(),
|
||||
Namespace: "",
|
||||
Env: "default",
|
||||
FileOrDir: tc.filename,
|
||||
}
|
||||
|
||||
expectNoCallsToHelm(app)
|
||||
app = appWithFs(app, files)
|
||||
|
||||
err := app.ForEachState(
|
||||
func(run *Run) (bool, []error) {
|
||||
if tc.shouldRender {
|
||||
// If rendering is expected, check that the template was rendered
|
||||
require.NotNil(t, run.state)
|
||||
require.NotEmpty(t, run.state.Releases)
|
||||
// The rendered name should be "default-app" not "{{ .Environment.Name }}-app"
|
||||
actualName := run.state.Releases[0].Name
|
||||
require.Equal(t, tc.expectedOutput, actualName, "expected release name to be rendered as %s, got %s", tc.expectedOutput, actualName)
|
||||
} else {
|
||||
// If rendering is NOT expected, check that the template was NOT rendered
|
||||
// In this case, the YAML parser will likely fail or the template syntax will remain
|
||||
// We just verify that if there are releases, they contain the unrendered template
|
||||
if run.state != nil && len(run.state.Releases) > 0 {
|
||||
actualName := run.state.Releases[0].Name
|
||||
require.Contains(t, actualName, "{{", "expected template syntax to remain unrendered, got %s", actualName)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
},
|
||||
false,
|
||||
SetFilter(true),
|
||||
)
|
||||
|
||||
if tc.expectErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
if err != nil && !tc.shouldRender {
|
||||
// It's OK if there's an error when we don't expect rendering
|
||||
// because the template syntax might cause YAML parsing issues
|
||||
t.Logf("Expected error when not rendering template: %v", err)
|
||||
} else if tc.shouldRender {
|
||||
require.NoError(t, err, "unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
|
|
@ -12,6 +13,7 @@ import (
|
|||
"go.uber.org/zap"
|
||||
|
||||
"github.com/helmfile/helmfile/pkg/environment"
|
||||
"github.com/helmfile/helmfile/pkg/envvar"
|
||||
"github.com/helmfile/helmfile/pkg/filesystem"
|
||||
"github.com/helmfile/helmfile/pkg/helmexec"
|
||||
"github.com/helmfile/helmfile/pkg/policy"
|
||||
|
|
@ -216,7 +218,9 @@ func (ld *desiredStateLoader) load(env, overrodeEnv *environment.Environment, ba
|
|||
|
||||
var rawContent []byte
|
||||
|
||||
if filepath.Ext(filename) == ".gotmpl" {
|
||||
shouldRender := filepath.Ext(filename) == ".gotmpl" || os.Getenv(envvar.RenderYaml) == "true"
|
||||
|
||||
if shouldRender {
|
||||
var yamlBuf *bytes.Buffer
|
||||
var err error
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ const (
|
|||
GoYamlV3 = "HELMFILE_GO_YAML_V3"
|
||||
CacheHome = "HELMFILE_CACHE_HOME"
|
||||
Interactive = "HELMFILE_INTERACTIVE"
|
||||
RenderYaml = "HELMFILE_RENDER_YAML" // force helmfile.yaml to be rendered as template regardless of extension, expecting "true" lower case
|
||||
|
||||
// AWSSDKLogLevel controls AWS SDK logging level
|
||||
// Valid values: "off" (default), "minimal", "standard", "verbose", or custom (e.g., "request,response")
|
||||
|
|
|
|||
Loading…
Reference in New Issue