bucket.go 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124
  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 aws
  15. import (
  16. "context"
  17. "fmt"
  18. "io"
  19. "net/http"
  20. "net/url"
  21. "strconv"
  22. "strings"
  23. "time"
  24. v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
  25. "github.com/aws/aws-sdk-go-v2/service/s3"
  26. "github.com/aws/aws-sdk-go-v2/service/s3/types"
  27. "yunion.io/x/jsonutils"
  28. "yunion.io/x/log"
  29. "yunion.io/x/pkg/errors"
  30. "yunion.io/x/pkg/util/fileutils"
  31. "yunion.io/x/pkg/utils"
  32. "yunion.io/x/s3cli"
  33. "yunion.io/x/cloudmux/pkg/cloudprovider"
  34. "yunion.io/x/cloudmux/pkg/multicloud"
  35. )
  36. type SBucket struct {
  37. multicloud.SBaseBucket
  38. AwsTags
  39. region *SRegion
  40. Name string
  41. CreationDate time.Time
  42. Location string
  43. acl cloudprovider.TBucketACLType
  44. }
  45. func (b *SBucket) GetProjectId() string {
  46. return ""
  47. }
  48. func (b *SBucket) GetGlobalId() string {
  49. return b.Name
  50. }
  51. func (b *SBucket) GetName() string {
  52. return b.Name
  53. }
  54. func (b *SBucket) GetLocation() string {
  55. return b.Location
  56. }
  57. func (b *SBucket) GetIRegion() cloudprovider.ICloudRegion {
  58. return b.region
  59. }
  60. func (b *SBucket) GetCreatedAt() time.Time {
  61. return b.CreationDate
  62. }
  63. func (b *SBucket) GetStorageClass() string {
  64. return ""
  65. }
  66. func s3ToCannedAcl(acls []types.Grant) cloudprovider.TBucketACLType {
  67. switch {
  68. case len(acls) == 1:
  69. if acls[0].Grantee.URI == nil && acls[0].Permission == types.Permission(s3cli.PERMISSION_FULL_CONTROL) {
  70. return cloudprovider.ACLPrivate
  71. }
  72. case len(acls) == 2:
  73. for _, g := range acls {
  74. if g.Grantee.Type == types.Type(s3cli.GRANTEE_TYPE_GROUP) && g.Grantee.URI != nil && *g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_AUTH_USERS && g.Permission == types.Permission(s3cli.PERMISSION_READ) {
  75. return cloudprovider.ACLAuthRead
  76. }
  77. if g.Grantee.Type == types.Type(s3cli.GRANTEE_TYPE_GROUP) && g.Grantee.URI != nil && *g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && g.Permission == types.Permission(s3cli.PERMISSION_READ) {
  78. return cloudprovider.ACLPublicRead
  79. }
  80. }
  81. case len(acls) == 3:
  82. for _, g := range acls {
  83. if g.Grantee.Type == types.Type(s3cli.GRANTEE_TYPE_GROUP) && g.Grantee.URI != nil && *g.Grantee.URI == s3cli.GRANTEE_GROUP_URI_ALL_USERS && g.Permission == types.Permission(s3cli.PERMISSION_WRITE) {
  84. return cloudprovider.ACLPublicReadWrite
  85. }
  86. }
  87. }
  88. return cloudprovider.ACLUnknown
  89. }
  90. func (b *SBucket) GetAcl() cloudprovider.TBucketACLType {
  91. acl := cloudprovider.ACLPrivate
  92. s3cli, err := b.region.GetS3Client()
  93. if err != nil {
  94. log.Errorf("GetS3Client fail %v", err)
  95. return acl
  96. }
  97. input := &s3.GetBucketAclInput{}
  98. input.Bucket = &b.Name
  99. output, err := s3cli.GetBucketAcl(context.Background(), input)
  100. if err != nil {
  101. log.Errorf("s3cli.GetBucketAcl fail %s", err)
  102. return acl
  103. }
  104. return s3ToCannedAcl(output.Grants)
  105. }
  106. func (b *SBucket) SetAcl(aclStr cloudprovider.TBucketACLType) error {
  107. s3cli, err := b.region.GetS3Client()
  108. if err != nil {
  109. return errors.Wrap(err, "GetS3Client")
  110. }
  111. input := &s3.PutBucketAclInput{}
  112. input.Bucket = &b.Name
  113. input.ACL = types.BucketCannedACL(string(aclStr))
  114. _, err = s3cli.PutBucketAcl(context.Background(), input)
  115. if err != nil {
  116. return errors.Wrap(err, "PutBucketAcl")
  117. }
  118. return nil
  119. }
  120. func (b *SBucket) GetAccessUrls() []cloudprovider.SBucketAccessUrl {
  121. return []cloudprovider.SBucketAccessUrl{
  122. {
  123. Url: fmt.Sprintf("https://%s.%s", b.Name, b.region.getS3Endpoint()),
  124. Description: "bucket domain",
  125. Primary: true,
  126. },
  127. {
  128. Url: fmt.Sprintf("https://%s/%s", b.region.getS3Endpoint(), b.Name),
  129. Description: "s3 domain",
  130. },
  131. }
  132. }
  133. func (b *SBucket) GetWebsiteUrl() string {
  134. return fmt.Sprintf("http://%s.%s", b.Name, b.region.getS3WebsiteEndpoint())
  135. }
  136. func (b *SBucket) GetStats() cloudprovider.SBucketStats {
  137. stats, _ := cloudprovider.GetIBucketStats(b)
  138. return stats
  139. }
  140. func (b *SBucket) ListObjects(prefix string, marker string, delimiter string, maxCount int) (cloudprovider.SListObjectResult, error) {
  141. result := cloudprovider.SListObjectResult{}
  142. s3cli, err := b.region.GetS3Client()
  143. if err != nil {
  144. return result, errors.Wrap(err, "GetS3Client")
  145. }
  146. input := &s3.ListObjectsInput{}
  147. input.Bucket = &b.Name
  148. if len(prefix) > 0 {
  149. input.Prefix = &prefix
  150. }
  151. if len(marker) > 0 {
  152. input.Marker = &marker
  153. }
  154. if len(delimiter) > 0 {
  155. input.Delimiter = &delimiter
  156. }
  157. if maxCount > 0 {
  158. mc := int32(maxCount)
  159. input.MaxKeys = &mc
  160. }
  161. if len(prefix) > 0 {
  162. input.Prefix = &prefix
  163. }
  164. oResult, err := s3cli.ListObjects(context.Background(), input)
  165. if err != nil {
  166. return result, errors.Wrap(err, "ListObjects")
  167. }
  168. result.Objects = make([]cloudprovider.ICloudObject, 0)
  169. for _, object := range oResult.Contents {
  170. obj := &SObject{
  171. bucket: b,
  172. SBaseCloudObject: cloudprovider.SBaseCloudObject{
  173. StorageClass: string(object.StorageClass),
  174. Key: *object.Key,
  175. SizeBytes: *object.Size,
  176. ETag: *object.ETag,
  177. LastModified: *object.LastModified,
  178. },
  179. }
  180. result.Objects = append(result.Objects, obj)
  181. }
  182. if oResult.CommonPrefixes != nil {
  183. result.CommonPrefixes = make([]cloudprovider.ICloudObject, len(oResult.CommonPrefixes))
  184. for i, commPrefix := range oResult.CommonPrefixes {
  185. result.CommonPrefixes[i] = &SObject{
  186. bucket: b,
  187. SBaseCloudObject: cloudprovider.SBaseCloudObject{Key: *commPrefix.Prefix},
  188. }
  189. }
  190. }
  191. if oResult.IsTruncated != nil {
  192. result.IsTruncated = *oResult.IsTruncated
  193. }
  194. if oResult.NextMarker != nil {
  195. result.NextMarker = *oResult.NextMarker
  196. }
  197. return result, nil
  198. }
  199. func (b *SBucket) PutObject(ctx context.Context, key string, body io.Reader, sizeBytes int64, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
  200. if sizeBytes < 0 {
  201. return errors.Error("content length expected")
  202. }
  203. s3cli, err := b.region.GetS3Client()
  204. if err != nil {
  205. return errors.Wrap(err, "GetS3Client")
  206. }
  207. input := &s3.PutObjectInput{}
  208. input.Bucket = &b.Name
  209. input.Key = &key
  210. seeker, err := fileutils.NewReadSeeker(body, sizeBytes)
  211. if err != nil {
  212. return errors.Wrap(err, "newFakeSeeker")
  213. }
  214. defer seeker.Close()
  215. input.Body = seeker
  216. input.ContentLength = &sizeBytes
  217. if meta != nil {
  218. metaHdr := make(map[string]string)
  219. for k, v := range meta {
  220. if len(v) == 0 || len(v[0]) == 0 {
  221. continue
  222. }
  223. value := strings.TrimSpace(v[0])
  224. switch http.CanonicalHeaderKey(k) {
  225. case cloudprovider.META_HEADER_CACHE_CONTROL:
  226. input.CacheControl = &value
  227. case cloudprovider.META_HEADER_CONTENT_TYPE:
  228. input.ContentType = &value
  229. case cloudprovider.META_HEADER_CONTENT_MD5:
  230. input.ContentMD5 = &value
  231. case cloudprovider.META_HEADER_CONTENT_LANGUAGE:
  232. input.ContentLanguage = &value
  233. case cloudprovider.META_HEADER_CONTENT_ENCODING:
  234. input.ContentEncoding = &value
  235. case cloudprovider.META_HEADER_CONTENT_DISPOSITION:
  236. input.ContentDisposition = &value
  237. default:
  238. metaHdr[k] = value
  239. }
  240. }
  241. if len(metaHdr) > 0 {
  242. input.Metadata = metaHdr
  243. }
  244. }
  245. if len(cannedAcl) == 0 {
  246. cannedAcl = b.GetAcl()
  247. }
  248. input.ACL = types.ObjectCannedACL(string(cannedAcl))
  249. if len(storageClassStr) > 0 {
  250. input.StorageClass = types.StorageClass(storageClassStr)
  251. }
  252. _, err = s3cli.PutObject(ctx, input)
  253. if err != nil {
  254. return errors.Wrap(err, "PutObjectWithContext")
  255. }
  256. return nil
  257. }
  258. func (b *SBucket) NewMultipartUpload(ctx context.Context, key string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) (string, error) {
  259. s3cli, err := b.region.GetS3Client()
  260. if err != nil {
  261. return "", errors.Wrap(err, "GetS3Client")
  262. }
  263. input := &s3.CreateMultipartUploadInput{}
  264. input.Bucket = &b.Name
  265. input.Key = &key
  266. if meta != nil {
  267. metaHdr := make(map[string]string)
  268. for k, v := range meta {
  269. if len(v) == 0 || len(v[0]) == 0 {
  270. continue
  271. }
  272. value := strings.TrimSpace(v[0])
  273. switch http.CanonicalHeaderKey(k) {
  274. case cloudprovider.META_HEADER_CACHE_CONTROL:
  275. input.CacheControl = &value
  276. case cloudprovider.META_HEADER_CONTENT_TYPE:
  277. input.ContentType = &value
  278. case cloudprovider.META_HEADER_CONTENT_LANGUAGE:
  279. input.ContentLanguage = &value
  280. case cloudprovider.META_HEADER_CONTENT_ENCODING:
  281. input.ContentEncoding = &value
  282. case cloudprovider.META_HEADER_CONTENT_DISPOSITION:
  283. input.ContentDisposition = &value
  284. default:
  285. metaHdr[k] = value
  286. }
  287. }
  288. if len(metaHdr) > 0 {
  289. input.Metadata = metaHdr
  290. }
  291. }
  292. if len(cannedAcl) == 0 {
  293. cannedAcl = b.GetAcl()
  294. }
  295. input.ACL = types.ObjectCannedACL(string(cannedAcl))
  296. if len(storageClassStr) > 0 {
  297. input.StorageClass = types.StorageClass(storageClassStr)
  298. }
  299. output, err := s3cli.CreateMultipartUpload(ctx, input)
  300. if err != nil {
  301. return "", errors.Wrap(err, "CreateMultipartUpload")
  302. }
  303. return *output.UploadId, nil
  304. }
  305. func (b *SBucket) UploadPart(ctx context.Context, key string, uploadId string, partIndex int, part io.Reader, partSize int64, offset, totalSize int64) (string, error) {
  306. s3cli, err := b.region.GetS3Client()
  307. if err != nil {
  308. return "", errors.Wrap(err, "GetS3Client")
  309. }
  310. input := &s3.UploadPartInput{}
  311. input.Bucket = &b.Name
  312. input.Key = &key
  313. input.UploadId = &uploadId
  314. pn := int32(partIndex)
  315. input.PartNumber = &pn
  316. seeker, err := fileutils.NewReadSeeker(part, partSize)
  317. if err != nil {
  318. return "", errors.Wrap(err, "newFakeSeeker")
  319. }
  320. defer seeker.Close()
  321. input.Body = seeker
  322. input.ContentLength = &partSize
  323. output, err := s3cli.UploadPart(ctx, input)
  324. if err != nil {
  325. return "", errors.Wrap(err, "UploadPartWithContext")
  326. }
  327. return *output.ETag, nil
  328. }
  329. func (b *SBucket) CompleteMultipartUpload(ctx context.Context, key string, uploadId string, partEtags []string) error {
  330. s3cli, err := b.region.GetS3Client()
  331. if err != nil {
  332. return errors.Wrap(err, "GetS3Client")
  333. }
  334. input := &s3.CompleteMultipartUploadInput{}
  335. input.Bucket = &b.Name
  336. input.Key = &key
  337. input.UploadId = &uploadId
  338. uploads := &types.CompletedMultipartUpload{}
  339. parts := make([]types.CompletedPart, len(partEtags))
  340. for i := range partEtags {
  341. parts[i] = types.CompletedPart{}
  342. number := int32(i + 1)
  343. parts[i].PartNumber = &number
  344. parts[i].ETag = &partEtags[i]
  345. }
  346. uploads.Parts = parts
  347. input.MultipartUpload = uploads
  348. _, err = s3cli.CompleteMultipartUpload(ctx, input)
  349. if err != nil {
  350. return errors.Wrap(err, "CompleteMultipartUploadWithContext")
  351. }
  352. return nil
  353. }
  354. func (b *SBucket) AbortMultipartUpload(ctx context.Context, key string, uploadId string) error {
  355. s3cli, err := b.region.GetS3Client()
  356. if err != nil {
  357. return errors.Wrap(err, "GetS3Client")
  358. }
  359. input := &s3.AbortMultipartUploadInput{}
  360. input.Bucket = &b.Name
  361. input.Key = &key
  362. input.UploadId = &uploadId
  363. _, err = s3cli.AbortMultipartUpload(ctx, input)
  364. if err != nil {
  365. return errors.Wrap(err, "AbortMultipartUploadWithContext")
  366. }
  367. return nil
  368. }
  369. func (b *SBucket) DeleteObject(ctx context.Context, key string) error {
  370. s3cli, err := b.region.GetS3Client()
  371. if err != nil {
  372. return errors.Wrap(err, "GetS3Client")
  373. }
  374. input := &s3.DeleteObjectInput{}
  375. input.Bucket = &b.Name
  376. input.Key = &key
  377. _, err = s3cli.DeleteObject(ctx, input)
  378. if err != nil {
  379. return errors.Wrap(err, "DeleteObject")
  380. }
  381. return nil
  382. }
  383. func (b *SBucket) GetTempUrl(method string, key string, expire time.Duration) (string, error) {
  384. s3cli, err := b.region.GetS3Client()
  385. if err != nil {
  386. return "", errors.Wrap(err, "GetS3Client")
  387. }
  388. scli := s3.NewPresignClient(s3cli)
  389. ctx := context.Background()
  390. var request *v4.PresignedHTTPRequest
  391. switch method {
  392. case "GET":
  393. input := &s3.GetObjectInput{}
  394. input.Bucket = &b.Name
  395. input.Key = &key
  396. request, _ = scli.PresignGetObject(ctx, input)
  397. case "PUT":
  398. input := &s3.PutObjectInput{}
  399. input.Bucket = &b.Name
  400. input.Key = &key
  401. request, _ = scli.PresignPutObject(ctx, input)
  402. case "DELETE":
  403. input := &s3.DeleteObjectInput{}
  404. input.Bucket = &b.Name
  405. input.Key = &key
  406. request, _ = scli.PresignDeleteObject(ctx, input)
  407. default:
  408. return "", errors.Error("unsupported method")
  409. }
  410. return request.URL, nil
  411. }
  412. func (b *SBucket) CopyObject(ctx context.Context, destKey string, srcBucket, srcKey string, cannedAcl cloudprovider.TBucketACLType, storageClassStr string, meta http.Header) error {
  413. s3cli, err := b.region.GetS3Client()
  414. if err != nil {
  415. return errors.Wrap(err, "GetS3Client")
  416. }
  417. log.Debugf("copy from %s/%s to %s/%s", srcBucket, srcKey, b.Name, destKey)
  418. input := &s3.CopyObjectInput{}
  419. input.Bucket = &b.Name
  420. input.Key = &destKey
  421. copySource := fmt.Sprintf("%s/%s", srcBucket, url.PathEscape(srcKey))
  422. input.CopySource = &copySource
  423. input.StorageClass = types.StorageClass(storageClassStr)
  424. if len(cannedAcl) == 0 {
  425. cannedAcl = b.GetAcl()
  426. }
  427. input.ACL = types.ObjectCannedACL(string(cannedAcl))
  428. var metaDir string
  429. if meta != nil {
  430. metaHdr := make(map[string]string)
  431. for k, v := range meta {
  432. if len(v) == 0 || len(v[0]) == 0 {
  433. continue
  434. }
  435. value := strings.TrimSpace(v[0])
  436. switch http.CanonicalHeaderKey(k) {
  437. case cloudprovider.META_HEADER_CACHE_CONTROL:
  438. input.CacheControl = &value
  439. case cloudprovider.META_HEADER_CONTENT_TYPE:
  440. input.ContentType = &value
  441. case cloudprovider.META_HEADER_CONTENT_LANGUAGE:
  442. input.ContentLanguage = &value
  443. case cloudprovider.META_HEADER_CONTENT_ENCODING:
  444. input.ContentEncoding = &value
  445. case cloudprovider.META_HEADER_CONTENT_DISPOSITION:
  446. input.ContentDisposition = &value
  447. default:
  448. metaHdr[k] = value
  449. }
  450. }
  451. if len(metaHdr) > 0 {
  452. input.Metadata = metaHdr
  453. }
  454. metaDir = "REPLACE"
  455. } else {
  456. metaDir = "COPY"
  457. }
  458. input.MetadataDirective = types.MetadataDirective(metaDir)
  459. _, err = s3cli.CopyObject(ctx, input)
  460. if err != nil {
  461. return errors.Wrap(err, "CopyObject")
  462. }
  463. return nil
  464. }
  465. func (b *SBucket) GetObject(ctx context.Context, key string, rangeOpt *cloudprovider.SGetObjectRange) (io.ReadCloser, error) {
  466. s3cli, err := b.region.GetS3Client()
  467. if err != nil {
  468. return nil, errors.Wrap(err, "GetS3Client")
  469. }
  470. input := &s3.GetObjectInput{}
  471. input.Bucket = &b.Name
  472. input.Key = &key
  473. if rangeOpt != nil {
  474. rangeStr := rangeOpt.String()
  475. input.Range = &rangeStr
  476. }
  477. output, err := s3cli.GetObject(ctx, input)
  478. if err != nil {
  479. return nil, errors.Wrap(err, "GetObject")
  480. }
  481. return output.Body, nil
  482. }
  483. func (b *SBucket) CopyPart(ctx context.Context, key string, uploadId string, partNumber int, srcBucket string, srcKey string, srcOffset int64, srcLength int64) (string, error) {
  484. s3cli, err := b.region.GetS3Client()
  485. if err != nil {
  486. return "", errors.Wrap(err, "GetS3Client")
  487. }
  488. input := &s3.UploadPartCopyInput{}
  489. input.Bucket = &b.Name
  490. input.Key = &key
  491. input.UploadId = &uploadId
  492. pn := int32(partNumber)
  493. input.PartNumber = &pn
  494. copySource := fmt.Sprintf("/%s/%s", srcBucket, url.PathEscape(srcKey))
  495. input.CopySource = &copySource
  496. if srcLength > 0 {
  497. copySourceRange := fmt.Sprintf("bytes=%d-%d", srcOffset, srcOffset+srcLength-1)
  498. input.CopySourceRange = &copySourceRange
  499. }
  500. output, err := s3cli.UploadPartCopy(ctx, input)
  501. if err != nil {
  502. return "", errors.Wrap(err, "s3cli.UploadPartCopy")
  503. }
  504. return *output.CopyPartResult.ETag, nil
  505. }
  506. func (b *SBucket) SetWebsite(websitConf cloudprovider.SBucketWebsiteConf) error {
  507. s3cli, err := b.region.GetS3Client()
  508. if err != nil {
  509. return errors.Wrap(err, "GetS3Client")
  510. }
  511. input := &s3.PutBucketWebsiteInput{}
  512. input.WebsiteConfiguration = &types.WebsiteConfiguration{
  513. IndexDocument: &types.IndexDocument{Suffix: &websitConf.Index},
  514. ErrorDocument: &types.ErrorDocument{Key: &websitConf.ErrorDocument},
  515. }
  516. input.Bucket = &b.Name
  517. _, err = s3cli.PutBucketWebsite(context.Background(), input)
  518. if err != nil {
  519. return errors.Wrapf(err, "s3cli.PutBucketWebsite(%s)", jsonutils.Marshal(input).String())
  520. }
  521. return nil
  522. }
  523. func (b *SBucket) GetWebsiteConf() (cloudprovider.SBucketWebsiteConf, error) {
  524. result := cloudprovider.SBucketWebsiteConf{}
  525. s3cli, err := b.region.GetS3Client()
  526. if err != nil {
  527. return result, errors.Wrap(err, "GetS3Client")
  528. }
  529. input := &s3.GetBucketWebsiteInput{}
  530. input.Bucket = &b.Name
  531. webconfResult, err := s3cli.GetBucketWebsite(context.Background(), input)
  532. if err != nil {
  533. return result, errors.Wrapf(err, "s3cli.GetBucketWebsite(%s)", b.Name)
  534. }
  535. if webconfResult.IndexDocument != nil && webconfResult.IndexDocument.Suffix != nil {
  536. result.Index = *webconfResult.IndexDocument.Suffix
  537. }
  538. if webconfResult.ErrorDocument != nil && webconfResult.ErrorDocument.Key != nil {
  539. result.ErrorDocument = *webconfResult.ErrorDocument.Key
  540. }
  541. result.Url = b.GetWebsiteUrl()
  542. return result, nil
  543. }
  544. func (b *SBucket) DeleteWebSiteConf() error {
  545. s3cli, err := b.region.GetS3Client()
  546. if err != nil {
  547. return errors.Wrap(err, "GetS3Client")
  548. }
  549. input := s3.DeleteBucketWebsiteInput{}
  550. input.Bucket = &b.Name
  551. _, err = s3cli.DeleteBucketWebsite(context.Background(), &input)
  552. if err != nil {
  553. return errors.Wrapf(err, "s3cli.DeleteBucketWebsite(%s)", b.Name)
  554. }
  555. return nil
  556. }
  557. func InputToAwsApiSliceString(input []string) []string {
  558. result := []string{}
  559. for i := range input {
  560. result = append(result, input[i])
  561. }
  562. return result
  563. }
  564. func InputToAwsApiInt64(input int64) int64 {
  565. return input
  566. }
  567. func AwsApiSliceStringToOutput(input []*string) []string {
  568. result := []string{}
  569. for i := range input {
  570. if input[i] != nil {
  571. result = append(result, *input[i])
  572. } else {
  573. result = append(result, "")
  574. }
  575. }
  576. return result
  577. }
  578. func AwsApiInt64ToOutput(input *int64) int64 {
  579. if input == nil {
  580. return 0
  581. }
  582. return *input
  583. }
  584. func (b *SBucket) SetCORS(rules []cloudprovider.SBucketCORSRule) error {
  585. s3cli, err := b.region.GetS3Client()
  586. if err != nil {
  587. return errors.Wrap(err, "GetS3Client")
  588. }
  589. opts := []types.CORSRule{}
  590. for i := range rules {
  591. maxAgeSeconds := int32(rules[i].MaxAgeSeconds)
  592. opts = append(opts, types.CORSRule{
  593. AllowedOrigins: InputToAwsApiSliceString(rules[i].AllowedOrigins),
  594. AllowedMethods: InputToAwsApiSliceString(rules[i].AllowedMethods),
  595. AllowedHeaders: InputToAwsApiSliceString(rules[i].AllowedHeaders),
  596. MaxAgeSeconds: &maxAgeSeconds,
  597. ExposeHeaders: InputToAwsApiSliceString(rules[i].ExposeHeaders),
  598. })
  599. }
  600. input := &s3.PutBucketCorsInput{}
  601. input.Bucket = &b.Name
  602. input.CORSConfiguration = &types.CORSConfiguration{CORSRules: opts}
  603. _, err = s3cli.PutBucketCors(context.Background(), input)
  604. if err != nil {
  605. return errors.Wrapf(err, "PutBucketCors %v", err)
  606. }
  607. return nil
  608. }
  609. func (b *SBucket) GetCORSRules() ([]cloudprovider.SBucketCORSRule, error) {
  610. s3cli, err := b.region.GetS3Client()
  611. if err != nil {
  612. return nil, errors.Wrap(err, "GetS3Client")
  613. }
  614. input := &s3.GetBucketCorsInput{}
  615. input.Bucket = &b.Name
  616. conf, err := s3cli.GetBucketCors(context.Background(), input)
  617. if err != nil {
  618. if !strings.Contains(err.Error(), "NoSuchCORSConfiguration") {
  619. return nil, errors.Wrapf(err, "s3cli.GetBucketCors(%s)", b.Name)
  620. }
  621. }
  622. if conf == nil {
  623. return []cloudprovider.SBucketCORSRule{}, nil
  624. }
  625. result := []cloudprovider.SBucketCORSRule{}
  626. for i := range conf.CORSRules {
  627. result = append(result, cloudprovider.SBucketCORSRule{
  628. AllowedOrigins: conf.CORSRules[i].AllowedOrigins,
  629. AllowedMethods: conf.CORSRules[i].AllowedMethods,
  630. AllowedHeaders: conf.CORSRules[i].AllowedHeaders,
  631. MaxAgeSeconds: int(*conf.CORSRules[i].MaxAgeSeconds),
  632. ExposeHeaders: conf.CORSRules[i].ExposeHeaders,
  633. Id: strconv.Itoa(i),
  634. })
  635. }
  636. return result, nil
  637. }
  638. func (b *SBucket) DeleteCORS() error {
  639. s3cli, err := b.region.GetS3Client()
  640. if err != nil {
  641. return errors.Wrap(err, "GetS3Client")
  642. }
  643. input := s3.DeleteBucketCorsInput{}
  644. input.Bucket = &b.Name
  645. _, err = s3cli.DeleteBucketCors(context.Background(), &input)
  646. if err != nil {
  647. return errors.Wrapf(err, "s3cli.DeleteBucketCors(%s)", b.Name)
  648. }
  649. return nil
  650. }
  651. func (b *SBucket) GetTags() (map[string]string, error) {
  652. s3cli, err := b.region.GetS3Client()
  653. if err != nil {
  654. return nil, errors.Wrap(err, "GetS3Client")
  655. }
  656. tagresult, err := s3cli.GetBucketTagging(context.Background(), &s3.GetBucketTaggingInput{Bucket: &b.Name})
  657. if err != nil {
  658. if strings.Contains(err.Error(), "NoSuchTagSet") {
  659. return nil, nil
  660. }
  661. return nil, errors.Wrapf(err, "osscli.GetBucketTagging(%s)", b.Name)
  662. }
  663. if tagresult == nil {
  664. return nil, nil
  665. }
  666. result := map[string]string{}
  667. for i := range tagresult.TagSet {
  668. if tagresult.TagSet[i].Key != nil && tagresult.TagSet[i].Value != nil {
  669. result[*tagresult.TagSet[i].Key] = *tagresult.TagSet[i].Value
  670. }
  671. }
  672. return result, nil
  673. }
  674. func (b *SBucket) SetTags(tags map[string]string, replace bool) error {
  675. if !replace {
  676. return cloudprovider.ErrNotSupported
  677. }
  678. s3cli, err := b.region.GetS3Client()
  679. if err != nil {
  680. return errors.Wrap(err, "GetS3Client")
  681. }
  682. _, err = s3cli.DeleteBucketTagging(context.Background(), &s3.DeleteBucketTaggingInput{Bucket: &b.Name})
  683. if err != nil {
  684. return errors.Wrapf(err, "DeleteBucketTagging")
  685. }
  686. if len(tags) == 0 {
  687. return nil
  688. }
  689. input := &s3.PutBucketTaggingInput{Tagging: &types.Tagging{}}
  690. input.Bucket = &b.Name
  691. apiTagKeys := []string{}
  692. apiTagValues := []string{}
  693. for k, v := range tags {
  694. apiTagKeys = append(apiTagKeys, k)
  695. apiTagValues = append(apiTagValues, v)
  696. }
  697. for i := range apiTagKeys {
  698. input.Tagging.TagSet = append(input.Tagging.TagSet, types.Tag{Key: &apiTagKeys[i], Value: &apiTagValues[i]})
  699. }
  700. _, err = s3cli.PutBucketTagging(context.Background(), input)
  701. if err != nil {
  702. return errors.Wrapf(err, "obscli.SetBucketTagging(%s)", jsonutils.Marshal(input))
  703. }
  704. return nil
  705. }
  706. func (b *SBucket) ListMultipartUploads() ([]cloudprovider.SBucketMultipartUploads, error) {
  707. s3cli, err := b.region.GetS3Client()
  708. if err != nil {
  709. return nil, errors.Wrap(err, "GetS3Client")
  710. }
  711. result := []cloudprovider.SBucketMultipartUploads{}
  712. input := &s3.ListMultipartUploadsInput{}
  713. input.Bucket = &b.Name
  714. keyMarker := ""
  715. uploadIDMarker := ""
  716. for {
  717. if len(keyMarker) > 0 {
  718. input.KeyMarker = &keyMarker
  719. }
  720. if len(uploadIDMarker) > 0 {
  721. input.UploadIdMarker = &uploadIDMarker
  722. }
  723. output, err := s3cli.ListMultipartUploads(context.Background(), input)
  724. if err != nil {
  725. return nil, errors.Wrap(err, " coscli.Bucket.ListMultipartUploads(context.Background(), &input)")
  726. }
  727. if output == nil {
  728. return nil, nil
  729. }
  730. for i := range output.Uploads {
  731. temp := cloudprovider.SBucketMultipartUploads{}
  732. if output.Uploads[i].Key != nil {
  733. temp.ObjectName = *output.Uploads[i].Key
  734. }
  735. if output.Uploads[i].Initiator != nil {
  736. temp.Initiator = *output.Uploads[i].Initiator.DisplayName
  737. }
  738. if output.Uploads[i].Initiated != nil {
  739. temp.Initiated = *output.Uploads[i].Initiated
  740. }
  741. if output.Uploads[i].UploadId != nil {
  742. temp.UploadID = *output.Uploads[i].UploadId
  743. }
  744. result = append(result, temp)
  745. }
  746. if output.NextKeyMarker != nil {
  747. keyMarker = *output.NextKeyMarker
  748. }
  749. if output.NextUploadIdMarker != nil {
  750. uploadIDMarker = *output.NextUploadIdMarker
  751. }
  752. if output.IsTruncated == nil || !*output.IsTruncated {
  753. break
  754. }
  755. }
  756. return result, nil
  757. }
  758. type SBucketPolicyStatement struct {
  759. Version string `json:"Version"`
  760. Id string `json:"Id"`
  761. Statement []SBucketPolicyStatementDetails `json:"Statement"`
  762. }
  763. type SBucketPolicyStatementDetails struct {
  764. Sid string `json:"Sid"`
  765. Principal map[string][]string `json:"Principal"`
  766. Action []string `json:"Action"`
  767. Resource []string `json:"Resource"`
  768. Effect string `json:"Effect"`
  769. Condition map[string]map[string]interface{} `json:"Condition"`
  770. }
  771. func (b *SBucket) GetPolicy() ([]cloudprovider.SBucketPolicyStatement, error) {
  772. res := []cloudprovider.SBucketPolicyStatement{}
  773. policies, err := b.getPolicy()
  774. if err != nil {
  775. if errors.Cause(err) == errors.ErrNotFound {
  776. return res, nil
  777. }
  778. return nil, errors.Wrap(err, "get policy")
  779. }
  780. for i, policy := range policies {
  781. temp := cloudprovider.SBucketPolicyStatement{}
  782. temp.Action = policy.Action
  783. temp.Principal = policy.Principal
  784. temp.PrincipalId = getLocalPrincipalId(policy.Principal["AWS"])
  785. temp.PrincipalNames = getLocalPrincipalNames(policy.Principal["AWS"])
  786. temp.Effect = policy.Effect
  787. temp.Resource = policy.Resource
  788. temp.ResourcePath = policy.Resource
  789. temp.CannedAction = b.actionToCannedAction(policy.Action)
  790. temp.Id = fmt.Sprintf("%d", i)
  791. temp.Condition = policy.Condition
  792. res = append(res, temp)
  793. }
  794. return res, nil
  795. }
  796. func (b *SBucket) getPolicy() ([]SBucketPolicyStatementDetails, error) {
  797. s3cli, err := b.region.GetS3Client()
  798. if err != nil {
  799. return nil, errors.Wrap(err, "GetS3Client")
  800. }
  801. input := &s3.GetBucketPolicyInput{}
  802. input.Bucket = &b.Name
  803. conf, err := s3cli.GetBucketPolicy(context.Background(), input)
  804. if err != nil {
  805. if !strings.Contains(err.Error(), "NoSuch") {
  806. return nil, errors.Wrapf(err, "s3cli.GetBucketCors(%s)", b.Name)
  807. }
  808. }
  809. if conf == nil {
  810. return []SBucketPolicyStatementDetails{}, nil
  811. }
  812. if conf.Policy == nil {
  813. return nil, errors.ErrNotFound
  814. }
  815. obj, err := jsonutils.Parse([]byte(*conf.Policy))
  816. if err != nil {
  817. return nil, errors.Wrap(err, "parse policy")
  818. }
  819. policies := []SBucketPolicyStatementDetails{}
  820. err = obj.Unmarshal(&policies, "Statement")
  821. if err != nil {
  822. return nil, errors.Wrap(err, "Statement")
  823. }
  824. err = obj.Unmarshal(&policies, "Statement")
  825. if err != nil {
  826. return nil, errors.Wrap(err, "unmarshal")
  827. }
  828. return policies, nil
  829. }
  830. func (b *SBucket) SetPolicy(policy cloudprovider.SBucketPolicyStatementInput) error {
  831. old, err := b.getPolicy()
  832. if err != nil && err != errors.ErrNotFound {
  833. return errors.Wrap(err, "getPolicy")
  834. }
  835. if old == nil {
  836. old = []SBucketPolicyStatementDetails{}
  837. }
  838. ids := []string{}
  839. for i := range policy.PrincipalId {
  840. id := strings.Split(policy.PrincipalId[i], ":")
  841. if len(id) == 1 {
  842. ids = append(ids, id...)
  843. }
  844. if len(id) == 2 {
  845. // 没有主账号id,设为owner id
  846. if len(id[0]) == 0 {
  847. id[0] = b.region.client.GetAccountId()
  848. }
  849. // 没有子账号,默认和主账号相同
  850. if len(id[1]) == 0 {
  851. id[1] = "*"
  852. }
  853. ids = append(ids, id[1])
  854. }
  855. if len(id) > 2 {
  856. return errors.Wrap(cloudprovider.ErrNotSupported, "Invalida PrincipalId Input")
  857. }
  858. }
  859. old = append(old, SBucketPolicyStatementDetails{
  860. Effect: policy.Effect,
  861. Action: b.getAction(policy.CannedAction),
  862. Resource: b.getResources(policy.ResourcePath),
  863. Sid: utils.GenRequestId(20),
  864. Principal: map[string][]string{
  865. // "AWS": b.getPrincipal(policy.PrincipalId, policy.AccountId),
  866. "AWS": ids,
  867. },
  868. Condition: policy.Condition,
  869. })
  870. return b.setPolicy(old)
  871. }
  872. func (b *SBucket) setPolicy(policies []SBucketPolicyStatementDetails) error {
  873. s3cli, err := b.region.GetS3Client()
  874. if err != nil {
  875. return errors.Wrap(err, "GetS3Client")
  876. }
  877. input := &s3.PutBucketPolicyInput{}
  878. input.Bucket = &b.Name
  879. // example
  880. // test := `{"Statement":[{"Action":["s3:GetBucketAcl"],"Effect":"Allow","Principal":{"Service":["config.amazonaws.com"]},"Resource":["arn:aws-cn:s3:::config-bucket-2xxxxx6"],"Sid":"AWSConfigBucketPermissionsCheck"},{"Action":["s3:PutObject"],"Effect":"Allow","Principal":{"Service":["config.amazonaws.com"]},"Resource":["arn:aws-cn:s3:::config-bucket-2xxxxx6/AWSLogs/2xxxxx6/Config/*"],"Sid":"AWSConfigBucketDelivery"},{"Action":["s3:PutObject"],"Effect":"Allow","Principal":{"Service":["config.amazonaws.com"]},"Resource":["arn:aws-cn:s3:::config-bucket-2xxxxx6/AWSLogs/2xxxxx6/Config/*"],"Sid":"test"}]}`
  881. param := SBucketPolicyStatement{}
  882. param.Statement = policies
  883. policyStr := jsonutils.Marshal(param).String()
  884. input.Policy = &policyStr
  885. _, err = s3cli.PutBucketPolicy(context.Background(), input)
  886. if err != nil {
  887. return errors.Wrap(err, "PutBucketPolicy")
  888. }
  889. return nil
  890. }
  891. func (b *SBucket) DeletePolicy(id []string) ([]cloudprovider.SBucketPolicyStatement, error) {
  892. s3cli, err := b.region.GetS3Client()
  893. if err != nil {
  894. return nil, errors.Wrap(err, "GetS3Client")
  895. }
  896. policies, err := b.getPolicy()
  897. if err != nil {
  898. return nil, errors.Wrap(err, "GetPolicy")
  899. }
  900. needKeep := []SBucketPolicyStatementDetails{}
  901. for i, policy := range policies {
  902. if utils.IsInStringArray(fmt.Sprintf("%d", i), id) {
  903. continue
  904. }
  905. needKeep = append(needKeep, policy)
  906. }
  907. _, err = s3cli.DeleteBucketPolicy(context.Background(), &s3.DeleteBucketPolicyInput{Bucket: &b.Name})
  908. if err != nil {
  909. return nil, errors.Wrap(err, "DeleteBucketPolicy")
  910. }
  911. if len(needKeep) > 0 {
  912. err = b.setPolicy(needKeep)
  913. if err != nil {
  914. return nil, errors.Wrap(err, "setPolicy")
  915. }
  916. }
  917. res := []cloudprovider.SBucketPolicyStatement{}
  918. for _, policy := range needKeep {
  919. temp := cloudprovider.SBucketPolicyStatement{}
  920. temp.Action = policy.Action
  921. temp.Principal = policy.Principal
  922. temp.Effect = policy.Effect
  923. temp.Resource = policy.Resource
  924. temp.ResourcePath = policy.Resource
  925. temp.CannedAction = b.actionToCannedAction(policy.Action)
  926. temp.Id = policy.Sid
  927. temp.Condition = policy.Condition
  928. res = append(res, temp)
  929. }
  930. return res, nil
  931. }
  932. func (b *SBucket) getResources(paths []string) []string {
  933. res := []string{}
  934. for _, path := range paths {
  935. res = append(res, fmt.Sprintf("arn:%s:s3:::%s%s", b.region.GetARNPartition(), b.Name, path))
  936. }
  937. return res
  938. }
  939. func (b *SBucket) getPrincipal(principalIds []string, accountId string) []string {
  940. res := []string{}
  941. for _, id := range principalIds {
  942. res = append(res, fmt.Sprintf("arn:%s:iam::%s:user/%s", b.region.GetARNPartition(), accountId, id))
  943. }
  944. return res
  945. }
  946. func (b *SBucket) getAwsAction(actions []string) []string {
  947. res := []string{}
  948. for _, action := range actions {
  949. res = append(res, fmt.Sprintf("s3:%s", action))
  950. }
  951. return res
  952. }
  953. var readActions = []string{
  954. "s3:Get*",
  955. "s3:List*",
  956. }
  957. var readWriteActions = []string{
  958. "s3:Get*",
  959. "s3:List*",
  960. "s3:Create*",
  961. "s3:Put*",
  962. "s3:Delete*",
  963. "s3:Create*",
  964. "s3:AbortMultipartUpload",
  965. }
  966. var fullControlActions = []string{
  967. "s3:*",
  968. }
  969. func (b *SBucket) getAction(s string) []string {
  970. switch s {
  971. case "Read":
  972. return readActions
  973. case "ReadWrite":
  974. return readWriteActions
  975. case "FullControl":
  976. return fullControlActions
  977. default:
  978. return nil
  979. }
  980. }
  981. func (b *SBucket) actionToCannedAction(actions []string) string {
  982. if len(actions) == len(readActions) {
  983. for _, action := range actions {
  984. if !utils.IsInStringArray(action, readActions) {
  985. return ""
  986. }
  987. }
  988. return "Read"
  989. } else if len(actions) == len(fullControlActions) {
  990. for _, action := range actions {
  991. if !utils.IsInStringArray(action, fullControlActions) {
  992. return ""
  993. }
  994. }
  995. return "FullControl"
  996. } else if len(actions) == len(readWriteActions) {
  997. for _, action := range actions {
  998. if !utils.IsInStringArray(action, readWriteActions) {
  999. return ""
  1000. }
  1001. }
  1002. return "ReadWrite"
  1003. }
  1004. return ""
  1005. }
  1006. /*
  1007. example: in:arn:aws-cn:iam::248697896586:user/yunion-test
  1008. out:[248697896586:yunion-test]
  1009. */
  1010. func getLocalPrincipalId(principals []string) []string {
  1011. res := []string{}
  1012. for _, principal := range principals {
  1013. temp := strings.Split(principal, "::")
  1014. temp1 := strings.Split(temp[1], ":user/")
  1015. if len(temp1) > 1 {
  1016. if temp1[1] == "*" {
  1017. temp1[1] = temp1[0]
  1018. }
  1019. res = append(res, fmt.Sprintf("%s:%s", temp1[0], temp1[1]))
  1020. } else {
  1021. res = append(res, temp[1])
  1022. }
  1023. }
  1024. return res
  1025. }
  1026. /*
  1027. example: in:arn:aws-cn:iam::248697896586:user/yunion-test
  1028. out:["248697896586:yunion-test":"yunion-test"]
  1029. */
  1030. func getLocalPrincipalNames(principals []string) map[string]string {
  1031. res := map[string]string{}
  1032. for _, principal := range principals {
  1033. temp := strings.Split(principal, "::")
  1034. temp1 := strings.Split(temp[1], ":user/")
  1035. if len(temp1) > 1 {
  1036. if temp1[1] == "*" {
  1037. temp1[1] = temp1[0]
  1038. }
  1039. res[fmt.Sprintf("%s:%s", temp1[0], temp1[1])] = temp1[1]
  1040. }
  1041. }
  1042. return res
  1043. }