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 ( import (
"bufio" "bufio"
"fmt" "fmt"
"io"
"os" "os"
"strings" "strings"
"github.com/hashicorp/terraform/helper/wrappedreadline" "github.com/hashicorp/terraform/helper/wrappedstreams"
"github.com/hashicorp/terraform/repl" "github.com/hashicorp/terraform/repl"
"github.com/chzyer/readline"
"github.com/mitchellh/cli" "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 // Setup the UI so we can output directly to stdout
ui := &cli.BasicUi{ ui := &cli.BasicUi{
Writer: c.Stdout(), Writer: wrappedstreams.Stdout(),
ErrorWriter: c.Stderr(), ErrorWriter: wrappedstreams.Stderr(),
} }
// IO Loop // IO Loop
@ -82,7 +80,7 @@ func (c *ConsoleCommand) Run(args []string) int {
func (c *ConsoleCommand) modePiped(session *repl.Session, ui cli.Ui) int { func (c *ConsoleCommand) modePiped(session *repl.Session, ui cli.Ui) int {
var lastResult string var lastResult string
scanner := bufio.NewScanner(c.Stdin()) scanner := bufio.NewScanner(wrappedstreams.Stdin())
for scanner.Scan() { for scanner.Scan() {
// Handle it. If there is an error exit immediately // Handle it. If there is an error exit immediately
result, err := session.Handle(strings.TrimSpace(scanner.Text())) 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 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 { func (c *ConsoleCommand) Help() string {
helpText := ` helpText := `
Usage: terraform console [options] [DIR] 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"
"github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/helper/experiment" "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/state"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
"github.com/mitchellh/colorstring" "github.com/mitchellh/colorstring"
"github.com/mitchellh/panicwrap"
) )
// Meta are the meta-options that are available on all or most commands. // 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. // StdinPiped returns true if the input is piped.
func (m *Meta) StdinPiped() bool { func (m *Meta) StdinPiped() bool {
fi, err := m.Stdin().Stat() fi, err := wrappedstreams.Stdin().Stat()
if err != nil { if err != nil {
// If there is an error, let's just say its not piped // If there is an error, let's just say its not piped
return false return false
@ -323,36 +322,6 @@ func (m *Meta) StdinPiped() bool {
return fi.Mode()&os.ModeNamedPipe != 0 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 // contextOpts returns the options to use to initialize a Terraform
// context with the settings from this Meta. // context with the settings from this Meta.
func (m *Meta) contextOpts() *terraform.ContextOpts { func (m *Meta) contextOpts() *terraform.ContextOpts {

View File

@ -10,38 +10,24 @@
package wrappedreadline package wrappedreadline
import ( import (
"os"
"runtime" "runtime"
"github.com/chzyer/readline" "github.com/chzyer/readline"
)
// These are the file descriptor numbers for the original stdin, stdout, stderr "github.com/hashicorp/terraform/helper/wrappedstreams"
// 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")
) )
// Override overrides the values in readline.Config that need to be // Override overrides the values in readline.Config that need to be
// set with wrapped values. // set with wrapped values.
func Override(cfg *readline.Config) *readline.Config { func Override(cfg *readline.Config) *readline.Config {
cfg.Stdin = Stdin cfg.Stdin = wrappedstreams.Stdin()
cfg.Stdout = Stdout cfg.Stdout = wrappedstreams.Stdout()
cfg.Stderr = Stderr cfg.Stderr = wrappedstreams.Stderr()
cfg.FuncGetWidth = TerminalWidth cfg.FuncGetWidth = TerminalWidth
cfg.FuncIsTerminal = IsTerminal cfg.FuncIsTerminal = IsTerminal
var rm RawMode rm := RawMode{StdinFd: int(wrappedstreams.Stdin().Fd())}
cfg.FuncMakeRaw = rm.Enter cfg.FuncMakeRaw = rm.Enter
cfg.FuncExitRaw = rm.Exit cfg.FuncExitRaw = rm.Exit
@ -56,7 +42,9 @@ func IsTerminal() bool {
} }
// Same implementation as readline but with our custom fds // 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. // TerminalWidth gets the terminal width in characters.
@ -70,11 +58,13 @@ func TerminalWidth() int {
// RawMode is a helper for entering and exiting raw mode. // RawMode is a helper for entering and exiting raw mode.
type RawMode struct { type RawMode struct {
StdinFd int
state *readline.State state *readline.State
} }
func (r *RawMode) Enter() (err error) { func (r *RawMode) Enter() (err error) {
r.state, err = readline.MakeRaw(StdinFd) r.state, err = readline.MakeRaw(r.StdinFd)
return err return err
} }
@ -83,5 +73,5 @@ func (r *RawMode) Exit() error {
return nil return nil
} }
return readline.Restore(StdinFd, r.state) return readline.Restore(r.StdinFd, r.state)
} }

View File

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