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

Commit 68abbee

Browse files
authored
Merge pull request #224 from secrethub/release/v0.33.0
Release v0.33.0
2 parents 7cc6b42 + 0d5c44f commit 68abbee

16 files changed

Lines changed: 688 additions & 32 deletions

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/mitchellh/mapstructure v1.1.2
1717
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
1818
github.com/secrethub/demo-app v0.1.0
19-
github.com/secrethub/secrethub-go v0.24.0
19+
github.com/secrethub/secrethub-go v0.25.0
2020
github.com/zalando/go-keyring v0.0.0-20190208082241-fbe81aec3a07
2121
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a
2222
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223

go.sum

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 h1:pSm8mp0T2OH2CP
44
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
55
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 h1:y8Gs8CzNfDF5AZvjr+5UyGQvQEBL7pwo+v+wX6q9JI8=
66
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
7-
github.com/alecthomas/kingpin v0.0.0-20190705085659-95eb9edaa399 h1:NoJOfPUP5uDdYYuWS/+I6UQDH4+Fo1wDlaw4CUEeHmk=
8-
github.com/alecthomas/kingpin v0.0.0-20190705085659-95eb9edaa399/go.mod h1:idxgS9pV6OOpAhZvx+gcoGRMX9/tt0iqkw/pNxI0C14=
97
github.com/alecthomas/kingpin v0.0.0-20190930021037-0a108b7f5563 h1:YT8l7Flq7VNXnjqwtjCF9bzffTPGgedBC+xyj88lVe4=
108
github.com/alecthomas/kingpin v0.0.0-20190930021037-0a108b7f5563/go.mod h1:idxgS9pV6OOpAhZvx+gcoGRMX9/tt0iqkw/pNxI0C14=
119
github.com/alecthomas/kingpin v0.0.0-20191009151950-9e366cbf24ad h1:tMnaQBlddYTQC6SHj8IrLTuOf006vB7yUmYn79/QXlM=
@@ -70,29 +68,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
7068
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7169
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
7270
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
73-
github.com/secrethub/demo-app v0.0.0-20191015143627-8b86c8671e54 h1:u1tfHZH5FE6Lv8CcV9dnimz5uMZUFvHKTyOqbsqlGFo=
74-
github.com/secrethub/demo-app v0.0.0-20191015143627-8b86c8671e54/go.mod h1:ymjm8+WXTSDTFqsGVBNVmHSnwtZMYi7KptHvpo/fLH4=
75-
github.com/secrethub/demo-app v0.0.0-20191015144738-eed4af2178f4 h1:04IDTEVBJUVXqXAFwdoOvHeO87a8uTzMHb5nzqHtIVk=
76-
github.com/secrethub/demo-app v0.0.0-20191015144738-eed4af2178f4/go.mod h1:ymjm8+WXTSDTFqsGVBNVmHSnwtZMYi7KptHvpo/fLH4=
77-
github.com/secrethub/demo-app v0.0.0-20191015145040-d297a9c939a6 h1:BA2nciOEMOjuy0xMcMyEDcaKhvAyB7cVG8H6LW1/XUg=
78-
github.com/secrethub/demo-app v0.0.0-20191015145040-d297a9c939a6/go.mod h1:ymjm8+WXTSDTFqsGVBNVmHSnwtZMYi7KptHvpo/fLH4=
7971
github.com/secrethub/demo-app v0.1.0 h1:HwPPxuiSvx4TBE7Qppzu3A9eHqmsBrIz4Ko8u8pqMqw=
8072
github.com/secrethub/demo-app v0.1.0/go.mod h1:ymjm8+WXTSDTFqsGVBNVmHSnwtZMYi7KptHvpo/fLH4=
81-
github.com/secrethub/kingpin v0.0.0-20190920111600-67b1cb231087 h1:n4V94OQPe04crRRmcyAURLk3AImueFd44Ne99sN5TO8=
82-
github.com/secrethub/kingpin v0.0.0-20190920111600-67b1cb231087/go.mod h1:idxgS9pV6OOpAhZvx+gcoGRMX9/tt0iqkw/pNxI0C14=
8373
github.com/secrethub/secrethub-cli v0.30.0/go.mod h1:dC0wd40v+iQdV83/0rUrOa01LYq+8Yj2AtJB1vzh2ao=
84-
github.com/secrethub/secrethub-go v0.21.0 h1:5xbC+gdku7MXUQmlP5SPhAPLF+/U31soLKbyXwNyM+M=
8574
github.com/secrethub/secrethub-go v0.21.0/go.mod h1:rc2IfKKBJ4L0wGec0u4XnF5/pe0FFPE4Q1MWfrFso7s=
86-
github.com/secrethub/secrethub-go v0.23.0 h1:6NzVGcAJDXeORUc2uZyuaYRCXCawmOKYvhNPP3ASNSM=
87-
github.com/secrethub/secrethub-go v0.23.0/go.mod h1:rc2IfKKBJ4L0wGec0u4XnF5/pe0FFPE4Q1MWfrFso7s=
88-
github.com/secrethub/secrethub-go v0.23.1-0.20191120150410-759ed0ccde7e h1:9FjAfwJiwGWGo7I0LbQ7CTS2qvjpaI5DTeZVIWisGBQ=
89-
github.com/secrethub/secrethub-go v0.23.1-0.20191120150410-759ed0ccde7e/go.mod h1:rc2IfKKBJ4L0wGec0u4XnF5/pe0FFPE4Q1MWfrFso7s=
90-
github.com/secrethub/secrethub-go v0.23.1-0.20191126142216-0736401365d7 h1:GH23+Ig/6Gsqyat8FQ06bMUVJvy1tisKuWhj22oS344=
91-
github.com/secrethub/secrethub-go v0.23.1-0.20191126142216-0736401365d7/go.mod h1:rc2IfKKBJ4L0wGec0u4XnF5/pe0FFPE4Q1MWfrFso7s=
92-
github.com/secrethub/secrethub-go v0.23.1-0.20191126145052-23fd5b7a4363 h1:0lHIjcVCBFrlpKv6Ag2JhuI5FlvxiGXXRnJUqgrFiaE=
93-
github.com/secrethub/secrethub-go v0.23.1-0.20191126145052-23fd5b7a4363/go.mod h1:rc2IfKKBJ4L0wGec0u4XnF5/pe0FFPE4Q1MWfrFso7s=
94-
github.com/secrethub/secrethub-go v0.24.0 h1:psxPZzPk5DrZTTTvnIE9F1oj43QVwHjJ2ZnBvsXn0wc=
95-
github.com/secrethub/secrethub-go v0.24.0/go.mod h1:rc2IfKKBJ4L0wGec0u4XnF5/pe0FFPE4Q1MWfrFso7s=
75+
github.com/secrethub/secrethub-go v0.25.0 h1:cpYmkLRurrrw6NNE4PagPNDOn7kvY6UMrnnDxrvuI1M=
76+
github.com/secrethub/secrethub-go v0.25.0/go.mod h1:rc2IfKKBJ4L0wGec0u4XnF5/pe0FFPE4Q1MWfrFso7s=
9677
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
9778
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
9879
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=

