Просмотр исходного кода

Merge pull request #81 from tyrannosaurus-becks/add-credential-providers

Add credential providers
jxyowen 7 лет назад
Родитель
Сommit
9c7a9d28fc

+ 34 - 0
sdk/auth/credentials/providers/chain.go

@@ -0,0 +1,34 @@
+package providers
+
+import (
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth"
+)
+
+type Provider interface {
+	Retrieve() (auth.Credential, error)
+}
+
+// NewChainProvider will attempt to use its given providers in the order
+// in which they're provided. It will return credentials for the first
+// provider that doesn't return an error.
+func NewChainProvider(providers []Provider) Provider {
+	return &ChainProvider{
+		Providers: providers,
+	}
+}
+
+type ChainProvider struct {
+	Providers []Provider
+}
+
+func (p *ChainProvider) Retrieve() (auth.Credential, error) {
+	var lastErr error
+	for _, provider := range p.Providers {
+		creds, err := provider.Retrieve()
+		if err == nil {
+			return creds, nil
+		}
+		lastErr = err
+	}
+	return nil, lastErr
+}

+ 122 - 0
sdk/auth/credentials/providers/chain_test.go

@@ -0,0 +1,122 @@
+package providers
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+)
+
+func TestNewChainProvider_Retrieve_UseFirstCredential(t *testing.T) {
+
+	accessKeyCredProvider := &TestProvider{
+		Credential: &credentials.AccessKeyCredential{},
+		Err:        nil,
+	}
+
+	stsTokenCredProvider := &TestProvider{
+		Credential: &credentials.StsTokenCredential{},
+		Err:        nil,
+	}
+
+	roleCredential := &TestProvider{
+		Credential: &credentials.EcsRamRoleCredential{},
+		Err:        nil,
+	}
+
+	credential, err := NewChainProvider([]Provider{accessKeyCredProvider, stsTokenCredProvider, roleCredential}).Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, ok := credential.(*credentials.AccessKeyCredential); !ok {
+		t.Fatal("expected access key credential")
+	}
+}
+
+func TestNewChainProvider_Retrieve_UseSecondCredential(t *testing.T) {
+
+	accessKeyCredProvider := &TestProvider{
+		Credential: nil,
+		Err:        errors.New("I don't work"),
+	}
+
+	stsTokenCredProvider := &TestProvider{
+		Credential: &credentials.StsTokenCredential{},
+		Err:        nil,
+	}
+
+	roleCredential := &TestProvider{
+		Credential: &credentials.EcsRamRoleCredential{},
+		Err:        nil,
+	}
+
+	credential, err := NewChainProvider([]Provider{accessKeyCredProvider, stsTokenCredProvider, roleCredential}).Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, ok := credential.(*credentials.StsTokenCredential); !ok {
+		t.Fatal("expected sts token credential")
+	}
+}
+
+func TestNewChainProvider_Retrieve_UseThirdCredential(t *testing.T) {
+
+	accessKeyCredProvider := &TestProvider{
+		Credential: nil,
+		Err:        errors.New("I don't work"),
+	}
+
+	stsTokenCredProvider := &TestProvider{
+		Credential: nil,
+		Err:        errors.New("I don't work"),
+	}
+
+	roleCredential := &TestProvider{
+		Credential: &credentials.EcsRamRoleCredential{},
+		Err:        nil,
+	}
+
+	credential, err := NewChainProvider([]Provider{accessKeyCredProvider, stsTokenCredProvider, roleCredential}).Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, ok := credential.(*credentials.EcsRamRoleCredential); !ok {
+		t.Fatal("expected ecs ram role credential")
+	}
+}
+
+func TestNewChainProvider_Retrieve_NoneWork(t *testing.T) {
+
+	accessKeyCredProvider := &TestProvider{
+		Credential: nil,
+		Err:        errors.New("I don't work"),
+	}
+
+	stsTokenCredProvider := &TestProvider{
+		Credential: nil,
+		Err:        errors.New("I don't work"),
+	}
+
+	roleCredential := &TestProvider{
+		Credential: nil,
+		Err:        errors.New("I don't work"),
+	}
+
+	_, err := NewChainProvider([]Provider{accessKeyCredProvider, stsTokenCredProvider, roleCredential}).Retrieve()
+	if err == nil {
+		t.Fatal("expected error")
+	}
+}
+
+type TestProvider struct {
+	Credential auth.Credential
+	Err        error
+}
+
+func (p *TestProvider) Retrieve() (auth.Credential, error) {
+	return p.Credential, p.Err
+}

