Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit 5ef5b4a

Browse files
authored
Feature: Interactive Role Finder (#87)
1 parent 268014d commit 5ef5b4a

File tree

5 files changed

+447
-110
lines changed

5 files changed

+447
-110
lines changed

cmd/export.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,17 @@ var exportCmd = &cobra.Command{
3737
Use: "export [role_name]",
3838
Short: exportShortHelp,
3939
Long: exportLongHelp,
40-
Args: cobra.ExactArgs(1),
40+
Args: cobra.MaximumNArgs(1),
4141
RunE: runExport,
4242
}
4343

4444
func runExport(cmd *cobra.Command, args []string) error {
45-
role := args[0]
45+
// If a role was provided, use it, otherwise prompt
46+
role, err := InteractiveRolePrompt(args, region, nil)
47+
if err != nil {
48+
return err
49+
}
50+
4651
creds, err := creds.GetCredentials(role, noIpRestrict, assumeRole, "")
4752
if err != nil {
4853
return err

cmd/file.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,18 @@ var fileCmd = &cobra.Command{
4545
Use: "file [role_name]",
4646
Short: fileShortHelp,
4747
Long: fileLongHelp,
48-
Args: cobra.ExactArgs(1),
48+
Args: cobra.MaximumNArgs(1),
4949
RunE: runFile,
5050
}
5151

5252
func runFile(cmd *cobra.Command, args []string) error {
53-
role := args[0]
54-
err := updateCredentialsFile(role, profileName, destination, noIpRestrict, assumeRole)
53+
// If a role was provided, use it, otherwise prompt
54+
role, err := InteractiveRolePrompt(args, region, nil)
55+
if err != nil {
56+
return err
57+
}
58+
59+
err = updateCredentialsFile(role, profileName, destination, noIpRestrict, assumeRole)
5560
if err != nil {
5661
return err
5762
}

cmd/helpers.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2020 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package cmd
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"strings"
23+
24+
"github.com/lithammer/fuzzysearch/fuzzy"
25+
"github.com/manifoldco/promptui"
26+
"github.com/netflix/weep/pkg/creds"
27+
)
28+
29+
// InteractiveRolePrompt will present the user with a fuzzy-searchable list of roles if
30+
// - We are currently attached to an interactive tty
31+
// - The user has not disabled them through the WEEP_DISABLE_INTERACTIVE_PROMPTS option
32+
func InteractiveRolePrompt(args []string, region string, client *creds.Client) (string, error) {
33+
// If a role was provided, just use that
34+
if len(args) > 0 {
35+
return args[0], nil
36+
}
37+
38+
if !isRunningInTerminal() {
39+
return "", fmt.Errorf("no role provided, and cannot prompt for input")
40+
}
41+
42+
if os.Getenv("WEEP_DISABLE_INTERACTIVE_PROMPTS") == "1" {
43+
return "", fmt.Errorf("no role provided, and interactive prompts are disabled")
44+
}
45+
46+
// If a client was not provided, create one using the provided region
47+
if client == nil {
48+
var err error
49+
client, err = creds.GetClient(region)
50+
if err != nil {
51+
return "", err
52+
}
53+
}
54+
55+
// Retrieve the list of roles
56+
roles, err := client.Roles()
57+
if err != nil {
58+
return "", err
59+
}
60+
61+
// Prompt the user
62+
prompt := promptui.Select{
63+
Label: "Select Role",
64+
Items: roles,
65+
Size: 16,
66+
Searcher: func(input string, index int) bool {
67+
return fuzzy.MatchNormalized(strings.ToLower(input), strings.ToLower(roles[index]))
68+
},
69+
StartInSearchMode: true,
70+
}
71+
72+
_, role, err := prompt.Run()
73+
if err != nil {
74+
return "", err
75+
}
76+
77+
return role, nil
78+
}
79+
80+
func isRunningInTerminal() bool {
81+
fileInfo, _ := os.Stdout.Stat()
82+
return (fileInfo.Mode() & os.ModeCharDevice) != 0
83+
}

go.mod

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,17 @@ require (
99
github.com/golang/glog v0.0.0-20210429001901-424d2337a529
1010
github.com/gorilla/mux v1.8.0
1111
github.com/kardianos/service v1.2.0
12-
github.com/kr/pretty v0.2.0 // indirect
12+
github.com/lithammer/fuzzysearch v1.1.2
1313
github.com/lunixbochs/vtclean v1.0.0 // indirect
14-
github.com/magiconair/properties v1.8.5 // indirect
1514
github.com/manifoldco/promptui v0.8.0
16-
github.com/mattn/go-colorable v0.1.8 // indirect
1715
github.com/mattn/go-isatty v0.0.13 // indirect
1816
github.com/mitchellh/go-homedir v1.1.0
19-
github.com/mitchellh/mapstructure v1.4.1 // indirect
20-
github.com/pelletier/go-toml v1.9.2 // indirect
2117
github.com/pkg/errors v0.9.1
2218
github.com/russross/blackfriday/v2 v2.1.0 // indirect
2319
github.com/sirupsen/logrus v1.8.1
24-
github.com/spf13/afero v1.6.0 // indirect
25-
github.com/spf13/cast v1.3.1 // indirect
26-
github.com/spf13/cobra v1.1.3
27-
github.com/spf13/jwalterweatherman v1.1.0 // indirect
28-
github.com/spf13/viper v1.7.1
29-
github.com/stretchr/testify v1.5.1 // indirect
20+
github.com/spf13/cobra v1.2.1
21+
github.com/spf13/viper v1.8.1
3022
golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 // indirect
31-
golang.org/x/text v0.3.6 // indirect
32-
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
3323
gopkg.in/ini.v1 v1.62.0
3424
gopkg.in/yaml.v2 v2.4.0
3525
)

0 commit comments

Comments
 (0)