command: split out and tag code so compilation works on Solaris

The readline library doesn't support Solaris. For now, we'll just not
support console there.
This commit is contained in:
Mitchell Hashimoto 2016-11-14 00:32:01 -08:00
parent 0232b39db6
commit 1a6056b287
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
7 changed files with 151 additions and 107 deletions

View File

@ -3,14 +3,12 @@ package command
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"github.com/hashicorp/terraform/helper/wrappedreadline"
"github.com/hashicorp/terraform/helper/wrappedstreams"
"github.com/hashicorp/terraform/repl"
"github.com/chzyer/readline"
"github.com/mitchellh/cli"
)
@ -63,8 +61,8 @@ func (c *ConsoleCommand) Run(args []string) int {
// Setup the UI so we can output directly to stdout
ui := &cli.BasicUi{
Writer: c.Stdout(),
ErrorWriter: c.Stderr(),
Writer: wrappedstreams.Stdout(),
ErrorWriter: wrappedstreams.Stderr(),
}
// IO Loop
@ -82,7 +80,7 @@ func (c *ConsoleCommand) Run(args []string) int {
func (c *ConsoleCommand) modePiped(session *repl.Session, ui cli.Ui) int {
var lastResult string
scanner := bufio.NewScanner(c.Stdin())
scanner := bufio.NewScanner(wrappedstreams.Stdin())
for scanner.Scan() {
// Handle it. If there is an error exit immediately
result, err := session.Handle(strings.TrimSpace(scanner.Text()))
@ -101,50 +99,6 @@ func (c *ConsoleCommand) modePiped(session *repl.Session, ui cli.Ui) int {
return 0
}
func (c *ConsoleCommand) modeInteractive(session *repl.Session, ui cli.Ui) int {
// Configure input
l, err := readline.NewEx(wrappedreadline.Override(&readline.Config{
Prompt: "> ",
InterruptPrompt: "^C",
EOFPrompt: "exit",
HistorySearchFold: true,
}))
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing console: %s",
err))
return 1
}
defer l.Close()
for {
// Read a line
line, err := l.Readline()
if err == readline.ErrInterrupt {
if len(line) == 0 {
break
} else {
continue
}
} else if err == io.EOF {
break
}
out, err := session.Handle(line)
if err == repl.ErrSessionExit {
break
}
if err != nil {
ui.Error(err.Error())
continue
}
ui.Output(out)
}
return 0
}
func (c *ConsoleCommand) Help() string {
helpText := `
Usage: terraform console [options] [DIR]

View File

@ -0,0 +1,61 @@
// +build !solaris
// The readline library we use doesn't currently support solaris so
// we just build tag it off.
package command
import (
"fmt"
"io"
"github.com/hashicorp/terraform/helper/wrappedreadline"
"github.com/hashicorp/terraform/repl"
"github.com/chzyer/readline"
"github.com/mitchellh/cli"
)
func (c *ConsoleCommand) modeInteractive(session *repl.Session, ui cli.Ui) int {
// Configure input
l, err := readline.NewEx(wrappedreadline.Override(&readline.Config{
Prompt: "> ",
InterruptPrompt: "^C",
EOFPrompt: "exit",
HistorySearchFold: true,
}))
if err != nil {
c.Ui.Error(fmt.Sprintf(
"Error initializing console: %s",
err))
return 1
}
defer l.Close()
for {
// Read a line
line, err := l.Readline()
if err == readline.ErrInterrupt {
if len(line) == 0 {
break
} else {
continue
}
} else if err == io.EOF {
break
}
out, err := session.Handle(line)
if err == repl.ErrSessionExit {
break
}
if err != nil {
ui.Error(err.Error())
continue
}
ui.Output(out)
}
return 0
}

View File

@ -0,0 +1,18 @@
// +build solaris
package command
import (
"fmt"
"github.com/hashicorp/terraform/repl"
"github.com/mitchellh/cli"
)
func (c *ConsoleCommand) modeInteractive(session *repl.Session, ui cli.Ui) int {
ui.Error(fmt.Sprintf(
"The readline library Terraform currently uses for the interactive\n" +
"console is not supported by Solaris. Interactive mode is therefore\n" +
"not supported on Solaris currently."))
return 1
}

View File

@ -15,12 +15,11 @@ import (
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/helper/experiment"
"github.com/hashicorp/terraform/helper/wrappedreadline"
"github.com/hashicorp/terraform/helper/wrappedstreams"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli"
"github.com/mitchellh/colorstring"
"github.com/mitchellh/panicwrap"
)
// Meta are the meta-options that are available on all or most commands.
@ -314,7 +313,7 @@ func (m *Meta) Input() bool {
// StdinPiped returns true if the input is piped.
func (m *Meta) StdinPiped() bool {
fi, err := m.Stdin().Stat()
fi, err := wrappedstreams.Stdin().Stat()
if err != nil {
// If there is an error, let's just say its not piped
return false
@ -323,36 +322,6 @@ func (m *Meta) StdinPiped() bool {
return fi.Mode()&os.ModeNamedPipe != 0
}
// Stdin returns the stdin for this command.
func (m *Meta) Stdin() *os.File {
stdin := os.Stdin
if panicwrap.Wrapped(nil) {
stdin = wrappedreadline.Stdin
}
return stdin
}
// Stdout returns the stdout for this command.
func (m *Meta) Stdout() *os.File {
stdout := os.Stdout
if panicwrap.Wrapped(nil) {
stdout = wrappedreadline.Stdout
}
return stdout
}
// Stderr returns the stderr for this command.
func (m *Meta) Stderr() *os.File {
stderr := os.Stderr
if panicwrap.Wrapped(nil) {
stderr = wrappedreadline.Stderr
}
return stderr
}
// contextOpts returns the options to use to initialize a Terraform
// context with the settings from this Meta.
func (m *Meta) contextOpts() *terraform.ContextOpts {

View File

@ -10,38 +10,24 @@
package wrappedreadline
import (
"os"
"runtime"
"github.com/chzyer/readline"
)
// These are the file descriptor numbers for the original stdin, stdout, stderr
// streams from the parent process.
const (
StdinFd = 3
StdoutFd = 4
StderrFd = 5
)
// These are the *os.File values for the standard streams.
var (
Stdin = os.NewFile(uintptr(StdinFd), "stdin")
Stdout = os.NewFile(uintptr(StdoutFd), "stdout")
Stderr = os.NewFile(uintptr(StderrFd), "stderr")
"github.com/hashicorp/terraform/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
var rm RawMode
rm := RawMode{StdinFd: int(wrappedstreams.Stdin().Fd())}
cfg.FuncMakeRaw = rm.Enter
cfg.FuncExitRaw = rm.Exit
@ -56,7 +42,9 @@ func IsTerminal() bool {
}
// Same implementation as readline but with our custom fds
return readline.IsTerminal(StdinFd) && (readline.IsTerminal(StdoutFd) || readline.IsTerminal(StderrFd))
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.
@ -70,11 +58,13 @@ func TerminalWidth() int {
// RawMode is a helper for entering and exiting raw mode.
type RawMode struct {
StdinFd int
state *readline.State
}
func (r *RawMode) Enter() (err error) {
r.state, err = readline.MakeRaw(StdinFd)
r.state, err = readline.MakeRaw(r.StdinFd)
return err
}
@ -83,5 +73,5 @@ func (r *RawMode) Exit() error {
return nil
}
return readline.Restore(StdinFd, r.state)
return readline.Restore(r.StdinFd, r.state)
}

View File

@ -5,13 +5,18 @@ package wrappedreadline
import (
"syscall"
"unsafe"
"github.com/hashicorp/terraform/helper/wrappedstreams"
)
// getWidth impl for Unix
func getWidth() int {
w := getWidthFd(StdoutFd)
stdoutFd := int(wrappedstreams.Stdout().Fd())
stderrFd := int(wrappedstreams.Stderr().Fd())
w := getWidthFd(stdoutFd)
if w < 0 {
w = getWidthFd(StderrFd)
w = getWidthFd(stderrFd)
}
return w

View File

@ -0,0 +1,47 @@
// 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 := 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 streams. There doesn't appear to be a negative
// impact of opening these files even if the file descriptor doesn't exist.
var (
wrappedStdin = os.NewFile(uintptr(3), "stdin")
wrappedStdout = os.NewFile(uintptr(4), "stdout")
wrappedStderr = os.NewFile(uintptr(5), "stderr")
)