Skip to content

Commit

Permalink
feat: Better exec error reporting (#1159)
Browse files Browse the repository at this point in the history
Enhances Helmfile to print more helpful message on error while calling `exec` template function.

Helmfile has been printing error messages like the below:

```
in ./helmfile.yaml: error during helmfile.yaml.part.0 parsing: template: stringTemplate:5:8: executing "stringTemplate" at <exec "./exectest.sha" (list)>: error calling exec: exit status 1
```

Adding captured stdout and stderr, with some indentation to make it readable, it now produces the following message on missing executable:

```
$ make build && ./helmfile build
go build
in ./helmfile.yaml: error during helmfile.yaml.part.0 parsing: template: stringTemplate:5:8: executing "stringTemplate" at <exec "./exectest.sha" (list)>: error calling exec: fork/exec ./exectest.sha: no such file or directory

COMMAND:
  ./exectest.sha

ERROR:
  fork/exec ./exectest.sha: no such file or directory
```

On non-zero exit status without output:

```
$ make build && ./helmfile build
go build
in ./helmfile.yaml: error during helmfile.yaml.part.0 parsing: template: stringTemplate:5:8: executing "stringTemplate" at <exec "./exectest.sh" (list)>: error calling exec: exit status 1

COMMAND:
  ./exectest.sh

ERROR:
  exit status 1
```

On non-zero exit status with output:

```
$ make build && ./helmfile build
go build
in ./helmfile.yaml: error during helmfile.yaml.part.0 parsing: template: stringTemplate:5:8: executing "stringTemplate" at <exec "./exectest.sh" (list)>: error calling exec: exit status 2

COMMAND:
  ./exectest.sh

ERROR:
  exit status 2

COMBINED OUTPUT:
  out1
  err1
```

Resolves #1158
  • Loading branch information
mumoshu authored Mar 29, 2020
1 parent 20a39e9 commit f676c61
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 4 deletions.
45 changes: 43 additions & 2 deletions pkg/tmpl/context_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,27 @@ func (c *Context) Exec(command string, args []interface{}, inputs ...string) (st
var bytes []byte

g.Go(func() error {
bs, err := cmd.Output()
// We use CombinedOutput to produce helpful error messages
// See https://github.com/roboll/helmfile/issues/1158
bs, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("exec cmd=%s args=[%s] failed: %v", command, strings.Join(strArgs, ", "), err)
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)
}

bytes = bs
Expand All @@ -109,6 +127,29 @@ 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) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/tmpl/context_tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"text/template"
)

func (c *Context) stringTemplate() *template.Template {
func (c *Context) newTemplate() *template.Template {
funcMap := sprig.TxtFuncMap()
for name, f := range c.createFuncMap() {
funcMap[name] = f
Expand All @@ -21,7 +21,7 @@ func (c *Context) stringTemplate() *template.Template {
}

func (c *Context) RenderTemplateToBuffer(s string, data ...interface{}) (*bytes.Buffer, error) {
var t, parseErr = c.stringTemplate().Parse(s)
var t, parseErr = c.newTemplate().Parse(s)
if parseErr != nil {
return nil, parseErr
}
Expand Down

0 comments on commit f676c61

Please sign in to comment.