aws: Refactor API mock helpers (#12769)

This makes helpers generic enough to be useful for any AWS service
This commit is contained in:
Radek Simko 2017-03-16 17:41:57 +00:00 committed by GitHub
parent f1b0c57de3
commit e74449792d
2 changed files with 198 additions and 114 deletions

View File

@ -1,7 +1,6 @@
package aws package aws
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -10,13 +9,9 @@ import (
"net/http/httptest" "net/http/httptest"
"os" "os"
"testing" "testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/sts" "github.com/aws/aws-sdk-go/service/sts"
) )
@ -28,9 +23,14 @@ func TestAWSGetAccountInfo_shouldBeValid_fromEC2Role(t *testing.T) {
awsTs := awsEnv(t) awsTs := awsEnv(t)
defer awsTs() defer awsTs()
iamEndpoints := []*iamEndpoint{} closeEmpty, emptySess, err := getMockedAwsApiSession("zero", []*awsMockEndpoint{})
ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) defer closeEmpty()
defer ts() if err != nil {
t.Fatal(err)
}
iamConn := iam.New(emptySess)
stsConn := sts.New(emptySess)
part, id, err := GetAccountInfo(iamConn, stsConn, ec2rolecreds.ProviderName) part, id, err := GetAccountInfo(iamConn, stsConn, ec2rolecreds.ProviderName)
if err != nil { if err != nil {
@ -55,14 +55,24 @@ func TestAWSGetAccountInfo_shouldBeValid_EC2RoleHasPriority(t *testing.T) {
awsTs := awsEnv(t) awsTs := awsEnv(t)
defer awsTs() defer awsTs()
iamEndpoints := []*iamEndpoint{ iamEndpoints := []*awsMockEndpoint{
{ {
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, Response: &awsMockResponse{200, iamResponse_GetUser_valid, "text/xml"},
}, },
} }
ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints)
defer ts() defer closeIam()
if err != nil {
t.Fatal(err)
}
iamConn := iam.New(iamSess)
closeSts, stsSess, err := getMockedAwsApiSession("STS", []*awsMockEndpoint{})
defer closeSts()
if err != nil {
t.Fatal(err)
}
stsConn := sts.New(stsSess)
part, id, err := GetAccountInfo(iamConn, stsConn, ec2rolecreds.ProviderName) part, id, err := GetAccountInfo(iamConn, stsConn, ec2rolecreds.ProviderName)
if err != nil { if err != nil {
@ -81,15 +91,26 @@ func TestAWSGetAccountInfo_shouldBeValid_EC2RoleHasPriority(t *testing.T) {
} }
func TestAWSGetAccountInfo_shouldBeValid_fromIamUser(t *testing.T) { func TestAWSGetAccountInfo_shouldBeValid_fromIamUser(t *testing.T) {
iamEndpoints := []*iamEndpoint{ iamEndpoints := []*awsMockEndpoint{
{ {
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, Response: &awsMockResponse{200, iamResponse_GetUser_valid, "text/xml"},
}, },
} }
ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints)
defer ts() defer closeIam()
if err != nil {
t.Fatal(err)
}
closeSts, stsSess, err := getMockedAwsApiSession("STS", []*awsMockEndpoint{})
defer closeSts()
if err != nil {
t.Fatal(err)
}
iamConn := iam.New(iamSess)
stsConn := sts.New(stsSess)
part, id, err := GetAccountInfo(iamConn, stsConn, "") part, id, err := GetAccountInfo(iamConn, stsConn, "")
if err != nil { if err != nil {
@ -108,18 +129,32 @@ func TestAWSGetAccountInfo_shouldBeValid_fromIamUser(t *testing.T) {
} }
func TestAWSGetAccountInfo_shouldBeValid_fromGetCallerIdentity(t *testing.T) { func TestAWSGetAccountInfo_shouldBeValid_fromGetCallerIdentity(t *testing.T) {
iamEndpoints := []*iamEndpoint{ iamEndpoints := []*awsMockEndpoint{
{ {
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, Response: &awsMockResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
},
{
Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"},
Response: &iamResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"},
}, },
} }
ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints)
defer ts() defer closeIam()
if err != nil {
t.Fatal(err)
}
stsEndpoints := []*awsMockEndpoint{
{
Request: &awsMockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"},
Response: &awsMockResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"},
},
}
closeSts, stsSess, err := getMockedAwsApiSession("STS", stsEndpoints)
defer closeSts()
if err != nil {
t.Fatal(err)
}
iamConn := iam.New(iamSess)
stsConn := sts.New(stsSess)
part, id, err := GetAccountInfo(iamConn, stsConn, "") part, id, err := GetAccountInfo(iamConn, stsConn, "")
if err != nil { if err != nil {
@ -138,22 +173,36 @@ func TestAWSGetAccountInfo_shouldBeValid_fromGetCallerIdentity(t *testing.T) {
} }
func TestAWSGetAccountInfo_shouldBeValid_fromIamListRoles(t *testing.T) { func TestAWSGetAccountInfo_shouldBeValid_fromIamListRoles(t *testing.T) {
iamEndpoints := []*iamEndpoint{ iamEndpoints := []*awsMockEndpoint{
{ {
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, Response: &awsMockResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
}, },
{ {
Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, Request: &awsMockRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
Response: &iamResponse{403, stsResponse_GetCallerIdentity_unauthorized, "text/xml"}, Response: &awsMockResponse{200, iamResponse_ListRoles_valid, "text/xml"},
},
{
Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"},
}, },
} }
ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints)
defer ts() defer closeIam()
if err != nil {
t.Fatal(err)
}
stsEndpoints := []*awsMockEndpoint{
{
Request: &awsMockRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"},
Response: &awsMockResponse{403, stsResponse_GetCallerIdentity_unauthorized, "text/xml"},
},
}
closeSts, stsSess, err := getMockedAwsApiSession("STS", stsEndpoints)
defer closeSts()
if err != nil {
t.Fatal(err)
}
iamConn := iam.New(iamSess)
stsConn := sts.New(stsSess)
part, id, err := GetAccountInfo(iamConn, stsConn, "") part, id, err := GetAccountInfo(iamConn, stsConn, "")
if err != nil { if err != nil {
@ -172,18 +221,30 @@ func TestAWSGetAccountInfo_shouldBeValid_fromIamListRoles(t *testing.T) {
} }
func TestAWSGetAccountInfo_shouldBeValid_federatedRole(t *testing.T) { func TestAWSGetAccountInfo_shouldBeValid_federatedRole(t *testing.T) {
iamEndpoints := []*iamEndpoint{ iamEndpoints := []*awsMockEndpoint{
{ {
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
Response: &iamResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"}, Response: &awsMockResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"},
}, },
{ {
Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, Request: &awsMockRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"}, Response: &awsMockResponse{200, iamResponse_ListRoles_valid, "text/xml"},
}, },
} }
ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints)
defer ts() defer closeIam()
if err != nil {
t.Fatal(err)
}
closeSts, stsSess, err := getMockedAwsApiSession("STS", []*awsMockEndpoint{})
defer closeSts()
if err != nil {
t.Fatal(err)
}
iamConn := iam.New(iamSess)
stsConn := sts.New(stsSess)
part, id, err := GetAccountInfo(iamConn, stsConn, "") part, id, err := GetAccountInfo(iamConn, stsConn, "")
if err != nil { if err != nil {
@ -202,18 +263,30 @@ func TestAWSGetAccountInfo_shouldBeValid_federatedRole(t *testing.T) {
} }
func TestAWSGetAccountInfo_shouldError_unauthorizedFromIam(t *testing.T) { func TestAWSGetAccountInfo_shouldError_unauthorizedFromIam(t *testing.T) {
iamEndpoints := []*iamEndpoint{ iamEndpoints := []*awsMockEndpoint{
{ {
Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, Response: &awsMockResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
}, },
{ {
Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, Request: &awsMockRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
Response: &iamResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"}, Response: &awsMockResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"},
}, },
} }
ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints)
defer ts() defer closeIam()
if err != nil {
t.Fatal(err)
}
closeSts, stsSess, err := getMockedAwsApiSession("STS", []*awsMockEndpoint{})
defer closeSts()
if err != nil {
t.Fatal(err)
}
iamConn := iam.New(iamSess)
stsConn := sts.New(stsSess)
part, id, err := GetAccountInfo(iamConn, stsConn, "") part, id, err := GetAccountInfo(iamConn, stsConn, "")
if err == nil { if err == nil {
@ -697,51 +770,6 @@ func invalidAwsEnv(t *testing.T) func() {
return ts.Close return ts.Close
} }
// getMockedAwsIamStsApi establishes a httptest server to simulate behaviour
// of a real AWS' IAM & STS server
func getMockedAwsIamStsApi(endpoints []*iamEndpoint) (func(), *iam.IAM, *sts.STS) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
requestBody := buf.String()
log.Printf("[DEBUG] Received API %q request to %q: %s",
r.Method, r.RequestURI, requestBody)
for _, e := range endpoints {
if r.Method == e.Request.Method && r.RequestURI == e.Request.Uri && requestBody == e.Request.Body {
log.Printf("[DEBUG] Mock API responding with %d: %s", e.Response.StatusCode, e.Response.Body)
w.WriteHeader(e.Response.StatusCode)
w.Header().Set("Content-Type", e.Response.ContentType)
w.Header().Set("X-Amzn-Requestid", "1b206dd1-f9a8-11e5-becf-051c60f11c4a")
w.Header().Set("Date", time.Now().Format(time.RFC1123))
fmt.Fprintln(w, e.Response.Body)
return
}
}
w.WriteHeader(400)
return
}))
sc := awsCredentials.NewStaticCredentials("accessKey", "secretKey", "")
sess, err := session.NewSession(&aws.Config{
Credentials: sc,
Region: aws.String("us-east-1"),
Endpoint: aws.String(ts.URL),
CredentialsChainVerboseErrors: aws.Bool(true),
})
if err != nil {
panic(fmt.Sprintf("Error creating AWS Session: %s", err))
}
iamConn := iam.New(sess)
stsConn := sts.New(sess)
return ts.Close, iamConn, stsConn
}
func getEnv() *currentEnv { func getEnv() *currentEnv {
// Grab any existing AWS keys and preserve. In some tests we'll unset these, so // Grab any existing AWS keys and preserve. In some tests we'll unset these, so
// we need to have them and restore them after // we need to have them and restore them after
@ -790,23 +818,6 @@ const metadataApiRoutes = `
} }
` `
type iamEndpoint struct {
Request *iamRequest
Response *iamResponse
}
type iamRequest struct {
Method string
Uri string
Body string
}
type iamResponse struct {
StatusCode int
Body string
ContentType string
}
const iamResponse_GetUser_valid = `<GetUserResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/"> const iamResponse_GetUser_valid = `<GetUserResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
<GetUserResult> <GetUserResult>
<User> <User>

View File

@ -0,0 +1,73 @@
package aws
import (
"bytes"
"fmt"
"log"
"net/http"
"net/http/httptest"
"time"
"github.com/aws/aws-sdk-go/aws"
awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
)
// getMockedAwsApiSession establishes a httptest server to simulate behaviour
// of a real AWS API server
func getMockedAwsApiSession(svcName string, endpoints []*awsMockEndpoint) (func(), *session.Session, error) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
requestBody := buf.String()
log.Printf("[DEBUG] Received %s API %q request to %q: %s",
svcName, r.Method, r.RequestURI, requestBody)
for _, e := range endpoints {
if r.Method == e.Request.Method && r.RequestURI == e.Request.Uri && requestBody == e.Request.Body {
log.Printf("[DEBUG] Mocked %s API responding with %d: %s",
svcName, e.Response.StatusCode, e.Response.Body)
w.WriteHeader(e.Response.StatusCode)
w.Header().Set("Content-Type", e.Response.ContentType)
w.Header().Set("X-Amzn-Requestid", "1b206dd1-f9a8-11e5-becf-051c60f11c4a")
w.Header().Set("Date", time.Now().Format(time.RFC1123))
fmt.Fprintln(w, e.Response.Body)
return
}
}
w.WriteHeader(400)
return
}))
sc := awsCredentials.NewStaticCredentials("accessKey", "secretKey", "")
sess, err := session.NewSession(&aws.Config{
Credentials: sc,
Region: aws.String("us-east-1"),
Endpoint: aws.String(ts.URL),
CredentialsChainVerboseErrors: aws.Bool(true),
})
return ts.Close, sess, err
}
type awsMockEndpoint struct {
Request *awsMockRequest
Response *awsMockResponse
}
type awsMockRequest struct {
Method string
Uri string
Body string
}
type awsMockResponse struct {
StatusCode int
Body string
ContentType string
}