Skip to content

Commit

Permalink
awsbase: Add unit testing for session code base (#9)
Browse files Browse the repository at this point in the history
* Bump Go version to 1.12
* Upgrade [email protected]
  • Loading branch information
nywilken authored and aeschright committed Oct 2, 2019
1 parent 8fac286 commit cf17a84
Show file tree
Hide file tree
Showing 189 changed files with 61,824 additions and 178 deletions.
14 changes: 8 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
dist: xenial
language: go
go:
- "1.11.x"
- "1.12.x"

env:
GOFLAGS=-mod=vendor

matrix:
fast_finish: true
allow_failures:
- go: tip

install:
- make tools

script:
- make lint
- make test
- go test -timeout=30s -parallel=4 -v ./...

branches:
only:
- master
matrix:
fast_finish: true
allow_failures:
- go: tip
4 changes: 4 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
default: test lint

fmt:
@echo "==> Fixing source code with gofmt..."
gofmt -s -w ./

lint:
@echo "==> Checking source code against linters..."
@golangci-lint run ./...
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ An opinionated [AWS Go SDK](https://github.com/aws/aws-sdk-go) library for consi

## Requirements

- [Go](https://golang.org/doc/install) 1.11.4+
- [Go](https://golang.org/doc/install) 1.12

## Development

Expand Down
2 changes: 1 addition & 1 deletion awsauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) {
return awsCredentials.NewChainCredentials(providers), nil
}

// Otherwise we need to construct and STS client with the main credentials, and verify
// Otherwise we need to construct an STS client with the main credentials, and verify
// that we can assume the defined role.
log.Printf("[INFO] Attempting to AssumeRole %s (SessionName: %q, ExternalId: %q, Policy: %q)",
c.AssumeRoleARN, c.AssumeRoleSessionName, c.AssumeRoleExternalID, c.AssumeRolePolicy)
Expand Down
196 changes: 30 additions & 166 deletions awsauth_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package awsbase

import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"os"
Expand All @@ -19,7 +17,7 @@ func TestGetAccountIDAndPartition(t *testing.T) {
var testCases = []struct {
Description string
AuthProviderName string
EC2MetadataEndpoints []*endpoint
EC2MetadataEndpoints []*MetadataResponse
IAMEndpoints []*MockEndpoint
STSEndpoints []*MockEndpoint
ErrCount int
Expand Down Expand Up @@ -420,25 +418,27 @@ func TestAWSParseAccountIDAndPartitionFromARN(t *testing.T) {
}
}

func TestAWSGetCredentials_shouldError(t *testing.T) {
func TestAWSGetCredentials_shouldErrorWhenBlank(t *testing.T) {
resetEnv := unsetEnv(t)
defer resetEnv()
cfg := Config{}

cfg := Config{}
c, err := GetCredentials(&cfg)
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() != "NoCredentialProviders" {
t.Fatal("Expected NoCredentialProviders error")
}

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

_, err = c.Get()
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() != "NoCredentialProviders" {
t.Fatal("Expected NoCredentialProviders error")
}
} else {
t.Fatal("Expected AWS error")
}
if err == nil {
t.Fatal("Expected an error with empty env, keys, and IAM in AWS Config")
t.Fatal("Expected an error given empty env, keys, and IAM in AWS Config")
}
}

Expand Down Expand Up @@ -747,6 +747,17 @@ func TestAWSGetCredentials_shouldBeENV(t *testing.T) {
}
}

// invalidAwsEnv establishes a httptest server to simulate behaviour
// when endpoint doesn't respond as expected
func invalidAwsEnv(t *testing.T) func() {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(400)
}))

os.Setenv("AWS_METADATA_URL", ts.URL+"/latest")
return ts.Close
}

