diff --git a/cmd/ain/main.go b/cmd/ain/main.go index c41dc88..8182f49 100644 --- a/cmd/ain/main.go +++ b/cmd/ain/main.go @@ -106,6 +106,7 @@ Project home page: https://github.com/jonaslu/ain` } if fatal != "" { + // Is this valid? checkSignalRaisedAndExit(assembledCtx, signalRaised) fmt.Fprintln(os.Stderr, fatal) @@ -113,21 +114,43 @@ Project home page: https://github.com/jonaslu/ain` } backendInput.PrintCommand = printCommand + + call, err := call.Setup(backendInput) + if err != nil { + fmt.Fprint(os.Stderr, err.Error()) + os.Exit(1) + } + + if printCommand { + // Tempfile always left when calling as string + fmt.Fprint(os.Stdout, call.CallAsString()) + return + } + backendInput.LeaveTempFile = leaveTmpFile + backendOutput, err := call.CallAsCmd(assembledCtx) - backendOutput, err := call.CallBackend(assembledCtx, backendInput) + teardownErr := call.Teardown() + if teardownErr != nil { + fmt.Fprint(os.Stderr, teardownErr.Error()) + } - if err != nil { - fmt.Fprint(os.Stderr, err) - checkSignalRaisedAndExit(assembledCtx, signalRaised) + if err != nil && assembledCtx.Err() != context.Canceled { + fmt.Fprint(os.Stderr, err.Error()) + } - var backendErr *call.BackedErr - if errors.As(err, &backendErr) { - os.Exit(backendErr.ExitCode) - } + if backendOutput != nil { + // It's customary to print stderr first + // to get the users attention on the error + fmt.Fprint(os.Stderr, backendOutput.Stderr) + fmt.Fprint(os.Stdout, backendOutput.Stdout) + } + + checkSignalRaisedAndExit(assembledCtx, signalRaised) + if assembledCtx.Err() == context.DeadlineExceeded || teardownErr != nil { os.Exit(1) } - fmt.Fprint(os.Stdout, backendOutput) + os.Exit(backendOutput.ExitCode) } diff --git a/internal/pkg/call/call.go b/internal/pkg/call/call.go index e6426f6..b0adb58 100644 --- a/internal/pkg/call/call.go +++ b/internal/pkg/call/call.go @@ -1,20 +1,14 @@ package call import ( + "bytes" "context" - "fmt" - "strings" + "os/exec" "github.com/jonaslu/ain/internal/pkg/data" - "github.com/jonaslu/ain/internal/pkg/utils" "github.com/pkg/errors" ) -type BackedErr struct { - Err error - ExitCode int -} - type backendConstructor struct { BinaryName string constructor func(*data.BackendInput, string) backend @@ -35,15 +29,17 @@ var ValidBackends = map[string]backendConstructor{ }, } -func (err *BackedErr) Error() string { - return fmt.Sprintf("Error: %v, exit code: %d\n", err.Err, err.ExitCode) -} - type backend interface { - runAsCmd(context.Context) ([]byte, error) + getAsCmd(context.Context) *exec.Cmd getAsString() string } +type BackendOutput struct { + Stderr string + Stdout string + ExitCode int +} + func getBackend(backendInput *data.BackendInput) (backend, error) { requestedBackend := backendInput.Backend @@ -62,44 +58,65 @@ func ValidBackend(backendName string) bool { return false } -func CallBackend(ctx context.Context, backendInput *data.BackendInput) (string, error) { +type Call struct { + backendInput *data.BackendInput + backend backend + forceRemoveTempFile bool +} + +func Setup(backendInput *data.BackendInput) (*Call, error) { + call := Call{backendInput: backendInput} + backend, err := getBackend(backendInput) if err != nil { - return "", errors.Wrapf(err, "Could not instantiate backend: %s", backendInput.Backend) + return nil, err } + call.backend = backend + if err := backendInput.CreateBodyTempFile(); err != nil { - return "", err + return nil, err } - if backendInput.PrintCommand { - return backend.getAsString(), nil - } + return &call, nil +} + +func (c *Call) CallAsString() string { + return c.backend.getAsString() +} + +func (c *Call) CallAsCmd(ctx context.Context) (*BackendOutput, error) { + backendCmd := c.backend.getAsCmd(ctx) - output, err := backend.runAsCmd(ctx) - removeTmpFileErr := backendInput.RemoveBodyTempFile(err != nil) + var stdout, stderr bytes.Buffer + backendCmd.Stdout = &stdout + backendCmd.Stderr = &stderr - if ctx.Err() == context.Canceled { - return "", removeTmpFileErr + err := backendCmd.Run() + + c.forceRemoveTempFile = err != nil + + backendOutput := &BackendOutput{ + Stderr: stderr.String(), + Stdout: stdout.String(), + ExitCode: backendCmd.ProcessState.ExitCode(), } if ctx.Err() == context.DeadlineExceeded { - return "", utils.CascadeErrorMessage( - errors.Errorf("Backend-call: %s timed out after %d seconds", backendInput.Backend, ctx.Value(data.TimeoutContextValueKey{})), - removeTmpFileErr, - ) + err = errors.Errorf("Backend-call: %s timed out after %d seconds", + c.backendInput.Backend, + ctx.Value(data.TimeoutContextValueKey{})) + + return backendOutput, err } if err != nil { - return "", utils.CascadeErrorMessage( - errors.Wrapf(err, "Error running: %s\n%s", backendInput.Backend, strings.TrimSpace(string(output))), - removeTmpFileErr, - ) + return backendOutput, errors.Wrapf(err, "Error running: %s", c.backendInput.Backend) } - if removeTmpFileErr != nil { - return "", errors.Wrapf(removeTmpFileErr, "Error running: %s\n%s", backendInput.Backend, strings.TrimSpace(string(output))) - } + return backendOutput, nil +} - return string(output), nil +func (c *Call) Teardown() error { + return c.backendInput.RemoveBodyTempFile(c.forceRemoveTempFile) } diff --git a/internal/pkg/call/curl.go b/internal/pkg/call/curl.go index b3d3063..d5b644e 100644 --- a/internal/pkg/call/curl.go +++ b/internal/pkg/call/curl.go @@ -56,7 +56,7 @@ func (curl *curl) getBodyArgument() []string { return []string{} } -func (curl *curl) runAsCmd(ctx context.Context) ([]byte, error) { +func (curl *curl) getAsCmd(ctx context.Context) *exec.Cmd { args := []string{} for _, backendOpt := range curl.backendInput.BackendOptions { args = append(args, backendOpt...) @@ -70,16 +70,7 @@ func (curl *curl) runAsCmd(ctx context.Context) ([]byte, error) { args = append(args, curl.getBodyArgument()...) args = append(args, curl.backendInput.Host.String()) - curlCmd := exec.CommandContext(ctx, curl.binaryName, args...) - output, err := curlCmd.CombinedOutput() - if err != nil { - return output, &BackedErr{ - Err: err, - ExitCode: curlCmd.ProcessState.ExitCode(), - } - } - - return output, err + return exec.CommandContext(ctx, curl.binaryName, args...) } func (curl *curl) getAsString() string { @@ -101,7 +92,7 @@ func (curl *curl) getAsString() string { utils.EscapeForShell(curl.backendInput.Host.String()), }) - output := curl.binaryName + " " + utils.PrettyPrintStringsForShell(args) + cmdAsString := curl.binaryName + " " + utils.PrettyPrintStringsForShell(args) - return output + return cmdAsString } diff --git a/internal/pkg/call/httpie.go b/internal/pkg/call/httpie.go index 18c5429..ecc5742 100644 --- a/internal/pkg/call/httpie.go +++ b/internal/pkg/call/httpie.go @@ -51,7 +51,7 @@ func (httpie *httpie) getBodyArgument() []string { return []string{} } -func (httpie *httpie) runAsCmd(ctx context.Context) ([]byte, error) { +func (httpie *httpie) getAsCmd(ctx context.Context) *exec.Cmd { args := []string{} for _, backendOpt := range httpie.backendInput.BackendOptions { args = append(args, backendOpt...) @@ -66,16 +66,7 @@ func (httpie *httpie) runAsCmd(ctx context.Context) ([]byte, error) { args = append(args, httpie.getBodyArgument()...) httpCmd := exec.CommandContext(ctx, httpie.binaryName, args...) - output, err := httpCmd.CombinedOutput() - - if err != nil { - return output, &BackedErr{ - Err: err, - ExitCode: httpCmd.ProcessState.ExitCode(), - } - } - - return output, nil + return httpCmd } func (httpie *httpie) getAsString() string { diff --git a/internal/pkg/call/wget.go b/internal/pkg/call/wget.go index 4047548..067f4da 100644 --- a/internal/pkg/call/wget.go +++ b/internal/pkg/call/wget.go @@ -76,7 +76,7 @@ func (wget *wget) getBodyArgument() []string { return []string{} } -func (wget *wget) runAsCmd(ctx context.Context) ([]byte, error) { +func (wget *wget) getAsCmd(ctx context.Context) *exec.Cmd { args := []string{} for _, backendOpt := range wget.backendInput.BackendOptions { args = append(args, backendOpt...) @@ -92,15 +92,7 @@ func (wget *wget) runAsCmd(ctx context.Context) ([]byte, error) { args = append(args, wget.backendInput.Host.String()) wgetCmd := exec.CommandContext(ctx, wget.binaryName, args...) - output, err := wgetCmd.CombinedOutput() - if err != nil { - return output, &BackedErr{ - Err: err, - ExitCode: wgetCmd.ProcessState.ExitCode(), - } - } - - return output, nil + return wgetCmd } func (wget *wget) getAsString() string {