Skip to content

Commit 386a0d0

Browse files
authored
feat: client support credentialsProvider (aliyun#216)
* feat: client support credentialsProvider * fix build, remove 1.20 func usage * fix conflict * refine to code review * refine readme * use atomic, add ecs ram fetcher * add ram fetcher ut
1 parent 2c71d01 commit 386a0d0

15 files changed

+753
-43
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ go get -u github.com/aliyun/aliyun-log-go-sdk
3333
```go
3434
AccessKeyID = "your ak id"
3535
AccessKeySecret = "your ak secret"
36+
credentialsProvider := NewStaticCredentialsProvider(AccessKeyID, AccessKeySecret, "")
3637
Endpoint = "your endpoint" // just like cn-hangzhou.log.aliyuncs.com
37-
Client = sls.CreateNormalInterface(Endpoint,AccessKeyID,AccessKeySecret,"")
38+
Client = sls.CreateNormalInterfaceV2(Endpoint, credentialsProvider)
3839
```
3940

4041
2. **创建project**

client.go

+19-5
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,18 @@ func IsTokenError(err error) bool {
9696
// Client ...
9797
type Client struct {
9898
Endpoint string // IP or hostname of SLS endpoint
99-
AccessKeyID string
100-
AccessKeySecret string
101-
SecurityToken string
99+
AccessKeyID string // Deprecated: use credentialsProvider instead
100+
AccessKeySecret string // Deprecated: use credentialsProvider instead
101+
SecurityToken string // Deprecated: use credentialsProvider instead
102102
UserAgent string // default defaultLogUserAgent
103103
RequestTimeOut time.Duration
104104
RetryTimeOut time.Duration
105105
HTTPClient *http.Client
106106
Region string
107107
AuthVersion AuthVersionType // v1 or v4 signature,default is v1
108108

109-
accessKeyLock sync.RWMutex
109+
accessKeyLock sync.RWMutex
110+
credentialsProvider CredentialsProvider
110111
// User defined common headers.
111112
// When conflict with sdk pre-defined headers, the value will
112113
// be ignored
@@ -120,7 +121,13 @@ func convert(c *Client, projName string) *LogProject {
120121
}
121122

122123
func convertLocked(c *Client, projName string) *LogProject {
123-
p, _ := NewLogProject(projName, c.Endpoint, c.AccessKeyID, c.AccessKeySecret)
124+
var p *LogProject
125+
if c.credentialsProvider != nil {
126+
p, _ = NewLogProjectV2(projName, c.Endpoint, c.credentialsProvider)
127+
} else { // back compatible
128+
p, _ = NewLogProject(projName, c.Endpoint, c.AccessKeyID, c.AccessKeySecret)
129+
}
130+
124131
p.SecurityToken = c.SecurityToken
125132
p.UserAgent = c.UserAgent
126133
p.AuthVersion = c.AuthVersion
@@ -139,6 +146,12 @@ func convertLocked(c *Client, projName string) *LogProject {
139146
return p
140147
}
141148

149+
// Set credentialsProvider for client and returns the same client.
150+
func (c *Client) WithCredentialsProvider(provider CredentialsProvider) *Client {
151+
c.credentialsProvider = provider
152+
return c
153+
}
154+
142155
// SetUserAgent set a custom userAgent
143156
func (c *Client) SetUserAgent(userAgent string) {
144157
c.UserAgent = userAgent
@@ -169,6 +182,7 @@ func (c *Client) ResetAccessKeyToken(accessKeyID, accessKeySecret, securityToken
169182
c.AccessKeyID = accessKeyID
170183
c.AccessKeySecret = accessKeySecret
171184
c.SecurityToken = securityToken
185+
c.credentialsProvider = NewStaticCredentialsProvider(accessKeyID, accessKeySecret, securityToken)
172186
c.accessKeyLock.Unlock()
173187
}
174188

client_interface.go

+37-3
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,54 @@ import (
55
"time"
66
)
77

8-
// CreateNormalInterface create a normal client
8+
// CreateNormalInterface create a normal client.
9+
//
10+
// Deprecated: use CreateNormalInterfaceV2 instead.
11+
// If you keep using long-lived AccessKeyID and AccessKeySecret,
12+
// use the example code below.
13+
//
14+
// provider := NewStaticCredProvider(accessKeyID, accessKeySecret, securityToken)
15+
// client := CreateNormalInterfaceV2(endpoint, provider)
916
func CreateNormalInterface(endpoint, accessKeyID, accessKeySecret, securityToken string) ClientInterface {
1017
return &Client{
1118
Endpoint: endpoint,
1219
AccessKeyID: accessKeyID,
1320
AccessKeySecret: accessKeySecret,
1421
SecurityToken: securityToken,
22+
23+
credentialsProvider: NewStaticCredentialsProvider(
24+
accessKeyID,
25+
accessKeySecret,
26+
securityToken,
27+
),
28+
}
29+
}
30+
31+
// CreateNormalInterfaceV2 create a normal client, with a CredentialsProvider.
32+
//
33+
// It is highly recommended to use a CredentialsProvider that provides dynamic
34+
// expirable credentials for security.
35+
//
36+
// See [credentials_provider.go] for more details.
37+
func CreateNormalInterfaceV2(endpoint string, credentialsProvider CredentialsProvider) ClientInterface {
38+
return &Client{
39+
Endpoint: endpoint,
40+
credentialsProvider: credentialsProvider,
1541
}
1642
}
1743

18-
type UpdateTokenFunction func() (accessKeyID, accessKeySecret, securityToken string, expireTime time.Time, err error)
44+
type UpdateTokenFunction = func() (accessKeyID, accessKeySecret, securityToken string, expireTime time.Time, err error)
1945

20-
// CreateTokenAutoUpdateClient crate a TokenAutoUpdateClient
46+
// CreateTokenAutoUpdateClient create a TokenAutoUpdateClient,
2147
// this client will auto fetch security token and retry when operation is `Unauthorized`
48+
//
49+
// Deprecated: Use CreateNormalInterfaceV2 and UpdateFuncProviderAdapter instead.
50+
//
51+
// Example:
52+
//
53+
// provider := NewUpdateFuncProviderAdapter(updateStsTokenFunc)
54+
// client := CreateNormalInterfaceV2(endpoint, provider)
55+
//
2256
// @note TokenAutoUpdateClient will destroy when shutdown channel is closed
2357
func CreateTokenAutoUpdateClient(endpoint string, tokenUpdateFunc UpdateTokenFunction, shutdown <-chan struct{}) (client ClientInterface, err error) {
2458
accessKeyID, accessKeySecret, securityToken, expireTime, err := tokenUpdateFunc()

client_request.go

+10
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ func (c *Client) request(project, method, uri string, headers map[string]string,
5757
authVersion := c.AuthVersion
5858
c.accessKeyLock.RUnlock()
5959

60+
if c.credentialsProvider != nil {
61+
res, err := c.credentialsProvider.GetCredentials()
62+
if err != nil {
63+
return nil, fmt.Errorf("fail to fetch credentials: %w", err)
64+
}
65+
accessKeyID = res.AccessKeyID
66+
accessKeySecret = res.AccessKeySecret
67+
stsToken = res.SecurityToken
68+
}
69+
6070
// Access with token
6171
if stsToken != "" {
6272
headers[HTTPHeaderAcsSecurityToken] = stsToken

credentials.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package sls
2+
3+
import (
4+
"time"
5+
)
6+
7+
type Credentials struct {
8+
AccessKeyID string
9+
AccessKeySecret string
10+
SecurityToken string
11+
}
12+
13+
const DEFAULT_EXPIRED_FACTOR = 0.8
14+
15+
// Expirable credentials with an expiration.
16+
type TempCredentials struct {
17+
Credentials
18+
expiredFactor float64
19+
expirationInMills int64 // The time when the credentials expires, unix timestamp in millis
20+
lastUpdatedInMills int64
21+
}
22+
23+
func NewTempCredentials(accessKeyId, accessKeySecret, securityToken string,
24+
expirationInMills, lastUpdatedInMills int64) *TempCredentials {
25+
26+
return &TempCredentials{
27+
Credentials: Credentials{
28+
AccessKeyID: accessKeyId,
29+
AccessKeySecret: accessKeySecret,
30+
SecurityToken: securityToken,
31+
},
32+
expirationInMills: expirationInMills,
33+
lastUpdatedInMills: lastUpdatedInMills,
34+
expiredFactor: DEFAULT_EXPIRED_FACTOR,
35+
}
36+
}
37+
38+
// @param factor must > 0.0 and <= 1.0, the less the factor is,
39+
// the more frequently the credentials will be updated.
40+
//
41+
// If factor is set to 0, the credentials will be fetched every time
42+
// [GetCredentials] is called.
43+
//
44+
// If factor is set to 1, the credentials will be fetched only when expired .
45+
func (t *TempCredentials) WithExpiredFactor(factor float64) *TempCredentials {
46+
if factor > 0.0 && factor <= 1.0 {
47+
t.expiredFactor = factor
48+
}
49+
return t
50+
}
51+
52+
// Returns true if credentials has expired already or will expire soon.
53+
func (t *TempCredentials) ShouldRefresh() bool {
54+
now := time.Now().UnixMilli()
55+
if now >= t.expirationInMills {
56+
return true
57+
}
58+
duration := (float64)(t.expirationInMills-t.lastUpdatedInMills) * t.expiredFactor
59+
if duration < 0.0 { // check here
60+
duration = 0
61+
}
62+
return (now - t.lastUpdatedInMills) >= int64(duration)
63+
}
64+
65+
// Returns true if credentials has expired already.
66+
func (t *TempCredentials) HasExpired() bool {
67+
now := time.Now().UnixMilli()
68+
return now >= t.expirationInMills
69+
}

0 commit comments

Comments
 (0)