diff --git a/README.md b/README.md index a716ccbf..739aebed 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,9 @@ go get -u github.com/aliyun/aliyun-log-go-sdk ```go AccessKeyID = "your ak id" AccessKeySecret = "your ak secret" + credentialsProvider := NewStaticCredentialsProvider(AccessKeyID, AccessKeySecret, "") Endpoint = "your endpoint" // just like cn-hangzhou.log.aliyuncs.com - Client = sls.CreateNormalInterface(Endpoint,AccessKeyID,AccessKeySecret,"") + Client = sls.CreateNormalInterfaceV2(Endpoint, credentialsProvider) ``` 2. **创建project** diff --git a/client.go b/client.go index de4b8790..ca65a7ea 100644 --- a/client.go +++ b/client.go @@ -96,9 +96,9 @@ func IsTokenError(err error) bool { // Client ... type Client struct { Endpoint string // IP or hostname of SLS endpoint - AccessKeyID string - AccessKeySecret string - SecurityToken string + AccessKeyID string // Deprecated: use credentialsProvider instead + AccessKeySecret string // Deprecated: use credentialsProvider instead + SecurityToken string // Deprecated: use credentialsProvider instead UserAgent string // default defaultLogUserAgent RequestTimeOut time.Duration RetryTimeOut time.Duration @@ -106,7 +106,8 @@ type Client struct { Region string AuthVersion AuthVersionType // v1 or v4 signature,default is v1 - accessKeyLock sync.RWMutex + accessKeyLock sync.RWMutex + credentialsProvider CredentialsProvider // User defined common headers. // When conflict with sdk pre-defined headers, the value will // be ignored @@ -120,7 +121,13 @@ func convert(c *Client, projName string) *LogProject { } func convertLocked(c *Client, projName string) *LogProject { - p, _ := NewLogProject(projName, c.Endpoint, c.AccessKeyID, c.AccessKeySecret) + var p *LogProject + if c.credentialsProvider != nil { + p, _ = NewLogProjectV2(projName, c.Endpoint, c.credentialsProvider) + } else { // back compatible + p, _ = NewLogProject(projName, c.Endpoint, c.AccessKeyID, c.AccessKeySecret) + } + p.SecurityToken = c.SecurityToken p.UserAgent = c.UserAgent p.AuthVersion = c.AuthVersion @@ -139,6 +146,12 @@ func convertLocked(c *Client, projName string) *LogProject { return p } +// Set credentialsProvider for client and returns the same client. +func (c *Client) WithCredentialsProvider(provider CredentialsProvider) *Client { + c.credentialsProvider = provider + return c +} + // SetUserAgent set a custom userAgent func (c *Client) SetUserAgent(userAgent string) { c.UserAgent = userAgent @@ -169,6 +182,7 @@ func (c *Client) ResetAccessKeyToken(accessKeyID, accessKeySecret, securityToken c.AccessKeyID = accessKeyID c.AccessKeySecret = accessKeySecret c.SecurityToken = securityToken + c.credentialsProvider = NewStaticCredentialsProvider(accessKeyID, accessKeySecret, securityToken) c.accessKeyLock.Unlock() } diff --git a/client_interface.go b/client_interface.go index 62e7a081..bde91cd2 100644 --- a/client_interface.go +++ b/client_interface.go @@ -5,20 +5,54 @@ import ( "time" ) -// CreateNormalInterface create a normal client +// CreateNormalInterface create a normal client. +// +// Deprecated: use CreateNormalInterfaceV2 instead. +// If you keep using long-lived AccessKeyID and AccessKeySecret, +// use the example code below. +// +// provider := NewStaticCredProvider(accessKeyID, accessKeySecret, securityToken) +// client := CreateNormalInterfaceV2(endpoint, provider) func CreateNormalInterface(endpoint, accessKeyID, accessKeySecret, securityToken string) ClientInterface { return &Client{ Endpoint: endpoint, AccessKeyID: accessKeyID, AccessKeySecret: accessKeySecret, SecurityToken: securityToken, + + credentialsProvider: NewStaticCredentialsProvider( + accessKeyID, + accessKeySecret, + securityToken, + ), + } +} + +// CreateNormalInterfaceV2 create a normal client, with a CredentialsProvider. +// +// It is highly recommended to use a CredentialsProvider that provides dynamic +// expirable credentials for security. +// +// See [credentials_provider.go] for more details. +func CreateNormalInterfaceV2(endpoint string, credentialsProvider CredentialsProvider) ClientInterface { + return &Client{ + Endpoint: endpoint, + credentialsProvider: credentialsProvider, } } -type UpdateTokenFunction func() (accessKeyID, accessKeySecret, securityToken string, expireTime time.Time, err error) +type UpdateTokenFunction = func() (accessKeyID, accessKeySecret, securityToken string, expireTime time.Time, err error) -// CreateTokenAutoUpdateClient crate a TokenAutoUpdateClient +// CreateTokenAutoUpdateClient create a TokenAutoUpdateClient, // this client will auto fetch security token and retry when operation is `Unauthorized` +// +// Deprecated: Use CreateNormalInterfaceV2 and UpdateFuncProviderAdapter instead. +// +// Example: +// +// provider := NewUpdateFuncProviderAdapter(updateStsTokenFunc) +// client := CreateNormalInterfaceV2(endpoint, provider) +// // @note TokenAutoUpdateClient will destroy when shutdown channel is closed func CreateTokenAutoUpdateClient(endpoint string, tokenUpdateFunc UpdateTokenFunction, shutdown <-chan struct{}) (client ClientInterface, err error) { accessKeyID, accessKeySecret, securityToken, expireTime, err := tokenUpdateFunc() diff --git a/client_request.go b/client_request.go index e76546ad..d8064180 100644 --- a/client_request.go +++ b/client_request.go @@ -57,6 +57,16 @@ func (c *Client) request(project, method, uri string, headers map[string]string, authVersion := c.AuthVersion c.accessKeyLock.RUnlock() + if c.credentialsProvider != nil { + res, err := c.credentialsProvider.GetCredentials() + if err != nil { + return nil, fmt.Errorf("fail to fetch credentials: %w", err) + } + accessKeyID = res.AccessKeyID + accessKeySecret = res.AccessKeySecret + stsToken = res.SecurityToken + } + // Access with token if stsToken != "" { headers[HTTPHeaderAcsSecurityToken] = stsToken diff --git a/credentials.go b/credentials.go new file mode 100644 index 00000000..bfbdc95f --- /dev/null +++ b/credentials.go @@ -0,0 +1,69 @@ +package sls + +import ( + "time" +) + +type Credentials struct { + AccessKeyID string + AccessKeySecret string + SecurityToken string +} + +const DEFAULT_EXPIRED_FACTOR = 0.8 + +// Expirable credentials with an expiration. +type TempCredentials struct { + Credentials + expiredFactor float64 + expirationInMills int64 // The time when the credentials expires, unix timestamp in millis + lastUpdatedInMills int64 +} + +func NewTempCredentials(accessKeyId, accessKeySecret, securityToken string, + expirationInMills, lastUpdatedInMills int64) *TempCredentials { + + return &TempCredentials{ + Credentials: Credentials{ + AccessKeyID: accessKeyId, + AccessKeySecret: accessKeySecret, + SecurityToken: securityToken, + }, + expirationInMills: expirationInMills, + lastUpdatedInMills: lastUpdatedInMills, + expiredFactor: DEFAULT_EXPIRED_FACTOR, + } +} + +// @param factor must > 0.0 and <= 1.0, the less the factor is, +// the more frequently the credentials will be updated. +// +// If factor is set to 0, the credentials will be fetched every time +// [GetCredentials] is called. +// +// If factor is set to 1, the credentials will be fetched only when expired . +func (t *TempCredentials) WithExpiredFactor(factor float64) *TempCredentials { + if factor > 0.0 && factor <= 1.0 { + t.expiredFactor = factor + } + return t +} + +// Returns true if credentials has expired already or will expire soon. +func (t *TempCredentials) ShouldRefresh() bool { + now := time.Now().UnixMilli() + if now >= t.expirationInMills { + return true + } + duration := (float64)(t.expirationInMills-t.lastUpdatedInMills) * t.expiredFactor + if duration < 0.0 { // check here + duration = 0 + } + return (now - t.lastUpdatedInMills) >= int64(duration) +} + +// Returns true if credentials has expired already. +func (t *TempCredentials) HasExpired() bool { + now := time.Now().UnixMilli() + return now >= t.expirationInMills +} diff --git a/credentials_provider.go b/credentials_provider.go new file mode 100644 index 00000000..208fa2b8 --- /dev/null +++ b/credentials_provider.go @@ -0,0 +1,243 @@ +package sls + +import ( + "encoding/json" + "fmt" + io "io" + "net/http" + "strings" + "time" + + "go.uber.org/atomic" + + "github.com/go-kit/kit/log/level" +) + +const CRED_TIME_FORMAT = time.RFC3339 + +type CredentialsProvider interface { + GetCredentials() (Credentials, error) +} + +/** + * A static credetials provider that always returns the same long-lived credentials. + * For back compatible. + */ +type StaticCredentialsProvider struct { + Cred Credentials +} + +// Create a static credential provider with AccessKeyID/AccessKeySecret/SecurityToken. +// +// Param accessKeyID and accessKeySecret must not be an empty string. +func NewStaticCredentialsProvider(accessKeyID, accessKeySecret, securityToken string) *StaticCredentialsProvider { + return &StaticCredentialsProvider{ + Cred: Credentials{ + AccessKeyID: accessKeyID, + AccessKeySecret: accessKeySecret, + SecurityToken: securityToken, + }, + } +} + +func (p *StaticCredentialsProvider) GetCredentials() (Credentials, error) { + return p.Cred, nil +} + +type CredentialsRequestBuilder = func() (*http.Request, error) +type CredentialsRespParser = func(*http.Response) (*TempCredentials, error) +type CredentialsFetcher = func() (*TempCredentials, error) + +// Combine RequestBuilder and RespParser, return a CredentialsFetcher +func NewCredentialsFetcher(builder CredentialsRequestBuilder, parser CredentialsRespParser, customClient *http.Client) CredentialsFetcher { + return func() (*TempCredentials, error) { + req, err := builder() + if err != nil { + return nil, fmt.Errorf("fail to build http request: %w", err) + } + + var client *http.Client + if customClient != nil { + client = customClient + } else { + client = &http.Client{} + } + + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("fail to do http request: %w", err) + } + defer resp.Body.Close() + cred, err := parser(resp) + if err != nil { + return nil, fmt.Errorf("fail to parse http response: %w", err) + } + return cred, nil + } +} + +// Wraps a CredentialsFetcher with retry. +// +// @param retryTimes If <= 0, no retry will be performed. +func fetcherWithRetry(fetcher CredentialsFetcher, retryTimes int) CredentialsFetcher { + return func() (*TempCredentials, error) { + var errs []error + for i := 0; i <= retryTimes; i++ { + cred, err := fetcher() + if err == nil { + + return cred, nil + } + errs = append(errs, err) + } + return nil, fmt.Errorf("exceed max retry times, errors: %w", + joinErrors(errs...)) + } +} + +// Replace this with errors.Join when go version >= 1.20 +func joinErrors(errs ...error) error { + if errs == nil { + return nil + } + errStrs := make([]string, 0, len(errs)) + for _, e := range errs { + errStrs = append(errStrs, e.Error()) + } + return fmt.Errorf("[%s]", strings.Join(errStrs, ", ")) +} + +const UPDATE_FUNC_RETRY_TIMES = 3 +const UPDATE_FUNC_FETCH_ADVANCED_DURATION = time.Second * 60 * 10 + +// Adapter for porting UpdateTokenFunc to a CredentialsProvider. +type UpdateFuncProviderAdapter struct { + cred atomic.Value // type *Credentials + + fetcher CredentialsFetcher + + expirationInMills atomic.Int64 + advanceDuration time.Duration // fetch before credentials expires in advance +} + +// Returns a new CredentialsProvider. +func NewUpdateFuncProviderAdapter(updateFunc UpdateTokenFunction) *UpdateFuncProviderAdapter { + retryTimes := UPDATE_FUNC_RETRY_TIMES + fetcher := fetcherWithRetry(updateFuncFetcher(updateFunc), retryTimes) + + return &UpdateFuncProviderAdapter{ + advanceDuration: UPDATE_FUNC_FETCH_ADVANCED_DURATION, + fetcher: fetcher, + } +} + +func updateFuncFetcher(updateFunc UpdateTokenFunction) CredentialsFetcher { + return func() (*TempCredentials, error) { + id, secret, token, expireTime, err := updateFunc() + if err != nil { + return nil, fmt.Errorf("updateTokenFunc fetch credentials failed: %w", err) + } + + if !checkSTSTokenValid(id, secret, token, expireTime) { + return nil, fmt.Errorf("updateTokenFunc result not valid, expirationTime:%s", + expireTime.Format(time.RFC3339)) + } + return NewTempCredentials(id, secret, token, expireTime.UnixMilli(), -1), nil + } + +} + +// If credentials expires or will be exipred soon, fetch a new credentials and return it. +// +// Otherwise returns the credentials fetched last time. +// +// Retry at most maxRetryTimes if failed to fetch. +func (adp *UpdateFuncProviderAdapter) GetCredentials() (Credentials, error) { + if !adp.shouldRefresh() { + res := adp.cred.Load().(*Credentials) + return *res, nil + } + level.Debug(Logger).Log("reason", "updateTokenFunc start to fetch new credentials") + + res, err := adp.fetcher() // res.lastUpdatedTime is not valid, do not use it + + if err != nil { + return Credentials{}, fmt.Errorf("updateTokenFunc fail to fetch credentials, err:%w", err) + } + + adp.cred.Store(&res.Credentials) + adp.expirationInMills.Store(res.expirationInMills) + level.Debug(Logger).Log("reason", "updateTokenFunc fetch new credentials succeed", + "expirationTime", time.UnixMilli(res.expirationInMills).Format(CRED_TIME_FORMAT), + ) + return res.Credentials, nil +} + +// Returns true if no credentials ever fetched or credentials expired, +// or credentials will be expired soon +func (adp *UpdateFuncProviderAdapter) shouldRefresh() bool { + v := adp.cred.Load() + if v == nil { + return true + } + now := time.Now() + return time.UnixMilli(adp.expirationInMills.Load()).Sub(now) <= adp.advanceDuration +} + +func checkSTSTokenValid(accessKeyID, accessKeySecret, securityToken string, expirationTime time.Time) bool { + return accessKeyID != "" && accessKeySecret != "" && expirationTime.UnixMilli() > 0 +} + +const ECS_RAM_ROLE_URL_PREFIX = "http://100.100.100.200/latest/meta-data/ram/security-credentials/" +const ECS_RAM_ROLE_RETRY_TIMES = 3 + +func NewEcsRamRoleFetcher(urlPrefix, ramRole string, customClient *http.Client) CredentialsFetcher { + return NewCredentialsFetcher(newEcsRamRoleReqBuilder(urlPrefix, ramRole), + ecsRamRoleParser, customClient) +} + +// Build http GET request with url(urlPrefix + ramRole) +func newEcsRamRoleReqBuilder(urlPrefix, ramRole string) func() (*http.Request, error) { + return func() (*http.Request, error) { + url := urlPrefix + ramRole + return http.NewRequest(http.MethodGet, url, nil) + } +} + +// Parse ECS Ram Role http response, convert it to TempCredentials +func ecsRamRoleParser(resp *http.Response) (*TempCredentials, error) { + // 1. read body + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("fail to read http resp body: %w", err) + } + fetchResp := EcsRamRoleHttpResp{} + // 2. unmarshal + err = json.Unmarshal(data, &fetchResp) + if err != nil { + return nil, fmt.Errorf("fail to unmarshal json: %w, body: %s", err, string(data)) + } + // 3. check json param + if !fetchResp.isValid() { + return nil, fmt.Errorf("invalid fetch result, body: %s", string(data)) + } + return NewTempCredentials( + fetchResp.AccessKeyID, + fetchResp.AccessKeySecret, + fetchResp.SecurityToken, fetchResp.Expiration, fetchResp.LastUpdated), nil +} + +// Response struct for http response of ecs ram role fetch request +type EcsRamRoleHttpResp struct { + Code string `json:"Code"` + AccessKeyID string `json:"AccessKeyId"` + AccessKeySecret string `json:"AccessKeySecret"` + SecurityToken string `json:"SecurityToken"` + Expiration int64 `json:"Expiration"` + LastUpdated int64 `json:"LastUpdated"` +} + +func (r *EcsRamRoleHttpResp) isValid() bool { + return strings.ToLower(r.Code) == "success" && r.AccessKeyID != "" && + r.AccessKeySecret != "" && r.Expiration > 0 && r.LastUpdated > 0 +} diff --git a/credentials_test.go b/credentials_test.go new file mode 100644 index 00000000..bddc66c2 --- /dev/null +++ b/credentials_test.go @@ -0,0 +1,220 @@ +package sls + +import ( + "bytes" + "errors" + "fmt" + io "io" + "net/http" + "os" + "testing" + "time" + + env "github.com/Netflix/go-env" + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + sts "github.com/alibabacloud-go/sts-20150401/v2/client" + "github.com/stretchr/testify/assert" +) + +func TestTempCred(t *testing.T) { + now := time.Now() + nowInMills := now.UnixMilli() + + // now = lastUpdated = expirationTime + c := NewTempCredentials("", "", "", nowInMills, nowInMills) + assert.True(t, c.ShouldRefresh()) + + // now = lastUpdated < expirationTime + oneHourInMills := int64(60 * 60 * 1000) + c = NewTempCredentials("", "", "", nowInMills+oneHourInMills, nowInMills) + assert.False(t, c.ShouldRefresh()) + + // expirationTime < now < lastUpdateTime + c = NewTempCredentials("", "", "", nowInMills-oneHourInMills, nowInMills+oneHourInMills) + assert.True(t, c.ShouldRefresh()) + // now < expirationTime < lastUpdateTime + c = NewTempCredentials("", "", "", nowInMills+oneHourInMills, nowInMills+2*oneHourInMills) + assert.False(t, c.ShouldRefresh()) + + // lastUpdateTime < now < expirationTime and factored-expirationTime + c = NewTempCredentials("", "", "", nowInMills+30*oneHourInMills, nowInMills-oneHourInMills) + assert.False(t, c.ShouldRefresh()) + + // lastUpdateTime < now < expirationTime , now > factored-expirationTime + c = NewTempCredentials("", "", "", nowInMills+oneHourInMills, nowInMills-2*oneHourInMills).WithExpiredFactor(0.5) + assert.True(t, c.ShouldRefresh()) + +} + +func TestUpdateFuncAdapter(t *testing.T) { + callCnt := 0 + now := time.Now() + nowInMills := now.UnixMilli() + hourInMills := int64(100000) + id, secret, token := "a", "b", "c" + expiration := now.Add(time.Hour) + var err error + updateFunc := func() (string, string, string, time.Time, error) { + callCnt++ + return id, secret, token, expiration, err + } + adp := NewUpdateFuncProviderAdapter(updateFunc) + adpRetry := UPDATE_FUNC_RETRY_TIMES + cred, err2 := adp.GetCredentials() + assert.Equal(t, 1, callCnt) + assert.NoError(t, err2) + assert.Equal(t, cred.AccessKeyID, id) + assert.Equal(t, cred.AccessKeySecret, secret) + assert.Equal(t, cred.SecurityToken, token) + + // not fetch new + oldId := id + id = "a2" + cred, err2 = adp.GetCredentials() + assert.NoError(t, err2) + assert.Equal(t, 1, callCnt) + assert.Equal(t, cred.AccessKeyID, oldId) + + // fetch new + adp.expirationInMills.Store(nowInMills - hourInMills) + cred, err2 = adp.GetCredentials() + assert.NoError(t, err2) + assert.Equal(t, 2, callCnt) + assert.Equal(t, cred.AccessKeyID, id) + + // fetch failed test + adp.expirationInMills.Store(nowInMills - hourInMills) + err = errors.New("mock err") + cred, err2 = adp.GetCredentials() + assert.Error(t, err2) + assert.Equal(t, 3+adpRetry, callCnt) + assert.Equal(t, cred, Credentials{}) + + // fetch in advance + adp.advanceDuration = time.Hour * 10 + adp.expirationInMills.Store(nowInMills + hourInMills) + err = nil + cred, err2 = adp.GetCredentials() + assert.NoError(t, err2) + assert.Equal(t, 4+adpRetry, callCnt) + assert.Equal(t, cred.AccessKeyID, id) +} + +func TestBuilderParser(t *testing.T) { + reqBuider := newEcsRamRoleReqBuilder(ECS_RAM_ROLE_URL_PREFIX, "test-ram-role") + _, err := reqBuider() + assert.NoError(t, err) + + body := `` + resp := http.Response{ + Body: io.NopCloser(bytes.NewBufferString(body)), + } + _, err = ecsRamRoleParser(&resp) + assert.Error(t, err) + body = `{"Code": "Success", "AccessKeyID": "xxxx", "AccessKeySecret": "yyyy", + "SecurityToken": "zzzz", "Expiration": 234, "LastUpdated": 456 + }` + resp = http.Response{ + Body: io.NopCloser(bytes.NewBufferString(body)), + } + cred, err := ecsRamRoleParser(&resp) + assert.NoError(t, err) + assert.Equal(t, "xxxx", cred.AccessKeyID) + assert.Equal(t, "yyyy", cred.AccessKeySecret) + assert.Equal(t, int64(234), cred.expirationInMills) +} + +type testCredentials struct { + AccessKeyID string `env:"LOG_TEST_ACCESS_KEY_ID"` + AccessKeySecret string `env:"LOG_TEST_ACCESS_KEY_SECRET"` + RoleArn string `env:"LOG_TEST_ROLE_ARN"` + Endpoint string `env:"LOG_STS_TEST_ENDPOINT"` +} + +func getStsClient(c *testCredentials) (*sts.Client, error) { + conf := &openapi.Config{ + AccessKeyId: &c.AccessKeyID, + AccessKeySecret: &c.AccessKeySecret, + Endpoint: &c.Endpoint, + } + return sts.NewClient(conf) +} + +// set env virables before test +func TestStsToken(t *testing.T) { + c := testCredentials{} + _, err := env.UnmarshalFromEnviron(&c) + if err != nil { + assert.Fail(t, "set ACCESS_KEY_ID/ACCESS_KEY_SECRET in environment first") + } + client, err := getStsClient(&c) + assert.NoError(t, err) + callCnt := 0 + updateFunc := func() (string, string, string, time.Time, error) { + callCnt++ + name := "test-go-sdk-session" + req := &sts.AssumeRoleRequest{ + RoleArn: &c.RoleArn, + RoleSessionName: &name, + } + resp, err := client.AssumeRole(req) + assert.NoError(t, err) + cred := resp.Body.Credentials + e := cred.Expiration + assert.NotNil(t, e) + ex, err := time.Parse(time.RFC3339, *e) + assert.NoError(t, err) + return *cred.AccessKeyId, *cred.AccessKeySecret, *cred.SecurityToken, ex, nil + } + provider := NewUpdateFuncProviderAdapter(updateFunc) + + cred1, err := provider.GetCredentials() + assert.NoError(t, err) + assert.Equal(t, 1, callCnt) + // fetch again, updateFunc not called, use cache + cred2, err := provider.GetCredentials() + assert.NoError(t, err) + assert.EqualValues(t, cred1, cred2) + assert.Equal(t, 1, callCnt) + endpoint := os.Getenv("LOG_TEST_ENDPOINT") + project := os.Getenv("LOG_TEST_PROJECT") + client2 := CreateNormalInterfaceV2(endpoint, provider) + res, err := client2.CheckProjectExist(project) + assert.NoError(t, err) + fmt.Println(res) +} + +func TestTokenAutoUpdateClient(t *testing.T) { + c := testCredentials{} + _, err := env.UnmarshalFromEnviron(&c) + if err != nil { + assert.Fail(t, "set ACCESS_KEY_ID/ACCESS_KEY_SECRET in environment first") + } + client, err := getStsClient(&c) + assert.NoError(t, err) + endpoint := os.Getenv("LOG_TEST_ENDPOINT") + project := os.Getenv("LOG_TEST_PROJECT") + callCnt := 0 + updateFunc := func() (string, string, string, time.Time, error) { + callCnt++ + name := "test-go-sdk-session" + req := &sts.AssumeRoleRequest{ + RoleArn: &c.RoleArn, + RoleSessionName: &name, + } + resp, err := client.AssumeRole(req) + assert.NoError(t, err) + cred := resp.Body.Credentials + e := cred.Expiration + assert.NotNil(t, e) + ex, err := time.Parse(time.RFC3339, *e) + assert.NoError(t, err) + return *cred.AccessKeyId, *cred.AccessKeySecret, *cred.SecurityToken, ex, nil + } + done := make(chan struct{}) + updateClient, err := CreateTokenAutoUpdateClient(endpoint, updateFunc, done) + assert.NoError(t, err) + res, err := updateClient.CheckProjectExist(project) + assert.NoError(t, err) + fmt.Println(res) +} diff --git a/go.mod b/go.mod index 831ba3c1..92898025 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,9 @@ module github.com/aliyun/aliyun-log-go-sdk go 1.13 require ( - github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d // indirect + github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d + github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.4 + github.com/alibabacloud-go/sts-20150401/v2 v2.0.1 github.com/cenkalti/backoff v2.2.1+incompatible github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 github.com/frankban/quicktest v1.10.2 // indirect @@ -12,7 +14,7 @@ require ( github.com/golang/protobuf v1.4.2 github.com/pierrec/lz4 v2.6.0+incompatible github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.5.1 go.uber.org/atomic v1.5.0 golang.org/x/net v0.0.0-20201021035429-f5854403a974 google.golang.org/protobuf v1.25.0 // indirect diff --git a/go.sum b/go.sum index b1a0bfbc..43505ad8 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,35 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.4 h1:7Q2FEyqxeZeIkwYMwRC3uphxV4i7O2eV4ETe21d6lS4= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.4/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 h1:NqugFkGxx1TXSh/pBcU00Y6bljgDPaFdh5MUSeJ7e50= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= +github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= +github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY= +github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/sts-20150401/v2 v2.0.1 h1:CevZp0VdG7Q+1J3qwNj+JL7ztKxsL27+tknbdTK9Y6M= +github.com/alibabacloud-go/sts-20150401/v2 v2.0.1/go.mod h1:8wJW1xC4mVcdRXzOvWJYfCCxmvFzZ0VB9iilVjBeWBc= +github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= +github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.1.19 h1:Xroq0M+pr0mC834Djj3Fl4ZA8+GGoA0i7aWse1vmgf4= +github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I= +github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4= +github.com/alibabacloud-go/tea-utils/v2 v2.0.1 h1:K6kwgo+UiYx+/kr6CO0PN5ACZDzE3nnn9d77215AkTs= +github.com/alibabacloud-go/tea-utils/v2 v2.0.1/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4= +github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M= +github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY= +github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -31,6 +60,8 @@ github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEe github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= @@ -107,6 +138,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -143,6 +176,9 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -173,8 +209,10 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= @@ -184,6 +222,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -242,6 +282,9 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= @@ -252,15 +295,20 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= @@ -281,6 +329,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -311,6 +361,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -321,6 +372,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -337,6 +389,8 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -357,6 +411,7 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -397,12 +452,15 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.56.0 h1:DPMeDvGTM54DXbPkVIZsp19fp/I2K7zwA/itHYHKo8Y= +gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -410,8 +468,9 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/log_project.go b/log_project.go index bce7bc56..d96c057c 100644 --- a/log_project.go +++ b/log_project.go @@ -37,25 +37,28 @@ type LogProject struct { CreateTime string `json:"createTime"` // unix time seconds, eg 1524539357 LastModifyTime string `json:"lastModifyTime"` // unix time seconds, eg 1524539357 - Endpoint string // IP or hostname of SLS endpoint - AccessKeyID string - AccessKeySecret string - SecurityToken string - UsingHTTP bool // default https - UserAgent string // default defaultLogUserAgent - AuthVersion AuthVersionType - baseURL string - retryTimeout time.Duration - httpClient *http.Client + Endpoint string // IP or hostname of SLS endpoint + AccessKeyID string // Deprecated: use CredentialsProvider instead + AccessKeySecret string // Deprecated: use CredentialsProvider instead + SecurityToken string // Deprecated: use CredentialsProvider instead + UsingHTTP bool // default https + UserAgent string // default defaultLogUserAgent + AuthVersion AuthVersionType + baseURL string + retryTimeout time.Duration + httpClient *http.Client + credentialProvider CredentialsProvider // User defined common headers. // // When conflict with sdk pre-defined headers, the value will // be ignored - CommonHeaders map[string]string + CommonHeaders map[string]string } // NewLogProject creates a new SLS project. +// +// Deprecated: use NewLogProjectV2 instead. func NewLogProject(name, endpoint, accessKeyID, accessKeySecret string) (p *LogProject, err error) { p = &LogProject{ Name: name, @@ -69,6 +72,25 @@ func NewLogProject(name, endpoint, accessKeyID, accessKeySecret string) (p *LogP return p, nil } +// NewLogProjectV2 creates a new SLS project, with a CredentialsProvider. +func NewLogProjectV2(name, endpoint string, provider CredentialsProvider) (p *LogProject, err error) { + p = &LogProject{ + Name: name, + Endpoint: endpoint, + httpClient: defaultHttpClient, + retryTimeout: defaultRetryTimeout, + credentialProvider: provider, + } + p.parseEndpoint() + return p, nil +} + +// With credentials provider +func (p *LogProject) WithCredentialsProvider(provider CredentialsProvider) *LogProject { + p.credentialProvider = provider + return p +} + // WithToken add token parameter func (p *LogProject) WithToken(token string) (*LogProject, error) { p.SecurityToken = token diff --git a/logstore_test.go b/logstore_test.go index f81b4fdc..cf110412 100644 --- a/logstore_test.go +++ b/logstore_test.go @@ -286,6 +286,7 @@ func (s *LogstoreTestSuite) TestGetLogs() { time.Sleep(5 * time.Second) endTime := uint32(time.Now().Unix()) + time.Sleep(5 * time.Second) hResp, hErr := s.Logstore.GetHistograms("", int64(beginTime), int64(endTime), "InternalServerError") s.Nil(hErr) if hErr != nil { diff --git a/producer/README.md b/producer/README.md index d7702e65..5dee4207 100644 --- a/producer/README.md +++ b/producer/README.md @@ -43,8 +43,8 @@ producer同时提供了简单的使用代码simple:([producer_simple_demo](https ```go langgo l producerConfig := producer.GetDefaultProducerConfig() producerConfig.Endpoint = os.Getenv("Endpoint") -producerConfig.AccessKeyID = os.Getenv("AccessKeyID") -producerConfig.AccessKeySecret = os.Getenv("AccessKeySecret") +provider := sls.NewStaticCredProvider(os.Getenv("AccessKeyID"), os.Getenv("AccessKeySecret"), "") +producerConfig.CredentialsProvider = provider producerInstance:=producer.InitProducer(producerConfig) ch := make(chan os.Signal) signal.Notify(ch, os.Kill, os.Interrupt) diff --git a/producer/producer.go b/producer/producer.go index a491245c..e3224eed 100644 --- a/producer/producer.go +++ b/producer/producer.go @@ -32,15 +32,11 @@ type Producer struct { func InitProducer(producerConfig *ProducerConfig) *Producer { logger := logConfig(producerConfig) - client := sls.CreateNormalInterface(producerConfig.Endpoint, producerConfig.AccessKeyID, producerConfig.AccessKeySecret, "") - if producerConfig.UpdateStsToken != nil && producerConfig.StsTokenShutDown != nil { - stsClient, err := sls.CreateTokenAutoUpdateClient(producerConfig.Endpoint, producerConfig.UpdateStsToken, producerConfig.StsTokenShutDown) - if err != nil { - level.Warn(logger).Log("msg", "Failed to create ststoken client, use default client without ststoken.", "error", err) - } else { - client = stsClient - } + client, err := createClient(producerConfig) + if err != nil { + level.Warn(logger).Log("msg", "Failed to create ststoken client, use default client without ststoken.", "error", err) } + if producerConfig.HTTPClient != nil { client.SetHTTPClient(producerConfig.HTTPClient) } @@ -75,6 +71,20 @@ func InitProducer(producerConfig *ProducerConfig) *Producer { return producer } +func createClient(producerConfig *ProducerConfig) (sls.ClientInterface, error) { + // use CredentialsProvider + if producerConfig.CredentialsProvider != nil { + return sls.CreateNormalInterfaceV2(producerConfig.Endpoint, producerConfig.CredentialsProvider), nil + } + // use UpdateStsTokenFunc + if producerConfig.UpdateStsToken != nil && producerConfig.StsTokenShutDown != nil { + return sls.CreateTokenAutoUpdateClient(producerConfig.Endpoint, producerConfig.UpdateStsToken, producerConfig.StsTokenShutDown) + } + // fallback to default static long-lived AK + staticProvider := sls.NewStaticCredentialsProvider(producerConfig.AccessKeyID, producerConfig.AccessKeySecret, "") + return sls.CreateNormalInterfaceV2(producerConfig.Endpoint, staticProvider), nil +} + func validateProducerConfig(producerConfig *ProducerConfig) *ProducerConfig { logger := logConfig(producerConfig) if producerConfig.MaxReservedAttempts <= 0 { diff --git a/producer/producer_config.go b/producer/producer_config.go index e5920f66..f1822918 100644 --- a/producer/producer_config.go +++ b/producer/producer_config.go @@ -10,6 +10,8 @@ import ( const Delimiter = "|" +type UpdateStsTokenFunc = func() (accessKeyID, accessKeySecret, securityToken string, expireTime time.Time, err error) + type ProducerConfig struct { TotalSizeLnBytes int64 MaxIoWorkerCount int64 @@ -30,19 +32,28 @@ type ProducerConfig struct { LogMaxBackups int LogCompress bool Endpoint string - AccessKeyID string - AccessKeySecret string NoRetryStatusCodeList []int - UpdateStsToken func() (accessKeyID, accessKeySecret, securityToken string, expireTime time.Time, err error) - StsTokenShutDown chan struct{} HTTPClient *http.Client UserAgent string LogTags []*sls.LogTag GeneratePackId bool + CredentialsProvider sls.CredentialsProvider packLock sync.Mutex packPrefix string packNumber int64 + + // Deprecated: use CredentialsProvider and UpdateFuncProviderAdapter instead. + // + // Example: + // provider := sls.NewUpdateFuncProviderAdapter(updateStsTokenFunc) + // config := &ProducerConfig{ + // CredentialsProvider: provider, + // } + UpdateStsToken UpdateStsTokenFunc + StsTokenShutDown chan struct{} + AccessKeyID string // Deprecated: use CredentialsProvider instead + AccessKeySecret string // Deprecated: use CredentialsProvider instead } func GetDefaultProducerConfig() *ProducerConfig { diff --git a/request.go b/request.go index 207716a9..30cac2fe 100644 --- a/request.go +++ b/request.go @@ -153,9 +153,23 @@ func realRequest(ctx context.Context, project *LogProject, method, uri string, h headers[HTTPHeaderUserAgent] = DefaultLogUserAgent } + stsToken := project.SecurityToken + accessKeyID := project.AccessKeyID + accessKeySecret := project.AccessKeySecret + + if project.credentialProvider != nil { + c, err := project.credentialProvider.GetCredentials() + if err != nil { + return nil, NewClientError(fmt.Errorf("fail to get credentials: %w", err)) + } + stsToken = c.SecurityToken + accessKeyID = c.AccessKeyID + accessKeySecret = c.AccessKeySecret + } + // Access with token - if project.SecurityToken != "" { - headers[HTTPHeaderAcsSecurityToken] = project.SecurityToken + if stsToken != "" { + headers[HTTPHeaderAcsSecurityToken] = stsToken } if body != nil { @@ -167,10 +181,10 @@ func realRequest(ctx context.Context, project *LogProject, method, uri string, h var signer Signer if project.AuthVersion == AuthV4 { headers[HTTPHeaderLogDate] = dateTimeISO8601() - signer = NewSignerV4(project.AccessKeyID, project.AccessKeySecret, project.Region) + signer = NewSignerV4(accessKeyID, accessKeySecret, project.Region) } else { headers[HTTPHeaderDate] = nowRFC1123() - signer = NewSignerV1(project.AccessKeyID, project.AccessKeySecret) + signer = NewSignerV1(accessKeyID, accessKeySecret) } if err := signer.Sign(method, uri, headers, body); err != nil { return nil, err