// unsetEnv unsets environment variables for testing a "clean slate" with no
// credentials in the environment
func unsetEnv(t *testing.T) func() {
Expand All @@ -768,6 +779,10 @@ func unsetEnv(t *testing.T) func() {
if err := os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE"); err != nil {
t.Fatalf("Error unsetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
}
// The Shared Credentials Provider has a very reasonable fallback option of
// checking the user's home directory for credentials, which may create
// unexpected results for users running these tests
os.Setenv("HOME", "/dev/null")

return func() {
// re-set all the envs we unset above
Expand All @@ -786,6 +801,9 @@ func unsetEnv(t *testing.T) func() {
if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", e.CredsFilename); err != nil {
t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
}
if err := os.Setenv("HOME", e.Home); err != nil {
t.Fatalf("Error resetting env var HOME: %s", err)
}
}
}

Expand Down Expand Up @@ -828,40 +846,6 @@ func setEnv(s string, t *testing.T) func() {
}
}

// awsMetadataApiMock establishes a httptest server to mock out the internal AWS Metadata
// service. IAM Credentials are retrieved by the EC2RoleProvider, which makes
// API calls to this internal URL. By replacing the server with a test server,
// we can simulate an AWS environment
func awsMetadataApiMock(endpoints []*endpoint) func() {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.Header().Add("Server", "MockEC2")
log.Printf("[DEBUG] Mocker server received request to %q", r.RequestURI)
for _, e := range endpoints {
if r.RequestURI == e.Uri {
fmt.Fprintln(w, e.Body)
w.WriteHeader(200)
return
}
}
w.WriteHeader(400)
}))

os.Setenv("AWS_METADATA_URL", ts.URL+"/latest")
return ts.Close
}

// invalidAwsEnv establishes a httptest server to simulate behaviour
// when endpoint doesn't respond as expected
func invalidAwsEnv(t *testing.T) func() {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(400)
}))

os.Setenv("AWS_METADATA_URL", ts.URL+"/latest")
return ts.Close
}

func getEnv() *currentEnv {
// Grab any existing AWS keys and preserve. In some tests we'll unset these, so
// we need to have them and restore them after
Expand All @@ -871,131 +855,11 @@ func getEnv() *currentEnv {
Token: os.Getenv("AWS_SESSION_TOKEN"),
Profile: os.Getenv("AWS_PROFILE"),
CredsFilename: os.Getenv("AWS_SHARED_CREDENTIALS_FILE"),
Home: os.Getenv("HOME"),
}
}

// struct to preserve the current environment
type currentEnv struct {
Key, Secret, Token, Profile, CredsFilename string
}

type endpoint struct {
Uri string `json:"uri"`
Body string `json:"body"`
}

var ec2metadata_instanceIdEndpoint = &endpoint{
Uri: "/latest/meta-data/instance-id",
Body: "mock-instance-id",
}

var ec2metadata_securityCredentialsEndpoints = []*endpoint{
{
Uri: "/latest/meta-data/iam/security-credentials/",
Body: "test_role",
},
{
Uri: "/latest/meta-data/iam/security-credentials/test_role",
Body: "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"somekey\",\"SecretAccessKey\":\"somesecret\",\"Token\":\"sometoken\"}",
},
Key, Secret, Token, Profile, CredsFilename, Home string
}

var ec2metadata_iamInfoEndpoint = &endpoint{
Uri: "/latest/meta-data/iam/info",
Body: "{\"Code\": \"Success\",\"LastUpdated\": \"2016-03-17T12:27:32Z\",\"InstanceProfileArn\": \"arn:aws:iam::000000000000:instance-profile/my-instance-profile\",\"InstanceProfileId\": \"AIPAABCDEFGHIJKLMN123\"}",
}

const ec2metadata_iamInfoEndpoint_expectedAccountID = `000000000000`
const ec2metadata_iamInfoEndpoint_expectedPartition = `aws`

const iamResponse_GetUser_valid = `<GetUserResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<GetUserResult>
<User>
<UserId>AIDACKCEVSQ6C2EXAMPLE</UserId>
<Path>/division_abc/subdivision_xyz/</Path>
<UserName>Bob</UserName>
<Arn>arn:aws:iam::111111111111:user/division_abc/subdivision_xyz/Bob</Arn>
<CreateDate>2013-10-02T17:01:44Z</CreateDate>
<PasswordLastUsed>2014-10-10T14:37:51Z</PasswordLastUsed>
</User>
</GetUserResult>
<ResponseMetadata>
<RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
</ResponseMetadata>
</GetUserResponse>`

