Skip to content

Commit

Permalink
tests/session: Add initial testing for GetSession logic
Browse files Browse the repository at this point in the history
* Remove repetitive error handling for credentials.Get() in GetSession

Test after change
```
> make test
[2443]
go test -timeout=30s -parallel=4 ./...
ok      github.com/hashicorp/aws-sdk-go-base    4.147s
```
  • Loading branch information
nywilken committed Aug 2, 2019
1 parent cc9eb09 commit 99ecdfb
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 41 deletions.
5 changes: 5 additions & 0 deletions awsauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/hashicorp/go-multierror"
)

// GetAccountIDAndPartition loads the AWS AccountID and Partition associated with the authenticated client
func GetAccountIDAndPartition(iamconn *iam.IAM, stsconn *sts.STS, authProviderName string) (string, string, error) {
var accountID, partition string
var err, errors error
Expand Down Expand Up @@ -249,6 +250,10 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) {

log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)

/* TODO [nywilken] determine if we need to persist any custom endpoints for the assumeRoleSession
This makes a call to the AWS do we need to copy the Endpoint Settings?
Or is that not the case when trying to assume a role?
*/
awsConfig := &aws.Config{
Credentials: creds,
Region: aws.String(c.Region),
Expand Down
56 changes: 15 additions & 41 deletions session.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,7 @@ func GetSessionOptions(c *Config) (*session.Options, error) {
return nil, err
}

// Call Get to check for credential provider. If nothing found, we'll get an
// error, and we can present it nicely to the user
cp, err := creds.Get()
if err != nil {
if IsAWSErr(err, "NoCredentialProviders", "") {
// If a profile wasn't specified, the session may still be able to resolve credentials from shared config.
if c.Profile == "" {
sess, err := session.NewSession()
if err != nil {
return nil, errors.New(`No valid credential sources found for AWS Provider.
Please see https://terraform.io/docs/providers/aws/index.html for more information on
providing credentials for the AWS Provider`)
}
_, err = sess.Config.Credentials.Get()
if err != nil {
return nil, errors.New(`No valid credential sources found for AWS Provider.
Please see https://terraform.io/docs/providers/aws/index.html for more information on
providing credentials for the AWS Provider`)
}
log.Printf("[INFO] Using session-derived AWS Auth")
options.Config.Credentials = sess.Config.Credentials
} else {
log.Printf("[INFO] AWS Auth using Profile: %q", c.Profile)
options.Profile = c.Profile
options.SharedConfigState = session.SharedConfigEnable
}
} else {
return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
}
} else {
// add the validated credentials to the session options
log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
options.Config.Credentials = creds
}
options.Config.Credentials = creds

if c.Insecure {
transport := options.Config.HTTPClient.Transport.(*http.Transport)
Expand Down Expand Up @@ -150,6 +117,14 @@ func GetSession(c *Config) (*session.Session, error) {
func GetSessionWithAccountIDAndPartition(c *Config) (*session.Session, string, string, error) {
sess, err := GetSession(c)

/* session testing (can we mock?)
Simple Get Session test from ARN
Simple Get Session test with All skips in Config to get session and partition
Test for Creds validation
Test for Requesting Account ID
Test for bad config
*/

if err != nil {
return nil, "", "", err
}
Expand Down Expand Up @@ -180,15 +155,14 @@ func GetSessionWithAccountIDAndPartition(c *Config) (*session.Session, string, s
}

accountID, partition, err := GetAccountIDAndPartition(iamClient, stsClient, credentialsProviderName)

if err == nil {
return sess, accountID, partition, nil
if err != nil {
return nil, "", "", fmt.Errorf(
`AWS account ID not previously found and failed retrieving via all available methods.
See https://www.terraform.io/docs/providers/aws/index.html#skip_requesting_account_id for workaround and implications.
Errors: %s`, err)
}

return nil, "", "", fmt.Errorf(
"AWS account ID not previously found and failed retrieving via all available methods. "+
"See https://www.terraform.io/docs/providers/aws/index.html#skip_requesting_account_id for workaround and implications. "+
"Errors: %s", err)
return sess, accountID, partition, nil
}

var partition string
Expand Down
123 changes: 123 additions & 0 deletions session_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package awsbase

import (
"testing"
)

/*
Might be worth looking into a few mock configs
StaticProvider
SharedFileProvider
TODO: Validate that session contains the specified configuration overrides.
*/

func TestGetSessionOptions(t *testing.T) {
tt := []struct {
desc string
config *Config
hasError bool
}{
{"UnconfiguredConfig",
&Config{},
true,
},
{"ConfigWithCredentials",
&Config{AccessKey: "MockAccessKey", SecretKey: "MockSecretKey"},
false,
},
{"ConfigWithAllSupportedOptions",
&Config{AccessKey: "MockAccessKey", SecretKey: "MockSecretKey", Insecure: true, DebugLogging: true},
false,
},
}

for _, tc := range tt {
t.Run(tc.desc, func(t *testing.T) {
opts, err := GetSessionOptions(tc.config)
if err != nil && tc.hasError == false {
t.Fatalf("GetSessionOptions(c) resulted in an error %s", err)
}

if opts == nil && tc.hasError == false {
t.Error("GetSessionOptions(...) resulted in a nil set of options")
}
})

}
}

func TestGetSession(t *testing.T) {
_, err := GetSession(&Config{})
if err == nil {
t.Fatal("GetSession(&Config{}) with an empty config should result in an error but got nil")
}

/* Need a test case for getting a session that assumes a role
Setting the AssumeRoleARN field triggers a call to the AWS API in the GetCredentials
function that uses a raw AWS config client that does not contain any of the endpoint overrides
being provided to the awsbase.Config object. I left a TODO in the code to determine if we need to honor
any custom endpoints. Or if we should any calls for assuming a role should always use the default API endpoints.
AssumeRoleARN: "arn:aws:iam::222222222222:user/Alice",
*/
sess, err := GetSession(&Config{
AccessKey: "MockAccessKey",
SecretKey: "MockSecretKey",
SkipCredsValidation: true,
SkipMetadataApiCheck: true,
MaxRetries: 6,
UserAgentProducts: []*UserAgentProduct{{}},
})
if err != nil {
t.Fatalf("GetSession(&Config{...}) should return a valid session, but got the error %s", err)
}

if sess == nil {
t.Error("GetSession(...) resulted in a nil session")
}
}

func TestGetSessionWithAccountIDAndPartition(t *testing.T) {
ts := MockAwsApiServer("STS", []*MockEndpoint{
{
Request: &MockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"},
Response: &MockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"},
},
})
defer ts.Close()

tt := []struct {
desc string
config *Config
expectedAcctID string
expectedPartition string
}{
//{"AssumeRoleARN_Config", &Config{AccessKey: "MockAccessKey", SecretKey: "MockSecretKey", AssumeRoleARN: "arn:aws:iam::222222222222:user/Alice", SkipMetadataApiCheck: true}, "222222222222", "aws"},
{"StandardProvider_Config", &Config{AccessKey: "MockAccessKey", SecretKey: "MockSecretKey", Region: "us-west-2", UserAgentProducts: []*UserAgentProduct{{}}, StsEndpoint: ts.URL}, "222222222222", "aws"},
{"SkipCredsValidation_Config", &Config{AccessKey: "MockAccessKey", SecretKey: "MockSecretKey", Region: "us-west-2", SkipCredsValidation: true, UserAgentProducts: []*UserAgentProduct{{}}, StsEndpoint: ts.URL}, "222222222222", "aws"},
{"SkipRequestingAccountId_Config", &Config{AccessKey: "MockAccessKey", SecretKey: "MockSecretKey", Region: "us-west-2", SkipCredsValidation: true, SkipRequestingAccountId: true, UserAgentProducts: []*UserAgentProduct{{}}, StsEndpoint: ts.URL}, "", "aws"},
}

for _, tc := range tt {
t.Run(tc.desc, func(t *testing.T) {
sess, acctID, part, err := GetSessionWithAccountIDAndPartition(tc.config)
if err != nil {
t.Fatalf("GetSessionWithAccountIDAndPartition(&Config{...}) should return a valid session, but got the error %s", err)
}

if sess == nil {
t.Error("GetSession(c) resulted in a nil session")
}

if acctID != tc.expectedAcctID {
t.Errorf("GetSession(c) returned an incorrect AWS account ID, expected %q but got %q", tc.expectedAcctID, acctID)
}

if part != tc.expectedPartition {
t.Errorf("GetSession(c) returned an incorrect AWS partition, expected %q but got %q", tc.expectedPartition, part)
}
})
}
}

0 comments on commit 99ecdfb

Please sign in to comment.