From 07ecbd0668e7f89f36627fd1bb6676bfa0a7a240 Mon Sep 17 00:00:00 2001 From: joshrivers Date: Thu, 4 Jan 2024 13:38:33 -0800 Subject: [PATCH] fix: quiet flag to stop credential process errors (#160) * fix: quiet flag to stop credential process errors * Remove redundant flag The quiet flag and alias are sufficient for the need and the redundant non-interactive flag can be left out Co-authored-by: Tim Heurich * Whitespace correction in README Fixes #159 --------- Co-authored-by: Josh Rivers Co-authored-by: Tim Heurich --- .github/workflows/build-and-test.yaml | 4 + README.md | 19 ++-- cmd/go-aws-sso/main.go | 12 +++ cmd/go-aws-sso/main_test.go | 130 +++++++++++++++++++++++++- internal/assume_test.go | 7 +- internal/config_test.go | 3 +- pkg/sso/file_system.go | 2 +- pkg/sso/file_system_test.go | 3 +- 8 files changed, 160 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index cdec2ab..26163cf 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -35,5 +35,9 @@ jobs: go vet github.com/theurichde/go-aws-sso/internal - name: Tests run: | + go clean -r -testcache + go clean -r -x -modcache + go clean -r -x -testcache + go clean -r -x -cache go test -v ./... go test -v github.com/theurichde/go-aws-sso/internal diff --git a/README.md b/README.md index ba49630..a9477f8 100644 --- a/README.md +++ b/README.md @@ -67,15 +67,16 @@ DESCRIPTION: Assume directly into an account and SSO role OPTIONS: - --start-url value, -u value set / override the SSO login start-url. (Example: https://my-login.awsapps.com/start#/) - --region value, -r value set / override the AWS region - --profile value, -p value the profile name you want to set in your ~/.aws/credentials file (default: "default") - --persist whether or not you want to write your short-living credentials to ~/.aws/credentials (default: false) - --force removes the temporary access token and forces the retrieval of a new token (default: false) - --debug enables debug logging (default: false) - --role-name value, -n value The role name you want to assume - --account-id value, -a value The account id where your role lives in - --help, -h show help + --start-url value, -u value set / override the SSO login start-url. (Example: https://my-login.awsapps.com/start#/) + --region value, -r value set / override the AWS region + --profile value, -p value the profile name you want to set in your ~/.aws/credentials file (default: "default") + --persist whether or not you want to write your short-living credentials to ~/.aws/credentials (default: false) + --force removes the temporary access token and forces the retrieval of a new token (default: false) + --debug enables debug logging (default: false) + --role-name value, -n value The role name you want to assume + --account-id value, -a value The account id where your role lives in + --quiet, -q disables logger output (default: false) + --help, -h show help ``` * Execute `go-aws-sso assume --account-id YOUR_ID --role-name YOUR_ROLE_NAME` diff --git a/cmd/go-aws-sso/main.go b/cmd/go-aws-sso/main.go index 435da74..6bc8a61 100644 --- a/cmd/go-aws-sso/main.go +++ b/cmd/go-aws-sso/main.go @@ -130,6 +130,14 @@ func main() { Aliases: []string{"a"}, Usage: "The account id where your role lives in", }), + &cli.BoolFlag{ + Name: "quiet", + Usage: "disables logger output", + Aliases: []string{"q"}, + Value: false, + Hidden: false, + Required: false, + }, }...), }, } @@ -256,6 +264,10 @@ func applyForceFlag(context *cli.Context) { } func initializeLogger(context *cli.Context) { + if context.Bool("quiet") { + zap.ReplaceGlobals(zap.NewNop()) + return + } config := zap.NewProductionEncoderConfig() config.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05") diff --git a/cmd/go-aws-sso/main_test.go b/cmd/go-aws-sso/main_test.go index e0b1b89..b93b3b1 100644 --- a/cmd/go-aws-sso/main_test.go +++ b/cmd/go-aws-sso/main_test.go @@ -2,6 +2,8 @@ package main import ( "flag" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" "os" "testing" "time" @@ -61,10 +63,14 @@ func (t mockTime) Now() time.Time { } func Test_start(t *testing.T) { - + os.Remove(os.TempDir() + "/go-aws-sso.lock") temp, err := os.CreateTemp("", "go-aws-sso_start") check(err) CredentialsFilePath = temp.Name() + defer func(path string) { + os.RemoveAll(path) + os.Remove(os.TempDir() + "/go-aws-sso.lock") + }(CredentialsFilePath) dummyInt := int64(132465) dummy := "dummy" @@ -153,9 +159,6 @@ func Test_start(t *testing.T) { if got != want { t.Errorf("Got: %v, but wanted: %v", got, want) } - - defer os.RemoveAll(CredentialsFilePath) - } type mockPromptUISelector struct { @@ -168,3 +171,122 @@ func (receiver mockPromptUISelector) Select(_ string, _ []string, _ func(input s func (receiver mockPromptUISelector) Prompt(_ string, _ string) string { return "" } + +func Test_initializeLogger(t *testing.T) { + type levelsEnabled struct { + fatal bool + error bool + warn bool + info bool + debug bool + } + tests := []struct { + name string + flags []string + want levelsEnabled + }{ + { + name: "default", + flags: []string{}, + want: levelsEnabled{ + fatal: true, + error: true, + warn: true, + info: true, + debug: false, + }, + }, + { + name: "debug flag only", + flags: []string{"--debug"}, + want: levelsEnabled{ + fatal: true, + error: true, + warn: true, + info: true, + debug: true, + }, + }, + { + name: "quiet flag only", + flags: []string{"--quiet"}, + want: levelsEnabled{ + fatal: false, + error: false, + warn: false, + info: false, + debug: false, + }, + }, + { + name: "quiet flag alias only", + flags: []string{"-q"}, + want: levelsEnabled{ + fatal: false, + error: false, + warn: false, + info: false, + debug: false, + }, + }, + { + name: "quiet flag alternate alias only", + flags: []string{"--non-interactive"}, + want: levelsEnabled{ + fatal: false, + error: false, + warn: false, + info: false, + debug: false, + }, + }, + { + name: "quiet flag overrides debug flag", + flags: []string{"--debug", "--quiet"}, + want: levelsEnabled{ + fatal: false, + error: false, + warn: false, + info: false, + debug: false, + }, + }, + } + // replace the zap logger with a temporary instance + emptyLogger := &zap.Logger{} + reset := zap.ReplaceGlobals(emptyLogger) + defer reset() + for _, tt := range tests { + zap.ReplaceGlobals(emptyLogger) + t.Run(tt.name, func(t *testing.T) { + flagSet := flag.NewFlagSet("test-set", flag.ContinueOnError) + flagSet.Bool("debug", false, "") + flagPtr := flagSet.Bool("quiet", false, "") + flagSet.BoolVar(flagPtr, "q", false, "") + flagSet.BoolVar(flagPtr, "non-interactive", false, "") + + err := flagSet.Parse(tt.flags) + if err != nil { + t.Fatal(err) + } + context := cli.NewContext(nil, flagSet, nil) + + initializeLogger(context) + initializedLogger := zap.L() + if initializedLogger == emptyLogger { + t.Errorf("initializeLogger() did not initialize the logger") + } + // check if the logger is enabled for the desired levels + gotLevels := levelsEnabled{ + fatal: initializedLogger.Core().Enabled(zapcore.FatalLevel), + error: initializedLogger.Core().Enabled(zapcore.ErrorLevel), + warn: initializedLogger.Core().Enabled(zapcore.WarnLevel), + info: initializedLogger.Core().Enabled(zapcore.InfoLevel), + debug: initializedLogger.Core().Enabled(zapcore.DebugLevel), + } + if tt.want != gotLevels { + t.Errorf("Got: %v, but wanted: %v", gotLevels, tt.want) + } + }) + } +} diff --git a/internal/assume_test.go b/internal/assume_test.go index a7f3ddf..79a01c2 100644 --- a/internal/assume_test.go +++ b/internal/assume_test.go @@ -44,10 +44,14 @@ func (m mockSSOClient) GetRoleCredentials(*sso.GetRoleCredentialsInput) (*sso.Ge } func TestAssumeDirectly(t *testing.T) { - + os.Remove(os.TempDir() + "/go-aws-sso.lock") temp, err := os.CreateTemp("", "go-aws-sso-assume-directly_") check(err) CredentialsFilePath = temp.Name() + defer func(path string) { + os.RemoveAll(path) + os.Remove(os.TempDir() + "/go-aws-sso.lock") + }(CredentialsFilePath) dummyInt := int64(132465) dummy := "dummy_assume_directly" @@ -97,7 +101,6 @@ func TestAssumeDirectly(t *testing.T) { AssumeDirectly(oidcClient, ssoClient, ctx) content, _ := os.ReadFile(CredentialsFilePath) - defer os.RemoveAll(CredentialsFilePath) got := string(content) want := "[default]\naws_access_key_id = dummy_assume_directly\naws_secret_access_key = dummy_assume_directly\naws_session_token = dummy_assume_directly\nregion = eu-central-1\n" diff --git a/internal/config_test.go b/internal/config_test.go index ac57755..29384ec 100644 --- a/internal/config_test.go +++ b/internal/config_test.go @@ -4,7 +4,6 @@ import ( "flag" "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" - "io/ioutil" "os" "path" "reflect" @@ -52,7 +51,7 @@ func TestWriteConfig(t *testing.T) { configFile, err := os.Open(tempFile) fail(err, t) - bytes, err := ioutil.ReadFile(configFile.Name()) + bytes, err := os.ReadFile(configFile.Name()) fail(err, t) gotAppConfig := AppConfig{} diff --git a/pkg/sso/file_system.go b/pkg/sso/file_system.go index 4e73c46..38b4dbf 100644 --- a/pkg/sso/file_system.go +++ b/pkg/sso/file_system.go @@ -38,7 +38,7 @@ func ProcessCredentialProcessTemplate(accountId string, roleName string, region exeName, err := os.Executable() check(err) profileTemplate := CredentialsFileTemplate{ - CredentialProcess: fmt.Sprintf("%s assume -a %s -n %s", exeName, accountId, roleName), + CredentialProcess: fmt.Sprintf("%s assume -q -a %s -n %s", exeName, accountId, roleName), Region: region, } return profileTemplate diff --git a/pkg/sso/file_system_test.go b/pkg/sso/file_system_test.go index b344187..344c986 100644 --- a/pkg/sso/file_system_test.go +++ b/pkg/sso/file_system_test.go @@ -3,7 +3,6 @@ package sso import ( "encoding/json" "gopkg.in/ini.v1" - "io/ioutil" "os" "reflect" "testing" @@ -75,7 +74,7 @@ func TestWriteClientInfoToFile(t *testing.T) { } got := ClientInformation{} - content, _ := ioutil.ReadFile(tt.args.dest) + content, _ := os.ReadFile(tt.args.dest) err = json.Unmarshal(content, &got) if err != nil { t.Error(err)