bucket.go 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package qcloud
  15. import (
  16. "context"
  17. "fmt"
  18. "io"
  19. "net/http"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "github.com/tencentyun/cos-go-sdk-v5"
  24. "gopkg.in/fatih/set.v0"
  25. "yunion.io/x/jsonutils"
  26. "yunion.io/x/log"
  27. "yunion.io/x/pkg/errors"
  28. "yunion.io/x/pkg/util/timeutils"
  29. "yunion.io/x/s3cli"
  30. api "yunion.io/x/cloudmux/pkg/apis/compute"
  31. "yunion.io/x/cloudmux/pkg/cloudprovider"
  32. "yunion.io/x/cloudmux/pkg/multicloud"
  33. )
  34. const (
  35. COS_META_HEADER = "X-Cos-Meta-"
  36. )
  37. type SBucket struct {
  38. multicloud.SBaseBucket
  39. QcloudTags
  40. appId string
  41. region *SRegion
  42. zone *SZone
  43. Name string
  44. Location string
  45. CreateDate time.Time
  46. }
  47. func (b *SBucket) GetProjectId() string {
  48. return ""
  49. }
  50. func (b *SBucket) GetGlobalId() string {
  51. appId, _ := b.region.client.GetAppId()
  52. if b.getAppId() == appId {
  53. return b.Name
  54. } else {
  55. return b.getFullName()
  56. }
  57. }
  58. func (b *SBucket) GetName() string {
  59. return b.GetGlobalId()
  60. }
  61. func (b *SBucket) GetLocation() string {
  62. return b.Location
  63. }
  64. func (b *SBucket) GetIRegion() cloudprovider.ICloudRegion {
  65. return b.region
  66. }
  67. func (b *SBucket) GetCreatedAt() time.Time {
  68. return b.CreateDate
  69. }
  70. func (b *SBucket) GetStorageClass() string {
  71. return ""
  72. }
  73. const (
  74. ACL_GROUP_URI_ALL_USERS = "http://cam.qcloud.com/groups/global/AllUsers"
  75. ACL_GROUP_URI_AUTH_USERS = "http://cam.qcloud.com/groups/global/AuthenticatedUsers"
  76. )
  77. func cosAcl2CannedAcl(acls []cos.ACLGrant) cloudprovider.TBucketACLType {
  78. switch {
  79. case len(acls) == 1:
  80. if acls[0].Grantee.URI == "" && acls[0].Permission == s3cli.PERMISSION_FULL_CONTROL {
  81. return cloudprovider.ACLPrivate
  82. }
  83. case len(acls) == 2:
  84. for _, g := range acls {
  85. if g.Grantee.URI == ACL_GROUP_URI_AUTH_USERS && g.Permission == s3cli.PERMISSION_READ {
  86. return cloudprovider.ACLAuthRead
  87. }
  88. if g.Grantee.URI == ACL_GROUP_URI_ALL_USERS && g.Permission == s3cli.PERMISSION_READ {
  89. return cloudprovider.ACLPublicRead
  90. }
  91. }
  92. case len(acls) == 3:
  93. for _, g := range acls {
  94. if g.Grantee.URI == ACL_GROUP_URI_ALL_USERS && g.Permission == s3cli.PERMISSION_WRITE {
  95. return cloudprovider.ACLPublicReadWrite
  96. }
  97. }
  98. }
  99. return cloudprovider.ACLUnknown
  100. }
  101. func (b *SBucket) GetAcl() cloudprovider.TBucketACLType {
  102. acl := cloudprovider.ACLPrivate
  103. coscli, err := b.region.GetCosClient(b)
  104. if err != nil {
  105. log.Errorf("GetCosClient fail %s", err)
  106. return acl
  107. }
  108. result, _, err := coscli.Bucket.GetACL(context.Background())
  109. if err != nil {
  110. log.Errorf("coscli.Bucket.GetACL fail %s", err)
  111. return acl
  112. }
  113. return cosAcl2CannedAcl(result.AccessControlList)
  114. }
  115. func (b *SBucket) SetAcl(aclStr cloudprovider.TBucketACLType) error {
  116. coscli, err := b.region.GetCosClient(b)
  117. if err != nil {
  118. return errors.Wrap(err, "b.region.GetCosClient")
  119. }
  120. opts := &cos.BucketPutACLOptions{}
  121. opts.Header = &cos.ACLHeaderOptions{}
  122. opts.Header.XCosACL = string(aclStr)
  123. _, err = coscli.Bucket.PutACL(context.Background(), opts)
  124. if err != nil {
  125. return errors.Wrap(err, "PutACL")
  126. }
  127. return nil
  128. }
  129. func (b *SBucket) getAppId() string {
  130. if len(b.appId) > 0 {
  131. return b.appId
  132. }
  133. if b.zone != nil {
  134. appId, _ := b.zone.region.client.GetAppId()
  135. return appId
  136. }
  137. appId, _ := b.region.client.GetAppId()
  138. return appId
  139. }
  140. func (b *SBucket) getFullName() string {
  141. return fmt.Sprintf("%s-%s", b.Name, b.getAppId())
  142. }
  143. func (b *SBucket) getBucketUrlHost() string {
  144. if b.zone != nil {
  145. return fmt.Sprintf("%s.%s", b.getFullName(), b.zone.getCosEndpoint())
  146. } else {
  147. return fmt.Sprintf("%s.%s", b.getFullName(), b.region.getCosEndpoint())
  148. }
  149. }
  150. func (b *SBucket) getBucketUrl() string {
  151. return fmt.Sprintf("https://%s", b.getBucketUrlHost())
  152. }
  153. func (b *SBucket) getBucketWebsiteUrlHost() string {
  154. if b.zone != nil {
  155. return fmt.Sprintf("%s.%s", b.getFullName(), b.zone.getCosWebsiteEndpoint())
  156. } else {
  157. return fmt.Sprintf("%s.%s", b.getFullName(), b.region.getCosWebsiteEndpoint())
  158. }
  159. }
  160. func (b *SBucket) getWebsiteUrl() string {
  161. return fmt.Sprintf("https://%s", b.getBucketWebsiteUrlHost())
  162. }
  163. func (b *SBucket) GetAccessUrls() []cloudprovider.SBucketAccessUrl {
  164. return []cloudprovider.SBucketAccessUrl{
  165. {
  166. Url: b.getBucketUrl(),
  167. Description: "bucket domain",
  168. Primary: true,
  169. },
  170. {
  171. Url: fmt.Sprintf("https://%s/%s", b.region.getCosEndpoint(), b.getFullName()),
  172. Description: "cos domain",
  173. },
  174. }
  175. }
  176. func (b *SBucket) GetStats() cloudprovider.SBucketStats {
  177. stats, _ := cloudprovider.GetIBucketStats(b)
  178. return stats
  179. }
  180. func (b *SBucket) ListObjects(prefix string, marker string, delimiter string, maxCount int) (cloudprovider.SListObjectResult, error) {
  181. result := cloudprovider.SListObjectResult{}
  182. coscli, err := b.region.GetCosClient(b)
  183. if err != nil {
  184. return result, errors.Wrap(err, "GetCosClient")
  185. }
  186. opts := &cos.BucketGetOptions{}
  187. if len(prefix) > 0 {
  188. opts.Prefix = prefix
  189. }
  190. if len(marker) > 0 {
  191. opts.Marker = marker
  192. }
  193. if len(delimiter) > 0 {
  194. opts.Delimiter = delimiter
  195. }
  196. if maxCount > 0 {
  197. opts.MaxKeys = maxCount
  198. }
  199. oResult, _, err := coscli.Bucket.Get(context.Background(), opts)
  200. if err != nil {
  201. return result, errors.Wrap(err, "coscli.Bucket.Get")
  202. }
  203. result.Objects = make([]cloudprovider.ICloudObject, 0)
  204. for _, object := range oResult.Contents {
  205. lastModified, _ := timeutils.ParseTimeStr(object.LastModified)
  206. obj := &SObject{
  207. bucket: b,
  208. SBaseCloudObject: cloudprovider.SBaseCloudObject{
  209. StorageClass: string(object.StorageClass),
  210. Key: object.Key,
  211. SizeBytes: int64(object.Size),
  212. ETag: object.ETag,
  213. LastModified: lastModified,
  214. },
  215. }
  216. result.Objects = append(result.Objects, obj)
  217. }
  218. if oResult.CommonPrefixes != nil {
  219. result.CommonPrefixes = make([]cloudprovider.ICloudObject, 0)
  220. for _, commPrefix := range oResult.CommonPrefixes {
  221. obj := &SObject{
  222. bucket: b,
  223. SBaseCloudObject: cloudprovider.SBaseCloudObject{
  224. Key: commPrefix,
  225. },
  226. }
  227. result.CommonPrefixes = append(result.CommonPrefixes, obj)
  228. }
  229. }
  230. result.IsTruncated = oResult.IsTruncated
  231. result.NextMarker = oResult.NextMarker
  232. return result, nil
  233. }
  234. func (b *SBucket) PutObject(ctx context.Context, key string, reader io.Reader, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
  235. coscli, err := b.region.GetCosClient(b)
  236. if err != nil {
  237. return errors.Wrap(err, "GetCosClient")
  238. }
  239. opts := &cos.ObjectPutOptions{
  240. ACLHeaderOptions: &cos.ACLHeaderOptions{},
  241. ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{},
  242. }
  243. if sizeBytes > 0 {
  244. opts.ContentLength = sizeBytes
  245. }
  246. if meta != nil {
  247. extraHdr := http.Header{}
  248. for k, v := range meta {
  249. if len(v) == 0 || len(v[0]) == 0 {
  250. continue
  251. }
  252. switch http.CanonicalHeaderKey(k) {
  253. case cloudprovider.META_HEADER_CACHE_CONTROL:
  254. opts.CacheControl = v[0]
  255. case cloudprovider.META_HEADER_CONTENT_TYPE:
  256. opts.ContentType = v[0]
  257. case cloudprovider.META_HEADER_CONTENT_MD5:
  258. opts.ContentMD5 = v[0]
  259. case cloudprovider.META_HEADER_CONTENT_ENCODING:
  260. opts.ContentEncoding = v[0]
  261. case cloudprovider.META_HEADER_CONTENT_DISPOSITION:
  262. opts.ContentDisposition = v[0]
  263. default:
  264. extraHdr.Add(fmt.Sprintf("%s%s", COS_META_HEADER, k), v[0])
  265. }
  266. }
  267. if len(extraHdr) > 0 {
  268. opts.XCosMetaXXX = &extraHdr
  269. }
  270. }
  271. if len(cannedAcl) == 0 {
  272. cannedAcl = b.GetAcl()
  273. }
  274. opts.XCosACL = string(cannedAcl)
  275. if len(storageClassStr) > 0 {
  276. opts.XCosStorageClass = storageClassStr
  277. }
  278. _, err = coscli.Object.Put(ctx, key, reader, opts)
  279. if err != nil {
  280. return errors.Wrap(err, "coscli.Object.Put")
  281. }
  282. return nil
  283. }
  284. func (b *SBucket) NewMultipartUpload(ctx context.Context, key string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) (string, error) {
  285. coscli, err := b.region.GetCosClient(b)
  286. if err != nil {
  287. return "", errors.Wrap(err, "GetCosClient")
  288. }
  289. opts := &cos.InitiateMultipartUploadOptions{
  290. ACLHeaderOptions: &cos.ACLHeaderOptions{},
  291. ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{},
  292. }
  293. if meta != nil {
  294. extraHdr := http.Header{}
  295. for k, v := range meta {
  296. if len(v) == 0 || len(v[0]) == 0 {
  297. continue
  298. }
  299. switch http.CanonicalHeaderKey(k) {
  300. case cloudprovider.META_HEADER_CACHE_CONTROL:
  301. opts.CacheControl = v[0]
  302. case cloudprovider.META_HEADER_CONTENT_TYPE:
  303. opts.ContentType = v[0]
  304. case cloudprovider.META_HEADER_CONTENT_MD5:
  305. opts.ContentMD5 = v[0]
  306. case cloudprovider.META_HEADER_CONTENT_ENCODING:
  307. opts.ContentEncoding = v[0]
  308. case cloudprovider.META_HEADER_CONTENT_DISPOSITION:
  309. opts.ContentDisposition = v[0]
  310. default:
  311. extraHdr.Add(fmt.Sprintf("%s%s", COS_META_HEADER, k), v[0])
  312. }
  313. }
  314. if len(extraHdr) > 0 {
  315. opts.XCosMetaXXX = &extraHdr
  316. }
  317. }
  318. if len(cannedAcl) == 0 {
  319. cannedAcl = b.GetAcl()
  320. }
  321. opts.XCosACL = string(cannedAcl)
  322. if len(storageClassStr) > 0 {
  323. opts.XCosStorageClass = storageClassStr
  324. }
  325. result, _, err := coscli.Object.InitiateMultipartUpload(ctx, key, opts)
  326. if err != nil {
  327. return "", errors.Wrap(err, "InitiateMultipartUpload")
  328. }
  329. return result.UploadID, nil
  330. }
  331. func (b *SBucket) UploadPart(ctx context.Context, key string, uploadId string, partIndex int, input io.Reader, partSize int64, offset, totalSize int64) (string, error) {
  332. coscli, err := b.region.GetCosClient(b)
  333. if err != nil {
  334. return "", errors.Wrap(err, "GetCosClient")
  335. }
  336. opts := &cos.ObjectUploadPartOptions{}
  337. opts.ContentLength = partSize
  338. resp, err := coscli.Object.UploadPart(ctx, key, uploadId, partIndex, input, opts)
  339. if err != nil {
  340. return "", errors.Wrap(err, "UploadPart")
  341. }
  342. return resp.Header.Get("Etag"), nil
  343. }
  344. func (b *SBucket) CompleteMultipartUpload(ctx context.Context, key string, uploadId string, partEtags []string) error {
  345. coscli, err := b.region.GetCosClient(b)
  346. if err != nil {
  347. return errors.Wrap(err, "GetCosClient")
  348. }
  349. opts := &cos.CompleteMultipartUploadOptions{}
  350. parts := make([]cos.Object, len(partEtags))
  351. for i := range partEtags {
  352. parts[i] = cos.Object{
  353. PartNumber: i + 1,
  354. ETag: partEtags[i],
  355. }
  356. }
  357. opts.Parts = parts
  358. _, _, err = coscli.Object.CompleteMultipartUpload(ctx, key, uploadId, opts)
  359. if err != nil {
  360. return errors.Wrap(err, "CompleteMultipartUpload")
  361. }
  362. return nil
  363. }
  364. func (b *SBucket) AbortMultipartUpload(ctx context.Context, key string, uploadId string) error {
  365. coscli, err := b.region.GetCosClient(b)
  366. if err != nil {
  367. return errors.Wrap(err, "GetCosClient")
  368. }
  369. _, err = coscli.Object.AbortMultipartUpload(ctx, key, uploadId)
  370. if err != nil {
  371. return errors.Wrap(err, "AbortMultipartUpload")
  372. }
  373. return nil
  374. }
  375. func (b *SBucket) DeleteObject(ctx context.Context, key string) error {
  376. coscli, err := b.region.GetCosClient(b)
  377. if err != nil {
  378. return errors.Wrap(err, "GetCosClient")
  379. }
  380. _, err = coscli.Object.Delete(ctx, key)
  381. if err != nil {
  382. return errors.Wrap(err, "coscli.Object.Delete")
  383. }
  384. return nil
  385. }
  386. func (b *SBucket) GetTempUrl(method string, key string, expire time.Duration) (string, error) {
  387. if method != "GET" && method != "PUT" && method != "DELETE" {
  388. return "", errors.Error("unsupported method")
  389. }
  390. coscli, err := b.region.GetCosClient(b)
  391. if err != nil {
  392. return "", errors.Wrap(err, "GetCosClient")
  393. }
  394. url, err := coscli.Object.GetPresignedURL(context.Background(), method, key,
  395. b.region.client.secretId,
  396. b.region.client.secretKey,
  397. expire, nil)
  398. if err != nil {
  399. return "", errors.Wrap(err, "coscli.Object.GetPresignedURL")
  400. }
  401. return url.String(), nil
  402. }
  403. func (b *SBucket) CopyObject(ctx context.Context, destKey string, srcBucketName, srcKey string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
  404. coscli, err := b.region.GetCosClient(b)
  405. if err != nil {
  406. return errors.Wrap(err, "GetCosClient")
  407. }
  408. opts := &cos.ObjectCopyOptions{
  409. ObjectCopyHeaderOptions: &cos.ObjectCopyHeaderOptions{},
  410. ACLHeaderOptions: &cos.ACLHeaderOptions{},
  411. }
  412. if len(cannedAcl) == 0 {
  413. cannedAcl = b.GetAcl()
  414. }
  415. opts.XCosACL = string(cannedAcl)
  416. if len(storageClassStr) > 0 {
  417. opts.XCosStorageClass = storageClassStr
  418. }
  419. if meta != nil {
  420. opts.XCosMetadataDirective = "Replaced"
  421. extraHdr := http.Header{}
  422. for k, v := range meta {
  423. if len(v) == 0 || len(v[0]) == 0 {
  424. continue
  425. }
  426. switch http.CanonicalHeaderKey(k) {
  427. case cloudprovider.META_HEADER_CACHE_CONTROL:
  428. opts.CacheControl = v[0]
  429. case cloudprovider.META_HEADER_CONTENT_TYPE:
  430. opts.ContentType = v[0]
  431. case cloudprovider.META_HEADER_CONTENT_ENCODING:
  432. opts.ContentEncoding = v[0]
  433. case cloudprovider.META_HEADER_CONTENT_DISPOSITION:
  434. opts.ContentDisposition = v[0]
  435. default:
  436. extraHdr.Add(fmt.Sprintf("%s%s", COS_META_HEADER, k), v[0])
  437. }
  438. }
  439. if len(extraHdr) > 0 {
  440. opts.XCosMetaXXX = &extraHdr
  441. }
  442. } else {
  443. opts.XCosMetadataDirective = "Copy"
  444. }
  445. srcBucket := SBucket{
  446. region: b.region,
  447. Name: srcBucketName,
  448. }
  449. srcUrl := fmt.Sprintf("%s/%s", srcBucket.getBucketUrlHost(), srcKey)
  450. _, _, err = coscli.Object.Copy(ctx, destKey, srcUrl, opts)
  451. if err != nil {
  452. return errors.Wrap(err, "coscli.Object.Copy")
  453. }
  454. return nil
  455. }
  456. func (b *SBucket) GetObject(ctx context.Context, key string, rangeOpt *cloudprovider.SGetObjectRange) (io.ReadCloser, error) {
  457. coscli, err := b.region.GetCosClient(b)
  458. if err != nil {
  459. return nil, errors.Wrap(err, "GetCosClient")
  460. }
  461. opts := &cos.ObjectGetOptions{}
  462. if rangeOpt != nil {
  463. opts.Range = rangeOpt.String()
  464. }
  465. resp, err := coscli.Object.Get(ctx, key, opts)
  466. if err != nil {
  467. return nil, errors.Wrap(err, "coscli.Object.Get")
  468. }
  469. return resp.Body, nil
  470. }
  471. func (b *SBucket) CopyPart(ctx context.Context, key string, uploadId string, partIndex int, srcBucketName string, srcKey string, srcOffset int64, srcLength int64) (string, error) {
  472. coscli, err := b.region.GetCosClient(b)
  473. if err != nil {
  474. return "", errors.Wrap(err, "GetCosClient")
  475. }
  476. srcBucket := SBucket{
  477. region: b.region,
  478. Name: srcBucketName,
  479. }
  480. opts := cos.ObjectCopyPartOptions{}
  481. srcUrl := fmt.Sprintf("%s/%s", srcBucket.getBucketUrlHost(), srcKey)
  482. opts.XCosCopySourceRange = fmt.Sprintf("bytes=%d-%d", srcOffset, srcOffset+srcLength-1)
  483. result, _, err := coscli.Object.CopyPart(ctx, key, uploadId, partIndex, srcUrl, &opts)
  484. if err != nil {
  485. return "", errors.Wrap(err, "coscli.Object.CopyPart")
  486. }
  487. return result.ETag, nil
  488. }
  489. func (b *SBucket) SetWebsite(websitConf cloudprovider.SBucketWebsiteConf) error {
  490. if len(websitConf.Index) == 0 {
  491. return errors.Wrap(cloudprovider.ErrNotSupported, "missing Index")
  492. }
  493. if len(websitConf.ErrorDocument) == 0 {
  494. return errors.Wrap(cloudprovider.ErrNotSupported, "missing ErrorDocument")
  495. }
  496. if websitConf.Protocol != "http" && websitConf.Protocol != "https" {
  497. return errors.Wrap(cloudprovider.ErrNotSupported, "missing Protocol")
  498. }
  499. coscli, err := b.region.GetCosClient(b)
  500. if err != nil {
  501. return errors.Wrap(err, "b.region.GetCosClient")
  502. }
  503. rulesOpts := []cos.WebsiteRoutingRule{}
  504. for i := range websitConf.Rules {
  505. rulesOpts = append(rulesOpts, cos.WebsiteRoutingRule{
  506. ConditionErrorCode: websitConf.Rules[i].ConditionErrorCode,
  507. ConditionPrefix: websitConf.Rules[i].ConditionPrefix,
  508. RedirectProtocol: websitConf.Rules[i].RedirectProtocol,
  509. RedirectReplaceKey: websitConf.Rules[i].RedirectReplaceKey,
  510. RedirectReplaceKeyPrefix: websitConf.Rules[i].ConditionPrefix,
  511. })
  512. }
  513. opts := &cos.BucketPutWebsiteOptions{
  514. Index: websitConf.Index,
  515. Error: &cos.ErrorDocument{Key: websitConf.ErrorDocument},
  516. RedirectProtocol: &cos.RedirectRequestsProtocol{Protocol: websitConf.Protocol},
  517. }
  518. if len(rulesOpts) > 0 {
  519. opts.RoutingRules = &cos.WebsiteRoutingRules{Rules: rulesOpts}
  520. }
  521. _, err = coscli.Bucket.PutWebsite(context.Background(), opts)
  522. if err != nil {
  523. return errors.Wrap(err, "PutWebsite")
  524. }
  525. return nil
  526. }
  527. func (b *SBucket) GetWebsiteConf() (cloudprovider.SBucketWebsiteConf, error) {
  528. coscli, err := b.region.GetCosClient(b)
  529. if err != nil {
  530. return cloudprovider.SBucketWebsiteConf{}, errors.Wrap(err, "b.region.GetCosClient")
  531. }
  532. websiteResult, _, err := coscli.Bucket.GetWebsite(context.Background())
  533. if err != nil {
  534. if strings.Contains(err.Error(), "NoSuchWebsiteConfiguration") {
  535. return cloudprovider.SBucketWebsiteConf{}, nil
  536. }
  537. return cloudprovider.SBucketWebsiteConf{}, errors.Wrap(err, "coscli.Bucket.GetWebsite")
  538. }
  539. result := cloudprovider.SBucketWebsiteConf{
  540. Index: websiteResult.Index,
  541. }
  542. if websiteResult.Error != nil {
  543. result.ErrorDocument = websiteResult.Error.Key
  544. }
  545. if websiteResult.RedirectProtocol != nil {
  546. result.Protocol = websiteResult.RedirectProtocol.Protocol
  547. }
  548. routingRules := []cloudprovider.SBucketWebsiteRoutingRule{}
  549. if websiteResult.RoutingRules != nil {
  550. for i := range websiteResult.RoutingRules.Rules {
  551. routingRules = append(routingRules, cloudprovider.SBucketWebsiteRoutingRule{
  552. ConditionErrorCode: websiteResult.RoutingRules.Rules[i].ConditionErrorCode,
  553. ConditionPrefix: websiteResult.RoutingRules.Rules[i].ConditionPrefix,
  554. RedirectProtocol: websiteResult.RoutingRules.Rules[i].RedirectProtocol,
  555. RedirectReplaceKey: websiteResult.RoutingRules.Rules[i].RedirectReplaceKey,
  556. RedirectReplaceKeyPrefix: websiteResult.RoutingRules.Rules[i].RedirectReplaceKeyPrefix,
  557. })
  558. }
  559. }
  560. result.Rules = routingRules
  561. result.Url = b.getWebsiteUrl()
  562. return result, nil
  563. }
  564. func (b *SBucket) DeleteWebSiteConf() error {
  565. coscli, err := b.region.GetCosClient(b)
  566. if err != nil {
  567. return errors.Wrap(err, "b.region.GetCosClient")
  568. }
  569. _, err = coscli.Bucket.DeleteWebsite(context.Background())
  570. if err != nil {
  571. return errors.Wrap(err, "coscli.Bucket.DeleteWebsite")
  572. }
  573. return nil
  574. }
  575. func (b *SBucket) SetCORS(rules []cloudprovider.SBucketCORSRule) error {
  576. if len(rules) == 0 {
  577. return nil
  578. }
  579. coscli, err := b.region.GetCosClient(b)
  580. if err != nil {
  581. return errors.Wrap(err, "b.region.GetCosClient")
  582. }
  583. input := cos.BucketPutCORSOptions{}
  584. for i := range rules {
  585. input.Rules = append(input.Rules, cos.BucketCORSRule{
  586. AllowedOrigins: rules[i].AllowedOrigins,
  587. AllowedMethods: rules[i].AllowedMethods,
  588. AllowedHeaders: rules[i].AllowedHeaders,
  589. MaxAgeSeconds: rules[i].MaxAgeSeconds,
  590. ExposeHeaders: rules[i].ExposeHeaders,
  591. ID: rules[i].Id,
  592. })
  593. }
  594. _, err = coscli.Bucket.PutCORS(context.Background(), &input)
  595. if err != nil {
  596. return errors.Wrap(err, "coscli.Bucket.PutCORS")
  597. }
  598. return nil
  599. }
  600. func (b *SBucket) GetCORSRules() ([]cloudprovider.SBucketCORSRule, error) {
  601. coscli, err := b.region.GetCosClient(b)
  602. if err != nil {
  603. return nil, errors.Wrap(err, "b.region.GetCosClient")
  604. }
  605. conf, _, err := coscli.Bucket.GetCORS(context.Background())
  606. if err != nil {
  607. if strings.Contains(err.Error(), "NoSuchCORSConfiguration") {
  608. return nil, nil
  609. }
  610. return nil, errors.Wrap(err, "b.region.GetCORS")
  611. }
  612. result := []cloudprovider.SBucketCORSRule{}
  613. for i := range conf.Rules {
  614. result = append(result, cloudprovider.SBucketCORSRule{
  615. AllowedOrigins: conf.Rules[i].AllowedOrigins,
  616. AllowedMethods: conf.Rules[i].AllowedMethods,
  617. AllowedHeaders: conf.Rules[i].AllowedHeaders,
  618. MaxAgeSeconds: conf.Rules[i].MaxAgeSeconds,
  619. ExposeHeaders: conf.Rules[i].ExposeHeaders,
  620. Id: strconv.Itoa(i),
  621. })
  622. }
  623. return result, nil
  624. }
  625. func (b *SBucket) DeleteCORS() error {
  626. coscli, err := b.region.GetCosClient(b)
  627. if err != nil {
  628. return errors.Wrap(err, "b.region.GetCosClient")
  629. }
  630. _, err = coscli.Bucket.DeleteCORS(context.Background())
  631. if err != nil {
  632. return errors.Wrap(err, "coscli.Bucket.DeleteCORS")
  633. }
  634. return nil
  635. }
  636. func (b *SBucket) SetReferer(conf cloudprovider.SBucketRefererConf) error {
  637. coscli, err := b.region.GetCosClient(b)
  638. if err != nil {
  639. return errors.Wrap(err, "b.region.GetCosClient")
  640. }
  641. if !conf.Enabled {
  642. _, err = coscli.Bucket.PutReferer(context.Background(), nil)
  643. return errors.Wrap(err, "Disable Refer")
  644. }
  645. opts := cos.BucketPutRefererOptions{
  646. Status: "Enabled",
  647. EmptyReferConfiguration: "Deny",
  648. RefererType: conf.RefererType,
  649. DomainList: conf.DomainList,
  650. }
  651. if conf.AllowEmptyRefer {
  652. opts.EmptyReferConfiguration = "Allow"
  653. }
  654. _, err = coscli.Bucket.PutReferer(context.Background(), &opts)
  655. if err != nil {
  656. return errors.Wrap(err, "coscli.Bucket.PutReferer")
  657. }
  658. return nil
  659. }
  660. func (b *SBucket) GetReferer() (cloudprovider.SBucketRefererConf, error) {
  661. result := cloudprovider.SBucketRefererConf{}
  662. coscli, err := b.region.GetCosClient(b)
  663. if err != nil {
  664. return result, errors.Wrap(err, "b.region.GetCosClient")
  665. }
  666. referResult, _, err := coscli.Bucket.GetReferer(context.Background())
  667. if err != nil {
  668. return result, errors.Wrap(err, " coscli.Bucket.GetReferer")
  669. }
  670. result.AllowEmptyRefer = (referResult.EmptyReferConfiguration == "Allow")
  671. result.Enabled = (referResult.Status == "Enabled")
  672. result.RefererType = referResult.RefererType
  673. result.DomainList = referResult.DomainList
  674. return result, nil
  675. }
  676. func toAPICdnArea(area string) string {
  677. switch area {
  678. case "mainland":
  679. return api.CDN_DOMAIN_AREA_MAINLAND
  680. case "overseas":
  681. return api.CDN_DOMAIN_AREA_OVERSEAS
  682. case "global":
  683. return api.CDN_DOMAIN_AREA_GLOBAL
  684. default:
  685. return ""
  686. }
  687. }
  688. func toAPICdnStatus(status string) string {
  689. switch status {
  690. case "online":
  691. return api.CDN_DOMAIN_STATUS_ONLINE
  692. case "offline":
  693. return api.CDN_DOMAIN_STATUS_OFFLINE
  694. case "processing":
  695. return api.CDN_DOMAIN_STATUS_PROCESSING
  696. case "rejected":
  697. return api.CDN_DOMAIN_STATUS_REJECTED
  698. default:
  699. return ""
  700. }
  701. }
  702. func (b *SBucket) GetCdnDomains() ([]cloudprovider.SCdnDomain, error) {
  703. result := []cloudprovider.SCdnDomain{}
  704. bucketHost := b.getBucketUrlHost()
  705. bucketWebsiteHost := b.getBucketWebsiteUrlHost()
  706. bucketCdnDomains, err := b.region.client.DescribeAllCdnDomains(nil, []string{bucketHost}, "cos")
  707. if err != nil {
  708. return nil, errors.Wrapf(err, `b.region.client.DescribeAllCdnDomains(nil, []string{%s}, "cos")`, bucketHost)
  709. }
  710. for i := range bucketCdnDomains {
  711. result = append(result, cloudprovider.SCdnDomain{
  712. Domain: bucketCdnDomains[i].Domain,
  713. Status: toAPICdnStatus(bucketCdnDomains[i].Status),
  714. Cname: bucketCdnDomains[i].Cname,
  715. Area: toAPICdnArea(bucketCdnDomains[i].Area),
  716. Origin: bucketHost,
  717. OriginType: api.CDN_DOMAIN_ORIGIN_TYPE_BUCKET,
  718. })
  719. }
  720. bucketWebsiteCdnDomains, err := b.region.client.DescribeAllCdnDomains(nil, []string{bucketWebsiteHost}, "cos")
  721. if err != nil {
  722. return nil, errors.Wrapf(err, `b.region.client.DescribeAllCdnDomains(nil, []string{%s}, "cos")`, bucketWebsiteHost)
  723. }
  724. for i := range bucketWebsiteCdnDomains {
  725. result = append(result, cloudprovider.SCdnDomain{
  726. Domain: bucketWebsiteCdnDomains[i].Domain,
  727. Status: toAPICdnStatus(bucketWebsiteCdnDomains[i].Status),
  728. Cname: bucketWebsiteCdnDomains[i].Cname,
  729. Area: toAPICdnArea(bucketWebsiteCdnDomains[i].Area),
  730. Origin: bucketWebsiteHost,
  731. OriginType: api.CDN_DOMAIN_ORIGIN_TYPE_BUCKET,
  732. })
  733. }
  734. return result, nil
  735. }
  736. func getQcsResourcePath(resource []string) []string {
  737. path := []string{}
  738. for i := range resource {
  739. strs := strings.Split(resource[i], ":")
  740. path = append(path, strs[len(strs)-1])
  741. }
  742. return path
  743. }
  744. func getQcsUserId(principal []string) []string {
  745. ids := []string{}
  746. for i := range principal {
  747. // qcs::cam::uin/100008182714:uin/100008182714
  748. // qcs::cam::uin/100008182714:service/cdn
  749. // qcs::cam::anyone:anyone
  750. strs := strings.Split(principal[i], "::")
  751. ids = append(ids, strings.Replace(strs[len(strs)-1], "uin/", "", 2))
  752. }
  753. return ids
  754. }
  755. var cannedReadActions = [...]string{
  756. "name/cos:GetBucket",
  757. "name/cos:GetBucketObjectVersions",
  758. "name/cos:HeadBucket",
  759. "name/cos:ListMultipartUploads",
  760. "name/cos:ListParts",
  761. "name/cos:GetObject",
  762. "name/cos:HeadObject",
  763. "name/cos:OptionsObject",
  764. }
  765. var cannedReadWriteActions = [...]string{
  766. "name/cos:GetBucket",
  767. "name/cos:GetBucketObjectVersions",
  768. "name/cos:HeadBucket",
  769. "name/cos:ListMultipartUploads",
  770. "name/cos:ListParts",
  771. "name/cos:GetObject",
  772. "name/cos:HeadObject",
  773. "name/cos:OptionsObject",
  774. "name/cos:PutObject",
  775. "name/cos:PostObject",
  776. "name/cos:DeleteObject",
  777. "name/cos:InitiateMultipartUpload",
  778. "name/cos:UploadPart",
  779. "name/cos:CompleteMultipartUpload",
  780. "name/cos:AbortMultipartUpload",
  781. }
  782. func getCannedAction(action []string) string {
  783. cannedAction := ""
  784. actionSet := set.New(set.NonThreadSafe)
  785. for i := range action {
  786. actionSet.Add(action[i])
  787. }
  788. if actionSet.Has("name/cos:*") {
  789. return "FullControl"
  790. }
  791. readSet := set.New(set.NonThreadSafe)
  792. for i := range cannedReadActions {
  793. readSet.Add(cannedReadActions[i])
  794. }
  795. if set.Difference(readSet, actionSet).Size() == 0 {
  796. cannedAction = "Read"
  797. }
  798. readWriteSet := set.New(set.NonThreadSafe)
  799. for i := range cannedReadWriteActions {
  800. readWriteSet.Add(cannedReadWriteActions[i])
  801. }
  802. if set.Difference(readWriteSet, actionSet).Size() == 0 {
  803. cannedAction = "ReadWrite"
  804. }
  805. return cannedAction
  806. }
  807. func (b *SBucket) GetPolicy() ([]cloudprovider.SBucketPolicyStatement, error) {
  808. policyOptions := []cloudprovider.SBucketPolicyStatement{}
  809. coscli, err := b.region.GetCosClient(b)
  810. if err != nil {
  811. return nil, errors.Wrap(err, "GetCosClient")
  812. }
  813. result, _, err := coscli.Bucket.GetPolicy(context.Background())
  814. if err != nil {
  815. if strings.Contains(err.Error(), "404") {
  816. return nil, nil
  817. }
  818. return nil, errors.Wrap(err, "GetPolicy")
  819. }
  820. users, err := b.region.client.GetICloudusers()
  821. if err != nil {
  822. return nil, errors.Wrapf(err, "GetICloudusers")
  823. }
  824. userMaps := map[string]string{}
  825. for i := range users {
  826. userMaps[fmt.Sprintf("%s:%s", b.region.client.ownerName, users[i].GetGlobalId())] = users[i].GetName()
  827. }
  828. for i := range result.Statement {
  829. policyOption := cloudprovider.SBucketPolicyStatement{
  830. Principal: result.Statement[i].Principal,
  831. Action: result.Statement[i].Action,
  832. Effect: result.Statement[i].Effect,
  833. Resource: result.Statement[i].Resource,
  834. Condition: result.Statement[i].Condition,
  835. PrincipalId: getQcsUserId(result.Statement[i].Principal["qcs"]),
  836. CannedAction: getCannedAction(result.Statement[i].Action),
  837. ResourcePath: getQcsResourcePath(result.Statement[i].Resource),
  838. Id: strconv.Itoa(i),
  839. }
  840. policyOption.PrincipalNames = func() map[string]string {
  841. ret := map[string]string{}
  842. for _, id := range policyOption.PrincipalId {
  843. ret[id], _ = userMaps[id]
  844. }
  845. return ret
  846. }()
  847. policyOptions = append(policyOptions, policyOption)
  848. }
  849. return policyOptions, nil
  850. }
  851. func (b *SBucket) SetPolicy(policy cloudprovider.SBucketPolicyStatementInput) error {
  852. coscli, err := b.region.GetCosClient(b)
  853. if err != nil {
  854. return errors.Wrapf(err, "GetCosClient")
  855. }
  856. opts := cos.BucketPutPolicyOptions{}
  857. opts.Version = "2.0"
  858. oldOpts, _, err := coscli.Bucket.GetPolicy(context.Background())
  859. if err != nil {
  860. if !strings.Contains(err.Error(), "404") {
  861. return errors.Wrap(err, "GetPolicy")
  862. }
  863. }
  864. if len(oldOpts.Statement) > 0 {
  865. opts.Statement = oldOpts.Statement
  866. }
  867. newStatement := cos.BucketStatement{}
  868. ids := []string{}
  869. for i := range policy.PrincipalId {
  870. id := strings.Split(policy.PrincipalId[i], ":")
  871. if len(id) == 1 {
  872. ids = append(ids, fmt.Sprintf("qcs::cam::uin/%s:uin/%s", id[0], id[0]))
  873. }
  874. if len(id) == 2 {
  875. // 没有主账号id,设为owner id
  876. if len(id[0]) == 0 {
  877. s, _, err := coscli.Service.Get(context.Background())
  878. if err != nil {
  879. return errors.Wrap(err, "coscli.Service.Get")
  880. }
  881. id[0] = s.Owner.DisplayName
  882. }
  883. // 没有子账号,默认和主账号相同
  884. if len(id[1]) == 0 {
  885. id[1] = id[0]
  886. }
  887. ids = append(ids, fmt.Sprintf("qcs::cam::uin/%s:uin/%s", id[0], id[1]))
  888. }
  889. if len(id) > 2 {
  890. return errors.Wrap(cloudprovider.ErrNotSupported, "Invalida PrincipalId Input")
  891. }
  892. }
  893. principal := map[string][]string{}
  894. principal["qcs"] = ids
  895. newStatement.Principal = principal
  896. newStatement.Effect = policy.Effect
  897. resources := []string{}
  898. for i := range policy.ResourcePath {
  899. resources = append(resources, fmt.Sprintf("qcs::cos:%s:uid/%s:%s%s", b.GetIRegion().GetId(), b.appId, b.getFullName(), policy.ResourcePath[i]))
  900. }
  901. newStatement.Resource = resources
  902. ipEqual := []string{}
  903. ipNotEqual := []string{}
  904. for i := range policy.IpEquals {
  905. ipEqual = append(ipEqual, policy.IpEquals[i])
  906. }
  907. for i := range policy.IpNotEquals {
  908. ipNotEqual = append(ipNotEqual, policy.IpNotEquals[i])
  909. }
  910. condition := map[string]map[string]interface{}{}
  911. newStatement.Condition = condition
  912. if len(ipEqual) > 0 {
  913. newStatement.Condition["ip_equal"] = map[string]interface{}{"qcs:ip": ipEqual}
  914. }
  915. if len(ipNotEqual) > 0 {
  916. newStatement.Condition["ip_not_equal"] = map[string]interface{}{"qcs:ip": ipNotEqual}
  917. }
  918. if policy.CannedAction == "FullControl" {
  919. newStatement.Action = []string{"name/cos:*"}
  920. }
  921. if policy.CannedAction == "Read" {
  922. newStatement.Action = cannedReadActions[:]
  923. }
  924. if policy.CannedAction == "ReadWrite" {
  925. newStatement.Action = cannedReadWriteActions[:]
  926. }
  927. opts.Statement = append([]cos.BucketStatement{newStatement}, opts.Statement...)
  928. _, err = coscli.Bucket.PutPolicy(context.Background(), &opts)
  929. if err != nil {
  930. log.Errorf("coscli.Bucket.GetACL fail %s", err)
  931. return errors.Wrapf(err, " coscli.Bucket.PutPolicy(context.Background(), %s)", jsonutils.Marshal(opts).String())
  932. }
  933. return nil
  934. }
  935. func (b *SBucket) DeletePolicy(id []string) ([]cloudprovider.SBucketPolicyStatement, error) {
  936. deletedPolicy := []cloudprovider.SBucketPolicyStatement{}
  937. coscli, err := b.region.GetCosClient(b)
  938. if err != nil {
  939. log.Errorf("GetCosClient fail %s", err)
  940. return nil, errors.Wrap(err, "b.region.GetCosClient(b)")
  941. }
  942. result, _, err := coscli.Bucket.GetPolicy(context.Background())
  943. if err != nil {
  944. if strings.Contains(err.Error(), "404") {
  945. return nil, nil
  946. }
  947. log.Errorf("coscli.Bucket.GetACL fail %s", err)
  948. return nil, errors.Wrap(err, "coscli.Bucket.GetPolicy(context.Background())")
  949. }
  950. newOpts := cos.BucketPutPolicyOptions{}
  951. newOpts.Version = result.Version
  952. newOpts.Principal = result.Principal
  953. excludeMap := map[int]bool{}
  954. for i := range id {
  955. index, err := strconv.Atoi(id[i])
  956. if err == nil {
  957. excludeMap[index] = true
  958. }
  959. }
  960. for i := range result.Statement {
  961. if _, ok := excludeMap[i]; !ok {
  962. newOpts.Statement = append(newOpts.Statement, result.Statement[i])
  963. } else {
  964. deletedPolicy = append(deletedPolicy, cloudprovider.SBucketPolicyStatement{
  965. Principal: result.Statement[i].Principal,
  966. Action: result.Statement[i].Action,
  967. Effect: result.Statement[i].Effect,
  968. Resource: result.Statement[i].Resource,
  969. Condition: result.Statement[i].Condition,
  970. PrincipalId: getQcsUserId(result.Statement[i].Principal["qcs"]),
  971. CannedAction: getCannedAction(result.Statement[i].Action),
  972. ResourcePath: getQcsResourcePath(result.Statement[i].Resource),
  973. })
  974. }
  975. }
  976. if len(newOpts.Statement) == 0 {
  977. _, err := coscli.Bucket.DeletePolicy(context.Background())
  978. if err != nil {
  979. log.Errorf("coscli.Bucket.DeletePolicy fail %s", err)
  980. return nil, errors.Wrap(err, "coscli.Bucket.DeletePolicy(context.Background())")
  981. }
  982. return deletedPolicy, nil
  983. }
  984. _, err = coscli.Bucket.PutPolicy(context.Background(), &newOpts)
  985. if err != nil {
  986. log.Errorf("coscli.Bucket.GetACL fail %s", err)
  987. return nil, errors.Wrapf(err, "coscli.Bucket.PutPolicy(context.Background(), %s)", jsonutils.Marshal(newOpts).String())
  988. }
  989. return deletedPolicy, nil
  990. }
  991. func (b *SBucket) GetTags() (map[string]string, error) {
  992. coscli, err := b.region.GetCosClient(b)
  993. if err != nil {
  994. return nil, errors.Wrap(err, "GetCosClient")
  995. }
  996. tagresult, _, err := coscli.Bucket.GetTagging(context.Background())
  997. if err != nil {
  998. if strings.Contains(err.Error(), "404") {
  999. return nil, nil
  1000. }
  1001. return nil, errors.Wrap(err, "GetTagging")
  1002. }
  1003. result := map[string]string{}
  1004. for i := range tagresult.TagSet {
  1005. result[tagresult.TagSet[i].Key] = tagresult.TagSet[i].Value
  1006. }
  1007. return result, nil
  1008. }
  1009. func (b *SBucket) SetTags(tags map[string]string, replace bool) error {
  1010. if !replace {
  1011. return cloudprovider.ErrNotSupported
  1012. }
  1013. coscli, err := b.region.GetCosClient(b)
  1014. if err != nil {
  1015. return errors.Wrapf(err, "b.region.GetCosClient(%s)", b.Name)
  1016. }
  1017. _, err = coscli.Bucket.DeleteTagging(context.Background())
  1018. if err != nil {
  1019. return errors.Wrapf(err, "DeleteTagging")
  1020. }
  1021. if len(tags) == 0 {
  1022. return nil
  1023. }
  1024. input := cos.BucketPutTaggingOptions{}
  1025. for k, v := range tags {
  1026. input.TagSet = append(input.TagSet, cos.BucketTaggingTag{Key: k, Value: v})
  1027. }
  1028. _, err = coscli.Bucket.PutTagging(context.Background(), &input)
  1029. if err != nil {
  1030. return errors.Wrapf(err, "coscli.Bucket.PutTagging(%s)", jsonutils.Marshal(input))
  1031. }
  1032. return nil
  1033. }
  1034. func (b *SBucket) ListMultipartUploads() ([]cloudprovider.SBucketMultipartUploads, error) {
  1035. coscli, err := b.region.GetCosClient(b)
  1036. if err != nil {
  1037. log.Errorf("GetCosClient fail %s", err)
  1038. return nil, errors.Wrap(err, "b.region.GetCosClient(b)")
  1039. }
  1040. result := []cloudprovider.SBucketMultipartUploads{}
  1041. input := cos.ListMultipartUploadsOptions{}
  1042. keyMarker := ""
  1043. uploadIDMarker := ""
  1044. for {
  1045. input.KeyMarker = keyMarker
  1046. input.UploadIDMarker = uploadIDMarker
  1047. output, _, err := coscli.Bucket.ListMultipartUploads(context.Background(), &input)
  1048. if err != nil {
  1049. return nil, errors.Wrap(err, " coscli.Bucket.ListMultipartUploads(context.Background(), &input)")
  1050. }
  1051. for i := range output.Uploads {
  1052. temp := cloudprovider.SBucketMultipartUploads{
  1053. ObjectName: output.Uploads[i].Key,
  1054. UploadID: output.Uploads[i].UploadID,
  1055. Initiator: output.Uploads[i].Initiator.DisplayName,
  1056. }
  1057. temp.Initiated, _ = timeutils.ParseTimeStr(output.Uploads[i].Initiated)
  1058. result = append(result, temp)
  1059. }
  1060. keyMarker = output.NextKeyMarker
  1061. uploadIDMarker = output.NextUploadIDMarker
  1062. if !output.IsTruncated {
  1063. break
  1064. }
  1065. }
  1066. return result, nil
  1067. }