internals/cli/ui/ask.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ui
22

33
import (
4+
"errors"
45
"fmt"
56
"io"
67
"io/ioutil"
@@ -35,6 +36,19 @@ func Ask(io IO, question string) (string, error) {
3536
return Readln(r)
3637
}
3738

39+
// AskWithDefault prints out the question and reads the first line of input.
40+
// If no input is given, the default value is returned.
41+
func AskWithDefault(io IO, question, defaultValue string) (string, error) {
42+
res, err := Ask(io, fmt.Sprintf("%s [%s] ", question, defaultValue))
43+
if err != nil {
44+
return "", err
45+
}
46+
if res == "" {
47+
return defaultValue, nil
48+
}
49+
return res, nil
50+
}
51+
3852
// AskSecret prints out the question and reads back the input,
3953
// without echoing it back. Useful for passwords and other sensitive inputs.
4054
func AskSecret(io IO, question string) (string, error) {
@@ -211,6 +225,44 @@ func AskYesNo(io IO, question string, t ConfirmationType) (bool, error) {
211225
return false, nil
212226
}
213227

228+
// Choose gives the user the provided options asks them to choose one.
229+
// It returns the index of the option chosen, starting with 0.
230+
func Choose(io IO, question string, options []string, n int) (int, error) {
231+
_, w, err := io.Prompts()
232+
if err != nil {
233+
return 0, err
234+
}
235+
236+
_, err = fmt.Fprintf(w, "%s\n", question)
237+
if err != nil {
238+
return 0, err
239+
}
240+
241+
for i, option := range options {
242+
fmt.Fprintf(w, " %d) %s\n", i+1, option)
243+
}
244+
245+
parseFunc := func(in string) (int, error) {
246+
res, err := strconv.Atoi(strings.Trim(in, " )."))
247+
if err != nil {
248+
return 0, errors.New("not a valid number")
249+
}
250+
if res < 1 || res > len(options) {
251+
return 0, errors.New("out of bounds")
252+
}
253+
return res - 1, nil
254+
}
255+
256+
res, err := AskAndValidate(io, "Give the number of an option: ", n, func(option string) error {
257+
_, err := parseFunc(option)
258+
return err
259+
})
260+
if err != nil {
261+
return 0, err
262+
}
263+
return parseFunc(res)
264+
}
265+
214266
type Option struct {
215267
Value string
216268
Display string
@@ -220,7 +272,7 @@ func (o Option) String() string {
220272
return o.Display
221273
}
222274

223-
func Choose(io IO, question string, getOptions func() ([]Option, bool, error), addOwn bool, optionName string) (string, error) {
275+
func ChooseDynamicOptions(io IO, question string, getOptions func() ([]Option, bool, error), addOwn bool, optionName string) (string, error) {
224276
r, w, err := io.Prompts()
225277
if err != nil {
226278
return "", err

internals/cli/ui/ask_test.go

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,52 @@ package ui
22

33
import (
44
"bytes"
5+
"errors"
56
"fmt"
67
"testing"
78

89
"github.com/secrethub/secrethub-go/internals/assert"
910
)
1011

12+
func TestAskWithDefault(t *testing.T) {
13+
question := "foo?"
14+
defaultValue := "bar"
15+
defaultOutput := "foo? [" + defaultValue + "] "
16+
cases := map[string]struct {
17+
in []string
18+
expected string
19+
expectedOut string
20+
}{
21+
"value entered": {
22+
in: []string{"foobar\n"},
23+
expected: "foobar",
24+
expectedOut: defaultOutput,
25+
},
26+
"no value entered": {
27+
in: []string{"\n"},
28+
expected: defaultValue,
29+
expectedOut: defaultOutput,
30+
},
31+
}
32+
33+
for name, tc := range cases {
34+
t.Run(name, func(t *testing.T) {
35+
// Setup
36+
io := NewFakeIO()
37+
io.PromptIn.Reads = tc.in
38+
39+
// Run
40+
actual, err := AskWithDefault(io, question, defaultValue)
41+
42+
// Assert
43+
assert.OK(t, err)
44+
assert.Equal(t, actual, tc.expected)
45+
46+
assert.Equal(t, io.PromptOut.String(), tc.expectedOut)
47+
})
48+
}
49+
}
50+
1151
func TestConfirmCaseInsensitive(t *testing.T) {
1252
cases := map[string]struct {
1353
expectedConfirmation []string
@@ -214,6 +254,88 @@ func TestAskYesNo(t *testing.T) {
214254
}
215255

216256
func TestChoose(t *testing.T) {
257+
question := "foo?"
258+
defaultOptions := []string{
259+
"option 1",
260+
"second option",
261+
}
262+
defaultOutput := "foo?\n" +
263+
" 1) option 1\n" +
264+
" 2) second option\n" +
265+
"Give the number of an option: "
266+
267+
cases := map[string]struct {
268+
in []string
269+
options []string
270+
n int
271+
expected int
272+
expectedErr error
273+
expectedOut string
274+
}{
275+
"first option": {
276+
in: []string{"1\n"},
277+
options: defaultOptions,
278+
n: 3,
279+
expected: 0,
280+
expectedOut: defaultOutput,
281+
},
282+
"retry": {
283+
in: []string{"a\n", "1\n"},
284+
options: defaultOptions,
285+
n: 3,
286+
expected: 0,
287+
expectedOut: defaultOutput + "\nInvalid input: not a valid number\nPlease try again.\nGive the number of an option: ",
288+
},
289+
"filter out )": {
290+
in: []string{"1)\n"},
291+
options: defaultOptions,
292+
n: 3,
293+
expected: 0,
294+
expectedOut: defaultOutput,
295+
},
296+
"out of bounds lower": {
297+
in: []string{"0\n"},
298+
options: defaultOptions,
299+
n: 1,
300+
expectedErr: errors.New("out of bounds"),
301+
expectedOut: defaultOutput + "\nInvalid input: out of bounds\n",
302+
},
303+
"out of bounds upper": {
304+
in: []string{"3\n"},
305+
options: defaultOptions,
306+
n: 1,
307+
expectedErr: errors.New("out of bounds"),
308+
expectedOut: defaultOutput + "\nInvalid input: out of bounds\n",
309+
},
310+
"not a number": {
311+
in: []string{"abc\n"},
312+
options: defaultOptions,
313+
n: 1,
314+
expectedErr: errors.New("not a valid number"),
315+
expectedOut: defaultOutput + "\nInvalid input: not a valid number\n",
316+
},
317+
}
318+
319+
for name, tc := range cases {
320+
t.Run(name, func(t *testing.T) {
321+
// Setup
322+
io := NewFakeIO()
323+
io.PromptIn.Reads = tc.in
324+
325+
// Run
326+
actual, err := Choose(io, question, tc.options, tc.n)
327+
328+
// Assert
329+
assert.Equal(t, err, tc.expectedErr)
330+
if tc.expectedErr == nil {
331+
assert.Equal(t, actual, tc.expected)
332+
}
333+
assert.Equal(t, io.PromptOut.String(), tc.expectedOut)
334+
})
335+
}
336+
}
337+
338+
func TestChooseDynamicOptions(t *testing.T) {
217339
cases := map[string]struct {
218340
question string
219341
getOptions func() ([]Option, bool, error)
@@ -302,7 +424,7 @@ func TestChoose(t *testing.T) {
302424
}
303425

304426
// Run
305-
actual, err := Choose(io, tc.question, tc.getOptions, tc.addOwn, "value")
427+
actual, err := ChooseDynamicOptions(io, tc.question, tc.getOptions, tc.addOwn, "value")
306428

307429
// Assert
308430
assert.Equal(t, err, nil)

internals/secrethub/app.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,11 @@ func (app *App) registerCommands() {
161161
NewACLCommand(app.io, app.clientFactory.NewClient).Register(app.cli)
162162
NewServiceCommand(app.io, app.clientFactory.NewClient).Register(app.cli)
163163
NewAccountCommand(app.io, app.clientFactory.NewClient, app.credentialStore).Register(app.cli)
164+
NewCredentialCommand(app.io, app.clientFactory, app.credentialStore).Register(app.cli)
164165
NewConfigCommand(app.io, app.credentialStore).Register(app.cli)
165166

166167
// Commands
168+
NewInitCommand(app.io, app.clientFactory.NewUnauthenticatedClient, app.clientFactory.NewClientWithCredentials, app.credentialStore).Register(app.cli)
167169
NewSignUpCommand(app.io, app.clientFactory.NewUnauthenticatedClient, app.credentialStore).Register(app.cli)
168170
NewWriteCommand(app.io, app.clientFactory.NewClient).Register(app.cli)
169171
NewReadCommand(app.io, app.clientFactory.NewClient).Register(app.cli)

internals/secrethub/client_factory.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var (
1818
type ClientFactory interface {
1919
// NewClient returns a new SecretHub client.
2020
NewClient() (secrethub.ClientInterface, error)
21+
NewClientWithCredentials(credentials.Provider) (secrethub.ClientInterface, error)
2122
NewUnauthenticatedClient() (secrethub.ClientInterface, error)
2223
Register(FlagRegisterer)
2324
}
@@ -70,6 +71,18 @@ func (f *clientFactory) NewClient() (secrethub.ClientInterface, error) {
7071
return f.client, nil
7172
}
7273

74+
func (f *clientFactory) NewClientWithCredentials(provider credentials.Provider) (secrethub.ClientInterface, error) {
75+
options := f.baseClientOptions()
76+
options = append(options, secrethub.WithCredentials(provider))
77+
78+
client, err := secrethub.NewClient(options...)
79+
if err != nil {
80+
return nil, err
81+
}
82+
83+
return client, nil
84+
}
85+
7386
func (f *clientFactory) NewUnauthenticatedClient() (secrethub.ClientInterface, error) {
7487
options := f.baseClientOptions()
7588

internals/secrethub/credential.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package secrethub
2+
3+
import (
4+
"github.com/secrethub/secrethub-cli/internals/cli/ui"
5+
"github.com/secrethub/secrethub-cli/internals/secrethub/command"
6+
)
7+
8+
// CredentialCommand handles operations on SecretHub credentials.
9+
type CredentialCommand struct {
10+
io ui.IO
11+
clientFactory ClientFactory
12+
credentialStore CredentialConfig
13+
}
14+
15+
// NewCredentialCommand creates a new CredentialCommand.
16+
func NewCredentialCommand(io ui.IO, clientFactory ClientFactory, credentialStore CredentialConfig) *CredentialCommand {
17+
return &CredentialCommand{
18+
io: io,
19+
clientFactory: clientFactory,
20+
credentialStore: credentialStore,
21+
}
22+
}
23+
24+
// Register registers the command and its sub-commands on the provided Registerer.
25+
func (cmd *CredentialCommand) Register(r command.Registerer) {
26+
clause := r.Command("credential", "Manage your credentials.")
27+
NewCredentialListCommand(cmd.io, cmd.clientFactory.NewClient).Register(clause)
28+
NewCredentialBackupCommand(cmd.io, cmd.clientFactory.NewClient).Register(clause)
29+
NewCredentialDisableCommand(cmd.io, cmd.clientFactory.NewClient).Register(clause)
30+
}

0 commit comments

Comments
 (0)