Skip to content

Commit ca24198

Browse files
authored
feat: cos acl verbose (#866)
* WIP: cos acl verbose * fix: acl body testcase * fix: cos acl - supress empty body * fix: verbose acl testcase * fix: cos testcase block name
1 parent 442fba9 commit ca24198

File tree

6 files changed

+198
-18
lines changed

6 files changed

+198
-18
lines changed

tencentcloud/data_source_tc_cos_buckets.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ package tencentcloud
1414

1515
import (
1616
"context"
17+
"encoding/xml"
1718
"fmt"
19+
"log"
1820
"strings"
1921

2022
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
@@ -271,10 +273,15 @@ func dataSourceTencentCloudCosBuckets() *schema.Resource {
271273
},
272274
},
273275
},
276+
"acl": {
277+
Type: schema.TypeString,
278+
Computed: true,
279+
Description: "Bucket access control configurations.",
280+
},
274281
"acl_body": {
275282
Type: schema.TypeString,
276283
Computed: true,
277-
Description: "Bucket acl configurations.",
284+
Description: "Bucket verbose acl configurations.",
278285
},
279286
"tags": {
280287
Type: schema.TypeMap,
@@ -366,11 +373,20 @@ func dataSourceTencentCloudCosBucketsRead(d *schema.ResourceData, meta interface
366373
bucket["origin_domain_rules"] = domainRules
367374
}
368375

369-
aclBody, err := cosService.GetBucketACLXML(ctx, *v.Name)
376+
aclBody, err := cosService.GetBucketACL(ctx, *v.Name)
377+
370378
if err != nil {
371379
return err
372380
}
373-
bucket["acl_body"] = aclBody
381+
382+
aclXML, err := xml.Marshal(aclBody)
383+
384+
if err != nil {
385+
log.Printf("WARN: acl body marshal failed: %s", err.Error())
386+
} else {
387+
bucket["acl"] = GetBucketPublicACL(aclBody)
388+
bucket["acl_body"] = string(aclXML)
389+
}
374390

375391
bucket["tags"] = respTags
376392
bucket["cos_bucket_url"] = fmt.Sprintf("%s.cos.%s.myqcloud.com", *v.Name, meta.(*TencentCloudClient).apiV3Conn.Region)

tencentcloud/resource_tc_cos_bucket.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Using verbose acl
2828
```hcl
2929
resource "tencentcloud_cos_bucket" "with_acl_body" {
3030
bucket = "mycos-1258798060"
31+
# NOTE: Granting http://cam.qcloud.com/groups/global/AllUsers `READ` Permission is equivalent to "public-read" acl
3132
acl_body = <<EOF
3233
<AccessControlPolicy>
3334
<Owner>
@@ -43,12 +44,14 @@ resource "tencentcloud_cos_bucket" "with_acl_body" {
4344
<Grant>
4445
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
4546
<ID>qcs::cam::uin/100000000001:uin/100000000001</ID>
47+
<DisplayName>qcs::cam::uin/100000000001:uin/100000000001</DisplayName>
4648
</Grantee>
4749
<Permission>WRITE</Permission>
4850
</Grant>
4951
<Grant>
5052
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
5153
<ID>qcs::cam::uin/100000000001:uin/100000000001</ID>
54+
<DisplayName>qcs::cam::uin/100000000001:uin/100000000001</DisplayName>
5255
</Grantee>
5356
<Permission>READ_ACP</Permission>
5457
</Grant>
@@ -233,8 +236,10 @@ package tencentcloud
233236
import (
234237
"bytes"
235238
"context"
239+
"encoding/xml"
236240
"fmt"
237241
"log"
242+
"reflect"
238243
"time"
239244

240245
"github.com/tencentyun/cos-go-sdk-v5"
@@ -404,9 +409,24 @@ func resourceTencentCloudCosBucket() *schema.Resource {
404409
Description: "The canned ACL to apply. Valid values: private, public-read, and public-read-write. Defaults to private.",
405410
},
406411
"acl_body": {
407-
Type: schema.TypeString,
408-
Optional: true,
409-
Description: "ACL XML body for multiple grant info.",
412+
Type: schema.TypeString,
413+
Optional: true,
414+
415+
DiffSuppressFunc: func(k, olds, news string, d *schema.ResourceData) bool {
416+
var oldXML cos.BucketGetACLResult
417+
err := xml.Unmarshal([]byte(olds), &oldXML)
418+
if err != nil {
419+
return olds == news
420+
}
421+
var newXML cos.BucketGetACLResult
422+
err = xml.Unmarshal([]byte(news), &newXML)
423+
if err != nil {
424+
return olds == news
425+
}
426+
suppress := reflect.DeepEqual(oldXML, newXML)
427+
return suppress
428+
},
429+
Description: "ACL XML body for multiple grant info. NOTE: this argument will overwrite `acl`. Check https://intl.cloud.tencent.com/document/product/436/7737 for more detail.",
410430
},
411431
"encryption_algorithm": {
412432
Type: schema.TypeString,
@@ -737,6 +757,26 @@ func resourceTencentCloudCosBucketRead(d *schema.ResourceData, meta interface{})
737757
if err != nil {
738758
return err
739759
}
760+
761+
// acl
762+
aclResult, err := cosService.GetBucketACL(ctx, bucket)
763+
764+
if err != nil {
765+
return err
766+
}
767+
768+
aclBody, err := xml.Marshal(aclResult)
769+
770+
if err != nil {
771+
log.Printf("[WARN] Marshal XML Error: %s", err.Error())
772+
} else if v, ok := d.Get("acl_body").(string); ok && v != "" {
773+
_ = d.Set("acl_body", string(aclBody))
774+
}
775+
776+
acl := GetBucketPublicACL(aclResult)
777+
778+
_ = d.Set("acl", acl)
779+
740780
// read the cors
741781
corsRules, err := cosService.GetBucketCors(ctx, bucket)
742782
if err != nil {

tencentcloud/resource_tc_cos_bucket_test.go

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,44 @@ func TestAccTencentCloudCosBucket_basic(t *testing.T) {
8686
),
8787
},
8888
{
89-
ResourceName: "tencentcloud_cos_bucket.bucket_basic",
89+
ResourceName: "tencentcloud_cos_bucket.bucket_basic",
90+
ImportState: true,
91+
ImportStateVerify: true,
92+
},
93+
},
94+
})
95+
}
96+
97+
func TestAccTencentCloudCosBucket_ACL(t *testing.T) {
98+
t.Parallel()
99+
100+
resource.Test(t, resource.TestCase{
101+
PreCheck: func() { testAccPreCheck(t) },
102+
Providers: testAccProviders,
103+
CheckDestroy: testAccCheckCosBucketDestroy,
104+
Steps: []resource.TestStep{
105+
{
106+
Config: testAccCosBucket_ACL(appid, ownerUin),
107+
Check: resource.ComposeAggregateTestCheckFunc(
108+
testAccCheckCosBucketExists("tencentcloud_cos_bucket.bucket_acl"),
109+
resource.TestCheckResourceAttr("tencentcloud_cos_bucket.bucket_acl", "acl", "public-read"),
110+
resource.TestCheckResourceAttrSet("tencentcloud_cos_bucket.bucket_acl", "acl_body"),
111+
),
112+
},
113+
// test update bucket acl
114+
{
115+
Config: testAccCosBucket_ACLUpdate(appid, ownerUin),
116+
Check: resource.ComposeAggregateTestCheckFunc(
117+
testAccCheckCosBucketExists("tencentcloud_cos_bucket.bucket_acl"),
118+
resource.TestCheckResourceAttr("tencentcloud_cos_bucket.bucket_acl", "acl", "private"),
119+
resource.TestCheckResourceAttrSet("tencentcloud_cos_bucket.bucket_acl", "acl_body"),
120+
),
121+
},
122+
{
123+
ResourceName: "tencentcloud_cos_bucket.bucket_acl",
90124
ImportState: true,
91125
ImportStateVerify: true,
92-
ImportStateVerifyIgnore: []string{"acl"},
126+
ImportStateVerifyIgnore: []string{"acl_body"},
93127
},
94128
},
95129
})
@@ -487,6 +521,64 @@ resource "tencentcloud_cos_bucket" "bucket_basic" {
487521
`, appid)
488522
}
489523

