Skip to content

Commit 87a23e1

Browse files
authored
Feat/cos bucket replication (#817)
* feat: cos - replication * cos - add replica examples
1 parent e63aa1a commit 87a23e1

File tree

21 files changed

+1749
-121
lines changed

21 files changed

+1749
-121
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ require (
5757
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.199
5858
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.199
5959
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/wss v1.0.199
60-
github.com/tencentyun/cos-go-sdk-v5 v0.7.31-0.20210902132439-360bc9b1be6b
60+
github.com/tencentyun/cos-go-sdk-v5 v0.7.33
6161
github.com/yangwenmai/ratelimit v0.0.0-20180104140304-44221c2292e1
6262
github.com/zclconf/go-cty v1.4.2 // indirect
6363
golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/wss v1.0.199 h1:hMBLtiJ
536536
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/wss v1.0.199/go.mod h1:nnY91/H3j/Gu7V/oCA6Zeg8T5D3q36EUdBh4EjmHwqY=
537537
github.com/tencentyun/cos-go-sdk-v5 v0.7.31-0.20210902132439-360bc9b1be6b h1:rLl5sAeLt382023Kd3X4TaOEaT2hdgXWwTGyKiy16Zo=
538538
github.com/tencentyun/cos-go-sdk-v5 v0.7.31-0.20210902132439-360bc9b1be6b/go.mod h1:4E4+bQ2gBVJcgEC9Cufwylio4mXOct2iu05WjgEBx1o=
539+
github.com/tencentyun/cos-go-sdk-v5 v0.7.33 h1:5jmJU7U/1nf/7ZPDkrUL8KlF1oDUzTHsdtLNY6x0hq4=
540+
github.com/tencentyun/cos-go-sdk-v5 v0.7.33/go.mod h1:4E4+bQ2gBVJcgEC9Cufwylio4mXOct2iu05WjgEBx1o=
539541
github.com/tetafro/godot v0.3.7 h1:+mecr7RKrUKB5UQ1gwqEMn13sDKTyDR8KNIquB9mm+8=
540542
github.com/tetafro/godot v0.3.7/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
541543
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q=

tencentcloud/basic_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ the following must be changed to your resource id.
1010
*/
1111

1212
var appid string = os.Getenv("TENCENTCLOUD_APPID")
13+
var ownerUin string = os.Getenv("TENCENTCLOUD_OWNER_UIN")
1314

1415
const (
1516
defaultRegion = "ap-guangzhou"

tencentcloud/connectivity/client.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,12 @@ func (me *TencentCloudClient) UseCosClient() *s3.S3 {
152152

153153
// UseTencentCosClient tencent cloud own client for service instead of aws
154154
func (me *TencentCloudClient) UseTencentCosClient(bucket string) *cos.Client {
155-
if me.tencentCosConn != nil {
155+
u, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, me.Region))
156+
157+
if me.tencentCosConn != nil && me.tencentCosConn.BaseURL.BucketURL == u {
156158
return me.tencentCosConn
157159
}
158160

159-
u, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, me.Region))
160161
baseUrl := &cos.BaseURL{
161162
BucketURL: u,
162163
}

tencentcloud/resource_tc_cos_bucket.go

Lines changed: 185 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,28 @@ resource "tencentcloud_cos_bucket" "with_origin" {
133133
}
134134
```
135135
136+
Using replication
137+
```hcl
138+
resource "tencentcloud_cos_bucket" "replica1" {
139+
bucket = "tf-replica-foo-1234567890"
140+
acl = "private"
141+
versioning_enable = true
142+
}
143+
144+
resource "tencentcloud_cos_bucket" "with_replication" {
145+
bucket = "tf-bucket-replica-1234567890"
146+
acl = "private"
147+
versioning_enable = true
148+
replica_role = "qcs::cam::uin/100000000001:uin/100000000001"
149+
replica_rules {
150+
id = "test-rep1"
151+
status = "Enabled"
152+
prefix = "dist"
153+
destination_bucket = "qcs::cos:%s::${tencentcloud_cos_bucket.replica1.bucket}"
154+
}
155+
}
156+
```
157+
136158
Setting log status
137159
138160
```hcl
@@ -385,6 +407,47 @@ func resourceTencentCloudCosBucket() *schema.Resource {
385407
Default: false,
386408
Description: "Enable bucket versioning.",
387409
},
410+
"replica_role": {
411+
Type: schema.TypeString,
412+
Optional: true,
413+
RequiredWith: []string{"replica_rules", "versioning_enable"},
414+
Description: "Request initiator identifier, format: `qcs::cam::uin/<owneruin>:uin/<subuin>`. NOTE: only `versioning_enable` is true can configure this argument.",
415+
},
416+
"replica_rules": {
417+
Type: schema.TypeList,
418+
Optional: true,
419+
Description: "List of replica rule. NOTE: only `versioning_enable` is true and `replica_role` set can configure this argument.",
420+
RequiredWith: []string{"replica_role", "versioning_enable"},
421+
Elem: &schema.Resource{
422+
Schema: map[string]*schema.Schema{
423+
"id": {
424+
Type: schema.TypeString,
425+
Optional: true,
426+
Description: "Name of a specific rule.",
427+
},
428+
"status": {
429+
Type: schema.TypeString,
430+
Required: true,
431+
Description: "Status identifier, available values: `Enabled`, `Disabled`.",
432+
},
433+
"prefix": {
434+
Type: schema.TypeString,
435+
Optional: true,
436+
Description: "Prefix matching policy. Policies cannot overlap; otherwise, an error will be returned. To match the root directory, leave this parameter empty.",
437+
},
438+
"destination_bucket": {
439+
Type: schema.TypeString,
440+
Required: true,
441+
Description: "Destination bucket identifier, format: `qcs::cos:<region>::<bucketname-appid>`. NOTE: destination bucket must enable versioning.",
442+
},
443+
"destination_storage_class": {
444+
Type: schema.TypeString,
445+
Optional: true,
446+
Description: "Storage class of destination, available values: `STANDARD`, `INTELLIGENT_TIERING`, `STANDARD_IA`. default is following current class of destination.",
447+
},
448+
},
449+
},
450+
},
388451
"cors_rules": {
389452
Type: schema.TypeList,
390453
Optional: true,
@@ -569,18 +632,18 @@ func resourceTencentCloudCosBucket() *schema.Resource {
569632
Computed: true,
570633
Description: "The prefix log name which saves the access log of this bucket per 5 minutes. Eg. `MyLogPrefix/`. The log access file format is `log_target_bucket`/`log_prefix`{YYYY}/{MM}/{DD}/{time}_{random}_{index}.gz. Only valid when `log_enable` is `true`.",
571634
},
572-
//computed
573-
"cos_bucket_url": {
574-
Type: schema.TypeString,
575-
Computed: true,
576-
Description: "The URL of this cos bucket.",
577-
},
578635
"multi_az": {
579636
Type: schema.TypeBool,
580637
Optional: true,
581638
ForceNew: true,
582639
Description: "Indicates whether to create a bucket of multi available zone.",
583640
},
641+
//computed
642+
"cos_bucket_url": {
643+
Type: schema.TypeString,
644+
Computed: true,
645+
Description: "The URL of this cos bucket.",
646+
},
584647
},
585648
}
586649
}
@@ -595,6 +658,17 @@ func resourceTencentCloudCosBucketCreate(d *schema.ResourceData, meta interface{
595658

596659
bucket := d.Get("bucket").(string)
597660
acl := d.Get("acl").(string)
661+
role, roleOk := d.GetOk("replica_role")
662+
rule, ruleOk := d.GetOk("replica_rules")
663+
versioning, versioningOk := d.GetOk("versioning_enable")
664+
665+
if v := versioning.(bool); !versioningOk || !v {
666+
if roleOk || role.(string) != "" {
667+
return fmt.Errorf("cannot configure role unless versioning enable")
668+
} else if ruleOk || len(rule.([]interface{})) > 0 {
669+
return fmt.Errorf("cannot configure replica rule unless versioning enable")
670+
}
671+
}
598672

599673
cosService := CosService{client: meta.(*TencentCloudClient).apiV3Conn}
600674

@@ -713,6 +787,18 @@ func resourceTencentCloudCosBucketRead(d *schema.ResourceData, meta interface{})
713787
return fmt.Errorf("setting versioning_enable error: %v", err)
714788
}
715789

790+
replicaResult, err := cosService.GetBucketReplication(ctx, bucket)
791+
if err != nil {
792+
return err
793+
}
794+
795+
if replicaResult != nil {
796+
err := setBucketReplication(d, *replicaResult)
797+
if err != nil {
798+
return err
799+
}
800+
}
801+
716802
//read the log
717803
logEnable, logTargetBucket, logPrefix, err := cosService.GetBucketLogStatus(ctx, bucket)
718804
if err != nil {
@@ -823,6 +909,14 @@ func resourceTencentCloudCosBucketUpdate(d *schema.ResourceData, meta interface{
823909
d.SetPartial("versioning_enable")
824910
}
825911

912+
if d.HasChange("replica_role") || d.HasChange("replica_rules") {
913+
err := resourceTencentCloudCosBucketReplicaUpdate(ctx, cosService, d)
914+
915+
if err != nil {
916+
return err
917+
}
918+
}
919+
826920
if d.HasChange("tags") {
827921
bucket := d.Id()
828922

@@ -949,6 +1043,40 @@ func resourceTencentCloudCosBucketVersioningUpdate(ctx context.Context, client *
9491043
return nil
9501044
}
9511045

1046+
func resourceTencentCloudCosBucketReplicaUpdate(ctx context.Context, service CosService, d *schema.ResourceData) error {
1047+
bucket := d.Get("bucket").(string)
1048+
oldRole, newRole := d.GetChange("replica_role")
1049+
oldRules, newRules := d.GetChange("replica_rules")
1050+
oldRuleLength := len(oldRules.([]interface{}))
1051+
newRuleLength := len(newRules.([]interface{}))
1052+
1053+
// check if remove
1054+
if oldRole.(string) != "" && newRole.(string) == "" || oldRuleLength > 0 && newRuleLength == 0 {
1055+
result, err := service.GetBucketReplication(ctx, bucket)
1056+
if err != nil {
1057+
return err
1058+
}
1059+
1060+
if result != nil {
1061+
err := service.DeleteBucketReplication(ctx, d.Get("bucket").(string))
1062+
if err != nil {
1063+
return err
1064+
}
1065+
}
1066+
} else if newRole.(string) != "" || newRuleLength > 0 {
1067+
role, rules, _ := getBucketReplications(d)
1068+
err := service.PutBucketReplication(ctx, d.Get("bucket").(string), role, rules)
1069+
if err != nil {
1070+
return err
1071+
}
1072+
}
1073+
1074+
d.SetPartial("replica_role")
1075+
d.SetPartial("replica_rules")
1076+
1077+
return nil
1078+
}
1079+
9521080
func resourceTencentCloudCosBucketAclUpdate(ctx context.Context, client *s3.S3, d *schema.ResourceData) error {
9531081
logId := getLogId(ctx)
9541082

@@ -1471,3 +1599,54 @@ func transitionHash(v interface{}) int {
14711599
}
14721600
return hashcode.String(buf.String())
14731601
}
1602+
1603+
func getBucketReplications(d *schema.ResourceData) (role string, rules []cos.BucketReplicationRule, err error) {
1604+
role = d.Get("replica_role").(string)
1605+
replicaRules := d.Get("replica_rules").([]interface{})
1606+
for i := range replicaRules {
1607+
item := replicaRules[i].(map[string]interface{})
1608+
rule := cos.BucketReplicationRule{
1609+
Status: item["status"].(string),
1610+
Destination: &cos.ReplicationDestination{
1611+
Bucket: item["destination_bucket"].(string),
1612+
},
1613+
}
1614+
if v, ok := item["prefix"].(string); ok {
1615+
rule.Prefix = v
1616+
}
1617+
if v, ok := item["id"].(string); ok {
1618+
rule.ID = v
1619+
}
1620+
if v, ok := item["destination_storage_class"].(string); ok {
1621+
rule.Destination.StorageClass = v
1622+
}
1623+
rules = append(rules, rule)
1624+
}
1625+
return
1626+
}
1627+
1628+
func setBucketReplication(d *schema.ResourceData, result cos.GetBucketReplicationResult) (err error) {
1629+
if result.Role != "" {
1630+
err = d.Set("replica_role", result.Role)
1631+
}
1632+
rules := make([]map[string]interface{}, 0)
1633+
if len(result.Rule) > 0 {
1634+
for i := range result.Rule {
1635+
item := result.Rule[i]
1636+
rule := map[string]interface{}{
1637+
"status": item.Status,
1638+
"destination_bucket": item.Destination.Bucket,
1639+
"destination_storage_class": item.Destination.StorageClass,
1640+
}
1641+
if item.ID != "" {
1642+
rule["id"] = item.ID
1643+
}
1644+
if item.Prefix != "" {
1645+
rule["prefix"] = item.Prefix
1646+
}
1647+
rules = append(rules, rule)
1648+
}
1649+
}
1650+
err = d.Set("replica_rules", rules)
1651+
return
1652+
}

0 commit comments

Comments
 (0)