fix: Do not include stderr content in result of `{{ exec }}` (#1298)

Seems like this was a regression since f676c61425 and #1159

Fixes #1258
This commit is contained in:
KUOKA Yusuke 2020-06-06 09:29:12 +09:00 committed by GitHub
parent 63c8df9873
commit 7db31846cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 57 deletions

View File

@ -5,19 +5,55 @@ import (
"strings"
)
func newExitError(helmCmdPath string, exitStatus int, errorMessage string) ExitError {
func newExitError(path string, args []string, exitStatus int, err error, stderr, combined string) ExitError {
var out string
out += fmt.Sprintf("PATH:\n%s", Indent(path, " "))
out += "\n\nARGS:"
for i, a := range args {
out += fmt.Sprintf("\n%s", Indent(fmt.Sprintf("%d: %s (%d bytes)", i, a, len(a)), " "))
}
out += fmt.Sprintf("\n\nERROR:\n%s", Indent(err.Error(), " "))
out += fmt.Sprintf("\n\nEXIT STATUS\n%s", Indent(fmt.Sprintf("%d", exitStatus), " "))
if len(stderr) > 0 {
out += fmt.Sprintf("\n\nSTDERR:\n%s", Indent(stderr, " "))
}
if len(combined) > 0 {
out += fmt.Sprintf("\n\nCOMBINED OUTPUT:\n%s", Indent(combined, " "))
}
return ExitError{
Message: fmt.Sprintf("the following cmd exited with status %d:\n%s\n\n%s", exitStatus, indent(strings.TrimSpace(helmCmdPath)), indent(strings.TrimSpace(errorMessage))),
Message: fmt.Sprintf("command %q exited with non-zero status:\n\n%s", path, out),
Code: exitStatus,
}
}
func indent(text string) string {
// indents a block of text with an indent string
func Indent(text, indent string) string {
var b strings.Builder
b.Grow(len(text) * 2)
lines := strings.Split(text, "\n")
for i := range lines {
lines[i] = " " + lines[i]
last := len(lines) - 1
for i, j := range lines {
if i > 0 && i < last && j != "" {
b.WriteString("\n")
}
if j != "" {
b.WriteString(indent + j)
}
}
return strings.Join(lines, "\n")
return b.String()
}
// ExitError is created whenever your shell command exits with a non-zero exit status

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"go.uber.org/zap"
"io"
"os"
"os/exec"
"strings"
@ -33,10 +34,10 @@ func (shell ShellRunner) Execute(cmd string, args []string, env map[string]strin
preparedCmd := exec.Command(cmd, args...)
preparedCmd.Dir = shell.Dir
preparedCmd.Env = mergeEnv(os.Environ(), env)
return combinedOutput(preparedCmd, shell.Logger)
return Output(preparedCmd)
}
func combinedOutput(c *exec.Cmd, logger *zap.SugaredLogger) ([]byte, error) {
func Output(c *exec.Cmd) ([]byte, error) {
if c.Stdout != nil {
return nil, errors.New("exec: Stdout already set")
}
@ -45,13 +46,11 @@ func combinedOutput(c *exec.Cmd, logger *zap.SugaredLogger) ([]byte, error) {
}
var stdout bytes.Buffer
var stderr bytes.Buffer
c.Stdout = &stdout
c.Stderr = &stderr
var combined bytes.Buffer
c.Stdout = io.MultiWriter(&stdout, &combined)
c.Stderr = io.MultiWriter(&stderr, &combined)
err := c.Run()
o := stdout.Bytes()
e := stderr.Bytes()
if err != nil {
// TrimSpace is necessary, because otherwise helmfile prints the redundant new-lines after each error like:
//
@ -64,14 +63,13 @@ func combinedOutput(c *exec.Cmd, logger *zap.SugaredLogger) ([]byte, error) {
// so that helmfile could return its own exit code accordingly
waitStatus := ee.Sys().(syscall.WaitStatus)
exitStatus := waitStatus.ExitStatus()
cmd := fmt.Sprintf("%s %s", c.Path, strings.Join(c.Args, " "))
err = newExitError(cmd, exitStatus, string(e))
err = newExitError(c.Path, c.Args, exitStatus, ee, stderr.String(), combined.String())
default:
panic(fmt.Sprintf("unexpected error: %v", err))
}
}
return o, err
return stdout.Bytes(), err
}
func mergeEnv(orig []string, new map[string]string) []string {

View File

@ -2,6 +2,7 @@ package tmpl
import (
"fmt"
"github.com/roboll/helmfile/pkg/helmexec"
"golang.org/x/sync/errgroup"
"gopkg.in/yaml.v2"
"io"
@ -97,25 +98,9 @@ func (c *Context) Exec(command string, args []interface{}, inputs ...string) (st
g.Go(func() error {
// We use CombinedOutput to produce helpful error messages
// See https://github.com/roboll/helmfile/issues/1158
bs, err := cmd.CombinedOutput()
bs, err := helmexec.Output(cmd)
if err != nil {
args := strings.Join(strArgs, ", ")
shownCmd := []string{command}
if len(args) > 0 {
shownCmd = append(shownCmd, args)
}
var out string
out += fmt.Sprintf("\n\nCOMMAND:\n%s", Indent(strings.Join(shownCmd, " "), " "))
out += fmt.Sprintf("\n\nERROR:\n%s", Indent(err.Error(), " "))
if len(bs) > 0 {
out += fmt.Sprintf("\n\nCOMBINED OUTPUT:\n%s", Indent(string(bs), " "))
}
return fmt.Errorf("%v%s", err, out)
return err
}
bytes = bs
@ -130,29 +115,6 @@ func (c *Context) Exec(command string, args []interface{}, inputs ...string) (st
return string(bytes), nil
}
// indents a block of text with an indent string
func Indent(text, indent string) string {
var b strings.Builder
b.Grow(len(text) * 2)
lines := strings.Split(text, "\n")
last := len(lines) - 1
for i, j := range lines {
if i > 0 && i < last && j != "" {
b.WriteString("\n")
}
if j != "" {
b.WriteString(indent + j)
}
}
return b.String()
}
func (c *Context) ReadFile(filename string) (string, error) {
var path string
if filepath.IsAbs(filename) {