From 655bea123ca606183dcb6146907776d601d579c5 Mon Sep 17 00:00:00 2001 From: Keisuke Kondo Date: Wed, 1 Mar 2023 18:11:39 +0900 Subject: [PATCH] output stderr from a command by default in external probe with once mode --- probes/external/external.go | 16 ++++++++++------ probes/external/external_test.go | 8 ++++---- probes/external/runcmd_linux.go | 10 +++------- probes/external/runcmd_nonlinux.go | 9 +++++++-- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/probes/external/external.go b/probes/external/external.go index c32b0589953..2cc0e3ba164 100644 --- a/probes/external/external.go +++ b/probes/external/external.go @@ -97,7 +97,7 @@ type Probe struct { dataChan chan *metrics.EventMetrics // This is used for overriding run command logic for testing. - runCommandFunc func(ctx context.Context, cmd string, args []string) ([]byte, error) + runCommandFunc func(ctx context.Context, cmd string, args []string) ([]byte, []byte, error) // default payload metrics that we clone from to build per-target payload // metrics. @@ -559,29 +559,33 @@ func (p *Probe) runOnceProbe(ctx context.Context) { result.total++ startTime := time.Now() - var b []byte + var stdout, stderr []byte var err error if p.runCommandFunc != nil { - b, err = p.runCommandFunc(ctx, p.cmdName, args) + stdout, stderr, err = p.runCommandFunc(ctx, p.cmdName, args) } else { - b, err = p.runCommand(ctx, p.cmdName, args) + stdout, stderr, err = p.runCommand(ctx, p.cmdName, args) } success := true if err != nil { success = false if exitErr, ok := err.(*exec.ExitError); ok { - p.l.Errorf("external probe process died with the status: %s. Stderr: %s", exitErr.Error(), exitErr.Stderr) + p.l.Errorf("external probe process died with the status: %s. Stderr: %s", exitErr.Error(), stderr) } else { p.l.Errorf("Error executing the external program. Err: %v", err) } + } else { + if len(stderr) != 0 { + p.l.Warningf("Stderr: %s", stderr) + } } p.processProbeResult(&probeStatus{ target: target.Name, success: success, latency: time.Since(startTime), - payload: string(b), + payload: string(stdout), }, result) }(target, p.results[target.Name]) } diff --git a/probes/external/external_test.go b/probes/external/external_test.go index 8f931c36ba5..909c682d934 100644 --- a/probes/external/external_test.go +++ b/probes/external/external_test.go @@ -342,11 +342,11 @@ func TestProbeOnceMode(t *testing.T) { tgts := []string{"target1", "target2"} // Set runCommand to a function that runs successfully and returns a pyload. - p.runCommandFunc = func(ctx context.Context, cmd string, cmdArgs []string) ([]byte, error) { + p.runCommandFunc = func(ctx context.Context, cmd string, cmdArgs []string) ([]byte, []byte, error) { var resp []string resp = append(resp, fmt.Sprintf("cmd \"%s\"", cmd)) resp = append(resp, fmt.Sprintf("num-args %d", len(cmdArgs))) - return []byte(strings.Join(resp, "\n")), nil + return []byte(strings.Join(resp, "\n")), nil, nil } total, success := make(map[string]int64), make(map[string]int64) @@ -359,8 +359,8 @@ func TestProbeOnceMode(t *testing.T) { runAndVerifyProbe(t, p, tgts, total, success) // Try with failing command now - p.runCommandFunc = func(ctx context.Context, cmd string, cmdArgs []string) ([]byte, error) { - return nil, fmt.Errorf("error executing %s", cmd) + p.runCommandFunc = func(ctx context.Context, cmd string, cmdArgs []string) ([]byte, []byte, error) { + return nil, nil, fmt.Errorf("error executing %s", cmd) } for _, tgt := range tgts { diff --git a/probes/external/runcmd_linux.go b/probes/external/runcmd_linux.go index bb3bc551d31..e4aff9b31b6 100644 --- a/probes/external/runcmd_linux.go +++ b/probes/external/runcmd_linux.go @@ -30,14 +30,14 @@ import ( "time" ) -func (p *Probe) runCommand(ctx context.Context, cmd string, args []string) ([]byte, error) { +func (p *Probe) runCommand(ctx context.Context, cmd string, args []string) ([]byte, []byte, error) { c := exec.Command(cmd, args...) c.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} var stdout, stderr bytes.Buffer c.Stdout, c.Stderr = &stdout, &stderr if err := c.Start(); err != nil { - return stdout.Bytes(), err + return stdout.Bytes(), stderr.Bytes(), err } // This goroutine is similar to the one started by exec.Start if command is @@ -73,9 +73,5 @@ func (p *Probe) runCommand(ctx context.Context, cmd string, args []string) ([]by } }() - if exitErr, ok := err.(*exec.ExitError); ok { - exitErr.Stderr = stderr.Bytes() - } - - return stdout.Bytes(), err + return stdout.Bytes(), stderr.Bytes(), err } diff --git a/probes/external/runcmd_nonlinux.go b/probes/external/runcmd_nonlinux.go index 5cc71f4edae..7bdde3aab66 100644 --- a/probes/external/runcmd_nonlinux.go +++ b/probes/external/runcmd_nonlinux.go @@ -17,10 +17,15 @@ package external import ( + "bytes" "context" "os/exec" ) -func (p *Probe) runCommand(ctx context.Context, cmd string, args []string) ([]byte, error) { - return exec.CommandContext(ctx, cmd, args...).Output() +func (p *Probe) runCommand(ctx context.Context, cmd string, args []string) ([]byte, []byte, error) { + c := exec.CommandContext(ctx, cmd, args...) + var stdout, stderr bytes.Buffer + c.Stdout, c.Stderr = &stdout, &stderr + err := c.Run() + return stdout.Bytes(), stderr.Bytes(), err }