+ 62 - 0
sdk/auth/credentials/providers/configuration.go

@@ -0,0 +1,62 @@
+package providers
+
+import (
+	"errors"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+)
+
+var (
+	ErrNoValidCredentialsFound = errors.New("no valid credentials were found")
+)
+
+type Configuration struct {
+	AccessKeyID           string
+	AccessKeySecret       string
+	AccessKeyStsToken     string
+	RoleArn               string
+	RoleSessionName       string
+	RoleSessionExpiration *int
+	PrivateKey            string
+	PublicKeyID           string
+	SessionExpiration     *int
+	RoleName              string
+}
+
+func NewConfigurationCredentialProvider(configuration *Configuration) Provider {
+	return &ConfigurationProvider{
+		Configuration: configuration,
+	}
+}
+
+type ConfigurationProvider struct {
+	Configuration *Configuration
+}
+
+// Retrieve walks through all currently supported credential types and attempts to build them
+// using the given configuration.
+func (p *ConfigurationProvider) Retrieve() (auth.Credential, error) {
+
+	if p.Configuration.AccessKeyID != "" && p.Configuration.AccessKeySecret != "" {
+
+		if p.Configuration.RoleArn != "" && p.Configuration.RoleSessionName != "" && p.Configuration.RoleSessionExpiration != nil {
+			return credentials.NewRamRoleArnCredential(p.Configuration.AccessKeyID, p.Configuration.AccessKeySecret, p.Configuration.RoleArn, p.Configuration.RoleSessionName, *p.Configuration.RoleSessionExpiration), nil
+		}
+
+		if p.Configuration.AccessKeyStsToken != "" {
+			return credentials.NewStsTokenCredential(p.Configuration.AccessKeyID, p.Configuration.AccessKeySecret, p.Configuration.AccessKeyStsToken), nil
+		}
+
+		return credentials.NewAccessKeyCredential(p.Configuration.AccessKeyID, p.Configuration.AccessKeySecret), nil
+	}
+
+	if p.Configuration.RoleName != "" {
+		return credentials.NewEcsRamRoleCredential(p.Configuration.RoleName), nil
+	}
+
+	if p.Configuration.PrivateKey != "" && p.Configuration.PublicKeyID != "" && p.Configuration.SessionExpiration != nil {
+		return credentials.NewRsaKeyPairCredential(p.Configuration.PrivateKey, p.Configuration.PublicKeyID, *p.Configuration.SessionExpiration), nil
+	}
+	return nil, ErrNoValidCredentialsFound
+}

+ 167 - 0
sdk/auth/credentials/providers/configuration_test.go