524+
func testAccCosBucket_ACL(appid, uin string) string {
525+
return fmt.Sprintf(`
526+
resource "tencentcloud_cos_bucket" "bucket_acl" {
527+
bucket = "tf-bucket-acl-%s"
528+
acl = "public-read"
529+
acl_body = <<EOF
530+
<AccessControlPolicy>
531+
<Owner>
532+
<ID>qcs::cam::uin/%[2]v:uin/%[2]v</ID>
533+
<DisplayName>qcs::cam::uin/%[2]v:uin/%[2]v</DisplayName>
534+
</Owner>
535+
<AccessControlList>
536+
<Grant>
537+
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
538+
<URI>http://cam.qcloud.com/groups/global/AllUsers</URI>
539+
</Grantee>
540+
<Permission>READ</Permission>
541+
</Grant>
542+
<Grant>
543+
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
544+
<ID>qcs::cam::uin/%[2]v:uin/%[2]v</ID>
545+
<DisplayName>qcs::cam::uin/%[2]v:uin/%[2]v</DisplayName>
546+
</Grantee>
547+
<Permission>FULL_CONTROL</Permission>
548+
</Grant>
549+
</AccessControlList>
550+
</AccessControlPolicy>
551+
EOF
552+
}
553+
`, appid, uin)
554+
}
555+
556+
func testAccCosBucket_ACLUpdate(appid, uin string) string {
557+
return fmt.Sprintf(`
558+
resource "tencentcloud_cos_bucket" "bucket_acl" {
559+
bucket = "tf-bucket-acl-%s"
560+
acl = "private"
561+
acl_body = <<EOF
562+
<AccessControlPolicy>
563+
<Owner>
564+
<ID>qcs::cam::uin/%[2]v:uin/%[2]v</ID>
565+
<DisplayName>qcs::cam::uin/%[2]v:uin/%[2]v</DisplayName>
566+
</Owner>
567+
<AccessControlList>
568+
<Grant>
569+
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
570+
<ID>qcs::cam::uin/%[2]v:uin/%[2]v</ID>
571+
<DisplayName>qcs::cam::uin/%[2]v:uin/%[2]v</DisplayName>
572+
</Grantee>
573+
<Permission>FULL_CONTROL</Permission>
574+
</Grant>
575+
</AccessControlList>
576+
</AccessControlPolicy>
577+
EOF
578+
}
579+
`, appid, uin)
580+
}
581+
490582
func testAccCosBucket_tags(appid string) string {
491583
return fmt.Sprintf(`
492584
resource "tencentcloud_cos_bucket" "bucket_tags" {

tencentcloud/service_tencentcloud_cos.go

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ type CosService struct {
2222
client *connectivity.TencentCloudClient
2323
}
2424

25+
const PUBLIC_GRANTEE = "http://cam.qcloud.com/groups/global/AllUsers"
26+
2527
func (me *CosService) HeadObject(ctx context.Context, bucket, key string) (info *s3.HeadObjectOutput, errRet error) {
2628
logId := getLogId(ctx)
2729

@@ -823,7 +825,7 @@ func (me *CosService) DeleteBucketPolicy(ctx context.Context, bucket string) (er
823825
return nil
824826
}
825827

826-
func (me *CosService) GetBucketACLXML(ctx context.Context, bucket string) (result *string, errRet error) {
828+
func (me *CosService) GetBucketACL(ctx context.Context, bucket string) (result *cos.BucketGetACLResult, errRet error) {
827829
logId := getLogId(ctx)
828830

829831
defer func() {
@@ -834,11 +836,11 @@ func (me *CosService) GetBucketACLXML(ctx context.Context, bucket string) (resul
834836
}()
835837

836838
ratelimit.Check("TencentcloudCosPutBucketACL")
837-
acl, response, err := me.client.UseTencentCosClient(bucket).Bucket.GetACL(ctx)
839+
acl, _, err := me.client.UseTencentCosClient(bucket).Bucket.GetACL(ctx)
838840

839841
if err != nil {
840842
errRet = fmt.Errorf("cos [GetBucketACL] error: %s, bucket: %s", err.Error(), bucket)
841-
return nil, errRet
843+
return
842844
}
843845

844846
aclXML, err := xml.Marshal(acl)
@@ -848,12 +850,38 @@ func (me *CosService) GetBucketACLXML(ctx context.Context, bucket string) (resul
848850
return nil, errRet
849851
}
850852

851-
resp, _ := json.Marshal(response)
853+
log.Printf("[DEBUG]%s api[%s] success, response body:\n%s\n",
854+
logId, "GetBucketACL", aclXML)
852855

853-
log.Printf("[DEBUG]%s api[%s] success, request body response body [%s]\n",
854-
logId, "GetBucketACL", resp)
856+
result = acl
857+
858+
return
859+
}
860+
861+
func GetBucketPublicACL(acl *cos.BucketGetACLResult) string {
862+
var publicRead, publicWrite bool
863+
864+
for i := range acl.AccessControlList {
865+
item := acl.AccessControlList[i]
866+
867+
if item.Grantee.URI == PUBLIC_GRANTEE && item.Permission == "READ" {
868+
publicRead = true
869+
}
870+
871+
if item.Grantee.URI == PUBLIC_GRANTEE && item.Permission == "WRITE" {
872+
publicWrite = true
873+
}
874+
}
875+
876+
if publicRead && !publicWrite {
877+
return s3.ObjectCannedACLPublicRead
878+
}
879+
880+
if publicRead && publicWrite {
881+
return s3.ObjectCannedACLPublicReadWrite
882+
}
855883

856-
return helper.String(string(aclXML)), nil
884+
return s3.ObjectCannedACLPrivate
857885
}
858886

859887
func (me *CosService) GetBucketPullOrigin(ctx context.Context, bucket string) (result []map[string]interface{}, errRet error) {

website/docs/d/cos_buckets.html.markdown

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ The following arguments are supported:
3333
In addition to all arguments above, the following attributes are exported:
3434

3535
* `bucket_list` - A list of bucket. Each element contains the following attributes:
36-
* `acl_body` - Bucket acl configurations.
36+
* `acl_body` - Bucket verbose acl configurations.
37+
* `acl` - Bucket access control configurations.
3738
* `bucket` - Bucket name, the format likes `<bucket>-<appid>`.
3839
* `cors_rules` - A list of CORS rule configurations.
3940
* `allowed_headers` - Specifies which headers are allowed.

website/docs/r/cos_bucket.html.markdown

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ Using verbose acl
3737

3838
```hcl
3939
resource "tencentcloud_cos_bucket" "with_acl_body" {
40-
bucket = "mycos-1258798060"
40+
bucket = "mycos-1258798060"
41+
# NOTE: Granting http://cam.qcloud.com/groups/global/AllUsers `READ` Permission is equivalent to "public-read" acl
4142
acl_body = <<EOF
4243
<AccessControlPolicy>
4344
<Owner>
@@ -53,12 +54,14 @@ resource "tencentcloud_cos_bucket" "with_acl_body" {
5354
<Grant>
5455
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
5556
<ID>qcs::cam::uin/100000000001:uin/100000000001</ID>
57+
<DisplayName>qcs::cam::uin/100000000001:uin/100000000001</DisplayName>
5658
</Grantee>
5759
<Permission>WRITE</Permission>
5860
</Grant>
5961
<Grant>
6062
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
6163
<ID>qcs::cam::uin/100000000001:uin/100000000001</ID>
64+
<DisplayName>qcs::cam::uin/100000000001:uin/100000000001</DisplayName>
6265
</Grantee>
6366
<Permission>READ_ACP</Permission>
6467
</Grant>
@@ -234,7 +237,7 @@ resource "tencentcloud_cos_bucket" "mycos" {
234237
The following arguments are supported:
235238

236239
* `bucket` - (Required, ForceNew) The name of a bucket to be created. Bucket format should be [custom name]-[appid], for example `mycos-1258798060`.
237-
* `acl_body` - (Optional) ACL XML body for multiple grant info.
240+
* `acl_body` - (Optional) ACL XML body for multiple grant info. NOTE: this argument will overwrite `acl`. Check https://intl.cloud.tencent.com/document/product/436/7737 for more detail.
238241
* `acl` - (Optional) The canned ACL to apply. Valid values: private, public-read, and public-read-write. Defaults to private.
239242
* `cors_rules` - (Optional) A rule of Cross-Origin Resource Sharing (documented below).
240243
* `encryption_algorithm` - (Optional) The server-side encryption algorithm to use. Valid value is `AES256`.

0 commit comments

Comments
 (0)