@@ -33,15 +33,51 @@ func swapECSContainerURI(path string) func() {
3333}
3434}
3535
36- func setupCredentialsEndpoints (t * testing.T ) (aws.EndpointResolverWithOptions , func ()) {
36+ const ecsFullPathResponse = `{
37+ "Code": "Success",
38+ "Type": "AWS-HMAC",
39+ "AccessKeyId": "ecs-full-path-access-key",
40+ "SecretAccessKey": "ecs-full-path-ecs-secret-key",
41+ "Token": "ecs-full-path-token",
42+ "Expiration": "2100-01-01T00:00:00Z",
43+ "LastUpdated": "2009-11-23T00:00:00Z"
44+ }`
45+
46+ const assumeRoleRespEcsFullPathMsg = `
47+ <AssumeRoleResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
48+ <AssumeRoleResult>
49+ <AssumedRoleUser>
50+ <Arn>arn:aws:sts::account_id:assumed-role/role/session_name</Arn>
51+ <AssumedRoleId>AKID:session_name</AssumedRoleId>
52+ </AssumedRoleUser>
53+ <Credentials>
54+ <AccessKeyId>AKID-Full-Path</AccessKeyId>
55+ <SecretAccessKey>SECRET-Full-Path</SecretAccessKey>
56+ <SessionToken>SESSION_TOKEN-Full-Path</SessionToken>
57+ <Expiration>%s</Expiration>
58+ </Credentials>
59+ </AssumeRoleResult>
60+ <ResponseMetadata>
61+ <RequestId>request-id</RequestId>
62+ </ResponseMetadata>
63+ </AssumeRoleResponse>
64+ `
65+
66+ var ecsMetadataServerURL string
67+
68+ func setupCredentialsEndpoints () (aws.EndpointResolverWithOptions , func ()) {
3769ecsMetadataServer := httptest .NewServer (http .HandlerFunc (
3870func (w http.ResponseWriter , r * http.Request ) {
3971if r .URL .Path == "/ECS" {
4072w .Write ([]byte (ecsResponse ))
73+ // Used when we specify a full path instead of relative path
74+ } else if r .URL .Path == "/ECSFullPath" {
75+ w .Write ([]byte (ecsFullPathResponse ))
4176} else {
4277w .Write ([]byte ("" ))
4378}
4479}))
80+ ecsMetadataServerURL = ecsMetadataServer .URL
4581resetECSEndpoint := swapECSContainerURI (ecsMetadataServer .URL )
4682
4783ec2MetadataServer := httptest .NewServer (http .HandlerFunc (
@@ -74,6 +110,15 @@ func setupCredentialsEndpoints(t *testing.T) (aws.EndpointResolverWithOptions, f
74110
75111switch form .Get ("Action" ) {
76112case "AssumeRole" :
113+ if val , ok := r .Header ["X-Amz-Security-Token" ]; ok {
114+ if val [0 ] == "ecs-full-path-token" {
115+ w .Write ([]byte (fmt .Sprintf (
116+ assumeRoleRespEcsFullPathMsg ,
117+ smithytime .FormatDateTime (time .Now ().
118+ Add (15 * time .Minute )))))
119+ return
120+ }
121+ }
77122w .Write ([]byte (fmt .Sprintf (
78123assumeRoleRespMsg ,
79124smithytime .FormatDateTime (time .Now ().
@@ -394,7 +439,7 @@ func TestSharedConfigCredentialSource(t *testing.T) {
394439os .Setenv ("AWS_PROFILE" , c .envProfile )
395440}
396441
397- endpointResolver , cleanupFn := setupCredentialsEndpoints (t )
442+ endpointResolver , cleanupFn := setupCredentialsEndpoints ()
398443defer cleanupFn ()
399444
400445var cleanup func ()
@@ -604,6 +649,107 @@ func TestResolveCredentialsIMDSClient(t *testing.T) {
604649}
605650}
606651
652+ func TestResolveCredentialsEcsContainer (t * testing.T ) {
653+ testCases := map [string ]struct {
654+ expectedAccessKey string
655+ expectedSecretKey string
656+ envVar map [string ]string
657+ configFile string
658+ }{
659+ "only relative ECS URI set" : {
660+ expectedAccessKey : "ecs-access-key" ,
661+ expectedSecretKey : "ecs-secret-key" ,
662+ envVar : map [string ]string {
663+ "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" : "/ECS" ,
664+ },
665+ },
666+ "only full ECS URI set" : {
667+ expectedAccessKey : "ecs-full-path-access-key" ,
668+ expectedSecretKey : "ecs-full-path-ecs-secret-key" ,
669+ envVar : map [string ]string {
670+ "AWS_CONTAINER_CREDENTIALS_FULL_URI" : "placeholder-replaced-at-runtime" ,
671+ },
672+ },
673+ "relative ECS URI has precedence over full" : {
674+ expectedAccessKey : "ecs-access-key" ,
675+ expectedSecretKey : "ecs-secret-key" ,
676+ envVar : map [string ]string {
677+ "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" : "/ECS" ,
678+ "AWS_CONTAINER_CREDENTIALS_FULL_URI" : "placeholder-replaced-at-runtime" ,
679+ },
680+ },
681+ "credential source only relative ECS URI set" : {
682+ expectedAccessKey : "AKID" ,
683+ expectedSecretKey : "SECRET" ,
684+ envVar : map [string ]string {
685+ "AWS_PROFILE" : "ecscontainer" ,
686+ "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" : "/ECS" ,
687+ },
688+ configFile : filepath .Join ("testdata" , "config_source_shared" ),
689+ },
690+ "credential source only full ECS URI set" : {
691+ expectedAccessKey : "AKID-Full-Path" ,
692+ expectedSecretKey : "SECRET-Full-Path" ,
693+ envVar : map [string ]string {
694+ "AWS_CONTAINER_CREDENTIALS_FULL_URI" : "placeholder-replaced-at-runtime" ,
695+ "AWS_PROFILE" : "ecscontainer" ,
696+ },
697+ configFile : filepath .Join ("testdata" , "config_source_shared" ),
698+ },
699+ "credential source relative ECS URI has precedence over full" : {
700+ expectedAccessKey : "AKID" ,
701+ expectedSecretKey : "SECRET" ,
702+ envVar : map [string ]string {
703+ "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" : "/ECS" ,
704+ "AWS_CONTAINER_CREDENTIALS_FULL_URI" : "placeholder-replaced-at-runtime" ,
705+ "AWS_PROFILE" : "ecscontainer" ,
706+ },
707+ configFile : filepath .Join ("testdata" , "config_source_shared" ),
708+ },
709+ }
710+
711+ for name , tc := range testCases {
712+ t .Run (name , func (t * testing.T ) {
713+ endpointResolver , cleanupFn := setupCredentialsEndpoints ()
714+ defer cleanupFn ()
715+ restoreEnv := awstesting .StashEnv ()
716+ defer awstesting .PopEnv (restoreEnv )
717+ var sharedConfigFiles []string
718+ if tc .configFile != "" {
719+ sharedConfigFiles = append (sharedConfigFiles , tc .configFile )
720+ }
721+ opts := []func (* LoadOptions ) error {
722+ WithEndpointResolverWithOptions (endpointResolver ),
723+ WithRetryer (func () aws.Retryer { return aws.NopRetryer {} }),
724+ WithSharedConfigFiles (sharedConfigFiles ),
725+ WithSharedCredentialsFiles ([]string {}),
726+ }
727+ for k , v := range tc .envVar {
728+ // since we don't know the value of this until the server starts
729+ if k == "AWS_CONTAINER_CREDENTIALS_FULL_URI" {
730+ v = ecsMetadataServerURL + "/ECSFullPath"
731+ }
732+ os .Setenv (k , v )
733+ }
734+ cfg , err := LoadDefaultConfig (context .TODO (), opts ... )
735+ if err != nil {
736+ t .Fatalf ("could not load config: %s" , err )
737+ }
738+ actual , err := cfg .Credentials .Retrieve (context .TODO ())
739+ if err != nil {
740+ t .Fatalf ("could not retrieve credentials: %s" , err )
741+ }
742+ if actual .AccessKeyID != tc .expectedAccessKey {
743+ t .Errorf ("expected access key to be %s, got %s" , tc .expectedAccessKey , actual .AccessKeyID )
744+ }
745+ if actual .SecretAccessKey != tc .expectedSecretKey {
746+ t .Errorf ("expected secret key to be %s, got %s" , tc .expectedSecretKey , actual .SecretAccessKey )
747+ }
748+ })
749+ }
750+
751+ }
752+
607753type stubErrorClient struct {
608754err error
609755}
0 commit comments