From 5726927cba1309e667607f0265444c2b30175a7c Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Sat, 18 Aug 2018 23:06:14 -0500 Subject: [PATCH] Moved the progress bar out of packer.Ui and unlinked it out of all the packer.Ui implementations. Split up the terminal-related functions into a separate terminal.go and calculate the progress bar width by traversing through packer.Ui to avoid the issue with github.com/ugorji/go/codec serializing private members (or unsafe pointers) of structs. Shuffled some arguments around in getConsoleScreenBufferInfo in common/terminal_windows.go so that the interface forces the user to correctly declare a _CONSOLE_SCREEN_BUFFER_INFO type. --- common/download.go | 21 +-- common/download_test.go | 25 ++- common/progress.go | 118 +++++++++++++++ common/progress_test.go | 142 ++++++++++++++++++ common/step_download.go | 4 +- common/terminal.go | 6 + .../ui_posix.go => common/terminal_posix.go | 10 +- common/terminal_test.go | 9 ++ common/terminal_windows.go | 86 +++++++++++ packer/rpc/ui.go | 13 -- packer/rpc/ui_test.go | 48 ++---- packer/ui.go | 92 ------------ packer/ui_windows.go | 82 ---------- provisioner/ansible-local/ui_stub.go | 5 - provisioner/ansible/adapter_test.go | 4 - provisioner/ansible/provisioner.go | 4 - provisioner/file/provisioner.go | 4 +- provisioner/file/provisioner_test.go | 4 - 18 files changed, 403 insertions(+), 274 deletions(-) create mode 100644 common/progress.go create mode 100644 common/progress_test.go create mode 100644 common/terminal.go rename packer/ui_posix.go => common/terminal_posix.go (70%) create mode 100644 common/terminal_test.go create mode 100644 common/terminal_windows.go delete mode 100644 packer/ui_windows.go diff --git a/common/download.go b/common/download.go index b80741f3f..f0e88420b 100644 --- a/common/download.go +++ b/common/download.go @@ -18,11 +18,6 @@ import ( "strings" ) -// required import for progress-bar -import ( - "github.com/hashicorp/packer/packer" -) - // imports related to each Downloader implementation import ( "io" @@ -86,12 +81,12 @@ func HashForType(t string) hash.Hash { // NewDownloadClient returns a new DownloadClient for the given // configuration. -func NewDownloadClient(c *DownloadConfig, bar packer.ProgressBar) *DownloadClient { +func NewDownloadClient(c *DownloadConfig, bar ProgressBar) *DownloadClient { const mtu = 1500 /* ethernet */ - 20 /* ipv4 */ - 20 /* tcp */ // If bar is nil, then use a dummy progress bar that doesn't do anything if bar == nil { - bar = packer.GetDummyProgressBar() + bar = GetDummyProgressBar() } // Create downloader map if it hasn't been specified already. @@ -242,7 +237,7 @@ type HTTPDownloader struct { total uint64 userAgent string - progress packer.ProgressBar + progress ProgressBar } func (d *HTTPDownloader) Cancel() { @@ -336,7 +331,7 @@ func (d *HTTPDownloader) Download(dst *os.File, src *url.URL) error { d.total = d.current + uint64(resp.ContentLength) bar := d.progress - bar.Total = int64(d.total) + bar.SetTotal64(int64(d.total)) progressBar := bar.Start() progressBar.Set64(int64(d.current)) @@ -379,7 +374,7 @@ type FileDownloader struct { current uint64 total uint64 - progress packer.ProgressBar + progress ProgressBar } func (d *FileDownloader) Progress() uint64 { @@ -471,7 +466,7 @@ func (d *FileDownloader) Download(dst *os.File, src *url.URL) error { d.total = uint64(fi.Size()) bar := d.progress - bar.Total = int64(d.total) + bar.SetTotal64(int64(d.total)) progressBar := bar.Start() progressBar.Set64(int64(d.current)) @@ -518,7 +513,7 @@ type SMBDownloader struct { current uint64 total uint64 - progress packer.ProgressBar + progress ProgressBar } func (d *SMBDownloader) Progress() uint64 { @@ -592,7 +587,7 @@ func (d *SMBDownloader) Download(dst *os.File, src *url.URL) error { d.total = uint64(fi.Size()) bar := d.progress - bar.Total = int64(d.total) + bar.SetTotal64(int64(d.total)) progressBar := bar.Start() progressBar.Set64(int64(d.current)) diff --git a/common/download_test.go b/common/download_test.go index 6100be1c2..153e8daf2 100644 --- a/common/download_test.go +++ b/common/download_test.go @@ -12,8 +12,6 @@ import ( "runtime" "strings" "testing" - - "github.com/cheggaaa/pb" ) func TestDownloadClientVerifyChecksum(t *testing.T) { @@ -38,7 +36,7 @@ func TestDownloadClientVerifyChecksum(t *testing.T) { Checksum: checksum, } - d := NewDownloadClient(config, *pb.New64(0)) + d := NewDownloadClient(config, nil) result, err := d.VerifyChecksum(tf.Name()) if err != nil { t.Fatalf("Verify err: %s", err) @@ -61,7 +59,7 @@ func TestDownloadClient_basic(t *testing.T) { Url: ts.URL + "/basic.txt", TargetPath: tf.Name(), CopyFile: true, - }, *pb.New64(0)) + }, nil) path, err := client.Get() if err != nil { @@ -97,7 +95,7 @@ func TestDownloadClient_checksumBad(t *testing.T) { Hash: HashForType("md5"), Checksum: checksum, CopyFile: true, - }, *pb.New64(0)) + }, nil) if _, err := client.Get(); err == nil { t.Fatal("should error") @@ -123,7 +121,7 @@ func TestDownloadClient_checksumGood(t *testing.T) { Hash: HashForType("md5"), Checksum: checksum, CopyFile: true, - }, *pb.New64(0)) + }, nil) path, err := client.Get() if err != nil { @@ -155,8 +153,7 @@ func TestDownloadClient_checksumNoDownload(t *testing.T) { Hash: HashForType("md5"), Checksum: checksum, CopyFile: true, - }, *pb.New64(0)) - + }, nil) path, err := client.Get() if err != nil { t.Fatalf("err: %s", err) @@ -186,7 +183,7 @@ func TestDownloadClient_notFound(t *testing.T) { client := NewDownloadClient(&DownloadConfig{ Url: ts.URL + "/not-found.txt", TargetPath: tf.Name(), - }, *pb.New64(0)) + }, nil) if _, err := client.Get(); err == nil { t.Fatal("should error") @@ -214,7 +211,7 @@ func TestDownloadClient_resume(t *testing.T) { Url: ts.URL, TargetPath: tf.Name(), CopyFile: true, - }, *pb.New64(0)) + }, nil) path, err := client.Get() if err != nil { @@ -276,7 +273,7 @@ func TestDownloadClient_usesDefaultUserAgent(t *testing.T) { CopyFile: true, } - client := NewDownloadClient(config, *pb.New64(0)) + client := NewDownloadClient(config, nil) _, err = client.Get() if err != nil { t.Fatal(err) @@ -309,7 +306,7 @@ func TestDownloadClient_setsUserAgent(t *testing.T) { CopyFile: true, } - client := NewDownloadClient(config, *pb.New64(0)) + client := NewDownloadClient(config, nil) _, err = client.Get() if err != nil { t.Fatal(err) @@ -408,7 +405,7 @@ func TestDownloadFileUrl(t *testing.T) { CopyFile: false, } - client := NewDownloadClient(config, *pb.New64(0)) + client := NewDownloadClient(config, nil) // Verify that we fail to match the checksum _, err = client.Get() @@ -439,7 +436,7 @@ func SimulateFileUriDownload(t *testing.T, uri string) (string, error) { } // go go go - client := NewDownloadClient(config, *pb.New64(0)) + client := NewDownloadClient(config, nil) path, err := client.Get() // ignore any non-important checksum errors if it's not a unc path diff --git a/common/progress.go b/common/progress.go new file mode 100644 index 000000000..6ab12dcd3 --- /dev/null +++ b/common/progress.go @@ -0,0 +1,118 @@ +package common + +import ( + "fmt" + "github.com/cheggaaa/pb" + "github.com/hashicorp/packer/packer" + "log" + "time" +) + +// The ProgressBar interface is used for abstracting cheggaaa's progress- +// bar, or any other progress bar. If a UI does not support a progress- +// bar, then it must return a null progress bar. +const ( + DefaultProgressBarWidth = 80 +) + +type ProgressBar = *pb.ProgressBar + +// Figure out the terminal dimensions and use it to calculate the available rendering space +func calculateProgressBarWidth(length int) int { + // If the UI's width is signed, then this is an interface that doesn't really benefit from a progress bar + if length < 0 { + log.Println("Refusing to render progress-bar for unsupported UI.") + return length + } + + // Figure out the terminal width if possible + width, _, err := GetTerminalDimensions() + if err != nil { + newerr := fmt.Errorf("Unable to determine terminal dimensions: %v", err) + log.Printf("Using default width (%d) for progress-bar due to error: %s", DefaultProgressBarWidth, newerr) + return DefaultProgressBarWidth + } + + // If the terminal width is smaller than the requested length, then complain + if width < length { + newerr := fmt.Errorf("Terminal width (%d) is smaller than UI message width (%d).", width, length) + log.Printf("Using default width (%d) for progress-bar due to error: %s", DefaultProgressBarWidth, newerr) + return DefaultProgressBarWidth + } + + // Otherwise subtract the minimum length and return it + return width - length +} + +// Get a progress bar with the default appearance +func GetDefaultProgressBar() ProgressBar { + bar := pb.New64(0) + bar.ShowPercent = true + bar.ShowCounters = true + bar.ShowSpeed = false + bar.ShowBar = true + bar.ShowTimeLeft = false + bar.ShowFinalTime = false + bar.SetUnits(pb.U_BYTES) + bar.Format("[=>-]") + bar.SetRefreshRate(1 * time.Second) + return bar +} + +// Return a dummy progress bar that doesn't do anything +func GetDummyProgressBar() ProgressBar { + bar := pb.New64(0) + bar.ManualUpdate = true + return bar +} + +// Given a packer.Ui, calculate the number of characters that a packer.Ui will +// prefix a message with. Then we can use this to calculate the progress bar's width. +func calculateUiPrefixLength(ui packer.Ui) int { + var recursiveCalculateUiPrefixLength func(packer.Ui, int) int + + // Define a recursive closure that traverses through all the known packer.Ui types + // and aggregates the length of the message prefix from each particular type + recursiveCalculateUiPrefixLength = func(ui packer.Ui, agg int) int { + switch ui.(type) { + + case *packer.ColoredUi: + // packer.ColoredUi is simply a wrapper around .Ui + u := ui.(*packer.ColoredUi) + return recursiveCalculateUiPrefixLength(u.Ui, agg) + + case *packer.TargetedUI: + // A TargetedUI added the .Target and an arrow by default + const targetedArrowText = "==>" + u := ui.(*packer.TargetedUI) + res := fmt.Sprintf("%s %s: ", targetedArrowText, u.Target) + return recursiveCalculateUiPrefixLength(u.Ui, agg+len(res)) + + case *packer.BasicUi: + // The standard BasicUi appends only a newline + return agg + len("\n") + + case *packer.MachineReadableUi: + // MachineReadableUi doesn't emit anything...like at all + return 0 + } + + log.Printf("Calculating the message prefix length for packer.Ui type (%T) is not implemented. Using the current aggregated length of %d.", ui, agg) + return agg + } + return recursiveCalculateUiPrefixLength(ui, 0) +} + +func GetProgressBar(ui packer.Ui) ProgressBar { + uiPrefixLength := calculateUiPrefixLength(ui) + width := calculateProgressBarWidth(uiPrefixLength) + + log.Printf("ProgressBar: Using progress bar width: %d\n", width) + + bar := GetDefaultProgressBar() + bar.SetWidth(width) + bar.Callback = func(message string) { + ui.Message(message) + } + return bar +} diff --git a/common/progress_test.go b/common/progress_test.go new file mode 100644 index 000000000..028492f88 --- /dev/null +++ b/common/progress_test.go @@ -0,0 +1,142 @@ +package common + +import ( + "github.com/hashicorp/packer/packer" + "testing" +) + +// test packer.Ui implementation to verify that progress bar is being written +type testProgressBarUi struct { + messageCalled bool + messageMessage string +} + +func (u *testProgressBarUi) Say(string) {} +func (u *testProgressBarUi) Error(string) {} +func (u *testProgressBarUi) Machine(string, ...string) {} + +func (u *testProgressBarUi) Ask(string) (string, error) { + return "", nil +} +func (u *testProgressBarUi) Message(message string) { + u.messageCalled = true + u.messageMessage = message +} + +// ..and now let's begin our actual tests +func TestCalculateUiPrefixLength_Unknown(t *testing.T) { + ui := &testProgressBarUi{} + + expected := 0 + if res := calculateUiPrefixLength(ui); res != expected { + t.Fatalf("calculateUiPrefixLength should have returned a length of %d", expected) + } +} + +func TestCalculateUiPrefixLength_BasicUi(t *testing.T) { + ui := &packer.BasicUi{} + + expected := 1 + if res := calculateUiPrefixLength(ui); res != expected { + t.Fatalf("calculateUiPrefixLength should have returned a length of %d", expected) + } +} + +func TestCalculateUiPrefixLength_TargetedUI(t *testing.T) { + ui := &packer.TargetedUI{} + ui.Target = "TestTarget" + arrowText := "==>" + + expected := len(arrowText + " " + ui.Target + ": ") + if res := calculateUiPrefixLength(ui); res != expected { + t.Fatalf("calculateUiPrefixLength should have returned a length of %d", expected) + } +} + +func TestCalculateUiPrefixLength_TargetedUIWrappingBasicUi(t *testing.T) { + ui := &packer.TargetedUI{} + ui.Target = "TestTarget" + ui.Ui = &packer.BasicUi{} + arrowText := "==>" + + expected := len(arrowText + " " + ui.Target + ": " + "\n") + if res := calculateUiPrefixLength(ui); res != expected { + t.Fatalf("calculateUiPrefixLength should have returned a length of %d", expected) + } +} + +func TestCalculateUiPrefixLength_TargetedUIWrappingMachineUi(t *testing.T) { + ui := &packer.TargetedUI{} + ui.Target = "TestTarget" + ui.Ui = &packer.MachineReadableUi{} + + expected := 0 + if res := calculateUiPrefixLength(ui); res != expected { + t.Fatalf("calculateUiPrefixLength should have returned a length of %d", expected) + } +} +func TestDefaultProgressBar(t *testing.T) { + var callbackCalled bool + + // Initialize the default progress bar + bar := GetDefaultProgressBar() + bar.Callback = func(state string) { + callbackCalled = true + t.Logf("TestDefaultProgressBar emitted %#v", state) + } + bar.SetTotal64(1) + + // Set it off + progressBar := bar.Start() + progressBar.Set64(1) + + // Check to see that the callback was hit + if !callbackCalled { + t.Fatalf("TestDefaultProgressBar.Callback should be called") + } +} + +func TestDummyProgressBar(t *testing.T) { + var callbackCalled bool + + // Initialize the dummy progress bar + bar := GetDummyProgressBar() + bar.Callback = func(state string) { + callbackCalled = true + t.Logf("TestDummyProgressBar emitted %#v", state) + } + bar.SetTotal64(1) + + // Now we can go + progressBar := bar.Start() + progressBar.Set64(1) + + // Check to see that the callback was hit + if callbackCalled { + t.Fatalf("TestDummyProgressBar.Callback should not be called") + } +} + +func TestUiProgressBar(t *testing.T) { + + ui := &testProgressBarUi{} + + // Initialize the Ui progress bar + bar := GetProgressBar(ui, nil) + bar.SetTotal64(1) + + // Ensure that callback has been set to something + if bar.Callback == nil { + t.Fatalf("TestUiProgressBar.Callback should be initialized") + } + + // Now we can go + progressBar := bar.Start() + progressBar.Set64(1) + + // Check to see that the callback was hit + if !ui.messageCalled { + t.Fatalf("TestUiProgressBar.messageCalled should be called") + } + t.Logf("TestUiProgressBar emitted %#v", ui.messageMessage) +} diff --git a/common/step_download.go b/common/step_download.go index e8520091d..52361b188 100644 --- a/common/step_download.go +++ b/common/step_download.go @@ -64,7 +64,7 @@ func (s *StepDownload) Run(_ context.Context, state multistep.StateBag) multiste ui.Say(fmt.Sprintf("Retrieving %s", s.Description)) // Get a progress bar from the ui so we can hand it off to the download client - bar := ui.GetProgressBar() + bar := GetProgressBar(ui) // First try to use any already downloaded file // If it fails, proceed to regular download logic @@ -144,7 +144,7 @@ func (s *StepDownload) download(config *DownloadConfig, state multistep.StateBag ui := state.Get("ui").(packer.Ui) // Get a progress bar and hand it off to the download client - bar := ui.GetProgressBar() + bar := GetProgressBar(ui) // Create download client with config and progress bar download := NewDownloadClient(config, bar) diff --git a/common/terminal.go b/common/terminal.go new file mode 100644 index 000000000..4e862880a --- /dev/null +++ b/common/terminal.go @@ -0,0 +1,6 @@ +package common + +// call into one of the platform-specific implementations to get the current terminal dimensions +func GetTerminalDimensions() (width, height int, err error) { + return platformGetTerminalDimensions() +} diff --git a/packer/ui_posix.go b/common/terminal_posix.go similarity index 70% rename from packer/ui_posix.go rename to common/terminal_posix.go index 27c94e8ee..6c69d79e9 100644 --- a/packer/ui_posix.go +++ b/common/terminal_posix.go @@ -1,6 +1,6 @@ // +build !windows -package packer +package common // Imports for determining terminal information across platforms import ( @@ -9,7 +9,13 @@ import ( ) // posix api -func GetTerminalDimensions() (width, height int, err error) { +func platformGetTerminalDimensions() (width, height int, err error) { + + // grab the handle to stdin + // XXX: in some cases, packer closes stdin, so the following can't be guaranteed + /* + tty := os.Stdin + */ // open up a handle to the current tty tty, err := os.Open("/dev/tty") diff --git a/common/terminal_test.go b/common/terminal_test.go new file mode 100644 index 000000000..f1c66b092 --- /dev/null +++ b/common/terminal_test.go @@ -0,0 +1,9 @@ +package common + +import "testing" + +func TestGetTerminalDimensions(t *testing.T) { + if _, _, err := GetTerminalDimensions(); err != nil { + t.Fatalf("Unable to get terminal dimensions: %s", err) + } +} diff --git a/common/terminal_windows.go b/common/terminal_windows.go new file mode 100644 index 000000000..556873eb0 --- /dev/null +++ b/common/terminal_windows.go @@ -0,0 +1,86 @@ +// +build windows + +package common + +import ( + "syscall" + "unsafe" +) + +// windows constants and structures pulled from msdn +const ( + _STD_INPUT_HANDLE = -10 + _STD_OUTPUT_HANDLE = -11 + _STD_ERROR_HANDLE = -12 +) + +type ( + _SHORT int16 + _WORD uint16 + + _SMALL_RECT struct { + Left, Top, Right, Bottom _SHORT + } + _COORD struct { + X, Y _SHORT + } + _CONSOLE_SCREEN_BUFFER_INFO struct { + dwSize, dwCursorPosition _COORD + wAttributes _WORD + srWindow _SMALL_RECT + dwMaximumWindowSize _COORD + } +) + +// Low-level functions that call into Windows API for getting console info +var kernel32 = syscall.NewLazyDLL("kernel32.dll") +var kernel32_GetStdHandleProc = kernel32.NewProc("GetStdHandle") +var kernel32_GetConsoleScreenBufferInfoProc = kernel32.NewProc("GetConsoleScreenBufferInfo") + +func kernel32_GetStdHandle(nStdHandle int32) (syscall.Handle, error) { + res, _, err := kernel32_GetStdHandleProc.Call(uintptr(nStdHandle)) + if res == uintptr(syscall.InvalidHandle) { + return syscall.InvalidHandle, error(err) + } + return syscall.Handle(res), nil +} + +func kernel32_GetConsoleScreenBufferInfo(hConsoleOutput syscall.Handle, info *_CONSOLE_SCREEN_BUFFER_INFO) error { + ok, _, err := kernel32_GetConsoleScreenBufferInfoProc.Call(uintptr(hConsoleOutput), uintptr(unsafe.Pointer(info))) + if int(ok) == 0 { + return error(err) + } + return nil +} + +// windows api to get the console screen buffer info +func getConsoleScreenBufferInfo(csbi *_CONSOLE_SCREEN_BUFFER_INFO) (err error) { + var ( + bi _CONSOLE_SCREEN_BUFFER_INFO + fd syscall.Handle + ) + + // Re-open CONOUT$ as in some instances, stdout may be closed and guaranteed an stdout + if fd, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0); err != nil { + return err + } + defer syscall.Close(fd) + + // grab the dimensions for the console + if err = kernel32_GetConsoleScreenBufferInfo(fd, &bi); err != nil { + return err + } + + *csbi = bi + return nil +} + +func platformGetTerminalDimensions() (width, height int, err error) { + var csbi _CONSOLE_SCREEN_BUFFER_INFO + + if err = getConsoleScreenBufferInfo(&csbi); err != nil { + return 0, 0, err + } + + return int(csbi.dwSize.X), int(csbi.dwSize.Y), nil +} diff --git a/packer/rpc/ui.go b/packer/rpc/ui.go index 80508be82..1c6356a65 100644 --- a/packer/rpc/ui.go +++ b/packer/rpc/ui.go @@ -60,13 +60,6 @@ func (u *Ui) Say(message string) { } } -func (u *Ui) GetProgressBar() (result packer.ProgressBar) { - if err := u.client.Call("Ui.GetProgressBar", nil, &result); err != nil { - log.Printf("Error in Ui RPC call: %s", err) - } - return -} - func (u *UiServer) Ask(query string, reply *string) (err error) { *reply, err = u.ui.Ask(query) return @@ -98,9 +91,3 @@ func (u *UiServer) Say(message *string, reply *interface{}) error { *reply = nil return nil } - -func (u *UiServer) GetProgressBar(noargs *interface{}, reply *packer.ProgressBar) error { - res := u.ui.GetProgressBar() - *reply = res - return nil -} diff --git a/packer/rpc/ui_test.go b/packer/rpc/ui_test.go index eef4be13c..4dd2d7036 100644 --- a/packer/rpc/ui_test.go +++ b/packer/rpc/ui_test.go @@ -1,26 +1,22 @@ package rpc import ( - "github.com/hashicorp/packer/packer" "reflect" "testing" ) type testUi struct { - askCalled bool - askQuery string - errorCalled bool - errorMessage string - machineCalled bool - machineType string - machineArgs []string - messageCalled bool - messageMessage string - sayCalled bool - sayMessage string - getProgressBarCalled bool - getProgressBarValue packer.ProgressBar - progessBarCallbackWasCalled bool + askCalled bool + askQuery string + errorCalled bool + errorMessage string + machineCalled bool + machineType string + machineArgs []string + messageCalled bool + messageMessage string + sayCalled bool + sayMessage string } func (u *testUi) Ask(query string) (string, error) { @@ -50,15 +46,6 @@ func (u *testUi) Say(message string) { u.sayMessage = message } -func (u *testUi) GetProgressBar() packer.ProgressBar { - u.getProgressBarCalled = true - u.getProgressBarValue = packer.GetDummyProgressBar() - u.getProgressBarValue.Callback = func(string) { - u.progessBarCallbackWasCalled = true - } - return u.getProgressBarValue -} - func TestUiRPC(t *testing.T) { // Create the UI to test ui := new(testUi) @@ -106,19 +93,6 @@ func TestUiRPC(t *testing.T) { t.Fatal("machine should be called") } - bar := uiClient.GetProgressBar() - if !ui.getProgressBarCalled { - t.Fatal("getprogressbar should be called") - } - - if bar.Callback == nil { - t.Fatal("getprogressbar returned a bar with an empty callback") - } - bar.Callback("test") - if !ui.progessBarCallbackWasCalled { - t.Fatal("progressbarcallback should be called") - } - if ui.machineType != "foo" { t.Fatalf("bad type: %#v", ui.machineType) } diff --git a/packer/ui.go b/packer/ui.go index cd88a1eab..c16a2eae0 100644 --- a/packer/ui.go +++ b/packer/ui.go @@ -15,8 +15,6 @@ import ( "syscall" "time" "unicode" - - "github.com/cheggaaa/pb" ) type UiColor uint @@ -30,15 +28,6 @@ const ( UiColorCyan = 36 ) -// The ProgressBar interface is used for abstracting cheggaaa's progress- -// bar, or any other progress bar. If a UI does not support a progress- -// bar, then it must return a null progress bar. -const ( - DefaultProgressBarWidth = 80 -) - -type ProgressBar = *pb.ProgressBar - // The Ui interface handles all communication for Packer with the outside // world. This sort of control allows us to strictly control how output // is formatted and various levels of output. @@ -48,7 +37,6 @@ type Ui interface { Message(string) Error(string) Machine(string, ...string) - GetProgressBar() ProgressBar } // ColoredUi is a UI that is colored using terminal colors. @@ -144,11 +132,6 @@ func (u *ColoredUi) supportsColors() bool { return cygwin } -func (u *ColoredUi) GetProgressBar() ProgressBar { - log.Printf("ColoredUi: Using wrapped UI for progress bar.\n") - return u.Ui.GetProgressBar() -} - func (u *TargetedUI) Ask(query string) (string, error) { return u.Ui.Ask(u.prefixLines(true, query)) } @@ -185,11 +168,6 @@ func (u *TargetedUI) prefixLines(arrow bool, message string) string { return strings.TrimRightFunc(result.String(), unicode.IsSpace) } -func (u *TargetedUI) GetProgressBar() ProgressBar { - log.Printf("TargetedUI: Using wrapped UI for progress bar.\n") - return u.Ui.GetProgressBar() -} - func (rw *BasicUi) Ask(query string) (string, error) { rw.l.Lock() defer rw.l.Unlock() @@ -282,23 +260,6 @@ func (rw *BasicUi) Machine(t string, args ...string) { log.Printf("machine readable: %s %#v", t, args) } -func (rw *BasicUi) GetProgressBar() ProgressBar { - width := calculateProgressBarWidth(0) - - log.Printf("BasicUi: Using progress bar width: %d\n", width) - - bar := GetDefaultProgressBar() - bar.SetWidth(width) - bar.Callback = func(message string) { - rw.l.Lock() - defer rw.l.Unlock() - - // Discard the error here so we don't emit an error everytime the progress-bar fails to update - fmt.Fprint(rw.Writer, message) - } - return bar -} - func (u *MachineReadableUi) Ask(query string) (string, error) { return "", errors.New("machine-readable UI can't ask") } @@ -344,56 +305,3 @@ func (u *MachineReadableUi) Machine(category string, args ...string) { } } } - -func (u *MachineReadableUi) GetProgressBar() ProgressBar { - log.Printf("MachineReadableUi: Using dummy progress bar.\n") - bar := GetDummyProgressBar() - return bar -} - -// Return a dummy progress bar that doesn't do anything -func GetDummyProgressBar() *pb.ProgressBar { - return pb.New64(0) -} - -// Get a progress bar with the default appearance -func GetDefaultProgressBar() *pb.ProgressBar { - bar := pb.New64(0) - bar.ShowPercent = true - bar.ShowCounters = true - bar.ShowSpeed = false - bar.ShowBar = true - bar.ShowTimeLeft = false - bar.ShowFinalTime = false - bar.SetUnits(pb.U_BYTES) - bar.Format("[=>-]") - bar.SetRefreshRate(1 * time.Second) - return bar -} - -// Figure out the terminal dimensions and use it to calculate the available rendering space -func calculateProgressBarWidth(length int) int { - // If the UI's width is signed, then this is an interface that doesn't really benefit from a progress bar - if length < 0 { - log.Println("Refusing to render progress-bar for unsupported UI.") - return length - } - - // Figure out the terminal width if possible - width, _, err := GetTerminalDimensions() - if err != nil { - newerr := fmt.Errorf("Unable to determine terminal dimensions: %v", err) - log.Printf("Using default width (%d) for progress-bar due to error: %s", DefaultProgressBarWidth, newerr) - return DefaultProgressBarWidth - } - - // If the terminal width is smaller than the requested length, then complain - if width < length { - newerr := fmt.Errorf("Terminal width (%d) is smaller than UI message width (%d).", width, length) - log.Printf("Using default width (%d) for progress-bar due to error: %s", DefaultProgressBarWidth, newerr) - return DefaultProgressBarWidth - } - - // Otherwise subtract the minimum length and return it - return width - length -} diff --git a/packer/ui_windows.go b/packer/ui_windows.go deleted file mode 100644 index ce99c3a35..000000000 --- a/packer/ui_windows.go +++ /dev/null @@ -1,82 +0,0 @@ -// +build windows - -package packer - -import ( - "syscall" - "unsafe" -) - -// windows constants and structures pulled from msdn -const ( - STD_INPUT_HANDLE = -10 - STD_OUTPUT_HANDLE = -11 - STD_ERROR_HANDLE = -12 -) - -type ( - SHORT int16 - WORD uint16 - - SMALL_RECT struct { - Left, Top, Right, Bottom SHORT - } - COORD struct { - X, Y SHORT - } - CONSOLE_SCREEN_BUFFER_INFO struct { - dwSize, dwCursorPosition COORD - wAttributes WORD - srWindow SMALL_RECT - dwMaximumWindowSize COORD - } -) - -// Low-level functions that call into Windows API for getting console info -var KERNEL32 = syscall.NewLazyDLL("kernel32.dll") -var KERNEL32_GetStdHandleProc = KERNEL32.NewProc("GetStdHandle") -var KERNEL32_GetConsoleScreenBufferInfoProc = KERNEL32.NewProc("GetConsoleScreenBufferInfo") - -func KERNEL32_GetStdHandle(nStdHandle int32) (syscall.Handle, error) { - res, _, err := KERNEL32_GetStdHandleProc.Call(uintptr(nStdHandle)) - if res == uintptr(syscall.InvalidHandle) { - return syscall.InvalidHandle, error(err) - } - return syscall.Handle(res), nil -} - -func KERNEL32_GetConsoleScreenBufferInfo(hConsoleOutput syscall.Handle, info *CONSOLE_SCREEN_BUFFER_INFO) error { - ok, _, err := KERNEL32_GetConsoleScreenBufferInfoProc.Call(uintptr(hConsoleOutput), uintptr(unsafe.Pointer(info))) - if int(ok) == 0 { - return error(err) - } - return nil -} - -// windows api -func GetTerminalDimensions() (width, height int, err error) { - var ( - fd syscall.Handle - csbi CONSOLE_SCREEN_BUFFER_INFO - ) - - // grab the handle for stdout - /* - if fd, err = KERNEL32_GetStdHandle(STD_OUTPUT_HANDLE); err != nil { - return 0, 0, err - } - */ - - if fd, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0); err != nil { - return 0, 0, err - } - defer syscall.Close(fd) - - // grab the dimensions for the console - if err = KERNEL32_GetConsoleScreenBufferInfo(fd, &csbi); err != nil { - return 0, 0, err - } - - // whee... - return int(csbi.dwSize.X), int(csbi.dwSize.Y), nil -} diff --git a/provisioner/ansible-local/ui_stub.go b/provisioner/ansible-local/ui_stub.go index bc2e40628..4faa2a215 100644 --- a/provisioner/ansible-local/ui_stub.go +++ b/provisioner/ansible-local/ui_stub.go @@ -1,7 +1,5 @@ package ansiblelocal -import "github.com/hashicorp/packer/packer" - type uiStub struct{} func (su *uiStub) Ask(string) (string, error) { @@ -15,6 +13,3 @@ func (su *uiStub) Machine(string, ...string) {} func (su *uiStub) Message(string) {} func (su *uiStub) Say(msg string) {} -func (su *uiStub) GetProcessBar() packer.ProgressBar { - return packer.GetDummyProgressBar() -} diff --git a/provisioner/ansible/adapter_test.go b/provisioner/ansible/adapter_test.go index 92e669a97..2ab2d350b 100644 --- a/provisioner/ansible/adapter_test.go +++ b/provisioner/ansible/adapter_test.go @@ -123,10 +123,6 @@ func (u *ui) Machine(s1 string, s2 ...string) { } } -func (u *ui) GetProgressBar() packer.ProgressBar { - return packer.GetDummyProgressBar() -} - type communicator struct{} func (c communicator) Start(*packer.RemoteCmd) error { diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 9cea3e435..99c4d07d9 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -599,7 +599,3 @@ func (ui *Ui) Machine(t string, args ...string) { ui.ui.Machine(t, args...) <-ui.sem } - -func (ui *Ui) GetProgressBar() packer.ProgressBar { - return packer.GetDummyProgressBar() -} diff --git a/provisioner/file/provisioner.go b/provisioner/file/provisioner.go index 616625a25..bda35bf31 100644 --- a/provisioner/file/provisioner.go +++ b/provisioner/file/provisioner.go @@ -127,7 +127,7 @@ func (p *Provisioner) ProvisionDownload(ui packer.Ui, comm packer.Communicator) defer f.Close() // Get a default progress bar - pb := ui.GetProgressBar() + pb := common.GetProgressBar(ui) bar := pb.Start() defer bar.Finish() @@ -176,7 +176,7 @@ func (p *Provisioner) ProvisionUpload(ui packer.Ui, comm packer.Communicator) er } // Get a default progress bar - pb := ui.GetProgressBar() + pb := common.GetProgressBar(ui) bar := pb.Start() defer bar.Finish() diff --git a/provisioner/file/provisioner_test.go b/provisioner/file/provisioner_test.go index b804f0e32..175082aae 100644 --- a/provisioner/file/provisioner_test.go +++ b/provisioner/file/provisioner_test.go @@ -120,10 +120,6 @@ func (su *stubUi) Say(msg string) { su.sayMessages += msg } -func (su *stubUi) GetProgressBar() packer.ProgressBar { - return packer.GetDummyProgressBar() -} - func TestProvisionerProvision_SendsFile(t *testing.T) { var p Provisioner tf, err := ioutil.TempFile("", "packer")