Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Go 1.13 compatible error types #42

Merged
merged 2 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 4 additions & 25 deletions awsauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,11 @@ import (
)

const (
// errMsgNoValidCredentialSources error getting credentials
errMsgNoValidCredentialSources = `No valid credential sources found for AWS.
Please see https://terraform.io/docs/providers/aws/index.html for more information on
providing credentials for the AWS Provider and Backend.`

// Default amount of time for EC2/ECS metadata client operations.
// Keep this value low to prevent long delays in non-EC2/ECS environments.
DefaultMetadataClientTimeout = 100 * time.Millisecond
)

var (
// ErrNoValidCredentialSources indicates that no credentials source could be found
ErrNoValidCredentialSources = errNoValidCredentialSources()
)

func errNoValidCredentialSources() error { return errors.New(errMsgNoValidCredentialSources) }

// GetAccountIDAndPartition gets the account ID and associated partition.
func GetAccountIDAndPartition(iamconn *iam.IAM, stsconn *sts.STS, authProviderName string) (string, string, error) {
var accountID, partition string
Expand Down Expand Up @@ -197,15 +185,15 @@ func GetCredentialsFromSession(c *Config) (*awsCredentials.Credentials, error) {
sess, err := session.NewSessionWithOptions(*options)
if err != nil {
if IsAWSErr(err, "NoCredentialProviders", "") {
return nil, ErrNoValidCredentialSources
return nil, c.NewNoValidCredentialSourcesError(err)
}
return nil, fmt.Errorf("Error creating AWS session: %w", err)
}

creds := sess.Config.Credentials
cp, err := sess.Config.Credentials.Get()
if err != nil {
return nil, ErrNoValidCredentialSources
return nil, c.NewNoValidCredentialSourcesError(err)
}

log.Printf("[INFO] Successfully derived credentials from session")
Expand Down Expand Up @@ -281,7 +269,7 @@ func GetCredentialsFromMetadata(c *Config) (*awsCredentials.Credentials, error)
cp, err := creds.Get()
if err != nil {
if IsAWSErr(err, "NoCredentialProviders", "") {
return nil, ErrNoValidCredentialSources
return nil, c.NewNoValidCredentialSourcesError(err)
}
return nil, fmt.Errorf("Error deriving credentials from metadata: %s", err)
}
Expand Down Expand Up @@ -417,16 +405,7 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) {
assumeRoleCreds := awsCredentials.NewChainCredentials(providers)
_, err = assumeRoleCreds.Get()
if err != nil {
if IsAWSErr(err, "NoCredentialProviders", "") {
return nil, fmt.Errorf("The role %q cannot be assumed.\n\n"+
" There are a number of possible causes of this - the most common are:\n"+
" * The credentials used in order to assume the role are invalid\n"+
" * The credentials do not have appropriate permission to assume the role\n"+
" * The role ARN is not valid",
c.AssumeRoleARN)
}

return nil, fmt.Errorf("Error loading credentials for AWS Provider: %w", err)
return nil, c.NewCannotAssumeRoleError(err)
}

return assumeRoleCreds, nil
Expand Down
5 changes: 3 additions & 2 deletions awsauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ func TestAWSGetCredentials_shouldErrorWhenBlank(t *testing.T) {
cfg := Config{}
_, err := GetCredentials(&cfg)

if err != ErrNoValidCredentialSources {
if !IsNoValidCredentialSourcesError(err) {
t.Fatalf("Unexpected error: %s", err)
}

Expand Down Expand Up @@ -592,7 +592,8 @@ func TestAWSGetCredentials_shouldErrorWithInvalidEndpoint(t *testing.T) {
defer ts()

_, err := GetCredentials(&Config{})
if err != ErrNoValidCredentialSources {

if !IsNoValidCredentialSourcesError(err) {
t.Fatalf("Error gettings creds: %s", err)
}

Expand Down
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type Config struct {
AssumeRoleSessionName string
AssumeRoleTags map[string]string
AssumeRoleTransitiveTagKeys []string
CallerDocumentationURL string
CallerName string
CredsFilename string
DebugLogging bool
IamEndpoint string
Expand Down
76 changes: 76 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package awsbase

import (
"errors"
"fmt"
)

// CannotAssumeRoleError occurs when AssumeRole cannot complete.
type CannotAssumeRoleError struct {
Config *Config
Err error
}

func (e CannotAssumeRoleError) Error() string {
if e.Config == nil {
return fmt.Sprintf("cannot assume role: %s", e.Err)
}

return fmt.Sprintf(`IAM Role (%s) cannot be assumed.
There are a number of possible causes of this - the most common are:
* The credentials used in order to assume the role are invalid
* The credentials do not have appropriate permission to assume the role
* The role ARN is not valid
Error: %s
`, e.Config.AssumeRoleARN, e.Err)
}

func (e CannotAssumeRoleError) Unwrap() error {
return e.Err
}

// IsCannotAssumeRoleError returns true if the error contains the CannotAssumeRoleError type.
func IsCannotAssumeRoleError(err error) bool {
var e CannotAssumeRoleError
return errors.As(err, &e)
}

func (c *Config) NewCannotAssumeRoleError(err error) CannotAssumeRoleError {
return CannotAssumeRoleError{Config: c, Err: err}
}

// NoValidCredentialSourcesError occurs when all credential lookup methods have been exhausted without results.
type NoValidCredentialSourcesError struct {
Config *Config
Err error
}

func (e NoValidCredentialSourcesError) Error() string {
if e.Config == nil {
return fmt.Sprintf("no valid credential sources found: %s", e.Err)
}

return fmt.Sprintf(`no valid credential sources for %s found.
Please see %s
for more information about providing credentials.
Error: %s
`, e.Config.CallerName, e.Config.CallerDocumentationURL, e.Err)
}

func (e NoValidCredentialSourcesError) Unwrap() error {
return e.Err
}

// IsNoValidCredentialSourcesError returns true if the error contains the NoValidCredentialSourcesError type.
func IsNoValidCredentialSourcesError(err error) bool {
var e NoValidCredentialSourcesError
return errors.As(err, &e)
}

func (c *Config) NewNoValidCredentialSourcesError(err error) NoValidCredentialSourcesError {
return NoValidCredentialSourcesError{Config: c, Err: err}
}
2 changes: 1 addition & 1 deletion session.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func GetSession(c *Config) (*session.Session, error) {
sess, err := session.NewSessionWithOptions(*options)
if err != nil {
if IsAWSErr(err, "NoCredentialProviders", "") {
return nil, ErrNoValidCredentialSources
return nil, c.NewNoValidCredentialSourcesError(err)
}
return nil, fmt.Errorf("Error creating AWS session: %w", err)
}
Expand Down
55 changes: 27 additions & 28 deletions session_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package awsbase

import (
"errors"
"io/ioutil"
"os"
"reflect"
Expand Down Expand Up @@ -79,7 +78,7 @@ func TestGetSession(t *testing.T) {
Config: &Config{},
Description: "no configuration or credentials",
ExpectedError: func(err error) bool {
return errors.Is(err, ErrNoValidCredentialSources)
return IsNoValidCredentialSourcesError(err)
},
},
{
Expand Down Expand Up @@ -885,7 +884,7 @@ aws_secret_access_key = DefaultSharedCredentialsSecretKey
},
Description: "assume role error",
ExpectedError: func(err error) bool {
return strings.Contains(err.Error(), "The role \"arn:aws:iam::555555555555:role/AssumeRole\" cannot be assumed.")
return IsCannotAssumeRoleError(err)
},
ExpectedRegion: "us-east-1",
MockStsEndpoints: []*MockEndpoint{
Expand Down Expand Up @@ -923,9 +922,7 @@ aws_secret_access_key = DefaultSharedCredentialsSecretKey
},
Description: "session creation error",
ExpectedError: func(err error) bool {
// TODO: Return wrapped error
//return IsAWSErr(err, "CredentialRequiresARNError", "")
return err.Error() == errMsgNoValidCredentialSources
return IsAWSErr(err, "NoCredentialProviders", "")
},
SharedConfigurationFile: `
[profile SharedConfigurationProfile]
Expand Down Expand Up @@ -1050,6 +1047,7 @@ source_profile = SourceSharedCredentials
t.Fatalf("unexpected GetSession() error: %s", err)
}

t.Logf("received expected error: %s", err)
return
}

Expand Down Expand Up @@ -1091,23 +1089,23 @@ func TestGetSessionWithAccountIDAndPartition(t *testing.T) {
config *Config
expectedAcctID string
expectedPartition string
expectedError string
expectedError bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}{
{"StandardProvider_Config", &Config{
AccessKey: "MockAccessKey",
SecretKey: "MockSecretKey",
Region: "us-west-2",
UserAgentProducts: []*UserAgentProduct{{}},
StsEndpoint: ts.URL},
"222222222222", "aws", ""},
"222222222222", "aws", false},
{"SkipCredsValidation_Config", &Config{
AccessKey: "MockAccessKey",
SecretKey: "MockSecretKey",
Region: "us-west-2",
SkipCredsValidation: true,
UserAgentProducts: []*UserAgentProduct{{}},
StsEndpoint: ts.URL},
"222222222222", "aws", ""},
"222222222222", "aws", false},
{"SkipRequestingAccountId_Config", &Config{
AccessKey: "MockAccessKey",
SecretKey: "MockSecretKey",
Expand All @@ -1116,7 +1114,7 @@ func TestGetSessionWithAccountIDAndPartition(t *testing.T) {
SkipRequestingAccountId: true,
UserAgentProducts: []*UserAgentProduct{{}},
StsEndpoint: ts.URL},
"", "aws", ""},
"", "aws", false},
// {"WithAssumeRole", &Config{
// AccessKey: "MockAccessKey",
// SecretKey: "MockSecretKey",
Expand All @@ -1130,7 +1128,7 @@ func TestGetSessionWithAccountIDAndPartition(t *testing.T) {
Region: "us-west-2",
UserAgentProducts: []*UserAgentProduct{{}},
StsEndpoint: ts.URL},
"", "", "No valid credential sources found for AWS."},
"", "", true},
}

for _, testCase := range testCases {
Expand All @@ -1139,27 +1137,28 @@ func TestGetSessionWithAccountIDAndPartition(t *testing.T) {
t.Run(tc.desc, func(t *testing.T) {
sess, acctID, part, err := GetSessionWithAccountIDAndPartition(tc.config)
if err != nil {
if tc.expectedError == "" {
t.Fatalf("GetSessionWithAccountIDAndPartition(&Config{...}) should return a valid session, but got the error %s", err)
} else {
if !strings.Contains(err.Error(), tc.expectedError) {
t.Fatalf("GetSession(c) expected error %q and got %q", tc.expectedError, err)
} else {
t.Logf("Found error message %q", err)
}
}
} else {
if sess == nil {
t.Error("GetSession(c) resulted in a nil session")
if !tc.expectedError {
t.Fatalf("expected no error, got: %s", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the more concise errors here

}

if acctID != tc.expectedAcctID {
t.Errorf("GetSession(c) returned an incorrect AWS account ID, expected %q but got %q", tc.expectedAcctID, acctID)
if !IsNoValidCredentialSourcesError(err) {
t.Fatalf("expected no valid credential sources error, got: %s", err)
}

if part != tc.expectedPartition {
t.Errorf("GetSession(c) returned an incorrect AWS partition, expected %q but got %q", tc.expectedPartition, part)
}
t.Logf("received expected error: %s", err)
return
}

if sess == nil {
t.Error("unexpected empty session")
}

if acctID != tc.expectedAcctID {
t.Errorf("expected account ID (%s), got: %s", tc.expectedAcctID, acctID)
}

if part != tc.expectedPartition {
t.Errorf("expected partition (%s), got: %s", tc.expectedPartition, part)
}
})
}
Expand Down