Commit 52dbb3c4 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer/plugin: more stable stderr logging

parent a380c1c9
package plugin package plugin
import ( import (
"bufio"
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
...@@ -53,6 +54,10 @@ type ClientConfig struct { ...@@ -53,6 +54,10 @@ type ClientConfig struct {
// StartTimeout is the timeout to wait for the plugin to say it // StartTimeout is the timeout to wait for the plugin to say it
// has started successfully. // has started successfully.
StartTimeout time.Duration StartTimeout time.Duration
// If non-nil, then the stderr of the client will be written to here
// (as well as the log).
Stderr io.Writer
} }
// This makes sure all the managed subprocesses are killed and properly // This makes sure all the managed subprocesses are killed and properly
...@@ -211,13 +216,13 @@ func (c *Client) Start() (address string, err error) { ...@@ -211,13 +216,13 @@ func (c *Client) Start() (address string, err error) {
} }
stdout := new(bytes.Buffer) stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer) stderr_r, stderr_w := io.Pipe()
cmd := c.config.Cmd cmd := c.config.Cmd
cmd.Env = append(cmd.Env, os.Environ()...) cmd.Env = append(cmd.Env, os.Environ()...)
cmd.Env = append(cmd.Env, env...) cmd.Env = append(cmd.Env, env...)
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stderr = stderr cmd.Stderr = stderr_w
cmd.Stdout = stdout cmd.Stdout = stdout
log.Printf("Starting plugin: %s %#v", cmd.Path, cmd.Args) log.Printf("Starting plugin: %s %#v", cmd.Path, cmd.Args)
...@@ -241,13 +246,14 @@ func (c *Client) Start() (address string, err error) { ...@@ -241,13 +246,14 @@ func (c *Client) Start() (address string, err error) {
// Start goroutine to wait for process to exit // Start goroutine to wait for process to exit
go func() { go func() {
defer stderr_w.Close()
cmd.Wait() cmd.Wait()
log.Printf("%s: plugin process exited\n", cmd.Path) log.Printf("%s: plugin process exited\n", cmd.Path)
c.exited = true c.exited = true
}() }()
// Start goroutine that logs the stderr // Start goroutine that logs the stderr
go c.logStderr(stderr) go c.logStderr(stderr_r)
// Some channels for the next step // Some channels for the next step
timeout := time.After(c.config.StartTimeout) timeout := time.After(c.config.StartTimeout)
...@@ -288,22 +294,21 @@ func (c *Client) Start() (address string, err error) { ...@@ -288,22 +294,21 @@ func (c *Client) Start() (address string, err error) {
return return
} }
func (c *Client) logStderr(buf *bytes.Buffer) { func (c *Client) logStderr(r io.Reader) {
for done := false; !done; { bufR := bufio.NewReader(r)
if c.Exited() { for {
done = true line, err := bufR.ReadString('\n')
} if line != "" {
log.Printf("%s: %s", c.config.Cmd.Path, line)
var err error if c.config.Stderr != nil {
for err != io.EOF { c.config.Stderr.Write([]byte(line))
var line string
line, err = buf.ReadString('\n')
if line != "" {
log.Printf("%s: %s", c.config.Cmd.Path, line)
} }
} }
time.Sleep(10 * time.Millisecond) if err == io.EOF {
break
}
} }
// Flag that we've completed logging for others // Flag that we've completed logging for others
......
package plugin package plugin
import ( import (
"bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"strings"
"testing" "testing"
"time" "time"
) )
...@@ -50,6 +52,32 @@ func TestClient_Start_Timeout(t *testing.T) { ...@@ -50,6 +52,32 @@ func TestClient_Start_Timeout(t *testing.T) {
} }
} }
func TestClient_Stderr(t *testing.T) {
stderr := new(bytes.Buffer)
process := helperProcess("stderr")
c := NewClient(&ClientConfig{
Cmd: process,
Stderr: stderr,
})
defer c.Kill()
if _, err := c.Start(); err != nil {
t.Fatalf("err: %s", err)
}
for !c.Exited() {
time.Sleep(10 * time.Millisecond)
}
if !strings.Contains(stderr.String(), "HELLO\n") {
t.Fatalf("bad log data: '%s'", stderr.String())
}
if !strings.Contains(stderr.String(), "WORLD\n") {
t.Fatalf("bad log data: '%s'", stderr.String())
}
}
func TestClient_Stdin(t *testing.T) { func TestClient_Stdin(t *testing.T) {
// Overwrite stdin for this test with a temporary file // Overwrite stdin for this test with a temporary file
tf, err := ioutil.TempFile("", "packer") tf, err := ioutil.TempFile("", "packer")
......
...@@ -67,6 +67,10 @@ func TestHelperProcess(*testing.T) { ...@@ -67,6 +67,10 @@ func TestHelperProcess(*testing.T) {
case "start-timeout": case "start-timeout":
time.Sleep(1 * time.Minute) time.Sleep(1 * time.Minute)
os.Exit(1) os.Exit(1)
case "stderr":
fmt.Println(":1234")
log.Println("HELLO")
log.Println("WORLD")
case "stdin": case "stdin":
fmt.Println(":1234") fmt.Println(":1234")
data := make([]byte, 5) data := make([]byte, 5)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment