command: UIInput implementation to ask via CLI
This commit is contained in:
parent
133a28e363
commit
a918833edd
|
@ -0,0 +1,82 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// UIInput is an implementation of terraform.UIInput that asks the CLI
|
||||
// for input stdin.
|
||||
type UIInput struct {
|
||||
// Reader and Writer for IO. If these aren't set, they will default to
|
||||
// Stdout and Stderr respectively.
|
||||
Reader io.Reader
|
||||
Writer io.Writer
|
||||
|
||||
interrupted bool
|
||||
l sync.Mutex
|
||||
}
|
||||
|
||||
func (i *UIInput) Input(opts *terraform.InputOpts) (string, error) {
|
||||
r := i.Reader
|
||||
w := i.Writer
|
||||
if r == nil {
|
||||
r = os.Stdin
|
||||
}
|
||||
if w == nil {
|
||||
w = os.Stdout
|
||||
}
|
||||
|
||||
// Make sure we only ask for input once at a time. Terraform
|
||||
// should enforce this, but it doesn't hurt to verify.
|
||||
i.l.Lock()
|
||||
defer i.l.Unlock()
|
||||
|
||||
// If we're interrupted, then don't ask for input
|
||||
if i.interrupted {
|
||||
return "", errors.New("interrupted")
|
||||
}
|
||||
|
||||
// Listen for interrupts so we can cancel the input ask
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, os.Interrupt)
|
||||
defer signal.Stop(sigCh)
|
||||
|
||||
// Ask the user for their input
|
||||
if _, err := fmt.Fprint(w, opts.Query); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Listen for the input in a goroutine. This will allow us to
|
||||
// interrupt this if we are interrupted (SIGINT)
|
||||
result := make(chan string, 1)
|
||||
go func() {
|
||||
var line string
|
||||
if _, err := fmt.Fscanln(r, &line); err != nil {
|
||||
log.Printf("[ERR] UIInput scan err: %s", err)
|
||||
}
|
||||
|
||||
result <- line
|
||||
}()
|
||||
|
||||
select {
|
||||
case line := <-result:
|
||||
return line, nil
|
||||
case <-sigCh:
|
||||
// Print a newline so that any further output starts properly
|
||||
// on a new line.
|
||||
fmt.Fprintln(w)
|
||||
|
||||
// Mark that we were interrupted so future Ask calls fail.
|
||||
i.interrupted = true
|
||||
|
||||
return "", errors.New("interrupted")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestUIInput_impl(t *testing.T) {
|
||||
var _ terraform.UIInput = new(UIInput)
|
||||
}
|
||||
|
||||
func TestUIInputInput(t *testing.T) {
|
||||
i := &UIInput{
|
||||
Reader: bytes.NewBufferString("foo\n"),
|
||||
Writer: bytes.NewBuffer(nil),
|
||||
}
|
||||
|
||||
v, err := i.Input(&terraform.InputOpts{})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if v != "foo" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue