Skip to content
Permalink
Browse files

show command output in errors

  • Loading branch information...
BenTheElder committed Sep 9, 2019
1 parent ff0cd69 commit c5fb3b976c801b399c5bb8aeb3fd69213ad446a0
Showing with 60 additions and 3 deletions.
  1. +1 −0 go.mod
  2. +2 −0 go.sum
  3. +57 −3 pkg/exec/local.go
1 go.mod
@@ -3,6 +3,7 @@ module sigs.k8s.io/kind
go 1.13

require (
github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053
github.com/google/gofuzz v1.0.0
github.com/google/uuid v1.1.1
github.com/pkg/errors v0.8.1
2 go.sum
@@ -5,6 +5,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053 h1:H/GMMKYPkEIC3DF/JWQz8Pdd+Feifov2EIgGfNpeogI=
github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053/go.mod h1:xW8sBma2LE3QxFSzCnH9qe6gAE2yO9GvQaWwX89HxbE=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -17,9 +17,13 @@ limitations under the License.
package exec

import (
"bytes"
"fmt"
"io"
osexec "os/exec"
"strings"

"github.com/alessio/shellescape"
"github.com/pkg/errors"

"sigs.k8s.io/kind/pkg/globals"
@@ -68,9 +72,59 @@ func (cmd *LocalCmd) SetStderr(w io.Writer) Cmd {
return cmd
}

// Run runs
// Run runs the command
func (cmd *LocalCmd) Run() error {
err := &localCmdError{
CmdArgs: cmd.Args,
}
// TODO(BenTheElder): adding bytes.Buffer to both multiwriters might need
// to be wrapped with a mutex
if cmd.Stdout != nil {
cmd.Stdout = io.MultiWriter(cmd.Stdout, &err.CmdOut)
} else {
cmd.Stdout = &err.CmdOut
}
if cmd.Stderr != nil {
cmd.Stderr = io.MultiWriter(cmd.Stderr, &err.CmdOut)
} else {
cmd.Stderr = &err.CmdOut
}
// TODO: should be in the caller or logger should be injected somehow ...
globals.GetLogger().V(3).Infof("Running: %v %v", cmd.Path, cmd.Args)
return errors.WithStack(cmd.Cmd.Run())
globals.GetLogger().V(3).Infof("Running: \"%s\"", err.prettyCommand())
if e := cmd.Cmd.Run(); e != nil {
err.Inner = e
return errors.WithStack(err)
}
return nil
}

type localCmdError struct {
CmdArgs []string
CmdOut bytes.Buffer
Inner error
}

// TODO(BenTheElder): implement formatter instead, and only show
// output for %+v ?
func (e *localCmdError) Error() string {
return fmt.Sprintf(
"command \"%s\" failed with error: %v and output:\n%s",
e.prettyCommand(), e.Inner, e.CmdOut.Bytes(),
)
}

func (e *localCmdError) prettyCommand() string {
var out strings.Builder
for i, arg := range e.CmdArgs {
out.WriteString(shellescape.Quote(arg))
if i+1 != len(e.CmdArgs) {
out.WriteByte(' ')
}
}
return out.String()
}

// Cause mimics github.com/pkg/errors's Cause pattern for errors
func (e *localCmdError) Cause() error {
return e.Inner
}

0 comments on commit c5fb3b9

Please sign in to comment.
You can’t perform that action at this time.