fix: apply post-renderer to output-dir-template output
When --output-dir and --post-renderer are both passed to helm template, Helm writes pre-post-renderer content to files and sends post-renderer output to stdout. This workaround strips --output-dir from helm flags, captures the post-renderer-processed stdout, and writes it to the output directory. Fixes #2515 Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
parent
b50c9d6256
commit
8de2d66952
|
|
@ -646,17 +646,55 @@ func (helm *execer) TemplateRelease(name string, chart string, flags ...string)
|
|||
helm.logger.Infof("Templating release=%v, chart=%v", name, redactedURL(chart))
|
||||
args := []string{"template", name, chart}
|
||||
|
||||
out, err := helm.exec(append(args, flags...), map[string]string{}, nil)
|
||||
|
||||
var outputToFile bool
|
||||
var hasPostRenderer bool
|
||||
|
||||
for _, f := range flags {
|
||||
if strings.HasPrefix("--output-dir", f) {
|
||||
outputToFile = true
|
||||
break
|
||||
}
|
||||
if f == "--post-renderer" {
|
||||
hasPostRenderer = true
|
||||
}
|
||||
}
|
||||
|
||||
if outputToFile && hasPostRenderer {
|
||||
// Helm does not apply --post-renderer to files written by --output-dir.
|
||||
// It writes pre-post-renderer content to files and sends post-renderer output to stdout.
|
||||
// Workaround: run without --output-dir, capture stdout (with post-renderer applied),
|
||||
// and write the output to the output directory ourselves.
|
||||
var outputDir string
|
||||
filteredFlags := make([]string, 0, len(flags))
|
||||
for i := 0; i < len(flags); i++ {
|
||||
if flags[i] == "--output-dir" && i+1 < len(flags) {
|
||||
outputDir = flags[i+1]
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(flags[i], "--output-dir=") {
|
||||
outputDir = strings.TrimPrefix(flags[i], "--output-dir=")
|
||||
continue
|
||||
}
|
||||
filteredFlags = append(filteredFlags, flags[i])
|
||||
}
|
||||
|
||||
out, err := helm.exec(append(args, filteredFlags...), map[string]string{}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(out) > 0 {
|
||||
outputPath := filepath.Join(outputDir, name+".yaml")
|
||||
if writeErr := os.WriteFile(outputPath, append(out, '\n'), 0644); writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
helm.logger.Debugf("Wrote post-renderer output to %s", outputPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := helm.exec(append(args, flags...), map[string]string{}, nil)
|
||||
|
||||
if outputToFile {
|
||||
// With --output-dir is passed to helm-template,
|
||||
// we can safely direct all the logs from it to our logger.
|
||||
|
|
|
|||
|
|
@ -1255,6 +1255,46 @@ exec: helm --kubeconfig config --kube-context dev template release https://examp
|
|||
}
|
||||
}
|
||||
|
||||
func Test_Template_PostRendererWithOutputDir(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
|
||||
runner := &mockRunner{output: []byte("apiVersion: v1\nkind: Namespace\n")}
|
||||
helm, err := New("helm", HelmExecOptions{}, logger, "config", "dev", runner)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
err = helm.TemplateRelease("myrelease", "path/to/chart",
|
||||
"--post-renderer", "/bin/echo",
|
||||
"--output-dir", tmpDir,
|
||||
"--values", "file.yml",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
outputPath := filepath.Join(tmpDir, "myrelease.yaml")
|
||||
data, err := os.ReadFile(outputPath)
|
||||
if err != nil {
|
||||
t.Fatalf("expected output file %s to exist: %v", outputPath, err)
|
||||
}
|
||||
|
||||
expected := "apiVersion: v1\nkind: Namespace\n\n"
|
||||
if string(data) != expected {
|
||||
t.Errorf("output file content:\nactual=%q\nexpect=%q", string(data), expected)
|
||||
}
|
||||
|
||||
outputLog := buffer.String()
|
||||
if strings.Contains(outputLog, "--output-dir") {
|
||||
t.Errorf("helm should NOT have been called with --output-dir, got: %s", outputLog)
|
||||
}
|
||||
if !strings.Contains(outputLog, "--post-renderer") {
|
||||
t.Errorf("helm should have been called with --post-renderer, got: %s", outputLog)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_IsHelm3(t *testing.T) {
|
||||
helm3Runner := mockRunner{output: []byte("v3.0.0+ge29ce2a\n")}
|
||||
helm, err := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner)
|
||||
|
|
|
|||
Loading…
Reference in New Issue