From 217dcbb97ff563a2f64647a5640df1a641ef2eea Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 9 Apr 2020 14:38:17 -0700 Subject: [PATCH] golang 1.14 doesn't like calling NewFile on existing files. Port solution over from Terraform --- command/console.go | 3 +- command/meta.go | 4 +- helper/wrappedreadline/wrappedreadline.go | 64 ++++--------------- .../wrappedreadline/wrappedreadline_unix.go | 14 ++-- .../wrappedreadline_windows.go | 49 -------------- helper/wrappedstreams/streams.go | 49 ++++++++++++++ helper/wrappedstreams/streams_other.go | 21 ++++++ helper/wrappedstreams/streams_windows.go | 52 +++++++++++++++ 8 files changed, 143 insertions(+), 113 deletions(-) create mode 100644 helper/wrappedstreams/streams.go create mode 100644 helper/wrappedstreams/streams_other.go create mode 100644 helper/wrappedstreams/streams_windows.go diff --git a/command/console.go b/command/console.go index 02faeb076..f63fc3211 100644 --- a/command/console.go +++ b/command/console.go @@ -9,6 +9,7 @@ import ( "github.com/chzyer/readline" "github.com/hashicorp/packer/helper/wrappedreadline" + "github.com/hashicorp/packer/helper/wrappedstreams" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template" "github.com/hashicorp/packer/template/interpolate" @@ -115,7 +116,7 @@ func (*ConsoleCommand) AutocompleteFlags() complete.Flags { func (c *ConsoleCommand) modePiped(session *REPLSession) int { var lastResult string - scanner := bufio.NewScanner(wrappedreadline.Stdin()) + scanner := bufio.NewScanner(wrappedstreams.Stdin()) for scanner.Scan() { result, err := session.Handle(strings.TrimSpace(scanner.Text())) if err != nil { diff --git a/command/meta.go b/command/meta.go index 72eb44c82..58d9a9359 100644 --- a/command/meta.go +++ b/command/meta.go @@ -9,7 +9,7 @@ import ( kvflag "github.com/hashicorp/packer/helper/flag-kv" sliceflag "github.com/hashicorp/packer/helper/flag-slice" - "github.com/hashicorp/packer/helper/wrappedreadline" + "github.com/hashicorp/packer/helper/wrappedstreams" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template" ) @@ -166,7 +166,7 @@ func (m *Meta) ValidateFlags() error { // StdinPiped returns true if the input is piped. func (m *Meta) StdinPiped() bool { - fi, err := wrappedreadline.Stdin().Stat() + fi, err := wrappedstreams.Stdin().Stat() if err != nil { // If there is an error, let's just say its not piped return false diff --git a/helper/wrappedreadline/wrappedreadline.go b/helper/wrappedreadline/wrappedreadline.go index 67cb80653..55972c82a 100644 --- a/helper/wrappedreadline/wrappedreadline.go +++ b/helper/wrappedreadline/wrappedreadline.go @@ -1,5 +1,7 @@ -// Shamelessly copied from the Terraform repo because it wasn't worth vendoring -// out two hundred lines of code so Packer could use it too. +// STOLEN SHAMELESSLY FROM THE TERRAFORM REPO BECAUSE VENDORING OUT +// WRAPPEDREADLINE AND WRAPPEDSTREAMS FELT LIKE TOO MUCH WORK. +// +// "a little copying is better than a lot of dependency" // // wrappedreadline is a package that has helpers for interacting with // readline from a panicwrap executable. @@ -13,24 +15,24 @@ package wrappedreadline import ( - "os" "runtime" "github.com/chzyer/readline" - "github.com/mitchellh/panicwrap" + + "github.com/hashicorp/packer/helper/wrappedstreams" ) // Override overrides the values in readline.Config that need to be // set with wrapped values. func Override(cfg *readline.Config) *readline.Config { - cfg.Stdin = Stdin() - cfg.Stdout = Stdout() - cfg.Stderr = Stderr() + cfg.Stdin = wrappedstreams.Stdin() + cfg.Stdout = wrappedstreams.Stdout() + cfg.Stderr = wrappedstreams.Stderr() cfg.FuncGetWidth = TerminalWidth cfg.FuncIsTerminal = IsTerminal - rm := RawMode{StdinFd: int(Stdin().Fd())} + rm := RawMode{StdinFd: int(wrappedstreams.Stdin().Fd())} cfg.FuncMakeRaw = rm.Enter cfg.FuncExitRaw = rm.Exit @@ -45,9 +47,9 @@ func IsTerminal() bool { } // Same implementation as readline but with our custom fds - return readline.IsTerminal(int(Stdin().Fd())) && - (readline.IsTerminal(int(Stdout().Fd())) || - readline.IsTerminal(int(Stderr().Fd()))) + return readline.IsTerminal(int(wrappedstreams.Stdin().Fd())) && + (readline.IsTerminal(int(wrappedstreams.Stdout().Fd())) || + readline.IsTerminal(int(wrappedstreams.Stderr().Fd()))) } // TerminalWidth gets the terminal width in characters. @@ -78,43 +80,3 @@ func (r *RawMode) Exit() error { return readline.Restore(r.StdinFd, r.state) } - -// Package provides access to the standard OS streams -// (stdin, stdout, stderr) even if wrapped under panicwrap. -// Stdin returns the true stdin of the process. -func Stdin() *os.File { - stdin := os.Stdin - if panicwrap.Wrapped(nil) { - stdin = wrappedStdin - } - - return stdin -} - -// Stdout returns the true stdout of the process. -func Stdout() *os.File { - stdout := os.Stdout - if panicwrap.Wrapped(nil) { - stdout = wrappedStdout - } - - return stdout -} - -// Stderr returns the true stderr of the process. -func Stderr() *os.File { - stderr := os.Stderr - if panicwrap.Wrapped(nil) { - stderr = wrappedStderr - } - - return stderr -} - -// These are the wrapped standard streams. These are setup by the -// platform specific code in initPlatform. -var ( - wrappedStdin *os.File - wrappedStdout *os.File - wrappedStderr *os.File -) diff --git a/helper/wrappedreadline/wrappedreadline_unix.go b/helper/wrappedreadline/wrappedreadline_unix.go index 8db87d1fa..5756b94b7 100644 --- a/helper/wrappedreadline/wrappedreadline_unix.go +++ b/helper/wrappedreadline/wrappedreadline_unix.go @@ -3,15 +3,16 @@ package wrappedreadline import ( - "os" "syscall" "unsafe" + + "github.com/hashicorp/packer/helper/wrappedstreams" ) // getWidth impl for Unix func getWidth() int { - stdoutFd := int(Stdout().Fd()) - stderrFd := int(Stderr().Fd()) + stdoutFd := int(wrappedstreams.Stdout().Fd()) + stderrFd := int(wrappedstreams.Stderr().Fd()) w := getWidthFd(stdoutFd) if w < 0 { @@ -43,10 +44,3 @@ func getWidthFd(stdoutFd int) int { return int(ws.Col) } - -func init() { - // The standard streams are passed in via extra file descriptors. - wrappedStdin = os.NewFile(uintptr(3), "stdin") - wrappedStdout = os.NewFile(uintptr(4), "stdout") - wrappedStderr = os.NewFile(uintptr(5), "stderr") -} diff --git a/helper/wrappedreadline/wrappedreadline_windows.go b/helper/wrappedreadline/wrappedreadline_windows.go index fc1793409..110ee5197 100644 --- a/helper/wrappedreadline/wrappedreadline_windows.go +++ b/helper/wrappedreadline/wrappedreadline_windows.go @@ -2,56 +2,7 @@ package wrappedreadline -import ( - "log" - "os" - "syscall" -) - // getWidth impl for other func getWidth() int { return 0 } - -func init() { - wrappedStdin = openConsole("CONIN$", os.Stdin) - wrappedStdout = openConsole("CONOUT$", os.Stdout) - wrappedStderr = wrappedStdout -} - -// openConsole opens a console handle, using a backup if it fails. -// This is used to get the exact console handle instead of the redirected -// handles from panicwrap. -func openConsole(name string, backup *os.File) *os.File { - // Convert to UTF16 - path, err := syscall.UTF16PtrFromString(name) - if err != nil { - log.Printf("[ERROR] wrappedstreams: %s", err) - return backup - } - - // Determine the share mode - var shareMode uint32 - switch name { - case "CONIN$": - shareMode = syscall.FILE_SHARE_READ - case "CONOUT$": - shareMode = syscall.FILE_SHARE_WRITE - } - - // Get the file - h, err := syscall.CreateFile( - path, - syscall.GENERIC_READ|syscall.GENERIC_WRITE, - shareMode, - nil, - syscall.OPEN_EXISTING, - 0, 0) - if err != nil { - log.Printf("[ERROR] wrappedstreams: %s", err) - return backup - } - - // Create the Go file - return os.NewFile(uintptr(h), name) -} diff --git a/helper/wrappedstreams/streams.go b/helper/wrappedstreams/streams.go new file mode 100644 index 000000000..b3a67c609 --- /dev/null +++ b/helper/wrappedstreams/streams.go @@ -0,0 +1,49 @@ +// STOLEN SHAMELESSLY FROM THE TERRAFORM REPO BECAUSE VENDORING OUT +// WRAPPEDREADLINE AND WRAPPEDSTREAMS FELT LIKE TOO MUCH WORK. +// +// "a little copying is better than a lot of dependency" +// +// Package wrappedstreams provides access to the standard OS streams +// (stdin, stdout, stderr) even if wrapped under panicwrap. +package wrappedstreams + +import ( + "os" + + "github.com/mitchellh/panicwrap" +) + +// Stdin returns the true stdin of the process. +func Stdin() *os.File { + stdin, _, _ := fds() + return stdin +} + +// Stdout returns the true stdout of the process. +func Stdout() *os.File { + _, stdout, _ := fds() + return stdout +} + +// Stderr returns the true stderr of the process. +func Stderr() *os.File { + _, _, stderr := fds() + return stderr +} + +func fds() (stdin, stdout, stderr *os.File) { + stdin, stdout, stderr = os.Stdin, os.Stdout, os.Stderr + if panicwrap.Wrapped(nil) { + initPlatform() + stdin, stdout, stderr = wrappedStdin, wrappedStdout, wrappedStderr + } + return +} + +// These are the wrapped standard streams. These are setup by the +// platform specific code in initPlatform. +var ( + wrappedStdin *os.File + wrappedStdout *os.File + wrappedStderr *os.File +) diff --git a/helper/wrappedstreams/streams_other.go b/helper/wrappedstreams/streams_other.go new file mode 100644 index 000000000..82f1e150c --- /dev/null +++ b/helper/wrappedstreams/streams_other.go @@ -0,0 +1,21 @@ +// +build !windows + +package wrappedstreams + +import ( + "os" + "sync" +) + +var initOnce sync.Once + +func initPlatform() { + // These must be initialized lazily, once it's been determined that this is + // a wrapped process. + initOnce.Do(func() { + // The standard streams are passed in via extra file descriptors. + wrappedStdin = os.NewFile(uintptr(3), "stdin") + wrappedStdout = os.NewFile(uintptr(4), "stdout") + wrappedStderr = os.NewFile(uintptr(5), "stderr") + }) +} diff --git a/helper/wrappedstreams/streams_windows.go b/helper/wrappedstreams/streams_windows.go new file mode 100644 index 000000000..2709cc0c6 --- /dev/null +++ b/helper/wrappedstreams/streams_windows.go @@ -0,0 +1,52 @@ +// +build windows + +package wrappedstreams + +import ( + "log" + "os" + "syscall" +) + +func initPlatform() { + wrappedStdin = openConsole("CONIN$", os.Stdin) + wrappedStdout = openConsole("CONOUT$", os.Stdout) + wrappedStderr = wrappedStdout +} + +// openConsole opens a console handle, using a backup if it fails. +// This is used to get the exact console handle instead of the redirected +// handles from panicwrap. +func openConsole(name string, backup *os.File) *os.File { + // Convert to UTF16 + path, err := syscall.UTF16PtrFromString(name) + if err != nil { + log.Printf("[ERROR] wrappedstreams: %s", err) + return backup + } + + // Determine the share mode + var shareMode uint32 + switch name { + case "CONIN$": + shareMode = syscall.FILE_SHARE_READ + case "CONOUT$": + shareMode = syscall.FILE_SHARE_WRITE + } + + // Get the file + h, err := syscall.CreateFile( + path, + syscall.GENERIC_READ|syscall.GENERIC_WRITE, + shareMode, + nil, + syscall.OPEN_EXISTING, + 0, 0) + if err != nil { + log.Printf("[ERROR] wrappedstreams: %s", err) + return backup + } + + // Create the Go file + return os.NewFile(uintptr(h), name) +}