Skip to content

Commit

Permalink
Introduce Go 1.13 compatible error types
Browse files Browse the repository at this point in the history
Reference: #24

Callers can configure their name and documentation URL to properly customize the error messaging.
  • Loading branch information
bflad committed Jun 3, 2020
1 parent 54bff5e commit f6fd304
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 55 deletions.
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
8 changes: 6 additions & 2 deletions awsauth_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package awsbase

import (
"errors"
"io/ioutil"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -434,7 +435,8 @@ func TestAWSGetCredentials_shouldErrorWhenBlank(t *testing.T) {
cfg := Config{}
_, err := GetCredentials(&cfg)

if err != ErrNoValidCredentialSources {
var credentialError NoValidCredentialSourcesError
if !errors.As(err, &credentialError) {
t.Fatalf("Unexpected error: %s", err)
}

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

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

var credentialError NoValidCredentialSourcesError
if !errors.As(err, &credentialError) {
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
63 changes: 63 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package awsbase

import (
"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
}

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
}

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
57 changes: 30 additions & 27 deletions session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ func TestGetSession(t *testing.T) {
Config: &Config{},
Description: "no configuration or credentials",
ExpectedError: func(err error) bool {
return errors.Is(err, ErrNoValidCredentialSources)
var credentialError NoValidCredentialSourcesError
return errors.As(err, &credentialError)
},
},
{
Expand Down Expand Up @@ -885,7 +886,8 @@ 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.")
var assumeRoleError CannotAssumeRoleError
return errors.As(err, &assumeRoleError)
},
ExpectedRegion: "us-east-1",
MockStsEndpoints: []*MockEndpoint{
Expand Down Expand Up @@ -923,9 +925,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 +1050,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 +1092,23 @@ func TestGetSessionWithAccountIDAndPartition(t *testing.T) {
config *Config
expectedAcctID string
expectedPartition string
expectedError string
expectedError bool
}{
{"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 +1117,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 +1131,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 +1140,29 @@ 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)
}

if acctID != tc.expectedAcctID {
t.Errorf("GetSession(c) returned an incorrect AWS account ID, expected %q but got %q", tc.expectedAcctID, acctID)
var credentialError NoValidCredentialSourcesError
if !errors.As(err, &credentialError) {
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

0 comments on commit f6fd304

Please sign in to comment.