From e74449792d53270ea34e3349c2ecdae2de0acc02 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 16 Mar 2017 17:41:57 +0000 Subject: [PATCH] aws: Refactor API mock helpers (#12769) This makes helpers generic enough to be useful for any AWS service --- builtin/providers/aws/auth_helpers_test.go | 239 +++++++++++---------- builtin/providers/aws/config_test.go | 73 +++++++ 2 files changed, 198 insertions(+), 114 deletions(-) create mode 100644 builtin/providers/aws/config_test.go diff --git a/builtin/providers/aws/auth_helpers_test.go b/builtin/providers/aws/auth_helpers_test.go index fb7dd6884..25120c43b 100644 --- a/builtin/providers/aws/auth_helpers_test.go +++ b/builtin/providers/aws/auth_helpers_test.go @@ -1,7 +1,6 @@ package aws import ( - "bytes" "encoding/json" "fmt" "io/ioutil" @@ -10,13 +9,9 @@ import ( "net/http/httptest" "os" "testing" - "time" - "github.com/aws/aws-sdk-go/aws" "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/session" "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/sts" ) @@ -28,9 +23,14 @@ func TestAWSGetAccountInfo_shouldBeValid_fromEC2Role(t *testing.T) { awsTs := awsEnv(t) defer awsTs() - iamEndpoints := []*iamEndpoint{} - ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) - defer ts() + closeEmpty, emptySess, err := getMockedAwsApiSession("zero", []*awsMockEndpoint{}) + defer closeEmpty() + if err != nil { + t.Fatal(err) + } + + iamConn := iam.New(emptySess) + stsConn := sts.New(emptySess) part, id, err := GetAccountInfo(iamConn, stsConn, ec2rolecreds.ProviderName) if err != nil { @@ -55,14 +55,24 @@ func TestAWSGetAccountInfo_shouldBeValid_EC2RoleHasPriority(t *testing.T) { awsTs := awsEnv(t) defer awsTs() - iamEndpoints := []*iamEndpoint{ + iamEndpoints := []*awsMockEndpoint{ { - Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, - Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, + Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &awsMockResponse{200, iamResponse_GetUser_valid, "text/xml"}, }, } - ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) - defer ts() + closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints) + 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) if err != nil { @@ -81,15 +91,26 @@ func TestAWSGetAccountInfo_shouldBeValid_EC2RoleHasPriority(t *testing.T) { } func TestAWSGetAccountInfo_shouldBeValid_fromIamUser(t *testing.T) { - iamEndpoints := []*iamEndpoint{ + iamEndpoints := []*awsMockEndpoint{ { - Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, - Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, + Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &awsMockResponse{200, iamResponse_GetUser_valid, "text/xml"}, }, } - ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) - defer ts() + closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints) + 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, "") if err != nil { @@ -108,18 +129,32 @@ func TestAWSGetAccountInfo_shouldBeValid_fromIamUser(t *testing.T) { } func TestAWSGetAccountInfo_shouldBeValid_fromGetCallerIdentity(t *testing.T) { - iamEndpoints := []*iamEndpoint{ + iamEndpoints := []*awsMockEndpoint{ { - Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, - Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, - }, - { - Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, - Response: &iamResponse{200, stsResponse_GetCallerIdentity_valid, "text/xml"}, + Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &awsMockResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, }, } - ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) - defer ts() + closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints) + 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, "") if err != nil { @@ -138,22 +173,36 @@ func TestAWSGetAccountInfo_shouldBeValid_fromGetCallerIdentity(t *testing.T) { } func TestAWSGetAccountInfo_shouldBeValid_fromIamListRoles(t *testing.T) { - iamEndpoints := []*iamEndpoint{ + iamEndpoints := []*awsMockEndpoint{ { - Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, - Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, + Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &awsMockResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, }, { - Request: &iamRequest{"POST", "/", "Action=GetCallerIdentity&Version=2011-06-15"}, - Response: &iamResponse{403, stsResponse_GetCallerIdentity_unauthorized, "text/xml"}, - }, - { - Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, - Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"}, + Request: &awsMockRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, + Response: &awsMockResponse{200, iamResponse_ListRoles_valid, "text/xml"}, }, } - ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) - defer ts() + closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints) + 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, "") if err != nil { @@ -172,18 +221,30 @@ func TestAWSGetAccountInfo_shouldBeValid_fromIamListRoles(t *testing.T) { } func TestAWSGetAccountInfo_shouldBeValid_federatedRole(t *testing.T) { - iamEndpoints := []*iamEndpoint{ + iamEndpoints := []*awsMockEndpoint{ { - Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, - Response: &iamResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"}, + Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &awsMockResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"}, }, { - Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, - Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"}, + Request: &awsMockRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, + Response: &awsMockResponse{200, iamResponse_ListRoles_valid, "text/xml"}, }, } - ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) - defer ts() + closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints) + 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, "") if err != nil { @@ -202,18 +263,30 @@ func TestAWSGetAccountInfo_shouldBeValid_federatedRole(t *testing.T) { } func TestAWSGetAccountInfo_shouldError_unauthorizedFromIam(t *testing.T) { - iamEndpoints := []*iamEndpoint{ + iamEndpoints := []*awsMockEndpoint{ { - Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, - Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, + Request: &awsMockRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &awsMockResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, }, { - Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, - Response: &iamResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"}, + Request: &awsMockRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, + Response: &awsMockResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"}, }, } - ts, iamConn, stsConn := getMockedAwsIamStsApi(iamEndpoints) - defer ts() + closeIam, iamSess, err := getMockedAwsApiSession("IAM", iamEndpoints) + 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, "") if err == nil { @@ -697,51 +770,6 @@ func invalidAwsEnv(t *testing.T) func() { 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 { // Grab any existing AWS keys and preserve. In some tests we'll unset these, so // 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 = ` diff --git a/builtin/providers/aws/config_test.go b/builtin/providers/aws/config_test.go new file mode 100644 index 000000000..dc93688df --- /dev/null +++ b/builtin/providers/aws/config_test.go @@ -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 +}