Skip to content

Commit a918833

Browse files
committed
command: UIInput implementation to ask via CLI
1 parent 133a28e commit a918833

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

‎command/ui_input.go‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package command
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io"
7+
"log"
8+
"os"
9+
"os/signal"
10+
"sync"
11+
12+
"github.com/hashicorp/terraform/terraform"
13+
)
14+
15+
// UIInput is an implementation of terraform.UIInput that asks the CLI
16+
// for input stdin.
17+
type UIInput struct {
18+
// Reader and Writer for IO. If these aren't set, they will default to
19+
// Stdout and Stderr respectively.
20+
Reader io.Reader
21+
Writer io.Writer
22+
23+
interrupted bool
24+
l sync.Mutex
25+
}
26+
27+
func (i *UIInput) Input(opts *terraform.InputOpts) (string, error) {
28+
r := i.Reader
29+
w := i.Writer
30+
if r == nil {
31+
r = os.Stdin
32+
}
33+
if w == nil {
34+
w = os.Stdout
35+
}
36+
37+
// Make sure we only ask for input once at a time. Terraform
38+
// should enforce this, but it doesn't hurt to verify.
39+
i.l.Lock()
40+
defer i.l.Unlock()
41+
42+
// If we're interrupted, then don't ask for input
43+
if i.interrupted {
44+
return "", errors.New("interrupted")
45+
}
46+
47+
// Listen for interrupts so we can cancel the input ask
48+
sigCh := make(chan os.Signal, 1)
49+
signal.Notify(sigCh, os.Interrupt)
50+
defer signal.Stop(sigCh)
51+
52+
// Ask the user for their input
53+
if _, err := fmt.Fprint(w, opts.Query); err != nil {
54+
return "", err
55+
}
56+
57+
// Listen for the input in a goroutine. This will allow us to
58+
// interrupt this if we are interrupted (SIGINT)
59+
result := make(chan string, 1)
60+
go func() {
61+
var line string
62+
if _, err := fmt.Fscanln(r, &line); err != nil {
63+
log.Printf("[ERR] UIInput scan err: %s", err)
64+
}
65+
66+
result <- line
67+
}()
68+
69+
select {
70+
case line := <-result:
71+
return line, nil
72+
case <-sigCh:
73+
// Print a newline so that any further output starts properly
74+
// on a new line.
75+
fmt.Fprintln(w)
76+
77+
// Mark that we were interrupted so future Ask calls fail.
78+
i.interrupted = true
79+
80+
return "", errors.New("interrupted")
81+
}
82+
}

‎command/ui_input_test.go‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package command
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform/terraform"
8+
)
9+
10+
func TestUIInput_impl(t *testing.T) {
11+
var _ terraform.UIInput = new(UIInput)
12+
}
13+
14+
func TestUIInputInput(t *testing.T) {
15+
i := &UIInput{
16+
Reader: bytes.NewBufferString("foo\n"),
17+
Writer: bytes.NewBuffer(nil),
18+
}
19+
20+
v, err := i.Input(&terraform.InputOpts{})
21+
if err != nil {
22+
t.Fatalf("err: %s", err)
23+
}
24+
25+
if v != "foo" {
26+
t.Fatalf("bad: %#v", v)
27+
}
28+
}

0 commit comments

Comments
 (0)