Skip to content

Commit 5c94480

Browse files
sincereflyXudong Liu
and
Xudong Liu
authored
Feature/support aliyun oss (#12)
* feat: Support AliCloud OSS with RRSA OIDC Token * fix: *string nil panic * fix: cache aliyun oss client, one endpoint, one client * fix: 优化代码 * fix: func 内函数小写 private 私有化 --------- Co-authored-by: Xudong Liu <xudong.liu@ewp-group.com>
1 parent c9d7606 commit 5c94480

File tree

10 files changed

+514
-0
lines changed

10 files changed

+514
-0
lines changed

cloud/common.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const (
1717
TencentCloudProvider Provider = "tencentcloud"
1818
StandaloneRedisProvider Provider = "standalone_redis"
1919
ClusterRedisProvider Provider = "cluster_redis"
20+
AliCloudStorageProvider Provider = "alicloud_storage"
2021
)
2122

2223
var (
@@ -25,6 +26,7 @@ var (
2526
ErrProviderNotAWS = errors.New("provider is not aws")
2627
ErrProviderNotStandaloneRedis = errors.New("provider is not standalone redis")
2728
ErrProviderNotClusterRedis = errors.New("provider is not cluster redis")
29+
ErrProviderNotAliCloudStorage = errors.New("provider is not aliyun oss")
2830
ErrEmptySecretID = errors.New("secret_id is empty")
2931
ErrEmptySecretKey = errors.New("secret_key is empty")
3032
ErrEmptyRegion = errors.New("region is empty")
@@ -41,6 +43,7 @@ type Option interface {
4143
CheckTencentCloud() error
4244
CheckStandaloneRedis() error
4345
CheckClusterRedis() error
46+
CheckAliCloudStorage() error
4447
}
4548

4649
type CommonOption struct {
@@ -110,6 +113,13 @@ func (option CommonOption) CheckTencentCloud() error {
110113
return option.check()
111114
}
112115

116+
func (option CommonOption) CheckAliCloudStorage() error {
117+
if option.Provider != AliCloudStorageProvider {
118+
return ErrProviderNotAliCloudStorage
119+
}
120+
return option.check()
121+
}
122+
113123
func (option CommonOption) CheckStandaloneRedis() error {
114124
return ErrProviderNotStandaloneRedis
115125
}

cloud/examples/object_storage/object_storage.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ func main() {
2727
Region: "aws_region_xxx",
2828
}
2929
object_storage_examples("aws_bucket_name_xxx", optionForAWS)
30+
31+
optionForAliOSS := object_storage.AliCloudStorageOption{
32+
CredentialType: "oidc_role_arn",
33+
EndPoint: "oss-cn-zhangjiakou.aliyuncs.com",
34+
SessionName: "test-rrsa-oidc-token",
35+
}
36+
object_storage_examples("my-bucket", optionForAliOSS)
3037
}
3138

3239
func object_storage_examples(bucketName string, option cloud.Option) {
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
package object_storage
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"errors"
7+
"fmt"
8+
"github.com/alibabacloud-go/tea/tea"
9+
"github.com/aliyun/aliyun-oss-go-sdk/oss"
10+
"github.com/byte-power/gorich/cloud"
11+
"github.com/byte-power/gorich/utils"
12+
"io"
13+
"time"
14+
)
15+
16+
var ossClientMap = make(map[string]*oss.Client)
17+
18+
var (
19+
ErrAliCloudStorageServiceCredentialTypeEmpty = errors.New("credential_type for alicloud storage service is empty")
20+
ErrAliCloudStorageServiceEndPointEmpty = errors.New("endpoint for alicloud storage service is empty")
21+
ErrAliCloudStorageServiceSessionNameEmpty = errors.New("session_name for alicloud storage service is empty")
22+
)
23+
24+
type AliCloudStorageOption struct {
25+
CredentialType string // eg: "oidc_role_arn"
26+
EndPoint string // eg: "oss-cn-zhangjiakou.aliyuncs.com"
27+
SessionName string // eg: "test-rrsa-oidc-token"
28+
}
29+
30+
func (option AliCloudStorageOption) GetProvider() cloud.Provider {
31+
return cloud.AliCloudStorageProvider
32+
}
33+
34+
func (option AliCloudStorageOption) GetSecretID() string {
35+
return ""
36+
}
37+
38+
func (option AliCloudStorageOption) GetSecretKey() string {
39+
return ""
40+
}
41+
42+
func (option AliCloudStorageOption) GetAssumeRoleArn() string {
43+
return ""
44+
}
45+
46+
func (option AliCloudStorageOption) GetRegion() string {
47+
return ""
48+
}
49+
50+
func (option AliCloudStorageOption) GetAssumeRegion() string {
51+
return ""
52+
}
53+
54+
func (option AliCloudStorageOption) CheckAWS() error {
55+
return cloud.ErrProviderNotAWS
56+
}
57+
58+
func (option AliCloudStorageOption) CheckTencentCloud() error {
59+
return cloud.ErrProviderNotTencentCloud
60+
}
61+
62+
func (option AliCloudStorageOption) CheckStandaloneRedis() error {
63+
return cloud.ErrProviderNotStandaloneRedis
64+
}
65+
66+
func (option AliCloudStorageOption) CheckClusterRedis() error {
67+
return cloud.ErrProviderNotClusterRedis
68+
}
69+
70+
func (option AliCloudStorageOption) CheckAliCloudStorage() error {
71+
return option.check()
72+
}
73+
74+
func (option AliCloudStorageOption) check() error {
75+
if option.CredentialType == "" {
76+
return ErrAliCloudStorageServiceCredentialTypeEmpty
77+
}
78+
if option.EndPoint == "" {
79+
return ErrAliCloudStorageServiceEndPointEmpty
80+
}
81+
if option.SessionName == "" {
82+
return ErrAliCloudStorageServiceSessionNameEmpty
83+
}
84+
return nil
85+
}
86+
87+
type AliCloudObjectStorageService struct {
88+
client *oss.Client
89+
bucketName string
90+
}
91+
92+
// GetAliCloudObjectService
93+
// option.credentialType Only Support "oidc_role_arn"
94+
func GetAliCloudObjectService(bucketName string, option cloud.Option) (ObjectStorageService, error) {
95+
if bucketName == "" {
96+
return nil, ErrBucketNameEmpty
97+
}
98+
if err := option.CheckAliCloudStorage(); err != nil {
99+
return nil, err
100+
}
101+
storageOption, ok := option.(AliCloudStorageOption)
102+
if !ok {
103+
return nil, fmt.Errorf("parameter option %+v should be AliCloudStorageOption", option)
104+
}
105+
106+
// one endpoint, one client, return if exist
107+
if client, ok := ossClientMap[storageOption.EndPoint]; ok {
108+
return &AliCloudObjectStorageService{client: client, bucketName: bucketName}, nil
109+
}
110+
111+
cred, err := newOidcCredential(storageOption.CredentialType, storageOption.SessionName)
112+
if err != nil {
113+
return nil, err
114+
}
115+
116+
provider := &aliCloudCredentialsProvider{
117+
cred: cred,
118+
}
119+
client, err := oss.New(storageOption.EndPoint, "", "", oss.SetCredentialsProvider(provider))
120+
if err != nil {
121+
return nil, err
122+
}
123+
124+
// cache client
125+
ossClientMap[storageOption.EndPoint] = client
126+
return &AliCloudObjectStorageService{client: client, bucketName: bucketName}, nil
127+
}
128+
129+
func (service *AliCloudObjectStorageService) ListObjects(ctx context.Context, prefix string, continueToken *string, maxObjects int) ([]Object, *string, error) {
130+
bucket, err := service.client.Bucket(service.bucketName)
131+
if err != nil {
132+
return nil, nil, err
133+
}
134+
options := make([]oss.Option, 0)
135+
if prefix != "" {
136+
options = append(options, oss.Prefix(prefix))
137+
}
138+
if continueToken != nil {
139+
options = append(options, oss.ContinuationToken(tea.StringValue(continueToken)))
140+
}
141+
if maxObjects > 0 {
142+
options = append(options, oss.MaxKeys(maxObjects))
143+
}
144+
resp, err := bucket.ListObjectsV2(options...)
145+
if err != nil {
146+
return nil, nil, err
147+
}
148+
objects := make([]Object, 0, len(resp.Objects))
149+
for _, obj := range resp.Objects {
150+
object := Object{
151+
key: obj.Key,
152+
eTag: obj.ETag,
153+
lastModified: obj.LastModified,
154+
size: obj.Size,
155+
}
156+
objects = append(objects, object)
157+
}
158+
var nextToken *string
159+
if resp.IsTruncated {
160+
nextToken = &resp.NextContinuationToken
161+
}
162+
return objects, nextToken, nil
163+
}
164+
165+
func (service *AliCloudObjectStorageService) HeadObject(ctx context.Context, key string) (Object, error) {
166+
if key == "" {
167+
return Object{}, ErrObjectKeyEmpty
168+
}
169+
bucket, err := service.client.Bucket(service.bucketName)
170+
if err != nil {
171+
return Object{}, err
172+
}
173+
174+
isExist, err := bucket.IsObjectExist(key)
175+
if err != nil {
176+
return Object{}, err
177+
}
178+
if !isExist {
179+
return Object{}, ErrObjectNotFound
180+
}
181+
182+
metadata, err := bucket.GetObjectDetailedMeta(key)
183+
if err != nil {
184+
return Object{}, err
185+
}
186+
187+
lastModified, err := HTTPHeaderLastModifiedToTime(metadata.Get(oss.HTTPHeaderLastModified))
188+
if err != nil {
189+
return Object{}, err
190+
}
191+
size, err := utils.StringToInt64(metadata.Get(oss.HTTPHeaderContentLength))
192+
if err != nil {
193+
return Object{}, err
194+
}
195+
return Object{
196+
key: key,
197+
eTag: metadata.Get(oss.HTTPHeaderEtag),
198+
lastModified: lastModified,
199+
size: size,
200+
contentType: metadata.Get(oss.HTTPHeaderContentType),
201+
}, nil
202+
}
203+
204+
func (service *AliCloudObjectStorageService) GetObject(ctx context.Context, key string) (Object, error) {
205+
if key == "" {
206+
return Object{}, ErrObjectKeyEmpty
207+
}
208+
bucket, err := service.client.Bucket(service.bucketName)
209+
if err != nil {
210+
return Object{}, err
211+
}
212+
213+
isExist, err := bucket.IsObjectExist(key)
214+
if err != nil {
215+
return Object{}, err
216+
}
217+
if !isExist {
218+
return Object{}, ErrObjectNotFound
219+
}
220+
221+
metadata, err := bucket.GetObjectDetailedMeta(key)
222+
if err != nil {
223+
return Object{}, err
224+
}
225+
226+
resp, err := bucket.GetObject(key)
227+
if err != nil {
228+
return Object{}, err
229+
}
230+
defer resp.Close()
231+
232+
bs, err := io.ReadAll(resp)
233+
if err != nil {
234+
return Object{}, err
235+
}
236+
237+
lastModified, err := HTTPHeaderLastModifiedToTime(metadata.Get(oss.HTTPHeaderLastModified))
238+
if err != nil {
239+
return Object{}, err
240+
}
241+
size, err := utils.StringToInt64(metadata.Get(oss.HTTPHeaderContentLength))
242+
if err != nil {
243+
return Object{}, err
244+
}
245+
return Object{
246+
key: key,
247+
isContentLoaded: true,
248+
content: bs,
249+
eTag: metadata.Get(oss.HTTPHeaderEtag),
250+
lastModified: lastModified,
251+
size: size,
252+
contentType: metadata.Get(oss.HTTPHeaderContentType),
253+
}, nil
254+
}
255+
256+
func (service *AliCloudObjectStorageService) PutObject(ctx context.Context, key string, input *PutObjectInput) error {
257+
if key == "" {
258+
return ErrObjectKeyEmpty
259+
}
260+
if input == nil {
261+
return errors.New("parameter input is nil")
262+
}
263+
bucket, err := service.client.Bucket(service.bucketName)
264+
if err != nil {
265+
return err
266+
}
267+
return bucket.PutObject(key, bytes.NewReader(input.Body), oss.ContentType(input.ContentType))
268+
}
269+
270+
func (service *AliCloudObjectStorageService) DeleteObject(ctx context.Context, key string) error {
271+
if key == "" {
272+
return ErrObjectKeyEmpty
273+
}
274+
bucket, err := service.client.Bucket(service.bucketName)
275+
if err != nil {
276+
return err
277+
}
278+
return bucket.DeleteObject(key)
279+
}
280+
281+
func (service *AliCloudObjectStorageService) DeleteObjects(ctx context.Context, keys ...string) error {
282+
if len(keys) == 0 {
283+
return errors.New("parameter keys should not be empty")
284+
}
285+
bucket, err := service.client.Bucket(service.bucketName)
286+
if err != nil {
287+
return err
288+
}
289+
_, err = bucket.DeleteObjects(keys)
290+
return err
291+
}
292+
293+
func (service *AliCloudObjectStorageService) GetSignedURL(key string, duration time.Duration) (string, error) {
294+
if key == "" {
295+
return "", ErrObjectKeyEmpty
296+
}
297+
bucket, err := service.client.Bucket(service.bucketName)
298+
if err != nil {
299+
return "", err
300+
}
301+
return bucket.SignURL(key, oss.HTTPGet, int64(duration.Seconds()))
302+
}
303+
304+
func (service *AliCloudObjectStorageService) GetSignedURLForExistedKey(ctx context.Context, key string, duration time.Duration) (string, error) {
305+
if key == "" {
306+
return "", ErrObjectKeyEmpty
307+
}
308+
_, err := service.HeadObject(ctx, key)
309+
if err != nil {
310+
return "", err
311+
}
312+
return service.GetSignedURL(key, duration)
313+
}

0 commit comments

Comments
 (0)