github.com/anfernee/terraform@v0.6.16-0.20160430000239-06e5085a92f2/builtin/providers/aws/auth_helpers_test.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"log"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"os"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/aws/aws-sdk-go/aws"
    16  	"github.com/aws/aws-sdk-go/aws/awserr"
    17  	awsCredentials "github.com/aws/aws-sdk-go/aws/credentials"
    18  	"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
    19  	"github.com/aws/aws-sdk-go/aws/session"
    20  	"github.com/aws/aws-sdk-go/service/iam"
    21  )
    22  
    23  func TestAWSGetAccountId_shouldBeValid_fromEC2Role(t *testing.T) {
    24  	resetEnv := unsetEnv(t)
    25  	defer resetEnv()
    26  	// capture the test server's close method, to call after the test returns
    27  	awsTs := awsEnv(t)
    28  	defer awsTs()
    29  
    30  	iamEndpoints := []*iamEndpoint{}
    31  	ts, iamConn := getMockedAwsIamApi(iamEndpoints)
    32  	defer ts()
    33  
    34  	id, err := GetAccountId(iamConn, ec2rolecreds.ProviderName)
    35  	if err != nil {
    36  		t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err)
    37  	}
    38  
    39  	expectedAccountId := "123456789013"
    40  	if id != expectedAccountId {
    41  		t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
    42  	}
    43  }
    44  
    45  func TestAWSGetAccountId_shouldBeValid_EC2RoleHasPriority(t *testing.T) {
    46  	resetEnv := unsetEnv(t)
    47  	defer resetEnv()
    48  	// capture the test server's close method, to call after the test returns
    49  	awsTs := awsEnv(t)
    50  	defer awsTs()
    51  
    52  	iamEndpoints := []*iamEndpoint{
    53  		&iamEndpoint{
    54  			Request:  &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
    55  			Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"},
    56  		},
    57  	}
    58  	ts, iamConn := getMockedAwsIamApi(iamEndpoints)
    59  	defer ts()
    60  
    61  	id, err := GetAccountId(iamConn, ec2rolecreds.ProviderName)
    62  	if err != nil {
    63  		t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err)
    64  	}
    65  
    66  	expectedAccountId := "123456789013"
    67  	if id != expectedAccountId {
    68  		t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
    69  	}
    70  }
    71  
    72  func TestAWSGetAccountId_shouldBeValid_fromIamUser(t *testing.T) {
    73  	iamEndpoints := []*iamEndpoint{
    74  		&iamEndpoint{
    75  			Request:  &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
    76  			Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"},
    77  		},
    78  	}
    79  	ts, iamConn := getMockedAwsIamApi(iamEndpoints)
    80  	defer ts()
    81  
    82  	id, err := GetAccountId(iamConn, "")
    83  	if err != nil {
    84  		t.Fatalf("Getting account ID via GetUser failed: %s", err)
    85  	}
    86  
    87  	expectedAccountId := "123456789012"
    88  	if id != expectedAccountId {
    89  		t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
    90  	}
    91  }
    92  
    93  func TestAWSGetAccountId_shouldBeValid_fromIamListRoles(t *testing.T) {
    94  	iamEndpoints := []*iamEndpoint{
    95  		&iamEndpoint{
    96  			Request:  &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
    97  			Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
    98  		},
    99  		&iamEndpoint{
   100  			Request:  &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
   101  			Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"},
   102  		},
   103  	}
   104  	ts, iamConn := getMockedAwsIamApi(iamEndpoints)
   105  	defer ts()
   106  
   107  	id, err := GetAccountId(iamConn, "")
   108  	if err != nil {
   109  		t.Fatalf("Getting account ID via ListRoles failed: %s", err)
   110  	}
   111  
   112  	expectedAccountId := "123456789012"
   113  	if id != expectedAccountId {
   114  		t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
   115  	}
   116  }
   117  
   118  func TestAWSGetAccountId_shouldBeValid_federatedRole(t *testing.T) {
   119  	iamEndpoints := []*iamEndpoint{
   120  		&iamEndpoint{
   121  			Request:  &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
   122  			Response: &iamResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"},
   123  		},
   124  		&iamEndpoint{
   125  			Request:  &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
   126  			Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"},
   127  		},
   128  	}
   129  	ts, iamConn := getMockedAwsIamApi(iamEndpoints)
   130  	defer ts()
   131  
   132  	id, err := GetAccountId(iamConn, "")
   133  	if err != nil {
   134  		t.Fatalf("Getting account ID via ListRoles failed: %s", err)
   135  	}
   136  
   137  	expectedAccountId := "123456789012"
   138  	if id != expectedAccountId {
   139  		t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id)
   140  	}
   141  }
   142  
   143  func TestAWSGetAccountId_shouldError_unauthorizedFromIam(t *testing.T) {
   144  	iamEndpoints := []*iamEndpoint{
   145  		&iamEndpoint{
   146  			Request:  &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"},
   147  			Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"},
   148  		},
   149  		&iamEndpoint{
   150  			Request:  &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"},
   151  			Response: &iamResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"},
   152  		},
   153  	}
   154  	ts, iamConn := getMockedAwsIamApi(iamEndpoints)
   155  	defer ts()
   156  
   157  	id, err := GetAccountId(iamConn, "")
   158  	if err == nil {
   159  		t.Fatal("Expected error when getting account ID")
   160  	}
   161  
   162  	if id != "" {
   163  		t.Fatalf("Expected no account ID, given: %s", id)
   164  	}
   165  }
   166  
   167  func TestAWSParseAccountIdFromArn(t *testing.T) {
   168  	validArn := "arn:aws:iam::101636750127:instance-profile/aws-elasticbeanstalk-ec2-role"
   169  	expectedId := "101636750127"
   170  	id, err := parseAccountIdFromArn(validArn)
   171  	if err != nil {
   172  		t.Fatalf("Expected no error when parsing valid ARN: %s", err)
   173  	}
   174  	if id != expectedId {
   175  		t.Fatalf("Parsed id doesn't match with expected (%q != %q)", id, expectedId)
   176  	}
   177  
   178  	invalidArn := "blablah"
   179  	id, err = parseAccountIdFromArn(invalidArn)
   180  	if err == nil {
   181  		t.Fatalf("Expected error when parsing invalid ARN (%q)", invalidArn)
   182  	}
   183  }
   184  
   185  func TestAWSGetCredentials_shouldError(t *testing.T) {
   186  	resetEnv := unsetEnv(t)
   187  	defer resetEnv()
   188  	cfg := Config{}
   189  
   190  	c := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
   191  	_, err := c.Get()
   192  	if awsErr, ok := err.(awserr.Error); ok {
   193  		if awsErr.Code() != "NoCredentialProviders" {
   194  			t.Fatalf("Expected NoCredentialProviders error")
   195  		}
   196  	}
   197  	if err == nil {
   198  		t.Fatalf("Expected an error with empty env, keys, and IAM in AWS Config")
   199  	}
   200  }
   201  
   202  func TestAWSGetCredentials_shouldBeStatic(t *testing.T) {
   203  	simple := []struct {
   204  		Key, Secret, Token string
   205  	}{
   206  		{
   207  			Key:    "test",
   208  			Secret: "secret",
   209  		}, {
   210  			Key:    "test",
   211  			Secret: "test",
   212  			Token:  "test",
   213  		},
   214  	}
   215  
   216  	for _, c := range simple {
   217  		cfg := Config{
   218  			AccessKey: c.Key,
   219  			SecretKey: c.Secret,
   220  			Token:     c.Token,
   221  		}
   222  
   223  		creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
   224  		if creds == nil {
   225  			t.Fatalf("Expected a static creds provider to be returned")
   226  		}
   227  		v, err := creds.Get()
   228  		if err != nil {
   229  			t.Fatalf("Error gettings creds: %s", err)
   230  		}
   231  		if v.AccessKeyID != c.Key {
   232  			t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID)
   233  		}
   234  		if v.SecretAccessKey != c.Secret {
   235  			t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey)
   236  		}
   237  		if v.SessionToken != c.Token {
   238  			t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken)
   239  		}
   240  	}
   241  }
   242  
   243  // TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform
   244  // from an EC2 instance, without environment variables or manually supplied
   245  // credentials.
   246  func TestAWSGetCredentials_shouldIAM(t *testing.T) {
   247  	// clear AWS_* environment variables
   248  	resetEnv := unsetEnv(t)
   249  	defer resetEnv()
   250  
   251  	// capture the test server's close method, to call after the test returns
   252  	ts := awsEnv(t)
   253  	defer ts()
   254  
   255  	// An empty config, no key supplied
   256  	cfg := Config{}
   257  
   258  	creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
   259  	if creds == nil {
   260  		t.Fatalf("Expected a static creds provider to be returned")
   261  	}
   262  
   263  	v, err := creds.Get()
   264  	if err != nil {
   265  		t.Fatalf("Error gettings creds: %s", err)
   266  	}
   267  	if v.AccessKeyID != "somekey" {
   268  		t.Fatalf("AccessKeyID mismatch, expected: (somekey), got (%s)", v.AccessKeyID)
   269  	}
   270  	if v.SecretAccessKey != "somesecret" {
   271  		t.Fatalf("SecretAccessKey mismatch, expected: (somesecret), got (%s)", v.SecretAccessKey)
   272  	}
   273  	if v.SessionToken != "sometoken" {
   274  		t.Fatalf("SessionToken mismatch, expected: (sometoken), got (%s)", v.SessionToken)
   275  	}
   276  }
   277  
   278  // TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform
   279  // from an EC2 instance, without environment variables or manually supplied
   280  // credentials.
   281  func TestAWSGetCredentials_shouldIgnoreIAM(t *testing.T) {
   282  	resetEnv := unsetEnv(t)
   283  	defer resetEnv()
   284  	// capture the test server's close method, to call after the test returns
   285  	ts := awsEnv(t)
   286  	defer ts()
   287  	simple := []struct {
   288  		Key, Secret, Token string
   289  	}{
   290  		{
   291  			Key:    "test",
   292  			Secret: "secret",
   293  		}, {
   294  			Key:    "test",
   295  			Secret: "test",
   296  			Token:  "test",
   297  		},
   298  	}
   299  
   300  	for _, c := range simple {
   301  		cfg := Config{
   302  			AccessKey: c.Key,
   303  			SecretKey: c.Secret,
   304  			Token:     c.Token,
   305  		}
   306  
   307  		creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
   308  		if creds == nil {
   309  			t.Fatalf("Expected a static creds provider to be returned")
   310  		}
   311  		v, err := creds.Get()
   312  		if err != nil {
   313  			t.Fatalf("Error gettings creds: %s", err)
   314  		}
   315  		if v.AccessKeyID != c.Key {
   316  			t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID)
   317  		}
   318  		if v.SecretAccessKey != c.Secret {
   319  			t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey)
   320  		}
   321  		if v.SessionToken != c.Token {
   322  			t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken)
   323  		}
   324  	}
   325  }
   326  
   327  func TestAWSGetCredentials_shouldErrorWithInvalidEndpoint(t *testing.T) {
   328  	resetEnv := unsetEnv(t)
   329  	defer resetEnv()
   330  	// capture the test server's close method, to call after the test returns
   331  	ts := invalidAwsEnv(t)
   332  	defer ts()
   333  
   334  	creds := GetCredentials("", "", "", "", "")
   335  	v, err := creds.Get()
   336  	if err == nil {
   337  		t.Fatal("Expected error returned when getting creds w/ invalid EC2 endpoint")
   338  	}
   339  
   340  	if v.ProviderName != "" {
   341  		t.Fatalf("Expected provider name to be empty, %q given", v.ProviderName)
   342  	}
   343  }
   344  
   345  func TestAWSGetCredentials_shouldIgnoreInvalidEndpoint(t *testing.T) {
   346  	resetEnv := unsetEnv(t)
   347  	defer resetEnv()
   348  	// capture the test server's close method, to call after the test returns
   349  	ts := invalidAwsEnv(t)
   350  	defer ts()
   351  
   352  	creds := GetCredentials("accessKey", "secretKey", "", "", "")
   353  	v, err := creds.Get()
   354  	if err != nil {
   355  		t.Fatalf("Getting static credentials w/ invalid EC2 endpoint failed: %s", err)
   356  	}
   357  
   358  	if v.ProviderName != "StaticProvider" {
   359  		t.Fatalf("Expected provider name to be %q, %q given", "StaticProvider", v.ProviderName)
   360  	}
   361  
   362  	if v.AccessKeyID != "accessKey" {
   363  		t.Fatalf("Static Access Key %q doesn't match: %s", "accessKey", v.AccessKeyID)
   364  	}
   365  
   366  	if v.SecretAccessKey != "secretKey" {
   367  		t.Fatalf("Static Secret Key %q doesn't match: %s", "secretKey", v.SecretAccessKey)
   368  	}
   369  }
   370  
   371  func TestAWSGetCredentials_shouldCatchEC2RoleProvider(t *testing.T) {
   372  	resetEnv := unsetEnv(t)
   373  	defer resetEnv()
   374  	// capture the test server's close method, to call after the test returns
   375  	ts := awsEnv(t)
   376  	defer ts()
   377  
   378  	creds := GetCredentials("", "", "", "", "")
   379  	if creds == nil {
   380  		t.Fatalf("Expected an EC2Role creds provider to be returned")
   381  	}
   382  	v, err := creds.Get()
   383  	if err != nil {
   384  		t.Fatalf("Expected no error when getting creds: %s", err)
   385  	}
   386  	expectedProvider := "EC2RoleProvider"
   387  	if v.ProviderName != expectedProvider {
   388  		t.Fatalf("Expected provider name to be %q, %q given",
   389  			expectedProvider, v.ProviderName)
   390  	}
   391  }
   392  
   393  var credentialsFileContents = `[myprofile]
   394  aws_access_key_id = accesskey
   395  aws_secret_access_key = secretkey
   396  `
   397  
   398  func TestAWSGetCredentials_shouldBeShared(t *testing.T) {
   399  	file, err := ioutil.TempFile(os.TempDir(), "terraform_aws_cred")
   400  	if err != nil {
   401  		t.Fatalf("Error writing temporary credentials file: %s", err)
   402  	}
   403  	_, err = file.WriteString(credentialsFileContents)
   404  	if err != nil {
   405  		t.Fatalf("Error writing temporary credentials to file: %s", err)
   406  	}
   407  	err = file.Close()
   408  	if err != nil {
   409  		t.Fatalf("Error closing temporary credentials file: %s", err)
   410  	}
   411  
   412  	defer os.Remove(file.Name())
   413  
   414  	resetEnv := unsetEnv(t)
   415  	defer resetEnv()
   416  
   417  	if err := os.Setenv("AWS_PROFILE", "myprofile"); err != nil {
   418  		t.Fatalf("Error resetting env var AWS_PROFILE: %s", err)
   419  	}
   420  	if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", file.Name()); err != nil {
   421  		t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
   422  	}
   423  
   424  	creds := GetCredentials("", "", "", "myprofile", file.Name())
   425  	if creds == nil {
   426  		t.Fatalf("Expected a provider chain to be returned")
   427  	}
   428  	v, err := creds.Get()
   429  	if err != nil {
   430  		t.Fatalf("Error gettings creds: %s", err)
   431  	}
   432  
   433  	if v.AccessKeyID != "accesskey" {
   434  		t.Fatalf("AccessKeyID mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID)
   435  	}
   436  
   437  	if v.SecretAccessKey != "secretkey" {
   438  		t.Fatalf("SecretAccessKey mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID)
   439  	}
   440  }
   441  
   442  func TestAWSGetCredentials_shouldBeENV(t *testing.T) {
   443  	// need to set the environment variables to a dummy string, as we don't know
   444  	// what they may be at runtime without hardcoding here
   445  	s := "some_env"
   446  	resetEnv := setEnv(s, t)
   447  
   448  	defer resetEnv()
   449  
   450  	cfg := Config{}
   451  	creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename)
   452  	if creds == nil {
   453  		t.Fatalf("Expected a static creds provider to be returned")
   454  	}
   455  	v, err := creds.Get()
   456  	if err != nil {
   457  		t.Fatalf("Error gettings creds: %s", err)
   458  	}
   459  	if v.AccessKeyID != s {
   460  		t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", s, v.AccessKeyID)
   461  	}
   462  	if v.SecretAccessKey != s {
   463  		t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", s, v.SecretAccessKey)
   464  	}
   465  	if v.SessionToken != s {
   466  		t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", s, v.SessionToken)
   467  	}
   468  }
   469  
   470  // unsetEnv unsets enviornment variables for testing a "clean slate" with no
   471  // credentials in the environment
   472  func unsetEnv(t *testing.T) func() {
   473  	// Grab any existing AWS keys and preserve. In some tests we'll unset these, so
   474  	// we need to have them and restore them after
   475  	e := getEnv()
   476  	if err := os.Unsetenv("AWS_ACCESS_KEY_ID"); err != nil {
   477  		t.Fatalf("Error unsetting env var AWS_ACCESS_KEY_ID: %s", err)
   478  	}
   479  	if err := os.Unsetenv("AWS_SECRET_ACCESS_KEY"); err != nil {
   480  		t.Fatalf("Error unsetting env var AWS_SECRET_ACCESS_KEY: %s", err)
   481  	}
   482  	if err := os.Unsetenv("AWS_SESSION_TOKEN"); err != nil {
   483  		t.Fatalf("Error unsetting env var AWS_SESSION_TOKEN: %s", err)
   484  	}
   485  	if err := os.Unsetenv("AWS_PROFILE"); err != nil {
   486  		t.Fatalf("Error unsetting env var AWS_PROFILE: %s", err)
   487  	}
   488  	if err := os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE"); err != nil {
   489  		t.Fatalf("Error unsetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
   490  	}
   491  
   492  	return func() {
   493  		// re-set all the envs we unset above
   494  		if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil {
   495  			t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err)
   496  		}
   497  		if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil {
   498  			t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err)
   499  		}
   500  		if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil {
   501  			t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err)
   502  		}
   503  		if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil {
   504  			t.Fatalf("Error resetting env var AWS_PROFILE: %s", err)
   505  		}
   506  		if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", e.CredsFilename); err != nil {
   507  			t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err)
   508  		}
   509  	}
   510  }
   511  
   512  func setEnv(s string, t *testing.T) func() {
   513  	e := getEnv()
   514  	// Set all the envs to a dummy value
   515  	if err := os.Setenv("AWS_ACCESS_KEY_ID", s); err != nil {
   516  		t.Fatalf("Error setting env var AWS_ACCESS_KEY_ID: %s", err)
   517  	}
   518  	if err := os.Setenv("AWS_SECRET_ACCESS_KEY", s); err != nil {
   519  		t.Fatalf("Error setting env var AWS_SECRET_ACCESS_KEY: %s", err)
   520  	}
   521  	if err := os.Setenv("AWS_SESSION_TOKEN", s); err != nil {
   522  		t.Fatalf("Error setting env var AWS_SESSION_TOKEN: %s", err)
   523  	}
   524  	if err := os.Setenv("AWS_PROFILE", s); err != nil {
   525  		t.Fatalf("Error setting env var AWS_PROFILE: %s", err)
   526  	}
   527  	if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil {
   528  		t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err)
   529  	}
   530  
   531  	return func() {
   532  		// re-set all the envs we unset above
   533  		if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil {
   534  			t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err)
   535  		}
   536  		if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil {
   537  			t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err)
   538  		}
   539  		if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil {
   540  			t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err)
   541  		}
   542  		if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil {
   543  			t.Fatalf("Error setting env var AWS_PROFILE: %s", err)
   544  		}
   545  		if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil {
   546  			t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err)
   547  		}
   548  	}
   549  }
   550  
   551  // awsEnv establishes a httptest server to mock out the internal AWS Metadata
   552  // service. IAM Credentials are retrieved by the EC2RoleProvider, which makes
   553  // API calls to this internal URL. By replacing the server with a test server,
   554  // we can simulate an AWS environment
   555  func awsEnv(t *testing.T) func() {
   556  	routes := routes{}
   557  	if err := json.Unmarshal([]byte(metadataApiRoutes), &routes); err != nil {
   558  		t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err)
   559  	}
   560  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   561  		w.Header().Set("Content-Type", "text/plain")
   562  		w.Header().Add("Server", "MockEC2")
   563  		log.Printf("[DEBUG] Mocker server received request to %q", r.RequestURI)
   564  		for _, e := range routes.Endpoints {
   565  			if r.RequestURI == e.Uri {
   566  				fmt.Fprintln(w, e.Body)
   567  				w.WriteHeader(200)
   568  				return
   569  			}
   570  		}
   571  		w.WriteHeader(400)
   572  	}))
   573  
   574  	os.Setenv("AWS_METADATA_URL", ts.URL+"/latest")
   575  	return ts.Close
   576  }
   577  
   578  // invalidAwsEnv establishes a httptest server to simulate behaviour
   579  // when endpoint doesn't respond as expected
   580  func invalidAwsEnv(t *testing.T) func() {
   581  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   582  		w.WriteHeader(400)
   583  	}))
   584  
   585  	os.Setenv("AWS_METADATA_URL", ts.URL+"/latest")
   586  	return ts.Close
   587  }
   588  
   589  // getMockedAwsIamApi establishes a httptest server to simulate behaviour
   590  // of a real AWS' IAM server
   591  func getMockedAwsIamApi(endpoints []*iamEndpoint) (func(), *iam.IAM) {
   592  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   593  		buf := new(bytes.Buffer)
   594  		buf.ReadFrom(r.Body)
   595  		requestBody := buf.String()
   596  
   597  		log.Printf("[DEBUG] Received IAM API %q request to %q: %s",
   598  			r.Method, r.RequestURI, requestBody)
   599  
   600  		for _, e := range endpoints {
   601  			if r.Method == e.Request.Method && r.RequestURI == e.Request.Uri && requestBody == e.Request.Body {
   602  				log.Printf("[DEBUG] Mock API responding with %d: %s", e.Response.StatusCode, e.Response.Body)
   603  
   604  				w.WriteHeader(e.Response.StatusCode)
   605  				w.Header().Set("Content-Type", e.Response.ContentType)
   606  				w.Header().Set("X-Amzn-Requestid", "1b206dd1-f9a8-11e5-becf-051c60f11c4a")
   607  				w.Header().Set("Date", time.Now().Format(time.RFC1123))
   608  
   609  				fmt.Fprintln(w, e.Response.Body)
   610  				return
   611  			}
   612  		}
   613  
   614  		w.WriteHeader(400)
   615  		return
   616  	}))
   617  
   618  	sc := awsCredentials.NewStaticCredentials("accessKey", "secretKey", "")
   619  
   620  	sess := session.New(&aws.Config{
   621  		Credentials:                   sc,
   622  		Region:                        aws.String("us-east-1"),
   623  		Endpoint:                      aws.String(ts.URL),
   624  		CredentialsChainVerboseErrors: aws.Bool(true),
   625  	})
   626  	iamConn := iam.New(sess)
   627  
   628  	return ts.Close, iamConn
   629  }
   630  
   631  func getEnv() *currentEnv {
   632  	// Grab any existing AWS keys and preserve. In some tests we'll unset these, so
   633  	// we need to have them and restore them after
   634  	return &currentEnv{
   635  		Key:           os.Getenv("AWS_ACCESS_KEY_ID"),
   636  		Secret:        os.Getenv("AWS_SECRET_ACCESS_KEY"),
   637  		Token:         os.Getenv("AWS_SESSION_TOKEN"),
   638  		Profile:       os.Getenv("AWS_PROFILE"),
   639  		CredsFilename: os.Getenv("AWS_SHARED_CREDENTIALS_FILE"),
   640  	}
   641  }
   642  
   643  // struct to preserve the current environment
   644  type currentEnv struct {
   645  	Key, Secret, Token, Profile, CredsFilename string
   646  }
   647  
   648  type routes struct {
   649  	Endpoints []*endpoint `json:"endpoints"`
   650  }
   651  type endpoint struct {
   652  	Uri  string `json:"uri"`
   653  	Body string `json:"body"`
   654  }
   655  
   656  const metadataApiRoutes = `
   657  {
   658    "endpoints": [
   659      {
   660        "uri": "/latest/meta-data/instance-id",
   661        "body": "mock-instance-id"
   662      },
   663      {
   664        "uri": "/latest/meta-data/iam/info",
   665        "body": "{\"Code\": \"Success\",\"LastUpdated\": \"2016-03-17T12:27:32Z\",\"InstanceProfileArn\": \"arn:aws:iam::123456789013:instance-profile/my-instance-profile\",\"InstanceProfileId\": \"AIPAABCDEFGHIJKLMN123\"}"
   666      },
   667      {
   668        "uri": "/latest/meta-data/iam/security-credentials",
   669        "body": "test_role"
   670      },
   671      {
   672        "uri": "/latest/meta-data/iam/security-credentials/test_role",
   673        "body": "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"somekey\",\"SecretAccessKey\":\"somesecret\",\"Token\":\"sometoken\"}"
   674      }
   675    ]
   676  }
   677  `
   678  
   679  type iamEndpoint struct {
   680  	Request  *iamRequest
   681  	Response *iamResponse
   682  }
   683  
   684  type iamRequest struct {
   685  	Method string
   686  	Uri    string
   687  	Body   string
   688  }
   689  
   690  type iamResponse struct {
   691  	StatusCode  int
   692  	Body        string
   693  	ContentType string
   694  }
   695  
   696  const iamResponse_GetUser_valid = `<GetUserResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
   697    <GetUserResult>
   698      <User>
   699        <UserId>AIDACKCEVSQ6C2EXAMPLE</UserId>
   700        <Path>/division_abc/subdivision_xyz/</Path>
   701        <UserName>Bob</UserName>
   702        <Arn>arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob</Arn>
   703        <CreateDate>2013-10-02T17:01:44Z</CreateDate>
   704        <PasswordLastUsed>2014-10-10T14:37:51Z</PasswordLastUsed>
   705      </User>
   706    </GetUserResult>
   707    <ResponseMetadata>
   708      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
   709    </ResponseMetadata>
   710  </GetUserResponse>`
   711  
   712  const iamResponse_GetUser_unauthorized = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
   713    <Error>
   714      <Type>Sender</Type>
   715      <Code>AccessDenied</Code>
   716      <Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:GetUser on resource: arn:aws:iam::123456789012:user/Bob</Message>
   717    </Error>
   718    <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
   719  </ErrorResponse>`
   720  
   721  const iamResponse_GetUser_federatedFailure = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
   722    <Error>
   723      <Type>Sender</Type>
   724      <Code>ValidationError</Code>
   725      <Message>Must specify userName when calling with non-User credentials</Message>
   726    </Error>
   727    <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
   728  </ErrorResponse>`
   729  
   730  const iamResponse_ListRoles_valid = `<ListRolesResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
   731    <ListRolesResult>
   732      <IsTruncated>true</IsTruncated>
   733      <Marker>AWceSSsKsazQ4IEplT9o4hURCzBs00iavlEvEXAMPLE</Marker>
   734      <Roles>
   735        <member>
   736          <Path>/</Path>
   737          <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>
   738          <RoleId>AROACKCEVSQ6C2EXAMPLE</RoleId>
   739          <RoleName>elasticbeanstalk-role</RoleName>
   740          <Arn>arn:aws:iam::123456789012:role/elasticbeanstalk-role</Arn>
   741          <CreateDate>2013-10-02T17:01:44Z</CreateDate>
   742        </member>
   743      </Roles>
   744    </ListRolesResult>
   745    <ResponseMetadata>
   746      <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
   747    </ResponseMetadata>
   748  </ListRolesResponse>`
   749  
   750  const iamResponse_ListRoles_unauthorized = `<ErrorResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
   751    <Error>
   752      <Type>Sender</Type>
   753      <Code>AccessDenied</Code>
   754      <Message>User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:ListRoles on resource: arn:aws:iam::123456789012:role/</Message>
   755    </Error>
   756    <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
   757  </ErrorResponse>`