Skip to content
This repository was archived by the owner on Feb 16, 2023. It is now read-only.

Commit dc6c3a6

Browse files
committed
Merge branch 'develop' into feature/ci-verify-build
2 parents 5fffecf + 5434d0c commit dc6c3a6

8 files changed

Lines changed: 76 additions & 83 deletions

File tree

internals/cli/ui/ask.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ func AskSecret(io IO, question string) (string, error) {
6262
return "", err
6363
}
6464

65-
secret, err := readPassword(promptIn)
65+
raw, err := promptIn.ReadPassword()
6666
if err != nil {
6767
return "", ErrReadInput(err)
6868
}
6969

7070
fmt.Fprintln(promptOut, "")
7171

72-
return secret, nil
72+
return string(raw), nil
7373
}
7474

7575
// AskMultiline prints out the question and reads back the input until an EOF is reached.

internals/cli/ui/io.go

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,35 @@ var (
1818

1919
// IO is an interface to work with input/output.
2020
type IO interface {
21-
Stdin() io.Reader
22-
Stdout() io.Writer
23-
Prompts() (io.Reader, io.Writer, error)
24-
IsStdinPiped() bool
25-
IsStdoutPiped() bool
21+
Stdin() Reader
22+
Stdout() Writer
23+
Prompts() (Reader, Writer, error)
2624
}
2725

2826
// UserIO is a middleware between input and output to the CLI program.
2927
// It implements userIO.Prompter and can be passed to libraries.
3028
type UserIO struct {
31-
Input *os.File
32-
Output *os.File
33-
tty *os.File
29+
Input Reader
30+
Output Writer
31+
tty file
3432
ttyAvailable bool
3533
}
3634

3735
// NewStdUserIO creates a new UserIO middleware only from os.Stdin and os.Stdout.
3836
func NewStdUserIO() UserIO {
3937
return UserIO{
40-
Input: os.Stdin,
41-
Output: os.Stdout,
38+
Input: file{os.Stdin},
39+
Output: file{os.Stdout},
4240
}
4341
}
4442

4543
// Stdin returns the UserIO's Input.
46-
func (o UserIO) Stdin() io.Reader {
44+
func (o UserIO) Stdin() Reader {
4745
return o.Input
4846
}
4947

5048
// Stdout returns the UserIO's Output.
51-
func (o UserIO) Stdout() io.Writer {
49+
func (o UserIO) Stdout() Writer {
5250
return o.Output
5351
}
5452

@@ -57,8 +55,8 @@ func (o UserIO) Stdout() io.Writer {
5755
// bypass stdin and stdout by connecting to /dev/tty on Unix systems when
5856
// available. On systems where tty is not available and when either input
5957
// or output is piped, prompting is not possible so an error is returned.
60-
func (o UserIO) Prompts() (io.Reader, io.Writer, error) {
61-
if o.IsStdoutPiped() || o.IsStdinPiped() {
58+
func (o UserIO) Prompts() (Reader, Writer, error) {
59+
if o.Input.IsPiped() || o.Output.IsPiped() {
6260
if o.ttyAvailable {
6361
return o.tty, o.tty, nil
6462
}
@@ -67,30 +65,12 @@ func (o UserIO) Prompts() (io.Reader, io.Writer, error) {
6765
return o.Input, o.Output, nil
6866
}
6967

70-
func (o UserIO) IsStdinPiped() bool {
71-
return isPiped(o.Input)
72-
}
73-
74-
func (o UserIO) IsStdoutPiped() bool {
75-
return isPiped(o.Output)
76-
}
77-
78-
// readPassword reads one line of input from the terminal without echoing the user input.
79-
func readPassword(r io.Reader) (string, error) {
80-
file, ok := r.(*os.File)
81-
if !ok {
82-
return "", ErrCannotAsk
83-
}
84-
// this case happens among other things when input is piped and ReadPassword is called.
85-
if !terminal.IsTerminal(int(file.Fd())) {
86-
return "", ErrCannotAsk
87-
}
88-
89-
password, err := terminal.ReadPassword(int(file.Fd()))
90-
if err != nil {
91-
return "", err
92-
}
93-
return string(password), nil
68+
// Reader can read input for a CLI program.
69+
type Reader interface {
70+
io.Reader
71+
// ReadPassword reads a line of input from a terminal without local echo.
72+
ReadPassword() ([]byte, error)
73+
IsPiped() bool
9474
}
9575

9676
// Readln reads 1 line of input from a io.Reader. The newline character is not included in the response.
@@ -104,10 +84,31 @@ func Readln(r io.Reader) (string, error) {
10484
return s.Text(), nil
10585
}
10686

107-
// isPiped checks whether the file is a pipe.
87+
// Writer can write output for a CLI program.
88+
type Writer interface {
89+
io.Writer
90+
IsPiped() bool
91+
}
92+
93+
// file implements the Reader and Writer interface.
94+
type file struct {
95+
*os.File
96+
}
97+
98+
// ReadPassword reads from a terminal without echoing back the typed input.
99+
func (f file) ReadPassword() ([]byte, error) {
100+
// this case happens among other things when input is piped and ReadPassword is called.
101+
if !terminal.IsTerminal(int(f.Fd())) {
102+
return nil, ErrCannotAsk
103+
}
104+
105+
return terminal.ReadPassword(int(f.Fd()))
106+
}
107+
108+
// IsPiped checks whether the file is a pipe.
108109
// If the file does not exist, it returns false.
109-
func isPiped(file *os.File) bool {
110-
stat, err := file.Stat()
110+
func (f file) IsPiped() bool {
111+
stat, err := f.Stat()
111112
if err != nil {
112113
return false
113114
}

internals/cli/ui/io_unix.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ func NewUserIO() UserIO {
99
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
1010
if err == nil {
1111
return UserIO{
12-
Input: os.Stdin,
13-
Output: os.Stdout,
14-
tty: tty,
12+
Input: file{os.Stdin},
13+
Output: file{os.Stdout},
14+
tty: file{tty},
1515
ttyAvailable: true,
1616
}
1717
}

internals/cli/ui/testing.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package ui
55
import (
66
"bytes"
77
"errors"
8-
"io"
98
)
109

1110
// FakeIO is a helper type for testing that implements the ui.IO interface
@@ -36,28 +35,20 @@ func NewFakeIO() *FakeIO {
3635
}
3736

3837
// Stdin returns the mocked StdIn.
39-
func (f *FakeIO) Stdin() io.Reader {
38+
func (f *FakeIO) Stdin() Reader {
4039
return f.StdIn
4140
}
4241

4342
// Stdout returns the mocked StdOut.
44-
func (f *FakeIO) Stdout() io.Writer {
43+
func (f *FakeIO) Stdout() Writer {
4544
return f.StdOut
4645
}
4746

4847
// Prompts returns the mocked prompts and error.
49-
func (f *FakeIO) Prompts() (io.Reader, io.Writer, error) {
48+
func (f *FakeIO) Prompts() (Reader, Writer, error) {
5049
return f.PromptIn, f.PromptOut, f.PromptErr
5150
}
5251

53-
func (f *FakeIO) IsStdinPiped() bool {
54-
return f.StdIn.Piped
55-
}
56-
57-
func (f *FakeIO) IsStdoutPiped() bool {
58-
return f.StdOut.Piped
59-
}
60-
6152
// FakeReader implements the Reader interface.
6253
type FakeReader struct {
6354
*bytes.Buffer
@@ -67,6 +58,20 @@ type FakeReader struct {
6758
ReadErr error
6859
}
6960

61+
// ReadPassword reads a line from the mocked buffer.
62+
func (f *FakeReader) ReadPassword() ([]byte, error) {
63+
pass, err := Readln(f)
64+
if err != nil {
65+
return nil, err
66+
}
67+
return []byte(pass), nil
68+
}
69+
70+
// IsPiped returns the mocked Piped.
71+
func (f *FakeReader) IsPiped() bool {
72+
return f.Piped
73+
}
74+
7075
// Read returns the mocked ReadErr or reads from the mocked buffer.
7176
func (f *FakeReader) Read(p []byte) (n int, err error) {
7277
if f.ReadErr != nil {
@@ -87,3 +92,8 @@ type FakeWriter struct {
8792
*bytes.Buffer
8893
Piped bool
8994
}
95+
96+
// IsPiped returns the mocked Piped.
97+
func (f *FakeWriter) IsPiped() bool {
98+
return f.Piped
99+
}

internals/secrethub/inject.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (cmd *InjectCommand) Run() error {
9191
return ErrReadFile(cmd.inFile, err)
9292
}
9393
} else {
94-
if !cmd.io.IsStdinPiped() {
94+
if !cmd.io.Stdin().IsPiped() {
9595
return ErrNoDataOnStdin
9696
}
9797

@@ -139,7 +139,7 @@ func (cmd *InjectCommand) Run() error {
139139
} else if cmd.outFile != "" {
140140
_, err := os.Stat(cmd.outFile)
141141
if err == nil && !cmd.force {
142-
if cmd.io.IsStdoutPiped() {
142+
if cmd.io.Stdout().IsPiped() {
143143
return ErrFileAlreadyExists
144144
}
145145

internals/secrethub/service_deploy_winrm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func (cmd *ServiceDeployWinRmCommand) Run() error {
179179

180180
deployer := newWindowsDeployer(client, destinationPath)
181181

182-
if !cmd.io.IsStdinPiped() {
182+
if !cmd.io.Stdin().IsPiped() {
183183
return ErrNoDataOnStdin
184184
}
185185

internals/secrethub/write.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ var (
2222
// WriteCommand is a command to write content to a secret.
2323
type WriteCommand struct {
2424
io ui.IO
25-
askSecret func(io ui.IO, question string) (string, error)
2625
path api.SecretPath
2726
inFile string
2827
multiline bool
@@ -37,7 +36,6 @@ func NewWriteCommand(io ui.IO, newClient newClientFunc) *WriteCommand {
3736
return &WriteCommand{
3837
clipper: clip.NewClipboard(),
3938
io: io,
40-
askSecret: ui.AskSecret,
4139
newClient: newClient,
4240
}
4341
}
@@ -84,7 +82,7 @@ func (cmd *WriteCommand) Run() error {
8482
if err != nil {
8583
return ErrReadFile(cmd.inFile, err)
8684
}
87-
} else if cmd.io.IsStdinPiped() {
85+
} else if cmd.io.Stdin().IsPiped() {
8886
data, err = ioutil.ReadAll(cmd.io.Stdin())
8987
if err != nil {
9088
return ui.ErrReadInput(err)
@@ -96,7 +94,7 @@ func (cmd *WriteCommand) Run() error {
9694
return err
9795
}
9896
} else {
99-
str, err := cmd.askSecret(cmd.io, "Please type in the value of the secret, followed by an [ENTER]:")
97+
str, err := ui.AskSecret(cmd.io, "Please type in the value of the secret, followed by an [ENTER]:")
10098
if err != nil {
10199
return err
102100
}

internals/secrethub/write_test.go

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package secrethub
22

33
import (
4-
"bufio"
54
"bytes"
65
"testing"
76

@@ -19,19 +18,6 @@ import (
1918
func TestWriteCommand_Run(t *testing.T) {
2019
testErr := errio.Namespace("test").Code("test").Error("test error")
2120

22-
fakeAskSecretFunc := func(io ui.IO, question string) (s string, err error) {
23-
reader, writer, err := io.Prompts()
24-
if err != nil {
25-
return "", err
26-
}
27-
_, err = writer.Write([]byte(question + "\n"))
28-
if err != nil {
29-
return "", err
30-
}
31-
line, _, err := bufio.NewReader(reader).ReadLine()
32-
return string(line), err
33-
}
34-
3521
cases := map[string]struct {
3622
cmd WriteCommand
3723
writeFunc func(path string, data []byte) (*api.SecretVersion, error)
@@ -135,8 +121,7 @@ func TestWriteCommand_Run(t *testing.T) {
135121
},
136122
"ask secret success": {
137123
cmd: WriteCommand{
138-
path: "namespace/repo/secret",
139-
askSecret: fakeAskSecretFunc,
124+
path: "namespace/repo/secret",
140125
},
141126
promptIn: "asked secret value",
142127
promptOut: "Please type in the value of the secret, followed by an [ENTER]:\n",
@@ -152,8 +137,7 @@ func TestWriteCommand_Run(t *testing.T) {
152137
},
153138
"ask secret error": {
154139
cmd: WriteCommand{
155-
path: "namespace/repo/secret",
156-
askSecret: fakeAskSecretFunc,
140+
path: "namespace/repo/secret",
157141
},
158142
promptErr: testErr,
159143
err: testErr,

0 commit comments

Comments
 (0)