Skip to content

Commit

Permalink
Improve CLI UX (#49)
Browse files Browse the repository at this point in the history
* commit progress on ux-focused CLI refactoring

* fix all flag, rename server function

* use fancy new metadata in other places

* simplify handling of json responses, log errors
  • Loading branch information
patricksanders authored Mar 4, 2021
1 parent 77f6d7c commit 1afbcd0
Show file tree
Hide file tree
Showing 24 changed files with 393 additions and 173 deletions.
18 changes: 18 additions & 0 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,24 @@ func (cc *CredentialCache) GetDefault() (*creds.RefreshableProvider, error) {
return nil, errors.NoCredentialsFoundInCache
}

func (cc *CredentialCache) DefaultLastUpdated() string {
c, err := cc.GetDefault()
if err != nil {
log.Debugf("cannot get last updated time of default creds: %v", err)
return ""
}
return c.LastRefreshed.UTC().Format("2006-01-02T15:04:05Z")
}

func (cc *CredentialCache) DefaultArn() string {
c, err := cc.GetDefault()
if err != nil {
log.Debugf("cannot get arn of default creds: %v", err)
return ""
}
return c.RoleArn
}

func (cc *CredentialCache) get(slug string) (*creds.RefreshableProvider, bool) {
cc.RLock()
defer cc.RUnlock()
Expand Down
80 changes: 80 additions & 0 deletions cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,86 @@ func TestCredentialCache_SetDefault(t *testing.T) {
}
}

func TestCredentialCache_DefaultLastUpdated(t *testing.T) {
testCache := CredentialCache{
RoleCredentials: map[string]*creds.RefreshableProvider{},
}
testClient, err := creds.GetTestClient(creds.ConsolemeCredentialResponseType{
Credentials: &creds.AwsCredentials{
AccessKeyId: "a",
SecretAccessKey: "b",
SessionToken: "c",
Expiration: creds.Time(time.Unix(1, 0)),
RoleArn: "e",
},
})
if err != nil {
t.Errorf("test setup failure: %e", err)
}
err = testCache.SetDefault(testClient, "a", "b", make([]string, 0))
if err != nil {
t.Errorf("test failure: %e", err)
}
timeFormat := "2006-01-02T15:04:05Z"
result := testCache.DefaultLastUpdated()
resultTime, err := time.Parse(timeFormat, result)
if err != nil {
t.Errorf("invalid time format returned: expected format %s, got result %s", timeFormat, result)
}
now := time.Now().Format(timeFormat)
timeDiff := time.Now().Sub(resultTime)
if timeDiff < 0*time.Second || timeDiff > 1*time.Second {
t.Errorf("last refreshed time more than 1 second different than current time: %s, current time %s, difference %v seconds", result, now, timeDiff)
}
}

func TestCredentialCache_DefaultLastUpdated_NoDefault(t *testing.T) {
testCache := CredentialCache{
RoleCredentials: map[string]*creds.RefreshableProvider{},
}
result := testCache.DefaultLastUpdated()
if result != "" {
t.Errorf("wrong last updated returned: got %s, expected empty string", result)
}
}

func TestCredentialCache_DefaultArn(t *testing.T) {
testCache := CredentialCache{
RoleCredentials: map[string]*creds.RefreshableProvider{},
}
testClient, err := creds.GetTestClient(creds.ConsolemeCredentialResponseType{
Credentials: &creds.AwsCredentials{
AccessKeyId: "a",
SecretAccessKey: "b",
SessionToken: "c",
Expiration: creds.Time(time.Unix(1, 0)),
RoleArn: "e",
},
})
if err != nil {
t.Errorf("test setup failure: %e", err)
}
err = testCache.SetDefault(testClient, "a", "b", make([]string, 0))
if err != nil {
t.Errorf("test failure: %e", err)
}
expected := "e"
result := testCache.DefaultArn()
if result != expected {
t.Errorf("wrong arn returned: got %s, expected %s", result, expected)
}
}

func TestCredentialCache_DefaultArn_NoDefault(t *testing.T) {
testCache := CredentialCache{
RoleCredentials: map[string]*creds.RefreshableProvider{},
}
result := testCache.DefaultArn()
if result != "" {
t.Errorf("wrong arn returned: got %s, expected empty string", result)
}
}

func TestCredentialCache_GetOrSet(t *testing.T) {
cases := []struct {
CacheContents map[string]*creds.RefreshableProvider
Expand Down
7 changes: 4 additions & 3 deletions cmd/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ import (

// completionCmd represents the completion command
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: `Generate shell completion script for Bash, Zsh, Fish, and Powershell.`,
Use: "completion [bash|zsh|fish|powershell]",
Short: completionShortHelp,
Long: completionLongHelp,
Hidden: true,
}

var bashCompletionCmd = &cobra.Command{
Expand Down
33 changes: 17 additions & 16 deletions cmd/credential_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,21 @@ import (
)

func init() {
CredentialProcessCmd.PersistentFlags().BoolVarP(&noIpRestrict, "no-ip", "n", false, "remove IP restrictions")
GenerateCredentialProcessCmd.PersistentFlags().StringVarP(&destinationConfig, "output", "o", getDefaultAwsConfigFile(), "output file for AWS config")
CredentialProcessCmd.PersistentFlags().BoolVarP(&generate, "generate", "g", false, "generate ~/.aws/config with credential process config")
CredentialProcessCmd.PersistentFlags().BoolVarP(&showAll, "all", "a", false, "include all roles (console and application)")
CredentialProcessCmd.PersistentFlags().StringVarP(&destinationConfig, "output", "o", getDefaultAwsConfigFile(), "output file for AWS config")
rootCmd.AddCommand(CredentialProcessCmd)
rootCmd.AddCommand(GenerateCredentialProcessCmd)
}

var GenerateCredentialProcessCmd = &cobra.Command{
Use: "generate_credential_process_config",
Short: "Write all of your eligible roles as profiles in your AWS Config to source credentials from Weep",
RunE: runGenerateCredentialProcessConfig,
}

var CredentialProcessCmd = &cobra.Command{
Use: "credential_process [role_name]",
Short: "Retrieve credentials and writes them in credential_process format",
Args: cobra.ExactArgs(1),
Short: credentialProcessShortHelp,
Long: credentialProcessLongHelp,
Args: cobra.MaximumNArgs(1),
RunE: runCredentialProcess,
}

func writeConfigFile(roles []string) error {
func writeConfigFile(roles []string, destination string) error {
var configINI *ini.File
var err error

Expand Down Expand Up @@ -78,24 +73,30 @@ func writeConfigFile(roles []string) error {
return nil
}

func runGenerateCredentialProcessConfig(cmd *cobra.Command, args []string) error {
func generateCredentialProcessConfig(destination string) error {
if destination == "" {
return fmt.Errorf("no destination provided")
}
client, err := creds.GetClient()
if err != nil {
return err
}
roles, err := client.Roles()
roles, err := client.Roles(showAll)
if err != nil {
return err
}
err = writeConfigFile(roles)
err = writeConfigFile(roles, destination)
if err != nil {
return err
}
return nil
}

func runCredentialProcess(cmd *cobra.Command, args []string) error {
role = args[0]
if generate {
return generateCredentialProcessConfig(destination)
}
role := args[0]
credentials, err := creds.GetCredentials(role, noIpRestrict, assumeRole)
if err != nil {
return err
Expand Down
3 changes: 2 additions & 1 deletion cmd/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import (

var docCommand = &cobra.Command{
Use: "docs",
Short: "Generate Markdown docs for CLI commands",
Short: docsShortHelp,
Long: docsLongHelp,
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
err := doc.GenMarkdownTree(rootCmd, "./docs/")
Expand Down
67 changes: 0 additions & 67 deletions cmd/ecs_credential_provider.go

This file was deleted.

5 changes: 3 additions & 2 deletions cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ func init() {

var exportCmd = &cobra.Command{
Use: "export [role_name]",
Short: "Retrieve credentials to be exported as environment variables",
Short: exportShortHelp,
Long: exportLongHelp,
Args: cobra.ExactArgs(1),
RunE: runExport,
}

func runExport(cmd *cobra.Command, args []string) error {
role = args[0]
role := args[0]
creds, err := creds.GetCredentials(role, noIpRestrict, assumeRole)
if err != nil {
return err
Expand Down
6 changes: 3 additions & 3 deletions cmd/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
)

func init() {
fileCmd.PersistentFlags().BoolVarP(&noIpRestrict, "no-ip", "n", false, "remove IP restrictions")
fileCmd.PersistentFlags().StringVarP(&destination, "output", "o", getDefaultCredentialsFile(), "output file for credentials")
fileCmd.PersistentFlags().StringVarP(&profileName, "profile", "p", "default", "profile name")
fileCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, "overwrite existing profile without prompting")
Expand All @@ -41,13 +40,14 @@ func init() {

var fileCmd = &cobra.Command{
Use: "file [role_name]",
Short: "Retrieve credentials and save them to a credentials file",
Short: fileShortHelp,
Long: fileLongHelp,
Args: cobra.ExactArgs(1),
RunE: runFile,
}

func runFile(cmd *cobra.Command, args []string) error {
role = args[0]
role := args[0]
err := updateCredentialsFile(role, profileName, destination, noIpRestrict, assumeRole)
if err != nil {
return err
Expand Down
6 changes: 4 additions & 2 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ import (
)

func init() {
listCmd.PersistentFlags().BoolVarP(&showAll, "all", "a", false, "include all roles (console and application)")
rootCmd.AddCommand(listCmd)
}

var listCmd = &cobra.Command{
Use: "list",
Short: "List available roles",
Short: listShortHelp,
Long: listLongHelp,
RunE: runList,
}

Expand All @@ -38,7 +40,7 @@ func runList(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
roles, err := client.Roles()
roles, err := client.Roles(showAll)
if err != nil {
return err
}
Expand Down
11 changes: 6 additions & 5 deletions cmd/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ func init() {
}

var metadataCmd = &cobra.Command{
Use: "metadata [role_name]",
Short: "RunService a local Instance Metadata Service (IMDS) endpoint that serves credentials",
Args: cobra.ExactArgs(1),
RunE: runMetadata,
Use: "imds [role_name]",
Aliases: []string{"metadata"},
Short: "Run a local Instance Metadata Service (IMDS) endpoint that serves credentials",
Args: cobra.ExactArgs(1),
RunE: runMetadata,
}

func runMetadata(cmd *cobra.Command, args []string) error {
role = args[0]
role := args[0]
client, err := creds.GetClient()
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func init() {
cobra.OnInitialize(initConfig)
cobra.OnInitialize(updateLoggingConfig)

rootCmd.PersistentFlags().BoolVarP(&noIpRestrict, "no-ip", "n", false, "remove IP restrictions")
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.weep.yaml)")
rootCmd.PersistentFlags().StringSliceVarP(&assumeRole, "assume-role", "A", make([]string, 0), "one or more roles to assume after retrieving credentials")
rootCmd.PersistentFlags().StringVar(&logFormat, "log-format", "", "log format (json or tty)")
Expand Down
Loading

0 comments on commit 1afbcd0

Please sign in to comment.