@@ -0,0 +1,167 @@
+package providers
+
+import (
+	"testing"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+)
+
+func TestConfigurationProvider_Retrieve_NewRamRoleArnCredential(t *testing.T) {
+
+	expectedAccesKeyId := "access-key-id"
+	expectedAccessKeySecret := "access-key-secret"
+	expectedRoleArn := "role-arn"
+	expectedRoleSessionName := "role-session-name"
+	expectedRoleSessionExpiration := 1
+
+	configuration := &Configuration{
+		AccessKeyID:           expectedAccesKeyId,
+		AccessKeySecret:       expectedAccessKeySecret,
+		RoleArn:               expectedRoleArn,
+		RoleSessionName:       expectedRoleSessionName,
+		RoleSessionExpiration: &expectedRoleSessionExpiration,
+	}
+	credential, err := NewConfigurationCredentialProvider(configuration).Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	ramRoleArnCredential, ok := credential.(*credentials.RamRoleArnCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if ramRoleArnCredential.AccessKeyId != expectedAccesKeyId {
+		t.Fatalf("expected AccessKeyId %s but received %s", expectedAccesKeyId, ramRoleArnCredential.AccessKeyId)
+	}
+	if ramRoleArnCredential.AccessKeySecret != expectedAccessKeySecret {
+		t.Fatalf("expected AccessKeySecret %s but received %s", expectedAccessKeySecret, ramRoleArnCredential.AccessKeySecret)
+	}
+	if ramRoleArnCredential.RoleArn != expectedRoleArn {
+		t.Fatalf("expected RoleArn %s but received %s", expectedRoleArn, ramRoleArnCredential.RoleArn)
+	}
+	if ramRoleArnCredential.RoleSessionName != expectedRoleSessionName {
+		t.Fatalf("expected RoleSessionName %s but received %s", expectedRoleSessionName, ramRoleArnCredential.RoleSessionName)
+	}
+	if ramRoleArnCredential.RoleSessionExpiration != expectedRoleSessionExpiration {
+		t.Fatalf("expected expectedRoleSessionExpiration %d but received %d", expectedRoleSessionExpiration, ramRoleArnCredential.RoleSessionExpiration)
+	}
+}
+
+func TestConfigurationProvider_Retrieve_NewStsTokenCredential(t *testing.T) {
+
+	expectedAccesKeyId := "access-key-id"
+	expectedAccessKeySecret := "access-key-secret"
+	expectedAccessKeyStsToken := "access-key-sts-token"
+
+	configuration := &Configuration{
+		AccessKeyID:       expectedAccesKeyId,
+		AccessKeySecret:   expectedAccessKeySecret,
+		AccessKeyStsToken: expectedAccessKeyStsToken,
+	}
+	credential, err := NewConfigurationCredentialProvider(configuration).Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	stsTokenCredential, ok := credential.(*credentials.StsTokenCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if stsTokenCredential.AccessKeyId != expectedAccesKeyId {
+		t.Fatalf("expected AccessKeyId %s but received %s", expectedAccesKeyId, stsTokenCredential.AccessKeyId)
+	}
+	if stsTokenCredential.AccessKeySecret != expectedAccessKeySecret {
+		t.Fatalf("expected AccessKeySecret %s but received %s", expectedAccessKeySecret, stsTokenCredential.AccessKeySecret)
+	}
+	if stsTokenCredential.AccessKeyStsToken != expectedAccessKeyStsToken {
+		t.Fatalf("expected AccessKeyStsToken %s but received %s", expectedAccessKeyStsToken, stsTokenCredential.AccessKeyStsToken)
+	}
+}
+
+func TestConfigurationProvider_Retrieve_NewAccessKeyCredential(t *testing.T) {
+
+	expectedAccesKeyId := "access-key-id"
+	expectedAccessKeySecret := "access-key-secret"
+
+	configuration := &Configuration{
+		AccessKeyID:     expectedAccesKeyId,
+		AccessKeySecret: expectedAccessKeySecret,
+	}
+	credential, err := NewConfigurationCredentialProvider(configuration).Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	accessKeyCredential, ok := credential.(*credentials.AccessKeyCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if accessKeyCredential.AccessKeyId != expectedAccesKeyId {
+		t.Fatalf("expected AccessKeyId %s but received %s", expectedAccesKeyId, accessKeyCredential.AccessKeyId)
+	}
+	if accessKeyCredential.AccessKeySecret != expectedAccessKeySecret {
+		t.Fatalf("expected AccessKeySecret %s but received %s", expectedAccessKeySecret, accessKeyCredential.AccessKeySecret)
+	}
+}
+
+func TestConfigurationProvider_Retrieve_NewEcsRamRoleCredential(t *testing.T) {
+
+	expectedRoleName := "role-name"
+
+	configuration := &Configuration{
+		RoleName: expectedRoleName,
+	}
+	credential, err := NewConfigurationCredentialProvider(configuration).Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	ecsRamRoleCredential, ok := credential.(*credentials.EcsRamRoleCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if ecsRamRoleCredential.RoleName != expectedRoleName {
+		t.Fatalf("expected RoleName %s but received %s", expectedRoleName, ecsRamRoleCredential.RoleName)
+	}
+}
+
+func TestConfigurationProvider_Retrieve_NewRsaKeyPairCredential(t *testing.T) {
+
+	expectedPrivateKey := "private-key"
+	expectedPublicKeyId := "public-key-id"
+	expectedSessionExpiration := 1
+
+	configuration := &Configuration{
+		PrivateKey:        expectedPrivateKey,
+		PublicKeyID:       expectedPublicKeyId,
+		SessionExpiration: &expectedSessionExpiration,
+	}
+	credential, err := NewConfigurationCredentialProvider(configuration).Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	rsaKeyPairCredential, ok := credential.(*credentials.RsaKeyPairCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if rsaKeyPairCredential.PrivateKey != expectedPrivateKey {
+		t.Fatalf("expected PrivateKey %s but received %s", expectedPrivateKey, rsaKeyPairCredential.PrivateKey)
+	}
+	if rsaKeyPairCredential.PublicKeyId != expectedPublicKeyId {
+		t.Fatalf("expected PublicKeyId %s but received %s", expectedPublicKeyId, rsaKeyPairCredential.PublicKeyId)
+	}
+	if rsaKeyPairCredential.SessionExpiration != expectedSessionExpiration {
+		t.Fatalf("expected SessionExpiration %d but received %d", expectedSessionExpiration, rsaKeyPairCredential.SessionExpiration)
+	}
+}
+
+func TestConfigurationProvider_Retrieve_ErrNoValidCredentialsFound(t *testing.T) {
+	_, err := NewConfigurationCredentialProvider(&Configuration{}).Retrieve()
+	if err == nil {
+		t.Fatal("expected ErrNoValidCredentialsFound for empty configuration")
+	}
+	if err != ErrNoValidCredentialsFound {
+		t.Fatal("expected ErrNoValidCredentialsFound for empty configuration")
+	}
+}

+ 65 - 0
sdk/auth/credentials/providers/env.go

@@ -0,0 +1,65 @@
+package providers
+
+import (
+	"fmt"
+	"os"
+	"strconv"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth"
+)
+
+const (
+	EnvVarAccessKeyID           = "ALICLOUD_ACCESS_KEY"
+	EnvVarAccessKeySecret       = "ALICLOUD_SECRET_KEY"
+	EnvVarAccessKeyStsToken     = "ALICLOUD_ACCESS_KEY_STS_TOKEN"
+	EnvVarRoleArn               = "ALICLOUD_ROLE_ARN"
+	EnvVarRoleSessionName       = "ALICLOUD_ROLE_SESSION_NAME"
+	EnvVarRoleSessionExpiration = "ALICLOUD_ROLE_SESSION_EXPIRATION"
+	EnvVarPrivateKey            = "ALICLOUD_PRIVATE_KEY"
+	EnvVarPublicKeyID           = "ALICLOUD_PUBLIC_KEY_ID"
+	EnvVarSessionExpiration     = "ALICLOUD_SESSION_EXPIRATION"
+	EnvVarRoleName              = "ALICLOUD_ROLE_NAME"
+)
+
+func NewEnvCredentialProvider() Provider {
+	return &EnvProvider{}
+}
+
+type EnvProvider struct{}
+
+func (p *EnvProvider) Retrieve() (auth.Credential, error) {
+	roleSessionExpiration, err := envVarToInt(EnvVarRoleSessionExpiration)
+	if err != nil {
+		return nil, err
+	}
+	sessionExpiration, err := envVarToInt(EnvVarSessionExpiration)
+	if err != nil {
+		return nil, err
+	}
+	c := &Configuration{
+		AccessKeyID:           os.Getenv(EnvVarAccessKeyID),
+		AccessKeySecret:       os.Getenv(EnvVarAccessKeySecret),
+		AccessKeyStsToken:     os.Getenv(EnvVarAccessKeyStsToken),
+		RoleArn:               os.Getenv(EnvVarRoleArn),
+		RoleSessionName:       os.Getenv(EnvVarRoleSessionName),
+		RoleSessionExpiration: &roleSessionExpiration,
+		PrivateKey:            os.Getenv(EnvVarPrivateKey),
+		PublicKeyID:           os.Getenv(EnvVarPublicKeyID),
+		SessionExpiration:     &sessionExpiration,
+		RoleName:              os.Getenv(EnvVarRoleName),
+	}
+	return NewConfigurationCredentialProvider(c).Retrieve()
+}
+
+func envVarToInt(envVar string) (int, error) {
+	asInt := 0
+	asStr := os.Getenv(envVar)
+	if asStr != "" {
+		if i, err := strconv.Atoi(asStr); err != nil {
+			return 0, fmt.Errorf("error parsing %s: %s", envVar, err)
+		} else {
+			asInt = i
+		}
+	}
+	return asInt, nil
+}

+ 215 - 0
sdk/auth/credentials/providers/env_test.go

@@ -0,0 +1,215 @@
+package providers
+
+import (
+	"os"
+	"strconv"
+	"testing"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+)
+
+func TestEnvProvider_Retrieve_NewRamRoleArnCredential(t *testing.T) {
+
+	expectedAccesKeyId := "access-key-id"
+	expectedAccessKeySecret := "access-key-secret"
+	expectedRoleArn := "role-arn"
+	expectedRoleSessionName := "role-session-name"
+	expectedRoleSessionExpiration := 1
+
+	if err := os.Setenv(EnvVarAccessKeyID, expectedAccesKeyId); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarAccessKeyID)
+
+	if err := os.Setenv(EnvVarAccessKeySecret, expectedAccessKeySecret); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarAccessKeySecret)
+
+	if err := os.Setenv(EnvVarRoleArn, expectedRoleArn); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarRoleArn)
+
+	if err := os.Setenv(EnvVarRoleSessionName, expectedRoleSessionName); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarRoleSessionName)
+
+	if err := os.Setenv(EnvVarRoleSessionExpiration, strconv.Itoa(expectedRoleSessionExpiration)); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarRoleSessionExpiration)
+
+	credential, err := NewEnvCredentialProvider().Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	ramRoleArnCredential, ok := credential.(*credentials.RamRoleArnCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if ramRoleArnCredential.AccessKeyId != expectedAccesKeyId {
+		t.Fatalf("expected AccessKeyId %s but received %s", expectedAccesKeyId, ramRoleArnCredential.AccessKeyId)
+	}
+	if ramRoleArnCredential.AccessKeySecret != expectedAccessKeySecret {
+		t.Fatalf("expected AccessKeySecret %s but received %s", expectedAccessKeySecret, ramRoleArnCredential.AccessKeySecret)
+	}
+	if ramRoleArnCredential.RoleArn != expectedRoleArn {
+		t.Fatalf("expected RoleArn %s but received %s", expectedRoleArn, ramRoleArnCredential.RoleArn)
+	}
+	if ramRoleArnCredential.RoleSessionName != expectedRoleSessionName {
+		t.Fatalf("expected RoleSessionName %s but received %s", expectedRoleSessionName, ramRoleArnCredential.RoleSessionName)
+	}
+	if ramRoleArnCredential.RoleSessionExpiration != expectedRoleSessionExpiration {
+		t.Fatalf("expected RoleSessionExpiration %d but received %d", expectedRoleSessionExpiration, ramRoleArnCredential.RoleSessionExpiration)
+	}
+}
+
+func TestEnvProvider_Retrieve_NewStsTokenCredential(t *testing.T) {
+
+	expectedAccesKeyId := "access-key-id"
+	expectedAccessKeySecret := "access-key-secret"
+	expectedAccessKeyStsToken := "access-key-sts-token"
+
+	if err := os.Setenv(EnvVarAccessKeyID, expectedAccesKeyId); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarAccessKeyID)
+
+	if err := os.Setenv(EnvVarAccessKeySecret, expectedAccessKeySecret); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarAccessKeySecret)
+
+	if err := os.Setenv(EnvVarAccessKeyStsToken, expectedAccessKeyStsToken); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarAccessKeyStsToken)
+
+	credential, err := NewEnvCredentialProvider().Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	stsTokenCredential, ok := credential.(*credentials.StsTokenCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if stsTokenCredential.AccessKeyId != expectedAccesKeyId {
+		t.Fatalf("expected AccessKeyId %s but received %s", expectedAccesKeyId, stsTokenCredential.AccessKeyId)
+	}
+	if stsTokenCredential.AccessKeySecret != expectedAccessKeySecret {
+		t.Fatalf("expected AccessKeySecret %s but received %s", expectedAccessKeySecret, stsTokenCredential.AccessKeySecret)
+	}
+	if stsTokenCredential.AccessKeyStsToken != expectedAccessKeyStsToken {
+		t.Fatalf("expected AccessKeyStsToken %s but received %s", expectedAccessKeyStsToken, stsTokenCredential.AccessKeyStsToken)
+	}
+}
+
+func TestEnvProvider_Retrieve_NewAccessKeyCredential(t *testing.T) {
+
+	expectedAccesKeyId := "access-key-id"
+	expectedAccessKeySecret := "access-key-secret"
+
+	if err := os.Setenv(EnvVarAccessKeyID, expectedAccesKeyId); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarAccessKeyID)
+
+	if err := os.Setenv(EnvVarAccessKeySecret, expectedAccessKeySecret); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarAccessKeySecret)
+
+	credential, err := NewEnvCredentialProvider().Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	accessKeyCredential, ok := credential.(*credentials.AccessKeyCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if accessKeyCredential.AccessKeyId != expectedAccesKeyId {
+		t.Fatalf("expected AccessKeyId %s but received %s", expectedAccesKeyId, accessKeyCredential.AccessKeyId)
+	}
+	if accessKeyCredential.AccessKeySecret != expectedAccessKeySecret {
+		t.Fatalf("expected AccessKeySecret %s but received %s", expectedAccessKeySecret, accessKeyCredential.AccessKeySecret)
+	}
+}
+
+func TestEnvProvider_Retrieve_NewEcsRamRoleCredential(t *testing.T) {
+
+	expectedRoleName := "role-name"
+
+	if err := os.Setenv(EnvVarRoleName, expectedRoleName); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarRoleName)
+
+	credential, err := NewEnvCredentialProvider().Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	ecsRamRoleCredential, ok := credential.(*credentials.EcsRamRoleCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if ecsRamRoleCredential.RoleName != expectedRoleName {
+		t.Fatalf("expected RoleName %s but received %s", expectedRoleName, ecsRamRoleCredential.RoleName)
+	}
+}
+
+func TestEnvProvider_Retrieve_NewRsaKeyPairCredential(t *testing.T) {
+
+	expectedPrivateKey := "private-key"
+	expectedPublicKeyId := "public-key-id"
+	expectedSessionExpiration := 1
+
+	if err := os.Setenv(EnvVarPrivateKey, expectedPrivateKey); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarPrivateKey)
+
+	if err := os.Setenv(EnvVarPublicKeyID, expectedPublicKeyId); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarPublicKeyID)
+
+	if err := os.Setenv(EnvVarSessionExpiration, strconv.Itoa(expectedSessionExpiration)); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Unsetenv(EnvVarSessionExpiration)
+
+	credential, err := NewEnvCredentialProvider().Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+	rsaKeyPairCredential, ok := credential.(*credentials.RsaKeyPairCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if rsaKeyPairCredential.PrivateKey != expectedPrivateKey {
+		t.Fatalf("expected PrivateKey %s but received %s", expectedPrivateKey, rsaKeyPairCredential.PrivateKey)
+	}
+	if rsaKeyPairCredential.PublicKeyId != expectedPublicKeyId {
+		t.Fatalf("expected PublicKeyId %s but received %s", expectedPublicKeyId, rsaKeyPairCredential.PublicKeyId)
+	}
+	if rsaKeyPairCredential.SessionExpiration != expectedSessionExpiration {
+		t.Fatalf("expected SessionExpiration %d but received %d", expectedSessionExpiration, rsaKeyPairCredential.SessionExpiration)
+	}
+}
+
+func TestEnvProvider_Retrieve_ErrNoValidCredentialsFound(t *testing.T) {
+	_, err := NewEnvCredentialProvider().Retrieve()
+	if err == nil {
+		t.Fatal("expected ErrNoValidCredentialsFound for empty configuration")
+	}
+	if err != ErrNoValidCredentialsFound {
+		t.Fatal("expected ErrNoValidCredentialsFound for empty configuration")
+	}
+}

