...

Source file src/github.com/aws/aws-sdk-go-v2/config/shared_config_test.go

Documentation: github.com/aws/aws-sdk-go-v2/config

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"path/filepath"
     8  	"reflect"
     9  	"strconv"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/aws/aws-sdk-go-v2/aws"
    15  	"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
    16  	"github.com/aws/aws-sdk-go-v2/internal/ini"
    17  	"github.com/aws/smithy-go/logging"
    18  	"github.com/aws/smithy-go/ptr"
    19  )
    20  
    21  var _ regionProvider = (*SharedConfig)(nil)
    22  
    23  var (
    24  	testConfigFilename      = filepath.Join("testdata", "shared_config")
    25  	testConfigOtherFilename = filepath.Join("testdata", "shared_config_other")
    26  	testCredentialsFilename = filepath.Join("testdata", "shared_credentials")
    27  )
    28  
    29  func TestNewSharedConfig(t *testing.T) {
    30  	cases := map[string]struct {
    31  		ConfigFilenames      []string
    32  		CredentialsFilenames []string
    33  		Profile              string
    34  		Expected             SharedConfig
    35  		Err                  error
    36  	}{
    37  		"file not exist": {
    38  			ConfigFilenames: []string{"file_not_exist"},
    39  			Profile:         "default",
    40  			Err:             fmt.Errorf("failed to get shared config profile"),
    41  		},
    42  		"default profile": {
    43  			ConfigFilenames: []string{testConfigFilename},
    44  			Profile:         "default",
    45  			Expected: SharedConfig{
    46  				Profile: "default",
    47  				Region:  "default_region",
    48  			},
    49  		},
    50  		"multiple config files": {
    51  			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
    52  			Profile:         "config_file_load_order",
    53  			Expected: SharedConfig{
    54  				Profile: "config_file_load_order",
    55  				Region:  "shared_config_region",
    56  				Credentials: aws.Credentials{
    57  					AccessKeyID:     "shared_config_akid",
    58  					SecretAccessKey: "shared_config_secret",
    59  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
    60  				},
    61  			},
    62  		},
    63  		"mutliple config files reverse order": {
    64  			ConfigFilenames: []string{testConfigFilename, testConfigOtherFilename},
    65  			Profile:         "config_file_load_order",
    66  			Expected: SharedConfig{
    67  				Profile: "config_file_load_order",
    68  				Region:  "shared_config_other_region",
    69  				Credentials: aws.Credentials{
    70  					AccessKeyID:     "shared_config_other_akid",
    71  					SecretAccessKey: "shared_config_other_secret",
    72  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigOtherFilename),
    73  				},
    74  			},
    75  		},
    76  		"Assume role": {
    77  			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
    78  			Profile:         "assume_role",
    79  			Expected: SharedConfig{
    80  				Profile:           "assume_role",
    81  				RoleARN:           "assume_role_role_arn",
    82  				SourceProfileName: "complete_creds",
    83  				Source: &SharedConfig{
    84  					Profile: "complete_creds",
    85  					Credentials: aws.Credentials{
    86  						AccessKeyID:     "complete_creds_akid",
    87  						SecretAccessKey: "complete_creds_secret",
    88  						Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
    89  					},
    90  				},
    91  			},
    92  		},
    93  		"Assume role with invalid source profile": {
    94  			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
    95  			Profile:         "assume_role_invalid_source_profile",
    96  			Err: SharedConfigAssumeRoleError{
    97  				Profile: "profile_not_exists",
    98  				RoleARN: "assume_role_invalid_source_profile_role_arn",
    99  				Err: SharedConfigProfileNotExistError{
   100  					Profile: "profile_not_exists",
   101  					Err:     nil,
   102  				},
   103  			},
   104  		},
   105  		"Assume role with creds": {
   106  			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
   107  			Profile:         "assume_role_w_creds",
   108  			Expected: SharedConfig{
   109  				Profile:           "assume_role_w_creds",
   110  				RoleARN:           "assume_role_w_creds_role_arn",
   111  				ExternalID:        "1234",
   112  				RoleSessionName:   "assume_role_w_creds_session_name",
   113  				SourceProfileName: "assume_role_w_creds",
   114  				Source: &SharedConfig{
   115  					Profile: "assume_role_w_creds",
   116  					Credentials: aws.Credentials{
   117  						AccessKeyID:     "assume_role_w_creds_akid",
   118  						SecretAccessKey: "assume_role_w_creds_secret",
   119  						Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
   120  					},
   121  				},
   122  			},
   123  		},
   124  		"Assume role without creds": {
   125  			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
   126  			Profile:         "assume_role_wo_creds",
   127  			Expected: SharedConfig{
   128  				Profile:           "assume_role_wo_creds",
   129  				RoleARN:           "assume_role_wo_creds_role_arn",
   130  				SourceProfileName: "assume_role_wo_creds",
   131  			},
   132  			Err: SharedConfigAssumeRoleError{
   133  				Profile: "assume_role_wo_creds",
   134  				RoleARN: "assume_role_wo_creds_role_arn",
   135  			},
   136  		},
   137  		"Invalid INI file": {
   138  			ConfigFilenames: []string{filepath.Join("testdata", "shared_config_invalid_ini")},
   139  			Profile:         "profile_name",
   140  			Err: SharedConfigProfileNotExistError{
   141  				Filename: []string{filepath.Join("testdata", "shared_config_invalid_ini")},
   142  				Profile:  "profile_name",
   143  				Err:      nil,
   144  			},
   145  		},
   146  		"S3UseARNRegion property on profile": {
   147  			Profile:         "valid_arn_region",
   148  			ConfigFilenames: []string{testConfigFilename},
   149  			Expected: SharedConfig{
   150  				Profile:        "valid_arn_region",
   151  				S3UseARNRegion: ptr.Bool(true),
   152  			},
   153  		},
   154  		"S3DisableMultiRegionAccessPoints property on profile": {
   155  			Profile:         "disable_mrap",
   156  			ConfigFilenames: []string{testConfigFilename},
   157  			Expected: SharedConfig{
   158  				Profile:                          "disable_mrap",
   159  				S3DisableMultiRegionAccessPoints: ptr.Bool(true),
   160  			},
   161  		},
   162  		"EndpointDiscovery property enabled on profile": {
   163  			Profile:         "endpoint_discovery_enabled",
   164  			ConfigFilenames: []string{testConfigFilename},
   165  			Expected: SharedConfig{
   166  				Profile:                 "endpoint_discovery_enabled",
   167  				EnableEndpointDiscovery: aws.EndpointDiscoveryEnabled,
   168  			},
   169  		},
   170  		"EndpointDiscovery property disabled on profile": {
   171  			Profile:         "endpoint_discovery_disabled",
   172  			ConfigFilenames: []string{testConfigFilename},
   173  			Expected: SharedConfig{
   174  				Profile:                 "endpoint_discovery_disabled",
   175  				EnableEndpointDiscovery: aws.EndpointDiscoveryDisabled,
   176  			},
   177  		},
   178  		"EndpointDiscovery property set as auto on profile": {
   179  			Profile:         "endpoint_discovery_auto",
   180  			ConfigFilenames: []string{testConfigFilename},
   181  			Expected: SharedConfig{
   182  				Profile:                 "endpoint_discovery_auto",
   183  				EnableEndpointDiscovery: aws.EndpointDiscoveryAuto,
   184  			},
   185  		},
   186  		"EndpointDiscovery property set as unknown on profile": {
   187  			Profile:         "endpoint_discovery_unknown",
   188  			ConfigFilenames: []string{testConfigFilename},
   189  			Expected: SharedConfig{
   190  				Profile:                 "endpoint_discovery_unknown",
   191  				EnableEndpointDiscovery: aws.EndpointDiscoveryUnset,
   192  			},
   193  		},
   194  		"Assume role with credential source Ec2Metadata": {
   195  			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
   196  			Profile:         "assume_role_with_credential_source",
   197  			Expected: SharedConfig{
   198  				Profile:          "assume_role_with_credential_source",
   199  				RoleARN:          "assume_role_with_credential_source_role_arn",
   200  				CredentialSource: credSourceEc2Metadata,
   201  			},
   202  		},
   203  		"Assume role chained with creds": {
   204  			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
   205  			Profile:         "multiple_assume_role",
   206  			Expected: SharedConfig{
   207  				Profile:           "multiple_assume_role",
   208  				RoleARN:           "multiple_assume_role_role_arn",
   209  				SourceProfileName: "assume_role",
   210  				Source: &SharedConfig{
   211  					Profile:           "assume_role",
   212  					RoleARN:           "assume_role_role_arn",
   213  					SourceProfileName: "complete_creds",
   214  					Source: &SharedConfig{
   215  						Profile: "complete_creds",
   216  						Credentials: aws.Credentials{
   217  							AccessKeyID:     "complete_creds_akid",
   218  							SecretAccessKey: "complete_creds_secret",
   219  							Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
   220  						},
   221  					},
   222  				},
   223  			},
   224  		},
   225  		"Assume role chained with credential source": {
   226  			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
   227  			Profile:         "multiple_assume_role_with_credential_source",
   228  			Expected: SharedConfig{
   229  				Profile:           "multiple_assume_role_with_credential_source",
   230  				RoleARN:           "multiple_assume_role_with_credential_source_role_arn",
   231  				SourceProfileName: "assume_role_with_credential_source",
   232  				Source: &SharedConfig{
   233  					Profile:          "assume_role_with_credential_source",
   234  					RoleARN:          "assume_role_with_credential_source_role_arn",
   235  					CredentialSource: credSourceEc2Metadata,
   236  				},
   237  			},
   238  		},
   239  		"Assume role chained with credential source reversed order": {
   240  			ConfigFilenames: []string{testConfigOtherFilename, testConfigFilename},
   241  			Profile:         "multiple_assume_role_with_credential_source2",
   242  			Expected: SharedConfig{
   243  				Profile:           "multiple_assume_role_with_credential_source2",
   244  				RoleARN:           "multiple_assume_role_with_credential_source2_role_arn",
   245  				SourceProfileName: "multiple_assume_role_with_credential_source",
   246  				Source: &SharedConfig{
   247  					Profile:           "multiple_assume_role_with_credential_source",
   248  					RoleARN:           "multiple_assume_role_with_credential_source_role_arn",
   249  					SourceProfileName: "assume_role_with_credential_source",
   250  					Source: &SharedConfig{
   251  						Profile:          "assume_role_with_credential_source",
   252  						RoleARN:          "assume_role_with_credential_source_role_arn",
   253  						CredentialSource: credSourceEc2Metadata,
   254  					},
   255  				},
   256  			},
   257  		},
   258  		"AWS SSO Profile": {
   259  			ConfigFilenames: []string{testConfigFilename},
   260  			Profile:         "sso_creds",
   261  			Expected: SharedConfig{
   262  				Profile:      "sso_creds",
   263  				SSOAccountID: "012345678901",
   264  				SSORegion:    "us-west-2",
   265  				SSORoleName:  "TestRole",
   266  				SSOStartURL:  "https://127.0.0.1/start",
   267  			},
   268  		},
   269  		"Assume Role with AWS SSO Credentials": {
   270  			ConfigFilenames: []string{testConfigFilename},
   271  			Profile:         "source_sso_creds",
   272  			Expected: SharedConfig{
   273  				Profile:           "source_sso_creds",
   274  				RoleARN:           "source_sso_creds_arn",
   275  				SourceProfileName: "sso_creds",
   276  				Source: &SharedConfig{
   277  					Profile:      "sso_creds",
   278  					SSOAccountID: "012345678901",
   279  					SSORegion:    "us-west-2",
   280  					SSORoleName:  "TestRole",
   281  					SSOStartURL:  "https://127.0.0.1/start",
   282  				},
   283  			},
   284  		},
   285  		"AWS SSO Profile and Static Credentials": {
   286  			ConfigFilenames: []string{testConfigFilename},
   287  			Profile:         "sso_and_static",
   288  			Expected: SharedConfig{
   289  				Profile: "sso_and_static",
   290  				Credentials: aws.Credentials{
   291  					AccessKeyID:     "sso_and_static_akid",
   292  					SecretAccessKey: "sso_and_static_secret",
   293  					SessionToken:    "sso_and_static_token",
   294  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testConfigFilename),
   295  				},
   296  				SSOAccountID: "012345678901",
   297  				SSORegion:    "us-west-2",
   298  				SSORoleName:  "TestRole",
   299  				SSOStartURL:  "https://THIS_SHOULD_NOT_BE_IN_TESTDATA_CACHE/start",
   300  			},
   301  		},
   302  		"Assume Role with AWS SSO Configuration and Source Profile": {
   303  			ConfigFilenames: []string{testConfigFilename},
   304  			Profile:         "source_sso_and_assume",
   305  			Expected: SharedConfig{
   306  				Profile:           "source_sso_and_assume",
   307  				RoleARN:           "source_sso_and_assume_arn",
   308  				SourceProfileName: "sso_and_assume",
   309  				Source: &SharedConfig{
   310  					Profile:           "sso_and_assume",
   311  					RoleARN:           "sso_with_assume_role_arn",
   312  					SourceProfileName: "multiple_assume_role_with_credential_source",
   313  					Source: &SharedConfig{
   314  						Profile:           "multiple_assume_role_with_credential_source",
   315  						RoleARN:           "multiple_assume_role_with_credential_source_role_arn",
   316  						SourceProfileName: "assume_role_with_credential_source",
   317  						Source: &SharedConfig{
   318  							Profile:          "assume_role_with_credential_source",
   319  							RoleARN:          "assume_role_with_credential_source_role_arn",
   320  							CredentialSource: credSourceEc2Metadata,
   321  						},
   322  					},
   323  				},
   324  			},
   325  		},
   326  		"SSO Mixed with Additional Credential Providrer": {
   327  			ConfigFilenames: []string{testConfigFilename},
   328  			Profile:         "sso_mixed_credproc",
   329  			Expected: SharedConfig{
   330  				Profile:           "sso_mixed_credproc",
   331  				SSOAccountID:      "012345678901",
   332  				SSORegion:         "us-west-2",
   333  				SSORoleName:       "TestRole",
   334  				SSOStartURL:       "https://127.0.0.1/start",
   335  				CredentialProcess: "/path/to/process",
   336  			},
   337  		},
   338  		"SSO Session success": {
   339  			ConfigFilenames: []string{testConfigFilename},
   340  			Profile:         "sso-session-success",
   341  			Expected: SharedConfig{
   342  				Profile:        "sso-session-success",
   343  				Region:         "us-east-1",
   344  				SSOAccountID:   "123456789012",
   345  				SSORoleName:    "testRole",
   346  				SSOSessionName: "sso-session-success-dev",
   347  				SSOSession: &SSOSession{
   348  					Name:        "sso-session-success-dev",
   349  					SSORegion:   "us-east-1",
   350  					SSOStartURL: "https://d-123456789a.awsapps.com/start",
   351  				},
   352  			},
   353  		},
   354  		"profile names are case-sensitive (Mixed)": {
   355  			ConfigFilenames:      []string{testConfigFilename},
   356  			CredentialsFilenames: []string{testCredentialsFilename},
   357  			Profile:              "DoNotNormalize",
   358  			Expected: SharedConfig{
   359  				Profile: "DoNotNormalize",
   360  				Credentials: aws.Credentials{
   361  					AccessKeyID:     "DoNotNormalize_credentials_akid",
   362  					SecretAccessKey: "DoNotNormalize_credentials_secret",
   363  					SessionToken:    "DoNotNormalize_config_session_token",
   364  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testCredentialsFilename),
   365  				},
   366  				RoleDurationSeconds: func() *time.Duration { d := time.Minute * 20; return &d }(),
   367  				Region:              "eu-west-1",
   368  			},
   369  		},
   370  		"profile names are case-sensitive (lower)": {
   371  			ConfigFilenames:      []string{testConfigFilename},
   372  			CredentialsFilenames: []string{testCredentialsFilename},
   373  			Profile:              "donotnormalize",
   374  			Expected: SharedConfig{
   375  				Profile: "donotnormalize",
   376  				Credentials: aws.Credentials{
   377  					AccessKeyID:     "donotnormalize_credentials_akid",
   378  					SecretAccessKey: "donotnormalize_credentials_secret",
   379  					SessionToken:    "donotnormalize_config_session_token",
   380  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testCredentialsFilename),
   381  				},
   382  				RoleDurationSeconds: func() *time.Duration { d := time.Minute * 25; return &d }(),
   383  				Region:              "eu-west-2",
   384  			},
   385  		},
   386  		"profile names are case-sensitive (upper)": {
   387  			ConfigFilenames:      []string{testConfigFilename},
   388  			CredentialsFilenames: []string{testCredentialsFilename},
   389  			Profile:              "DONOTNORMALIZE",
   390  			Expected: SharedConfig{
   391  				Profile: "DONOTNORMALIZE",
   392  				Credentials: aws.Credentials{
   393  					AccessKeyID:     "DONOTNORMALIZE_credentials_akid",
   394  					SecretAccessKey: "DONOTNORMALIZE_credentials_secret",
   395  					SessionToken:    "DONOTNORMALIZE_config_session_token",
   396  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", testCredentialsFilename),
   397  				},
   398  				RoleDurationSeconds: func() *time.Duration { d := time.Minute * 30; return &d }(),
   399  				Region:              "eu-west-3",
   400  			},
   401  		},
   402  		"source profile name is case-sensitive": {
   403  			ConfigFilenames:      []string{testConfigFilename},
   404  			CredentialsFilenames: []string{testCredentialsFilename},
   405  			Profile:              "AssumeWithDoNotNormalize",
   406  			Expected: SharedConfig{
   407  				Profile:           "AssumeWithDoNotNormalize",
   408  				RoleARN:           "AssumeWithDoNotNormalize_role_arn",
   409  				SourceProfileName: "DoNotNormalize",
   410  				Source: &SharedConfig{
   411  					Profile: "DoNotNormalize",
   412  					Credentials: aws.Credentials{
   413  						AccessKeyID:     "DoNotNormalize_credentials_akid",
   414  						SecretAccessKey: "DoNotNormalize_credentials_secret",
   415  						SessionToken:    "DoNotNormalize_config_session_token",
   416  						Source:          fmt.Sprintf("SharedConfigCredentials: %s", testCredentialsFilename),
   417  					},
   418  					RoleDurationSeconds: func() *time.Duration { d := time.Minute * 20; return &d }(),
   419  					Region:              "eu-west-1",
   420  				},
   421  			},
   422  		},
   423  		"profile with ec2_metadata_service_endpoint": {
   424  			ConfigFilenames:      []string{testConfigFilename},
   425  			CredentialsFilenames: []string{testCredentialsFilename},
   426  			Profile:              "EC2MetadataServiceEndpoint",
   427  			Expected: SharedConfig{
   428  				Profile:         "EC2MetadataServiceEndpoint",
   429  				EC2IMDSEndpoint: "http://endpoint.localhost",
   430  			},
   431  		},
   432  		"profile with ec2_metadata_service_endpoint_mode as IPv6": {
   433  			ConfigFilenames:      []string{testConfigFilename},
   434  			CredentialsFilenames: []string{testCredentialsFilename},
   435  			Profile:              "EC2MetadataServiceEndpointModeIPv6",
   436  			Expected: SharedConfig{
   437  				Profile:             "EC2MetadataServiceEndpointModeIPv6",
   438  				EC2IMDSEndpointMode: imds.EndpointModeStateIPv6,
   439  			},
   440  		},
   441  		"profile with ec2_metadata_service_endpoint_mode as IPv4": {
   442  			ConfigFilenames:      []string{testConfigFilename},
   443  			CredentialsFilenames: []string{testCredentialsFilename},
   444  			Profile:              "EC2MetadataServiceEndpointModeIPv4",
   445  			Expected: SharedConfig{
   446  				Profile:             "EC2MetadataServiceEndpointModeIPv4",
   447  				EC2IMDSEndpointMode: imds.EndpointModeStateIPv4,
   448  			},
   449  		},
   450  		"profile with ec2_metadata_service_endpoint_mode is unknown": {
   451  			ConfigFilenames:      []string{testConfigFilename},
   452  			CredentialsFilenames: []string{testCredentialsFilename},
   453  			Profile:              "EC2MetadataServiceEndpointModeUnknown",
   454  			Expected: SharedConfig{
   455  				Profile: "EC2MetadataServiceEndpointModeUnknown",
   456  			},
   457  			Err: fmt.Errorf("unknown EC2 IMDS endpoint mode"),
   458  		},
   459  		"profile with ec2_metadata_service_endpoint and ec2_metadata_service_endpoint_mode": {
   460  			ConfigFilenames:      []string{testConfigFilename},
   461  			CredentialsFilenames: []string{testCredentialsFilename},
   462  			Profile:              "EC2MetadataServiceEndpointAndModeMixed",
   463  			Expected: SharedConfig{
   464  				Profile:             "EC2MetadataServiceEndpointAndModeMixed",
   465  				EC2IMDSEndpoint:     "http://endpoint.localhost",
   466  				EC2IMDSEndpointMode: imds.EndpointModeStateIPv6,
   467  			},
   468  		},
   469  		"dual-stack endpoint enabled": {
   470  			ConfigFilenames:      []string{testConfigFilename},
   471  			CredentialsFilenames: []string{testCredentialsFilename},
   472  			Profile:              "UseDualStackEndpointEnabled",
   473  			Expected: SharedConfig{
   474  				Profile:              "UseDualStackEndpointEnabled",
   475  				Region:               "us-west-2",
   476  				UseDualStackEndpoint: aws.DualStackEndpointStateEnabled,
   477  			},
   478  		},
   479  		"dual-stack endpoint disabled": {
   480  			ConfigFilenames:      []string{testConfigFilename},
   481  			CredentialsFilenames: []string{testCredentialsFilename},
   482  			Profile:              "UseDualStackEndpointDisabled",
   483  			Expected: SharedConfig{
   484  				Profile:              "UseDualStackEndpointDisabled",
   485  				Region:               "us-west-2",
   486  				UseDualStackEndpoint: aws.DualStackEndpointStateDisabled,
   487  			},
   488  		},
   489  		"dual-stack endpoint invalid": {
   490  			ConfigFilenames:      []string{testConfigFilename},
   491  			CredentialsFilenames: []string{testCredentialsFilename},
   492  			Profile:              "UseDualStackEndpointInvalid",
   493  			Expected: SharedConfig{
   494  				Profile:              "UseDualStackEndpointInvalid",
   495  				Region:               "us-west-2",
   496  				UseDualStackEndpoint: aws.DualStackEndpointStateDisabled,
   497  			},
   498  		},
   499  		"fips endpoint enabled": {
   500  			ConfigFilenames:      []string{testConfigFilename},
   501  			CredentialsFilenames: []string{testCredentialsFilename},
   502  			Profile:              "UseFIPSEndpointEnabled",
   503  			Expected: SharedConfig{
   504  				Profile:         "UseFIPSEndpointEnabled",
   505  				Region:          "us-west-2",
   506  				UseFIPSEndpoint: aws.FIPSEndpointStateEnabled,
   507  			},
   508  		},
   509  		"fips endpoint disabled": {
   510  			ConfigFilenames:      []string{testConfigFilename},
   511  			CredentialsFilenames: []string{testCredentialsFilename},
   512  			Profile:              "UseFIPSEndpointDisabled",
   513  			Expected: SharedConfig{
   514  				Profile:         "UseFIPSEndpointDisabled",
   515  				Region:          "us-west-2",
   516  				UseFIPSEndpoint: aws.FIPSEndpointStateDisabled,
   517  			},
   518  		},
   519  		"fips endpoint unknown": {
   520  			ConfigFilenames:      []string{testConfigFilename},
   521  			CredentialsFilenames: []string{testCredentialsFilename},
   522  			Profile:              "UseFIPSEndpointInvalid",
   523  			Expected: SharedConfig{
   524  				Profile:         "UseFIPSEndpointInvalid",
   525  				Region:          "us-west-2",
   526  				UseFIPSEndpoint: aws.FIPSEndpointStateDisabled,
   527  			},
   528  		},
   529  		"defaults mode auto": {
   530  			ConfigFilenames:      []string{testConfigFilename},
   531  			CredentialsFilenames: []string{testCredentialsFilename},
   532  			Profile:              "autodefaultsmode",
   533  			Expected: SharedConfig{
   534  				Profile:      "autodefaultsmode",
   535  				DefaultsMode: aws.DefaultsModeAuto,
   536  			},
   537  		},
   538  		"defaults mode standard": {
   539  			ConfigFilenames:      []string{testConfigFilename},
   540  			CredentialsFilenames: []string{testCredentialsFilename},
   541  			Profile:              "standarddefaultsmode",
   542  			Expected: SharedConfig{
   543  				Profile:      "standarddefaultsmode",
   544  				DefaultsMode: aws.DefaultsModeStandard,
   545  			},
   546  		},
   547  		"defaults mode invalid": {
   548  			ConfigFilenames:      []string{testConfigFilename},
   549  			CredentialsFilenames: []string{testCredentialsFilename},
   550  			Profile:              "invaliddefaultsmode",
   551  			Err:                  fmt.Errorf("failed to load defaults_mode from shared config, invalid value: invalid"),
   552  		},
   553  		"retry options auto": {
   554  			ConfigFilenames:      []string{testConfigFilename},
   555  			CredentialsFilenames: []string{testCredentialsFilename},
   556  			Profile:              "retryunset",
   557  			Expected: SharedConfig{
   558  				Profile: "retryunset",
   559  			},
   560  		},
   561  		"retry options standard": {
   562  			ConfigFilenames:      []string{testConfigFilename},
   563  			CredentialsFilenames: []string{testCredentialsFilename},
   564  			Profile:              "retrywithstandard",
   565  			Expected: SharedConfig{
   566  				Profile:          "retrywithstandard",
   567  				RetryMode:        aws.RetryModeStandard,
   568  				RetryMaxAttempts: 5,
   569  			},
   570  		},
   571  		"retry options adaptive": {
   572  			ConfigFilenames:      []string{testConfigFilename},
   573  			CredentialsFilenames: []string{testCredentialsFilename},
   574  			Profile:              "retrywithadaptive",
   575  			Expected: SharedConfig{
   576  				Profile:          "retrywithadaptive",
   577  				RetryMode:        aws.RetryModeAdaptive,
   578  				RetryMaxAttempts: 4,
   579  			},
   580  		},
   581  		"retry options invalid": {
   582  			ConfigFilenames:      []string{testConfigFilename},
   583  			CredentialsFilenames: []string{testCredentialsFilename},
   584  			Profile:              "retrywithinvalidmode",
   585  			Err:                  fmt.Errorf("failed to load retry_mode from shared config, unknown RetryMode, invalid"),
   586  		},
   587  		"retry options invalid retry attempts": {
   588  			ConfigFilenames:      []string{testConfigFilename},
   589  			CredentialsFilenames: []string{testCredentialsFilename},
   590  			Profile:              "retrywithinvalidattempts",
   591  			Err:                  fmt.Errorf("failed to load max_attempts from shared config, invalid value max_attempts=invalid, expect integer"),
   592  		},
   593  		"ca bundle options": {
   594  			ConfigFilenames:      []string{testConfigFilename},
   595  			CredentialsFilenames: []string{testCredentialsFilename},
   596  			Profile:              "with_ca_bundle",
   597  			Expected: SharedConfig{
   598  				Profile:        "with_ca_bundle",
   599  				CustomCABundle: "custom_ca_bundle_file.pem",
   600  			},
   601  		},
   602  		"merged profiles across files": {
   603  			ConfigFilenames:      []string{testConfigFilename},
   604  			CredentialsFilenames: []string{testCredentialsFilename},
   605  			Profile:              "merged_profiles",
   606  			Expected: SharedConfig{
   607  				Profile:             "merged_profiles",
   608  				RoleARN:             "creds_profile_arn",
   609  				RoleDurationSeconds: aws.Duration(1023 * time.Second),
   610  				SSOAccountID:        "0123456789",
   611  				SSORegion:           "us-west-2",
   612  				SSORoleName:         "CredProfileRole",
   613  				SSOStartURL:         "https://my-sso-cred-profile-role.awsapps.com/start",
   614  				CustomCABundle:      "/path/to/bundle.b",
   615  			},
   616  		},
   617  		"merged profiles across config files": {
   618  			ConfigFilenames:      []string{testConfigFilename, testConfigFilename},
   619  			CredentialsFilenames: []string{},
   620  			Profile:              "merged_profiles",
   621  			Expected: SharedConfig{
   622  				Profile:             "merged_profiles",
   623  				RoleARN:             "config_profile_arn",
   624  				RoleDurationSeconds: aws.Duration(3601 * time.Second),
   625  				SSOAccountID:        "1234567890",
   626  				SSORegion:           "us-east-1",
   627  				SSORoleName:         "ConfigProfileRole",
   628  				SSOStartURL:         "https://my-sso-config-profile-role.awsapps.com/start",
   629  				CustomCABundle:      "/path/to/bundle.a",
   630  			},
   631  		},
   632  		"merged profiles across credentials files": {
   633  			ConfigFilenames:      []string{},
   634  			CredentialsFilenames: []string{testCredentialsFilename, testCredentialsFilename},
   635  			Profile:              "merged_profiles",
   636  			Expected: SharedConfig{
   637  				Profile:             "merged_profiles",
   638  				RoleARN:             "creds_profile_arn",
   639  				RoleDurationSeconds: aws.Duration(1023 * time.Second),
   640  				SSOAccountID:        "0123456789",
   641  				SSORegion:           "us-west-2",
   642  				SSORoleName:         "CredProfileRole",
   643  				SSOStartURL:         "https://my-sso-cred-profile-role.awsapps.com/start",
   644  				CustomCABundle:      "/path/to/bundle.b",
   645  			},
   646  		},
   647  		"Profile with app ID": {
   648  			ConfigFilenames: []string{testConfigFilename},
   649  			Profile:         "sdk_app_id",
   650  			Expected: SharedConfig{
   651  				Profile: "sdk_app_id",
   652  				AppID:   "12345",
   653  			},
   654  		},
   655  		"endpoint config test": {
   656  			ConfigFilenames: []string{testConfigFilename},
   657  			Profile:         "endpoint_config",
   658  			Expected: SharedConfig{
   659  				Profile:                   "endpoint_config",
   660  				BaseEndpoint:              "https://example.com",
   661  				IgnoreConfiguredEndpoints: ptr.Bool(true),
   662  			},
   663  		},
   664  		"imdsv1 disabled = false": {
   665  			ConfigFilenames: []string{testConfigFilename},
   666  			Profile:         "ec2-metadata-v1-disabled-false",
   667  			Expected: SharedConfig{
   668  				Profile:           "ec2-metadata-v1-disabled-false",
   669  				EC2IMDSv1Disabled: aws.Bool(false),
   670  			},
   671  		},
   672  		"imdsv1 disabled = true": {
   673  			ConfigFilenames: []string{testConfigFilename},
   674  			Profile:         "ec2-metadata-v1-disabled-true",
   675  			Expected: SharedConfig{
   676  				Profile:           "ec2-metadata-v1-disabled-true",
   677  				EC2IMDSv1Disabled: aws.Bool(true),
   678  			},
   679  		},
   680  		"imdsv1 disabled = invalid": {
   681  			ConfigFilenames: []string{testConfigFilename},
   682  			Profile:         "ec2-metadata-v1-disabled-invalid",
   683  			Expected: SharedConfig{
   684  				Profile:           "ec2-metadata-v1-disabled-invalid",
   685  				EC2IMDSv1Disabled: aws.Bool(false),
   686  			},
   687  		},
   688  		"profile configuring request compression": {
   689  			ConfigFilenames: []string{testConfigFilename},
   690  			Profile:         "request_compression",
   691  			Expected: SharedConfig{
   692  				Profile:                     "request_compression",
   693  				DisableRequestCompression:   aws.Bool(true),
   694  				RequestMinCompressSizeBytes: aws.Int64(12345),
   695  			},
   696  		},
   697  		"profile with invalid disableRequestCompression": {
   698  			ConfigFilenames: []string{testConfigFilename},
   699  			Profile:         "request_compression_invalid_disable",
   700  			Err: fmt.Errorf("invalid value for shared config profile field, %s=%s, need true or false",
   701  				disableRequestCompression, "blabla"),
   702  		},
   703  		"profile with non-int requestMinCompressSizeBytes": {
   704  			ConfigFilenames: []string{testConfigFilename},
   705  			Profile:         "request_compression_non_int_min_request",
   706  			Err:             fmt.Errorf("invalid value for min request compression size bytes hahaha, need int64"),
   707  		},
   708  		"profile with requestMinCompressSizeBytes out of bounds": {
   709  			ConfigFilenames: []string{testConfigFilename},
   710  			Profile:         "request_compression_min_request_out_of_bounds",
   711  			Err:             fmt.Errorf("invalid range for min request compression size bytes 10485761, must be within 0 and 10485760 inclusively"),
   712  		},
   713  		"services section": {
   714  			ConfigFilenames: []string{testConfigFilename},
   715  			Profile:         "service_endpoint_url",
   716  			Expected: SharedConfig{
   717  				Profile:             "service_endpoint_url",
   718  				ServicesSectionName: "service_endpoint_url_services",
   719  				Services: Services{
   720  					ServiceValues: map[string]map[string]string{
   721  						"s3": {
   722  							"endpoint_url": "http://127.0.0.1",
   723  							"other":        "foo",
   724  						},
   725  						"ec2": {
   726  							"endpoint_url": "http://127.0.0.1:81",
   727  						},
   728  					},
   729  				},
   730  			},
   731  		},
   732  	}
   733  
   734  	for name, c := range cases {
   735  		t.Run(name, func(t *testing.T) {
   736  			cfg, err := LoadSharedConfigProfile(context.TODO(), c.Profile, func(o *LoadSharedConfigOptions) {
   737  				o.ConfigFiles = c.ConfigFilenames
   738  				if c.CredentialsFilenames != nil {
   739  					o.CredentialsFiles = c.CredentialsFilenames
   740  				} else {
   741  					o.CredentialsFiles = []string{filepath.Join("testdata", "empty_creds_config")}
   742  				}
   743  			})
   744  			if c.Err != nil && err != nil {
   745  				if e, a := c.Err.Error(), err.Error(); !strings.Contains(a, e) {
   746  					t.Errorf("expect %q to be in %q", e, a)
   747  				}
   748  				return
   749  			}
   750  			if err != nil {
   751  				t.Fatalf("expect no error, got %v", err)
   752  			}
   753  			if c.Err != nil {
   754  				t.Errorf("expect error: %v, got none", c.Err)
   755  			}
   756  			if diff := cmpDiff(c.Expected, cfg); len(diff) > 0 {
   757  				t.Error(diff)
   758  			}
   759  		})
   760  	}
   761  }
   762  
   763  func TestLoadSharedConfigFromSection(t *testing.T) {
   764  	filename := testConfigFilename
   765  	sections, err := ini.OpenFile(filename)
   766  
   767  	if err != nil {
   768  		t.Fatalf("failed to load test config file, %s, %v", filename, err)
   769  	}
   770  	cases := map[string]struct {
   771  		Profile  string
   772  		Expected SharedConfig
   773  		Err      error
   774  	}{
   775  		"Default as profile": {
   776  			Profile:  "default",
   777  			Expected: SharedConfig{Region: "default_region"},
   778  		},
   779  		"prefixed profile": {
   780  			Profile:  "profile alt_profile_name",
   781  			Expected: SharedConfig{Region: "alt_profile_name_region"},
   782  		},
   783  		"prefixed profile 2": {
   784  			Profile:  "profile short_profile_name_first",
   785  			Expected: SharedConfig{Region: "short_profile_name_first_alt"},
   786  		},
   787  		"profile with partial creds": {
   788  			Profile:  "profile partial_creds",
   789  			Expected: SharedConfig{},
   790  		},
   791  		"profile with role duration": {
   792  			Profile: "profile with_role_duration",
   793  			Expected: SharedConfig{
   794  				RoleDurationSeconds: aws.Duration(3601 * time.Second),
   795  			},
   796  		},
   797  		"profile with complete creds": {
   798  			Profile: "profile complete_creds",
   799  			Expected: SharedConfig{
   800  				Credentials: aws.Credentials{
   801  					AccessKeyID:     "complete_creds_akid",
   802  					SecretAccessKey: "complete_creds_secret",
   803  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", filename),
   804  				},
   805  			},
   806  		},
   807  		"profile with complete creds and token": {
   808  			Profile: "profile complete_creds_with_token",
   809  			Expected: SharedConfig{
   810  				Credentials: aws.Credentials{
   811  					AccessKeyID:     "complete_creds_with_token_akid",
   812  					SecretAccessKey: "complete_creds_with_token_secret",
   813  					SessionToken:    "complete_creds_with_token_token",
   814  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", filename),
   815  				},
   816  			},
   817  		},
   818  		"complete profile": {
   819  			Profile: "profile full_profile",
   820  			Expected: SharedConfig{
   821  				Credentials: aws.Credentials{
   822  					AccessKeyID:     "full_profile_akid",
   823  					SecretAccessKey: "full_profile_secret",
   824  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", filename),
   825  				},
   826  				Region: "full_profile_region",
   827  			},
   828  		},
   829  		"profile with partial assume role": {
   830  			Profile: "profile partial_assume_role",
   831  			Expected: SharedConfig{
   832  				RoleARN: "partial_assume_role_role_arn",
   833  			},
   834  		},
   835  		"profile using assume role": {
   836  			Profile: "profile assume_role",
   837  			Expected: SharedConfig{
   838  				RoleARN:           "assume_role_role_arn",
   839  				SourceProfileName: "complete_creds",
   840  			},
   841  		},
   842  		"profile with assume role and MFA": {
   843  			Profile: "profile assume_role_w_mfa",
   844  			Expected: SharedConfig{
   845  				RoleARN:           "assume_role_role_arn",
   846  				SourceProfileName: "complete_creds",
   847  				MFASerial:         "0123456789",
   848  			},
   849  		},
   850  		"does not exist": {
   851  			Profile: "does_not_exist",
   852  			Err: SharedConfigProfileNotExistError{
   853  				Filename: []string{filename},
   854  				Profile:  "does_not_exist",
   855  				Err:      nil,
   856  			},
   857  		},
   858  		"profile with mixed casing": {
   859  			Profile: "profile with_mixed_case_keys",
   860  			Expected: SharedConfig{
   861  				Credentials: aws.Credentials{
   862  					AccessKeyID:     "accessKey",
   863  					SecretAccessKey: "secret",
   864  					Source:          fmt.Sprintf("SharedConfigCredentials: %s", filename),
   865  				},
   866  			},
   867  		},
   868  	}
   869  
   870  	for name, c := range cases {
   871  		t.Run(name, func(t *testing.T) {
   872  			var cfg SharedConfig
   873  
   874  			section, ok := sections.GetSection(c.Profile)
   875  			if !ok {
   876  				if c.Err == nil {
   877  					t.Fatalf("expected section to be present, was not")
   878  				} else {
   879  					if e, a := c.Err.Error(), "failed to get shared config profile"; !strings.Contains(e, a) {
   880  						t.Fatalf("expect %q to be in %q", a, e)
   881  					}
   882  					return
   883  				}
   884  			}
   885  
   886  			err := cfg.setFromIniSection(c.Profile, section)
   887  			if c.Err != nil {
   888  				if e, a := c.Err.Error(), err.Error(); !strings.Contains(a, e) {
   889  					t.Errorf("expect %q to be in %q", e, a)
   890  				}
   891  				return
   892  			}
   893  			if err != nil {
   894  				t.Fatalf("expect no error, got %v", err)
   895  			}
   896  
   897  			if diff := cmpDiff(c.Expected, cfg); diff != "" {
   898  				t.Errorf("expect shared config match\n%s", diff)
   899  			}
   900  		})
   901  	}
   902  }
   903  
   904  func TestLoadSharedConfig(t *testing.T) {
   905  	origProf := defaultSharedConfigProfile
   906  	origConfigFiles := DefaultSharedConfigFiles
   907  	origCredentialFiles := DefaultSharedCredentialsFiles
   908  	defer func() {
   909  		defaultSharedConfigProfile = origProf
   910  		DefaultSharedConfigFiles = origConfigFiles
   911  		DefaultSharedCredentialsFiles = origCredentialFiles
   912  	}()
   913  
   914  	cases := []struct {
   915  		LoadOptionFn func(*LoadOptions) error
   916  		Files        []string
   917  		Profile      string
   918  		LoadFn       func(context.Context, configs) (Config, error)
   919  		Expect       SharedConfig
   920  		Err          string
   921  	}{
   922  		{
   923  			LoadOptionFn: WithSharedConfigProfile("alt_profile_name"),
   924  			Files: []string{
   925  				filepath.Join("testdata", "shared_config"),
   926  			},
   927  			LoadFn: loadSharedConfig,
   928  			Expect: SharedConfig{
   929  				Profile: "alt_profile_name",
   930  				Region:  "alt_profile_name_region",
   931  			},
   932  		},
   933  		{
   934  			LoadOptionFn: WithSharedConfigFiles([]string{
   935  				filepath.Join("testdata", "shared_config"),
   936  			}),
   937  			Profile: "alt_profile_name",
   938  			LoadFn:  loadSharedConfig,
   939  			Expect: SharedConfig{
   940  				Profile: "alt_profile_name",
   941  				Region:  "alt_profile_name_region",
   942  			},
   943  		},
   944  		{
   945  			LoadOptionFn: WithSharedConfigProfile("default"),
   946  			Files: []string{
   947  				filepath.Join("file_not_exist"),
   948  			},
   949  			LoadFn: loadSharedConfig,
   950  			Err:    "failed to get shared config profile",
   951  		},
   952  		{
   953  			LoadOptionFn: WithSharedConfigProfile("profile_not_exist"),
   954  			Files: []string{
   955  				filepath.Join("testdata", "shared_config"),
   956  			},
   957  			LoadFn: loadSharedConfig,
   958  			Err:    "failed to get shared config profile",
   959  		},
   960  		{
   961  			LoadOptionFn: WithSharedConfigProfile("default"),
   962  			Files: []string{
   963  				filepath.Join("file_not_exist"),
   964  			},
   965  			LoadFn: loadSharedConfigIgnoreNotExist,
   966  		},
   967  		{
   968  			LoadOptionFn: WithSharedConfigProfile("assume_role_invalid_source_profile"),
   969  			Files: []string{
   970  				testConfigOtherFilename, testConfigFilename,
   971  			},
   972  			LoadFn: loadSharedConfig,
   973  			Err:    "failed to get shared config profile",
   974  		},
   975  		{
   976  			LoadOptionFn: WithSharedConfigProfile("assume_role_invalid_source_profile"),
   977  			Files: []string{
   978  				testConfigOtherFilename, testConfigFilename,
   979  			},
   980  			LoadFn: loadSharedConfigIgnoreNotExist,
   981  			Err:    "failed to get shared config profile",
   982  		},
   983  	}
   984  
   985  	for i, c := range cases {
   986  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   987  			defaultSharedConfigProfile = origProf
   988  			DefaultSharedConfigFiles = origConfigFiles
   989  			DefaultSharedCredentialsFiles = origCredentialFiles
   990  
   991  			if len(c.Profile) > 0 {
   992  				defaultSharedConfigProfile = c.Profile
   993  			}
   994  			if len(c.Files) > 0 {
   995  				DefaultSharedConfigFiles = c.Files
   996  			}
   997  
   998  			DefaultSharedCredentialsFiles = []string{}
   999  
  1000  			var options LoadOptions
  1001  			c.LoadOptionFn(&options)
  1002  
  1003  			cfg, err := c.LoadFn(context.Background(), configs{options})
  1004  			if len(c.Err) > 0 {
  1005  				if err == nil {
  1006  					t.Fatalf("expected error %v, got none", c.Err)
  1007  				}
  1008  				if e, a := c.Err, err.Error(); !strings.Contains(a, e) {
  1009  					t.Fatalf("expect %q to be in %q", e, a)
  1010  				}
  1011  				return
  1012  			} else if err != nil {
  1013  				t.Fatalf("expect no error, got %v", err)
  1014  			}
  1015  
  1016  			if e, a := c.Expect, cfg; !reflect.DeepEqual(e, a) {
  1017  				t.Errorf("expect %v got %v", e, a)
  1018  			}
  1019  		})
  1020  	}
  1021  }
  1022  
  1023  func TestSharedConfigLoading(t *testing.T) {
  1024  	// initialize a logger
  1025  	var loggerBuf bytes.Buffer
  1026  	logger := logging.NewStandardLogger(&loggerBuf)
  1027  
  1028  	cases := map[string]struct {
  1029  		LoadOptionFns []func(*LoadOptions) error
  1030  		LoadFn        func(context.Context, configs) (Config, error)
  1031  		Expect        SharedConfig
  1032  		ExpectLog     string
  1033  		Err           string
  1034  	}{
  1035  		"duplicate profiles in the configuration files": {
  1036  			LoadOptionFns: []func(*LoadOptions) error{
  1037  				WithSharedConfigProfile("duplicate-profile"),
  1038  				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
  1039  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1040  				WithLogConfigurationWarnings(true),
  1041  				WithLogger(logger),
  1042  			},
  1043  			LoadFn: loadSharedConfig,
  1044  			Expect: SharedConfig{
  1045  				Profile: "duplicate-profile",
  1046  				Region:  "us-west-2",
  1047  			},
  1048  			ExpectLog: "For profile: profile duplicate-profile, overriding region value, with a region value found in a " +
  1049  				"duplicate profile defined later in the same file",
  1050  		},
  1051  
  1052  		"profile prefix not used in the configuration files": {
  1053  			LoadOptionFns: []func(*LoadOptions) error{
  1054  				WithSharedConfigProfile("no-such-profile"),
  1055  				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
  1056  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1057  				WithLogConfigurationWarnings(true),
  1058  				WithLogger(logger),
  1059  			},
  1060  			LoadFn: loadSharedConfig,
  1061  			Expect: SharedConfig{},
  1062  			Err:    "failed to get shared config profile",
  1063  		},
  1064  
  1065  		"profile prefix overrides default": {
  1066  			LoadOptionFns: []func(*LoadOptions) error{
  1067  				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
  1068  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1069  				WithLogConfigurationWarnings(true),
  1070  				WithLogger(logger),
  1071  			},
  1072  			LoadFn: loadSharedConfig,
  1073  			Expect: SharedConfig{
  1074  				Profile: "default",
  1075  				Region:  "ap-north-1",
  1076  			},
  1077  			ExpectLog: "non-default profile not prefixed with `profile `",
  1078  		},
  1079  
  1080  		"duplicate profiles in credentials file": {
  1081  			LoadOptionFns: []func(*LoadOptions) error{
  1082  				WithSharedConfigProfile("duplicate-profile"),
  1083  				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1084  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
  1085  				WithLogConfigurationWarnings(true),
  1086  				WithLogger(logger),
  1087  			},
  1088  			LoadFn: loadSharedConfig,
  1089  			Expect: SharedConfig{
  1090  				Profile: "duplicate-profile",
  1091  				Region:  "us-west-2",
  1092  			},
  1093  			ExpectLog: "overriding region value, with a region value found in a duplicate profile defined later in the same file",
  1094  			Err:       "",
  1095  		},
  1096  
  1097  		"profile prefix used in credentials files": {
  1098  			LoadOptionFns: []func(*LoadOptions) error{
  1099  				WithSharedConfigProfile("unused-profile"),
  1100  				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1101  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
  1102  				WithLogConfigurationWarnings(true),
  1103  				WithLogger(logger),
  1104  			},
  1105  			LoadFn:    loadSharedConfig,
  1106  			ExpectLog: "profile defined with name `profile unused-profile` is ignored.",
  1107  			Err:       "failed to get shared config profile, unused-profile",
  1108  		},
  1109  		"partial credentials in configuration files": {
  1110  			LoadOptionFns: []func(*LoadOptions) error{
  1111  				WithSharedConfigProfile("partial-creds-1"),
  1112  				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
  1113  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1114  				WithLogConfigurationWarnings(true),
  1115  				WithLogger(logger),
  1116  			},
  1117  			LoadFn: loadSharedConfig,
  1118  			Expect: SharedConfig{
  1119  				Profile: "partial-creds-1",
  1120  			},
  1121  			Err: "partial credentials",
  1122  		},
  1123  		"parital credentials in the credentials files": {
  1124  			LoadOptionFns: []func(*LoadOptions) error{
  1125  				WithSharedConfigProfile("partial-creds-1"),
  1126  				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1127  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
  1128  				WithLogConfigurationWarnings(true),
  1129  				WithLogger(logger),
  1130  			},
  1131  			LoadFn: loadSharedConfig,
  1132  			Expect: SharedConfig{
  1133  				Profile: "partial-creds-1",
  1134  			},
  1135  			Err: "partial credentials found for profile partial-creds-1",
  1136  		},
  1137  		"credentials override configuration profile": {
  1138  			LoadOptionFns: []func(*LoadOptions) error{
  1139  				WithSharedConfigProfile("complete"),
  1140  				WithSharedConfigFiles([]string{filepath.Join("testdata", "load_config")}),
  1141  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
  1142  				WithLogConfigurationWarnings(true),
  1143  				WithLogger(logger),
  1144  			},
  1145  			LoadFn: loadSharedConfig,
  1146  			Expect: SharedConfig{
  1147  				Profile: "complete",
  1148  				Credentials: aws.Credentials{
  1149  					AccessKeyID:     "credsAccessKey",
  1150  					SecretAccessKey: "credsSecretKey",
  1151  					Source: fmt.Sprintf("SharedConfigCredentials: %v",
  1152  						filepath.Join("testdata", "load_credentials")),
  1153  				},
  1154  				Region: "us-west-2",
  1155  			},
  1156  		},
  1157  		"credentials profile has complete credentials": {
  1158  			LoadOptionFns: []func(*LoadOptions) error{
  1159  				WithSharedConfigProfile("complete"),
  1160  				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1161  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "load_credentials")}),
  1162  				WithLogConfigurationWarnings(true),
  1163  				WithLogger(logger),
  1164  			},
  1165  			LoadFn: loadSharedConfig,
  1166  			Expect: SharedConfig{
  1167  				Profile: "complete",
  1168  				Credentials: aws.Credentials{
  1169  					AccessKeyID:     "credsAccessKey",
  1170  					SecretAccessKey: "credsSecretKey",
  1171  					Source:          fmt.Sprintf("SharedConfigCredentials: %v", filepath.Join("testdata", "load_credentials")),
  1172  				},
  1173  			},
  1174  		},
  1175  		"credentials split between multiple credentials files": {
  1176  			LoadOptionFns: []func(*LoadOptions) error{
  1177  				WithSharedConfigProfile("partial-creds-1"),
  1178  				WithSharedConfigFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1179  				WithSharedCredentialsFiles([]string{
  1180  					filepath.Join("testdata", "load_credentials"),
  1181  					filepath.Join("testdata", "load_credentials_secondary"),
  1182  				}),
  1183  				WithLogConfigurationWarnings(true),
  1184  				WithLogger(logger),
  1185  			},
  1186  			LoadFn: loadSharedConfig,
  1187  			Expect: SharedConfig{
  1188  				Profile: "partial-creds-1",
  1189  			},
  1190  			Err: "partial credentials",
  1191  		},
  1192  		"credentials split between multiple configuration files": {
  1193  			LoadOptionFns: []func(*LoadOptions) error{
  1194  				WithSharedConfigProfile("partial-creds-1"),
  1195  				WithSharedCredentialsFiles([]string{filepath.Join("testdata", "empty_creds_config")}),
  1196  				WithSharedConfigFiles([]string{
  1197  					filepath.Join("testdata", "load_config"),
  1198  					filepath.Join("testdata", "load_config_secondary"),
  1199  				}),
  1200  				WithLogConfigurationWarnings(true),
  1201  				WithLogger(logger),
  1202  			},
  1203  			LoadFn: loadSharedConfig,
  1204  			Expect: SharedConfig{
  1205  				Profile: "partial-creds-1",
  1206  				Region:  "us-west-2",
  1207  			},
  1208  			ExpectLog: "",
  1209  			Err:       "partial credentials",
  1210  		},
  1211  		"credentials split between credentials and config files": {
  1212  			LoadOptionFns: []func(*LoadOptions) error{
  1213  				WithSharedConfigProfile("partial-creds-1"),
  1214  				WithSharedConfigFiles([]string{
  1215  					filepath.Join("testdata", "load_config"),
  1216  				}),
  1217  				WithSharedCredentialsFiles([]string{
  1218  					filepath.Join("testdata", "load_credentials"),
  1219  				}),
  1220  				WithLogConfigurationWarnings(true),
  1221  				WithLogger(logger),
  1222  			},
  1223  			LoadFn: loadSharedConfig,
  1224  			Expect: SharedConfig{
  1225  				Profile: "partial-creds-1",
  1226  			},
  1227  			ExpectLog: "",
  1228  			Err:       "partial credentials",
  1229  		},
  1230  		"replaced profile with prefixed profile in config files": {
  1231  			LoadOptionFns: []func(*LoadOptions) error{
  1232  				WithSharedConfigProfile("replaced-profile"),
  1233  				WithSharedConfigFiles([]string{
  1234  					filepath.Join("testdata", "load_config"),
  1235  				}),
  1236  				WithSharedCredentialsFiles([]string{
  1237  					filepath.Join("testdata", "empty_creds_config"),
  1238  				}),
  1239  				WithLogConfigurationWarnings(true),
  1240  				WithLogger(logger),
  1241  			},
  1242  			LoadFn: loadSharedConfig,
  1243  			Expect: SharedConfig{
  1244  				Profile: "replaced-profile",
  1245  				Region:  "eu-west-1",
  1246  			},
  1247  			ExpectLog: "non-default profile not prefixed with `profile `",
  1248  		},
  1249  		"replaced profile with prefixed profile in credentials files": {
  1250  			LoadOptionFns: []func(*LoadOptions) error{
  1251  				WithSharedConfigProfile("replaced-profile"),
  1252  				WithSharedCredentialsFiles([]string{
  1253  					filepath.Join("testdata", "load_credentials"),
  1254  				}),
  1255  				WithSharedConfigFiles([]string{
  1256  					filepath.Join("testdata", "empty_creds_config"),
  1257  				}),
  1258  				WithLogConfigurationWarnings(true),
  1259  				WithLogger(logger),
  1260  			},
  1261  			LoadFn: loadSharedConfig,
  1262  			Expect: SharedConfig{
  1263  				Profile: "replaced-profile",
  1264  				Region:  "us-west-2",
  1265  			},
  1266  			ExpectLog: "profile defined with name `profile replaced-profile` is ignored.",
  1267  		},
  1268  		"ignored profiles w/o prefixed profile across credentials and config files": {
  1269  			LoadOptionFns: []func(*LoadOptions) error{
  1270  				WithSharedConfigProfile("replaced-profile"),
  1271  				WithSharedCredentialsFiles([]string{
  1272  					filepath.Join("testdata", "load_credentials"),
  1273  				}),
  1274  				WithSharedConfigFiles([]string{
  1275  					filepath.Join("testdata", "load_config"),
  1276  				}),
  1277  				WithLogConfigurationWarnings(true),
  1278  				WithLogger(logger),
  1279  			},
  1280  			LoadFn: loadSharedConfig,
  1281  			Expect: SharedConfig{
  1282  				Profile: "replaced-profile",
  1283  				Region:  "us-west-2",
  1284  			},
  1285  			ExpectLog: "profile defined with name `profile replaced-profile` is ignored.",
  1286  		},
  1287  		"1. profile with name as `profile` in config file": {
  1288  			LoadOptionFns: []func(*LoadOptions) error{
  1289  				WithSharedConfigProfile("profile"),
  1290  				WithSharedCredentialsFiles([]string{
  1291  					filepath.Join("testdata", "empty_creds_config"),
  1292  				}),
  1293  				WithSharedConfigFiles([]string{
  1294  					filepath.Join("testdata", "load_config"),
  1295  				}),
  1296  				WithLogConfigurationWarnings(true),
  1297  				WithLogger(logger),
  1298  			},
  1299  			LoadFn:    loadSharedConfig,
  1300  			Err:       "failed to get shared config profile, profile",
  1301  			ExpectLog: "profile defined with name `profile` is ignored",
  1302  		},
  1303  		"2. profile with name as `profile ` in config file": {
  1304  			LoadOptionFns: []func(*LoadOptions) error{
  1305  				WithSharedConfigProfile("profile "),
  1306  				WithSharedCredentialsFiles([]string{
  1307  					filepath.Join("testdata", "empty_creds_config"),
  1308  				}),
  1309  				WithSharedConfigFiles([]string{
  1310  					filepath.Join("testdata", "load_config"),
  1311  				}),
  1312  				WithLogConfigurationWarnings(true),
  1313  				WithLogger(logger),
  1314  			},
  1315  			LoadFn:    loadSharedConfig,
  1316  			Err:       "failed to get shared config profile, profile",
  1317  			ExpectLog: "profile defined with name `profile` is ignored",
  1318  		},
  1319  		"3. profile with name as `profile\t` in config file": {
  1320  			LoadOptionFns: []func(*LoadOptions) error{
  1321  				WithSharedConfigProfile("profile"),
  1322  				WithSharedCredentialsFiles([]string{
  1323  					filepath.Join("testdata", "empty_creds_config"),
  1324  				}),
  1325  				WithSharedConfigFiles([]string{
  1326  					filepath.Join("testdata", "load_config"),
  1327  				}),
  1328  				WithLogConfigurationWarnings(true),
  1329  				WithLogger(logger),
  1330  			},
  1331  			LoadFn:    loadSharedConfig,
  1332  			Err:       "failed to get shared config profile, profile",
  1333  			ExpectLog: "profile defined with name `profile` is ignored",
  1334  		},
  1335  		"profile with tabs as delimiter for profile prefix in config file": {
  1336  			LoadOptionFns: []func(*LoadOptions) error{
  1337  				WithSharedConfigProfile("with-tab"),
  1338  				WithSharedCredentialsFiles([]string{
  1339  					filepath.Join("testdata", "empty_creds_config"),
  1340  				}),
  1341  				WithSharedConfigFiles([]string{
  1342  					filepath.Join("testdata", "load_config"),
  1343  				}),
  1344  				WithLogConfigurationWarnings(true),
  1345  				WithLogger(logger),
  1346  			},
  1347  			LoadFn: loadSharedConfig,
  1348  			Expect: SharedConfig{
  1349  				Profile: "with-tab",
  1350  				Region:  "cn-north-1",
  1351  			},
  1352  		},
  1353  		"profile with tabs as delimiter for profile prefix in credentials file": {
  1354  			LoadOptionFns: []func(*LoadOptions) error{
  1355  				WithSharedConfigProfile("with-tab"),
  1356  				WithSharedCredentialsFiles([]string{
  1357  					filepath.Join("testdata", "load_credentials"),
  1358  				}),
  1359  				WithSharedConfigFiles([]string{
  1360  					filepath.Join("testdata", "empty_creds_config"),
  1361  				}),
  1362  				WithLogConfigurationWarnings(true),
  1363  				WithLogger(logger),
  1364  			},
  1365  			LoadFn:    loadSharedConfig,
  1366  			Err:       "failed to get shared config profile, with-tab",
  1367  			ExpectLog: "profile defined with name `profile with-tab` is ignored",
  1368  		},
  1369  		"profile with name as `profile profile` in credentials file": {
  1370  			LoadOptionFns: []func(*LoadOptions) error{
  1371  				WithSharedConfigProfile("profile"),
  1372  				WithSharedCredentialsFiles([]string{
  1373  					filepath.Join("testdata", "load_credentials"),
  1374  				}),
  1375  				WithSharedConfigFiles([]string{
  1376  					filepath.Join("testdata", "empty_creds_config"),
  1377  				}),
  1378  				WithLogConfigurationWarnings(true),
  1379  				WithLogger(logger),
  1380  			},
  1381  			LoadFn:    loadSharedConfig,
  1382  			Err:       "failed to get shared config profile, profile",
  1383  			ExpectLog: "profile defined with name `profile profile` is ignored",
  1384  		},
  1385  		"profile with name profile-bar in credentials file": {
  1386  			LoadOptionFns: []func(*LoadOptions) error{
  1387  				WithSharedConfigProfile("profile-bar"),
  1388  				WithSharedCredentialsFiles([]string{
  1389  					filepath.Join("testdata", "load_credentials"),
  1390  				}),
  1391  				WithSharedConfigFiles([]string{
  1392  					filepath.Join("testdata", "empty_creds_config"),
  1393  				}),
  1394  				WithLogConfigurationWarnings(true),
  1395  				WithLogger(logger),
  1396  			},
  1397  			LoadFn: loadSharedConfig,
  1398  			Expect: SharedConfig{
  1399  				Profile: "profile-bar",
  1400  				Region:  "us-west-2",
  1401  			},
  1402  		},
  1403  		"profile with name profile-bar in config file": {
  1404  			LoadOptionFns: []func(*LoadOptions) error{
  1405  				WithSharedConfigProfile("profile-bar"),
  1406  				WithSharedCredentialsFiles([]string{
  1407  					filepath.Join("testdata", "empty_creds_config"),
  1408  				}),
  1409  				WithSharedConfigFiles([]string{
  1410  					filepath.Join("testdata", "load_config"),
  1411  				}),
  1412  				WithLogConfigurationWarnings(true),
  1413  				WithLogger(logger),
  1414  			},
  1415  			LoadFn:    loadSharedConfig,
  1416  			Err:       "failed to get shared config profile, profile-bar",
  1417  			ExpectLog: "profile defined with name `profile-bar` is ignored",
  1418  		},
  1419  		"profile ignored in credentials and config file": {
  1420  			LoadOptionFns: []func(*LoadOptions) error{
  1421  				WithSharedConfigProfile("ignored-profile"),
  1422  				WithSharedCredentialsFiles([]string{
  1423  					filepath.Join("testdata", "load_credentials"),
  1424  				}),
  1425  				WithSharedConfigFiles([]string{
  1426  					filepath.Join("testdata", "load_config"),
  1427  				}),
  1428  				WithLogConfigurationWarnings(true),
  1429  				WithLogger(logger),
  1430  			},
  1431  			LoadFn:    loadSharedConfig,
  1432  			Err:       "failed to get shared config profile, ignored-profile",
  1433  			ExpectLog: "profile defined with name `ignored-profile` is ignored.",
  1434  		},
  1435  		"profile with sso_session": {
  1436  			LoadOptionFns: []func(*LoadOptions) error{
  1437  				WithSharedConfigProfile("sso-session-test"),
  1438  				WithSharedCredentialsFiles([]string{
  1439  					filepath.Join("testdata", "load_credentials"),
  1440  				}),
  1441  				WithSharedConfigFiles([]string{
  1442  					filepath.Join("testdata", "load_config"),
  1443  				}),
  1444  				WithLogConfigurationWarnings(true),
  1445  				WithLogger(logger),
  1446  			},
  1447  			LoadFn: loadSharedConfig,
  1448  			Expect: SharedConfig{
  1449  				Profile:        "sso-session-test",
  1450  				SSOSessionName: "dev-session",
  1451  				SSOSession: &SSOSession{
  1452  					Name:        "dev-session",
  1453  					SSORegion:   "us-west-2",
  1454  					SSOStartURL: "https://example.aws/start",
  1455  				},
  1456  			},
  1457  		},
  1458  		"config and creds files explicitly set to empty slice": {
  1459  			LoadOptionFns: []func(*LoadOptions) error{
  1460  				WithSharedCredentialsFiles([]string{}),
  1461  				WithSharedConfigFiles([]string{}),
  1462  				WithLogConfigurationWarnings(true),
  1463  				WithLogger(logger),
  1464  			},
  1465  			LoadFn: loadSharedConfig,
  1466  			Err:    "failed to get shared config profile, default",
  1467  		},
  1468  	}
  1469  
  1470  	for name, c := range cases {
  1471  		t.Run(name, func(t *testing.T) {
  1472  			defer loggerBuf.Reset()
  1473  
  1474  			var options LoadOptions
  1475  
  1476  			for _, fn := range c.LoadOptionFns {
  1477  				fn(&options)
  1478  			}
  1479  
  1480  			cfg, err := c.LoadFn(context.Background(), configs{options})
  1481  
  1482  			if e, a := c.ExpectLog, loggerBuf.String(); !strings.Contains(a, e) {
  1483  				t.Errorf("expect %v logged in %v", e, a)
  1484  			}
  1485  			if loggerBuf.Len() == 0 && len(c.ExpectLog) != 0 {
  1486  				t.Errorf("expected log, got none")
  1487  			}
  1488  
  1489  			if len(c.Err) > 0 {
  1490  				if err == nil {
  1491  					t.Fatalf("expected error %v, got none", c.Err)
  1492  				}
  1493  				if e, a := c.Err, err.Error(); !strings.Contains(a, e) {
  1494  					t.Fatalf("expect %q to be in %q", e, a)
  1495  				}
  1496  				return
  1497  			} else if err != nil {
  1498  				t.Fatalf("expect no error, got %v", err)
  1499  			}
  1500  
  1501  			if diff := cmpDiff(c.Expect, cfg); diff != "" {
  1502  				t.Errorf("expect shared config match\n%s", diff)
  1503  			}
  1504  		})
  1505  	}
  1506  }
  1507  

View as plain text