From 48811ed4e5dd729e66dbb91b795da0a75ede4af1 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 14 Jan 2020 16:56:53 -0800 Subject: [PATCH] replace buffer copy loop with a scanner that handles carriage returns --- builder/vagrant/driver_2_2.go | 91 ++++++++++++++++++--------------- builder/vagrant/step_add_box.go | 3 +- 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/builder/vagrant/driver_2_2.go b/builder/vagrant/driver_2_2.go index 5d14cabb6..3b2a1b94c 100644 --- a/builder/vagrant/driver_2_2.go +++ b/builder/vagrant/driver_2_2.go @@ -1,6 +1,7 @@ package vagrant import ( + "bufio" "bytes" "fmt" "log" @@ -8,7 +9,6 @@ import ( "os/exec" "regexp" "strings" - "time" "github.com/hashicorp/go-version" ) @@ -190,16 +190,47 @@ func (d *Vagrant_2_2_Driver) Version() (string, error) { return version, nil } -func (d *Vagrant_2_2_Driver) vagrantCmd(args ...string) (string, string, error) { - var stdout, stderr bytes.Buffer +// Copied and modified from Bufio; this will return data that contains a +// carriage return, not just data that contains a newline. +// This allows us to stream progress output from vagrant that would otherwise +// be smothered. It is a bit noisy, but probably prefereable to suppressing +// the output in a way that looks like Packer has hung. +func ScanLinesInclCR(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + if i := bytes.IndexByte(data, '\n'); i >= 0 { + // We have a full newline-terminated line. + return i + 1, data[0:i], nil + } + if i := bytes.IndexByte(data, '\r'); i >= 0 { + // We have a CR-terminated line. + return i + 1, data[0:i], nil + } + // If we're at EOF, we have a final, non-terminated line. Return it. + if atEOF { + return len(data), data, nil + } + // Request more data. + return 0, nil, nil +} +func (d *Vagrant_2_2_Driver) vagrantCmd(args ...string) (string, string, error) { log.Printf("Calling Vagrant CLI: %#v", args) cmd := exec.Command(d.vagrantBinary, args...) cmd.Env = append(os.Environ(), fmt.Sprintf("VAGRANT_CWD=%s", d.VagrantCWD)) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Start() + stderr, err := cmd.StderrPipe() + if err != nil { + log.Printf("error getting err pipe") + } + + stdout, err := cmd.StdoutPipe() + if err != nil { + log.Printf("error getting out pioe") + } + + err = cmd.Start() if err != nil { return "", "", fmt.Errorf("Error starting vagrant command with args: %q", strings.Join(args, " ")) @@ -208,44 +239,24 @@ func (d *Vagrant_2_2_Driver) vagrantCmd(args ...string) (string, string, error) stdoutString := "" stderrString := "" - donech := make(chan bool) - + scanOut := bufio.NewScanner(stdout) + scanOut.Split(ScanLinesInclCR) + scanErr := bufio.NewScanner(stderr) + scanErr.Split(ScanLinesInclCR) go func() { - for { - select { - case <-donech: - line := stdout.String() - if line != "" { - log.Printf("[vagrant driver] stdout: %s", line) - } - stdoutString += line - line = stderr.String() - if line != "" { - log.Printf("[vagrant driver] stderr: %s", line) - } - stderrString += line - break - default: - line, _ := stdout.ReadString('\n') - if line != "" { - log.Printf("[vagrant driver] stdout: %s", line) - stdoutString += line - - } - line, _ = stderr.ReadString('\n') - if line != "" { - log.Printf("[vagrant driver] stderr: %s", line) - stderrString += line - - } - time.Sleep(500) - } + for scanErr.Scan() { + line := scanErr.Text() + log.Printf("[vagrant driver] stderr: %s", line) + stderrString += line + "\n" } }() - err = cmd.Wait() - - donech <- true + for scanOut.Scan() { + line := scanOut.Text() + log.Printf("[vagrant driver] stdout: %s", line) + stdoutString += line + "\n" + } + cmd.Wait() if _, ok := err.(*exec.ExitError); ok { err = fmt.Errorf("Vagrant error: %s", stderrString) diff --git a/builder/vagrant/step_add_box.go b/builder/vagrant/step_add_box.go index 7fa8db320..00b8af9b9 100644 --- a/builder/vagrant/step_add_box.go +++ b/builder/vagrant/step_add_box.go @@ -82,7 +82,8 @@ func (s *StepAddBox) Run(ctx context.Context, state multistep.StateBag) multiste return multistep.ActionContinue } - ui.Say("Adding box using vagrant box add..") + ui.Say("Adding box using vagrant box add ...") + ui.Message("(this can take some time if we need to download the box)") addArgs := s.generateAddArgs() log.Printf("[vagrant] Calling box add with following args %s", strings.Join(addArgs, " "))