+ 86 - 0
sdk/auth/credentials/providers/instance_metadata.go

@@ -0,0 +1,86 @@
+package providers
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+)
+
+var securityCredURL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/"
+
+func NewInstanceMetadataProvider() Provider {
+	return &InstanceMetadataProvider{}
+}
+
+type InstanceMetadataProvider struct {
+	RoleName string
+}
+
+func (p *InstanceMetadataProvider) Retrieve() (auth.Credential, error) {
+	if p.RoleName == "" {
+		// Instances can have only one role name that never changes,
+		// so attempt to populate it.
+		// If this call is executed in an environment that doesn't support instance metadata,
+		// it will time out after 30 seconds and return an err.
+		resp, err := http.Get(securityCredURL)
+		if err != nil {
+			return nil, err
+		}
+		defer resp.Body.Close()
+
+		bodyBytes, _ := ioutil.ReadAll(resp.Body)
+		if resp.StatusCode != 200 {
+			return nil, fmt.Errorf("received %d getting role name: %s", resp.StatusCode, bodyBytes)
+		}
+		roleName := string(bodyBytes)
+		if roleName == "" {
+			return nil, errors.New("unable to retrieve role name, it may be unset")
+		}
+		p.RoleName = roleName
+	}
+
+	resp, err := http.Get(securityCredURL + p.RoleName)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != 200 {
+		return nil, fmt.Errorf("received %d getting security credentials for %s", resp.StatusCode, p.RoleName)
+	}
+	body := make(map[string]interface{})
+	if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
+		return nil, err
+	}
+
+	accessKeyID, err := extractString(body, "AccessKeyId")
+	if err != nil {
+		return nil, err
+	}
+	accessKeySecret, err := extractString(body, "AccessKeySecret")
+	if err != nil {
+		return nil, err
+	}
+	securityToken, err := extractString(body, "SecurityToken")
+	if err != nil {
+		return nil, err
+	}
+	return credentials.NewStsTokenCredential(accessKeyID, accessKeySecret, securityToken), nil
+}
+
+func extractString(m map[string]interface{}, key string) (string, error) {
+	raw, ok := m[key]
+	if !ok {
+		return "", fmt.Errorf("%s not in %+v", key, m)
+	}
+	str, ok := raw.(string)
+	if !ok {
+		return "", fmt.Errorf("%s is not a string in %+v", key, m)
+	}
+	return str, nil
+}

