bucket.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  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 huawei
  15. import (
  16. "context"
  17. "fmt"
  18. "io"
  19. "net/http"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "yunion.io/x/jsonutils"
  24. "yunion.io/x/log"
  25. "yunion.io/x/pkg/errors"
  26. "yunion.io/x/pkg/utils"
  27. "yunion.io/x/s3cli"
  28. "yunion.io/x/cloudmux/pkg/cloudprovider"
  29. "yunion.io/x/cloudmux/pkg/multicloud"
  30. "yunion.io/x/cloudmux/pkg/multicloud/huawei/obs"
  31. )
  32. type SBucket struct {
  33. multicloud.SBaseBucket
  34. HuaweiTags
  35. region *SRegion
  36. Name string
  37. Location string
  38. CreationDate time.Time
  39. }
  40. func (b *SBucket) GetProjectId() string {
  41. resp, err := b.region.HeadBucket(b.Name)
  42. if err != nil {
  43. return ""
  44. }
  45. epid, _ := resp.ResponseHeaders["epid"]
  46. if len(epid) > 0 {
  47. return epid[0]
  48. }
  49. return ""
  50. }
  51. func (b *SBucket) GetGlobalId() string {
  52. return b.Name
  53. }
  54. func (b *SBucket) GetName() string {
  55. return b.Name
  56. }
  57. func (b *SBucket) GetLocation() string {
  58. return b.Location
  59. }
  60. func (b *SBucket) GetIRegion() cloudprovider.ICloudRegion {
  61. return b.region
  62. }
  63. func (b *SBucket) GetCreatedAt() time.Time {
  64. return b.CreationDate
  65. }
  66. func (b *SBucket) GetStorageClass() string {
  67. obscli, err := b.region.getOBSClient("")
  68. if err != nil {
  69. log.Errorf("b.region.getOBSClient error %s", err)
  70. return ""
  71. }
  72. output, err := obscli.GetBucketStoragePolicy(b.Name)
  73. if err != nil {
  74. log.Errorf("obscli.GetBucketStoragePolicy error %s", err)
  75. return ""
  76. }
  77. return output.StorageClass
  78. }
  79. func obsAcl2CannedAcl(acls []obs.Grant) cloudprovider.TBucketACLType {
  80. switch {
  81. case len(acls) == 1:
  82. if acls[0].Grantee.URI == "" && acls[0].Permission == s3cli.PERMISSION_FULL_CONTROL {
  83. return cloudprovider.ACLPrivate
  84. }
  85. case len(acls) == 2:
  86. for _, g := range acls {
  87. if g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_AUTH_USERS && g.Permission == s3cli.PERMISSION_READ {
  88. return cloudprovider.ACLAuthRead
  89. }
  90. if g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && g.Permission == s3cli.PERMISSION_READ {
  91. return cloudprovider.ACLPublicRead
  92. }
  93. }
  94. case len(acls) == 3:
  95. for _, g := range acls {
  96. if g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && g.Permission == s3cli.PERMISSION_WRITE {
  97. return cloudprovider.ACLPublicReadWrite
  98. }
  99. }
  100. }
  101. return cloudprovider.ACLUnknown
  102. }
  103. func (b *SBucket) GetAcl() cloudprovider.TBucketACLType {
  104. acl := cloudprovider.ACLPrivate
  105. obscli, err := b.region.getOBSClient("")
  106. if err != nil {
  107. log.Errorf("b.region.getOBSClient error %s", err)
  108. return acl
  109. }
  110. output, err := obscli.GetBucketAcl(b.Name)
  111. if err != nil {
  112. log.Errorf("obscli.GetBucketAcl error %s", err)
  113. return acl
  114. }
  115. acl = obsAcl2CannedAcl(output.Grants)
  116. return acl
  117. }
  118. func (b *SBucket) SetAcl(acl cloudprovider.TBucketACLType) error {
  119. obscli, err := b.region.getOBSClient("")
  120. if err != nil {
  121. return errors.Wrap(err, "b.region.getOBSClient")
  122. }
  123. input := &obs.SetBucketAclInput{}
  124. input.Bucket = b.Name
  125. input.ACL = obs.AclType(string(acl))
  126. _, err = obscli.SetBucketAcl(input)
  127. if err != nil {
  128. return errors.Wrap(err, "obscli.SetBucketAcl")
  129. }
  130. return nil
  131. }
  132. func (b *SBucket) GetAccessUrls() []cloudprovider.SBucketAccessUrl {
  133. return []cloudprovider.SBucketAccessUrl{
  134. {
  135. Url: fmt.Sprintf("https://%s.%s", b.Name, b.region.getOBSEndpoint()),
  136. Description: "bucket url",
  137. Primary: true,
  138. },
  139. {
  140. Url: fmt.Sprintf("https://%s/%s", b.region.getOBSEndpoint(), b.Name),
  141. Description: "obs url",
  142. },
  143. }
  144. }
  145. func (b *SBucket) GetStats() cloudprovider.SBucketStats {
  146. stats := cloudprovider.SBucketStats{}
  147. obscli, err := b.region.getOBSClient("")
  148. if err != nil {
  149. log.Errorf("b.region.getOBSClient error %s", err)
  150. stats.SizeBytes = -1
  151. stats.ObjectCount = -1
  152. return stats
  153. }
  154. output, err := obscli.GetBucketStorageInfo(b.Name)
  155. if err != nil {
  156. log.Errorf("obscli.GetBucketStorageInfo error %s", err)
  157. stats.SizeBytes = -1
  158. stats.ObjectCount = -1
  159. return stats
  160. }
  161. stats.SizeBytes = output.Size
  162. stats.ObjectCount = output.ObjectNumber
  163. return stats
  164. }
  165. func (b *SBucket) ListObjects(prefix string, marker string, delimiter string, maxCount int) (cloudprovider.SListObjectResult, error) {
  166. result := cloudprovider.SListObjectResult{}
  167. obscli, err := b.region.getOBSClient("")
  168. if err != nil {
  169. return result, errors.Wrap(err, "GetOBSClient")
  170. }
  171. input := &obs.ListObjectsInput{}
  172. input.Bucket = b.Name
  173. if len(prefix) > 0 {
  174. input.Prefix = prefix
  175. }
  176. if len(marker) > 0 {
  177. input.Marker = marker
  178. }
  179. if len(delimiter) > 0 {
  180. input.Delimiter = delimiter
  181. }
  182. if maxCount > 0 {
  183. input.MaxKeys = maxCount
  184. }
  185. oResult, err := obscli.ListObjects(input)
  186. if err != nil {
  187. return result, errors.Wrap(err, "ListObjects")
  188. }
  189. result.Objects = make([]cloudprovider.ICloudObject, 0)
  190. for _, object := range oResult.Contents {
  191. obj := &SObject{
  192. bucket: b,
  193. SBaseCloudObject: cloudprovider.SBaseCloudObject{
  194. StorageClass: string(object.StorageClass),
  195. Key: object.Key,
  196. SizeBytes: object.Size,
  197. ETag: object.ETag,
  198. LastModified: object.LastModified,
  199. },
  200. }
  201. result.Objects = append(result.Objects, obj)
  202. }
  203. if oResult.CommonPrefixes != nil {
  204. result.CommonPrefixes = make([]cloudprovider.ICloudObject, 0)
  205. for _, commonPrefix := range oResult.CommonPrefixes {
  206. obj := &SObject{
  207. bucket: b,
  208. SBaseCloudObject: cloudprovider.SBaseCloudObject{
  209. Key: commonPrefix,
  210. },
  211. }
  212. result.CommonPrefixes = append(result.CommonPrefixes, obj)
  213. }
  214. }
  215. result.IsTruncated = oResult.IsTruncated
  216. result.NextMarker = oResult.NextMarker
  217. return result, nil
  218. }
  219. func (b *SBucket) PutObject(ctx context.Context, key string, reader io.Reader, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
  220. obscli, err := b.region.getOBSClient("")
  221. if err != nil {
  222. return errors.Wrap(err, "GetOBSClient")
  223. }
  224. input := &obs.PutObjectInput{}
  225. input.Bucket = b.Name
  226. input.Key = key
  227. input.Body = reader
  228. if sizeBytes > 0 {
  229. input.ContentLength = sizeBytes
  230. }
  231. if len(storageClassStr) > 0 {
  232. input.StorageClass, err = str2StorageClass(storageClassStr)
  233. if err != nil {
  234. return err
  235. }
  236. }
  237. if len(cannedAcl) == 0 {
  238. cannedAcl = b.GetAcl()
  239. }
  240. input.ACL = obs.AclType(string(cannedAcl))
  241. if meta != nil {
  242. val := meta.Get(cloudprovider.META_HEADER_CONTENT_TYPE)
  243. if len(val) > 0 {
  244. input.ContentType = val
  245. }
  246. val = meta.Get(cloudprovider.META_HEADER_CONTENT_MD5)
  247. if len(val) > 0 {
  248. input.ContentMD5 = val
  249. }
  250. extraMeta := make(map[string]string)
  251. for k, v := range meta {
  252. if utils.IsInStringArray(k, []string{
  253. cloudprovider.META_HEADER_CONTENT_TYPE,
  254. cloudprovider.META_HEADER_CONTENT_MD5,
  255. }) {
  256. continue
  257. }
  258. if len(v[0]) > 0 {
  259. extraMeta[k] = v[0]
  260. }
  261. }
  262. input.Metadata = extraMeta
  263. }
  264. _, err = obscli.PutObject(input)
  265. if err != nil {
  266. return errors.Wrap(err, "PutObject")
  267. }
  268. return nil
  269. }
  270. func (b *SBucket) NewMultipartUpload(ctx context.Context, key string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) (string, error) {
  271. obscli, err := b.region.getOBSClient("")
  272. if err != nil {
  273. return "", errors.Wrap(err, "GetOBSClient")
  274. }
  275. input := &obs.InitiateMultipartUploadInput{}
  276. input.Bucket = b.Name
  277. input.Key = key
  278. if meta != nil {
  279. val := meta.Get(cloudprovider.META_HEADER_CONTENT_TYPE)
  280. if len(val) > 0 {
  281. input.ContentType = val
  282. }
  283. extraMeta := make(map[string]string)
  284. for k, v := range meta {
  285. if utils.IsInStringArray(k, []string{
  286. cloudprovider.META_HEADER_CONTENT_TYPE,
  287. }) {
  288. continue
  289. }
  290. if len(v[0]) > 0 {
  291. extraMeta[k] = v[0]
  292. }
  293. }
  294. input.Metadata = extraMeta
  295. }
  296. if len(cannedAcl) == 0 {
  297. cannedAcl = b.GetAcl()
  298. }
  299. input.ACL = obs.AclType(string(cannedAcl))
  300. if len(storageClassStr) > 0 {
  301. input.StorageClass, err = str2StorageClass(storageClassStr)
  302. if err != nil {
  303. return "", errors.Wrap(err, "str2StorageClass")
  304. }
  305. }
  306. output, err := obscli.InitiateMultipartUpload(input)
  307. if err != nil {
  308. return "", errors.Wrap(err, "InitiateMultipartUpload")
  309. }
  310. return output.UploadId, nil
  311. }
  312. func (b *SBucket) UploadPart(ctx context.Context, key string, uploadId string, partIndex int, part io.Reader, partSize int64, offset, totalSize int64) (string, error) {
  313. obscli, err := b.region.getOBSClient("")
  314. if err != nil {
  315. return "", errors.Wrap(err, "GetOBSClient")
  316. }
  317. input := &obs.UploadPartInput{}
  318. input.Bucket = b.Name
  319. input.Key = key
  320. input.UploadId = uploadId
  321. input.PartNumber = partIndex
  322. input.PartSize = partSize
  323. input.Body = part
  324. output, err := obscli.UploadPart(input)
  325. if err != nil {
  326. return "", errors.Wrap(err, "UploadPart")
  327. }
  328. return output.ETag, nil
  329. }
  330. func (b *SBucket) CompleteMultipartUpload(ctx context.Context, key string, uploadId string, partEtags []string) error {
  331. obscli, err := b.region.getOBSClient("")
  332. if err != nil {
  333. return errors.Wrap(err, "GetOBSClient")
  334. }
  335. input := &obs.CompleteMultipartUploadInput{}
  336. input.Bucket = b.Name
  337. input.Key = key
  338. input.UploadId = uploadId
  339. parts := make([]obs.Part, len(partEtags))
  340. for i := range partEtags {
  341. parts[i] = obs.Part{
  342. PartNumber: i + 1,
  343. ETag: partEtags[i],
  344. }
  345. }
  346. input.Parts = parts
  347. _, err = obscli.CompleteMultipartUpload(input)
  348. if err != nil {
  349. return errors.Wrap(err, "CompleteMultipartUpload")
  350. }
  351. return nil
  352. }
  353. func (b *SBucket) AbortMultipartUpload(ctx context.Context, key string, uploadId string) error {
  354. obscli, err := b.region.getOBSClient("")
  355. if err != nil {
  356. return errors.Wrap(err, "GetOBSClient")
  357. }
  358. input := &obs.AbortMultipartUploadInput{}
  359. input.Bucket = b.Name
  360. input.Key = key
  361. input.UploadId = uploadId
  362. _, err = obscli.AbortMultipartUpload(input)
  363. if err != nil {
  364. return errors.Wrap(err, "AbortMultipartUpload")
  365. }
  366. return nil
  367. }
  368. func (b *SBucket) DeleteObject(ctx context.Context, key string) error {
  369. obscli, err := b.region.getOBSClient("")
  370. if err != nil {
  371. return errors.Wrap(err, "GetOBSClient")
  372. }
  373. input := &obs.DeleteObjectInput{}
  374. input.Bucket = b.Name
  375. input.Key = key
  376. _, err = obscli.DeleteObject(input)
  377. if err != nil {
  378. return errors.Wrap(err, "DeleteObject")
  379. }
  380. return nil
  381. }
  382. func (b *SBucket) GetTempUrl(method string, key string, expire time.Duration) (string, error) {
  383. obscli, err := b.region.getOBSClient("")
  384. if err != nil {
  385. return "", errors.Wrap(err, "GetOBSClient")
  386. }
  387. input := obs.CreateSignedUrlInput{}
  388. input.Bucket = b.Name
  389. input.Key = key
  390. input.Expires = int(expire / time.Second)
  391. switch method {
  392. case "GET":
  393. input.Method = obs.HttpMethodGet
  394. case "PUT":
  395. input.Method = obs.HttpMethodPut
  396. case "DELETE":
  397. input.Method = obs.HttpMethodDelete
  398. default:
  399. return "", errors.Error("unsupported method")
  400. }
  401. output, err := obscli.CreateSignedUrl(&input)
  402. return output.SignedUrl, nil
  403. }
  404. func (b *SBucket) LimitSupport() cloudprovider.SBucketStats {
  405. return cloudprovider.SBucketStats{
  406. SizeBytes: 1,
  407. ObjectCount: -1,
  408. }
  409. }
  410. func (b *SBucket) GetLimit() cloudprovider.SBucketStats {
  411. stats := cloudprovider.SBucketStats{}
  412. obscli, err := b.region.getOBSClient("")
  413. if err != nil {
  414. log.Errorf("getOBSClient error %s", err)
  415. return stats
  416. }
  417. output, err := obscli.GetBucketQuota(b.Name)
  418. if err != nil {
  419. return stats
  420. }
  421. stats.SizeBytes = output.Quota
  422. return stats
  423. }
  424. func (b *SBucket) SetLimit(limit cloudprovider.SBucketStats) error {
  425. obscli, err := b.region.getOBSClient("")
  426. if err != nil {
  427. return errors.Wrap(err, "getOBSClient")
  428. }
  429. input := &obs.SetBucketQuotaInput{}
  430. input.Bucket = b.Name
  431. input.Quota = limit.SizeBytes
  432. _, err = obscli.SetBucketQuota(input)
  433. if err != nil {
  434. return errors.Wrap(err, "SetBucketQuota")
  435. }
  436. return nil
  437. }
  438. func (b *SBucket) CopyObject(ctx context.Context, destKey string, srcBucket, srcKey string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
  439. obscli, err := b.region.getOBSClient("")
  440. if err != nil {
  441. return errors.Wrap(err, "GetOBSClient")
  442. }
  443. input := &obs.CopyObjectInput{}
  444. input.Bucket = b.Name
  445. input.Key = destKey
  446. input.CopySourceBucket = srcBucket
  447. input.CopySourceKey = srcKey
  448. if len(storageClassStr) > 0 {
  449. input.StorageClass, err = str2StorageClass(storageClassStr)
  450. if err != nil {
  451. return err
  452. }
  453. }
  454. if len(cannedAcl) == 0 {
  455. cannedAcl = b.GetAcl()
  456. }
  457. input.ACL = obs.AclType(string(cannedAcl))
  458. if meta != nil {
  459. val := meta.Get(cloudprovider.META_HEADER_CONTENT_TYPE)
  460. if len(val) > 0 {
  461. input.ContentType = val
  462. }
  463. extraMeta := make(map[string]string)
  464. for k, v := range meta {
  465. if utils.IsInStringArray(k, []string{
  466. cloudprovider.META_HEADER_CONTENT_TYPE,
  467. }) {
  468. continue
  469. }
  470. if len(v[0]) > 0 {
  471. extraMeta[k] = v[0]
  472. }
  473. }
  474. input.Metadata = extraMeta
  475. input.MetadataDirective = obs.ReplaceMetadata
  476. } else {
  477. input.MetadataDirective = obs.CopyMetadata
  478. }
  479. _, err = obscli.CopyObject(input)
  480. if err != nil {
  481. return errors.Wrap(err, "obscli.CopyObject")
  482. }
  483. return nil
  484. }
  485. func (b *SBucket) GetObject(ctx context.Context, key string, rangeOpt *cloudprovider.SGetObjectRange) (io.ReadCloser, error) {
  486. obscli, err := b.region.getOBSClient("")
  487. if err != nil {
  488. return nil, errors.Wrap(err, "GetOBSClient")
  489. }
  490. input := &obs.GetObjectInput{}
  491. input.Bucket = b.Name
  492. input.Key = key
  493. if rangeOpt != nil {
  494. input.RangeStart = rangeOpt.Start
  495. input.RangeEnd = rangeOpt.End
  496. }
  497. output, err := obscli.GetObject(input)
  498. if err != nil {
  499. return nil, errors.Wrap(err, "obscli.GetObject")
  500. }
  501. return output.Body, nil
  502. }
  503. func (b *SBucket) CopyPart(ctx context.Context, key string, uploadId string, partIndex int, srcBucket string, srcKey string, srcOffset int64, srcLength int64) (string, error) {
  504. obscli, err := b.region.getOBSClient("")
  505. if err != nil {
  506. return "", errors.Wrap(err, "GetOBSClient")
  507. }
  508. input := &obs.CopyPartInput{}
  509. input.Bucket = b.Name
  510. input.Key = key
  511. input.UploadId = uploadId
  512. input.PartNumber = partIndex
  513. input.CopySourceBucket = srcBucket
  514. input.CopySourceKey = srcKey
  515. input.CopySourceRangeStart = srcOffset
  516. input.CopySourceRangeEnd = srcOffset + srcLength - 1
  517. output, err := obscli.CopyPart(input)
  518. if err != nil {
  519. return "", errors.Wrap(err, "CopyPart")
  520. }
  521. return output.ETag, nil
  522. }
  523. func (b *SBucket) SetWebsite(websitConf cloudprovider.SBucketWebsiteConf) error {
  524. obscli, err := b.region.getOBSClient("")
  525. if err != nil {
  526. return errors.Wrap(err, "GetOBSClient")
  527. }
  528. obsWebConf := obs.SetBucketWebsiteConfigurationInput{}
  529. obsWebConf.Bucket = b.Name
  530. obsWebConf.BucketWebsiteConfiguration = obs.BucketWebsiteConfiguration{
  531. IndexDocument: obs.IndexDocument{Suffix: websitConf.Index},
  532. ErrorDocument: obs.ErrorDocument{Key: websitConf.ErrorDocument},
  533. }
  534. _, err = obscli.SetBucketWebsiteConfiguration(&obsWebConf)
  535. if err != nil {
  536. return errors.Wrap(err, "obscli.SetBucketWebsiteConfiguration(&obsWebConf)")
  537. }
  538. return nil
  539. }
  540. func (b *SBucket) GetWebsiteConf() (cloudprovider.SBucketWebsiteConf, error) {
  541. result := cloudprovider.SBucketWebsiteConf{}
  542. obscli, err := b.region.getOBSClient("")
  543. if err != nil {
  544. return result, errors.Wrap(err, "GetOBSClient")
  545. }
  546. out, err := obscli.GetBucketWebsiteConfiguration(b.Name)
  547. if out == nil {
  548. return result, nil
  549. }
  550. result.Index = out.IndexDocument.Suffix
  551. result.ErrorDocument = out.ErrorDocument.Key
  552. result.Url = fmt.Sprintf("https://%s.obs-website.%s.myhuaweicloud.com", b.Name, b.region.GetId())
  553. return result, nil
  554. }
  555. func (b *SBucket) DeleteWebSiteConf() error {
  556. obscli, err := b.region.getOBSClient("")
  557. if err != nil {
  558. return errors.Wrap(err, "GetOBSClient")
  559. }
  560. _, err = obscli.DeleteBucketWebsiteConfiguration(b.Name)
  561. if err != nil {
  562. return errors.Wrapf(err, "obscli.DeleteBucketWebsiteConfiguration(%s)", b.Name)
  563. }
  564. return nil
  565. }
  566. func (b *SBucket) SetCORS(rules []cloudprovider.SBucketCORSRule) error {
  567. obscli, err := b.region.getOBSClient("")
  568. if err != nil {
  569. return errors.Wrap(err, "GetOBSClient")
  570. }
  571. opts := []obs.CorsRule{}
  572. for i := range rules {
  573. opts = append(opts, obs.CorsRule{
  574. AllowedOrigin: rules[i].AllowedOrigins,
  575. AllowedMethod: rules[i].AllowedMethods,
  576. AllowedHeader: rules[i].AllowedHeaders,
  577. MaxAgeSeconds: rules[i].MaxAgeSeconds,
  578. ExposeHeader: rules[i].ExposeHeaders,
  579. })
  580. }
  581. input := obs.SetBucketCorsInput{}
  582. input.Bucket = b.Name
  583. input.BucketCors.CorsRules = opts
  584. _, err = obscli.SetBucketCors(&input)
  585. if err != nil {
  586. return errors.Wrapf(err, "obscli.SetBucketCors(%s)", jsonutils.Marshal(input).String())
  587. }
  588. return nil
  589. }
  590. func (b *SBucket) GetCORSRules() ([]cloudprovider.SBucketCORSRule, error) {
  591. obscli, err := b.region.getOBSClient("")
  592. if err != nil {
  593. return nil, errors.Wrap(err, "GetOBSClient")
  594. }
  595. conf, err := obscli.GetBucketCors(b.Name)
  596. if err != nil {
  597. if !strings.Contains(err.Error(), "NoSuchCORSConfiguration") {
  598. return nil, errors.Wrapf(err, "obscli.GetBucketCors(%s)", b.Name)
  599. }
  600. }
  601. if conf == nil {
  602. return nil, nil
  603. }
  604. result := []cloudprovider.SBucketCORSRule{}
  605. for i := range conf.CorsRules {
  606. result = append(result, cloudprovider.SBucketCORSRule{
  607. AllowedOrigins: conf.CorsRules[i].AllowedOrigin,
  608. AllowedMethods: conf.CorsRules[i].AllowedMethod,
  609. AllowedHeaders: conf.CorsRules[i].AllowedHeader,
  610. MaxAgeSeconds: conf.CorsRules[i].MaxAgeSeconds,
  611. ExposeHeaders: conf.CorsRules[i].ExposeHeader,
  612. Id: strconv.Itoa(i),
  613. })
  614. }
  615. return result, nil
  616. }
  617. func (b *SBucket) DeleteCORS() error {
  618. obscli, err := b.region.getOBSClient("")
  619. if err != nil {
  620. return errors.Wrap(err, "GetOBSClient")
  621. }
  622. _, err = obscli.DeleteBucketCors(b.Name)
  623. if err != nil {
  624. return errors.Wrapf(err, "obscli.DeleteBucketCors(%s)", b.Name)
  625. }
  626. return nil
  627. }
  628. func (b *SBucket) GetTags() (map[string]string, error) {
  629. obscli, err := b.region.getOBSClient("")
  630. if err != nil {
  631. return nil, errors.Wrap(err, "GetOBSClient")
  632. }
  633. tagresult, err := obscli.GetBucketTagging(b.Name)
  634. if err != nil {
  635. if strings.Contains(err.Error(), "404") {
  636. return nil, nil
  637. }
  638. return nil, errors.Wrapf(err, "osscli.GetBucketTagging(%s)", b.Name)
  639. }
  640. result := map[string]string{}
  641. for i := range tagresult.Tags {
  642. result[tagresult.Tags[i].Key] = tagresult.Tags[i].Value
  643. }
  644. return result, nil
  645. }
  646. func (b *SBucket) SetTags(tags map[string]string, replace bool) error {
  647. obscli, err := b.region.getOBSClient("")
  648. if err != nil {
  649. return errors.Wrap(err, "GetOBSClient")
  650. }
  651. _, err = obscli.DeleteBucketTagging(b.Name)
  652. if err != nil {
  653. return errors.Wrapf(err, "DeleteBucketTagging")
  654. }
  655. if len(tags) == 0 {
  656. return nil
  657. }
  658. input := obs.SetBucketTaggingInput{BucketTagging: obs.BucketTagging{}}
  659. input.Bucket = b.Name
  660. for k, v := range tags {
  661. input.BucketTagging.Tags = append(input.BucketTagging.Tags, obs.Tag{Key: k, Value: v})
  662. }
  663. _, err = obscli.SetBucketTagging(&input)
  664. if err != nil {
  665. return errors.Wrapf(err, "obscli.SetBucketTagging(%s)", jsonutils.Marshal(input).String())
  666. }
  667. return nil
  668. }
  669. func (b *SBucket) ListMultipartUploads() ([]cloudprovider.SBucketMultipartUploads, error) {
  670. obscli, err := b.region.getOBSClient("")
  671. if err != nil {
  672. return nil, errors.Wrap(err, "GetOBSClient")
  673. }
  674. result := []cloudprovider.SBucketMultipartUploads{}
  675. input := obs.ListMultipartUploadsInput{Bucket: b.Name}
  676. keyMarker := ""
  677. uploadIDMarker := ""
  678. for {
  679. if len(keyMarker) > 0 {
  680. input.KeyMarker = keyMarker
  681. }
  682. if len(uploadIDMarker) > 0 {
  683. input.UploadIdMarker = uploadIDMarker
  684. }
  685. output, err := obscli.ListMultipartUploads(&input)
  686. if err != nil {
  687. return nil, errors.Wrap(err, " coscli.Bucket.ListMultipartUploads(context.Background(), &input)")
  688. }
  689. for i := range output.Uploads {
  690. temp := cloudprovider.SBucketMultipartUploads{
  691. ObjectName: output.Uploads[i].Key,
  692. UploadID: output.Uploads[i].UploadId,
  693. Initiator: output.Uploads[i].Initiator.DisplayName,
  694. Initiated: output.Uploads[i].Initiated,
  695. }
  696. result = append(result, temp)
  697. }
  698. keyMarker = output.NextKeyMarker
  699. uploadIDMarker = output.NextUploadIdMarker
  700. if !output.IsTruncated {
  701. break
  702. }
  703. }
  704. return result, nil
  705. }
  706. type SBucketPolicyStatement struct {
  707. Version string `json:"version"`
  708. Statement []SBucketPolicyStatementDetails `json:"Statement"`
  709. }
  710. type SBucketPolicyStatementDetails struct {
  711. Id string `json:"id"`
  712. Sid string `json:"Sid"`
  713. Effect string `json:"Effect"`
  714. Principal map[string][]string `json:"Principal"`
  715. Action []string `json:"Action"`
  716. Resource []string `json:"Resource"`
  717. Condition map[string]map[string]interface{} `json:"Condition"`
  718. }
  719. func (b *SBucket) GetPolicy() ([]cloudprovider.SBucketPolicyStatement, error) {
  720. policies, err := b.getPolicy()
  721. if err != nil {
  722. if errors.Cause(err) == errors.ErrNotFound {
  723. return []cloudprovider.SBucketPolicyStatement{}, nil
  724. }
  725. return nil, errors.Wrap(err, "getPolicy")
  726. }
  727. res := []cloudprovider.SBucketPolicyStatement{}
  728. for i, policy := range policies {
  729. temp := cloudprovider.SBucketPolicyStatement{}
  730. temp.Action = policy.Action
  731. temp.Principal = policy.Principal
  732. temp.PrincipalId = getLocalPrincipalId(policy.Principal["ID"])
  733. temp.Effect = policy.Effect
  734. temp.Resource = policy.Resource
  735. temp.ResourcePath = b.getResourcePaths(policy.Resource)
  736. temp.CannedAction = b.actionToCannedAction(policy.Action)
  737. temp.Condition = policy.Condition
  738. temp.Id = fmt.Sprintf("%d", i)
  739. res = append(res, temp)
  740. }
  741. return res, nil
  742. }
  743. func (b *SBucket) getPolicy() ([]SBucketPolicyStatementDetails, error) {
  744. obscli, err := b.region.getOBSClient("OBS")
  745. if err != nil {
  746. return nil, errors.Wrap(err, "GetOBSClient")
  747. }
  748. policies := []SBucketPolicyStatementDetails{}
  749. resp, err := obscli.GetBucketPolicy(b.Name)
  750. if err != nil {
  751. if strings.Contains(err.Error(), "NoSuchBucketPolicy") {
  752. return policies, nil
  753. }
  754. return nil, errors.Wrap(err, "GetPolicy")
  755. }
  756. obj, err := jsonutils.Parse([]byte(resp.Policy))
  757. if err != nil {
  758. return nil, errors.Wrap(err, "parse resp")
  759. }
  760. return policies, obj.Unmarshal(&policies, "Statement")
  761. }
  762. func (b *SBucket) SetPolicy(policy cloudprovider.SBucketPolicyStatementInput) error {
  763. old, err := b.getPolicy()
  764. if err != nil && !strings.Contains(err.Error(), "Not Found") {
  765. return errors.Wrap(err, "getPolicy")
  766. }
  767. if old == nil {
  768. old = []SBucketPolicyStatementDetails{}
  769. }
  770. ids := []string{}
  771. domains, err := b.region.client.getEnabledDomains()
  772. if err != nil {
  773. return errors.Wrap(err, "getEnabledDomains")
  774. }
  775. if len(domains) == 0 {
  776. return errors.Wrap(errors.ErrNotFound, "getEnabledDomains")
  777. }
  778. for i := range policy.PrincipalId {
  779. id := strings.Split(policy.PrincipalId[i], ":")
  780. if len(id) == 1 {
  781. if id[0] != "*" {
  782. ids = append(ids, fmt.Sprintf("domain/%s:user/*", domains[0].ID))
  783. } else {
  784. ids = append(ids, id[0])
  785. }
  786. }
  787. if len(id) == 2 {
  788. // 没有主账号id,设为owner id
  789. if len(id[0]) == 0 {
  790. id[0] = b.region.client.cpcfg.AccountId
  791. }
  792. // 没有子账号,默认和主账号相同
  793. if len(id[1]) == 0 {
  794. id[1] = "*"
  795. }
  796. ids = append(ids, fmt.Sprintf("domain/%s:user/%s", domains[0].ID, id[1]))
  797. }
  798. if len(id) > 2 {
  799. return errors.Wrap(cloudprovider.ErrNotSupported, "Invalida PrincipalId Input")
  800. }
  801. }
  802. old = append(old, SBucketPolicyStatementDetails{
  803. Effect: policy.Effect,
  804. Action: b.getAction(policy.CannedAction),
  805. Resource: b.getResources(policy.ResourcePath),
  806. Sid: utils.GenRequestId(20),
  807. Principal: map[string][]string{
  808. "ID": ids,
  809. },
  810. Condition: policy.Condition,
  811. })
  812. return b.setPolicy(old)
  813. }
  814. func (b *SBucket) setPolicy(policies []SBucketPolicyStatementDetails) error {
  815. obscli, err := b.region.getOBSClient(obs.SignatureObs)
  816. if err != nil {
  817. return errors.Wrap(err, "GetOBSClient")
  818. }
  819. det := map[string]interface{}{"Statement": policies}
  820. input := &obs.SetBucketPolicyInput{}
  821. input.Bucket = b.Name
  822. input.Policy = jsonutils.Marshal(det).String()
  823. _, err = obscli.SetBucketPolicy(input)
  824. if err != nil {
  825. return errors.Wrap(err, "setbucketPolicy")
  826. }
  827. return nil
  828. }
  829. func (b *SBucket) DeletePolicy(id []string) ([]cloudprovider.SBucketPolicyStatement, error) {
  830. obscli, err := b.region.getOBSClient(obs.SignatureObs)
  831. if err != nil {
  832. return nil, errors.Wrap(err, "GetOBSClient")
  833. }
  834. policies, err := b.getPolicy()
  835. if err != nil {
  836. return nil, errors.Wrap(err, "GetPolicy")
  837. }
  838. needKeep := []SBucketPolicyStatementDetails{}
  839. for i, policy := range policies {
  840. if utils.IsInStringArray(fmt.Sprintf("%d", i), id) {
  841. continue
  842. }
  843. needKeep = append(needKeep, policy)
  844. }
  845. _, err = obscli.DeleteBucketPolicy(b.Name)
  846. if err != nil {
  847. return nil, errors.Wrap(err, "DeleteBucketPolicy")
  848. }
  849. if len(needKeep) > 0 {
  850. err = b.setPolicy(needKeep)
  851. if err != nil {
  852. return nil, errors.Wrap(err, "setPolicy")
  853. }
  854. }
  855. return nil, nil
  856. }
  857. func (b *SBucket) getResources(paths []string) []string {
  858. res := []string{}
  859. for _, path := range paths {
  860. res = append(res, b.Name+path)
  861. }
  862. return res
  863. }
  864. func (b *SBucket) getResourcePaths(paths []string) []string {
  865. res := []string{}
  866. for _, path := range paths {
  867. res = append(res, strings.TrimPrefix(path, b.Name))
  868. }
  869. return res
  870. }
  871. func (b *SBucket) getResourcePath(path string) string {
  872. i := 0
  873. for i+len(b.Name) < len(path) {
  874. if path[i:i+len(b.Name)] == b.Name {
  875. return path[i+len(b.Name):]
  876. }
  877. i++
  878. }
  879. return ""
  880. }
  881. var readActions = []string{
  882. "Get*",
  883. "List*",
  884. }
  885. var readWriteActions = []string{
  886. "Get*",
  887. "List*",
  888. "Put*",
  889. }
  890. var fullControlActions = []string{
  891. "*",
  892. }
  893. func (b *SBucket) getAction(s string) []string {
  894. switch s {
  895. case "Read":
  896. return readActions
  897. case "ReadWrite":
  898. return readWriteActions
  899. case "FullControl":
  900. return fullControlActions
  901. default:
  902. return nil
  903. }
  904. }
  905. func (b *SBucket) actionToCannedAction(actions []string) string {
  906. if len(actions) == len(readActions) {
  907. for _, action := range actions {
  908. if !utils.IsInStringArray(action, readActions) {
  909. return ""
  910. }
  911. }
  912. return "Read"
  913. } else if len(actions) == len(fullControlActions) {
  914. for _, action := range actions {
  915. if !utils.IsInStringArray(action, fullControlActions) {
  916. return ""
  917. }
  918. }
  919. return "FullControl"
  920. } else if len(actions) == len(readWriteActions) {
  921. for _, action := range actions {
  922. if !utils.IsInStringArray(action, readWriteActions) {
  923. return ""
  924. }
  925. }
  926. return "ReadWrite"
  927. }
  928. return ""
  929. }
  930. /*
  931. example: in:domain/93887001882246db9273e5f59d544191:user/0932bb867900f4ca1f17c013ba9e3203
  932. out:93887001882246db9273e5f59d544191:0932bb867900f4ca1f17c013ba9e3203
  933. */
  934. func getLocalPrincipalId(principals []string) []string {
  935. res := []string{}
  936. for _, principal := range principals {
  937. if principal == "*" {
  938. res = append(res, principal+":"+principal)
  939. continue
  940. }
  941. principal = strings.Replace(principal, "domain/", "", 1)
  942. principal = strings.Replace(principal, "user/", "", 1)
  943. res = append(res, principal)
  944. }
  945. return res
  946. }