const iamResponse_GetUser_valid_expectedAccountID = `111111111111`
const iamResponse_GetUser_valid_expectedPartition = `aws`

const iamResponse_GetUser_unauthorized = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<Error>
<Type>Sender</Type>
<Code>AccessDenied</Code>
<Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:GetUser on resource: arn:aws:iam::123456789012:user/Bob</Message>
</Error>
<RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
</ErrorResponse>`

const stsResponse_GetCallerIdentity_valid = `<GetCallerIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<GetCallerIdentityResult>
<Arn>arn:aws:iam::222222222222:user/Alice</Arn>
<UserId>AKIAI44QH8DHBEXAMPLE</UserId>
<Account>222222222222</Account>
</GetCallerIdentityResult>
<ResponseMetadata>
<RequestId>01234567-89ab-cdef-0123-456789abcdef</RequestId>
</ResponseMetadata>
</GetCallerIdentityResponse>`

const stsResponse_GetCallerIdentity_valid_expectedAccountID = `222222222222`
const stsResponse_GetCallerIdentity_valid_expectedPartition = `aws`

const stsResponse_GetCallerIdentity_unauthorized = `<ErrorResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<Error>
<Type>Sender</Type>
<Code>AccessDenied</Code>
<Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: sts:GetCallerIdentity</Message>
</Error>
<RequestId>01234567-89ab-cdef-0123-456789abcdef</RequestId>
</ErrorResponse>`

const iamResponse_GetUser_federatedFailure = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<Error>
<Type>Sender</Type>
<Code>ValidationError</Code>
<Message>Must specify userName when calling with non-User credentials</Message>
</Error>
<RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
</ErrorResponse>`

const iamResponse_ListRoles_valid = `<ListRolesResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<ListRolesResult>
<IsTruncated>true</IsTruncated>
<Marker>AWceSSsKsazQ4IEplT9o4hURCzBs00iavlEvEXAMPLE</Marker>
<Roles>
<member>
<Path>/</Path>
<AssumeRolePolicyDocument>%7B%22Version%22%3A%222008-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22ec2.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D</AssumeRolePolicyDocument>
<RoleId>AROACKCEVSQ6C2EXAMPLE</RoleId>
<RoleName>elasticbeanstalk-role</RoleName>
<Arn>arn:aws:iam::444444444444:role/elasticbeanstalk-role</Arn>
<CreateDate>2013-10-02T17:01:44Z</CreateDate>
</member>
</Roles>
</ListRolesResult>
<ResponseMetadata>
<RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
</ResponseMetadata>
</ListRolesResponse>`

const iamResponse_ListRoles_valid_expectedAccountID = `444444444444`
const iamResponse_ListRoles_valid_expectedPartition = `aws`

const iamResponse_ListRoles_unauthorized = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<Error>
<Type>Sender</Type>
<Code>AccessDenied</Code>
<Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:ListRoles on resource: arn:aws:iam::123456789012:role/</Message>
</Error>
<RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
</ErrorResponse>`
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module github.com/hashicorp/aws-sdk-go-base

require (
github.com/aws/aws-sdk-go v1.16.36
github.com/aws/aws-sdk-go v1.25.3
github.com/hashicorp/go-cleanhttp v0.5.0
github.com/hashicorp/go-multierror v1.0.0
github.com/stretchr/testify v1.3.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/aws/aws-sdk-go v1.16.36 h1:POeH34ZME++pr7GBGh+ZO6Y5kOwSMQpqp5BGUgooJ6k=
github.com/aws/aws-sdk-go v1.16.36/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.3 h1:uM16hIw9BotjZKMZlX05SN2EFtaWfi/NonPKIARiBLQ=
github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
Expand Down
Loading

0 comments on commit cf17a84

Please sign in to comment.