+ 65 - 0
sdk/auth/credentials/providers/instance_metadata_test.go

@@ -0,0 +1,65 @@
+package providers
+
+import (
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+)
+
+func TestInstanceMetadataProvider_Retrieve_Success(t *testing.T) {
+
+	// Start a test server locally.
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+		body := "unsupported path: " + r.URL.Path
+		status := 500
+
+		switch r.URL.Path {
+		case "/latest/meta-data/ram/security-credentials/":
+			body = "ELK"
+			status = 200
+		case "/latest/meta-data/ram/security-credentials/ELK":
+			body = ` {
+			  "AccessKeyId" : "STS.L4aBSCSJVMuKg5U1vFDw",
+			  "AccessKeySecret" : "wyLTSmsyPGP1ohvvw8xYgB29dlGI8KMiH2pKCNZ9",
+			  "Expiration" : "2018-08-20T22:30:02Z",
+			  "SecurityToken" : "CAESrAIIARKAAShQquMnLIlbvEcIxO6wCoqJufs8sWwieUxu45hS9AvKNEte8KRUWiJWJ6Y+YHAPgNwi7yfRecMFydL2uPOgBI7LDio0RkbYLmJfIxHM2nGBPdml7kYEOXmJp2aDhbvvwVYIyt/8iES/R6N208wQh0Pk2bu+/9dvalp6wOHF4gkFGhhTVFMuTDRhQlNDU0pWTXVLZzVVMXZGRHciBTQzMjc0KgVhbGljZTCpnJjwySk6BlJzYU1ENUJuCgExGmkKBUFsbG93Eh8KDEFjdGlvbkVxdWFscxIGQWN0aW9uGgcKBW9zczoqEj8KDlJlc291cmNlRXF1YWxzEghSZXNvdXJjZRojCiFhY3M6b3NzOio6NDMyNzQ6c2FtcGxlYm94L2FsaWNlLyo=",
+			  "LastUpdated" : "2018-08-20T16:30:01Z",
+			  "Code" : "Success"
+			}`
+			status = 200
+		}
+		w.Write([]byte(body))
+		w.WriteHeader(status)
+	}))
+	defer ts.Close()
+
+	// Update our securityCredURL to point at our local test server.
+	originalSecurityCredURL := securityCredURL
+	securityCredURL = strings.Replace(securityCredURL, "http://100.100.100.200", ts.URL, -1)
+	defer func() {
+		securityCredURL = originalSecurityCredURL
+	}()
+
+	credential, err := NewInstanceMetadataProvider().Retrieve()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	stsTokenCredential, ok := credential.(*credentials.StsTokenCredential)
+	if !ok {
+		t.Fatal("expected AccessKeyCredential")
+	}
+
+	if stsTokenCredential.AccessKeyId != "STS.L4aBSCSJVMuKg5U1vFDw" {
+		t.Fatalf("expected AccessKeyId STS.L4aBSCSJVMuKg5U1vFDw but received %s", stsTokenCredential.AccessKeyId)
+	}
+	if stsTokenCredential.AccessKeySecret != "wyLTSmsyPGP1ohvvw8xYgB29dlGI8KMiH2pKCNZ9" {
+		t.Fatalf("expected AccessKeySecret wyLTSmsyPGP1ohvvw8xYgB29dlGI8KMiH2pKCNZ9 but received %s", stsTokenCredential.AccessKeySecret)
+	}
+	if !strings.HasPrefix(stsTokenCredential.AccessKeyStsToken, "CAESrAIIARKAA") {
+		t.Fatalf("expected AccessKeyStsToken starting with CAESrAIIARKAA but received %s", stsTokenCredential.AccessKeyStsToken)
+	}
+}